teem-1.11.0~svn6057/0000775000175000017500000000000012203513761013605 5ustar domibeldomibelteem-1.11.0~svn6057/DartConfig.cmake0000775000175000017500000000260712165631065016644 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # INCLUDE(${CMAKE_SOURCE_DIR}/CTestConfig.cmake) SET(NIGHTLY_START_TIME "${CTEST_NIGHTLY_START_TIME}") SET(DROP_METHOD "${CTEST_DROP_METHOD}") SET(DROP_SITE "${CTEST_DROP_SITE}") SET(DROP_LOCATION "${CTEST_DROP_LOCATION}") SET(TRIGGER_SITE "${CTEST_TRIGGER_SITE}") teem-1.11.0~svn6057/data/0000775000175000017500000000000012203513760014515 5ustar domibeldomibelteem-1.11.0~svn6057/data/fmob-c4h.nrrd0000664000175000017500000050062412013004533017002 0ustar domibeldomibelNRRD0004 # Complete NRRD file format specification at: # http://teem.sourceforge.net/nrrd/format.html type: double dimension: 3 space dimension: 3 sizes: 32 40 16 space directions: (0.051282064516129031,0,0) (0,0.05128205128205128,0) (0,0,0.051282000000000001) centerings: cell cell cell kinds: space space space endian: little encoding: raw space origin: (-0.79487200000000002,-1,-0.38461499999999998)  lw?dT'?WYՎ?;A~?{x֒?tm7?(h _?*@0ۊ?D;?s v?TuI?>2?)Dkh?ڡ?=fGgF?GbHq?9`[?!&2?"H?Pٜ?cMw?F;?W$!?cTvx? e?,(B?NXץ? ?-?e47?tHi?I1?-,ŷ?H}r?,$?qp¤?7[?+Wݒ?Р?!)JП?w5C#C?1⑯-?cv?|֊?z0™?w3? 75?oa9?1#:??J#o?%⯆? 6`0?Gk?u!? ֬?V?gANݠ?ӕ:?]?ixƍ?Hd^k?FD50u?&䒥?Ŵ/T ?2>/?C'Q? -"?'ݼG~?\C_/x?| x4 ?\'^?s?CE?^;B3і?֥A?>Me?s'?Aa<]?;%qNv?0Н?e?;b^?;9?gЯ?|a?MI8 ?ӿgh?;4F?D9l?t>9? ?Jyn`R?yRi??e>xY?Bgʉ??a] ȥ?6r?D[ ??TZ?Jֲw?w@?-u?n-?A%SC?U,1?s?#GN,ߣ?RT?8= ?oW-d?pN?jN ]?>GQv?I`?l9?En7?{v(?P#??^\?f٨?eIC?x/?e+?&y9?m^.q~h?K~¼t?W&J*_?]._?gu?|?A& ߡ?Jq{*?pjm?S-?J?d%+h?$dUy?q>f$?c( ?h=6?gul?(n?ys?I8?%N?"?!kp?DL?9.?d;ѡx?3U.9&ء?;&k?࿐?iB?+?հ?WxL̥?e(?rǦg?gd ? g?6)S?4d#&?uSʖo?m< 1?/^0?ɫRFTf?ziZ?jma?ŕY_?pREqM*?vzuc?4?@bQ@g?b!?Sha-?xJ?U?5mU?xi^? >[?/s(?("?;?i?)u L?tYG1;ٟ?_7EXW?kmm}?lOHNb?9E@?>^?+f3X?f?f.2Z?20C?n%"??4yn*?Di,5?;dy틠?H[-?||8"??o?epM?XXr?aiY?p@E?`CS?c'O?_FzP?eϼ?ԕ ? ?a*e!?vx,Z?T痾?Tt?(T??d ;k?1 +?T!H,?6+Ա?d?An?%޷?ҭy.?pm*I?%??ㄻǡ?I"?,kG:eԡ?5?lF?(¥?c~?GM?훣?^a?[ ?!H;w?w?:6F?ȩT3?^E11?3ʨ2?l>3u?HJJS?=?;D?I?N?C,?%s?f?_W?\bg?>Ǎ?lp ??F8? Ƣ?NN, kt?ݔt9v?v?GM?U*ʢ?0 ޸ʨ?1?۹?0 ߒ?|U⨕?- ?a?nZ e3?4#F?_Tٞ??|C? phih?i5)G?}Uj??=A ?YT c2?`)??/[8?֢?j\C?9Zi ?O?վ?1?ɂ̓? 9?$0?rZR?L7LМ?*?W(0A?>7?do!?1??~i3̝? e? .*ɠ?y=Vx?fFiv?:$^?d?oMUy?Y()Cu?[E?|z1?D&X['?mKpv?}:EN˖?6?CDoɠ??$V?㧛?Qw-=?}A?h k?KcDݛ?h0.? E??K4ί ?4Ь?3^?@kX:?=o?-& ?卍?`??_Gpv?~#B?l?TO?q3?TK?Ù?p؁랡??u?8PHa? R36?t(ҡ?~/ٟ?4N VU?XSG?եm?t (7{?4?WGI8?؍yʧ?st?(SQ?Xvlf?dBg]?7U;?-gN?ڤ(@?f]?|!n?6M=?DΜo?g?Z0??-S?roۅi?eY떣?lp?e"#?rzn?wg~?;G?I# d?:=?1"?֢QJ?]e? ?z&׼?坣?Yd/?q?1PW?ų;?U.zş?o:?]54*\?o?f8?4c?xsJ?A1ޤ? 5?Pj^C? feM?Z}?1A?+l?*?t??-l?~;T?)8O,?K{?K1LZ?Hvu[?x( ܟ?}Kt?O0=?: YJ֞?Q˖]??7Y?aTc?L̠?\?;UO?,?sn?~?eɤ?&sCҺ?4\?Eh2?_G?&?s?0ҡ?l|?Sr ?7p??$.ǻ?ŚV? Շ?z)?? -?\b a?bb.?V#)?6?+(F?ʁ.>?a}9,?8Hj?S0?Zl[?__'X?{]HD?r"?[sހ?Wkŗ?8P f?$kFj%?Yݡ? |U?b ̡?E-?ypgX?f$%?foʟ?4C?8AѺ?4NƏJ?U?XuOe?4,?Tę?x?g޵?bJ?jTo?JR?˴Hԟ?A͡?ɾ['?Ţ?Dy?6?w?@?{([-?I?V~? ,?QD:?Sl&?P=bB?n5hn?o?Z?c?"}Au?zHNW?q2̣?`~"|?^o?cR?9w??ב/??` Y/?`@gc?5 ??!J?xc5#?ZK?;| {n?0y?ɤ >?g,d?&iwΔ?M?}#b?E?6mI?AR?A?Y":q@?cܝ ?;q}N?uI(Ѧ?)%=0?`<ʠ|?as?X'L?v|a?jL?2J ۣ?3U[?nD}?]Yn?>q?v0d?H«v?Uţ?;?Sͺ?,ʧ?o!?Tã?Yh˧?No?rɠ?!>uC?!j'A?5A?RCў?7`n}?(¢?8,?0 ̥?r)?l 7?:9?qG!^?A=!$?JV?mcYDf?gT>ߣ?rzg?G4'm?-wD?@}y?|Ny?3è.?ZK?xYϡ?P3?2?SI?y>. ?y?Q"~?hq6 ?^iQZ?6ITb?wnú?fm21?/7?upb{?*]?Le"i?7Xw9p?q@-?_Ѭ?֊D?Hg>?8! ? (?*`?Yՠ?u<>.?+Mi?.ΫN?NLV™?*K??#I< ;?ގ}5?WA=c?Ӂf?A8rs?w'k?3˾?s ?? (?6? ?FO[?zt;.?mb?n9?k;c?uɕ?X?taf/?;?U?:̥? QȠ?]c:?_q o?%&MD?适h?+`7g?Nl?#T?\ST?{〜?ޛ? &?^}?:;??X?~r5!|?;P,y?O?e̔v?Q&(?Twm?Qu`?Lu?S(?Gq? ?a^?T'?3h.p?vÜ?+\I?VF O?6RL?Gz?|ѫC?P\ꉦ?qR?Xc5§? ?.?ZgD?aɤ?+b?gF?c?|3d?+ȍϤ?/?\V¢?U.G?.?KZ ?yeN?Bo?5J?Ł,?hZ S?l_#?*"D?'@r2?G ?,/Oo?bF;?`pȬԤ?Ƙ?Д??H@rR?lV,?Ey?y?Z7'?ؑ?c#x?ޟO?׸ngR?hO5?tjw:?y-uӢ?Ѳ$5?nY?1'Pȡ?M0jN?,4A͙?%4?#?Ls?fNU&?cܯ?U\z?PfQ2?.Cu?Z~?qX֠?A/?H?rM%?|lQ?“m?[7]-?O;/?nV8? qr?qdj?(*e?sH?`ru?.k K?Pac_?_a??r)3`C?ۄA?V$F?#Ǡ?l u?HӤ?# 0!?\K~?g ?o?cT'?)Cp,?{d?/Tk6?^~e?I?Fk?V$zM?>ݵ? -TtS?~Bmv?im?$LnTo?_(&?$m֢??½??(k/?\w4?y[>Q?Iئ6?{W<0ݡ?(^?TRt"?kR*@?OC?HVt ?y@? UZ8?QRw̡?tϴԢ?]7A f?A?*E?2F?=Sh?r?mG?;?hpk?|fn?ؠ٠?{.k?jK]? 0Y?:XO⨠?/??X,h ?4?ߢ-?٪R?6agS?'?,b?P?:o(?+Ei?+p C?D~?AJԜ??-?h{?eϢ?BH)?r^X ?v>,o9?[UB?*j?ٚkE?z?E %?10}w?|l?ۑaw?Q?kl?~??7"4ѡ? N?/ynܦ?Pٮ?rT?倕*?̶)ꅤ?&(z?d;(?]܍-mڞ?3͏?XѦ?_V ?tP3?P?HgVK?k?d ?Y>\\?J< ?Bs?e}?yjrGO?w!sЃ?g=71v&?uC? ۾?uZ\?@̠?Z㍆`?X?nlH??#0`?GI?CuHj?͔ҟ?k?BA]?^ڕ?s)T?J*鿵?MY?3~}?xY?WmR?Z?y?H?|7R?"sc?SgУ?"?@?hg֥?58L?M5?!74?dߟ0-?bhD?|5n?g_N?g՞?x7&y?k ??n4U? V/?jj?HkO?8ɣB?Ұ'?o3<Σ?{ 0?u\?,ڤ?L-<ﻤ?(h?Ã<Ǡ?}RҔ?3tS?W4͠?p?';qԗ?6HB?ųE/?k眩?- k??X_?֒Kfl?L"0#?Ĝ*ꅤ?2?,?Sw?}?g?0Bx*?=ͧ?OӘ ?)HӞ?f?уQ?f?G?*^tm?RW 1A?|!T?>D?"+V?yG!^K?Z?uK?KH?`?1Vq?u"?3?S?L|?? ߅r?lE&?m ^?b?AgU,?/(?Y9A?ᦎ?tq%YJ?0?3|9?ZT?ZSUa?o6 ?$Í͡?`vVnN?ݼ?-V?ѱe0?{? t+:`D?NfE[?VG?X?z +ן?2({?%4n? [?w\?Y1?Q¡?ˤ?J@Pâ?,M?ϱ?9)n?dFˉ?bk? ̃eץ?G0?עܡ?(Uq?_V\ ~ʠ?Nj_@ڿ?Xa?IpG$?UI?*%\H?ۆ?R@ Nף?z?ǭ{f?$q?R!s ?lzХ?`z?(y0?rs?Ji?E0ᕡ?.〡?k?(*" ?Ϝʰ ?mB?;U>X?\?e k?ژL?Ů ? '{`?I5U??ڱ ?w?g Z?g}?!E[?e4u?D۾+?bJ{?ᩴ?R{?Y (?Şp?Al?e?I?^?V?_> ?U^`?A?o[??H:w?v6?2-?c?*ޜ?办?.ќ)?F"1?6h?$?ۆ?A&w\?qXڡ?V)?tH+?ۦ񢧔?am?}0*6Nף?Uա?Z%?o\?1&'?FT1z?2?L]uv?" I]Ĥ?Mb̢?#^?aA_Ƞ?4TUP?0Ɏ?0g(?gܾr?i5*q?־?T떠?Ǎ&uC?V]I?ZD:?@s}3F?F*)?g2?R@?q}?(=Қ?g:'T?RH?cc 04?D^p?j{?gT?hX?m]?%ʓ?/Ѣ?Ɉ?t?"@?H*V?#ȝWS?+ g?R?~ޜ? gc]?IeAn?_Iȣ?7:? 9?ʾn?kâ?HX?_Ң?”3o?N/^?T{G?Bز*?p8?@l?צ%z;?;|?8֧??Iѥ?QX?$qm?p2z,'?{?7;?@m?n~??ښ̢?1?GQ@?\VA?r:1?: -(?F??>Y?B?%l!?{?jH?]Hi4?G-?\Wv?m%w?]GD?Ŗ؟?Է>ơ?H Ģ?έܡh? ypUڣ?N/?UV?z?:?|/?,{?UeT? HWˣ?O 5!:?yؓ?sס{?Cɸ&ҫ?*8 ?[?\A ?oE:?˭?X蚡?k-?bQ=?k|{?"Ot[s?(S2?g5?Rn?4!?0x 2q?vf:?ڎw?^6lo?%KUǡ?Hgc?٬v?EA?-13?щK]?z?d9? ]O?dnCQ? T?{@Ȅ?h?iyQM?tHz?X5?Stڝ?Xx?"C[%?uxq?'ی?5!??&dž?.D+?$?4o=?%Cy?9٥?XK?!9S?¤?DU?, ?XPX?k!%?^wz)>?ך?JR=?VhE;?H?;)F?/,?JYʥ?]~С?>ƼI?\T?g~?pl&X?_^o?@-'Zƣ?aĠ2z?H{?9?9(q_?hm?  ?Gn(B!?U͢?գܘ?ⴾ ?CdLEۡ?FR}?N:I?y?!Ώ?6ݠ?Y[?Þp)S?'?E£?xsP??_?窋U?kk2?ƽQo&?;hB?Z}{C?DIޤ?f-bX?!b/?)z9*L?/x&4t2?-! ?] ?Yh:z?yhҦ?"<%Z*?>qɠ?3u}5?"?Hr%c]?{?TXR?V=?׾2?b?`zT?tV?M?n:(,4?W?h?װ{U?'?WW?O&PU?v:'8}?< C?j5?p~N? ő?P C?0 ?{`_?kf?Nd?˒$VZ?|g|?"?nKR? Ȣ?d07?P;΂?,D??V;? ~?%}?]?f>fc?$Dg?֢S5?o)?pF=?މv?K?j ~Ī?;wp?!t?W:[?+?Jl]?)?;ޡ?h?rL١?3pv?7?1J7?te?[`T?.c$?ܩX[(?l?,Y5?ڴOo6?* h"?6ء?5nY?۫3 ?q0@?3'3u?!q?F Z?QvȠ?䞒@o?tTn?prf?߾ 1?$?0hH?G"۩?ik ?",c?wJ??\ʒ$?#gԡ?#YG?Ȱ*ʠ?49?=t/>?[I|??O?SAƢ?ȀjjO?*;O? }?op:9?ĶO?o&⏲?UܢE>?)D?ұ?ٿG-? ?X_bņ? Nsm'#?F,?[a?'V?R ?RWFǣ?J??h罤?삥 ?.Zţ?&fԡ?xYw?/h) !?c?{v?)y?GV#?z<k!??+?zleT?YNӰ?,o23?;զ?_h?q«P?d*D?z ?>ʓ4 ?%8#?迉??~hR?OM?ኢ?,(ζi?[N?3-5j?i(}s?>J3,?h#)Ե?kcyX?0lrΡ?Z/ѣ?%_1?#4? -9+?\ԧ?*?lo?A"M?F/?@8W?K({?O?"x5[X?N5C4?j2B?σ`Q?EI?@s?k+r%?θ c?_䎨?-SJj?}~2k?ynw_? 7?V?2 -9?2z.ɠ?`? `)F?e\Nl?.H?hj`ڢ?v&B?)?a̠?R,\?"tq? iLG?稙U?\܉Ġ?H.#J#?n;?\ͺ#?-?P*o%q?C?3~r?dO?>b.?rC?Dh?)On?ݗ?U X?rݥ?`-?6\Ƣ? ~[? W?_]`?&i̡?]?b'd?)Z?܏?Ȕƾ?Ba?k)Ϧ?x>Π?|?#-G?)j 곥?IXh???&ʆT?E)UO?kd{Y?# ?0!ʢ?}e#?tS[?gc^?(ܧˡ?2?>sE?cs?&aG@? J£?[$?,}GJr? [y?YV'zϥ? Q6?gˠ?9q?^ap?"+?t3Ly?E/?/Jt?iQ?yk?*ȥ?DŽ{Ff?D%Ӡ?Uu?? ;#?|?k?sl?ĥ?b??̆nZ(? vƐ??k`?9DS?&e?O9٣? A?8]N?Nط3?rLlN?_l?;S>? ~8?X?ei,?Fo?-3ܢ?C?BJ?(&q?o0)ѡ?w]?Zä?׈Z[?L#ؠѤ?9xط?0 ?b)E̢?m8\c?l?OtV_?}? ţ? Z=?*a?[N?\N}"?'}!!?d.?x??:kQ?Y]zm?j?[$g1_?g7 ?|GĒ?f.AL?g)Ԣ?!]Cɦ?~0;:?4n3 ?gR?RIB?=1?:4?-/?}6ԡ?Dl/ ?Kj1?>8?`Τ?]HPZe?kw?*,R10?L]ܤ?tK!~ন? F)?c?gĉ??OQ?e?G/;q?N(?N^μ?rb~??AFbC?8+y?qpZc+?ru?l?#9=?HY6?J́??(hX?铞?;}e]?(?NF7?8r>?Ǵ3 ?[P?Wc?֤?߼My:?Wʂd?:x Ԥ?Ь^?h?WbR?F;?v S3?$v?#*ܠ?\}>?$y?a.aQL?olѠ?\?a.LϠ?O}eV?{3<?\"?7ڦ?Ed? t ɡ? 2KX?mS׫?aw<0?!0?S(:?露?^?] ɏܠ?'Kwv?Hݖm%?f/F??B?/ο?-?6/Ǔ-ҡ?> qD?&.y?Xd?8U|?n?t?>3?OJ?C54?z@/?P?å| ?Xx~??.l?rRn?jEV?dU?oڥ?; "?#S?|p?*wţ?v9?zFݨt?لg?)~~?gψ?$E?+(bSAݢ?j ?ܔ?7g?^(? ̰?M܌v?-E;?P ?m??^ )3?{0Ρ?nU?Jw;?yFb?ڕ02?}=?B?iw^)?>~?p?Hox_?KWo2?}?Oϡ?K!.?v?ܠ?LOj>?ܮ&?3?Bբ?IŠ?@y?Xr ?Dma:?0>?WD?`kC ? *?蠍ע??͛!8ǡ?lT ԡ?qգ??ڒC?N?gq6ʨ?t$Mͤ?'#%0?Ժy?'?ˁ  ??SS+?PHX`?7*W?f?_?Ꝣ?`*R?ܟ?LXWY?ܷ7a?1DK?mչ"?(Fq?/3$?Imj?2Fa? Ipm?lL?ӻٮw6?[H?;_i,?z?^XD0?_?l:Z?P30ڥ?祺O١?d[j?n?]0E?%*X?!S?'8?R?g;y??Z/?ҼB?Ԃ$M?x!9?hQTm?;2?D?use?`])Ρ?~*\?ߒ M?VǢ?YS ?TԠ?@ ?@u%>?o]c;v?c?.w-?WdEx?m!R?/_F?nv?%OZ?ѣ?'5aii?r6?6=w??ƌ\ `?]#F?o ?]y̓?AūO?f9 ?F@?=/?y󪠡?3?Y ?KR?æʞ^?Z?:l?@6ˣ?-r@?\8o}آ?ff? "`-?{?EZߟ?ŸͿ?ۆ“?]h?׌ O?ǩj?:ә?dMgF?ań?C?ܑXh?: s?I9?`~??{B; ?8;KY?Ǡ מ?/.m?MVM?XK?@NX?CEidk?eΘC?`&]?L#qǘ@?WVDA?#:2[?Fݤ?,Bf ?OeC?|,?Vs6!?np?Y2K?T_+b??¿?Pg?NV:y?F\^s?g? uU?FF`?/_?a&?]qʖ?З?@ʞ?3r99?uӒ?3&?L8涥?)yA}?wXE?`ݧY?:zTd?;"s?#<֢?fw`?I\?]٘?SWP4?$۳3?,Z?I??u(d?R 2??A¢?*Tu?Qҁn? ?tŠ?UÅ`?5m.3?ZCĢ? u?-e?uzp?~Ȣ??RKġ?nzG?M7o?`ԟ?wAg?zS?ow?]&WO?*?Τf_?LBܖ?"Y?`?l|?-U?׊V?ܢ?-=s?gM䚡?TG?y7?@ &?.[5S?0<ԅ'?{oܫ?jeۢ?uA?#:??㕳O?/-?; ww?UU$?B7?q((}˥?k#܏?7a2?Pl?Y8?7$?T??sڞ?|/?e I?o)59Ϧ?8A)?T@?ˎA ?D?M ɢ?ǟ<?[u?i(Ϥ?A &p? ?1v٠?O=(%ç?Mi?%Ԫ?'!?Ww?;U?[){?] ע?mWT???k?=L?$? j?dE29?K<?}S?V>N?k;3?o^ѳ?N?S5dm?@i?Q?PCA|??A:?,Ρ{f?0Fք?w}?}ن? 8 d?~?AD?UF5? b]?Oː?tds?ᨍ?>_.?F Dߠ? X?0S2ѣ?Ͷ6ߺ?/F?1bS?Fˡ?ɣ?̣ޏ?!eƣ?<? ?,;ű? ?Dx'W?i?t?EHE?zP6(?@E?=?62?<'7?Oѫ=?dkڠ?n x7 ?2=8?-oQ ?yF\? Y?g]?H?G?|/?в4N?:+٠?%Ǎ𰃣?Ԓh?]$4^?b\cy?Ql?_1?zԣ?qƭ-?FQm?w*H?{MhǮ?L?HSX?«^?*XǞ\?k/je?mhvݞ?f?Qՠ?ll?"Zt?t*Ņ?3l$/?a^?.;?Ĕ8?C(?l42г?l ?%*??I5ۥ?.nJr?>D[?Ba'? ?Am?w:xc?7i!i?qo'?5X?lK<8?t[g?F>J?El?j?=`r?6Nny?W]?NAt?37?R} ?R8PT?DV??$IǏ?" ??L`?[i?%s=E?Tb2͢?D?eu#J?YQ`E?^ܦM?X ?.ݙA?hȠ?-+bڹ?iT??-? 0ϥ?T:-D?S?,5Vv'?)՟?KK#l?O?YS?j϶?"3?Du}@?ڶ?M_?WeI?W?]Kʄ?ښf?~"?U&Z?!ܤ ?ԙ*?4p?"?`)JН?AW_?rj?1j4?o8_鏠?j?ćȄ?~դ?V9?YSѣ?T%*W?sV̞?\36??Hw<~?Pre?2.q?xI?`! ?*ʇ?ނ&?Pˡ?̙?BϠ?Q3?Gg?Qm ?is:??Hpa? lܰ?oo?})?~cϤ?ج$?E"6$?.)fs?5+?ǰЦĘ?zˢ@ޜ?)x ?iƣ? kl?5iC? &#H?;/?xfL?sGW?F>;I?9 ?BHk)?i[? ?K>I?=1I}?`SZ?CI?˂lh?>F.Q?F\ز?Ɉ?n_?3z:?sZM?ȟw.?Lv\B?q]Ц?جчy? ?-t̓?Y?S籢?qA댡?B4ND? w?ɋ?t"뙢?IJ'?Yd+Ȥ?qˬk?3?zq?n?d1Ģ?5 ?jM?lAb?'Kߤ?N |?^Ǟ?#Q?OWD?}6}?p#?7ח?)8[? z?*y-? $(.?GKpd?㐹?Gl?m$'aҟ?3 (?RP/?6j?Fɛ?IJ >?7y2?CmCۤ? Og?d͆p?J4?Kء?HT??y\TԆ? O??tIoä?\a?SYDC?]zf?vg???mሏ??Bɠ?ք~'?6?ͬ`ݤ?>!4&?O$H?k&?G>PX?l6ANx?^x֞?k?Z&?jif?$צ?hF?3$?V?3?()+g?Y ?S=8ܠ?c?EX1?F&)'Q?u%w?#5$??ЋT<آ??? )$o(?{= ?g? FW翤?۞?z%??1??;sʲ?v.6?E&!?;:$>5?œ+?Va?N1?ܚpDn??H|!?H'u?[og'?ܝ l?:F6?_<_j?Kgn?C 8?Ȉg?au?ζ I1?|P]}f?ϙXף?`L?$_? xs?E]?3ˏ!?^\?v;?4?c?12{?U ,?rpg6?bw2?г:?^mNs?Xhf?)@?sQ#ݟ?YRC?W&? ?Do?Cp?a"f>?**?b ?:Zl?&|?ڕ ?cT`?Cbe`?c^ ?Kf?M3?=ˠ?vEC?uС??*26?M?@Ш?KP?0@?4\ͥ?=Wś?2us?A/|U?&?&?/Q^?biH??fxL(?lxJ@?N譿2?A|g?6mY?5qơ=d?9Gܬɢ?nsg?9`W?`a?Ov?*rM&?F/?A?auI? ?P,dY?B!#k?F?a/?~zH%?{Fg?@T<5?}W] ?r^Ck?VOd?^ ?۴)?a?A!ʠ?क!?Nx?qҠ?bϓ?' ɡ?UiN?F?Vp?Mqܢ?M.???)MV3?*LUb?cb@?76f$?M/bᕟ?7í!?Ehwm?1? B;?uq?ܝa?cPx?U8S:?PȂu?֦?P|yJ?7 #?K@<g?&)\ҫ?J:?F,?,A?Aͫ?]qQ?yuO?ywܟ?ʣ?w??VVC?Jܡ?{+? ?#Zl?khz/̤?hH֡?Cb?7?gy?Ի֠?rnݑ?_Ӄ?:}?Ln4 ?tE _ѡ?aØʢ?.{e?K$?UH?-F~(??P_[0?ks:ӡ?vKc?mlM?%OFF?P ?O?2Z?lC? N?E?O`?L&?'?x ?=;ڠ?ZDH?Ť?l?+3?+ Np?h?NF<ա?G!Z?,{ a?jY]?nRsś?!81?w!?O4V ?%?*~w?.w?<?$ n?Ʌ?|6`?|?}~?(+ ?X?ģ?{?զ?Ƥ(?%̐?|Hѝ?wM3W̝? v?]fJ?GG3ݟ?%A?^ ͞?sI?p,֢?ҍО? 1) ??C?SW8?y4;5h?pZ(?f!?.u?jMp?Cc?\M?r{ȣ?U&Sa?G c|?ho?ռ2?<?„U?uyh?O?.?,_4?ې5?C2?Ǒ?BesrU?I&>?B8?, f?x=Χ?d2?3 ] ?v(;M?$$4?&?T1?q?ğ>ʨ?ZE;z+?gH=Y? _Mԝ? L`?|g+?eC%?#tڥ?У?{?S?pq5?_*?Z ?C`?CTǢ?@B$? m?% ?0*cY? 7?,N;?u?a^?T?kX?U?p( ?AMiN?!୥? T?7 ơ?)?!c"?jhݥ?'?EvI,4N?ďfb?^x_?%a1g?<~pk?8?{ץ?_YkXc?9eCɨ?wpx?W ?lǢ?$ ?StXإ?6$Ρ?^z ?qK+?ʢ?fy?|)?b%?d[?[ր?(?¥B?ֽ!y?Q{^T?ڴW?6g?j?t?ں0Ӧ?w?s~?Os2!(? iX?B?= #O?脄d?׳ūש?Tî\?h f?bfS̤?xp^[?qk?K->?n?)j\?  ?OPu?%?֦۾?/?D?=D?,!?>r)?o?p3+|?tzh?bX-$?wN?6qB ?CƷ?SaĤ?ȥ?>&?Jp\?\~? K>5,Ϩ?@l?f% U?xX ?feR?ۺ~Q?1d?!=\?o(@^E?˜?F9?sAZ ?Di?(+臩?6 ?a-?qGQ?6oԢ?@ !?ŧ:?&AcE?Le?L wX@?Mƣ?٥ X?Zf(?nne?6#?㪫?y ? ?\'?-l ?릲Fs?;5?VD6ᷫ?\Vu?h-Z?.p?/: *?H?CV*,?cԦ?:~9?8&?ߜ?bL.?BJB?,p`v?Aim?:%oi?+J ?q2"?;Tn?a~ߡ?0?ϩt֝?6NÓ?gT5(_?%XH?Т?N_3F?T8A?_E?5?]V*?& ˶?â?i?fѥ?bi?'BY?U/Ró?OÁ?D-Gգ?5t{? ??tn\?Jyt?q'%+?M*Q?>ˊ?&^a?6Kj-1?;zF?H΄"??Ŋ?r=cР?N!!?sm+Ģ?gx?bץ??ON`X? @?-fogW?wu1?L'?a}?5Ӑ&?ɔ?C?[>-.?b>x?0Ho(?R?ʶc?"n?nx5?-ޤˤ?9L5?Q_??pV p?U?P^2Vƹ?_{>?U;%:?G@+Ȉ?7k?h?`?Cc)?¸j?¸$Q#?Esb?J2?M7H֡?%P_۫?-};$?zwIc?A]U?b?!oN*?@K ?. K?h=]`?8Z6?Ђ?QS?|rҰ?Rz@ߧ?ʊjţ?: :?և(hx?B6?;ф@"?v?*~p ?{y?Dz?A{?+i"K?j 4?>I9?j?˄sNT??pT ?mE8?̨Gd?fTcX??#>?.-O4o?_8]?e7Θ?vڌ?0ĥ4?!l?`mU=?0#Z?ɶؾ?Gg? ?E9?9P+ǣ?3I?f/܋?\Gi?_W_(?!G?xB?+ؼ3?҃D?Á(B?h(j(B?2 ?9??d?r^?ܛO?YGߢ?-G3?02Ǥ?4*`ö1??,^Ue,? s;?I?a9f?nK.?3z?K?2٤?$[Nj?\1ҥ?,Ԥ?/;>??P6O?Sݾ?^Ԣ?{BkLխ?C?ITH? ?*xorf?)N9?y%"?S+?gPk?c5??ĸm?J$~?*_ů?t'y?{g?2n?hEG?Lfn?.lg|?"g? W?*~n*?Gܑ? *BU?d? 6p?Z/shD?uj\+?LТ?)ZE?54.?0s?clV"?4$;?%O? qR?IßO??:Y3 ?0T,(.?(3f8?S}r?i?+b?e*?<n??#?\J??mJG6?t-Q?V+,I?U?" ?+'o4(?JCGx?z 3u?{V3\3?Ȫ??Mv ??~,ڢ?Z0+G?N3 =C?+!S2|? ?T1?(#C?Rn-Df?Ӵ/?¥ ?/p-N??>M'?NZL?*qTV^?n31-p?(|R? ? ʩa?.7~?'Rޱ? &?s?,?OcN?I?6Ρ?< 쮣?8?,?&b?*n@?kW G?6+T? \60?K??ѕ?{ܝ?uS{?c3?T+lt? GL)?BaD4?,? 6=?t`?z$?踣S`? 1?g? /?"D?e??B/W??Tz\?o?~:M?A>~?Y[=?)H=W?T.?j:? 2J??Mޓ%?qc?*Go?l??u?7Z,&?/⥤?&,/ڠ?+˦?} {??z-۠?@*?tԍ?ܢ3?'t֞?[^Ч?f R?+/G?@?VGn? ض ?-7?J?0up?mT??g?%?ìw???X,C?Z¡?=:?d ?Ccm?K$u?)?Zf?)?7V]1?TEA?Rgj?-(h?E1u2?Q.+Z?I6_?͈Ǜ?<퉥?B ߛ? KN; ?^zh?MR[?iߡ?SSJ?/ X?=ޣ?'?IOX闣?+'!?sGg?7>?A?Lb?L?(x恠?gSv?r?Q`?}O?? @?8 ? ܺ?`???R?aʠ?\k?εK[?a{~͟?чT[?-?vG?a?Z_e?jT ?Ģ?[_?s}D?BO? M]!Π?aoo?ĚB;?.+ ?P.~?jv)?}U?9ج(H?˚?#?hgգ?s5-?²nh?g֩ץ?zO-w2?#&֯?,6VxJ?j?CҼ ?M{P?R;Pu^?)7h?D1Ӥ?(>?,{ՠ?ow?)}?3l?:.,>?!Eq?A?.e?Þ/?K4u? TU0 ?Zc,?a`x?jI? b38?drţ?<8?*?V7_?4G]q?*N?-?`m?uUBJ?Zߤ?\% ?#'?z8?5 ~vt?[$?Ue?-E~)?4Ts}?hf??7Jz?$ j?TI? u_Ǣ?;Խr9?{/? `?Kfp?em?^?(\|Kb?I=G?SS?I_M?ɍ4W?Q U?| M?z?6BXހ?r|+?[?;(*#?8? #T)?U^o?^gm?24ţ?K?= ?Ӌ/?a?9r!H?Dj?]м?`f?- ?b?k?@?o1?5|?lhJÜ? If?Rc|,?P?(kDX?i?,י` ?.&pN?d@?D@{?8!?p]{?8#?φM?|J?ܕ?Zg6?^eMl? n?e 9? ?7)?ġ?>E?KѸ?.?:?B/[|?'/͢?A,p?q?jӡ? ?i'?2>ȗ?F? ?{e+?* m@?'? !?wn? ?ʯ?:? ]7? P~I?FDTS?!y$?[?\"!?)~D'?8|\?22n?U6ݡ?_?zw?پG?&JC?#n=??~l⃡?Tz?T0?\?cl?r2S?h$О?0 ?w?b ?x?o?%D̠?ࠠ?%j9?؏wv?S\ĵ?[?EBW]h?p?Ӕ?+U~?}-?B޸?t?$+??+]?EJ?5 Ӡ?K{Ҹ?HnHfqҢ?T*_֥?wm?sʨ?lч?.rL?S{^?]?2?Yq/)?BqP?u?n?r:?>~:?1CPU?u?n;?b{(Vϡ?4Ǚg?t]ޤ?,;?⃼ ?ra?P?<ɔ̩?2֦?vexV?Wj?!Zo?+>? ^i]?a9i?s?7?9ww?E?M+Q~?:?nP`?E/o?H?Mȴ?@E]?:So0?<6_?|=?x?5?uz ?B??DO&à?o ? |S?3FN? kb?b̠?k4 0?uBޣ?GT?nUV?)>N̈́?Sѫ?."P?#P5?y0?yq?bd?xH_?I8,Y?? ֤?rL?vm?5z2u? Mף?qzF?s?ZPV?,w%;??јA1:?qf҆n?bt?ӱ"Y?\?юu?f?Nǣ?YG ?_ȝ?O^~ަ?LJ?: ?˰ V?Zɡ?i:)?gڕ?@_}?_ ?\X?J?? ˮ!?݌ho?*?֥???qqκ?R" o?JV?/tԠ?}Γ?coq?))3SC?@E?P ?D?_מ?S{?cϺ?Mͦ?k^bT?p o?}R>z?6oG?K=mϒ?]_C?*< ?"/?ųH?6T?Cv?iþ,?JC?G{0_?Ԫ!?Ê/r7?0ܕ2?P?H@D%?z2?7jG?0*)?) H?A_ ?M?Po0װ? Ŷ(@?s?#cI?+#G?ӳY6?U?)cGۢ?G:o??8[GB?Dnu4;?DqK@?H$?h%yv?ygO#? 4a?Ȇ6,?lT?+w?`ő(?3 y3?p?,w?hkw?vd?pT|ƿ?)R?$xn? 쓨?aa˴?"c?B?hCZHߡ? ??e ?3q8?+t =?O?3D>?cr?:̾? ?ڨ? -ج=?͊=5?tj?w?2 ?熅_??bT?ZyŬ?bФ?EzIn?p@?QXS?n?e·?M[?*E?t1i?P>*?F'[??Y٭JȤ?Wy.PG5?%T3繱?J(?^#ݡ?Y`J?>?C%߳?l廧?_P?q ?R ?zL#?ڍ?zMZ?L1? 1?`S_?cS?Ex?Ɋ?UQ?=?:]i? ;ϫ?'Q?N',V?bJ?53V? p[?;N?v,O?H3Ds ?-tʢ?4Ht?Di?N2?v?X𦾸?89?L L?:̩?[ʥ?'Ap?܍&?L (?l?nL?ڣKw?fu?.fx?Le]?b-Z=:?D2*?;x?}7V?1RBZ?zk[?׏?#ϐ?R(M?*]#&?$Wj ?-Us鐟?%Z?|oƢ? pC, ?a~G?#4}?]n?Y,[ ?{?=#?7pu?g.?c?T@ˡ?yGӫ?6??1Bl?)?0$?@ ?o ?ܼ6?#?m?Tĉ?ʍn z?{*9w?*?J?;lZr?ks;?]d?U]?Ǯ(?+8?wsY?A76?QZv?ߖ)?b&?8Y??h$Ե?*?8#f?r|ܥ?#~QV?KF)?古;?US?ǢϐĢ?!J?ukT?2C:?O~}d?̀M?YMz߆?JWT?%."?ۑ?7:?ț x?c0=ϟ? 8?T5Xh?CQQ/?̀Ġ? ?N?Gm?M?%(?Ȩ?VS{ˡ?Ÿ??z0\B?y1v?&֞c?H ?Zd?>rM{?]$6?Cq+8?,և?jȱ&E?Q? t?52 ? 6ץ? ]?Po4?߭kL?34Nؠ?L)G?u?e9v?h+ø?o?M5~=? XN/?xG?RyT?#ML?Fբ?ElR?!`?TMS?vR?+F?~!4F?/B? d"lŢ?~?,?bJ?Y?H?eR?k$D~?ŊC?De?n黸nڛ?nć&?Ș?Sn_@֠?PԢ?8=-?w?eF8 `?)r?C^W[%]?>l1,?Id?UhW?%?D7k?Y>zŬ?9Ll??J1?B?I?gK棣?X[ ?GԜΣ?)%|F/?E"H?N ?j$?|?طr?B(+ɞ?"qXơ?MUŢ?_ޣ?CE4a?{=?jϑ4?r3?i..D?_ ?abw?Jl?uΚ?( ?.ؤ?8s?\JF?6?4Ρ??H??i(zK?އA?Q "?!VU?TJЧ?lqb?+,Z?b ?y?:o%?L7֟?ҫ*o?*4k?5?z ?o}eEb?!YXB?}f΢?L?NO?ާנ?9u/Ѥ?B{)X??9H1n?vw;?T#}?a0?!"3?LĒ?mvuP?p^:? `V?uh ?rW8?6/?g֞?RlFX?bMsx?~ۉ?-8w?xg?װž?uY?0*e?Y1UzQ?˵O:?W?<?FFӭ??C{C ?1pU ?ULF?O?ȑ?c?s?C?¢g?,L?y B?"i(޴? ]wӃ?n`?0!l?=5R?2kL?2pΡ? ?ݡ"?r#2ţ?xF?Xh?"v?5|?E-ĥ?&Ϧl? ty??u%?&?/W?ahϳo?*i?|_?x”n?R"ly?m̡D(??BA?sF|n?Z|Wo?t?/?Rޢ?3$]?+o?Ⱡ?TxW?{9ߡ?SA?+tQE?jE?BѦ?Aհ=o?g*0+?d(J^? Vߪ?k__͢?+]f?Ȟ|?p=󐷥?5eg85?^H,?fؽ?)9g?Pt? ͜?_\;?*+/?ӱ ?Ը?.?=?Psx?bzR?K9|F ΢?ʐ&?̠Nv?>ۜ?t)? >PԼ?gb?+ LJ?*b?6?|vuW?=m?G?^?c$_?9~c͠?ȃT? 5^c8?> ~?$g|R?Z*?$DPD ?l2 ?,Tĝ?'9(?s9c?L\MB?9?AI? ƴf?; ?A?bi?[w?1?fa8ɶ=?3@[?`?%WY?z?$BZ8͢?(??'· ?cbE?**fi? %G?W$?d†wc?+CR?Q×?eY`Σ? ``?|L?I4?"<>?;-v?('?;i?g>!F?*C?wddU?byDtd?I{x{?𳘰 ?+?3e?%Df?6#?I?6ʃ?ب?VKΣ?]S? uΡ?L0,~? 8c?(.[?xe?#nDΤ?>66?IYy?qGLL?B܂Rz?17??IJN΢?{:Z?y?ȅp:޲?jٚ{ĵ?8 J?ّ ?P{ر?~6I?8eʁ?"?ͨ?\H2}?FMRq?#'t?M?.?Z?ޤ?a>髡?~Lbv?< /?pc5? n?&S"oH?ӪȖ?ph"?35[*??(@@ڝ?@kߡ?:'[l?Hrh?3ݲ?9?-*?)?k?KD?ּZ?\߲?ƶS?6ge?vZ?z"?uߚ?tD?kok?o8?y\dyҢ?+?&ZD:פ?$Wz;? FU ?^G.?@/Ӣ?&z?[?(?ʹ?l&?lޡ? 6֡?r e?z@/i?is? ?(d?dw?l?$?PT ?ٴ?%h?$;?m@i?WBn?lVg?}W!?&ctNئ?h2?"ޢ?ν? c?Vsi? Fh?Wԓ|?VТ7?׻d$A?Dt_ q.?iUff?$ᘯ?^~ $ɠ?)DDI?K^Q@p?{S1?6A?ۄ ?mW ??j?-?cI}?ߢ`?*5q>۲? Xz??w?h0 ?$?s9`?yRw?!?54?kեBc?hWΰ?vWj*"?l c?N| ?P ڤ? 4?DZ?00? ?9P?4[~?J?D|]B?h?oWG?ȹ?B?a K(?&??U?nM?Tq$i?eo&?Ѥ?Q?ay?u³?XnW?(d}?^m5z?⍮?/qi?T"M?3!?Fda?nCD?X?+i?gyit?QXwʦ??FXI?`cRT?*Ů?Pi"? a6?v?SL?{e?T?`cL? t?d'K?ik|?Pr?5*+-k?te?@#?+<ñ?ݐLL?v9Ւݶ?Xj??c(?}B ?o?w\?1qg?yOM?\9?ZdpU?{?$zC(?Bc%q?Iǎ?|m?AHLww?{Iȷ?X3nf??,6?tL~M?wO!???V{?]{\?ӟX]?`,?& î?g=殮?U2u?RG9?HJ?q]? w?&p.?pG9?%?2*?'|?o{M.?^#x ?? ?!6?=q?H዗u?O:H!?< )?Kc?7hӮ?c?p0>?IĆ ?5X.?*\L? ?2KT?7,fV?m?Nvt?KU?gYq?{ذ?e=:?",PO?p* ?"`\I?0O]?1֤?mHj3?D?I=,???߁x,?TL&:?(?u~?{xh:?_ql?{|9~?C?pZ`?WX©?~uð?3*?;?u$Y?rq?{u?΁K? qn?/ḷK?c?2DO? Г?/0f?Hvڣ?qW?Q\|?㯞^?c. ?Oj?y5Z ?I3?گY`3?(?I˞2?amom?_ԏ?G?yk?jz>?NM?a?f*N?32t? Gj{'?ڶ?GU?>'?.^?6FcF?) L?p0k?t/a ?ޞk?a*. ?E((O?}¥? ~pծ?dOۡ? ML?nz8?L)a*?[T?/?=΃*4?QCz?Ƴ@xH?Zς텭?\q?墎?WY=?#4 ?ɦc ?I}?_{i)#?>t8t?CSע?0] J?_g]?N!\X?bD?{)Z]?~?FM ?1 7?Y-"E?,d?{?n=kT?l?7qI?`4 dS?Z!v?s}?F .z?luG?[?`HG?pr{?/ZX?ʠ?Al?c`y'?ۜ?tS?E{7?J?&XL~?DW`?2N .? ]?K\sC?[e1?պ?ј߸?N:;?oj?32#!?|K%*q?]$?"e C?XOO?4sin?zX?=t,7?I?K8#?v ?rhz`?f(Xv?n_4?nvj@?Z[Y/?S87)?x+?4"n?ElZ|?AT?as[Ρ?*io,V?=6y?~ Kѡ?^7eh?|$v?3 Lj?w3Sb?V[r(?TBKE?"Ӑ(?z/?A>6?X/_ě?45^lr?}`?70 ?ġ?t݆)?I70i ??;ݟF;?;ڏR??J48խ?Նޚ?'1k9٣?/EG6ܠ?8UA?ŽTvˢ?$֥?y#",G?^7?#3?/֛??B; ?!,?P v?._?\Be?U'?z?Fg?:?rN=x?2 ?E_?]?E^נ?9|?L[?&?-!L?@f5}?M ?FEY`Nc?.qcb??9b쉥?9 !?T}1?~ec?Zf e?w?CY?5ձ E?.A?9?#kx?) !u?%; ?T ?$S?i"?攖?a?ɱK?ҔK$?P?ҁR?1#yj?^9?PCdw?,g?2FŦ?3;c?x9?IG?<$MS?1PD?17L?FT?&Ro?n?>?9Iܞ?5?x淜?)?t3?re}&CW?^g;y?pA-?k ?SN[A?[[?D.?*q(k?`@ʩ?YG7??cuו??pL?DK) ?~gd?R;L?5 )ޢ?]?64ڤ?QBH? ? >$kc?U<ȌF?]j?--ġ?&O ?Q:?@ˎv?)Gu>? <ŵ?*D.?)zAe?g޳4m?zu?`R?mX-]u?w?d #o?dA?R)Q??Z0?IS?Iϧ?AM?L;?4H{x?8 M+?塑>?>ek ?I?#F2C??HHY?O Y?.i}?4_?jSN? Sʺ?(W? :?wh?w?D>CL?9?Sޚ?v?8D?_T?6b?SDr ?Wz?R ?khx?B؃[?Xq?Ρp.y?Z??2|;?;F?8]ˉ[??&g?`](t? ;ˮ?xXء?([0c?]1%?Iug?9?fT!n?;Ủ?CMx??v&`?2?W?* ?Jk?n??_?c';E?Zd?m??(l6??ǯF?D?EW?y0Q?=^bƩ?җ_?m}?!w+:?h ?RtФ?L)? ٿ?}*ġ?xca?".?}}z?]*c?*Κ? \š?.bRׄ?݌T'?yMpϸ?i] ?8L?{llԗ?ހ4j?`qۥz??Qw? ?6??xS&?؜?΀[{?ny?Y~?+5?X[ǐ ?iGx??+"??H j?jYb ͥ?eUCj}(?Ј42?_vV?!8i??b5?k2{c`_?;Ҡ?M&̢?s[[&?O ?nb?PX?ݝ?]E\!??c@?P?u3?u"n?աG{?rz˻?i?2z0?m$??CWh2?K9?jm?3Oy?0'k"q?ySZ>?MI|ٚ??혢??~?ﷲȦ?bJm? N?_?w?EOã?+_?l*c?S>?C\,?ei?6Be?u/8?Uy*:?ؓ?,B+q?׈&?+ ע?,Bd?vkH?+p??ʐ?$w?q-|+?ֱO.C?`aIf?Ay?Gq?^DꝞ?q(J?j9T?䑐 ? P@?6D?R(?+?+?ʸ?$ͣ?2*߇?c? 2-8΢?sZ?уӤ?Ir?%5?*n?v?V.?n^\?CGF2?NΣ?JҪ?iM? 0p?K?])?H?)eԚ?F~'?W9fve? ʨ? +]?qyA?jgp?j޾?z/~?:5$?F?kZM?`j?Kς<(?Ʌ+=D?VkT?*㟮@u??-Q6?Z |?mㄥ?^h?^0?jߣ f?=~P?t>c?;h?W?W)ln?C ٛ?vX QA? N63?BR? v+?yZ:?}1?.Ѕ3?c|ꝡ?m%&'?濜?N8*?HK e? ?o|+?DTL?{T?Zؠ??MqƤ?hg??V,?a*zg??5m7?Cbil?Q aͤ?ۿ)?j?ż?+fn?uC?1;?Drޢ?MdvV?FH/?W8AOä?Jhw?ءAͦ? iޡ?'iġ?%\D?;/ף?R ?]a>NC?w^?1g}?_w@?syZϠ?רه?E tX?<<U{?@֪Z?̒?oآ?b8Z5?I?ҏ[싟?kdf?[y$?3m?XK?’?2?pL&?wFT?>1]ޞ?nԙ??ߠ?ƝZ&?/"f?~?1L?3s?>KRg?mY돡?G?NXM?o ӥ?m??3/?MI O? jW ?8"d?`)b?qU?J?3&-?(,?@a֟B?T7?6i?E~>?}?A)Mܢ?(Bx?q<Nĝ?3'3?rpgk?}TǠ?k&؝?ٓ6?\Lv?fj-? *?`?zi M?vXw?az?Ep?9?޿wq ?zW4?й\٦?5?tL?SKڝ? S?C331?Qߜ?G>?I Gm?PM:Ra?Jt,Ġ?2 ?:Kӣ?u?LX,?yD? nȢ?닯?"A|?Օl?՘? C?og?JM?[{"?w*_%7?:?&-6?[ԃ?7Ɠ?PZ?Xa? ȩ ?!r?%t6zM?hMn@?(=(&?vM-?p0?f?׹?K"?bRܫ?wʠ?ms?Sf?w?nQS0?6?d=?s3'D?CLj'?Z?< 4?C?ڵ9?Cc!?D'b?ػ\?Jb?ŠzZ?lm`8?˸!?D~?Ѱ?ƪ1?XXm?;Im֥?_?E?5V0?9 ӣ?%d;?9Yx6٢?l8R?C?K{;Ű8?L+?dHDĜ? e}x5?l#?E(|'?*?-<]?) E?v`Mo?򙟘?y=??;Z?M?b鯬?Mz"o?~!1?OK?=2?dq?~_CY? K?s?,60ͪ?N_Dǩ?-%N? zUӡ?e?\}?ݰ<?O*?cʢ?pͱ*{? Eo?M?rP ϴT?ȯQƀ?XU,?/??z2o?gȐޥ?pW6?G.~g?gQ0&?=]]?+q {?g2??"Xhu?a͟?E3K?t?Ps?6]-?'?.?{ל?bC?2֢?\d??B'?3X??ޔ/9? ꩑?&y ?t???&? ?Ef?+?/t$?-*Z+^??m`Zy?Ӡ\?,f9,?Ų?3ʑȩ?(#}+_?Ɩ('?h=ox^?7\?O1:?я?'?fTs?ɴĈ?6u/b?m}T?'Ѥ?9ǥ?j?}\F`?l?oϤ?K?c$?DB?..(IR?Q:O&n?)c?gb?{MP,?jUM'I?U?STI?+mwS?wrU?6_Z?"D*?" ?=?t? |@?=@e?'}w?ZV5? L5?i(?[:?H6?셿?'0?g14L?qt٫?:?TR?h}?yƶX?P?7>ݡ?W?i#? ܥ?w}?#[?'7þ?qՈ?a??Y9?HёA?h?Z:.P@?Dvu?C?(?W_pt?Q=?hh?4npP ?[r?G??y+?Ծ|?kZ?5.?M@ ??:?/??kt?N?>1?ʅ\3?oei%?t>!?\[-?4%? ئ?;y?;uc??v?aiM?Ң40?m?7N`?W٥]?`۫0? S(?dF(Q?'Eu?m:?H`>?z&?x܇?Pw1?մ7?j?C$H?.m?G^!H?]IV?>纭??d#`?fݣ?|BWb9?d)?dVu$?֣?"9T}?v? ?h7S?XC_? ?U?O?01q6?9v&?ۂk2?)1Q??H ,?VA?h j$?Ԕ+?C?Pyr0?r.H?P?5?”??">Y.?$:?H?VG %ߠ?~Ѣ?*`?3ޝ?hf?9z?~T&.ͱ?z'?X _?}l ?i3Ѱ?![? ]3?s(?` ?]X+?%; ?~5}?L?ɸu*?r-\m@?B=h?)1+?ڱK7?m?mS*?!E ?jHI%?;KϞ?Jr?)F8֯?eOd?XXD?e??kנ?k^c?W&W?ibc󬏹?BsPn?ċZ!?ƒǼ$?NP6?eb(?P*%?QO$?}`Bub?$aAy?]ǵ?f?޷ۥ?X?vj?~oܞ?=?Y"a}?YJ??t7R?`?1t?B?=!S&?̄?`'\?_.|%͡??Dqg? 9?GNv?5֞?SҜeJ? %?]=h?4T'Щ?QjB?ħ?Ψ }?K)#?Ё0AӰ? Q?v?+֫?'I?|1͈?ξ?[p@?Ǘ]E!?b?_bS#?>4H?Ajs?6 z?xD 0?侑c'?P+ϨyϪ??5I?ڇ-8y?*=?^kjs??J ?Ox?c ?'?~9?$1̊?vEd? 2*v?6u?7;?;/V1?G|֦?cbX|?m|?KO?%fU?[O!?WjXM4?oWk"I?rqc22?kv?΁?:f$-W??n)av?(D?2RT?WY4o?͕?s?yK?" %d¤?ݮ?޳,$(/?+Jiu?RQe\?q*?l<>?? ̟?ޛdC??fX?׿?3K?gf?}y[:?5h?h ?ޭQj?dDԢ?[l?t{e[?W?k?c?_F?\0!?0d?w$ +?jk?6n?1)0?I[ZĤ?5?8?miF?`?G 2U?4Cp4?g?ic?I?R$?+7L?j?A?z: ?#A?ն?R?7 ?˫8?Q?8N?Mh?~"Xe?sN?$??'x?*:!?DK9? L{Q9?e_?"~?uDK??I?jbן?Wzd?11P?")ۄ?,x'?7HE?2l?۟?QnrΪ?]?Pk*w?ck?Fh;?KiM?6Զs?R[q?u? KT?sNI?zš@?nv?OpȤ?IcM?*r?\_?4Y?-?S)?}y{?G!z?eCϤ?D&$?fԝ?+i?;罷?W8#?] Uʠ?!cOl?~Z?98??0u5l?k?1e?>`x?^llK? ,6+??8bS?1ʑ?X%,?QJ?5Mq?- ?5>J?n\!?"a\a?w„.?R:V? ?ˤ@x?3M?~?\á?KcK?BJ?pO?[m?y?M=?J(6?D_?TXە(? 7L?I`3㜷?fVE>? ?)T ?`E[WM?{d{?|?0?6J?WSxZ?"NΝ?o?;oV?Ӻ@X?kH"?i??\?im&j ?Tf?O ?<т?`؏7v?Qc.?D+ʠ?˹?_?Ԃ'?(l?&ܥ?1~?;9M!?vB~?|t?d% ?? ,?CوY?°?.56Tu??7;g??oX%?[ ן?٠Y3\?jrA?ƈ?M?ؒ?l7?&c?op8?·?b?^aL?=w?cHТ?_`;?Op8?> p^??~ z?"ְ?9i:?f?M!TR?[?+{_P?4YwF?*?~r(?S`?%{=?d+?ZƎ?r誻?s{!?GS?:C?F~?/8`?F൓?CW?3 h@?|uNUl?INx?髺?VY$?V5? n?YdVT?5لճ? 8Rۤ?X͊?񚓤i?BGK?nA‰?ڢ?9ҝ,?ʜ?0ub?͠?zi?܄?oTJL?P$?&h?9ȡr?Y  ?;C@?YZ ?lC?=f?/B?uB& ? $"?^|?v.6?S`XT?L\)?&H?)L?| @?ʳ#4 ?D? `?-?B?q? x?(/H?]?y/$?gV? ?uN?L,?,񝝦?ġ?SE?P,"?yPP?"bX?/?Y*:?lV^;?L2?ES!ͫ?5twP4?yKޗ? I?hqK9a?ȐК?.#cʦ?Pz?FC?J?W:g?)q?f"J?# ͣ?Crm?9\ۅ?A^@?`=k?Da"?'}V4?M )D??&uq?]o5?h?SpԐ??qjd?sO} K?5 ?s?Lsr?ڠM?b?U$?Y비?kק?Qobif?ev?Nj?BRD?k?Sz?1v?<ߣ? ?aꏛ?LIT|?j=? ])?=nġ?~{X? j"?.~ziF?,.^?!"?]!NB?+?sS>"?APZ?MnYߣ?l?ox=?6?sJ?)ݶP?J~?-D]8c?j/]?yRa?~{ O?'?=A\P?/Od!J?+.M?zP?_E'?hi?P'Ԡ?H~?ne?Mė?=Ţ?(3?JN?{&ҥ?k7?jm<? }[ՠ?J]?>,?4 پ?T߭?<|?^rH?dӡgM?L,T)?V??Ju?f HL?ט;?291?Pܠ?}7$? -l?P?K)?`~|?sc?o`Fh?Gd.??,q? C?{\n?8(e^ç?bZ?e8AS?4?`FN|?T,?? -ޜ?sR,? ,!?D>?F2?k)e?x K?DN?.e?|:)?Brk?DВX?xS gb? ?MJ=?o?E}?V{t?E:q?p~*?]($^?10CI?tVc9ޠ?H[>? dt?Jѡ?X>QZ? H}ף?_$?XS?~~[?831I̝?QɥC?9H?+ У?p-ۍ?_p'?Πנ?J???ذ9œ?D񶞢?zc?uMJzh?Κ-{?EՐ?)ٻzݡ?luQқ?y k?F9J+ɤ?^?6q?AN?~t?vs?qƳ ?Q,/?Ϥ/'<Ӣ?, o?oׂš?v ?t},?  ʲ?}N ?q0q'?2v4=?? ?M̱y?a((?^鐠?P?yXv(?f@?şy?q?i))?3X?Z֦?R\?wpB? ๿??9?a?A-H?ν?-0?L?4?O/Ƣ?S_?(D?`&? ?wB@?gp?? O?;͸T?-e5Y?e ?pΦ?ۤ?>q?gD8?7MuC?O?x?g|>?XapN?#[t?(N?Ng?4f ?x?'??Yӯ?v\8/?+yD?ݲ/wI?%P?yJ/? |cť?U/Iص?-=Q^?nr&?E=qx?#cAP?~J?j?l?Pq?vc? q?vت٠?b![?MP?nw<5?6>?*^8?yx|?1Y&p?W]??-w?@ѫ?ewU?uѡ?wHd2?D8c?,i?.Q?c˽?]4Ѫ?ZX?&?v_foa ?@l?*?G`?gQ.?^+?e?H5([?TP)?'xr 0?`?6 G`?X緇?fZ:5?Te?Yٳ(?+>E?XڹS?~ƒ]? ?(#۾?22"i?,Q?yqA?U?@P W? ?=?Xg(p?S?|-ͺY?_SPA?1K?<8 ??[k?/퓓? n?Ll?Tli?z܌??Y{M?{\5b? 47u?4W4?A6?&(!(?e`?R/S?rP?|)f?XH??w*I?ȃ9? y?ӝ?"?5#?*["?y{lr\?E_?D1g?׮?`/?$A,E?~%B `y?f?k1L?O&n ?R?A?ԨG?]ֈ?5N0?f?_Di+?k@q?ؗyM?2.A2?[|Ub??L?ղP!?QM?"j^?Ǡw?>X`?/ZfI?lM/?A?Ysr?%m?P(U?m?>!*nh?֡`?~t?koXF?c;ũv?Ư3?$Ǥ?5}? _6+ J?wc?T>}Fb?)54? ؐ,g?)>4˰?qb? ?>}Zћ?G`u?1> ??dVuY`?]雃?Uz3?i?Z>?ۚ?"y,?A4?Y^9b7??ZT`?4'~?`}?M;? ?] ٲ?: ?7?r:f?Xh~v?jK>?Wni?#Vd?{<"? M-~?`Y??;2?j z?&\?B,}?|>ظ?﹉??nfȪ?Viw˩?R[ ?s~Lc?8zA?I0N+?)h?;P ?5V^?W?,:+w?jb? ~?$_?3iGt?^攨?Ca.?-lG?_֪?7 Fe?ǞYҹ? K%?$,?N?`?E ?n4?Ͽ_M@?MJ坳?'<⿽?jSYn?ÔЧ? ?aܻ?F?Tjt?:{ǧi?5 k? ?'>X@R?:`-3?[ 9?y?" e? ӹ? ?g?Fvж?~iFV??beh?*HM?)Z?`?{?nPU??M7G: ?"AI?.?;/Y?N#9?[#lhؙ?Ƙ??5q?slhA?%"f?6/8+'?։6 ?PRL?|MqL?B?똤? 9? އ?JE?Fc?zR?!?[z1?_5R?h¡?Z?c ٩?]`?[(?HF^fI)?=n6?iQ??Uz?IH?R"m?} Ab?"n?\?s4; ?ח?qQ?V2a?XRՠ?qK ?Iv+R}?+wq?l?y?t ?p I? f7 ?[]a?@GV? u("??ar9LԀC?G?PNTi?7?lHqJ?ux?Xn?['? ܙ?_»?Pk? rc?5?$!=?%I?*D?^kY?tqmw?kEg\?btwt?99}?@?u)C?zP?5ea?' =?T2f?5?&Q?o??rbF#W?u!(=?2rC>|?)窨?ݟ?&F?vf??㌲?Y-?!]?߾$?6?v4|?bN?g]?6å?5r e?:^e)?!pS?(S?oqb? ?$R۹?/~S?Uqo6?Γ?tsɿ?؛?ު?^_???ž?=f:k?o t*??@AǢ?6ڤ?䟗_?>DBq?^?dԢ?y?Y? o!_?qY?6W?u?Y?~?:J?t?f?{?|X?k8?H`F?p2M?wMY?!%5pj?bL?A}p?˭r?l4?>43?UE?rx;\t&?#??jA:ؠ?V{?rTn:%?#r?/wọ?;&e?Mg? ?Ōpk?e?k]?:?@^?J=|d? ;?G6%?XU'?䚽Iģ?eEϟ?:?3w{?Ը? dwF?î}a١?b!oۡ?ީv?A+?Qf?&1<?W?z8?1=?'H? 0&??ycE ?J=?9Pե?:iK?@7?h3Z?]T?)XB?SZ?nᆣ?bĐ?A?1嵱l?^id? l?`L?R ?{?ǞN?`K;?);Ӕ?1A?4U[Ӣ?c04?&0o?0ozD\?F? (H??ݹCͥ?Ot?RRџ?((?~/?m?|^01a?ƭK?$'Mk?%&g??w?zh冡?HS?D\{٩?d?~-!؟?6)1? #5ۤ?^YP??a> ?UL[X?E?m^;?"mvm?#DHŸ?Ԕr*?x[?lǫ?i**p?bҡ?~E? ?^`?(#?P:T?r&?Tކ?!x+?[]v!?ZLU? Aq7?R|P?߅!?}*3n?64?h`9?[I'f?侼#H??&+y?!<}?'B;cN?H֔\?_EX(Sl?(nУ?? :2?'?lYb>?n?e(Fġ?,Fx2?Z?c=?qROQ? Ί-y??ӱp?Rԫ?U$?[?Hh?5^ɝ?~6g.e?ow=j6?G۷?;n+O?zJ>??[Ƭj?2ޙ_?h+@0\?V$?ZqD?8$?hޥ?$?k?? ڡ?2?a ? x?1pDɶ?m̐Z?N_[֠?*?>?L4k7?hIuC?V4:?~w ˣ?pw?!?:H ?Ss~?YTKs?E ܞ?v~U?Mס?<_/?Gr?8}S?]TvB?3 ?6`Lޏ? υ?Pbҥ?zN%ݘ?iЗ֡?LŞ?(QS?Y)?Kr:?xL?*JV?:\&Ć?:|'ѣ?7SztW?%gH? k?>2O@?y?f?X|Ѣ?;=? t!?:9V?'M->?L,`?1?ۇP?8炯?H+Il?c?=z?9?&?耟?Kdr ?ES,?i3X?5ܠ?qM?䆂?w&?]t?&n??!Q?wn?mߓ w?U!k?lڛ?Kl?۞x?S-?R>ӟ?[,A=????1n\t?Dq)?h ?89|?0(?G?sƘ?"p?J?=8+ٖ?g'+?qn? D?Of>?k`?*? ?cX?P+? V?qQI ?Z癵?NҪ?dR?3s?jO"??D|?cN?7̡?&4Yޞ?3e?Z?hcB咚?!5&B?Eˤ?.gw? ,"??#)vU?]x?7cmn ?$?OVn(?$?@5?~?˝/l?z ?, G_?E{ӷw???6 ?:2?;m?+ it ?˙H?hp?bq?T㟡??JV^?v 9Nę?ʦԇ?KY?4̩?V}%Ţ?K0˧?QF??@U?Zɩ=? ?*ܜb?w v?yئ?'>?$^?jN??w<?׀?BKjol?0/?@3"L?[&?2o6Z?(W?y?wc>?CHۢ?癙ٝ?ZBC? ?8Л?c[dk?v-?M_\?zyw? :z?b7;?6фOg?5ךjP?©?^?/?NDp?eor?g/???z? Κ??֒M?G?+.qy?)YHв?eBK?IѬ?uL?֐8?ީW^?Av?,a?",?Nƛ?nI?^t4?K4T/? ]}m?c?7p?Dt?ķ]:?7Q?sw2C?Pz5?D{?|T㉢?yx蚟?l+p??:|$p?^a=G?4?U9C?Dt/ ?s0C?}UjY?n'`L?piMd? eF?uef`?;1?ЍrI?FM\^ ?!%n ??\ׄ?ݔ?9(o?^C?P?A?6 u??5ho?g?aDT?.a??Х? ?lf]Vأ?s^Bk? }#C?n}Xu?ʳBV?&{ P1T?#͈?tl.Os?L1*Hj?w?+6?xC$?]xR4?)ң?$E?2=\?*s ϩ?/819?=i٫?x[?G!?Z?}Y?RlD ? sI?7/|h?Q[_?jr:P?=m?#+?(:,Bݶ??k;i?7N]?@??~Dw??N+s?ْ?5:?<p?ߟzR?ԛ?27}??Ǥj?7xB[?h_F?a-? 条?v.ټ?D[ޣ?"\?sw \?Wj?0$? d'?U0?Z^Ղ?9?7, ?SQ:?aL?i2{f?2ү/?,Ъh?>uN?Ys}?c?/Z8?ѸVQ)??d? XwP?1??bS"?#?Oժ?;?)Ρ?p.[f?oL?Ŏ޲ؠ? Ï^U?!eK3?l3e?4B??nStƷ?.7aI?Jv^?(ԇM??Y?ݸo?pT?W.?a?叢?ܠ?i ?v}? ? 3a? jw?lw} ?Oj ?id?p?%Re?g?$ "?X"d?i 6?Q$?Zl8.? H>ҹ?oʌ%?vTy 9?ěY?}[a?q4\ѩ?@&k?쥴V?xS?4q?z?A?/Ĵ?J\?]f w?Yi" ?c`?n㏜]?h $?.QuW?9{?$=?dNչ.^?xpBॡ?]3oD=?2O5?UCsu?H*'?F_?S<\?&d:E?Ñ;?ao?~?쨡?d ˠ?eq?$}?T8?&@?S&q;/?$??U?}6Rd%??3v?JeP?T}?޵S}?B?Fjd泥?m9*?=ҁ?a?Ѳ?@3?*JEm?F9?EzB.? !sq?k;P?v"_? z?"dm?<tSn?f߬?x?eDZ? R,?gG79?N IY;?u?A? w}?j>6??|[?Ag?0Pn?Ӣ?k6?^ݝC?gk?@R??Ӻ-?!YF?T ?\.P?nF`?-?(Q9?u ?v? κ ?`?uHNH?EWR{?՞܆?󲵩w?`$9?t&?73LR?2 [?Y顦?@ZX?ӜW7?t?f?Z!U8q?q?k̬?@0?;=?1ܟ?AP?3 ?g6?xֳ?a?em?3;?Q\⤐?3M7?bvb? P*a? 70 A?EC|M?5J?Rcɫ?7?|oJ? {?`zv?\?+?T̵֭?~Y? Sw?#&a\? H`c?m? f?^|Ѧ?3yKv?tW E?@U ?Y ?$i ?GP͗?EB>?wߔ?ٗq?]-?d22?$??9gx?) pC?_ը?`)?Up.S ?^rݢ?9\Q n?jma?@}!?SƸcl?lܬ?JV?ד݀??fb?<?w]d?C{?MbЉ?vx?~E}j?,¤?{?%kǢ?@!?Z8c?,?H? ?v??8lD?β?)x.0?hSBO?ʍ?  ה?`%?{,i;?فK? 7!?"PW?IOp?)2?fA?"?^s?ؒ?WM`*?q/?l=?rã2N?"V?yZ쬛?玎?*9?!8{l?E5^? (ݓ?H;?ߤ?"7?޾?ҭ%?v?b(B|?6?KT#?c^?} p?Q?vȞ޺?7+oG?Ͼ.???T08?pwo?0UJ?;?Y^K?a[?4D?lҞ?? x7Yؘ? ??ij_ޞ?1O^?(Ҕ?@D?*xD֠?IA(S?͑&r ?S:?o?g?)PJr?͑3?挖,?nZY?֟?@,B?:Da?b?)? :?a3$?ƞTG?L ?7SEپ?6>?Y?6?KhU4oK? gF?F-?,?$/I?QP|?߼[/?\? xC>8??Vv?Yۙ?IpՃW?1 ] ? {? ?oهƂ?m ?7P?nR?Ӽq?6?򜷵Th?& X?2>u?>)?z&#?N=?WNߣ?Ey?%0g?7Uo˯? ?Ч=t?&?7B۟?? ?H*? 2n ?AQ?T~3&?yٓ?]?hu ~tڦ?A?..k٢,?v՗?M?چng?DĄ`?Sy"a?3 ?$>Ƣ?OB?;Ŧ쬣? _?%?'r?ء??pġ?ʩ7?D?AS1?|Y|?0ᴧ?i?/? â? >|?JPt?۽n?2R? x5?d0eh?Ɉ?Zƚ?BqS?xE?? -ر-?]n0?cB?1.8?&O=ۤ?y?0?KŸ?E3dd/?Vs?|G?i?JL.?_lxà?FqDS?lJ?:??*dl?BEA5Xh?hAy;?Pš?m?킣?!j?̀a{ƻ?8Ԗ ?Q@@?rWL?HB[?V?_Z}?ތMY?9wX?^F?r?à?.ע?įs?:?2 'ģ?,$?x}B(?&5o?Ah1?Lwi?_RR+?dЁ!?j?׳PY"?'$`?S?$L}Yx?Hi?)']?:=?NRD?"5V>?Qڶ?rDכ?f{s钠?bʤ?>}?YPv?>zA?cIʣ?!])b?mmd?ޥT?.?i&fBƤ?B+$c?[Ff?ݪR? I\?8Ѷ?S$|?ɣ'͞?^C=E?kOٔ ?|+?&zZ;? /?7_ ?U`Z?%c#?]Wף?sN?ww@?pn?8v(ޘ?K`4Y?5ׁ?ZoМ?} ›P?,?&(ܞ?":?0YѠ?~Z]?&>?& Xg?] Kt?V>? 8Q:?@;F??}۟G?I|?4?fe?0? ^0"?\ז?4M?;S? )4?NP%?Zp?c?Xt3?k? Τ?<F?$D2m?u`? qh?zSt?k2?mCh?>UKK?HM?գ?NSe?2CDL?~E>?$瀋?M?h?JfՕ3?3Ԩ?u?!?-R?d{p¡?jIWw?v?I2?KvV^?hJye?Et?#K??=Ů?k4?E?P釠?]_ȝ?Zb'?&y6?]vX?t7xq?}n.'?''ˡ?IUi?ɳ?qj^?E8Ф? <8 ?.w&]A?~Bmۢ?ܠ?iƢț?H󙽙%?ivhE?۽?)qb?ڬ?n?WZ?9?4ةG?? VBڡ?7hA_o?L'?(Z)?Lf?^-MU?bqm?B ?11?٨]{#?ތh?f?2 dL?E ?dNLn?1r?tx`?)ц?VdF+?9?^;q5w?f(_?NکX??&6?RX?T=D!?,Mѭ?ӤI+?'M%{??F~LP? LY?`ܣ?AA?jB?vi8]3?e5f?<?"&?%3?F&wR?\M?s?3P+f?☎" ?+?P=8?[]?=؜?w?wN6?(N?73`P?%^?Ʃ?l`D=Z?)1@?;Z)_?@jpbP?MSR?SL? .?E??M!?WN-f?*}m?KoJ?Uuz?.B֢??ѣT?}?ar?4>??7,ީ?vO?Div?t? zǣ?m=Cu?K? `h`r?wV?P΅_?7Okp?:cx/?E?k\?1ƈ ?ɦ? ɡ? 5aE?O]?nA{ܫ?XPH{?9c?58kzϜ? l]?ż@B??6X?+u׎P?ڶ??97lf?Mx ?Nֵ? {I?z!H?Sfj?Š?o "?Rv(?^bM?\q?򡐗?a>Wf??jP??QŸ?(k?AU?qI"?!lv?O% (K? W,1?[?@Le~?`?? ?j??Pc?I?lQ?[߿̱?Vc/%?x??Ob?D6?6?0H?](?d&?Hm%W-?]l?J_?)̖# ?*Bs?Wp?]0?У/?[=$aO?Dz-?JC?yԄ?~wz?Îţ?@-9?#>h?ͬnI?h-Xܴ? ľ?̤P??ð ~ ?V ?vX%?Q? _,5 ?:8:?u;?7m+?ئ+ ?'?{"?>Rk?C;?8#?q?L?*hxj??81ap?g6Jϡ?ư\?j"F^?&%??V?_%I?3ɉI?@?"&==?ގ 7,?߸8]?ab/>J?l0r?(WP}?^ea? ??,jVF?2kfJQ? ja?BrT?'H铆?-:t?`&?jG? 6^?U\?Z,?aGg?{?iʹ?8I?ž?MQJ'?]!?6 ?P (?J2? ^0H?RfpbH?$X?wXq?먫!?PM+?"?5`-?{63?qJNF?l.?i$?>]?~u?_#N?XTC?{Q))-?9?S+W?R$Gp?1T?{:?}?8o/4?.4%?AI?Y#?7w?ũ8 ?'>E٣?9?R?φ*L?A?W E?)7^?ޤS(?QK{?-^ h?TA?Z$? Gh?c׉]?R?b̪?t#g?ǫTD? ^C?sF{S?R٫?R.X?`Z ?L?ՔZn??qJA%~?A?H!u`?T?xϤJ? ? 粡?}fֱ? {er?tp?%?*l4?\u*#?>P?0F?W;9?_?)~: ?Ex? *i?Y?n ?x D?qX?(!8߹?r)^&??Go"?+'|?9M}[?BE?a)?0+P@%?3{?3q g?|xwn?ѧ҄,??C?ž O9͡?x?3xj(O?k,N@I?Կ\ɬ?i?N):?cQ>?)Bx?]Ϸ?6?z]\?*[C?~^w֤?x ?8;?}\S?OI?x7?Yۑ!?ķG7?+[?!?}#Y?R|?d7]F?x?i+?Sl?mB@+?C{?{ρ?$ K?B??)-{?WgWۯ??=),?ID{/?Jz-?#?1&U?޻?Přg?l{?l%L?d[DL?ܪl?d?qkJz?jQVz?ٸ=\?6 J0?KƆc?Z?XN?:6ܨ?K?>? ??8F?'?2Y43?! -?9Y^QƢ?^l?].3W?n ?T*i.?kB?#B?]tn)s?8OGq?ρ?Yǟ? ? A}?\|ϣ?;1I?|Y$@B? O>B?j̿˛=?15?Ba5埤?ZK*?)9P? Rt"?$6F#?h?ǬC?,?I?i+?o-,?/@#?(FqM?Lƛj?{Erh??_G?j?B =?PWD?Y?McZi?:,??و?MQc9?W?|Pŧ? !M ?`s?8?Ym@?d!?'?`?R&L"?vHJF?4?ɔd?ֻL?o!2?Px?d8'? Hg?:`~?<ޠ?LWv?J}9Υ?QJ7? ? q ?6VI?f ?ޢt?.?Mi啠J?t~;/ P?AC_?CH4X ?Vu;?vFsRE?qa?t&J?'c_Ld=?_}?.^ g? kk?Q?m;F?FR?P?j1?Tr"(?}bZ6?h|?YbDݠ? ?6s? x?~tѠ?2K*?FBJ?lw;?3ȅ)??G(?+w?-z\~D?8z&?al ?KW?J?k0m?ڴ;?hנ?gK?Fs?7o?ha>GL?Nj~Ѱ?l`?hdH8@?e6#?캶z?S=?4g?sQ?&_]?r}j¡?њ?y?jSP?m??1Y^?q4[t%?H6;?tVy?M?1g?iP?JXB?jw/?d96%?mZ>|?3X?Ģ"4?*?.{֠?!AR?p?|G?f^?'ˣ?F Bߥ?6v?#4?8V?xT?A&?mk#?U?iz!?1?s#&%]?P]?p\Y=?P/#Ʀ?H\?nz S?[--?g[VM?g??TXRg?Z98P?gZ!0f?\V?G?C.?n?e滠?IL?C8X0?_;V? ۬Ď?NJV?|n9t?h2?ZZ?7eR??R?^dگ?^6V?V?Q?Yd3̣?ȎSS?[x>,W?#LNm ? ?ۘ&g?w%:9?{|DIs?{<8C?JaJ?$2?r7 ~?PM? FB#?˓Q?Od?~B~}?&T?*?>5?s_ \?-[Y ?ҪT?ͮߖ?3*H?ì8z?9I?= ?_٢?pIJ뤢?;ˤ?t* '?GG?+.kB?mݭ?d7j??dq?0W? 8?Cg4?=~?6c?H4%z?ˆ33?PwY?ЯdP?ZX?FF(?Vwk?S?8?SM ?&bU?.-Ԋ?Oo,??UX+?%p?l ?/80?xf9 ?ĸ4Da?W?ZS?Zx{d?`??b?ԅo?B0kD?u/?&o?h?%#hF?_oBq?Zzk?)ߨ?yTX5??vS!ΰ?$ѻ??~E?J&?P-cm?9?kNe?<H,?HCؤ?lP'?2T¡?Ѣ?iU5?d g8m?)t ?V ?*@.i?[MO?eɞϪ?6%$?Q??R;1?s?`,? & ?Nm?ۜ?Ռ&?` (?%G?o [?iYO?Cw5?p?dR?( y/?7?)7۽?V\W?t*?,<6z?-aDi??cՂ?l?!Wݠ?r,WM?Q.yi?ta?^7?P?'n?OܘQ?C]o?>?\Q??=7?| 4d?N?_Љ?es}C??˳k?S!}T?; ?AE?F~?XS?˼? ?WFf?}!?oΛ?9=J?_?}A?{ϮWB?vD'?1π\?:4^.v?dE8PΞ?H?.`Zі?M?Z)?š ?x}L?7ξ ?H52?%?76j?9P5?I=?uk~gd?? ?.N܇?qY?0?KJZ?K;,?߿|P?;;u?>4E?tYc?DY?ٺo?իg?>֝?I~?)=?R* ?3.?0Ͱ?3| Ϛ?ٟ? İ?W`̲?Oh~? %?\sO?+=?8`?y1+?0?!?? m?`*j?#J?H|N?d1ۦ?KL?rT??2wƊ?` [ݣ? n?A?JT ?waĞ?/$?ZS5ѡ?rQh?H+ݢ?Usm2,i?0cm?uΞ?9O"?]?lJ?܃wM?뢄2Ɂ?-"|V?V3˜Y?O"?sΗ0?:?}fjÒi?˃?>ʴ? ?^ގ?*?[*??k?I?Ws=F?š[j?) / *?L/|?p*>?Ep 6t?bv?e?IƎf??q%ˤ?61j?KW8?Lm ?wNˣ? ~?f?53æ?d+x?.ӽv?l]?E%j?>?3ʩ?Ty?q@*?Em0?sE0;?ޔ).?e?8I?&a? ??m>̣?*pa?xEQ? #K?ud(*X?d p?~\Z?Eh}?Kޢ?qj?OT:"?Ο؅?sIm¡?4RY)?'sϣ?UYm?!2?~=ì?#U?nu^?ƙ?f|?2?A}?RRh?˴ݣ?bХ[Z?$߫%o?k?Uze?NĞ? Sɡ?Jþ?:YP?N?js=?;?ec3?e8?u?F?]ݒ?PB_yV?- B?g".!?+*?ޤ?$)ԣ?)KL͗?f#?hU?Bgf?S\(=6?kI-]?pqR ?BA?R}r?O4?i~?MS_,?k?dg?5?^iמ?0C0?!.r?gJ?0?Б?.PF֣?q?/B?,$ٴ?w}٬C?FN?~V?ԊG?&ih+ ?B'D?ۮF?sP?j>c?x?/8?`w?u`B? +b.?}i>?jXΥ?5(?CF7?>^?j/m?R*˥?)-?RU?pp?Nr:?Ls?l?P$?ؚ?ґcġ?~yڹ?כ̣?h?RVX?.!bޠ?գd< W?Y?h7}{a?gw̢?`?kƚB,??˲\x?Q8B?K?gS#?!tN9?eU?D2=?]@{(?X&O?Th?#B?Ѕhmk?T.Sn!?|?ں(?H~&?m۽y? ?e ?J^?gy?í?p?r~:?o D?slV?լ:?Þ?2? L=æ?lmz+???F"lҠ?b?j8?m;?dZ2X??")P?ް?`q* ?syLIW?p-?k2ex?72萢?ʣ o?Wr0? "Q?@c`a?z'?뢤{ޣ?26'ܡ?gN9xTע? d?"J?EXe!?Pwԟ?poX|0?B~?֜"?c|?Aƶ?/~?ć*a?Ϊ1 w?3W?v=Ж?f]5?\؀?{ 0?79w?qP{41?JP?9@pZ?L= f0?^7??v*?0?y/ s? u?pxo?Sߦ>ˣ?#riD?L1ͱp?bP}?yB@?k(yo?OH\?w]_L?{e?;K?`ZMf?8"O? ??ej?0g7y?rٰ?{q5E?#dw0?+)la?~y?HL?k2?zͣ?d ?_̻((?LUO?>9?DWَ?'-9P?̒e?Z d?z%կ?Me?=Ǵ_?W~ ?)v?6t??R琠?E7g4??0" ?3T|?V ZU(?ɭ?@?(ҥ?aME?8q?54Ű?94s?*i@K?>wDZ?-we?zj?)`_?Vw|?ѝdz?;8?}?i .?"fV?kߎi?.?^?#;3?iE/+-_?a(%C?M?W?klR?Wݯ?i\?[ɜ?`Zeti?W?YJ?s@l[ ?Cmy?L8Ϋ?B(1 ?32VԲ?ԁB,[?#ڴ??5> g?>+&?)?\0F?h=G?ו槁?]:?SNǶ?Կ?؄?HwiJ?GxG?a/?;@?8?jؑ?L ?QTT(H?Q+?&׹^?i ;?&\?66?{?hL?/jY_?Fb?^ ?1 ޸?J!?8 I^?~6܀? >R?ٿO?sN?ًN9)?r3?O}?J"YÄ?lau?"?L?*/S?~ABK?3 ?P{?Uz`2?0?؛hݢ?\f6?s.9?=r3?hڣ?PDF?^M޿?z`?!ڦ?Q3NV?dWyG?E ̤?C@r?^? ???~ N?mwt=␰?gk?r.IF/?:W$?z,E??AR&?z?]'Y ?x+?eE_>?4x?-S?.m?-eP?RwR~1?~}2N?cʋ?{@&͠?F2m?Ek{?jz E?[?P ;i?l? p?GQ?€?^ ?#l8n?!M?vkJ?v?b('6ɦ?`+x|?@0e?|M?BݸX?sEBx9?&?$&ʢn?nr?[bI?y?Ĉ?=Q?Brl ?c-SS?'ە0?/IC?t?? KL?"?lm4?ꪾ0?Ol?=}? |;?qNJ;?Ygx? ghn?ߔaX? ?QDa?n?!DK?=|?>FQh?VvӢ?Q?lo?Yݩ?ٶ5?]pJ+?'Ƅ?%6?)l@L?v]?pp?X?ԧ?J?^ô퉴?X?F?YwE?o_:?Ӛ?Nۦ?;\L?;l?f~]!?A9?{.?x{?d2m?Ұt?ʁ?K̓U?5!?C~zc?pZ-@?k:܃?,izꅣ?ɣ?Mْ?F?r?ax?iuP?H%?R}W?@M↔?Rw?6-?'?ul?]y?, g? ֞?!K\G?.c9?ɡ?%q?R< Y.?i?͐H?a8*?61?>[?,~ ?ya?^J?>?W^Npf?C ?w?0}?P-+;@?Ph ?AB-?%"V?&?p?%D%?v?Y5]?0$el?4?8⇣?}rԥ?d#?Qi8$?o`T?>'& 5?6?o<>"?} y?HE f$?Z?"^s?e"A?eC?#?^?֞?{?F%6?c~m?^e?zm?Rl??&`?M?k8TL ?]6?ϋn?;9?8?l}?@r:'?Gރ?7@P?&ʜ?MsԜܫ?(s??'-?o!?/*0?C+ ?4l?u?rfxD5?PV.O?oT?.5ƳV?X͆? ߟ 7?xuZ?Ν?Vy ??ĜdБ?~@?!էP?Ź8?.4?/c?d@"?E?iYf?&q=rV?BB ?qǔ?txأ?45P? e?@Qr?w s?̿{?X0|?rhr?*v`?\ฯ?iX?h6⋷?Dxǡ?(}5??s}P?n;*?;͚??Sg[?|!b"Y?St?g/UW?T 9wib?=j/?6]D?-^?G*{+?Et?ֆnྣ?mJ?6??IC?7?8?٦?)[݀?t?,&?a ?It?mp?v'?ǩܧ?8w d?ǶbQ?Q/tO?on>?)!U?95?(=1?蝙l?TY0?$n1 w?=[5u??jة?[*??ln ?MyB ʛ?d\Io?o}mi?D^K? u͝? ?b?jR?o ?q-E?8?Bw [?7?.D?k̞?ګOa?#)?tD_C??-?T5ȡ?0>?eoj?D/Y?$5?nK?a>C?qJ?Vma,?g1 ? b]?n?ݍg?PV?Rs?6#1?Tzj?HA?Z.? *{%?nQPr?$øV?(sŧ?|bߣ?PQo?֍^? <8?'?X/­t?3S`?T1:?/?"U?,hGy?nn]#?%@?&ȥ?l0p?Fm?g !7r?QU?h?&P?(M?wg?SP?2C?~?].ևt?$'?˘rY?4MB?,gY4?2Ƭs?U}?^gݽ?M8J?7,&`? /*.?rv~?U7.#?-1?=kȟ? ȡ?i"ԑ?Y?ht$? 'ݣ? ?k;?K9?Z?+?iޏ?r)!?1?VWY?w:^? 8/?6w*?֒m??A.(?Qf?-(*n?#y?n8ä? ?ڎE$?TUr.?>m?0:+Ҥ?V"Du?*~㳠?Zew? bg?QS?1 ?ih?HB ?g[?_*5L)?qYQs?3=?ڼ?c9? dױ,=?ew??Im?*),?3pU?H ?5f?f֘,yգ?Iv6 ??MO&?i M?27 ң?/N/>ܢ?2]6N?4jV?Gig??#>?f,?NG?']K7?$? td?[^!??EuV?Lzp̮?V ?EM?^:?xإ?g04?Y ?#?p=>=?{}?38?)o?ǭ1?ef?>Z?ec?D]n֡?lE`?#?:qզ?xs?I"C8 ?ܳEХ? ARʢ?%{2?#2?,y|zDУ?YYi?uá?e=)?Un?aol?{^ҡ?ɯο٣?[{?\R!?7qͺ?bB3?R O?@I?fp?߲Z{ӡ?̀^?_xv¢?e6?;D?c?!RqM?шu]?rTFkc?"dM?=W?2_ 0^?ߥ?Z<?ir%?mʆM?aC?I ?eщ?,EE?T!/?\!rM?(/ԡ?…Z-9?mdv?L[԰x"?`ɰ?V6?]A?VV)O?⎘>?ܭ7?{oC2?ZvT?Qݠ?{0iҢ?giBx?ilk?K0?uգ?-'k?S8?-6'?MfU?1G:?n?1z?ZY?@? 06?{E?սIF?] ?:y?Зi?r?"?*stVs?kW ?rg&?U$.:?sY?&Mw?~] 4{?,f J?.1d?Os?G3 ?,~K?. _ކ? ֎|?aUt[?*:?4?J?nUƢ?U>A?~?߷˩?h-4?TbHh?~X??]? ?-_DU?w_@n?H^?Rrj?9h?v?L?ϟפ?w੣?3h3)?o g?{4"R? ?l^k|?D>K?8?'1(?MM"@X?ЂBq&?;;?a ?)VY?J? ]O?qԲ?|?? wB?8%IΡ??v(ū?ѕ?,ǟ?BA-?!jӢ?eh?i?KX?[-?_(T r?;?n=?NvZ?$3I(D?&4"+??i?4biy?V_?9w?R-ӥ?"H?"ʦ?tR盟?L??wK(?v_nRq?[ҽ.??ѧ? /+T?#'?j?CRO?n-?Zg1?9*؂?R?H7*dK?j7T š??"S?9?U18?Rq? +?T"Jh[?23?*XQg?e8š?oh? m?ah1?]2? # ?$"?;ps?~~gß?†@?] ?qE?'1V?ݲZ? % "0?JF?p4T?cyX?ZpM֢?|SVQLɢ?_Da?K]D?ﱠ?j"?#+z?DIf4ѡ?3*a?hU ?w޻i?JY ?o%pG?bZڟ?t?t?&)?&wW#?Q-?Eh?mUbG?Ԧ?ɣ?’B˜? B?̸~=?6E׈?2Ȧ+?`@?D?B7?mi??W!?{4}k?ߢa?CV?>ޣ?KE&q?? 5:5j?!豠?1}?a=tz?I"[?u?՟?f6L?W ß?I,6R?[,?&=?)Γ?#DaҠ? C?^4Ęd? X? Gg?| 6 ?vH?vTLE?tPV?bc?0M?H ϣ=?_Vs?l1?d+~_??!Z^s?6V?B2X٠?vr?6?J ?C[?7|%?e[?^N?gn˄?K;Iա?CÂ,5?R=?;?3 D?UQm?DBV?݉ b?G?^"M?oYk|ㅥ?+Ff? ?Np?T? ѡ?xlJ:?,`??WLB?}lI?OE?0l k?6\ ?&)Aj?=d?Oex3? fޠ? J?GXO?1?Y7E?:*@?cAg? Y? }J ?9??Jxm?=F1֡?9r ?Z ?Cޢ?;B(?8PFE? ﱨ?<̠?Ί?mM?AL?ыE2z?;ԢC^?-&JG?͋9P?2|_~?fg6 ??es?SG:]?G)j?WQB̠?[?̩ϵ?` _?sX ?BmU?Zk?B'G"??6r?*`1?C?Bn?v+p?Gͥo?`B@ ?–YԤ?eҘ[?B[ ?i$q?.VK"ٟ?> !?lw?7V?uo?BTn?P?N?@;'?^4Zeߡ?.?ܴ?Sa"ء?Ԏbt?yI37s?rn?UEƑ?m{? ;?MO?-/8&?ﲟM?$棣?LN?/0}?s-:?%Qbբ?'?!] ?H**?'C? J|?P,?*L,H?D4$Ua?qO.?yTD{W? ^?>O~GK?\?L?@^t4?׽F]?PM?y)?-Y3?8R?U6E0?խ|E?>WE#?c?z?-Lƪ?0)?M1tĮ?[Z?L]緲?u}?d-e,ڤ?N$)?)^}?| h?%Y&>?$]?*\n?y/?t@L?pOߢ?SL?ne?WX?:c@?-+`?}u?_ћ`â??B`GL/? 9?gZz?m/e?01?&|?Ҵ)Q?Iњ?[?0 ?ϊ?;b&?6q$>?}.X?S-cy?]w?z,|? t-7?`6?sWѼ?%=w?7Gʠ?FRќ?+r?&?Jv?=y3ċ?5^? H?0`*d?u~s-|?ȼ ?3^]?]2hq?BQ?Y?ow,?U?&[??uጦ?~t?ȏ:ꯨ?ZEHz?y_3xܲ?K-wmǺ?\?kW7??znʢ??" ZFj?3r ?;Bg?<L ? r?tbO :?T42!t?Gm{?Tf?s_"'?t`R?['ʤ?t~/?%L?qmǼ?]!ӗש?[>8Ԥ?`3?&? 1!?ѪNZs?.X?$??lk2 *?hh4q?Ú?MFm7?J?ڇѦ?5 A?vR?XP]?NeE?ЉW?Ū? ¯X~?ǁ J]?,? ?%̝`?%E? ?e!K|?" ?ogp=?U)fqDk?;? 4?]?(~v? jj?d5?X$V2?#ln~?P8ιE?ʂhJ? \??*YL?E?b?$*GV?ۣ@ ?=t?_oEǕ?Ռ?AOj?&h~?\.jذ?sUjY?jL˨?/'٧?h.8)-?N?d۰?Lװ?Lj-?OM?hv%%?,17?T?w{8?_M*V?S{r2? (;1?߃ J?rP?$c?Mӿ[?Z~X?s6?D?[DUv?T{U?XM0p??wH`?YB_M?7H?E)? }Sy?`?5f?z {`?'uOU@?ל/|? #V?|??Κp ?wiw?yȣ?טyo?AҢ?+wٵ?hc? USs?t?7Gy6? PJ~?~'?J*DŦ?.?M?.w8?~f?%X?xg?l3?S:]Qӗ?mLq@z?i ?} ?N؂?Sc?>.~ H?)aҰ?ZӮ??!?R9?a;x?!ƅ?8@@?w?ϳZ?qjz2?Ey?[0 ˡ?;?VY? *?&e!?=/zcy?cUh?'?a ?3%g ?Ծ/=B?5^?N6Ŷ?c?˥?pzڀ?@k?xgf?>\?5pJA?ѫE?ֶ?MU6? ?)?[?T?b ??U7ޝ?bT mj??2ʺ?J?ȥ.?N?"'՝?ꁵ֡?5yB?j?7\?hA,?=3]?~l ?1u?Ÿ?K?X_?ƅ?z8o9?6?p*c?$?c<ܖ?g~8?2x?p.e]ۥ?0Oٳ?:u^y?I?l?=zb?{_ɯ*?DA? +v?Q$?sQ?lN?A?h?_?AS?Q?`/{W?6vg? 8?3^?/^3? &?&`?2k?ornX?z4??,cC`O?Tƨ?soQǧ?^TD?j?z&??Be|.?W˜!?9&Mw?_?31*?.8h?:C?yo)P?V?D?a n8?/W?塽M?d L?|?^H?yB?u=?mbP?$?9Zᅴ?lK̙?|$R^Ӫ?;+?@?)?Hc4k?ے*p?|QЦ?}f?:,m?n&Ǧ?- }S?~&?3o|?Cښ)?&(ԡ?)9 ??>?ʺ1T?@և/?TT9?*y?+(̌?5k@#?!k?K:E[?PK?ܝ6RC?OO09?iO?$~ ?J*?a?_?Q9?3 Z?/mZ?2h?";?Ie? [O)?+ɣ?IfV?zq6I??h?v0?)xC?HR#*Up?ĈN5iV?nqR?)?շ?"9Af?UrF?,y>qc?  ?k+r?YJp>?zAc?9m?63??VrC ?zΖá?k]?o?>Ij?NwS?.U?_?]xJ?Iw?$?a?yr$Ц?4Ң?5qi?_*^ץ?0 2A?(٧?8m?y ?g$7?` ˶?*bGV?VhU?:; ?ဓ^"?y?F%d? pd?zҡ?vy? ]T?aIy?0_=.?pfġ?ԗe?n,?Q1?g?t!K?ي3?es?v>lK֦?_?`5m?U_?s$K?tRܳ?-?#\X?u?bNҍ?1 e?g?"H?3r?p7?If2?οJ;?'\?6?wf?j ?'ͤ?Θcc?tNխ?9qdž?t;<2?C.P?XX?Kw?,Nb+??ꋐ?P?=RZۧ?y?.>?^%?P=Ϣ?`A9?[K?+>?.Hʽ?R?X?bcP~?X<3?!c E?_4@?p{_?vꐜ?7N?zX@?-hk?l̖??Tr?Is-?zv5?x@[? |?H[g&?;g?4o8b?σ?g7)?j ?ї 7?APL?Xgˤ?c?jWG?JnH?D酃?&lZ?f+?L?+WGɢ?r8n?x?=&?<|?{jҡ?To_إ?ۏ ?Nbe?qPt?M?5?l%?h^@n?xeɪ?WJH ?3??EX?I;Q?n?W<?J1?ճ O?!Sk?z=?=j?ѻ'?l ?s?|?B~?0Ǧ?^O*c?΢?Sn>?4`=?'T'?'m"?h?f}f?2?>M?mt?:$?#f?*5.?ugߣ?CG?k?Xp?IQG?dp%?B|Ѧ?I ˬ4?w>8?CEHY?VU? H?X歲?@? m?bKح?T1eej?7D?Kdo?]?Hq7C?f EԊ?z#?rm 5?3Xs?ޘx,?Z?Ћ+?=?$Mo?j>=ӟ?&{a?GԂT? $?מT?vT*{?k\d?؋?v^?l",?bj~?,˃.5?L ?LK¿?K$:M?hm`?: {?/o*?o2?߶S N?eMY?zS?y(\O?zo?F?TN?H 5۫s?bFt?ϸˀ-?k7$?. ? ʫ_?X)3v?='9 $?J?={S?+H?%{?m^?+!o?`?"%́z?`aR ?19?T˥?%Ķz?k#Y,?k>m*v?3M?ôV?guà?uI?K`MX?Ng?n?>?)8/?4?m5.ʡ?u_D?ekG?ꃰ?P?Š?fܤ?- ?hq:M%?Cѽ7?)]?-> ӣ?yĉ?tUʽ?Q] \?Hˡ?] +/?HD,?> /?W~g?p?*N?t?YܴR?Uft_?#? VT?),?+?&/?O7?~?!??h3$?1CYn?2?`0?բIϢ?ʀهנ?5Zs.?)Hߣ?;edX?yF?߯5? 4?s~?){/פ?!fY) >?sA$?Rߓ??Hx?~\? $A?MHK?qdNh?)o{? ~?tpM?'p?*?XvmK ?jhEc?WNE9k?R?Tx?i\w3? nB?c{<4?}ԥ?Ii>G?z?r~54?;o?'a?ϡg?XU]؝?S?R/9p?U\r?)a`?D~??\!i?5ZŜ?|tA?|3?V4-?Jy?^?̭?C5dϞ?QAjD?s>$?O ӡ?/p=?`2?;?UҞ?"$ʨ?w?J9!?7/!?+Yl^С?~,?"O>?)ao@?K~P?vƆĢ?_f?f>}L?žuТ?"S?ۙe?9H%?6?( ?# ?S@?бל? ?.?Y?t:ҭ2?C)?aa?q?8`?r?n?AV*w?&И?l;) ?kf?쒅/?9d;Ρ??|?i1Ѥ?o@5?e6g? 4_#?Xg?3 ?xwT?e5?pD8?*,߁?qmf5?x?u?VAK?.wrH?^?怟?.W?]y;Ǜ?QZ\"?A<^m?MG7?*A]?0ҊA?j/Pdb?h^}?]wW>?1̝?Z"|ٝ?v??_?jɕ?:G̡?̯{w(?"L?<}?&Stjߣ?3ӣ? J?)G L?;?ahd*ޟ?̲?S?q̓?q6o/?ac?){?4 P?1R?r5?dZa)?L+˼?rs?J >?HX?dח(?El? k?:ͩy?db?#|?g?#tt??IEa ?4)ˢ?U?6R?ܼ# ?\%K6?q0|?7Oߣ?-J1j? !hq?{Ԥ?ļ+?k6p*?l5K?`{?.{2?TF䴜R??s?nW? 7?@sS?c7d?%8XKq?+kU?X/a-l?#J??$E?ؿڂ=?N]ՠ?]?°,?gY?jΓ?` =?J}'?OyBv?XΤ?( \R|?] ?EJ?=?7Z)ZI?L2Р? G?DA(dl?//l?{?ѵ~y[?QK?4?`zn`y?$? \)x?{co?/Fc?Ĥi?q;U;?@ '?̠2??sX d?t@P?R6M[?p8m$?H8z?q&zt??SX?va1s?F?euc?TjG?H&K?ڒGu ?*?=L7 ?^?7*?Ēʧ+`?5?d?p0?e(s?Qn?ӄ:?e$Is͢?[X?mHz?E.e݂?[!?Ma??泺Y?'6;?4"?7?*Dġ?q]x?@p?ͣ?^HF?x"?)?N'68?@@.X?k?;Nd?Oբ?Y?!aL?_0l?.xT?4,t?EM9?d ?؍/? ?fbע?'nŃ?x>?^r?+Y?yp?5g?ea6Q?Ԍ(%?vY?'bɠ?z?gPd?QGy?_ KE7?vUy?&?$bL?{졝?F74?j:?eP?v9? +I?%y(?A>B¥?FRTX?pv?M\?-UM~md?5~+?p%\T?j7~?^xtC|?C_??A sƠ?t|}~w?}>VL?lj:?ӛK?,F&ۣ?2p?"֗?BTkԢ?ܮ}?Naơ?޲&?{9}?7/HLC?yU.Bܤ?:ʴ?F96?`a?Ld? $ZO?qǶ%?قrE?:hD?XU? +?giS?pR?i4c?D d ? ??GKlqU?2c?%#o?!0"? l?讱WL?F70?u?Ԥ?!DW?M?s-?eL?{?7?>R?۝B侥?N9)? [ ?+o?l^/i?B#?]G}?Hٞ?1g1;{?<3:?^ %?-@C?p)b}?f@Ou?0myݢ?_{1? ?;?qL??7?{^?+9?`.E?jB?Ӳ?mz8ؠ?ހaȣ?$6?ݾZ~o͠?V?&*?GOб?-eGoy?Q ?^H;P?a(U?N8^&?B?oQh'?ry?&?qo^?7Bͣ?'?jqS:?38?A?KlCZ?)EX?_^?K ?F?Yj?#Ǜ? ?95^?*z?N?Ix? 7k{(?)%v?bqB?J?tSx룢?˜G?+M?і?wdL&?fA?#?5. ?yԢ?SЕ%?7=`?-lר?XkD?&?#!Lڢ?*? J?|^?h7ې"Y?秦x?* 1?e83?>7?F}7?Q?k;ǣ?ݒ̡?q9??aKFQ?b O֐?z?1 a?Ϸ_ib?w2?щT?~?q9?~ⶎ?*j%?Ft-?Қꓥ?tjl?2f ?Lz흹?GD??9Х(?Mv=?a?P%?q{ P?,ԡtP?R(wk?#h:~? 2?z瓤???i!d:?O&I?ZWK0?Յ,? * ?+?d?S4y̷?+n4?rr7?[N^ ?=҆Ԇ?X:?$?T ?=Y,)A?1a@:?OAy?_fv?o')?2m>?2齳ؤ?j-/?Fy"ێ?̉fԇ? ? m]?h^??FGu?1޿ ?~s?s!M??Ͻ?)??r?(Ĥ_?SMp?F?\?o+?X(bε?02!q? \?nZ=ꛠ?MGQ?ن-Ĥ?Pޠ?+J?ПR?Z )*? Q̼?<ʣ?4J???01C6?W]2? P*?jM?pi:??Ũ?&iW?CŠħ?%F?ef?6*£?`}T?iCՄ?3=??^?ҍl?ޱT?!?tB  ?P?S0ӡ?djk?$-A?91Q?UV[?Rb(V?E4`??Y5_?a?L*Ȧ?2?M?'00?T1?&P7(?s7?k D'?F;?E 6p?qH<ܢ?9k?a8=?PUf?#, U?u?F6H?Te?h? ,?]f?&f*f?Vz?hMs?yx ?{O?5yU;?7ҡ?{P }â?œ?'b?_2M?4[ ~?E̪9?7?*#Tb?4JO?N??wBG?ap2t? ~Ow?0@=?wuL? B(?T?+u!7?$ܷ? ޚ?3:y?"3?TY?C?a ڙ?p3x?3?qܦ?ָs?I/ ?p?54G? ?C)s?ڦ^?@&Ĵ?^aW?ny.^?&8eFJ?Ɩ\?owGϮ?6?:V?bO|??>i_2?d9#$???U%ܡ?7d?7$?J?zX[?˲*?&, ?? k+? 7?S\_? [=?=?^Nxi?k@7?9\,?U-?70_?;| %c?V%+9?2 T? C&]p?|ꢼ?NR? =+?T`?hmpo?U+ ?07!?;&,,?n Ϝ??|?,:UN?&Y? T/?Õ?=#h?6S-?y'#??`??&tV(X?sa?ג;?Tu'4?~na\?8j.?l?sqA?ep-?a}{?,l?M;K?r}?. j?i?Iy%z?(?b4 ?L 6?SN2?ϋc;?xjo?!}?su9q??hO;գ?@|C?O&C+?diŮ?=Ohj?#N??Ёu?1HU?<1? ?Kv?Fh?.?,4.q?$@i?a?-f8?Rc6v@?A}]J?ߠ?–>"?ɥS1?w͝e?]竕f?7*?M$y?s?C$Z{Ϟ?Fx? }s&?%%%sݣ?&זǦ?AdB{?Lݗe?㏧?AClM ?AiF%?aŔ?/-?1ץ?"QJ?U%a?UU-n?2tuU?m8}V?w ?.H?֚$?]s?Gy?1楢??yGC֢?6&ӣ?%a9?x?^;? xGD?1-5FZ?:Nl?k>?4?l4?VX`#?`j=?z??JV?R#l=?hׇ?*?ۍ?ȒǢ?м?^^ ?t$P?Mt?ҭ?WJy?I?5j?BW?Qf`L?8g* ?{LEĠ?Aϋz?R܌[?=?~GOˤ?_Iŭ?,8?0կ̡?B?XčC? p?L6䛞?3#h?޲? ??.`? ZD?/͕7?`.h?̠?j"?C-҃?>%&z?;)yf?14+?)?)?Go?a!?ȳv?Akp?ǂ?/4O?eMZ?ݝ?4,e??2%@š?v~e?'[Z?ыXe? tڥ?L)?V?.ze"}#?GL?ؼ̗?ƙ?DiN?4չ>?J) ?&R?78-C?ѱԤ?MΎB??NU!?Guy?#K?>q9t?IIj?KrwB?~i㠟?G,?C"?fI?56PG?Zd?ča??+n1?Qа? U:^?x9?.GF?"\?橚?3Ο+~?ڢiF?lP*?^?d&׻?[rZ?(ۨ?"K?'")?N]ྞ?9{?Ev?:h ?~bzc?$4e?r󼵫?w$kN~?ٙfU?Mf?w#JTK9?%e6??̋դ?^.{P?6R;?38e\Ń?ӹ?bѮ?y^?lp{??)?F?$*$ܝ?\ױ?ۤ?^Œ?K0?-.ܢ?a!"?xvX?ZN?;#`?zˡ?&{?1d?`E!?Tu|?|l&?tl'?zDE?LZ?f{à?|?ϡ?C(?i6?rӔ?G!8j?iKn?o]r*Ҝ?"B&?)ڢ? `}֠?$ʄQ?{Vڦ?F?y?oS#?R|?Y;?q/!?(W?@٠?)h?ظZ?4IM`? BD?@#2@?ZAqe\?Gy̘k?`???c ;I?B\x?k1Lڢ?ɻ$'?iHI??zBY?qB8??g Ϸ9ۢ?X ?D5%|?s%9=z[?B# w?%? ɵ? D?F ?e颤?1 R?nb?ǖtw?[z&wX?!iC?em%>?O~V?EӠ??H Ըm?d3<[40?ձ!?C1_?+/?^\Ǥ?V6Mm?"o?vw?Ȣ?1R?vVm?ţ?m0$"?Oy|?lv#?,?WH☴?6Yg?eh~??f?<^ä?Ts k~?$ğ ?sa?eș?WӚ?5 8?EKbm?[?5?Qf?$_G? ?j07?a7?51?Ʃ@?^L?zҮp?Wy6?ԟpŢ?cI_?N? }?a ?!ۂV?ZH@Z?2Q?y,]z?&ؖ?ODKڢ?TT? w?G٥??#d?O:GP?G%?ٌX۔0?'D!xݡ??g?ΰS)?aH散?&&\R?0_?>U$?S]?UOEk.?ˈpo?,oQ?"CU?A?!d?ݨ;S??b?P2c?Oa?ǟǡ?Bs?4rɑԟ?O+Wġ?YM2?v6?EaIc?<ř?j+?D(?vʦ?sDA?y73d?JNS0w?'JFV?hڧ?sG= D?<ыk?^jˢ?fy?Z̰,m?$(?W8j3?å?zR=. ?HDr?#tM?plZ9?dw$Ρ?BZ$*?2Ba΁?q_'?NB|\?sc=?u R?,?j׊?#V>?򞑏?nD9?Ԝ;t?n?HR?dg??-5|?D泡?>??L2ᲂ? Y?[#Hv?Dm%?͒8ڣ?DLuR?%(n[i?PR?w3?[4͡??EA?K??Ashki?tк.?Ѭ!?)=徵?ab}?Ӈ?_?!xנ?pvHߡ?)_?k[?]Rr?FVIC?{Pu?2Q^r?wS8?]}6@?*(E?B ?8?EU-? WlW?3f~?׀ ?SqK?"0L?J `o?&n?r2GV?YEm?wۣ?`9?nKtI?yՄ?n?p?$r?ʜ?^ep?#۞?2q"?d{ ?r!t?$Ȗ!?r#^T?O](?F|?57?  ?A0?=ȍ0?:̡?h?ZM`1? ZA?FT ?U3u?4x?֩?(^ۃ?Յ!?D\G?hOX;V?OI?!9?b˛?!GrrÙ?rM@?hb6?~4ݣ?Ai?@ 9`?P\??ud ?`?Mbif1 ?b?Yа? H*?)-?6[TE?M~9?9/?4Y?gX? ߏe?^?uC 0?.?JGFɣ?lk@A?T䀦?kLݡ?9]|Y?"Ja?spl?KcO2?폢?+C?9%3&?CЍ?( K#?jε ?fB4?g"1?w?]7֕?<к?l`ĥ?U)?7y\L?%s?:趢_?w>/?C,4?oaG?>bfpD?AC\m?]}?&Ͳ?Mc @?}5n?%daޢ?N`3c?ڼz֢?%lL?tdCa?f;ʢ? < ?ִZ?ȻԢ?Tp?1.d?l?yc#G?U|`ɧ?յ?D???D>?$e?s9ޢ?o;9?U?O?R,8?Q {? NfF?z?<-a?8v#?[^X?LrX?A#rl>Ɲ?;JG?eVa?ds)?*?MW(?oSM?r\?4û.z? T?{Q{? *[?p ?KR?ܧv¤?Ҧ8+?)N?Et Ѣ?{?2W?9 ? ?T=?ƥ;?0g?|]2?.2?/wυ?MT?'͠?{kzy?NJa?,$ M?gR? us?_y?`wF?jDi0?+dݜ?m ?/_{!?M12,?"O?=|<?)?fWhq~?}%4P?|ѐ?\/?Z U?= à?w ? ?P\?zJ-?"]?>>?%5?Qſ?W(qE?7?X?Mn>?@ q?k[!D=?ЄN?Nj!?J2?#ۮ?8Ƌ?tx?{0L? ޢ?`-m?c.?]m̪?'G?ި'?QHO?d%?7wYz?0f?g3?-RVo#$?} 3R?WA۶?nnFI?WuE?(z-?ڕ:?Mqs|?:?z+?k5?";0?bps?eE1|?[?w/X?qf W? Y}O?(=R?9Y?h0?%_cɨ?ή]?xkE?k[?0_?uຎ?K?%YƠ?iQ"?\?jV?n%ڣ?.?h?!21?l?~Q?[ 2r? n?5y?=Lh͡?= ?I? ʡ?5I ?:.n51?fMF?d~' ?s=?Ud? ?&?EB?[ ??q`l ?"?? bڿ?p$?כE?M0S~?a+?¸vв?Fj?A6h?T? ړ?<~o̠?ȸ w?nI 1?v]4?hQwt?&-m?0zb4y?$GAF?Gg?jqv?5ذ? K%?C?C^?fpW'?Id(?o!eK?So?% 'x?zLM??ř'?^R?CSt?Q 㱢? dT?ǜ??{@3>2?+hOw?\'Ş?6y&j?T.K?YeJ?CU\`?1`}ޕ?r|"0Ԥ?2x1?^Υ?ES"?E?xI??!>o?$?F ?l $6?=?u?k,\?up!?Dt!ԡ?Aj?zѵ?@޻?e*]!?$rK?H?8\ud?~? Ӑ?Dȯ?"Ҟ?U>?Dn˗@? `wVB?lb#֤?}9 pw?ԴSD?hi ?t)?=:]7B?4`?bh?oI o?>F?\K^?Hhaܣ?h*?%0?Ι7Ai?RԿ? ?@c=΢?rF\?vWfu~?GXM?})%?PTD?ښƅ?I'?U 8?ȑ? 5?zE?[=?X,?h<?J|%?в\?ekTe?4j?{9?u#{+ ?Mi ?`WJ?Bow?ҫȊ??PRB?0?>/|o?5̴?qNT? r%_?U׉.X?Q?3K7Q?NM?BAzE?uiҁ?Md0H? 6?9զ?n)?I?;~kD?R܂?WD?ʼ?~vf?(=p?`%?Z?@pW?*?0P}?RWڠ?dIG?@qJp?vF?a?!?Fb@?. ??eLA޴?;-Ec?Xn?u{}?hW7?Lz+?cC:?<$bx?4%F?{L^q?>[aݦ?q[W? nק?EQ+? r?V?N?r1?ठ?٬*?g\-?;?n*?y>z0?^c]h???g(O?bg ?x ?t?^?D3?+D+?pQW?bƟ)?P//?iIh`i?K /?GҊx٣?[~?I~{%?xZL?Ƭ? 91?*~>F?$sYb?/ ?0AI2?%Q? f?V@?C|]?rH%󯋢?!7Jp?QO;??xP,?baH?z43p?wsX?"Qߡ?`03p?oo[С?"SB?y3٤?TB;ޒ?YF?[{[?"yHD?61a?p&m?%^9?ơ?3:?h?wxm;?r6n?Ss؈?E;?Gc?tF?y"?=E/?)/?XeT?2{-?cǪ?͛HL?_ &?]=uy?3h?Rp?\ci?٤2? #?wv0?\:?9h]?f4?\}?E<:ǡ?\9z?ܲ?QM?r5?(ݦ?qGۥ?RM?Rw1?:&.=.إ?@$;ä?JTП?I2?y4t?3ɧ??}:?4?S?l3+?m٦?=ó?šg>?9 ?Meۦ?^kE?KVH?NŒ2ۢ?^n#I?I-ܫ:?JG?$:?i9X?er%?һJ֢?l=?? 1,`?|?&?B?;ɍ?6NK?I!t?s ?CD?EeN?! l?D$? AHt?iO??z;n.F?ζƤ?R?'@?C?곉M?m6Q?ݒ7?}M?*/?R¾?¨Vq@?kEՠ?ݠؙx?佉@)?f?#xgC.? F-?_6֡?ʚ@?XMˠ? ?~l\?]:4S?>SF|?mmD2?t*? `?C?8?+:?'ġ?NDN?";*on?j???h(c?󾩱?Drfj? nZ?EGX$??O|[?Ȟ?}oǡ?0˳by??)Ty? :Bc?(Y^?+zjN?˰P?)<➠?{)-?T⇣?&I4?K7{?צ??i`͡?1Dx?.x?LTh?`ʌlB^?Gzriơ?Yjߡ?'*yx?ǃlZ?qy;??X=?i*,_?A33^?){?t?E} ?m+ң??b?Ů떆?HZ?/v-?DMJ?߈P2C?<&?Yyd?ecN?,&]?%)?M?=?fI|ϥ?Z^ġ?6/?·I?`Lf?HY?T[?c[?p|?dѕ٣??o,?`?~/?Z*?+4b?>r9?Ey9?NBٰ?l+?P3/=??sH?V$?eq>? {?Y?/'F?=*a?lm?)X?x#5ߝ?q/?c;3c?=?hm?%836 ?р@G?Q?à?φK?kM?ig l?2D?N?vq񗚣? ?.,y ?[3k?*fġ?V/?n?D ?^^K_ ?MP[?c+?cw?5j$? ?p+j56?ԎwCO?ģҤ?xV?ٞ?oQ?L]ᠡ?IVН?'S\? 4"?Z?Ίݢ?uh \??Ɉ焥?)" ?Pc?a%?/&?1*4?"p ?4M? \/?ȏ?D瀉k?ed&\?\A{̡?9+٢?]Xџ?J[?ih?i?"l9?y,$?~x xv?NS?yz?LaY??Yݕߢ?fh-?m?(?xwMw?\E ?}r?9& ?U?&>`Y?bq淥?q́?ړ?r?n.?#$G'?^?Sxd?9n?l=Bz?jM?k*ݗ?rmi?5%?{IyinȢ?Le?T4?_$?}?1xx?a?3ϑ?C[`?ZӶZУ?9گ_?T?Gg?LJ'?}Nj b?!/?u`?2 ?\tϢ?bv ?M~?)r?-?#5S?SAW?{??$ڌ/6?5N?C..?,H?fп?Hbm?@;,h?z z?*׋*螢?fNj|?_3ⴤ?D1e?zTRޫ?'C(?+}?̣?:/ÄZT?Ikwͣ? Ð,?ҥ+?,:u?Lj4?5)??m銣?{qV\?ex? r?|УN?|?4ܴ?n8퍢?q(T2?g=-@?%}5H?ru?Y8q?!itA?F0|}?ĭD6?‹]x?Bej#?T*?[t?2q?<4:c0?-?ZO[!?r?R?s3{?uB?z*7sk?3m?6?X? 2m?lh?'^jΣ?Rj5?*+u٤?gk[?VgT.? /q?l̤8?@-L ?m}?>'',?P?U^#?MYZIM?*xeǝ?<_)? bNs-?~?kcˢ? ;"??|}[T?NKB:?4?foSף?&0?UV?VoE*??U,f?;6?RZ?LǼ|?~?t4ce?ӑ?ŷz?{}? ݧ?d_OUd?WѬۥ? n ?Nk#Ȥ?ՠ ?iY??Q}\cݙ/?_?5a;.?=ˣ?}8?擠#Dק?1ܤ?L?`ݣ?~ o?Ru°?0.?_??ɣ?d?lmk6?4 ?!kM?G.LQ?/5(?7ceУ?d\?uN?N?ơ?ގh?'l?O#;?g!?.Ϙ{? [:8?w4?ݎ@?+ݥ?s%Ԣ?Y`-?oNy?i'Nգ?(7?I?e?Z ^Y?ZB?ف_I?.O?UA?"P g)?(;?g/+? fg?Ashbn?DO ?Wb?Dϐ?m:%?#$?-cr?^4/d?=tz?鰣?K4Aۣ?jg?d?NK3?Du5?DPP?~?4?҂ע?@o?*؄͜ ?y '?o7??yj$?܍٢?+&C?مW? rg?s?B?HVa??2'&?U ?Ч?z ?k&N9?CE'F=?0+?,?D0 ?`o?:j흟?7V+?1MQ@?7'ˡ?ËO?♝?(z@?8;Gé?el9?3!?{h?A| -n?˅?Iی?ܚx?lr>2?P5̥?W-?ȣ?vb3?4i?c1.?Vţ?@X&?k@*WT?n S3?X"?픒? h?8m?7~d?Ǣٷ?m|@V?6cͣ?T?(-9ɨ?|e? ㉾O?*?? ?ǿ?AO?Lij?A?z"/?? "??E?[?*??u1?!ݏo?Vg̤?3=? %??f%y1?'#ʤ?$Ҥ?J? ?3Ļ?:s?1?6DH?1cI??t'?g?ڣ?\c1?K>e?;?uB7]?3?>a%? +?M6[T?Re?' z?79ǩ?sb?e +i?47ģ?)R?+?bE?@ڗ~?X£?mN? reF??NB1/?; i?;EdL?lnѧ?3\j?1h??{pm?R?{?)R&?t ?Pz{uJ?j4mޠ?6f?0V?|?'u3? >ާ?'z{?)\f?P? CT?$?L[p?f[$?, \ e?py??P?r?nZl?(%õ?]cY۠?#D^fM?a1ǡ?֭?'~$?;vS?Tva?tx:?'W?B0?@7ݣ?2Z8Q(?DS ?tW_?8̥Q?N=?Ub?ad??bIF?Ca??@$,?f;$2r?es\v?&C# >?:i?HDp?(ͧ?{KM?ﴡ?u&LΦ? cӥ?W}4?ak$ˢ?Ν<-?=Z?Ys-X?AJr?j@Mk΢?[Y*`?iö.;? 3? w?Me{l?/ȍ8?cp?Ҩjy?|ڹ.?`8?Hޡ?~^nK?S??k?F?l?g'X?kvEߤ?q/â?6l?czڪ?d֜?jΡ?Rt?kҧ?ic?|%2?D]6?n?Er?/?VK?)Tx??yyQ?VAvN?%¿?Vn%?[rbÖ?Ly?)g?6s?_yIp? \?K;?Øѡ?1c?0K?Ď?k?ܹ!?!U?E^?_@?5sͶ?p8]!8q?ԭJL(?n=o?ۑ0ң?]\?eM>b?B@I?tL ?6ft?kD?<^՞?IS*?9Ʊ?ٷl?_n*?.? ??'zI?A?j w?a4?X?q;٠?:?$4y:?_Jt?4?xF?E?K L?CE&?l?U"8?njy?ж*??f?MDTܢ?8Vh%?[U0?3%,LM?#? LI:?#?bͣ?d+g? ?ǒѡ?/?~xrK`?U [R?ڙZ? N?`2\?:%I?R:?5?Ь0?6Z$G(?78'M[??$?5v5M?i} ?".Y?ljҢ???2?ye,?*s?qlcvD??&%?jm?:ܤ?t.?+#`?; 4R?BPO?Aƶ? V6Y?Q?#E*?'? Ӣ?VB&?Д?Y]ɠ?xq?ӆ?rs?j$?~?L=?hTW?ʩؗ?1a?^G? ?/Yc?'wjCc'?B!;?֤?DXq?Ɉ~?%:GFsI?3铧p?]g?6.D?j&ߢ?e&{?A #-?nj?h?㼒௣?7[?Xy#?(8;?{?`R; R? wnc?EH ?$GspH?8Լū?&?W֒?1(4?^$?|?juݜ?e 4ģ?!6ڢ?- ? к1?sQ͡?p%%?Be?L?to?kOp?E{a?pQ?׏V\?΅z?7f?՛1T?Uh!?ve ?8O?pg괠?kQz?=Ν?뒫+?w?բ?\GU?dH_?6YJ?5k?gt?;5?vyᤰ?2Ym? (>?pp?R @?~q(?Je?X,? 48?;c2~?!;?^צս?dlU?;G ?bwQ7?ݭp?2O?K& _?Vy? 6T?ASw?ڌ.q?e;Kn?εN?Q?"Ys?0VO?򽼄TL?`h?'tT?+)(7?)c;N??a?}.j?کLb4?T?ޥR?6C?#nԟ?Of~?#)^?G?IhP(?i|ftI?T'?Hs1ܰ=?do?d֙+?sg썤?H@?cH͢?6?5|?ƭ?t0? nIl?%t};? ?kԠ?;^f?Zǀt?]it?llNЛ?eq?HW?tQM?˜?-in?ǭ^t ?ў?vW2e?Q ?r:f^Ԗ?n+R,١?B?xE*?}l ?/i? &4Ϥ?JU2 ?S<`A??Zݸ?؍5?xv@?Ṇ? M?&D?wVӢ?AL?UGa?D~?TLƢ?87SP?ZV?S͡? ם?zk˦*?N?SO]A~*?lA?Sm?-b?iϡ?OTִ?UN?='?.Ծ?$A?,&.?n_j?q 0ơ?]?0KB?$Ks%?UJr?v0?^]L? 5J?Q GO?Ӏ =?_Wp?ӑ@?95Ӣ? P͒?$ٯ?~`?fJ?b" +F?έDe?ls?mo?ZwP?#%X}M?98 ?xL?DlZ6?D%?^w?v ?!z? ٹq??àX?]?ya R?Q ?m1ۡ?ʜ?f7?`Ç?.Xzw?fٌ?J.2դ?gxR?mWBS?Mjc)?| I2? ?y&?2?_w#?/ ?_A;V?^9]?  ?j1Ϥ?Z?$?|N?W??3J?0K8;? nWؑ?h3Τ?RXɣ?`?( ?T@?J?UΘ?/w>x^?< ;,?Ɂ샡?9Dw?G?'?ĕ? hE̗?kFo?^v?*?5?.Ж??{覭?Kk?Z7U?Whg?%.ۣ?CM ?[W?W+W?'꠫ ?02?kE?][?uFi?@?STh?zڦ?Iy]?u?!_~?`+Ǹ?In?ο(?/?+?Juʡ?A ?}˙?G?fsFʞ?Q> ?B5k?1g +?Míǡ?i)?cݓ3?dH9?m4(?\kvrI?Ca89?; ?0Q0r?M?H?lwF?rMv8?I3GB?&H=?^5?r 3`?#:/? Kӗ?6죦s?nǪZ?#ˤ?VL?f?%ݡ?W?K-utz?R_A&?/j2?{'g?d2?'/?͛3?U^?0T!_?u[ ?>d3?]hୌ?5CGe؅?Lڲ?I?+,5?Ng?5q?K?Ad9?fNO?7Q]^{5?R^H?3V,n?5x??Q%?ډ?s@?bš?fed'?7۷?a' 9?0K~.?%K?A=?taȞ?&?~`)l+? O˺I_?O5\f?VR9o? ?Bxp#H???`'P?EvKQ?hJ"?DN?Dӥ?>ך?ևOl?ϵ :?8wp7? p?oCע?jʡ?jU;?m ʡ?0?$?B?7+W??ucQ?@ҷc?/4?ť?ĉ f?}?0%[?ޢ?h2e?]p?%?gQ΄?S?12?L;?Ҩ?թ?*H_F?n?$+Ź?}vÝ?¼^?hX(?u?G[$?Y@>ӡ?H ? ?@?Ο?^85?{B?IM?R}"?W淐E?Jڤ?ꄠ?~?1U?k]?bN?z7Ġ?l\?;K횡?-zso?_а?aX?7/?:@g? R,?;NLpZ?`.*?d>;e? _?<_?"7?lˠ? ?࿞L?.ѷ?& ?U.|c?/FS?h!?l?V2S?Lۡ?S ?"烣??d~ӡ?r瘢?ɭud?ա?mi? wW7?1SР? I%?l)K?kY?5񠳙? Li?"26?=XP?{P??^[?~wU?Jh* ?]d?/rqԣ?#Z#?A/?lڠ?HNX?p?K?DmH*?bε ?1>m?;љ?+=j??,|]?W? ?L^}?؄abH?'L?-)?&?|s?4??7v(ˤ?up?uQ.?Eі?۪?% ,?Jh? *?]]g?uy?e]? S?7)m?X`ч?|ˀWg?6? :Ϣ?άOYn?/-Q7?E?&?ɃyMP>?q,~LҚ?<|0?$l|C?.jĤҪ?)9?eB ?}?y*YD٣? ޵q?0I?uV.2?ز0 ?02?ZNJ? m?1?; ??w5 &?p?dRg?Ag"?;r?xu{o?-Z/?j6?\ Z?X?z?{\?'? a١?@td?%-?24 i? ]zDƠ?\9͗?{mI?9<3ʞO??Ym}?4L?h?l, *pn?0?uJiU? 6=?m[?9?:ᗇ?.q?Qh-S͝?S2?Yr^?De?b"?7Sh?`d{Ѣ?C,?О?Hϝ?uBK?C?Ρ(.my?|$QIէ?N_?|{\p??)<9??8J?CJ?.2ϡ?٬x?qIם?e.`ќ?[⁞?S7۷??:?? 5RE;?ҷ(ɢ?,l7)?̹T?By?_?r'Z?-^?G?q:\,h?!?dܟ?#q?tPz?,5 ?{d\$l?ѩΆ?4_#?+Ҥ?t;~v?Pa?zף?D ?W?Ӆi?? ) m?k?b85&K? rXZ? #?Q%?܇?JD?Ha?$I?6?GK?A񚩿?=2Ej??"6\?iEO憣?\YR?B?s=DX?yrtI?t?w<%?YhΡ?0?;˟?S?ch?HU8?d?x\? \?ʟ?>?{Z(?<Π?e0?F,ǣ?w??c!?)M?gP?s}R0R?Wd?psS? 4%?N ?<0J?:U]E?My/蟤?1= ? p?{x?9`?w)? ׋У?8P?|Oﴠ?%j˙?9 \?L?Ǥ?*?C )?5.)?a?"ɘ?|KC?(?@?`ʂ@cu? m?#@???t?-?`i sIοH?/?2&ֿح? 'ɿ?Uh??@J@? ݡ *?4? qĿ ?@R *Ͽ??׿@?|?8?J?`䏿iܿLk ?F\ͿZ3@@? B޿@?@:?v?@VQ\A?@V?Oп@(?@?QʿY3? ?OCֿ?@? B~?@տ Ųʜ?`x,?F?RZ{?yʿE?]? ,?Gȿ?p?`Ț$¿u?o׿ C`?y?Fٿ@͹?đ ?ݿ`4=@,D ? T@5? q?@~?}?g (ۿ??j)? :?`ӯڿOƿ+sJ?]?ɿd'?U? 7?% ??`e?@?NA'D?xnNwȿ@'?@@b,?X?VSǿ,?ӷ x`Gw@Dg?@jp *: *`p??wc߿?}?4 ? {??Hs?f[`m `OK?`mL=??@Hmj@R[?@g WؿZY?`C?@ a?ݿ2? ?@ u?`6@ ??(?N?dB^@ш?@ ?`DK?C I@w?tؿ ld?`ixƿ$ӿ@?^?@@N , @[<?@Y?O ,?`*?@ӿ?``wH?_O?!@?q?`?xٿ`D `@?@ ^7`ƿp`4?#!7tBֿ }:?ڿ@G? "ĺ?`?8h?¿ ߿}p?Eȿ@?)L?%?`ٿT?@?@X?@?#ٿ'K??@J?-A? .߿@hW@(տkɿ4@?@?`޿@{?`QԿ2?@ NJ?CÑ'?`ѿN^?@/tп%ۿj?gؿʿ y?@?ٿ5e@]?`ܿd?Z`\?`SD??-ӿi?`}1?I~V ? ?(? "??]_?`p?`?Fٿ p?(?Qzۿ@? *Loӿ?w @A?f? N?g_@`]4?ؿ?`M&`ڊ D׿`ܿ`3??w??U?s?@)??.F`?=`*h?] 57B???@{?l3??@/?@?@et?z?೿ >?`@L?P?`(?_x?@fҿ`|?b?5?@@W?`.ѿ}?@o}??g?`"@h??`g!?5?? e@lq?"uD?NbJR@;J?o ?`@l?]/?0??F?@U??=[.??@?@33ȿ f?Ev޿?~ ?0`@)?@@?Q@/˿@?@d?`m@? L?YV? ؿe? d|?8.?o.߿hK*?@S?@,?@&޿` ?M?0?I@?@?`hS?L?@?e?`0@rX+"޵k?F?`c?``@ F@}?`u?3?`?@Y?X?`??`v?I?``,@?`?`h?&o [?@?"?`_g˿+1`¿@?`{Y@p|??@#ܿ`@@RٿB̿`O/Sw9?;׿`_H?Vz?S]?*@u? ?k?`۳nп?@BNÿ }?D7oन?K?@T?ż? ҿL5%? ?`?cK?`Q?V?ڿ@T?3`d*X?6?yֿ@ܿ>(?[ɿ@8ƿkt?9?@Ʊ?ಧ?dr`?I?@<2@a? d???Ժ?#޿nÿ|@?o=?`@_@`8K`vM4? 6? ;Q s`h`f?)?E`?bIǻ7/?Sÿ`T?8Q lG u?@ ?_?5.ޗม??ະ? O<ؿѿ`]Dҿ?? M?@L@GV?E@@ѿ`xz ?V@>A`sl?Lj?ӿI ?@?rNd??v|?`??@:?ٶ?`?xS M??w?@(g?`z@ E? ?` |ѿ`7? e?lKS? [,?PI -ݿ'9? ?}??;@׿  ? >`[Ŀ 2q??1ڿ`ؿGs??x`Tݿ?6x??`CԿ@x?`7?N 2Z@Cx| i?!˿r*Qq?D_?@ 4!?]? GUܿ >x? W?R1߿`k߿ &п 흾?@o? ٿ ?`Q&׿~ӿu7G??r?п@%l?z?$}G?*-?Y< OEտb? ?`????B@'0o?2?b?@LI?a? ?k? ?gJ?d?@K9ɿ@p??Ֆѭ@i??{Mѿ7@K ?@? Bl?P?_ 5@mC׿@G/A`:῿z w1?E_¿(п~??`#ÿ` ??@jL?@ ? ? ܰ?Y?`@?P@F] U?7? YF@(7zɿ>߿t?3?<58#` `{?`ݿ ]?`;F@ٿ?`L@?X? ~?`gJZ?`ο0,?F?fп޿˿D ҿ?{a` ڿmix\ҿ`Y[?$y⵿A@`]̿??`jR@?m96?ൺ? 3Qտ?t?`??b?@l?+?N? "ǿJ Ӧ?`k?? i???`?ʿ??@;???௙Կ@?n0ҿ`)@d?Ɏ?Wv,?mR@` ?b?ֿ H?@? u? Y?@U?@Ԍ? `?`u-ڿ#ؿſ@?`W@X6?;@?@?>;?m߿@֍??@e?Hl}׿oҿ`?~\@Oܧ? H?#?aR? f? Y7? $伿z ߿ bϿͼ?)X?D? f`_8@`@x?&?ڿ?b@~??`ֺ?`[ÿw?-`yϿ??k?ο ) `m$?`@ ֿvnJ?8`x&v >ֿə?'ٿ`;'Ҡ?@ O?o??@`Zۿ!?$$jbտ@-r?/ ? F|? ,u?޿`?߿u?vB ?@8S F3bҿezſR?((?R]߿G?0?`s(ɿn/??O?ҿn?`2 %?`P@?ҿ`V6?R@`J?b ?$W?Y@RͿܿ&?WMʿ`AzԿd ׿3D?߿`? "?iө?`?@ۼ`?31`?|?8?@z?8?̿ %ο񃥿@b ~?q;@ u?@j<ҿ[?Z?Ƥ` @?ע0ʌ?Lef?@? @$H?@~?`˿:?X?@ི͕߿ D ]?@ ?ޤ?J?l?@ r?"G? `S m?x_?Q?y`3r?@ n?@_?@¿O ?b? ]Bʿof?w? ύ@տūZ?`#??R=?m. ?2lK??0?L?5?N?g`L?;^ ?c?YT g1 @׿.?` ?` O߿4N`]ֿ g?@ؿF?t? 9%?k?%y?@ ¿? ? ?!?2ٿ`c'?`?@pE?@B @`¼?o߿%q?@*@ ?_v3¿J?}T C??!? ?@ỽ? x`?`:@``L? L ??Ė@`/?m'@_XeпSۿ?`u׿FK4п (ܿxiM??Z?Wjih? k丿 &m?l!&c¿z?`g?@6: W?? )?ǿ9ѿ&[?@?4bRѿ@V?`ydǿo??@8? 0௬?`?J?`)h>??`3?@? b ڿ`YIÿfbK?ƚk?@P K??&ֿ@eٿտ2Ϳ@:uؿ@?1ۍ.`?p2?8?@敿@dg`b@|?Ϝ?` ?Ì? Pؿx?ؚ ʙ?`s!ݿE??^?[Խ?@?ܿn?'@Pa?H CϿ`8`'?`0+_`CBn?H?y??eϿ@*? i?7?g? ? |U?@@?`?- v?̨n?ڿOտVĿ@ d?`˿ _ h? :F`McֿQ=Bڿ `Mt߿@Q?@+ƿ?"`1?`?%qA c?[?Cq:?+?@ `? P"?%m?O?*?޿@ ?A? ҳ?W?s?Z?@2?hJĿ+`f? ,?`'` ?U@ s?B?`Z#b mF? r߿r~ ?=6@p?ӿ? ?Oo- ǿ6n?mؿ`4@? O?sɿD? ף]? @YLٿ`*F?|?`ؿt?߿`ˀ??@?`?q?X&{ֿ on?}? ?y??@?\:?q x@?A? :ӿ@?@ǞʿMԿ ?Bs?@?`_?K??p ?ؿ8ܿw׿`ݨ?@ :?9?rȿm?`/? 2@0 ׿@ O?@U Z\!޿U$ؿb`\??`DŽ݈`ܿ )8?? ? *Ŀ@>ӿo&?^?@@@8z?Կ ? Қ`e:?/ &@n?`? ?x?[?f;ɿ*?5?`:?~?`;ƿ`@ 9z?`* Z?@fJa?5nٿ ? @p?e? >- ?  X?@1?a? Y?F,?v?O?Gп yq` {|cٿ%u?I?RοC?I?@,$Eؿ9 `*?ey?k?B?'Q@ `?=?? IĿ`J ? (?T[?`??`$ ~o?+?9G.?wgҿ`6@?Y[Y@(?`n?mq?d<aH޿?@ح?@I@T:??Rk* 3M?`F=?ޮ?@ sn̿ ʿ??]?KQ@ݸ?Csع?I@i @?`@'ZAڿ`Sٶ ? T[??z?? x tzٿ.?#?os?4߿@ Yu?`m?I B? :@ ?T? @ B,?u?`a s?.ѿ6ٿo@t?c?@,?? ?ʌ?A(ҿ@V ?!!1?6տ`e?/^?`4,?j`RC@%?@??b׿õ@2?rU_c? ? ?@;ؿ`QrӿH"?2`@ҿܿ??`nCͿ(7? s8?ҿh?o7:?@`ɿ?@Nq?@ ?~@`?@?@a? 6? ο?? '?"o? l?`HGK D׿`?@ݿ M?ѿEǿ."?`e?S? v`^?6@t`B@`?+藿`տ ?ܥ?`eH?}cf?`@ }2޿+?@,?rֿ`zſ ]@ f?? "?5?)@0%? ƿI?@N@4.޿Y?P?a?e[?`ڀ??K"¿`SvϿۿ ?(U`?``2@1`?r ?`7_c??s@??0?`p@?? (ڿ? ߾?0@޿5?y? ٿǑ?H?Կ?y߿?  wݿ? |?YL޿X9??@$? %??@ ? s`& #?? K?@ W x?~v? O$? ?VɄ?`п?9Ŀ@@pտ@?9.? ۑK?H?0?`?`2@wܿ.] οտ*@^?`5??c`C?(?@??`(?`8¿ ?r ٷ?o?:?qȿ_u!;nn ӿp r?Z? m?.]f?Zx? z? 5?? v??n@`??&?c@~ǿ@c@ϣs?@`X?1ӿu?`p@̨j? a?@5`ݧ??*.?@߿ۿ@B??`#?ſ? t@ ?@?L?~+???`&;?i`?@ ?Կ8F@`i?@u 5)?Kڿؿ@Z?Kg;?E?` {˿ @!?@@O?`~? ٬>F?F?n?@U?|1??@?b? M?V?E?श??_ ?B6RsϿ[ ?@?xA`?@c? B~R?!?|`)? ?O Cӿ0?ӿ@| Iֿ? ?@?j?d? @`ڿ4Կ@r?løSſW\8? B?g?"/?v`~?`6? ?F?JԿ@%?@? G?`??w˖??п@ H+spĿ ?>@n?z? ? ?XH?ͮ?Aؿ@|6ʦ??CeRA?]b>Kп`-:?S8?`?t??#w?Ҁ`@@ѿ`޿4?`]?`\@ؿCп`@ h)?c0@?@@f?`Tٜ?@?` ?٠UH?`?@y@[A? ?`/A] ?`V?ƿͿ`C?]!q?'L@8??̸@Fٿ¿@?k?רv@`߿WڿԿ]? ӿ zh?@?i?`Iؿ ? ??`9lI@?п`@`Fۿ`o1@p? M ?WA?s?#ڿ+ ? i?`?? 8@I?@??@n{?/ſ@?߿c{?`rm `?:տ? R]?G?`m?`I@T?`?`{`CQ?@Q?л?1@6?t? ,|@*_?^? D տ)6߿ ? mۿ ?`_NU?`? * D0ɿ`??@kѿ 8?`xѲ?3?S??!@C?Iɿ %9׿ ?ݧ@l A?EzV?`<`@ ?s?B?A k?@wO?@?k?;? *?%?<ӿ \]`l@%? 7`'׉qrƿp?&`??dl?9?@S?`W?@ʂ?`h?@?m.`?2@?`B?@??A?`8ο#տ ( vٿl (@Q޿@g`?? n¿ ?`7ֿ`ȯ@H?z7fڥ?4;ɿҌ?{???@Ly?@?̿w?`Ej`~`l?r(?@?9@v? f`2?cu ߿,8?_?͕@p? ѿ`R7?)?#/?@Z?}}?] ? %`wۿBTȿ` j?S??`˿9Pݿ-?@?}E?]s@?ɝ?v? `/ B|ۿ (|??@V?`!Կ@ ?@ w?`???A\?`?0ۿ8?׉? (?Y? > E@|?@?xw Q? k?z ?) y??5??@C`?QwH???Ҏѿ@Yy?- ?@ÿ@qտ`7ܿ`Z+?x=? #?} ?@+ P?`=@`dҿ'??`)? [d@?`VӿR@Vt?`z?f?@U?? 촿 /?f? Ǒտ@q?1"?b?sѿ`jZ`@D? ?@W? aU .`? n?Ѽ?ೢ?dݿk??@(?? ?q`?g?!ӿ`(޿f?Wb+ȿ`a@"?@?`,?Ǽ;?`Yպ?`wL? 0?S`\??W???@?տ`??ZM?D˿?[?`ѿ?O@c ??@4?y+`nib@R? ĿԿ `5@oɿ`n?3p?`;\?@ϔʿ@ܤҿ+ӿ[?ſ@eӿM?@k'?=?x?޿ pĿ@?4?t?@`t.[u?п?^׿X?mw? v30?@*?@E:^?`mϿ `? `w?N? u?̿gY? 33@`nؿ`|?il?:}LϿO` \}пF@z(?`ab?@T?R?Y?X?W?? A?G# ݿ@ݏ>Vȿ \@>?{:*?fQƿ`FO?? Rb?h@? /Yֿ [f?il?+?= ?p?@P?`ki?W? #sZ?@|v˿ @ܘݻ?Y?? -?.S?1)?2?ֿ`ot`{? ??@Gl? ?b?`@<@O?@KSֿ`#X??`[9??@r? _?ēz??@ ڿ@*@EO? Qu?@]Kۿ??s?@x@fJ?ڿ@?Xѿؿ`P?CտX@ֿ W( ?``???Cӿ WX?^@b?X)?BϿmZ?PL? ?? 1?4R?@?sm .?HͿ ~~ ?`1H?? *ſs2?s?`7Y?@? 3%??@ܿ?a޿`? ,V?`Z?@7?G?Ð?п`j@? @@`?!@+ғ? "?]? D@C?!?`׿@ %ٿL?`?Ʃ?E!@, q? =? >YĿm?`?1?ͿT?lC? Y? *?`@ '?`/`hU? ?`?߿? a?8M??H ?^?`'ւ? 9̿2 ?@]?@Cju?v$׿se?@ ?@f޿`?`:?Y ?`=?ڿ*?@;? 9? ⑪+? vE]z&=?]o {r?`Bl? K ?``6?Q`?$?6?Q?/?`F?0*?Jֿ@N? Ϳ Կ`U`fpTY?? ׿ѿ?@ؿ`ج?@i?q? @' pyѿOX? |L?`?.i?`؃?Z>?@wWÿ jN lX? $?.?7?? 5?!D?.@Ǣ?K??`!`M@>?n? ?=,@5? ?9?kL?Jj-?@?`q?`?`?))?`?ɿ`׿`ˇ?@?|N{޿?%'? B?ɹ?@?`ȿտ`N??ѿ˿ Sп ?D)`FŨQٿdG?J??]ֿtƿ? V?@ģ 1?s@ b` ?@aӿ? J@? ?s?ѿ wO?@#?B?`Za /?q?`?p\ѿ? _?@0?`ي ???`?9gݿ]-ֿ*?hd?~?`@N?`7%@e?ssX?wѿrֿHo?O?B&?^7?\?@? #?!.wͿ@/?bͿ3Kտ w?h?a? _޿ע?w ?B?@? g? ǿ`yo!?@(? '?2&]S@@?`8O1ٿʿ`?@`?q?=2`?/п`3ڿQ'? ZW.x?5ۿ ƿw@?` ׿`M?@#?"-?d/?(B?`?{:ٿ` ?nϿ ? s??ݤ?@s@@Y?1?"?@S?bf@?'Ŀ/ k?@QѶ\?@!?$ؿ ?`i?`Vѿ@??o?Q?`z>?`4!ֿE?䕶EFn?K1Կi?`??U?VHݿ?xm? A+ҿ0?!i?r@e?¿7ؿο ?ſ_9??hg5?`Y?}?ؠ`??ԫֿ 75'?$?`?`4DYv? (߿ z?@dJ?Wɿ@cZI? ??jڿ&5aѿ 7Ϳ`ғ?s@)?G ? ?0? ?`^ ? tiMֿ`>޿??̿ ?@?@? ?`? QI?@~@q?'@ pҿV?ྟD+?@&?`?$gȱH?@kնN`b@??&?< ?J?+?j?@B`D?@??@1@`2@{?Q?`8??j?v%? J~Iտ@M?z @?޿5^@cѿ@kf^@`=?O?l8?$?Z?@?@Ҥ?iԿ #?&1??@ֿؿ`G?`'?.@K?e: /?@??@FYHb?@Z(?ƿ@~`)?t??} ? ??ܿ!O?@ ⬿`̡ڽY?@ֿ @$ֿ@i`?"`_? @ B![ ?IԠ? A@ ՉӿP?96 ?ݿ`r@???v? `?¦ֿ`Uȿ0W`k?? +.? Q? 􃞿a49ӿ@(ſ0п3? M`l? ?`? ?eui@n? h:ɿ@M(@@?`Z@9p߿`̹@`V@`$oB?T?@?AH? 9o?q?`3|@K3#?@|V?`$_`ܢTS6T (@Y޿N@d? 0,?BM? ?T ӿ`;? O?`xr+ȵ? ܡ? @@nu???4?@ ̿`Ϳ|޿`0ؿ? ?`, D`j? }@p`?#?m@O?x?|??G@ ٿ`?`Y?jϿ@: ؿ@U_ ? hɿxڿ)ެ@cS?`??@j? ? Wݿ@?2?`=?SM?D? l`& ٿL?m??<??3¿?6?'F?`9a? xh?`` T.@v?L? p޿ֿԿ?`@X?`?^O?`Կ` ? ?$?>?`xA?@Ɉ?LĘ ?ٿ|C|?@&??<߿?`g?`3 ƽmU?*I??`J?M#? ?T ѿ꘾?k?? w=?@x?@L?d?];@! ^~?B&`iC?$?ѿ?@X? *@e`5Gؿ gۿ`?u/@i?#??N?@-9?4?E3H`<{?ຩ?@ ? 0??@'@ 8`jƿ`@?^q? .?+?ӿP?gW??@ϻU?`o@)T?qu? >?rϿ ? }? +ͿS?3U&?` ?-ſN# @ ]`粿?`̈́#gd?/|?@el?Lȿ@=տv??%?`ȿp??@djfY?@Gr? e`Y1?@?`z.пq`4@ V`&?@ؿ`~??? ?տaF? ? ڿ? =>>?`6Q?f?Pڿ rS?/?@1?@ӿ?r~?`*(ƿߦ`0]wտ ٿ Կ W?ڻ?@˿K= ?BW|ȕ`Je?@$??`ks=ѿP6ƿi0?1`׿`Yd?@i? 20 v? [? ݿG? -sڿg??}c?1? ??[`ݿ?? ? A?пV $Lfٿn7 /m?8A? ?% km@f)?fʿd?k ? ^׿`y ?RT? K @<@@7? ?`ɍ)@@ ȿݪ? >׿ۦQ3 ?ڿ&"¿`d?ſ"cs?@h@?~@ Hn`?O?@OG??{@cxlݿ ?=?(`v߿@Pb "$?@ֿp\? ?:_?@U??̹K?(_? Mt?~U?o?c ĿEF?ש`&^?~п ¿I?@u?ڿ`@?? H? ??l`? u:?`sۿ s)?:? VLۿp׿@1L?@l?$??K?s?`@{տ?@??`'ؿ?@V{?@??`Exl¿`?A "?~? oɿ`z-@9ಽ`?@J? M??`hA#@?`ܿS?e߿ M?r??ց?@9?K|?@'?p?r?`?yE?o@o'?>?4ۿVܿ@`Q?@c(?z ſvS׿C? ? / CD߿@(?#?dv`>f?H?kd?o?f@ DS?`eg?@v?`Z@wm?o?(N?`?@ƿ? PΤ?o?޿`ؿ*ٿҳ`Q?K@?@a#?`K?^?UT@w3?@`2`mZ ޿@u'޿ ɋ?+d? ?/?`?c?@5?`ؿٿ@`(һ?ʿ<`? KM|?\?=Dпoy>?@?t?`*?F?b ?@ S? G? @F8?`*ѿ@V?`\?`R?߿U?Ժ?@H1Pܿ ݿ|@M?S? ?@??"?<%?%?p?@ֿ@?C_? NA?Ny?`?@?^ ?@ Ͽ Hm? ſY?Ϝ?u?XL !?`#?j?`;@fsܥ@a`)?}.N?Bſ} ҿۿ@e 9oܿܧ  ?y@(ǿn?9@ sZ?H?@\Y?-?P?`H?@?)?@ۿٿ.?`!??*?@l@i:n?@տ) ſ8?$?`o?`j?{?`r?@sp??j??`E?A(? ?@k?? w?.?y?]˿1!?f@sa?`3@D? ??U? !ۿT?`'P?H9?Œ?"?Sb`?]?3u?ǥʿ@f *? ? Xԇ@?u`N?R?k@] @ ?C?J-?%u{#? ?/?x????0 ?@ϸ?@@:Ϳ .*߿?c@D@%` g?@+*?@޿@t ?z?`6y? @n/P?`n@?`@MEB[@?`????ඖ?@n"u+?@ ?i?`j dCC+?=rg? ?`ݿ1*ӿ`?@?;-?O? \ 2??C?4?E? 䆴[ *w/m??@???`h ڿ q`¿X As?@Ok` ??u:׿9?] Ŀ2??%??@H-?Yf?@c`?`V޿ॐ h&[??@+B?9?@TZ`@@_?8ܳ? 3㈷?k5?`8? V?`\?`N?@*?oL?`6@`^~/?N? oWa,?nֿ??>? m? O??\c{?@?@T޿0^ ??=?%1?c?`@)p?`p? 8?,??q ݿ1-?'~?`(?w*ٿ` ?t? ?`Y?  ???@!`_@@ۿ୮? ?i? V?`? ??l]?Bѿ`S?@`?H `F̿?0? m?O8?`8??z}?=oֿ>ο?`'5?a ?`B?Y޿Ne?`? ?``)??;Ͽ> ?`syſ`hf@޿k?`?`?)ƿYѽ{`# :w? ? ?l ^@L`? U?7yҿ B@)ǿ  $ؿ [`"?g?u??`޿<? 8?Ahtۿi+? I@??j?K?LW?7@K?`s?@J?]``2]̿q ?Z@f?s/?+;? ?`0?@0޿? ?ಲ??x ?V@`? @l@? 9?`?J`X|?8?@VPֿ@ܿG? W`ǿ O?`vٿ@? ؿ :?cſ oƕ?@,;o@@?R???Q?=Կz?ٿ.ҿbl?`?i?Mb?? y? a)?9O?`?<ڿ@Ng? -:?TX?`~޿;[ ? &z?U?L@@?b?`B?ǚ?`q?[?@+¿@r?`+?o@?]FVv " p T? ?m?`T?`[ oſ ]N&?@ÿ~??k? %~?0@$?G?8D?h?``4?R"? ׿@)?QD?N? e9?@ >@k?v` _?@@?L6RyJ? 9?A̿f ? ֿY@*??`/G?m@0_?`{g? ??@w?ca?滿?}]j*߾?@ `%`E@yA?@~_ @^@{?Z ؿ ?1ÿ2@a`F? RoK@"??t?"? UݿA0?@EA?% 4? ?j(bȿ@8 0? ֿo?*x?@k]ۿ@ @ Y`?@{qſ a@ 9e?`= ?`@`4@'տ %?K?ٿt?`` .@8u/?? )?`_Z}??}[ ?ej?t?S[??ອw?w?A/:? Lƿ~Կ?@!@[`{h@lH"? +?@? D? L? @qqjo @L>@y?&?ӎ?`L?క?]?>{Ŀ`?8}ݿVs@xֿԿJտ S)?Xֿ 휿%f˿@? ?@@lӿc??R@'?(`j`e m:S ??u?&Կ`ο  ?w??o8ҿ iF`ngڿad ?`E"??@?U]?@q?`.~? Ԅ? ۿp@X?:m 8?3rۿX? {?o? ?z?? @8? y?A ?k;?Q?Ј s`XtU? Ѧ?^?"?H??ߌ??$?{g? ? 8?[@;/߿!ܿyB@iۿyn@is ? 2?@ؿ]Kѿx̿` ?$x?iJ޿\;? @??@ʿ@?@J?2{?Mv?B˿^?`P?@X@d?0P~? ?`@v?@?S"?[M@O`?`?@::3?@?C?>? L5??qC?ˇ``>h??`h̿ۿ@?@%ҿ@:? ѿEY ?qr?`/t k#ʿ 4?l?%? ?]?`J?} Ӗt K?!?`Eп qʹȿ`&I??2 ?k?_ο E?Zhl@`ҿ6?`Py޿"@??@9ؿ`$X?uҿ@fdG@ ^?`}??ubL?`?Q4Z? ?CM? s?d`PҮ?@#z`u?ܿ?C@S4?ƿ@ ??@< +??(? ?k(Կ ~?V"@?O`>ѿ#8 ڿ\?<?@k`?|?+/?X x?vr@?@T? ArB?R?F`?`-|@O?a??ü??@'?? ݵ? D @`?`7? ѿ@ &ۿ Bl?`J? C{ʿ?R_ @|޿e9?)l??`?@@`?d?Q1?@B?`Ŀ`\`\tSc?n?I f@ @:ٿ@@]! w?@5ο`ƚ??G{?vο?e+?`p?@p?@ `Ŀ-cٿ 01p@Oؿd^`?Z? J=? Tܿ??@=&?zʻ?X?Y?ÿL\?@j"??^¿@? Lۿ V?j?iʿ@?Eڿ`? &?@s Nc?`m$?#? ^Lѿ??@]? |?P??$?PK?P?`?`i$?>T?F@% L?)׿9?X9Q? ?`[ò?`Lw?$L$#ܿK?`?O'?`9{-߿e@/碿`y?T` ?s??`/ ƿ@$Կ j?.ۿ޵?+4? ? ? |x?~@?D?v`ȿ?? @? ?`|ڿz?`ֿ??Ž׿[?[Ŀ? jb? .?!? ? hտ ?Ӝ`@@?`ܿ`- ?: Dw:#?`}? Կ#?? @@.?@w?6??pj? @}? y?`k?ֿ { w@`@ =>?`\??n? uʿȰп S?@?@.?? ??`M?ż?pY?`ư?`ϼ` ٿ ȿK?`??ӿ2` 8 ?@?Y?a? ڧ?}(w?i?<$u?@?@?_ e?`?0?`?`>=`4ee?w? ubɿ Y87?HͿ*?2N@|? "?@C?k?& Ki?,Կ?a?пY? uĿ@ۿ??~`п4`?Aڿ`w@E?v? [֤? D?`l?L?@*@ ֿ wc?n@`}Y ny?߿ '˿OE?ϥ?g'ٿP??Ch@ ?Xa!̿6 ?N [Կ[w?D??<@؊? J?@&mп s? ?׿svT?֎?I,?9}HοU@1?lk?g?۲zQ?WH4?^q_@@]t? ??1???@oҿ!?oؿ`/߿??@k? 'ר?4?{?@@?P? ?2(க?=?޿@3(?vGs˿< ?_ȿ?`IHۿ ?yӿħ?:ܿj6?? 6u? ?w!@l ɜ?@??`@?p?@Z|?@R??k@fa`;ѿn@ οSֿ@zs?@Կ@?@?@.?e?ۿ['"ݿ@e?# ѿ P??I?@Ͽě`@q?2$?v9`^ο?@y?`?`l?b?`@n?@yɿ `O*p?Ӑ? $޿ࢵ? տ?e?78X??1 ?Gпj"8s?©?ȥ?6? ?i?`~?#?`[@2 ?@???@Nj ?` ?`' B`),???~o?Qſf2?]?`C????>?`3$I?? 18)?@[ҿ\?@ ݿ@ZjBڿJ?`k ` F?@?|6M?@?ֿ`?By? N>?_m??@V ?? ?ܿa=?S??+ſbJ4'? TǶ? @? z? y$?2?? ܿ@? N%?@@ؽ?vI JTƿd`?YUT?@?˿)?o?`?ۥ`?@G? ?L@S?@u8zS?Cڿʿ $ <@տny ?J߿Y!?`? ;&+Ŀ4?"?}?ۿ{k? ?6? ???`:?`N?_ǿ ?~yſ ?@p@ÿ@@M?d??YA5Y ?Wÿu?jɿb?I@C@=?Vq!???dп +``"3?cпEC?? ?``ӿUd`त?~(ǿ~Lտ`p?`пz? $pjM@@jٿO? ?޿r? c&?8e޿x ?TyԿ@s"?? ҿ?`oɿ}?@XB? @?`2?~?Pn`z?`@ݿ0? ª >?Q?`?D? ? ?z!b @#6?@?iJ@L? bn? `B0?G ?5J?Ya? Qv?`H?>?`uy` ? M?b @/`;?;?J$ࡐ?`z??;?WUӿ HI~ſѿ@QY?fwǿ`?`K?QϿnҿ |z?`տ`xl? F? zGE տi`"ʿfҿ@ 5?}W8?@?ѿ`8?ir3s]?6ޛ?z? ?@L ?`? n?տrl?@""F`ſx?ɿ?I?6?`:?W?ѿ9?zKO?HA? ??>Կ?w?UԿ@o?@ ?b '@?`? v??o?@D?@ɾൡddٿH@B@?@n`n ӿ`z?@пP~? ?=C^?s׿`J6?ƿ`ӿ?N?@@ཕ?xP@[@ =ϿP?n? `zQ?/~ s.?d?@MGt ݿ@Jj?+߿`#ؿ?` SĿv*}?`i??b 0? 7tA@??&??`Qÿk?ࡷ@WX?\d.?@@?? tԿeٿ{ƿ@h@;??@п@8ȿT Ȫ?,1?@y???{[ݿr?'?@Y?@,@+Cu@9?M@D,6~ ?2?+?@?۳?4=ϿD7ۿ4?`-˿? uuƿF:h??}"?`eؿ@??Lﲿ$6;@r??`*qP?7i?&@Sl@[xr?Z楸6??{ѿ Aָ@?`ÿ`n@ќ?(5Կ@??@?`TͿѿ^`?>?H@@ƿ@6@wٿ?@(?@!7Կ.@`^?@>``Id?*?`8?``;׿ݽ?` '|^? ?X`׿c t? qǿt׿Ȣ??@'?yhȿ ÿ?.`e?ۿ J?7!ӿ4C?0S?@j?@?9ݿ(Hӿ曢&? q5 ?%?}Z?M?`??? @@?@3@?/տ 2@q?֩?h? Mٿ`@&?@ ?9_տe@E? ʿ,r?RE? ͫ$? ?ѿ@?Cmҿ2{?}?@a? ? G`_;ʖ?@L˿@ʹ? *?`j@X@ ֿ3t`^Mʿ&?V?`Ŀr! Q?`C@B0EB˿@ſbѣ?`?@Mҿ п'>D$?o?H?`?xDn +?`{Ϳj?=?@\u?d ?o?f? ?b*@ ? `(տ?@1@?`*?@E?? )`?r'? b?/w??`:?@ߢտ`|?@޿nA? |?}?{c@`?Φڿo?P=d߿A0?ѿ@#ֿ Mݿ4z$?@d@&?u;?x`?'?@-?@m?qz?>.޿ пA?S?w޿ de!?o??Y9?? 1O?ǿd׿~?@?Q??=?`'? dٿՔ׿ Ѿ? п}?q D gW?@c^?J NU z?!m?@(m? ?@@4?Կ@p?j?`ش?u@?@z ?u? R?F?O?@y;@ Cҿ ??f@~GÿT ? ѿ @$ڿ%?@¿V?B?"ǿi? 4ݿ?p@Oc? ?@g@5?A4/? SڿwE? T@F?r@Do`#?1iO?`,@"Fa?@fUԿw?p`}t?@7??? km?? e?e??+=?v2? !%tտ@"?@P*V'$@?nֿ w ?3?@{?!X?A?`2? i??.`?@vѿ׿3H>p? ?` D? V? P?@m@R;? ` .cS N'? ֿ@g1?@?@D⥿{пdп`Rۿ?cz?`"? z׿R?!ڿ?@8?^?\@?.ſ4?@]?DR ~? ޿«?m߿҅ƿ`:lۿ@LK28@^?@@?2j?@m׿HZ?O?@?`u*Ͽ?`ː P ,D?@ޠ?SQ SN``9?{?Q?`b?`?*è?2ؿ8 bſ``Cǿ>ؿ/"?A߿`g5? ܿTֿ?-`y̿4@ c?[?@r?&~@?9h?M?@Nj? E?,?l`ֿh쪿2?@3?`w??t?`+?ߎ?? @? `?`?Z5??[_p??mZ?;̿ b?)`b?m- e?K? `%M? Pb?`? ? c\=?Q?5?P?GUݿ_r?~͟?޿^п? &?@b??LD?G ?qC?,?@,ů ۿſE߿ki%`?` f? ?l?ֿ?@!?c?(? 5 ?`l?`8?`??@O?Z?^?`y?@?տ d?kÕ??? ??W4?`? ?n?] =?.? @PҿHE? P?ۿ'@?{?X}?0пIb??q?`j`ÿK?`wIJ?7@"ο ra?@ɿ j@` KL?@?`hjŹ$ƽܿ\ԿF?ZW@[d`i?`a?F@@? `??ۿ@OA??@_ ;?@?`?ij?`?n?ؿE?S??@޿`GMP`#??@2??8@ \ĿKcٿ߿ Yr?^[ÿ 7?{`5@?`dlܿ׿``ۥ? w`Y?aYG?@^? {KiM6?@]? y,?K`? !|k?? ?d@?`6ݿ jW?@T?W ſ@tп j?3oӿr? м?@\? *`? ? JϿ`)F#ؿ?֮ſ $ٿ&R*`P?0`*)?ݿ;5(?? .ݿ@O@MU\`?V?`"r?~­ǿ 82@[?`?Ņ޿ถ?@?Y? 3@Z?`'mg&??`/ ߿5ؿT?@ ? j@`2?D?F??@d??`"?@8WB?%߿ \ ?`%? ҏ Z?3L?@?`fӿJ??E@[?@?S%?{@`۵?1p?ߓ>?JF?8??x?r?.п` P@ڬ̿ 3?`?rDѿ@ Qf?п Uӿ @@we2?b`Q``?`?? `PUڿ %T?`?`1?`?'d߿@\-?$?-???|w?? .א q ԿѣIտ0 ~T? ?@?Q-?i) A@߿ ʱy?@@0?` `&ۿ JT? )?p?. ?1?w?à?``?@83=?û?`7?@Q?@N'?@u\? B(0? ? @}`ur? C?@?w@?s@?7?.?"=?`c?`@uK?@@h?ο~X9@D?@ڿ ?p`d?@Jֿ';?LT@w.?qο`? k'@&??"1?(ָYW??`ܩп@?@_?&bٿY? ?F`5?`z?@xoyÿ@ ~?@?@4~?#t@OŨ?@rԿ@|?? _?$d?@? @пm?@?`?@e?` r@$x`w-?@?X2@F &?&a Gܿ@N? `?@?݄j?`?Nտ??gA?^ ? ɿ.Կ@0ɿ@ ? s?iD`ё???Q?Wǿ_I (??`׿@ P??X?{?@Tڿ?y?'?aR??@g@60{e?@?0R??`*?:4???-?` M?`t8 a^v?`h Ϳ@C`?Aۿq#? ?@?`{@Կ k ߿̠?j?@*[E`z?̿`"?Б?c@? f@`r߿׿T3?`?տ5ڿ Y0?@4`U? ?w. 4ֿ߿`D@#``D˿^ӿl$?!)?n@?`@8ֿ?H?T@ ˿?@?;?`X,?`Ŀ`]ӿ`_?@o,4?@<QY?Zc=?v ?@!i?`"??D<~?`@Yӿ3?n@W 2E? E_c,ѿпP?+p?ax˿~/ $?;?@{5bֿg? LS?? #F?O)?1?@{?@@ Q@K?`=?1|?ɿ?P`? v~?`5?Ld?` ?)2?@d]^Կ`_߿@?F?G?@SZY ?@ ?`?`>?5 @`V?`?+@@^ǿ1?`WXƿ`V?h?i??G? 5?? %"?@???oEv ?׫?`a^?ǯ??(?`7ݪ?*T?@g`|>?zܿ@=? 2k?N@ Q[?%$@M?? ߿1?`g? p@X?`?z? iۿ E?? ^0??@ -?3Ϳ`y?{?`4" X?`ߡ?@?T?lӿ`g? q@1? (??(?Me?@fɿjҿ @>?`V?_?G; k@"@ o` ѿ`ڿ`=̿@U?@C?ۿ? y?@ܲ g Y?@? Zw׿M@ w??٦ Vu?@Fg?k0?dM@0׿ kJ?{@/? Wӌ?@X?`@ 2?@޿ஹſ?z?H$P?Ǻ? >ӿH?@t?T?`ɿ d?? ֿk??!?n'?`:Ͽ@0Ͽ@:?g ۿ@ۿ?`?? ؿy1g8pȿ=??@ݫ?Y`? 5?`@ߺ?e?@6?@@`P?ŷ@(?\b@@??J?-s`@?k/οI߿[?@L? @ָ?@*ƿ@? `fȳw?@ٿ ?`mƶ? b! u8?`b+?@9?Hgh%o@qm??s @ ˿??9?@7??8?@??n7jNؿ`g??%ֿ`ށ?`z?Dz I[?@'j@i@`R6@dV$ѿ A?!?@?"E?`;ο Fٿ|ڿ pH? (8w?໷?kzI?@)?`@ ?A?˨8T?@v7?hd m?@e?Gڿ?@3ɿ n?}?a?hl?$x`cYѿ ?cT?PR@H ]o?ѿÿ?lܿ]c? m?T`]j`!? x?@1~?]?5? ?@Dп`?Jۿ? ?Y?Hǿ&+?e? uʿݿ@ͯп ?a?@E)?~`??Q?@E?ſ`?&??6?@Y.@ `??qƹ3?`o˿їۿHCc?6?.@?@Wz? M?'@,e?? EŴ 5;? ^@J J׿ d2?3? ?1?@"?@$L?\eſ2XֿMQƿX Z?@]G?`#?TL4k@@?@6@e?E?*>? Vv`4?4@@Z?s?M?@տ@fg̿#@R? 'l?@ҿfl`K ? ݿ K@K?W???`@?:?aο iҿߚ޿ɿ@&?N@`??妿@,@ӿ@ؿ4?`?Eݹ4 ?W `]d ?`Ŧ? 7=ۿ sa?n+@w?үT!Ϳ3s??@ ҿI61?W9?@Y?`͛@nֿ@ ?l`?`?:տѿ(?K? $X?׹?`x `v.??H{p?`]4ྖA?k?L@?P?`O̼@S? ?aο ?N[`?#0h?^?u@=a?  ?F?so@@x?`??o? N@0a???Q?پ?@ vƿs?b`q5t`. õ`?? :??? D?Z?ٿ =@u?߱?4?Kٿr޿ @J?N9?@?`@?B? Õ C?4˿z?Az޿@Q1??@`?l`B?@t;?`RR?fĿ{޿`l?$@?a? ׿?@ :?`"?@ ?yʿ?Ax?@T.@ֿ SпV'??׿.?!? x@ ??}@-?? Ͽ ݎѿW?{?U??c ?ؿ" ? 2޿? ?@?v? ]?`?C?@?@? 栿 ?nj?@?1'?`z ?`>[_@R?`ǿ`uj?`ʯ?߿`1@=/?l? :}п e3ҿ F@t7" mU??B?pֿ@R?e ? ??}7?t?ෟ?u޿SB Q?`@ۿ`.6׿@$?ݿܿX?)ǿO?ю? ~??S/ܿ?*Y?@w?C? ;@W޿? Nڿ@O:?|?gʿS?? Z8z? |`,?Կ£ֿ9??@_^? ?9`?K?E >??5?nEe? r ?@Կٿ ?@a?ġ?? ׿ -"?_? ^zj "? Ķ?`? @_}տS?" Gg?`-??@7L?@E?`z@`?Ϳ@? @~ )ĿY?`?0?( @[hqTZ?`@fmۥ`>?Yѿ`}?@6? q?`S?@BiN? Կ nɿ ?`?`ʿ?@@ Կω̿?:;?`ҿU` F qj? ?e? R߿+ [P?D?K?@ $?z?gbֿ?@U4?d? Q@n? W?@@@/?@`??͙Ϳvʿ?`"?? ZܿFP)#? ?c?@Ӣ?i.ݿ,JB`?4?@`@~ ?[?@͗@`?`]?@ӿ`Ο??6ݿ`XT޿U???`WsD?4R?@%?`J?,@@ \r??@!N¬Ŀh?`-?=?`6ѿdU?6? ??@ ?{@Ԇ ?`?`߿&?y?¿఍? +?@`hG D?HlŁϿz`q?`( ?`UMп`;?F?o(Z? u?);ѿ@M9'SJÿ`hJ?`>Z'=?`e ?տNֿ4P?H߿`eK`޿M?@ ' p??V?Z`ڿ@J?`??? ?@B2ܿ@#?`M?`BN?Vſad??޿R̿@{ۿ?;@%^`J5e;?ٿ`@??W. g? 0?@?@ƿU'?@S( WA?"ӿ@?@9\`uܿ@x? `e?`El? `8õ?`!? |?)?9/?? j,? vW(wտ=?v??`?P?`$0F?`_V`%.?{ӿ8[?`ѿax?* ?P˅?Z?@ްp? !`q7?`bJE0@?a?$=?^?N?@?x3?p`?@1?.&?z)?@zd? mO?`Կ@>K@:? m? ? '@j@Gg?Y?@o?@+w?`m )?'ֿ @ o?V׿`?``?u{?olt?"u? ]л?@bG,As? A?J3?3?D?? CR,86?@Ϳ@?qW׿?;?㇮?]@?`y&?@8pI`^?IU˿ ؿ`܊?@?@Ҽ@=?b?TU??@Wh?@?_?Qտ`Y`v]?@r1X`-??? ҿ ?E, $?"???6?G?!?`?7?@}@տ@˙̿?d׿pb?@I`ؐ˿բX??Aڿ qM?F`[?n?p?c?@@ſ`1AwK?@M5!Sÿt 7 Vn?*?V??`?@!=?į?`C?@ZҿGTt?I?~ho??_?t? B?`[6?eU?`o?@7?@Կ ʿ ȿ`?Ք?`Fۿ?`ѿk?y=N.?w?[?%Z@?@п`?@xп%K?ۿ`?3`W0?`!?@. Y?6? Jaҿ``1A5? J? ,ٿCɿ A޿ C?z`Ŀ ո? ?Z??_*ȿL9ѿ @#Q?U?&[߿فo@ ??`c? ? ֿ &p??@?` Ϳ˿ FG`?oA?`? տ@ %??1``ɿ Hu? ?W?B3?5ҋ?B?丿??٦@@0?`?J?Oz? տ@"?`?@2¿,?`k_?3e?~P?-@@ F`k?V?B? @?u?k5?`?]@@)οvv?`?r?@ο Ͽ@Lο?`?`6?Dj~Pҿ v? Wg?@e? d?`l??n?֒`@Rڿ@R?$?p??` ?jW? ?a`?`eſ[˿B[z?? )`s?@G; ?G@?L?{Y?`?g~8 S#ௗ?@ ?Ч`?? F}@ܿt\_`R@@? ?@2`/R?!f? u ? `޿ u޿ sҤ !y`I? 5q? m?`?U(??l?G?@S@L@@ѿ? `{?`+п`<`oƿ ??`?@w5 @ݿw U%i??ޭ ӿ@?+? Z@hz?`+?`@ 9?6K` ?f@ ?E? ~??!B??@+?`?@.}%?`9l?> ??Ύ?`5ɿR?@E? b?h8?@ÿ FYf?O?Jm?`o?vRg? Ŀ@D%? -տ@@@7?? ͪ?:AMw?x?7??NM?E˓ 1*ҿ@Ŀ@?2`J҃? R?`C¿`mҿ ?`z? g????x?@1@Ûݿm`^ ?V k3J׿?ɟ ?`@B??ÿ A1ѿ?@`F9c`?L??1?Klƿp=r]?@i?`dĿ ]?`(??[Lٿ p??tӿ%1?l/\`x?п`&@A?wQ?Ǘ  a@w޿RտO? @`x1?\Ϳҿ@ ?@?`-?0?#,@? X?B{?ȿh?>?`Ќ? ??-Xo'I}&#?jȿ\?6???`?&տ? S .?@S? ?@ @I?_?Hʿ/??@y?Nѿ?`.Pп}*Er@ ? ?Z?=1?cg"?@C˿"˿_?`ٿj??ſෘ?ֿQͿ@3???ٿ@,ngV? ? ZԿ G? ?@?` D? 7?3@`CWM`"?@hO?@" rH G Dư` ſ=@=ڿ ޚ ?@ ? Ҷؿ?`#ѿ@e?@ @@.?G+ӿ^|?`ѿ  xA O@뚥?ๅ?d?@2?(`U?!? D?TSjÿ ?-?ʿ?` ?` / ˿@fU'?` m?@fȿ l˿ ٧?Y?Z,H?"?пӿ`v?) /n?ֿ`T׿ ӿ  ?@?@C ??ۿ`'`μj@@* ?Ͽzӿۥֿ@@`~?@l?ZB}?ؿ D 3 ? ?f?6?Pп??+c?@`^??M@? Y ???*?A??@? D? ~V?19? ?Iж~1#?? W@*?@ ;?`̿9? ?gܿ ѿ̻?<@???`wB??%P s@G_?l߿?`c=տ ;NGu@Ξ/`p?c? ?K?b @q?@yȿ̿ @߿`@?Q@3?տ@G?Tҿ6пr?]? ~S`8?Mz?P? )?ӫϿ@1?`ٿD ? rҿ`i7?@N?@I?K?@Sl?P`9T@MW?@X?`v?iI?`? o?@)?!? .?'@gc?ɿL-?``\\?RQ? V? ?`L~6R??@տ?`s?6?`_B`:%z?T7? ٿwg?Fo? O@$?ca?Q?>vҿc6?j?`e?Σݿ`?\п@ҿH?@v?@i(G?@? t@ U?9?p?@LG- ? @!Vx+ߠ nȏ?@? ?? `>2? &?"?ڿ}y ︿?n޿ ? ?~?7?Ѵ?1Ȕ ?ɿ ˼@?ԍ@`ô`Z*?׿@??1?m@=˿JK?@_t M|@vĿ`~\?x?@e'? Sh t?` i޿ҿ` ?ƌ??@? /? 1?ݿw?*?@]f?`??+G K?`?8?@ Gnn??ͭ?\I ??H@ۿ@,Կ`* ?? ?@` } ?@?ସ?@g?O=??`B???`?`;?`[?jу?C?3 (?@?y`B`ad??XU= pxؿ@c} c??@91@ऌ?[ֿ}? 9.`?` ߿ ?J@ џÿڿ?Ե? ? Ŀ???J?_ 2?ο@<>~?@?`\?U?]?@ stӿ@l?`^,)@Xտ@s?@E׿7 ?$??#Aڿ$2`J?G?@ױ?2?L߿`K@߿7Y?#?@ @I}b4?`)v? `?@|?@/p?Cֶܿ?bK?wNL?D8@c`[ ǿx?=?Dڿ?@ ]?@,@[οc`-? ???@ѿ}?@?-ۿ 47?0???#?@2?@wB@?@2?,?@?`J߿nҤ?`aԿ?fC?6ܴ ?:ֿ :ؿV??`_b`r)? D?R`?fi??+?@V? V 2п`a@^m`ƌ`]8?۔ eخ?@ԩҿ ?8Z?v}??`?@JпgU ҿޮ?@M?@S>?vxe ?(?ip`V? ?&)@p?U`-`W3(? ? @f?@*W:`NY _?`D?ǿ$տ`%?0j???? e ^?bv Q?s? x?E~?!ǿ @ `.? (?Fѿ`?`1?@׿@ ?`cQbQ?@V%`kǁV^L? 3?L??? L@ B?@N? !?ۿJ@3` ?`%@@#q? D? ?K??@޵??s@?`t?B6b?`p@Qw?ܾ?m H[x޿ @?V??=¿l?`Ev*չ?/Y-;??@E`?@y`g@Q?[`O?@)uz?'=?d?`;??@@䣼#Wܿ`=?? ??`%?@0$?١ K]F#ҿ`?8?h?Ko@ ?@Mܿ έ?3,?/gʿ'?``M߿\{V J7ۿ`Um?@[A? g׿?}? WB޿3jI?`?? 4Mܿ@?@)?cݿ$1?`?k@O}ſr X@Ϙ?? qԿ@пcsؿ Կ`? I?`?p?8?ҿ޿+] ?@߿U?q@?O?8? _mɿqQ;?U)տǜ?Ms\ &.?l Ӵ@>i ? sȿ`IE?(@?v;@@ǿ? `4+I?{!~?@gu?nտ)??`@%??@1@)q?@&? ?:`sz?@՝Ŀ`+?t?`^?@'?֧?`I?V4a?`?`Q?`?`'q"¿D?g?j@<$@I9ޥ1 `{? Y)9?@Ζ Ͽ?@K?(?6ÿ ?`@?R?q@ <ܿ=?@z4`?B?V?`Q׿`r8?`J?:e@ܿA?س? ఼+a? Kǿ??.׿ N?@ѿY@H??mPݿ R`\?q?ܿJ?`߿@y?DŽ@z?@ <$ҿ@ [Hп-b{?k`2 ?? .?S?S ?d?h4m%?Z?)"?6?`t?@ο /? Q@&}?U?`~? ?C??`-@?ɓ??$0?ꐶ@Gx?`,ѿ@@ۿ@2`?`f?Sӿ)?j)ο??@-qd?@c?c .@`׿Hֳ%?9??@;@K@V?`d?hٿ,K`Z?@-?E?mS?;@?0I? N%?` ?v??Dѿ M@T@ t@R?? Jοҿ@:]?u? ??տK@?2? ??`S/;H? ](?m?߻@?R? M $`^Կ@ z?@z m?&?Ta? [ ??`5P`Y??O^?΀? 1ҿPBI@@yULM?rؿ cPտ9??¿`,?տ4 J?my?y?ϵ@ݿ ƿw޿@ mv?Ϸ˿?@a?@_^o? C?|u׿ο )¿׿s)>?@+G?C?@c?@eƿ?K?a-?{ ڿ?@u¿ `y@ D?pڿ1Nۿ ?`X @s0#?|d?h?c@H?`@ɿ@Qǿu?(?*;`m@ ſK 6?`@?@F?J@\u? h o?DͿ9??`y??y@}M\6t`( oԿ  `@ȿ?@5~?@.??w?QN8s?O?e+ҿxտl?? W@ ߔ`>? ?@ {y#?@+?ҿֿ@5?|?@ ?ؿ@X? >)? z|?`???] ??Zڿb/?`^?1`ӿ? #?? ?97?A~?/u`?5 ƿ@4?` ?<4H??`^`?"Y7?~w`jA?????%?ua?%`ؿ` Zf &`~?h??&?@}^?@dA?@Pٲ@߿{?`z ? A?`8?d? Yc@? *[`Q?@?|j?S~B_d?䂪?> r @`2C? `n?N߿J? ?? ?!?)??n?@ൿo8@E?A ?X) ~?4?@1¿=þ?@2Lȿ? ? ?P@L@?@.? `:)?`@>?@?ؿQ@/ ܿ;?@*ܿ0z?Ѥ?`G?@ɿW?@qK?%.? ? ?GM??b?o? fR? V?`vPH? @x? ?t?@t[q?Կ@?κ +ʿ@?mթ?(?L{Ͽ@#Qg??mR?D^? @.< ? K?uVz? Z@ะѿ@C??Ns? F @??ʿnB@\?``?J1? ?  G? h@ "`fq? ߿`>`g}¿CN? E`??:?@Q{???`?  - - F`*`O?@}ʽ?YV?6^ :,4X ?@| /?@X#?7'ҿǓ?? +?@U@? 8? ?rٿ`w?`7/?`?Ӗ? 6??!??5?@ѿ q?*Jʛ"D@%@R???/o?N@m?@֨?Zֿ \?}?4ۿ?`{ο1?:?ܿ? UTʿ`S;?` +??\?Z?Q"` q\?  O ;?iJڿMݿ& ?g?@?p?'? @?6gLF?@ÿ|Ͽ?@ܿ`?,?`?b@@k ?`0׿m |?` @x? !:[@п3??`8Կp ?d? ?@@?௶` l޿=?]?1? 5? +:_zտ?N?üSAX@`? ?`-`:?v4`9?`v??+3iܿ{??.HuXҿ`ȶ?@F?[h?@ܿ @e?_ xB?N?jN@B`@[ ?DP?oFϿ?@ʿ@4׿ ?\?dv?(?,z?@?ؿ@?DQ? &@?@? ɂ?`6 s?` `8ѿ@׿ WY\%W( L?@`P?`^Ŀgǿ-ʿV ? ?ء?Cس?ͤ? ?@`1?;?S??w?rRҿH? 3@DӿT}?@?~qJ?t?@?-? ^yֿ?@? ?4? ??8s@D?`*?@ɣ?`@@A?@d? 6?Ӵh ?@Wݿ2? `? P?ÿ`? @0?HϿG? 'ؿ@ug;?u?D߿@?ɿ b@^? 5u? Y:@h?hÄ޿ @? 1`l>? m?q?=@iݿ 4?`uQ޿ L3?>"@?`?@K?z=?"??@>i?@???W#? ? ]@?ܿ(?l??`uO??"?s ??#? \?D?[?w?`pۿ< oۿ E?@87߿@ ɇ?@?gʿӊ?`?eͿ-?` =?zx`YⷿTӿ@?Rӿc@ѿ`'J?@ ?@YT?@?NY??>ܿ? jп x??=?@C?͹?@E?FÿR?-?'ۿ?`? @? ӿ?`@1]ۿz%??*@?~3赿;D?ѱC@7a?`U?@" ?ൡ?@Z {?f? Ͽ`?pu?>ѿ +@e???ڤ?6`;@$ y?]?@'?@(V?ˠ? o6?`h?Ԣ;?_ d?@0 S? =?@ ?@?@?w6?@ϿOӿ^@z? j?@@"? ??Ơ??< KտF? 1?G Q? ?Ž??7?`?m(?c?``t^?տL@`?@ `]??`c??!?@+v?! @*x`zſ@P`=ƿn޿ c?iX?@?#(`J??`.տLπ;`D? Wm??@`2]?fz@P0;? I 0r?u?`~? ?4&u ׿ ػ#?1?l>ɿ i ߿e?@f? @ ??H?`q? ??#ѿW?o ?ѿc\ؿ?)? ?༗{ܿx1?ߪ@:;?V`5ȩ?5Z?ڿ@Df? a? U? )?? 7E?X? Iv.޿ e(?m?`X?@W?OsQп[ ܿ ׿bc-? i?q??@i\ܿ?R@Nn?{?`J`N -?6?`@῿ R_`? kܵ@?Y?7\+?[?>ʶv? Ym? n?ٿ S?N Gh?@[?')@T ʷ??T?`?:?`Ka?m?5?@O@Z׿`?տ@A`?@6 ?v&?JC;?E?@i,`7@`@˃/z 6?@ Կ@"ۜ? Lf?q????!Wݿ@?[@` ?` `0 T?@gٿ$&?@>x?O? N%ؿؿ 3??YI?@u?? ? M?{?, ???`;1]@z\ڿྑ???@ ?g?`Nzv?@? `8 ?O?9?`.`[#`u? ӿ UO?r??@C? aŢJ?\ݿc?ZQ?;ۿ`aq?5ƿ`h܎?ݿX?I?g?`?P&?®?a?@I?``lc?@ |?S@ q? ܿ44ٿ`& c ?L9? ??@T??)s޿  ¿?$Կ`?W?@?@s``T@ο ?$F??@jqx??N@Dؿ|?E`8?q?I?g/ @?D??? c\?Zۿ@?@ ???`)@a38? ?@w$??8Ԏ@??@8?@?=ҿ`?@L??`mn@ տ@'?`k-Lҿ.?#@?DV?e? @ 9?;4@`=? ^׿`,ؿ@3@`?6?"`C޿ ?@H?pؿ ٿ?`V?`?5? W?ο?`潭`@ `?@?.? ) ย?|?{ٿ@:@D@k?п:R?Oӿ` iͿ|?F@տHUȿ^@ :?``xؿ Z?mH??@8П?|?4#ۿ? ܿ? vݿ:ٿ@B?i?@K?p`?`୩οϿ?c@I? aѿ u4@ Z?S=f?`?wMh?ڮ??`@ ?@Æ`@`?G$?@]?@@ؿM5@g?|?F!?? ;ƿ`b?9?`ǿ ?`? ;T?ܿ>??`?@ྙ?@? ??E"? ȩ@@@?Q? ?ƣ? K?`Tv `?>U@@wS?@We?yտA(@Zɿ 6%?7? ??@{?? ̥?Tݿ``@J?`Ѥ? S?=&@HF?`O ƿ@ȜK 8?h?q6?`? ?@HHJ@M? ? 'vX@?@`?7?@h ???ǿ`Cr?`?`1g` *? \~??@}.?+[@\`?޿\@|`.?4?WL?/?O\?Z)ڿ$?l` |`˿?` Vm?@P޿^@@LxſD??@W?@h? T?XֿM?`/` ?`ߺK?EW@1п?`? ?KP:K`1?`J?? Qo?Q?R? "9@`r: iϿ}o?ʿ3? Nտ$`Ot?@?Đ@@!?fۿê`"@H?N#ſ`*?`0Kӿ{I¿?ˆ@?`?w?`v`? k=Y@?@dɿԿ@ȷ?`n??"v4?ݴ˿E? ?T`z?k`)>?`Oe ?; տ8!?VZ)@R@?@?'Ys?ѿ@4?`kDNſL[?-ѿ???N?``a@F?ĿLc@ 2@ÿ3ӿ ?˿ kѿJ?d2?`?U@`.Bu? eѿſ?`?mؿ?yҿ@04`? ?[?F$` H?]ٿ? ¿w? |?@T?¿@??b 禼?ǿs=?*ƿ^@;9 ۿ?rN?5ٿ`Z?wb˿?@ P?`#/տ@Կ@+@)c?? FS?/U?V? @"̿Z;?`EB`E?@ ?? տ? \??@G4 ?u!1?@[?@B?`%@?nտ@+{@T@nԿ vƿd /?;o?|?_̿@g+@A? ??@?`s MY?h8?iؿN?dĿʦr@e \C?G?@P|?[?7@@9?y:ÿ˒`+??I:ҿ x?]  ?`b?^0?`e? ?`?hV? e ?Q?,ص`?`?IJ@^۶? ;8e ȋ??!)?`߿ }ܿ?? sӿ??wב? EQտ`f?"0bb?@?Iſ` ?[l?1? ?`P?8?=?*?6Z??Y*ӿ#\ο@˿ Y`?AF@:ܿ??h?`oe3 v@#?B?@%? jM?bͿ@)!4?9? ?\ c~??@N?fQ%?bd!?ɿ@D?Hٿ`?J q?\Rֿ:?]=L?ٿ`oq?@G?`@?y?E? `r9?S??LX?`r`R ?K?XX?y@?`q?(w@@޿7:U?N9R“?` Y? ۿF\ie@޿@>J?:?@@@\+`[8¿@E@ ?b?` ?aw`:{?i Op׿dXt,[Dz=?6?ة :@``o?@1??@9?@N?G@ ?`J-frG ?@s?`և̃ ?U?zÿ?;{?1?[?y*?@<?]?<o2˿ οҾy?S;?#?w??m9HY?7`? ??`) ?@$տѿV`Sf?@4?`>?@@K@$?}?[L?x?0?!?,`?[0K$?@33? ? ?3?9? M@?oۿ`˿l? B^?`o?= ?@$q?`@PCֿ po@@?@ ?`t?@w?<6?y?@?k׿@?`?@?` s4? @?'??Z? ׿ ?s?`v"8??@ g???@K=?(ږ x?5?3`?`DVĿQ?S?`γ߿@,?h?`?? )@;L@Qb5 H? ȿ}?Ye?@? п${?@ҿ d ƿ ?oٿ׿3ۿ@5$ۿm?;?@O%? ' ?@?M@@ݿӿ (ȿ IQf?C?dԿ @`?lĿ.??H?ȗ"? >"?S) "?ഞs?W@'?'t?j?N@W- ?`Ɩ?`?=A??V?@ܿ?? y? J?)ӿ?@3@?ӿ'ܿ@`$!׿|?`;??@?L#?? t?@ ? ? 9ٿ S 2ӿ@C? 6O?`U?+?` A?`GO?_???T5=ݿ ʿ?z@??Jq?V@uM5?`?e?:??@ @@@^?`@:?[a@abl? <@ஐۿ Y- ? Կ?@?@Tk? ]B?ܿPݿ`*?@W????? ̿`O=8?@Dֿ֪`)߿ ?,ѿ@?@@&? l?ov? U_ٿ@Y`ƿlo¿3R? Y??@9?g?`6?`#? qAݿ @ ǿ ?ǿ@pH?`F@@Od@]`? =?@Q?Ŏ?ܿI~?y?@6?@4?sc@Ȭ`˿9j?? ƿ ֿb?m?F? ,_?,?@?K@ N? VK?`߿?? j)-?3?([ֿ=?? o?@G?ꔿ R?s? ?U`?@B?dҿG?Ҹ=_?׿?}A?S/`u??h? ?y} vܿJ? 9?Q9?H@aT?̢?c @b ?e`s@۶`C?@+}?@ /)? R?Y@ ؿ ?]'@@4ҿQ?@l?@g ? O?)~?|Ͽ1?h 5? S? O??`߿`y? ˿_DW?`[?8@y?`nG п`?@4`},?ؿֿ@q? җ?KX ? ):?U? 81?`3?o?@Y?@-?f `E?`?T?@q??ؿ  xyտK`п ??? ?4=??տ޿?`g'?g&???:?`??~}??@l? zp?@k?@?տ`!? ?L/@??[1fW`V?K_ȿؿ@%!?@EB?@:ο@`(@P?ڿݕ (?`?? ҿ@Q? aϿ-M`U`H@O )?@?FsM?L?`:l?$_M LA?`l?-? - `?V@Fs ?iؿ@6? M `? j?@)?$?޿gB׿B? 1ǿz wS@X9ӿ1 v@t?2=?ڿ?`ꅹ?*Կ` e@0?u? t?@pݿ&? Q ?׿yx??`Vg?b?]! iȿ?@r?@¿+``-xf?Za;˸@ن?@?O? f?o|N``E?w?d@?`#Qj@??QQ?q? 9?@V?`t?p?FT?kϻ<(@? 8-?Mb?(Ͽ *T4?@?`z@1?@=?P?Kпؑ?@|? O?(??W Z,?k?3?9?6r?5?` ~w?.k?Kd?4? ?n/@8?ds%?@ M?@ ??@?@?@ ?`F[ ??`?X?e?`Ǫؿ@@` ?3?`?%``{x@ݱi2`@ ? R? K? ?Xȿ UI?,@?|? ɅoҿO? 2g?`F`o ? M?`'?@3?`?@?2m?`t? zXҿ@N?DŽݿ ;1`F贿$?64߿i5@HH?@?@/ ʉ?P¿/4?`lC?'?5o;??`q?@U?3@ ?O??`}?@@TB?ʿ%?"??@ȿ? ݿ? , $9?=˿(?@w2?-~?@? o? +1ۿ@ ?`? ~Z?a?u?@գ S?&ѿ)?#ѿ{@/?`k?ɿ{skݿ?@ >ƿv?@l @`*M?? %? ?x޿`,?``?@ o u˿`f?=5?@X?*?1?@ ?]ѿ?uҿ?'$?@L? ? Xu?@`?Lu?6{;g`/[?/?t=?P j?@'?`V:?3ӿ :?p@ſʿ [!?``?? `?OC?@n5.??|?`{wп ?@` Mۿ3C`Wo? $ :ۿ`̂pɿ`q_? )?@6?w%?@,?Ag?`?8!?@Ͽ?`pÿ ??D?`g:?%?`2?? ?ܿW?@?Z?qV?@_?`̐?v*? >ۿ@iu `??@M@*I| >?!ٿ=?? [?@߿ $Ϳ 1?`߿@U?n?`?b? @׿?ɿф?z?A? Ѯ`4?s?:#?@ W߿@Zy? ???˿T?`=?`EW?xJ?@06?`>^t?t? ~ؿ@@4? .?f}?@x??Nտʛ?bFڿ4п@ Wg?@˿?ۿ??1/K?(0`+Tο? *?lR?ٿZ;?Y@l߿@w?:??@bϿd߿`??Fҿ?n`u?z?s'@`Q{ѿ?@~ۿ`9g .?SM??#?`? ʿ`Ѓ? ~C?з ҽQϿ@z?=I@vP?`?T?SH?e O??cSѿ =ϿK?-3V *?1?@ k@3 ?? K}?3?`ݞ\W?`ſ`?ƿ`;?{"?@a?gx? w @`::߿=C޿`? Gs?CϿ??S:eY?5?` ٿ1`)?{`[?`4? {?@)? X?`Ғ?`R?@6?@2Aܿkcƿ]@/?@W@?`[ݹ?,̿G?F W? V`?`@PO?`H@ ๿?7? Q9?aȿr ?@&? d? @`\`/?@S@Fr@`>Jɰ?`m: ?wc?`J?Pؿ Y?_?b? ;?n?[ֿ@?p@`0?8?`?g%U J?? i?@?6 ֿj~?`+Yo h?=3?@7 \?[0Φ@@ 袿Y朿 ?`xa? ܿ|?tA/?6_?JG?nG+#? W?ɿ ܿ ?Tҿ a?V???`#xۿ?@xѿ?π &@Bh? p8ǿ@Ų?SSԥԿ? 7 `|^`b?@ ?L ~@/ꩿ`O?`d`@?` t ¿Qݿ 1o?e?\0`D e@@??`esSܿ рQ?@%K?!ɿ#?Gs? 0`Z? ZVȿ2?m?``Pw@d Y@?`8?,`@\??QǷ? &R?@<?zv???D?`5? -??C?Uٿ ?ݿLf?߻?`):?@Ƅ?C޿' ?Xv?B?S/??`L?t [?@~?]Կ'`?ҿ峿??? A !? 5¿? ? ?Kڥ?l?ࣤ?`Q`N?gw% q??@'[?}?` TQ? ?S~*ݿ?ݿ~S?`bp?]?ՋX`Flֿ`-S?%?`Ͽ`S? ?4!?? տ @4з?`;¿ɉۿ> տM?`? 9A?`?@ƽ`dx? Sٿp[?}`I?f?l@՗Կ Yl?b??NJڿ` d?YK6ӿ/?`L@H ?rG?``[? )׿ ӿDYj?? ̿@,E?5?'˿^:̿I?Iǧ?Bٿ?e`}w&?v?-? ɫ? z ʅ?G? ?[?C4?;@׿X@sο`Xѿ`4? n? Z@ ?O??^6`#=?8? ?@@z?i6>? @?@D@ ?޿ͭ7 @* xU ߿@مGH4׿`(?P?r T*`? s`?>?g?Ӯ???VЛؿ Ϳe?-?@?@?qO m? @%9?`? ο׿cV?*@a?a"K@S@'̿@? @h@? ?? '?L9' ?R`пp?ห@Q?Ld?0?uj`\`t?׿/v?p?@g?@9\ۿ ?nҿBx4 ?@?@,B? g ? X??@ڣ???"`H?@"?l=?u??Ĩ?@-(Si? Iؿ_p?g? ?J߿[ٿ*?@'?`? ?0?. *`Gc`S?v?S ߿m` +?tcs??nҿ1C?@? Mtտ[?@0?`W? Z^??a.?`!?`'?޿@?AIȿ@mHL? ?`ݭο`7??`^J?@??`Wi?@B>??bZ cP|? )??`,?`x ?`0R? ? V?k?o?y0?`=?e??)? ׯ8T??տ`1b?@8ڿ@ (?`"׿`괿|?@??47?J??V@|>?p_?@? S?{@ c<@@@q? ?]??  ѿ 50? ??li?pQ?yؿ\?@?`r?`a?`|?suE`?`?@ Rݿ,:???> +c??Gेտ?`T>? 1Ŀj u ӿ@?I:__? 8?_?-?q@˿???пJcv?``[??8? Ix?Ξ;? H?-?`F?8-? ??`˺G@?o`D#`??@ά?Dο@Hٿ`?@j?=?@[F Ж?9$?Pp߿8??ۿQ?@#Aٿ@ε?9,?@h?`Ph?IW??*f?`ۿ`?@׿҈?8"? $`W;??v@R?  ? D`!?`14?? nB`6?o?˙??x<;@me׿ GB?ã@ aӉk@`u9?5 jܝXU?}?r*??w)?@?? Q?2?0ɿ'?`Q4ֿ@Ӊ?@DY?? ?r@-?`?/s`E? bؿ7|?`~A?4?&@<?@? Z??ց {?•?=?.οCK?`]?`]?tѿ@p@`H9?M(?@ t ڿF?@Dѿ`e ?r`?@?j? ]? 6?<8z?w yEĿ?yTF??@?9?@ש -?@fd@n??@?`̀?bd w?jӿ6?`X@ ?w@ 1?҇˿@j0?X? ?@rg̿ڲؿibA¿p.` VL?%տe5??ʿ? t@?ܿ@zL!?!, 1?@F`Oٿ@9?̿`?`?/LIh^?@~KH`7v@?"?@qr?@? 8߿.9? A?˩Ͽ`?Z?(@DCQ?@? -?c? F)??9?;?PoѿsײE?][?,`St7?@N@ܿ?̺Ŀ@?u?Y@fbgۿJ? H?`? gF`$V}@??`οk? ?;?E@oD?? ӿ2@r?@X?$?#r?@ӿ ?$͎?@pB?`b?Ϳ? /ؿ@I༁ؿ ?]߿`??^v?p@?fֿV?`@e?@B?̿@?`Wb?`s8¿``.տ5٢пƿBJ? P˅ӿڿ/y@/Ω@S:˿@ҫ?`9v@?? 8ʿ ?߿@?+. F)?:?`0?` ??V?B? WǿE??xyȿ`q п 5@B?jcĿ;aw>X?p@?`zOʨ -b@?Vӿ LP?Կ`f׿??H Ϳ@??3?~ @?O`{?@MDʿ "E2Ͽ п`?%Ŋ?=?Hu'?+qͿM6?`|߿(ƿ?@/wm \`?`?? '?@?U@L`?%?|$`ݤz3?]R?xw׿R`>? I??׿Y@ ߕ`3?Rտ@B???IM? V`? M(@~¿? ?` ?@ ?ֿ'ܿa?`-(?@kʹ?dܿ̿ 2?`J4?&?+?"?@?ٿ`x׿`8`ۿ 9@{2? eܿ0f@a?_@Wow?S?z?`@`׿ /E?g?`n?9!3Q`S?PNNƿ?`Ta??;`~??ݿ2J?@?Rٿ`? ԿD?͹? ?@?D@}¿e~ @H o Z"?@VBzK @ ̦.p?J@ ?=|?`ȿ@Eؿ=R ? ћۿt?`?a? a}?*߿`?@޿`qH?T'u?E[пL1`?;@?! :Oſ`yҡ?%?[:a?@+-ҿgI?@ ߿? b? LֿW?ο J'`ۿdN=@c$<@Y?`g?`տ@i?:堿??j?r?zBA?`>$?@ ,п\R`?7Կ ??@ڿs0?`?a&?? ?@?0OA?@j?@P?B?`??bҿӡ?G'R ?{/ؿO߿uɿ-?=?@b5 Z? ?`u'@i-?@n?P 3?pÿzſ@"??`(I@Z@6?he? bοkm?y?aV߿@yٿq?%?`m?'?೙?H8?@? @??? d?&l?qd`g?`?@?@C?[}??I?F?> Dz?@Xm?ڿ%p ?;?7?l? @F?bP?? ?.w??s@@M???Z?]>`?`@7?@>=o?(9?`3?杹?K̿`Nm`R?`t?ۦW@z$ٿj? ?{?`&$@@uym?F` ? !48? $h9??<En?s?@d?e?`YG ޘ`D c@d?KۿG?`ȿn?`S?`[77ؿw?|?ּٿ ?Q?.?-??@c*?XI[V@]?@G?|?@?fy`(? qm.>?w? 9F?I@ڿ~?7?q/ ? տ ?A?~?`C?Aq?`&?`Ya"??rͰ?Ijp?*?`?o ???)ѿ`@?`-?Dɿ3`?@F6pK?F?5?@]??L??@2?nV׿3?`!?J? ?@`uƿ@7?L?@.?6ؿ E`:." `m%?Em84:??^F?࠻? m? hԿȋ?p?@B?a@,n?@J`?@5? ]?V? ?`? M1+?`^ V@ ,h@? 6׿@rֿ@I?% N?@d.տu(? @Do@MXso?c?TۿGp?ӿ`? k@?`4d} ??`Y?X?_#?+;?I୍?"?00?I? 69@`쩿:̿`,?)ÿ`}??j? ?@s\? o?S ?a̿-S?@ֿ?п`U4?@~ֿ? r2@Jʿ($@UQ? ]|?@_???N?n?`y. K`׿U@x@Q? K?@?̿ |??`~`/ȧ?@p? [xտA`?u?Ⱦ??` ?@c?-`?`{ V? ??`d@},?` S? ?@3mҘ?~?@)j?@>?;gR@ Q?ܿxܿ ˍ @ ? 0 ??{?`"̿@o2莫?<:ɿ.{ j?pؿT}j}?@??'#AmV??\'?@:?`;X?S?@5Xdڿ`.? h?@W?ظ?aS?`?%?#M?F?~?j^?S`/W?@:nڿ}m@ .? p맿`??@@ x? #?E 3ͿA? C? 7@`1?@)?Qq?-Ŀ,?@c`~xK?`, ?=L?§? F"ۿ@Zw?`Y@=?''?m? f? H'@Qo`#?9?@ ^?@ٿ^J?`GQ 7?@u? p??R`a?7??q5i?@  =Ă`&X?? q? &?ʿ-?? @y?]b@ @ҿ=?`>l0ȗ? xܿ ,m?I, -ۿ=q?`Q?`jֿ?3&?@UG Þ?Oh?D@ ?`&?-]?dп`M? ?@\?t?4?i˿y?  ?ڿ@P? Pſ ý?`ݿ 1$@J?ȿw@h?` rͿƿƿ @\@Ǐ? ? ο ?P`?j?Lÿq?sJqr? xL?? cVE ? G?:jܿ] k? `?@ܿ`қ`3?@W>?` -? Èٿ`Mu??8¿,?@>$?&ſҁ`??@/?.`p׿`*st? ?@ؿ`?PW?j /Uƿ 1?? G?_ PU I,?`VTпd @@z?%ܿ  J&?`@?`V?`>N~j?`Qt;l?! 3?B?Ny u?@)? ο@}/׿?`oJ?s?G@?`V7??g`*?@v?b`7ޏ?xֿ g?? I?@? ?ǡ??YI?AWj?@ q58??@ϵ?eS?5῿ N\?`?I ?`s@?&?c?ؿ@ ?`? ?@̿.?,?? )?p?Z `?`?@rQ?ѿ`S? ?`;{F`{? ` =u/ w? ?!A?k`b ?R?? j?bӿ ?`տۿ@}mQ?ܑƿC*`9?@?-? ?`7 L?r`T?@a@? ?Z@L}? 3 ? A??`?H?@&`q3?I]b4?"? }&?@A ?Dڿ `@=~?d??@U׿@пp?H?` fn[?`]?"ӿ?Oܿ mL? e޿پѿྩ??@o[F?@?@r ~ݿ`dmu?U?@??5?)ֿ`Tѿ` ?_w`3?@?@?2?? H?`?k?`@׿k? x?@e =4@@ۿmܿ@ @<[?4?U? 1?`ٿ@b??N??Ա? d?t?fK??EAq? V.?Y?@w„?? j:? A?u?@`? i _#f?,i?I? +;*??V'?Ʊ@` @?` `H?@e???``Կ`4?\k?+߿ ? 6ֿ`ؿx?`8ɿ*?@n ?{ x`(˿PH@t5:? E[?@ Xr?H? 6пt`6 v?@`A?̿Կݿ@P? ׿Sw@`ْ?yx?`eƿW@!;?ҿg?`>ڿ`пقͿ`kϿB??`e)?@6? u?3?????`% ¿T? V `S˿`qҿap?bB?W߿s?ο Hο YĹ?ǿ?࿓?@ӿԿ@?a'`%?^??41?+߿ /T?@|u@7RU_?B2@p?s0JX@@Tؿ\/ݿy2 kk?@= ?`%?rPI?`׸p `~@ʵ``+@d?@` @??F?`.?%w`cÿ X?;`> E? ~@I?? }@?` ?4?s???ѿ{@? w5 ??@?rXԿP?S``ǿz?i?`J?`2t8ۿ@4)?ԿN׿@pt? nѿ?M??`?=? :C ݍ׿@[@C?@?@?@쏿O?`MK@? Q@cԿ?@d¿<׿ ^п`?@gn?@jpLr?`E ? ??C7??@I?@oͿv? s {%?ǘ? 8?'@Ectp?=?[?@?ô??̿`<)V?;?R?2 =*`y̿?펿 <ǿ` ɿ0?X?˝I?`yq@C?@\???s?@n?@灲?=Vk@C?$! !ֿ_?\??n5@ۿ@ M?X?@?Ne??@)?RS?`w?`?3\l Ӑ`?@ п?<%?Clÿ`m $`@**?-?@_g?@Y|?? [;?d?Hd? @[?9L?ԿH?w?@?k?`??]K,0?Gοy?`)ſȘ`?9ӿ >f?)??9ڿ?~`F@Jm\?s P J?ܿ_`?@ )?b׿8?'޿lR?7``C䰿u?d1@-x@D?`"Xڿ`}???]?kPQ`;?d`? 7?U?`*?`h?'@п`QPH@O` zֿ4 XI&=?>v?`#?Wj?ц?~? Q*W?ܵ.ܿ-G?@5"?gi Y?`]? v`?@WCN?gٿpV? ?`R? U?? ?c?^?1|?? ƿ D?q?3z?@?} ? ˿? 5߿& ? `F?@犮?."ֿ@Wa4??*N@C?k@gп@xj̿`>g@h ?|_??@{ͨ?`3TJ?`6?۵`h?`Z@s??@^?,"ڿ@?`eh"?0?G?@3` ?{H? a??t? `f?? k?``R? ?@߿?.?{?xԿ ?}??`x? Mg?`?E?? @ҿ@?`W ?x{?B D?`??@v?"?`>?@@Ϳ@& ??:E??KXF?'?`a ?j#?h?-j޿p?`<ҿٿ@a]m??_? l?a?^?Oe`hS?ؔҿ20?K?9?W {?W?f?c? m?7߿`i?9?@.?@uV?Q? W?@ң?@?0`kG?Uǿؿ@P ?3޿@?.Zƿ@ ?.N޿ӑҿ`?`4? ?PB?iQ)@xĿ`Y"?޿1u??`+2>?`e]@K??? ?_?Q@ ~ݿ5? e6?`L?  WV @6g? ߯NԿ?@? `b?`?`%?(k?'q޿5?໎?&? T?`(,`b?e?`5 E׿@q?,п?`?1 ?@yD*?`? xݿ߿"q?$W?n? -D†?? ͿL??>?1M?`?k?@?`ޫK? ,?\A`{ I?Sଉѿb?`* vֿ5ǿ???`Eҿڱ?`F?P??@`@Ŝ ؕ?@,IѿL@u?e"8 %>?@@?;? 9?Mӿ?fĶIz@ǿ`& s@v`8?@]aӿ`@ڿ E?U??Q???`C?,o.=? ML?f@\⸿JG L"?  @8?d?`, տ`Y޿NSw?`@mf޿&ٿf?`Nǿ4r?k@rg?@?`?)? v߿@Uz? L̿@0Z @ ӿ`!??8?x@'a? h`ҿb?@? a@ ? N?K? j ?A?@? 0m?'\?@gF??vGx@??@?Ґ׿Ƽ?Pܿ=ۿ)߿@ T4 ~?@?V?v?;40 \?`n`@?M?H?`zڿ@Ѽր?C? @h?@>qdԿ3? &l`@Wƿ`> Ko?P@a޿ `?"/? 8տ? _i`?L?6\ܿ`jgп`D@`g !???Կ?|?6@?`0?a;ѿ@1 \?I?az?@@!?OgF&ٿ ^?@`߿`(Ā`k4?@1`? *͢ڿ?P?=b{?@`er?@bFҿ`??.M`?Soʸ?#? U߿.=@ڿ`.? ?{?R5O? ?@?Ǣ`W `4ʿ@ տ:?&?@{?m@/g?@"Eܿ d5 t? j??.T?`ؿ ? ?`S? ޿&zĿ'?`P0? ?&? q?`? fϿHҿ K?`u߿`{ U $@^}A@ޚ%? տ@L?b|,!?@?`Ŀ%s@޿@z?? \ֿ?`l8ȿDѿ@B?]ҿ 0 ? ʿ f@??@??`??b?xx ,?l?Ȼ`(ܿ`տt@.?@qJ? /o? Em@E????M?B|? ,˿@ 6? sc@ %?I9?'O%?`?y?x? '? ??ݿ(rۿ?пbKy3 , ?.?-? t$??ҿ.e?@i?@ ?)U?0?`u,ƿocݿ`?Jr@?త{!?ѿTݿ ߿כ@_?ؚ@^%? ?S1?P~Q`DZ? z^Ͽ`@޿?`[޿sݿ@?'dڿl?Phv? (? D+?ݿ@u<75J?`ܙѿ@?`]?`Uɿ)7?@sI׿ B ?@??D`? ?S`wտ$y?`@$?w*ſT6?i?`j 5u? { j c׿?%?l? ;n? m?? ?@?O?@`Mҿ? RSN? C?Q?S?`?@?@yv? Ps Y? ?\2?`v*ҿb?K`@4?lJ@Sʿ`<?@?@0? Z` w@?`)?@ ¿@I ?o@G? 9 u?Рѿ?`=?nu?`l? ߿2 H?cZ`eLǰ`1`?x?8? \?݊ϿhӿEW?@8b>n@Yh`?6P+@` ?`)?`"9?y?qq?`àJ?3?1? ?y@;? :?@ü?G |?z;???@.@$?7?P??Ƕ?~m??1O@Ndſm|`?P+?BP@L?@kG$߿H?`;"?@??`ZZj`?+@AZ?? Lߤ?@Y??_??f? <? H??b4nnfӿ@r߿`? vĿ`x@%?`?+ĿQ?!ݿ'\??؛ b[@d?&? WҿȆ?G? ?w? D=?@?? q?3`jڿ @h3@ ?H-%5??a?tB?3Ŀ`B????@? ?V'?@kؿ???`߅?`¤`? 8?a C@³?@%?gݿ@OvL?@jp?`a@I?`?Aӿ`+?d?g~w??JZ?US !? |??`֯8@/@`X%? b?pT?@/ͿD?T? Nѿi)p w??ֿo@;p]Q@H?y?PÿT= ?B? 0?t(Ŀ t`(?`E?;?m?ycN< y?@K?`R ,c?@_Z?`@?:?ʿJ!Dg@"?q?aܿ Q=?@ܿ1M@ӿ@gE??@=@ ?@Y? ο`{``D?M̿`7U?@?9ؿur? J '¿`h? &?`p?@Cj@GX`@y?C`@1?|:?\]` ſ տ૛տ@ݥ޿Z??@?W@ ׿z? Nƿ`Լ??Á???7&Ŀt_?p?`m`??-?߿?1#ϿQ?-@ k՗˿@5_?`[-G? ????`.?@jM?5H? 3)?Կտ`8Tؿ??@ * ? {? Uğ?#?C?4?@V?`9?E? Jk@ɿ ?T??`^@`~?"?b?; +?V¿`@}᰿?@Y`?`?`?7z?:@FͿ?"`?`?g?@o7?@?=}9?x8??YZ?4`? 1? h`q? ? ?7? ?sN?5`bҿR?Q?t`п@x<`?@p{?@2? [H?n`-Y@I@?o8տ(?9 ˿P@[?]?@]Ͽ֚`[g!ӿIE?? ?ॊ?`* ?S?ݿ r? .? a?tп H8H@O4g@Z쿿`@w'q?!Ch? `¿`?Z d ? %?@#?`8?QFؿ?@?YR@N@`@~rDѿ 0h'?* W?ֲ`bCܿ??? ߇? M?@=@B^?`T?,ǿ`p?@Ϳտ%c?@p?z~??@?嵿@@ӳ?` yԿ`Z@ ?Z?EпJ?пh 5???n? @  ֿ @k?@?w?z?8 ?pnտ`2¿`8N?`/wҿ1?@ו`?3?`l?ֿ?lr< 0?@pk˿`)6??%?ҿo@u&`@^?D?`????@K<ֿ??`s?ZL@ ?i?@?@?@?b?&?C?d??}z?D?`Vm?@c@U޿ W?;‰? K?S@?!??``?ݡV ]νr? ޒ?G W?ڿJ?`r?J2? N5?'?` fj?Qp?`Q`v ??ܿ@?`I??*??j/?YP1?@Y?y?Mt@  ?@*`?Ad?f`Ŀq? *I?+?Ko /? ?p?`?`[? ܟ?h?@y?i?` t?m@I" ? k@Z?-g??@T?@?nz??K] 9?l? ?e??  Կ?`wۿӕ~f1?B?J¿?}a` @ ஔ?7@f??`?Lx@ Bh?`م``C Q@?`M *?ѿ@xѿ̿9ٿ|?`?M?`??`F"e??eܿ ?@=PJ>?@??1?`"?@ݟ? Ͽ?`'? 6 (`h? Of?@ PտH? ?__e? ?RҿCB`7[@*?< ?@Jӿڿ??@G?G?D(¿F 'Z?@ЮB5 @Uv=@/ 0ĿsS @`m?j)ٿ_?@Å?< 7:TT7ٿXzؿ`b(?Ze?@׿wm@?^?5 ۿp?r?`ֱ?b7?@C?.????W?`~?? 8?]̿Gǿ I?C?(??8?>?lN? b@^?$87p`O>? 4q?ٿ!ӿ?? Ͽ P?a>? ?@`??@Sp? nο` ?Z uۿ@:ٿ>?@@ֶ?H?\ؿap߿`Ӳ?(ւ?`SY?m?R? mֿ` {@bi`H Ͽ`Z?o?2?Z?!=?w?`¿3 r?srn??@$?`^?`? (`@`&3?V??8??k?l? v?=@5n7"&`U?@Ƒ?8?f+T`qPY?<???`*T@/?@ ? 6?`@w?`$?H?lؿR׿X!?`ſw?@p?l}A?lZ? )g?z?`9w=?޲ѿ?o? @B??? ?`Ws? ?`׿ ӿ`;Q?7V ޿ @ 7W~׿`??i??7׿`?-?.`|?l{ʿ$?Uƿ$޿` ? U? @,Fп@??t?x`?? N?`ٿ Md`Q?g?|?`Z?W?`W `hѿ %@T? B?g~Ht?% ʿ@X̣@z?6@r?@ ?@'@??`bA `mٿ U?k?@NҿeͿ :? Ƽ?D @J?s?zSؿ`оѿ' 1?`? ѿ0A ??bd9??O"Ͽ`` =`W?@? Q@׿`ſ@OQԿ!? ?@F@`n?v?:?@э?Kl?s??2@x? 0@3Tӿ5??@`3h̿?T?L}?@~ڿ`?@?`?7?`Q?\?@?Da? ?^ ?`??kg@߿f?n?@G@g?- ݿL?y??&? |^4?@_$?mW 䲿?? ߿MK?4?`ट??q?@ο`[ؿ,?@ @?`?`U`޸꯿>ӿVY߿ ?@?PFʿ??`pȿ`$`?@z?@)?@c'? pv D/?@"?L?`տ@?`#T?+? |ٿ`ۿ``V??@`/?m=G@ ^?7@??@Zr??\{D?@߿`?` \?mڿп O] A?J0@?B?.? Ϳ#˿k?`#??G޿ Qy?(?@g7ǿ@ w?@?J????Ϳ &k׿I J?`ۿ`?ʐտ H~<@{Qȿ 8? dm? AʿL?@ř8 @?u??@ ?p[`DE`j?u?`Te~ȿ%??`?^?Fs?mj޿`්?`0׿?? } п*?&P/ @@c`߿g߿]Y?`Ve?-NBϿ ? #$?:n#?x?@0a-?ؿ`D?ͷz?Կ?Zs`&?`C@Z4p޿֢?#? ƿ?`]ÿYۿ ;?/?%F?@l+g -Qп? H??r`??@㷿?iO?m\@ d? vC?h?f䛱.׿`?@?@@`h#?`@b ;@W@?տ@߿ I?(`ֿ|?ֿ++Ey?R Oڿd"@?b?=ÿL@?`x? Wf(??`?@HRG?t"?`|~˿?`A?j?`?i? ?/߿Կ@1KοgQ/? ?l%?@Yy?`SK?~ܿ@@F푿^\?Lw?\?@ڿ ?@? G?<@eL?`ڽ@_?}?w?ڿ ?`M??` ?@ѕz) sU?ભ?`R??Fj?`ٿK?Fҿ~?@t?`@@O?9@~Z??@}?`ܰ[?_޿ F>?SB~??g;ɿ@jxÿ ?@@}cȿ kR?@ =@dl`O ?w+ ?zԿZ@ru? 1P??*J@Eɿ ? ܿAͿD`+?`Bp?W> e@?尾r?@W?t`G]?ڿP?%??`W+ƿq@?(@  OϿ`Ub?4(?? >"?Ds?`??W? ڄ?? ??z5?Ҵ? ?r1 U -s,@FaϿ@? `z?Fbſ=?`@`e?n׿ ?? Hտu??H?,?;{?`׿/?&[@f ?@C@m?I`? ?ؿ&``?:ӿ:`2?r?` O?[k ?`X? {?ѿ? %?qEi _?pcݿw?ҿx? @@%?@K8`;%Of(LG?@ ֿU? ??0`?@L? k3?ʿ@}˿?`Hc¿?7q_4?Ri?@ف/$`*1[ϲw`h??VڿZ7?? f?\a?`ǡ? đ?@Y V?\??. Xc?r? 3Կ 2z@5?}׿@?`; ߿}@ ƿ ??@#?h@K?`6?5Vٿ9O? ?`.ۿ`ÿ?`94QD¿ c"@#˿X0?n& gԿ@_?`D5`1]?Y?ÿh?ٿGeg??z?9 @M?G?`U?/i?4?!ɿV^?S y?`/?@ ? {@@2?\ ?D@@P?`I@c?@૯?@n`"`,?`@ %8ҿ9?@h ?f?@$H`; ? 2`g?7?@g?>޿-?` ?KHؿ`a?ƆҿTsdʿܿ sD?`M?%?H ^燿 n¿@ӿ`9P?p 3ҿ`K?Y6C1ӿ`?`"@ @?_?@`ǿu߿Az?˿``}@sf!? n_?`4?$?1?,(@j;?6`˿m?@?O?>@p@Y?`? ? L?A@? ??@0@@f~??3 A@|ſ? u ?ۿE?+Կx޿@d˿@ۿ= ^ʿ`E? Cs?`³?պ`?}~h 0y}]ѿ Y?<ƿ%X?`? Y [ V?`U`>@2?@?`K@ٙ ??Կ`%ÿ *@?-? lA?@[?`> 3-??=?U?*]?`kHhgV? sL?d?q??``?@Jڿ< k-?` E?@x?_`?`Sz?@qS?@,? @@?? p?@?]?_uѿ ]66ο`Oӿ&;?#?@пݱ?`1?`E@ѥ?0?`i?3`5Կ`U?Wk?`X?@ӿqA`w? 3`x X?p?8.?r~?~? W? O?`L@X 7?3?b$ȿr?,ٻ?@0?`~? D??&,??๜`Mp??`<¿z?`zg?@`Y?h`@@7?`?? ?@?@0^@ۿ@"!?@?c?@`w?``w@g ??G? I ?@Z@?$?? ]?h!`?,?20`J=e?;v?Y@?"2@ەU˿`?{?Ac@?`ð? uڿ` 40?Eb` Fo` ӿ`ѿ`+`1j??`?m@L`@~? j@)?VF{wT? ?` ?L?@n S?@[~?x?`ѿ@c?Uѿ K` @?g?@ݿI? UGL?Wƿ ?@j?@2?? -ݿ/¿߿?g2( @ ſ |@'@`YJ?@_?cŧ?`?<K+ɿ]?@x@xb ȷ@z"ǿU?Ϗ? # -? $+ڭ?@N"?~?@$!ٿh?@'??B?@ֵ?>@ .W?@^@ 9?8@Jcܿ`/$?`?۵?@q3?` ߿`?8??`ۿ?¨ e? ?`_? y@fqv@Q9 ?2W`}pݿ`?Կ`?@@@N??@?3? ?~Y@B>ڿP?ֿ??ȿ +J`_?vU? 4? ? .`?z? p?#@`vG S'w"?H $ݿF?h? ?@P $?c?te(?@bf?`,?z`*`! \`?@7?`.@??`u?` e?K#? d? ? ս?&@`?}g?`8n?`9?` ?IdX@?@,`@b ?T?@*?`"?o@5? $`Qֿ(%?f?ۿ@\?`&O XÿV4?`ؿ@gg?@@$ 2 tL?3$ П? *5?ܤ`r`r?/?@U( ?o?`jX1U?@%i?)|?4@ D?@)"`[? ?&?`ˎ?]f?@??Q?`&?\n?@{v?`?`,@Uw?տ@?@eU̿j?U,F RZ?@J޿@6?K%ϿX?\"̿q?@d̿`_?wv?Ͽp˿? ܿ?@X~`x?޿eп??W=?`Oۿ`4?]#c*˿(ҿ@ʪ?`AĿ|@c9@%?*?Y?l޿` ?7?@E?Gſ0@$?@3?f@>9 7'?(!ǿ |?@@0A?`^@B?ؿT3@\]S?i?W?U? 8O? ֌ſ{տr}T&?@^ ? p?`|?@2?`? iؿ&@@R`:?@#? B? CR?`?`? a? *?@`r?w`}ſ S?M{? 5] rF̿֓?@fnп@&?@{ ?I+ p?!? ?$? m?@nF?SXo?๑ݿ@?S߿>o?c``>t??@J?x? Fq?@c9`%?vɹ?ڿk?A?AX@L?H?Uο`,4-?gԿ?˹?ݟٿj?`r0@o?`+Ͽ?,?Iʿ[?: "?`IԿ_Ϳ?@'y?ů@AS?`u?@?@3?ā? ÿy?`̸?7?P*ۿޫȿUÿx@%ƿ@9?+ @m?Pe)?(k`Ϳ?u\?VK¿m`l?'?۳?@?2???+L`qտ``p@ ݿ -\??t?a?/9?Lؿ ?x?`.??ֿ`Ѵ?v+? ?w6@C@b?`c?`!1=@?ѿY?? n h6?8?m? і@ @ſ 'ÿ?`[?c@Ѓ?@(`?kȢ >ؿEe? Ç?`=N͢??_ տ`?`C?2g?,?@ù?2w?^Ͽw6?>K? 6?`ʼ@M9 dcؿQϿH`;,zwů`W?dLۅ?@?н?@t޿,?`çտ@DI?`Q??ƕ?`V ?ҿ@!տT?s?W?DϿ@1i?`f?@/? VNV??Ŵ -?`ʒio? ?@?ϧ?:@|οL@q׿@9I?{?]??@ 2ࡈ?`+ ?0@ k̿?H??]?0l?%U?`@{? a?@A?@W/ڿ9?9ÿ@1vؿh8?˿ (ҿ ?lwy?କ?@??s п6?@t(?`J?SR! zݿ?^?X&?`#N4~`?`$?5f& ǡ?c? ˿ ޿?,I@??ಅֿ?? R?D?P`@x~t*? @ѿ@@Ͽ@Ѽ???F%ɿ`?`޿ ? !??ؿ .QٿH HƔ?@hɕɿ?`?p?@ |˾T??@6`=ÿ`gu?)οh^˿}z@fݿ@ؿ$?j ?D?=?QY?RX?y?`?*C޿? #?,?@tۿ`A+5ٿ!?F7? ?@p@ۿ(?@"ÿ"޿b?/?`*?_&kX?f¿ڱѿ`3?`?`!ۿO?@?@)'?C?d??@ƿzY?:4d?`??H??@? bD`$_`\? m?'?Mǡ??@y?ҿ7?9(?@?O Nӿ S`^Ͽ ǿ ,`?п25? !?@s?AJ) 'п@@(vL@l@*bd?bR?@ى?h?. :@`?k?F@ @8ҿ qÿȿ 8׿? 8@$?}?=h k?``4 1TU@׿ t? \)?Yj@V@<飿 s? @Sb? ?]?@ cq? :??F*?@6 /]޿ n*?ƭ?B!o?5 ?`? [?!Ϳ@N?#?r??`*??@y ?`PԿ `Dz?`Z?>?QV?@޵\?`? Ps?@e?`0?@:? q M?Qſ\)?LmM`?j?@?m?[`1Q?7?? fw˿q^?`-?`?Zп?-?J~?@H?? ?`?8?`>Q?@ӿD?¿w?ӿ C?@m??Ӣ?>]?>?? ?`6׿t??p??4`Wo?Vk?a?``?Y?@ w¿ d п.u?!?E@i@^?J? `??  @`?`A?`i"J߿ t`T?-?)?$οਪ??˿R Uؿ A?`kIR'C@ 1?W?4mٿ75``e;r? H@?yB̿@ظ@D?ึ?m _'}?G ߊ@ `$?H9?@D??dQ?1@Yѿӿ htG?? I?<෿@ݿ=}?ٿ@ 8?o?ڿݿ硿ć`?fY̿&?`?q׿(I?B?`? , ?¿w? @ƿS?x?@#?co5п ?? Z? qGӿ2׿x?`Ͽ`M)`? ?V? ?Z&??`?U?̿-߿a?@V?˥ѿ@q??@`l?kf>`U[?@~?U?@@?t?j@ @4]?@_?>:? =?*v? uG?@R?_f? ?m? {?`o`v ? ? ˿@VU-H?Mಖ?@bM?@AE@?gC@@ɮ? ?' `2`U?ӿĿrY?Գ?@t?`?@a?`?`?fӿ? ߿`̏?8?h p`O?w6? Ns3^K-ֿ`l`F?Dѿz¿Ŀe?`@ ?@AB? q@ 9W?`??`g@$?K@n?~?Is?@?`R?@HԿ 4&?"? a?T?^? H%? 0߿b?3?R̿m?^?``e?`/ '?@P^?@'?F?K?z?g?-ǿ~?`ϿmnWi?`M#տ`uS??0@?~?@yѿ?@ӛ@??? ?@n78 ?0ż? W`?ܿ)?A?`KڿJ '? R׿#@;? W?=O? ?@Ә?h`ۿ?`?H?@v? ? \?6`Tڿ/? Z`ڎ?;@$Hۿ`ֿMq1?`f? [?(ѿ =ʿ|ϿTθ?"1ο #߿ @?٭f8ѿAٿ & j?@?`?zؿ+ɿ}ҿ?@ Q?h?o@?R?D?o&?q?`f&?@׿@6?```jYȿ B@??m?JK`Tпү?@3`?@ ?D@ne?L??"?4?{??@?ͤ?@!?@?RK? 2ǿ`; 41?6п? f࿡@)?cG?ૐ?`??+? ҿ`V? O?`q? ? ƴ?? 1.Ŀٿݿ@@@"?t?@`ֿvտѿ@??%ӿ@?p޿@Q?? ;Ju?a?  ?? [$ڿ@j?.?6? K?@ @? SǾ?@rg ? uп @?@-/W?Ua?`W F?@?o@ Ͽ?؟?Q`?@¿[?`T߿1pn ?˸?HDSW? i?`dT?`?`!?+?`n:|@Ef?[ ? ?@@ѿr8`tf?n?1?`g?C;?`Mw?Z?P`3?K?= ?`j^?`x?f'?TU#j7׿1ʿ@S?@ѿY?`.@`ο??z@wvտ?V?p?+? @:?`< |?]?`T &blY?Bݿ "n@&Ŀ 7߿R? տ`) l)?*@ ?C?Z? ͅւ??@F?w?&n? ۪߿??`>S¿`R?R?`??-?`ahmH()??k-@^@?`4`?s?@UͿ*G?$?`%a? |Oݿmجɰ8@?@8??ƹܿ ƾ 6?`7?``#׿EƁ.l??@?@thz?T?2.ѿ`Z^z刺 ?`v(?G?@@?>Ͽ@?@/.)+ؿx@ ?? PVqȿ? ^͗GԿ?vTS@@\T?`Q?@\?`??OWɿ ֿ`× ?9Կ›ؿgVƹ?e?@?3?޿?? @4?`?`? 1 Z?WϿMk?b?f?M`/ؿL?`p?C?C?-?`o@?`\R?;A~?p3?u`}@A?i&?` ׿`.@?०?,? 'c? ?g??FտJ@ ?`?`ӿ ?| [?R)`@ַ?@|`Ua?d?ܿ;߿+`?` ?wʿ\x%?`Q`@? q@`_`fk?`}R?`*@?t?O?7տW57/?z?@?@+V@^?`ӌ@Q?H>?H?Ceؿ3`ZM}V@m?,r}? ?? ?`9 \I?`تܿ?%?uM[a?0@i7@ օ@8?0ѿ@'l?@X?n? s?=?౼?=?2?~ٿ@#n3<`M?@@޿? P"ڿ(? ?@k?rM?G`:H?Z?M??%>?`(Yѕ޿).?``" S|?@AY ??M HKܿL#ſ?9?`|ֿw?况?PO ??=? Pڿb @T? :Ƥ@cܿտ @?`G?`*~%? ۿ|ɿ`?.ҿ-E?>:@%'x%`'?@? tmȿ_?[Nҿ9?ٿba?Ϲ?J?fc?п`s`m {@}S4)??^? ?S-%ѿWl#`8?w r?b?:y?@O?׾@ykfj`"4¿A?`L?A?@?@I?q?_?!??iÿ? s`SW? z+}[S%?̇޿U? P?w ?D@@?c ?/?V@x{?t?`'?@ Za%1?pſ`?'޿ ּ6? c?@Q" ?=_ ]:?`k޿q7?_ǿ ?`6?GR@[?`?[տ@`?^?  Wt`Eg? q?}? I@?r ~?ҿ Ƭ?¿\??{?@?@?޿ 4?@?;?{dv@ӿ6@ ҿ@Y؀?Z|e?*? ? rۿe$̿? 3?9? T??W@=?jY?3 NG?mӿ`?N? ?@b?`J hX| `s?g?^?ſ@@? J R[D?YӿqĿsFk`W ?~?? 8 H"ӿ3?` J@-? 7@ W*տ J"?6 km?Է?@!?/ f?@S+ɿ+2 )ѿ@g?ҰeG?`{?Uu@5ؿj?I?Y?^@X([ڿsf>z? @OڿۿO?-?=Y?@=?|`oĥ?eBu?@?:??Z???(Ix?HTԿ- q?E `?K@6|߿D?^Tqٿ\?I.?@_?9ؿ#?85¿f@ૼ? H ܿ3?Ѥ?`F;? c?@??@@T? j``*?F?`@@5?ɿX?`*?`b?C;ȿ@Yտ@?K?@~??r =?ָ??` ?E,ѿ@hNA?@,B b? 0>? 5D@6?`KͿT@ /?oѿ`?? ?J(?jy@f bZ3:?@5iſ`?0@o޿˿~n?_?p?~o?E^?\@j? ? Uÿ@a^@`>@H?``?bID`xC?A0gǿ@j`ϭ? Bw߿?IM п@l1? 7?`ֿ`q{?`\?N= ?? w? *?;?`W'? ?@5? ? ?R`?@?@&?l?@? ' SNg޿@$d޿T? ?x?6@9ô?@}`Q?:@G` ಎֿ ? z?@߿&?`K{`뱿 '? v? տO??*_f`Q??$Ŀ5M?@)h?x`пÿ@r? EC`?T&?6"py9\ )@ $?`@?@`3,*ؿ\ؿMI? s̿[?V?`}?@Z??e?/?` v?[R`1޿e?h ?``@/?!M?п$?\̿@띿༭Gm@_Qܿ T?? ""@$`+b}??@@Qf?+?~`E?j6?@$ gn@7??׿ l?@?e?D?Y?ό@1?`a??@̼? ] ?}@ۿ=l?Y?V?p?`#?k?@Q*ӿ`$??@2`n??Ӆ ?`HĿ`ɿ`D%տg ܿ?<ۿ2M?߿ ڴ˿@,bؿw@߿Bӿ? j.?k9?@t0Ŀ@✳ ??i0 ?O?`?`[w?}@8s?`]$ ( п@' Ž?@zM?@Q? l*?`?`\?? ?Ru?+? ??p? ?@W??@?,p?ɰ? t`s@f? /?r<@Q?<j?<Կ?Wj?`ѿ#?` I? Kݿ;?b?RſyT??_Ź?u'?@$?C 4ӿ>@?ݿ?K?_`[1??? @tL:?`¿@?`Wѿ?@c? S?L?,`?ݗ?=A? \ @?`4?@oؿȝڸ?ȿ'?Ź??D?@i?Z?`dU?Qʿj?0崿`&a6FC?@# ?`/? B?@ @H?@C `?.?T`Ͽ3)?`d*?+?fԿV?D??@@`"? ?jȿԿ x??j?@*#?`D?`zW`#?g@"?@`+@??h? TW?̏B)ċ?`Bu߿ W`Ha? si?>@@`yҿ:,?gS? WX#q? (ӿ?aI@? Qyڿ`??.<?`]?_? }?l?`?? ?<`?-ƿԿ?? '`Կ ~?`:q$t?@1U?ׇ?` 3ֿ 0?fx@b"z?vݿ`l?lD?`g@?`6@Ah@C?`?<(?`nۿ?m?>?YZ??E?Jѿ^-?b???᭿28?Ho?@)?1ѿ i?`@@ty?`G??cѿ`H pտ @dܿ@d?9?g?ҍݿ@~+/? @|HĿ*?` f? r`i?=? xt}?@/?~`տ?# ?@F}?0˿? ҳ@?5?@Lo@ٿX̿`\? B?N?@- ;??b? @ '@? ?m`?@@4sſ`? 5?ࠋ?@9?6?? ś{ ??ެ?f7@ /?cZ~ܿ ?@;`"y?`??`t8ؿ@@?fKݲϥK?@?@?`" ??`^U@?<? .&?ۋ߿@Կ t? R0̿j-@M ?m?@+?]ѿ 姿z?ɾ?w[Z ,@S (?@!:ɿ ^?@n?!ҿ@?i'D?e?`?@m]׎?`i?B2?`R]72?n?C~@z? ? mѿX? oV?@W`9I ?,?`Tֿ`|5Ͽ=?!%7? }q?`i?`ЄʿͿ? C??@( ̴п?@O?@?@K? C?`Sӿz? ?' |#?@ؚCʿ@# ,G?@!}x?- ?`9`Î?E?@rp?Ǹ?@l(??@2?iTڿ@d?8Z@iiؿ ?vVٺ?t?cFֿc?k@ 7Ǧ?ě?_2??l? @S[?\ak? p?? P?!m*=A?.Dt? Ĺ? ׿w@@x?`4?@@vE?@N?_`?t?_?0׿NZ?`I6ҿ?b?f16@-@ܿ =@߿<A??D ??@7?`wD@ޕ?`n?@?`@?`տ@ @-@lB?Z? K?@?8S@aǿ`6*@`grm? B??@Q?YZ? `V"?LӿE?Lۿ@kۿs]2? rN?@!}Կ[? ;b-N?`?ƻ?~\?:b?K?@? ?(ؿ?*R?@?޿@`? ??:?пy8 #? O}?`zӿ@ۿ Q@Կ Z@,}&e?d? @@a?4#?@d?j?Kſۿ ?'?`l" ?/EIÿU?'N?\? Կ(ؿ *Τ?`? o?@? y?X?T? *@?`i@@`?@z!?D?8L@ʹ?:M@?i?``@?M#?vrCg? K)?@{@?by?p1@=?`<? ?y? {?!g2ѿY?A@??m ^?`?[{@&\?ò?I?@@Dn?@~?Y?D̿?4?Ǐ? H?,?`? ɿ Њ~f?ӿ?˿ؿؿGֿz?@? @@ ? { V?R|?C(?@jt?@@r௨`PP?`u? ?V?'ſ`-`wۿ@?@R@ `ٿ`*}9@?3s-?AG?0<˿@5Am?rܿ~F? e?l?_#sE5?y>`>?*̿@ҳ?Cw? dg?`:M@?p?@ ??ӿ`?@p?|޿`({?SԿFb? w˿ e< ?#;?4 ׉??`? ?@Q~?kL? ?Y|ݿ.@GyMٿ ?@/? }?пa I޿;?bƿ ? ?@ӹҿ d@ ï??}i? ?f_w"=?`VҿC?oٿ`+ Vӿ ?P? j@ο@:? sh?@Rѿ@п@X?@b?߿`:_o?`"?@L?,U!?`{ƿpU2l_ ÿ`k? ?I?ߓMP`JI?Ħ@~+@@9/? v@R???@ѿ*׿ƙ?@?@A1?@пS?`ӿ .?k?{?@Hw`F( ? 8+ 4?@ ?$/? b?@wM .[-ο$?` ?`?c?k`ே?߿K??`/@h?`ֿ`8?L m? ܻW 9=?@??-F? ¿ 3?ױ?C?-Ͽɯ??@Խ?`7Կ? 􀑿@;o$[8?/#?ÿ@m?#տ`r Կ@? ?@:a?-}`?ݎؿ @?@Y'?H@(? 8?| %ӿ'?}b?+??M?i:?@?B?s?YV?``I@??@ǿ : i}n?s?`!9@ۿ`*lX? ?; R߿F?w`ۿ`'ֿοe?eؿ0?($!????-?? m?n@Fȿ>n?&?&?NrۿL?L?????^Z@`ʱ@t?y?"]?B-`|_?@4#7?*? T(?: ?``? js?x?ܿ *?\?GHM?@C`?/qȿ`z/1@?a4Ŀ? s?n?_?"MƿzR?пI?`@?; 3? JY?ޏK9?h?7@9ܿ,Ͽ:? @V?`6@ݿ?@]@tt x?`Ŀ Gy??`8׿d ?o?f2? 4ǿ\?@ k@?_X?D׿? ^??hn@`@ ?3D?`o?8?w?@?Z`g Zw?@4?`?B?j׿`P/? ?u`? wV8qMV?|?#c?@?`9?`G @X`;?@ߍ`?)$?jҿE? }? 7ɿ\ 3Rӿ_V??? ?`T???` .̿ ? U?G?'Q@R? CnK?`??%Qӿ@?߿Ea?`? ,y?c֢??@ R? "?9K`"Ϥ?`? ? H? C N?rv?~ٿ`(f Wӿ &lֿJݿ`^Ŀ`?@9?`!? `l?KA ޿`lοv?@g6c ,ɿV `M? do?^?` 7M?# ?? }? jǿ[@J?@]ֿӿ7@`]o? "D?`Q`~޿`Ͽ@̿`Y?@"? A%? ? ߿`?@? 9)?o?`$ؿ? ڳ?@?Tɿ K"? +??@{;@Z_?T ?g?@?@@տ`W"?:?@`i%ڿT?`?S??@ +%?ɿԿcտ7Iп@8?@Z??[R? ?!eƿ`J@#?f?@i2 ? ? /CQFB`\8 eҿ?pg@۠?@@?%`!? t@& DϿ Rs@? r @^? ?`?s?g?I?-?`H+@9?@< CX`Q??` @e+? ڎ8? Lj??`?C` w?m8տ@WDzi?ZmͿ?"ҿЂ???@^v??`@z?п` R?@?ʿ?? A`\?A?gsٿ~?o`wH?2ƿ&?@94ֿC߳%a?H?@O?@?@В`^ȿI@ p?9!??`a?2? ?HR??@] e帿?9t`Տ??Cܿh?@=J?@N?H?$'I? ˿?i&M??Vn?@0޿{~ F ??O?ݿ[ ? i_?`?.U @?;R=ؿ?`Կ?4??? ?@?U?z@?@ s`y"`EĿ1@`Gʿ.zտ&C?Կ?N?P?c? ?HCx?Ͽ 7rw??s;ѿп`d?:e?`ϰ??K?u@.%?< b฿ V}?`͛:?7*?`Ϳ? ؿ h?mݿ``?@t ?L?w?^?D?? g#|??"?1?@%?I>ڿ~ѿL?@?@Rd?7ۯ?@eg?9Oɿ*?/ؿ)ۿ?@$ Ga(6 (aѿ`,?|P?Q?`?V ?@7)?`^-?@H??O+@9`p3?S?6??@K@i]`:-A ¿`N76?<~?2Hsۿ?V|Ͽ?tѿ@r?@l iѿ?`;ۿ\e`h?@@:ʿ/ s~? ?ؿ ޿ ?`?8 ÿ?[ѿ??WY?? ¦ѿ`O`i?e?Bٿ@|?`!???@?k2~? sT?WNͿ +?@?@ ??`? 1Q?  n ? 6?@e४;@`f?>af?9? 5i@]?J?M`rC 2T.?Z?@ ?@s{u? o?`? Zۿ`t?KS?@= n ?`\@?`"{?@[?? ?c? oHٿ ?[E? غ?Bڿ r? ? ?M?@1&ӿ H `¿P@?`SF??+~ܿGXXfo?J?@F#c@48?`LbA?g?`iO?D?Ķ?@U``?Q U? ]{@2? Ԫ`?oVO??Ď???`?`R? 5`m??Մ?l? @ll?@~?`` ??vj?@$L?'` (@@U?@S?@ b@?]@̿ ok?w?t?Q?#H?/׿B?` i@G%˿ۿ?@5ݿ׹`,?@'?`Zk`?nt;?̿7  4???@Dւ?@8m? l{?`?x?1ؿ,? [?`?@?ʲH~?vX`rm?޾? k?,?`~?? T޿`F?b?f@@$޿@@j?@J`9?@\VԿG`?@? ?`Zʿ H?`^?3?@K<@p?0gs?`f@ų? u? , c?hf? ? -??U?e@;{?(2 ǿWh;? c?X¿-"o?@Qn? ,ห A ?`Ghiؿ ?@@?=?@?= Q?`?I?@Hȿ`տVn?@?b ? e? ?`d@Rѿ^? ح@ { @@1?$Ç?@?)? e? nZ?+V?@>? `?$?? 1`g?k Pvq?@8z?[? H;?"pҿ`G ? \ `k?A??@z?Uֿ#a??``@ 76,T.?S` ??@dp? `?D@H՛ Ԙ4? >?@@?>?$q? H?|@*?Կ@s???ѿIrݿp@ۛp?@[?f?7ӿtֿ0`|ֿp q?F?, ұ?@FWݿۿi??ۿ;? ?M?hȿ@??@d? UJ?@8?@'vPm?G?@G?8gտK6?Ν?و? !ۿ<{ݿ _?wӿ@?> 1& @h? _?*9G?4?`X?ۇ?B@?o`G3@? ?@M6vɬ?@v%?~?w |?Eֿ?A?`?`?c{??`9 }?+u?@c?ؿ ? ?C?ഀ?L?`rj? ?n`Z?`H?`׿yO@7N` ? s?}?T?? /7?(9տz?@hi``B?)?@E?v:ҿ@r?@qy? ??`Щ?`?frֿ?e񲵿̢ >`B?@??`qEڿ N ?@տ`Ajr?|?ʖ? Wn?`?οN_? U@տ̿2L`?=??`|B@?ѿ9 `?+`K e7? 4k !`S?@6`{տ`?u?`p D? $B?v?@0?@``J?A?@?m?`K2 ? ?@dg?*i?|?>? ҿڿ'e !}ֿ?`iѿüҿ?`i?{??܈@?`?S?'?B? o@ ??ǹ??j?`^??`8?@?B??`j?@. r7? ?=R?@.? #&???`?[?ܿ/`Wݿvഗ? M=@%k?;?&ٿKH@DO`ǿ\?v?ݿYTο`̿@ܿ`AT?Q? Ԣ?Lӿ|?!W???Jݿ_?5p׿ [? Oގ?m??ݩ?G?`2ݿd@m^? JH׿@?9? ?yӿ @j?`?@?@I԰?[?@Ժ@?``}O?!?{1h?dcS`h?`;]T?N? a?`(B׿O `a? ‡޲ ? 3 f?@9?ϿvaB?@Lݿ @`j? ŋ? 0?E?`ߡ?೪`?@54?p?l? ?`Z@ݿ ( @ ??Lu?eԿah?0@???m? Iq??w?51? '`˿K?< ? ?E9[2?``? ;`?Eƿpy? 2?I ?\?.&?`?9?`@ {ԿW?l9?@?@4п@??.?Ho?@? 7M? b?`]??u@ ?@ٿ (?@7O`|?$=@? =@?@?@?@?_`@w*? t:?L?˿>" ]?LLA@Rf ?@@Ge`gƿ y ſ߶?@2\=?@? J??`F`Կ`\?w@?? ?` &ſ?*=?a?-i? bM?/a?.?@X?@ 3N "@ȿ?p@?"?sܠo@r?`?`G"ѿ }*?@"k@qտpD@^ݿ3?߿?;X?@ grؿ@ <*߿o?Խ?`s'? @*[?5Oc? >?`N?B4?\@:t@`IL`п <@v?`}?@d8`}- HB?+?*?H`"?4 S׿t?@E?@}?`Y?F?`n?)?@|? ?`E?X?H ʕ`PBKѿƅ? ?߿? d!?S?@f?`%?  7?:?M1`lٿЭ`{?c?q'?v?`?? H,?|F?@? p?wo?@w ?? y?`?@y?b)??v? p?c @?c#{?`?Q? v?y)̿@?\?@ "`W?S?LV?*?`ۖȿ@?࿘c@m`p@/ҿg1? ->??ӿ!8?#`?Pڿ`@ 4߿X? q??@,3 g ?`?@Q?@6Ͽ @f`8?`+ ?? ?*q5?ශFm?R?`J?`^??` ?|?R ǿ`jb 6V߿?n`?`i? ?v$޿`=?@ѿe?gA?oIQ?`\K?:?n??ଈ?@?`Z?6??b @K? q`o?F ?@`@@g?`%?@{?`V !_?n ?`F?0?`U?@,տP?'M?A]3!տ&?E/`??@Ԭ? z?G?f?@Bk пn?`?'?C?^}J zV?X?W ?Iڿ@;?).I?`RٿV?`PNO`?`??uY?I~տ`H "?@.^ҥ@c`\?@q EV?@MX?@@@?n?`U?@"5 ?g7?`a%? jɛ`XC?}ʿ *ٿ`o?ࠔ?ο  ތտ@ؿ40铬?`B 6S? ƿ`n*? "fD̿ 7?G?@ ѿ ƪ?Ɯ`7Կ˝?`Rֿb?9?J?߿ݿ`E@zޛ?V?dJz ?.?;V@@=1? @GU@?m>?/4"?`?Y ۿ ? ~? bf?Eοt?!Կ}?`4C[?@P?/? Xѡ?`xҿ|W??ۿ?B?JJ? ?go?L;>?? ?(ؿH??XE?&?N?"B?n1K?Y? =?#?r?Z1?@??@????+ ?`v.??^?M?@/?,߿b`}w@-2;ڿ?Sۿ`xh?m@$?t޿@@ӿ`J:?¿@'п`[? `? 5n-?}EZ?9@?L?AEr??@`?@vFz`6?iſ ?@Q? ^?kտ>ݿ ?,??.`p #qѿn`p?0Կ MY^,?@v?@? ÿQs? ?` W?ѿ@@? $?`z'?@ ؿӠv@@ڿ`Y? ??5@?t?n?8_? ?U`$ ͑ɿԀ?<r?`./ؿ@}d޿` Zi?`l>??`]Kƿƞ?@H?`+??6ѿ@?@?`@'@B}:?`B`??`;#k? ? r)1f:i?пU?` f?@ ڿ`+sQ?? b?@@,ٿeu? =,?@>l?@U?@e?@1?+YT $eԿW?? Aݿ`hQ?@t@?`u@u?o@ v ??w??༉``ܿO?:? ^'l@? -%G?`h^oӿ@3 :o?n` _m?@U+?`pg?@(?T?d?`Sx?@D??@? D`*@ӿE?@?`T?IQDڿ{ο ?6? 5@?L?@S?@¿` D??Þ?X`d?`? ? ѿ@s?s? `7`.ҿI?@"?Hh?W?@zؿ)?U?`?W?~?`g?l QxB? ??o^?`n̿ w?ҟ Q?9C?Ь??_)l\?a?uf5 WN s@^?c@<S?@*G@@gN?@=@R?Pn?@j?@?`6?``h?@?`o @@Z@,ǿn?.ݿ`??#p`u??OB)?꛿@l3 u??ۘ??` 1ɿx4@3ѯMl޿ ? _ H?!@:6*?@?@5?s?ӿ`2a ?~?Wn?{?#?E\ٿآ?`Y??9ݿ9c `E*̿^Ϳ.?_?`B`ĿԿo J׿Y , ]?@Gf? ?`H@p(@aA??` ?@(?O?`?? ?`ы?;aɿ b,N?@(?q?JO?@?@ ?`ڿ V?_?ь?Jq??п@r@Lǿ?u@sP?`X???@Q??`P?|п`ܿ'ۿE(׿ z-`p?p˿@ƫ?:?`??UG?X޿.?`-??1? ? RW/ݿa# ??2 AϿ>j?@;ֿ`?Aqx]*? y|Xܿ`/` ? |?@f?@I? ?`? K?1? z??T߿c?4??`ѱ,޿H@ L?_$3?@5C`?$%@2?=¿`Y`e:?@P8?` ( ?` YtͿ ?P@@? 8Y G@j? >@`s?[?Տ?Z?@']?`m?@3?@?`e?@t`}`?@B?|?$0o? ]P?$?`O?`Y?`8ܰ@`an?9,4?M? {4o?*"?@߿yeڿ@2@`@$?P@fпdܿ`i?q? tɎq׿'2r?X`i?9?1{ ]?`??Կ hx?O?q M-Rÿ` _ ?ڳ??$-:ҿ>ѿ`{?A??` "?M? Jӿ#? Q@?f2? "??@? U? j9??.?%1?`mt?V> |uG uS ?Cѿ闻 ;? i ?`?$ҭ d?{?O? RUv3?`1?X ۿ@?@4 ?`߿M???;?? _ٿ _n?@A˿d?/?s`y?@D?5?` r??] c:? g#? d?%?@A4?@B޿@tٿP? "@@Ab?> f? 8? :?ݿ@@wѿx`ӑ ?v?`:?`ȿܿ@s?`<ؿ`? ޿x%Y?@Q?t? ˿?@ݿ "Mɿ`ΑĿV"r?@pRu? `Mt߿z߿ZJsPj?@?@_`5Ϳ`@`P't?GY?@Ƈ?`Dž?ɒ`=ٿ @?ȿ ?`b ̿c&?@dža?`)Jܿ`Հ?@?@`a??-)ֿ@@\@W? ?п?? % N z߿T.@b׿Q@` ?F ?`!?sA꾫??@(XC?οJ??`? ~,ܿͿ`jѿ?`3 @ ? hӿ h?w?`?`A>Rٿ@?@Z?`}u?FѿM)@? ?Qؿ`? *?l?`K?F?X?;?ٿ`)@L?? a?Z ?_?o?@ƿ=? ;?>? :޿έ,?S?n@t??`(@@?Y?e?[D? SV`@k_<ʿ@lb7y?@? tD?@?@?PK >;޿l??`"ܿ ?_?@ @`ֿi.B?1?B9??`?-?`^Sѿ`y?໰?67? @`T޿@NC@ ?Ȝ?߿` ? D2 _ڿ`?`0ԿU?uF? ?? ο A????@ձ?ҟ`? @! ? V 7 z?`?`? _y? LಂֿU?`زF?7@K?r+? M@?b??`?(?ȵ ڿwNJ?@@@Q?f?*ѿ)?Wp?@ҿ@&?`~@? ?`z?5r? g?:?@Մ?`K˿%' @jÿ+? 8N Q?`5.?`]?ۅٿ@"?@ÿ 4??Oۿ?@F?`,@Sm? ?C?帿[@@?@?_>?޿CL<=?k̿ٱ?@ۿk@\?8g` )u˿+?? M[ @K&?x?@{?d?P?HϿh?5@`ڿ@}/%< V?@q¿`sؿ? ? ??n @7ӵ? *oؿ@H)N`%F?-Y@`?"?¿۾?@5?z?u7?4kzͿdw?<ؿ`<' ?`s!e?`j|@&@ɿ`Q\? ?`֩@?t?@y`?`@??c?h[?gǿ``?Տ?. O???5?`p^? y=v@`$?G?`4пYؐ?`? .- ֿyl??տ Yr?^Nmh?.QϿ?'r2P?Y0=׿ܿ]v?  ?8?ɡ?`l?'i=@Wܿ ?¿6 ޿ <RɈ?2Bƿ@جjWۿ Կ fi?@p`G$@M?-u@Z??3?p?@5ؿٜ?`j_?> ?z?@(q`vۿ%??@?`?`ǿ?K߿HԿY?D r?Ҳ?``@U;?@`C?`E׿d_տ4R?`B? eJ'ڿx?"?Ʒ? 8o?@oP "+?_nn@`V? d??W??B8I?x2?X? ?¿e?@/Yɿ ` .@ ޿,? ?ؿ'@B@_t?@9@6?[1׿@i???1? v.ؿ UB;?@n? _??Oο`BXȍտX?]?U??yx? q]wҿ`p ??2M@ Rӿ 6?@0, }??`?-п@?ɲ@?Q|JzΤ޿ ?`2(??d:`?L't?p?e?F?~oZ? `?x?׿*??Q?T[?@k?@5w?? -?п``[?? ??SV-#r@F? @e?s@U??=ܿE?`b?6?@S`? Eanտ G?Zbҿ@O:Ͽc?kҿ?!ڿ g?F?M@A?ϵſ?`S)? Y?b?H? ?i?É eֿV?Igeܿ`?H?٘ӿ i տ`f? }v??`]. ? 9?;l߿aȿ`@>e@q{R@&fj`?v ? 6|?`?٥*?`?@0@DЪ@ ?` `?`ǿaPۿ2 ֿ0.?@J? Ʈ?@Կ:`G?? |b?3O׿9?$ȿ#??r[?7|ɿ ~n@ۿ ?+?Z?S6?\u@?Q@~m?g Z/ i:@n;?@ Sm+mS?u@4@FfČ@A?`D``?ɯ?׿`w? HÿF?%޾`@Ԧ?`?RͿ A:? ?ӿ ?@?[? }_ǿ??@?`x?`f?ؿ ??@4b? ut?`?nǣ??`ѐ@? F @oҿ=?6G?CC?K?@,տg@ӿ`E?`V%ݿ`*p??Az? >??Ͽ`3?? |?`@?@>:?@??? 8?M? ?"8?ſ? %`z v0 ?`?6 `bf?NH?#,?U??`? 8Ŀ{ο@??~-?`?`E?o?UB3bʷ?`:?1v@Lȿ =`[ٿwؿ^?Gٿ@a@@Cÿ bB?``?y? ? ? >M̓ 1?@??mv? t? ? 0?T??@?Ŀ`ی?:(? ^?^?`̿@re?@:! ؉?? L@&??@E/?@@E?`@ ???Mv?RH ?@?@?@6?'eԿ@ Z?? ?Ie`ְ1?`+`?ÿ?V? =?A^`/?`?B??@?DIΤ?@?~JpZ/jٿ@ \ܿ?ݿ@bѿ = ?@0Hֿ.?@f?ف?@V?\`:? `I?h"? /@@ @,?ſ&xq<q?E?`; ?(??ˠ?@#? 7ӿ`?`f?`^?`?wh??,@|`?`^`k 1c?(H{?p?y@b?#'`&?`eK?׷? C?|Sr?`z+տp?oy?1@???? +ӿ ?@i? ?`α `ʿ ?O,2?`W?@Ŀ)?}c ?w{ W?,?$G?A?? HпgX?@ [ٿ @p?пƱ@VF?`|? ?8?@T? ?8?Ӂ?@aM?@7 ??,H?@)y?`ل???V?as@˿`Tn??@#??? 3v?@]?F? տU??@.ѿ'_??z??`騿@:0`5@ `?.׿Ό`? o?`t?`bֿkP? ? `W Lÿ`ڿEz S ?w?`EY?ܿ?G* >??r?-@?<_`S?c???`L?`,?ܜ` ??sӿIS߿ S?e?ۿ?@Y ؿ`ɿ@=`zĿ@H???n @8׿˷?@`Sm? BD?@Ŀ؄??h k? x`MTҿL f?(?@ҿѿ?ٌ? ;C?-?*`t_3?`cտc?`t>? J& S[ʿ`/XF?"?4?LJ???-v:?@п?Hb`?b?`n@<ѿ|`iک?nO@7? ƅٿ,Ͽ;"?`Fѿ ؿ`C޿ '@7?@??i9?`d?@ɥ' @?#f"`i@j?@q2??(?@ ?@ʿS?+.?{A?9?@,=?`ʿR#?༆?%ο@7U@`?@mAR?`I ſ`~?Hٿ "߿ zտ@g3տ?@X??`ؿ`?{kĿS׿@? *h@u? @~?@?y!п"z3ֿ ?`;? ?L? ?bE?@amCƿ(?~  ;?/? 0 -?5@ј?ׇ`J?sǿ@=p? @F?`Kp?D@$`9cdm1? ?j?0?;? @D? 8ӿ?@?:?ݿY4P@@ʽ ?ܿ ?@? ۿ/?@XJ4?`Q@LW?]??i?t&?:$?II?T?~HE- d~`O`Yȿ??p?<' _1? 6?Y^ؿL?? ??=?R?`sտ t?@ۿ |?w?߿Ul?? Cv??e?տ@?gٿ@)? ਺?B?`6ܿ@@ @Կ@y?`*`K?`!?$?`ԑ?`>?ٿnQPɿJ@?u?}i? JLۿ 4Ͽ'?`˫y_?3@ٿ` ӿ?!? v?`1[o?.`_?K0?>?? *Q`k G2׿`?|? +2߿'f?l? &@??0f?oۿV?My@KG?` '?@ @#ESݿ&?I? xla޿4cZ?{@_?` a?}ſ@?X???^ ?(?? ?`8 vտ8?eJ?G%l?b?@?׿`ۤſ`pSؿ U?`t- v? ^?AKl`׿S?@l?U?D A?`w2Y? +?`Pf? @ ?g@`?ࡨX?s?2?̿ ??w?`?@w:? wRg?G@??0 AL̿^$?`?"`?2@?`v?@ ֣?:?o?@Yes=?̀?جֿ@n'?! ?@v?G߿?` q?N?L??? N@@s?o@\ǠԿ@R?ེ?'߿`k? ϲ?1? ܕ=O} G@h?@Iܿ .?`uD8? b`?"b1J@`? пȲ?|0ؚ?x'ֿ̿ Y\ ՋѿJf@^>?'ܿ`Qp{:? r?.?,S?@ ??j@ ?@ehп?@G$?fӿ@?#?b? ???Iƿt!?`2?`?`X`x?0?`?=??@? ?oK?A)@|?`W?L2Ϳ`l@ eҿV?'rԿ> @Ĺ?`6??^E? %Dܿ?`|`??  q?Ͽ$`? OJܿ@ع@ aܿ t?g`.yx ?{9?!?D?`?9? @@r~I?@ ڿ|i[WL? i{¿@ƕ@@lV??H?5`/o?@R?? 1D?6R?Kп`I?5,?B?\`?V.? +ҿ Ӟ`%.S?U?3? 5?@A@@@طŢ?`֒?@Dw D)?b +?G\ۿ Y?3?Lb?@y?Kf?>ѿdc?7??2?'@?`?`u? ,J? \?'E?@ [?nAږݿ`m?t8f??@u J?@ؿmٿQ?>D ?FQӿ1?`l?@g?q? sͿ?2i@ 3?$?  ?7v?6? gbd?r?.?>?? +A?U?g?N޿` ۿ`?JV??/?ͿE?c?F ݿ@??v?R?`B?9?;K? i?V?@z@`Կ ?c?d`|?@R?@׿t?س@р?``?@?8? VοQo?C@J @ÿ"M,`XܿV@?M@ʎ??@k?@߁?@s?? ^loK`? l@pO&Ϳ@%? / @@ؿ?U`kUֿ` vv?`?h@<?ѿ7??@???d?@75&ҿ޵ |(?b¿tT?@? տ N?@`?y?@&?A5?w?i@%@S$@?7?`&O? 3k?׿ܿF?@|?6G0b0?@Qп ؿ@4?@!? F_`?1V:? ??K&ɿ?Z߿n8?_ο >?,?6UX&L?ڿ Gd?P` ?v?@瞾"!տ@?vW??@p@b `d@ɫ?@)+F:?'"?o`YտmX?@Z̿U? t@ ף?`w1? ???@/??`? @C*?"a??`?`pm? ?&??`j?'ֿ@;ſ,?޿4md%ۿο5?x`KX?`h-ݿοAl? Lg׿ vR?^?M ` !F?s?U?Eҿ? h?? ??t?`G @dM ƖϿۿ@>ٿL? ƿ`J?H_?`?X? mn??` ? L?wY??tݿ`?&?sJ?`D? g? ?A@DwϿڿsO`w)`y!?`6F???_>׿` `@[ X%ڿ?`.r?`Ϳo? d?g?? 5=?@Wȿ@??@d?J-߿?I .ο@dZVX?@ @`s`0?@mܿ1@ /t@U? .@5 >^?@4? 1ҿ@@I/?`9 ??ຸܿ?` ?Gƿ@?Jп+?)?@1?@??Ax@!?i@K zĿ .ݿ b@`?`W?Wӿ`6ſ ? ?@^? ܸ? , Sz@?OƥUտ?@?@?`? ?6,@Ċ?`@? ?#??1/?@e?B? Ǥp?`uD߿J?X?@?BY? ?@g?`*?@?`?k?($?3@F?n? v߿`ҿ~?0޿-ȿ&w`p @ω4L?f?`j ?@?`a@@x@ *ݿx p1 Pu`@?`?9b ?0Ƨ? ,?@;@?E? @c?Zeӿ@l`/?`3`Ԩ@*d /'?`L? i? Y ?@?9v;?n?`6?~`f? ? ?`9?,ӿ@#??[GA}@'ۿE@ ]?䝡?@0 @@5@qѿI``? @_;@ǿl߅? }?R*?cֿ@?@?S#?2~j?f? +?zݿÏ.ɿO`gx@ տx??@kҿ =5`@?jSҿ`??g?38?k?)ݿ@?`1Q? MJ|ؿ@???_?`d1@e?@a?`&?+?Կ ѿ?@??x8ʿ ʿ PB??>` ^ѿyǿ`a?@?տ /?l -@@s?lĿ3?@;ඩ¿R?+?b? '5@@?l?֔?z?,+޿ ? ? K??BO`!l3`] Xٿ@?@onӿ5~??ֿ@1?4¿/@?XM? ? F@T?rſu[?L?[w?y9?"`@׿ bZ ?`߿ {>?@e? V?`@S1?@L,`?e`h?`ſlVѿ`?`@``Tֿ@? ΀` ֿU?zC?Ǡ@? @m@Y?/ [p??Ŀ?tJ?@: ǿc?`ʓ@ t?ܲۿʡԿ b?y@ÿ? `=H`b-@`ݿ&@Vۿ@@@?|?`|a?@<Q@@?Em`M`@?oش??`T?.@x?@v@u@$?,?@@{L ?KTĿ|?@N? 7տ? `c@5?@?`(˪̿ ?x`x?=տ`d׿1? l?޿@l#??`b ??8? 6u u`h?<@gh ?H?@*? Tп2f?L``J?֮`=?-~?w?ޓBD?W-Q?@EF?dL?` b?`??`?/Yؿzֶ?ѿ`r@Ŀ ?@ȿ >@?@E?2޿e?>?@6?2J M?ֿ`U@ fG?f@f}?hǿqտpY??@ #?.b?? >0?YW<`&?#O?4ɿ@Tؿ?p@?+? r ԿM?`9 ?ۿǟj? ſ? s?`h?@?``@9G??#Z?@Erɿ"?,޿m4ۿ}?0`CJ? ? zH?b ˪?x? ?` ?: 0j A @ண@S?.?a?s?A¿tv?`V[Q?\Q?#?`eҿʲj ? (??`?@`9п@}?U? r@{?wA? @q@v?@@Uܿ깜?@ݭ<{t????X޿@0?~? ҿ (??^?O$ÿb M?`?`w?f`!NQ? sb?U׿n?`W? 5?g?R|s? @?|?^ o޿@?-?.??CԿm?dտwݿ ey? v??Ƴ@??`C? 3Ö 8حۿ`J?@`z ?? ?@w<@ U? d `qa?k?`ɞ# ܿ`ܙ? )ٿ Rѿ`;?5? v޿Sۿ~? ǖп?/ Ŀ@??S? ???a4?`4$?@e?K- ҿu?e ܿ ;?M˿ nۿ`??@)?̕? |?@0 { ? ?T?y?@8?iSѿ`;rC g? ?ѿ3A+࠸? 0?a~߿ u ?NwݿY?@S@ )?`̄??#@ @pd?c?`Z.?̿y?? *޿??aB ^`?N ܿ ٿkU??`񁥿;p@kѿ?y@?Hu?U6?L?@  cf??Y ݿx?@=勵49? N?K޿-D??il?`? @g?]{? տ>]g ?/`B@\߿`A?ZAֿX޿jۿ Կ?@񊷿@h`&e?`t%? ?"L?e`}?`+ׯ?Ϳ`?@e?`b ?Dຐڿ ܱ?@߿;4@*cٿ)s@:?sܿ`?>?n?޿e? ˿?`Y?@BOc?g#?8+׿ ?@;׿3ѯ? |@M?@F?` lj~ֿ ?ۿ`+? 0?܆?z@A`Ŀق?Uο? %?ܿG?@?`!ѿB!?#?@?5?,¿ c?Sҿ}A? bB?v]@~` 5?6ؿ ҿ?`<2?8ڿ)?e?? y@z?`h?چ?!?@ ?-׿? (`‘?\?`?@6"&?@f? ?Ӕ?L?*z !?`IX?ֿP9`?@y`> 1˿G?``@?1?i:?v?@?ǿ!m@ `??KHB?`Q̿@*C?٥?8% !? P?`("@ gѿ п?@ݿ`q? tk?w?``̀ @h#?bĿ?Ya M?`WԿ@?? `!?`H?5?p ?`?gð?`??@V?@ax?h@[??h@?G???`wؿQ | ?`k?@?@?@@gUп`4%̿ zZ? Ƶ??? ?y? @?+?̿ơ?`v?@?!r?W ?Ͳ?`ƙ$?`2? H~=? o z ?`ȿ`7A?7?@"ӿ@v?@$?@l׿3Q?@(4?>?Q?@͈п@!޿`z? Zѿp?@o? ?@C?;a?߿*ؿп>@)?@5o? !j?$?`?@¹G`R?yE?`1@s]?h?A4?M?\1h@?Bſl@?@ܳ@@N@J@ֿU? {3?9?` h?`K?@fܿ 8??@E`` Y5?8?m?``l? `??T?`?h¿ 4?xY*ڿК?@ @1?>?"@E3?@?`W?4׺Y? G]?Sy?``?@8@?`J&ҿ@?ѿ ? ?em@ ?0? ???!߿@j1?e`xп@`?@?lȿ`]ֿ|^ w`?q?h?t˿`ZtS?` n? ?sW@`x?`?`??bf?U?a??($I?@1`޿𞨿Be?@a `x?@п`Ri@N?_K?n??A@? g?tj? ?ڃA?|??xп4o?3? 3?ii? {` `9/t?6?ܛC?`?V? ؿҕ8? b?u ??@)?~'ӿY?O̿`x?(?q@c?`#&???O?տ ?ZG?`j?:W?`?  Aµ?-`_?`׿ ;`g? t?b?ʼ 1ѿshzy?W?`2x`^a? ?sɿ? Xٿ~E|?E @`r?%?2<ݿڐ?F`?"??yD/?p3?Ͷ`* `?qG_?`? *Կ@S@i? ]E?؞?@ݿ?` ? _KG????,?? 24V?$?? ;'?m?`G ?տ OxG\X@}?%̿ݿ߿J??`e?d@?`? /?E?JXL߿`,@c?`?^߿ z?A??`׿`?@k? `? ??;?w?k?`|??@y?ཻ?? B???Ua??f?O3 ԗɿk`K@? I?]?@%q?`eU@- @.?tZ`Eݿ p?E?!.޿@@Ib +R?`V?@ ?ŷ?ʿ@⎽?'1?@ 8h?R Sj`ٸ @@ f?ʒпͿq{[@?5?Ѿ?Ev "`0?$=Z?@u?@?@?? GC?s?΃Ƚ?[??h?ܾW? ?@?`iοൖz`^W? `@?g??@^߿`? @`cR? `WNҿ ?T+?@?$?.? ?`?`??:?@e? ?@R??\?Z@? 1ٿ 0?`?z p?@D`u/?4?R?@?``J?? `v? ƿ.滿_?Z? ?@r7?@L`-?`?`W?8 0=)j?@ ??9?n?@F? ofѿ5d?C?`z? ?S$ʿ?@)N@^e?zx?`ธտ?`տ`]Ƅ ju? Z@?`VԿ@?x(N? /?@@??I?@u]t]?h[!a?@7?i޿?`? 6@?ÿ ,?y?@lпxK@v?`5ſ`ڿ`aۿ?@п %? |@`@?I7@s? _ڿ>\?@?m?@&U@޿6?@? ?@;o`?` ѷ`pK#>T?`k??@c u@o= ?Q? ?Aѿz`<˿|=?n???`Y?@Ӎҿ`?`-9b߿ p.@9|`¿ q?`@?ƿ`?ɧ?xN޿ ?@R?k?\?@?G?`q@s?/ ~ϿT`+?`yۿ`e?˿ V?@6|?? X@v?Ѩۿ`w?? 7? :%?,:P H?`?\Y?@oҿ`@(ݿ@ҿ Qw? οO? ? Ƶ?w?`BM?? Vп 3߿ g#? z??@v?(?`xӿ u?R7ƿT@j@M?+ο??ޕ?T@`߿?r?`? о?-)?@4`? ߿`n?0@?@BοT?e'ܿ F? J?|T? b?9?`C :?? Ͽ`J̿ }Έο@J! Ȇ@cr dj?_@M? E?`Q?-???0@QԿ@`F\?^2ɿ`C`տPM?ǰ@' ǿ 0`h v|??\@@?@?ѿIԿ`Ŀ`^S`? p?x?W?`?D?? ?,?1(?@Ǚ?^?g ?K>?@Bӿ@?}?@}@L ?\?f(^@?$f^@v?jۿi?%@P?`¿@ O????` x?)?&@F9ֿ\o ܿο #iڿ `9 (Ͽ0@@?e?޿@F~?z?`e8`?>ο]?D6 ?߿ \ ̿@w?[?}?؊j?@2?@?@Xҿ RF?4? n?Q@d@Xh?j)??ٿ?@k0 kz? G-!ܿ!? `F:? ? w?`cw??5h?`??:Z?7Ͽ >?p??@?`?? H߿TԿIXK??`L?@'jzڿ`y?@ 4 t?O?6?ؿ?@J˿@,?@Eٿ@B Bw?H%* ?S|ѿ?4?< `w?2?_? o?`*(?`ÇdK?}ӿ??i? ?v`Fz?2n?`?@bԿ@?B ? 6@<?O d ޿8i?qTؿ`gn @%?@?]8?@w?fOAʉ?@a|?@I?`o a?J?Wſ@-? -E??`f=! ?޿󞋿@IGɿ%J׿ ?Ͽ@/E? +?A?? |q˿\?X?`?U? ?[ e$?`Aֿ ?? @द?ֿb~?Lп%?'ݿJGa?`?b?r7`;?y? `$*ӿ?-&ҿE? gu~ ? 4?? ψ ѩFп@"޿4|?w?J? i4ʿ3տ`A?jF@w?)?ѿd?@?@u ?-?@? ؾ???ȋ?w??Y{? ? HiY?? ׿Ҿc ;?@Ez? F K` ? *??e?T? Dmq? ?b??T4?teem-1.11.0~svn6057/data/test/tenGlyphBqdAbcUv.nrrd0000664000175000017500000165165712025165166021557 0ustar domibeldomibelNRRD0001 # Complete NRRD file format specification at: # http://teem.sourceforge.net/nrrd/format.html type: double dimension: 3 sizes: 3 100 200 endian: little encoding: raw Gz?Gz?Gz?Gz? ףp= ? ףp= ?Gz?ffffff?ffffff?Gz?(\?(\?Gz?Q?Q?Gz?{Gz?{Gz?Gz?ףp= ?ףp= ?Gz?433333?433333?Gz?(\?(\?Gz?Q?Q?Gz?HzG?HzG?Gz?p= ף?p= ף?Gz???Gz?\(\?\(\?Gz?Q?Q?Gz?Gz?Gz?Gz?p= ףp?p= ףp?Gz???Gz?)\(?)\(?Gz?Q?Q?Gz?zG?zG?Gz?> ףp=?> ףp=?Gz???Gz?(\?(\?Gz?RQ?RQ?Gz?\(\?\(\?Gz?Gz?Gz?Gz???Gz?Q?Q?Gz?> ףp=?> ףp=?Gz?(\?(\?Gz?Gz?Gz?Gz?ffffff?ffffff?Gz?Q?Q?Gz?أp= ?أp= ?Gz?(\?(\?Gz?HzG?HzG?Gz???Gz?p= ףp?p= ףp?Gz?zG?zG?Gz?PQ?PQ?Gz?(\?(\?Gz?433333?433333?Gz?p= ף?p= ף?Gz?&\(?&\(?Gz?ףp= ?ףp= ?Gz?Q?Q?Gz???Gz?Q?Q?Gz?Gz?Gz?Gz?l@Gz@Gz?Lo-؂-@> ףp=@Gz?&DDDD@gfffff@Gz?Z[@(\@Gz??qq@Q@Gz?M@zG@Gz?I@ ףp= @Gz?1` @333333@Gz?t@\(\@Gz?},8@Q@Gz?#O@Gz@Gz?ɛ@أp= @Gz?oS'}'@@Gz? >>@)\(@Gz?TUUU@RQ@Gz?azkl@{Gz@Gz?2-؂@p= ף@Gz?阙@@Gz?T[@(\@Gz?Xq@Q@Gz?@HzG@Gz?FI@p= ףp@Gz? ` @@Gz?7!"""@(\@Gz?878@Q@Gz?ަNO@Gz@Gz?^efff@> ףp=@Gz?*|'}@ffffff@Gz?͒>@(\@Gz?v@Q@Gz?=l@zG@Gz?ւ-@ ףp= @Gz?h@333333@Gz?d[@\(\@Gz?q@Q@Gz?[1333@Gz@Gz?HI@ףp= @Gz?B_ `@@Gz?Nuwww@)\(@Gz?8@SQ@Gz?iO@{Gz@Gz?@!@p= ף@Gz?'}@@Gz?>@(\@Gz?2H@Q@Gz?l@HzG@Gz?~+؂-@q= ףp@Gz?$oBDDD@@Gz?&Y[@(\@Gz?poq@Q@Gz? ףp= ? ףp= ? ףp= ? ףp= ? ףp= ? ףp= ?gfffff?gfffff? ףp= ?(\?(\? ףp= ?Q?Q? ףp= ?|Gz?|Gz? ףp= ?أp= ?أp= ? ףp= ?433333?433333? ףp= ?(\?(\? ףp= ?Q?Q? ףp= ?HzG?HzG? ףp= ?p= ף?p= ף? ףp= ??? ףp= ?\(\?\(\? ףp= ?Q?Q? ףp= ?Gz?Gz? ףp= ?q= ףp?q= ףp? ףp= ??? ףp= ?*\(?*\(? ףp= ?Q?Q? ףp= ?zG?zG? ףp= ?> ףp=?> ףp=? ףp= ??? ףp= ?(\?(\? ףp= ?RQ?RQ? ףp= ?\(\?\(\? ףp= ?Gz?Gz? ףp= ??? ףp= ?Q?Q? ףp= ?@ ףp=?@ ףp=? ףp= ?(\?(\? ףp= ?Gz?Gz? ףp= ?hfffff?hfffff? ףp= ? Q? Q? ףp= ?أp= ?أp= ? ףp= ?(\?(\? ףp= ?GzG?GzG? ףp= ??? ףp= ?n= ףp?n= ףp? ףp= ?zG?zG? ףp= ?NQ?NQ? ףp= ?(\?(\? ףp= ?633333?633333? ףp= ?p= ף?p= ף? ףp= ?,\(?,\(? ףp= ? ףp= ? ףp= ? ףp= ?Q?Q? ףp= ??? ףp= ?Q?Q?pGz?l@Gz@ ףp= ?Lo-؂-@Gz@ ףp= ?&DDDD@> ףp=@ ףp= ?Z[@gfffff@ ףp= ??qq@(\@ ףp= ?M@Q@ ףp= ?I@zG@ ףp= ?1` @ ףp= @ ףp= ?t@333333@ ףp= ?},8@\(\@ ףp= ?#O@Q@ ףp= ?ɛ@Gz@ ףp= ?oS'}'@ףp= @ ףp= ? >>@@ ףp= ?TUUU@)\(@ ףp= ?azkl@RQ@ ףp= ?2-؂@{Gz@ ףp= ?阙@p= ף@ ףp= ?S[@@ ףp= ?Xq@(\@ ףp= ?@Q@ ףp= ?FI@HzG@ ףp= ? ` @p= ףp@ ףp= ?7!"""@@ ףp= ?878@(\@ ףp= ?ަNO@Q@ ףp= ?^efff@Gz@ ףp= ?+|'}@> ףp=@ ףp= ?͒>@gfffff@ ףp= ?w@(\@ ףp= ?=l@Q@ ףp= ?ւ-@zG@ ףp= ?h@ ףp= @ ףp= ?d[@433333@ ףp= ?q@\(\@ ףp= ?[1333@Q@ ףp= ?HI@Gz@ ףp= ?B_ `@أp= @ ףp= ?Nuwww@@ ףp= ?8@*\(@ ףp= ?iO@SQ@ ףp= ?@!@{Gz@ ףp= ?'}@p= ף@ ףp= ?>@@ ףp= ?2H@(\@ ףp= ?l@Q@ ףp= ?~+؂-@HzG@ ףp= ?$oBDDD@q= ףp@ ףp= ?&Y[@@ ףp= ?poq@(\@Gz?&Y[@ףp= @Gz?ffffff?ffffff? ףp= ?gfffff?gfffff?gfffff?gfffff?gfffff?ffffff?(\?(\?efffff?Q?Q?efffff?zGz?zGz?gfffff?أp= ?أp= ?gfffff?433333?433333?gfffff?(\?(\?ffffff?Q?Q?hfffff?IzG?IzG?hfffff?p= ף?p= ף?ffffff???ffffff?\(\?\(\?ffffff?Q?Q?ffffff?Gz?Gz?ffffff?q= ףp?q= ףp?ffffff???ffffff?(\(?(\(?ffffff?Q?Q?ffffff?zG?zG?ffffff?> ףp=?> ףp=?gfffff???ffffff?(\?(\?ffffff?RQ?RQ?ffffff?\(\?\(\?ffffff?Gz?Gz?hfffff???hfffff?Q?Q?hfffff?? ףp=?? ףp=?hfffff?(\?(\?hfffff?Gz?Gz?hfffff?gfffff?gfffff?hfffff?Q?Q?hfffff?٣p= ?٣p= ?hfffff?(\?(\?hfffff?IzG?IzG?hfffff???hfffff?r= ףp?r= ףp?hfffff?zG?zG?hfffff?RQ?RQ?gfffff?(\?(\?gfffff?533333?533333?gfffff?p= ף?p= ף?gfffff?*\(?*\(?gfffff? ףp= ? ףp= ?gfffff?Q?Q?gfffff???Q?l@)\(@hGz?Lo-؂-@Gz@ffffff?&DDDD@Gz@ffffff?Z[@> ףp=@ffffff??qq@gfffff@ffffff?M@(\@ffffff?I@Q@ffffff?1` @zG@ffffff?t@ ףp= @ffffff?|,8@333333@ffffff?"O@\(\@ffffff?ț@Q@ffffff?nS'}'@Gz@ffffff? >>@ףp= @ffffff?TUUU@@ffffff?azkl@)\(@ffffff?2-؂@RQ@ffffff?阙@{Gz@ffffff?S[@p= ף@ffffff?Xq@@ffffff?@(\@ffffff?FI@Q@ffffff? ` @HzG@ffffff?7!"""@p= ףp@ffffff?878@@ffffff?ަNO@(\@ffffff?^efff@Q@gfffff?*|'}@Gz@gfffff?͒>@> ףp=@gfffff?w@gfffff@gfffff?=l@(\@ffffff?ւ-@Q@ffffff?i@zG@ffffff?d[@ ףp= @ffffff?q@433333@ffffff?[1333@](\@ffffff?HI@Q@ffffff?B_ `@Gz@ffffff?Muwww@أp= @gfffff?8@@gfffff?iO@*\(@gfffff?@!@TQ@gfffff?'}@|Gz@gfffff?>@p= ף@gfffff?2H@@gfffff?l@(\@gfffff?~+؂-@Q@gfffff?$oBDDD@HzG@gfffff?&Y[@q= ףp@gfffff?qoq@@ףp= ?&Y[@(\@Gz?$oBDDD@Gz@Gz?(\?(\? ףp= ?(\?(\?ffffff?(\?(\?(\?(\?(\?(\? Q? Q?(\?|Gz?|Gz?(\?أp= ?أp= ?(\?433333?433333?(\?(\?(\?(\?Q?Q?(\?HzG?HzG?(\?p= ף?p= ף?(\???(\?](\?](\?(\?Q?Q?(\?Gz?Gz?(\?r= ףp?r= ףp?(\???(\?*\(?*\(?(\?Q?Q?(\?zG?zG?(\?> ףp=?> ףp=?(\???(\?(\?(\?(\?RQ?RQ?(\?\(\?\(\?(\?Gz?Gz?(\???(\?Q?Q?(\?> ףp=?> ףp=?(\?(\?(\?(\?Gz?Gz?(\?ffffff?ffffff?(\?Q?Q?(\?٣p= ?٣p= ?(\?(\?(\?(\?IzG?IzG?(\???(\?r= ףp?r= ףp?(\?zG?zG?(\?QQ?QQ?(\?(\?(\?(\?433333?433333?(\?p= ף?p= ף?(\?(\(?(\(?(\?ףp= ?ףp= ?(\?Q?Q??l@)\(@Q?Lo-؂-@= ףp=@`Gz?&DDDD@Gz@(\?Z[@Gz@(\??qq@> ףp=@(\?M@gfffff@(\?I@(\@(\?2` @Q@(\?t@zG@(\?~,8@ ףp= @(\?$O@433333@(\?ʛ@](\@(\?pS'}'@Q@(\? >>@Gz@(\?TUUU@أp= @(\?bzkl@@(\?2-؂@*\(@(\?阙@RQ@(\?T[@|Gz@(\?Xq@p= ף@(\?@@(\?FI@(\@(\? ` @ Q@(\?7!"""@HzG@(\?878@r= ףp@(\?ަNO@@(\?^efff@(\@(\?+|'}@Q@(\?͒>@Gz@(\?w@> ףp=@(\?=l@gfffff@(\?ւ-@(\@(\?i@Q@(\?d[@zG@(\?q@ ףp= @(\?[1333@433333@(\?HI@](\@(\?B_ `@Q@(\?Muwww@Gz@(\?8@أp= @(\?iO@@(\?A!@*\(@(\?'}@TQ@(\?>@|Gz@(\?3H@p= ף@(\?l@@(\?+؂-@(\@(\?%oBDDD@ Q@(\?&Y[@IzG@(\?poq@r= ףp@efffff?&Y[@@ ףp= ?$oBDDD@Gz@Gz?}+؂-@Q@Gz?Q?Q? ףp= ?Q?Q?ffffff?Q?Q?(\?Q?Q?Q?Q?Q?Q?zGz?zGz?Q?֣p= ?֣p= ?Q?333333?333333?Q?(\?(\?Q?Q?Q?Q?GzG?GzG?Q?p= ף?p= ף?Q???Q?\(\?\(\?Q?Q?Q?Q?Gz?Gz?Q?p= ףp?p= ףp?Q???Q?(\(?(\(?Q?Q?Q?Q?zG?zG?Q?= ףp=?= ףp=?Q???Q?(\?(\?Q?RQ?RQ?Q?\(\?\(\?Q?Gz?Gz?Q???Q?Q?Q?Q?< ףp=?< ףp=?Q?(\?(\?Q?Gz?Gz?Q?dfffff?dfffff?Q?Q?Q?Q?٣p= ?٣p= ?Q?(\?(\?Q?IzG?IzG?Q???Q?r= ףp?r= ףp?Q?zG?zG?Q?RQ?RQ?Q?(\?(\?Q?233333?233333?Q?p= ף?p= ף?Q?$\(?$\(?Q?ףp= ?ףp= ?Q?l@)\(@?Lo-؂-@RQ@Q?&DDDD@> ףp=@PGz?Z[@Gz@Q?>qq@Gz@Q?M@> ףp=@Q?I@gfffff@Q?1` @(\@Q?t@Q@Q?},8@zG@Q?#O@ ףp= @Q?ɛ@333333@Q?oS'}'@\(\@Q? >>@Q@Q?TUUU@Gz@Q?bzkl@ףp= @Q?2-؂@@Q?阙@*\(@Q?T[@RQ@Q?Xq@|Gz@Q?@p= ף@Q?FI@@Q? ` @(\@Q?7!"""@Q@Q?878@GzG@Q?ަNO@p= ףp@Q?^efff@@Q?*|'}@(\@Q?͒>@Q@Q?v@Gz@Q?=l@= ףp=@Q?ւ-@ffffff@Q?i@(\@Q?d[@Q@Q?q@zG@Q?[1333@ ףp= @Q?HI@433333@Q?B_ `@](\@Q?Muwww@Q@Q?8@Gz@Q?iO@أp= @Q??!@@Q?'}@*\(@Q?>@SQ@Q?2H@|Gz@Q?l@p= ף@Q?~+؂-@@Q?$oBDDD@(\@Q?&Y[@Q@ Q?poq@HzG@(\?&Y[@q= ףp@gfffff?$oBDDD@@ ףp= ?~+؂-@Q@Gz?l@\(\@Gz?{Gz?{Gz? ףp= ?|Gz?|Gz?ffffff?{Gz?{Gz?(\?|Gz?|Gz?Q?{Gz?{Gz?{Gz?{Gz?{Gz?{Gz?ףp= ?ףp= ?{Gz?333333?333333?{Gz?(\?(\?|Gz?Q?Q?{Gz?HzG?HzG?{Gz?p= ף?p= ף?{Gz???{Gz?\(\?\(\?zGz?Q?Q?zGz?Gz?Gz?zGz?p= ףp?p= ףp?{Gz???{Gz?)\(?)\(?{Gz?Q?Q?zGz?zG?zG?zGz?= ףp=?= ףp=?zGz???{Gz?(\?(\?{Gz?RQ?RQ?{Gz?\(\?\(\?{Gz?Gz?Gz?{Gz???{Gz?Q?Q?{Gz?> ףp=?> ףp=?{Gz?(\?(\?{Gz?Gz?Gz?{Gz?ffffff?ffffff?{Gz?Q?Q?{Gz?أp= ?أp= ?{Gz?(\?(\?{Gz?HzG?HzG?{Gz???{Gz?q= ףp?q= ףp?{Gz?zG?zG?{Gz?QQ?QQ?{Gz?(\?(\?{Gz?233333?233333?{Gz?p= ף?p= ף?zGz?.\(?.\(?ףp= ?l@)\(@Q?Lo-؂-@RQ@?&DDDD@ffffff@Q?Z[@> ףp=@HGz?>qq@Gz@{Gz?M@Gz@{Gz?I@> ףp=@{Gz?1` @gfffff@{Gz?t@(\@{Gz?},8@Q@{Gz?#O@zG@{Gz?ɛ@ ףp= @{Gz?oS'}'@333333@{Gz? >>@\(\@{Gz?TUUU@Q@{Gz?bzkl@Gz@{Gz?2-؂@ףp= @{Gz?阙@@{Gz?T[@*\(@{Gz?Xq@RQ@{Gz?@|Gz@{Gz?FI@p= ף@{Gz? ` @@{Gz?7!"""@(\@{Gz?878@Q@{Gz?ަNO@HzG@{Gz?^efff@p= ףp@{Gz?*|'}@@{Gz?͒>@(\@{Gz?v@Q@{Gz?=l@Gz@{Gz?ւ-@> ףp=@{Gz?i@ffffff@{Gz?d[@(\@{Gz?q@Q@{Gz?[1333@zG@{Gz?HI@ ףp= @{Gz?B_ `@433333@{Gz?Muwww@](\@{Gz?8@Q@{Gz?iO@Gz@{Gz??!@أp= @{Gz?'}@@{Gz?>@*\(@{Gz?2H@SQ@{Gz?l@|Gz@{Gz?~+؂-@p= ף@{Gz?$oBDDD@@{Gz?&Y[@(\@{Gz?poq@Q@Q?&Y[@HzG@(\?$oBDDD@q= ףp@ffffff?~+؂-@Q@ ףp= ?l@\(\@Gz?2H@233333@Gz?ףp= ?ףp= ? ףp= ?أp= ?أp= ?gfffff?أp= ?أp= ?(\?أp= ?أp= ?Q?ףp= ?ףp= ?{Gz?ףp= ?ףp= ?ףp= ?ףp= ?ףp= ?ףp= ?333333?333333?ףp= ?(\?(\?أp= ?Q?Q?ףp= ?HzG?HzG?ףp= ?p= ף?p= ף?ףp= ???ףp= ?\(\?\(\?֣p= ?Q?Q?֣p= ?Gz?Gz?֣p= ?p= ףp?p= ףp?ףp= ???ףp= ?)\(?)\(?ףp= ?Q?Q?أp= ?zG?zG?أp= ?> ףp=?> ףp=?أp= ???ףp= ?(\?(\?ףp= ?RQ?RQ?ףp= ?\(\?\(\?ףp= ?Gz?Gz?ףp= ???ףp= ?Q?Q?ףp= ?> ףp=?> ףp=?ףp= ?(\?(\?ףp= ?Gz?Gz?ףp= ?ffffff?ffffff?ףp= ?Q?Q?ףp= ?أp= ?أp= ?ףp= ?(\?(\?ףp= ?HzG?HzG?ףp= ???ףp= ?p= ףp?p= ףp?ףp= ?zG?zG?ףp= ?PQ?PQ?أp= ?(\?(\?أp= ?833333?833333?أp= ?p= ף?p= ף?!\(?l@)\(@ףp= ?Lo-؂-@RQ@Q?&DDDD@{Gz@?Z[@ffffff@Q?>qq@> ףp=@@Gz?M@Gz@ףp= ?I@Gz@ףp= ?1` @> ףp=@ףp= ?t@gfffff@ףp= ?},8@(\@ףp= ?#O@Q@ףp= ?ɛ@zG@֣p= ?nS'}'@ ףp= @֣p= ? >>@233333@֣p= ?TUUU@\(\@֣p= ?azkl@Q@֣p= ?2-؂@Gz@֣p= ?阙@֣p= @֣p= ?T[@@֣p= ?Xq@(\(@֣p= ?@RQ@֣p= ?FI@zGz@֣p= ? ` @p= ף@֣p= ?7!"""@@֣p= ?878@(\@ףp= ?ަNO@Q@ףp= ?^efff@HzG@ףp= ?*|'}@p= ףp@ףp= ?͒>@@ףp= ?v@(\@ףp= ?=l@Q@ףp= ?ւ-@Gz@ףp= ?i@> ףp=@ףp= ?d[@ffffff@ףp= ?q@(\@ףp= ?[1333@Q@ףp= ?HI@zG@֣p= ?B_ `@ ףp= @֣p= ?Muwww@333333@֣p= ?8@\(\@֣p= ?iO@Q@֣p= ??!@Gz@֣p= ?'}@ףp= @ףp= ?>@@ףp= ?2H@*\(@ףp= ?l@SQ@ףp= ?~+؂-@|Gz@ףp= ?$oBDDD@p= ף@ףp= ?&Y[@@ףp= ?poq@(\@{Gz?&Y[@Q@Q?$oBDDD@GzG@(\?}+؂-@p= ףp@ffffff?l@[(\@ ףp= ?2H@333333@Gz?>@ ףp= @Gz?433333?433333? ףp= ?433333?433333?gfffff?433333?433333?(\?433333?433333?Q?333333?333333?{Gz?333333?333333?ףp= ?333333?333333?433333?433333?433333?433333?(\?(\?433333?Q?Q?333333?HzG?HzG?333333?p= ף?p= ף?333333???333333?\(\?\(\?433333?Q?Q?433333?Gz?Gz?433333?q= ףp?q= ףp?433333???433333?*\(?*\(?433333?Q?Q?433333?zG?zG?433333?> ףp=?> ףp=?433333???333333?(\?(\?333333?RQ?RQ?333333?\(\?\(\?333333?Gz?Gz?333333???433333?Q?Q?433333?@ ףp=?@ ףp=?433333?(\?(\?433333?Gz?Gz?433333?gfffff?gfffff?433333?Q?Q?333333?ףp= ?ףp= ?333333?(\?(\?333333?GzG?GzG?333333???333333?n= ףp?n= ףp?333333?zG?zG?333333?NQ?NQ?433333?(\?(\?433333?633333?633333?p= ף?l@*\(@0\(?Mo-؂-@RQ@ףp= ?&DDDD@|Gz@Q?Z[@(\@?>qq@ffffff@Q?M@> ףp=@Gz?I@Gz@333333?1` @Gz@333333?t@> ףp=@333333?},8@ffffff@333333?#O@(\@333333?ɛ@Q@233333?oS'}'@zG@233333? >>@ ףp= @233333?TUUU@233333@233333?azkl@\(\@333333?2-؂@Q@333333?阙@Gz@333333?T[@ףp= @333333?Xq@@233333?@(\(@233333?FI@RQ@233333? ` @zGz@233333?7!"""@p= ף@333333?878@@333333?ަNO@(\@433333?^efff@Q@433333?*|'}@HzG@433333?͒>@q= ףp@433333?v@@433333?=l@(\@333333?ւ-@Q@333333?h@Gz@333333?d[@> ףp=@333333?q@ffffff@333333?[1333@(\@333333?HI@Q@333333?B_ `@zG@333333?Muwww@ ףp= @333333?8@433333@333333?iO@[(\@333333??!@Q@333333?'}@Gz@333333?>@ףp= @433333?2H@@433333?l@)\(@433333?~+؂-@SQ@433333?$oBDDD@{Gz@433333?&Y[@p= ף@233333?poq@@֣p= ?&Y[@(\@{Gz?$oBDDD@Q@Q?}+؂-@GzG@(\?l@[(\@ffffff?1H@233333@ ףp= ?>@ ףp= @Gz?'}@zG@Gz?(\?(\? ףp= ?(\?(\?gfffff?(\?(\?(\?(\?(\?Q?(\?(\?{Gz?(\?(\?ףp= ?(\?(\?433333?(\?(\?(\?(\?(\?(\?Q?Q?(\?HzG?HzG?(\?p= ף?p= ף?(\???(\?\(\?\(\?(\?Q?Q?(\?Gz?Gz?(\?q= ףp?q= ףp?(\???(\?*\(?*\(?(\?Q?Q?(\?zG?zG?(\?> ףp=?> ףp=?(\???(\?(\?(\?(\?RQ?RQ?(\?\(\?\(\?(\?Gz?Gz?(\???(\?Q?Q?(\?? ףp=?? ףp=?(\?(\?(\?(\?Gz?Gz?(\?gfffff?gfffff?(\?Q?Q?(\?ףp= ?ףp= ?(\?(\?(\?(\?GzG?GzG?(\???(\?n= ףp?n= ףp?(\?zG?zG?(\?NQ?NQ?(\?(\?(\?833333?l@*\(@p= ף?Mo-؂-@RQ@0\(?&DDDD@|Gz@ףp= ?Z[@p= ף@Q?>qq@(\@?M@ffffff@Q?I@> ףp=@Gz?0` @Gz@(\?t@Gz@(\?},8@> ףp=@(\?#O@ffffff@(\?ɛ@(\@(\?oS'}'@Q@(\? >>@zG@(\?TUUU@ ףp= @(\?azkl@233333@(\?2-؂@\(\@(\?阙@Q@(\?T[@Gz@(\?Xq@֣p= @(\?@@(\?FI@(\(@(\? ` @RQ@(\?7!"""@zGz@(\?878@p= ף@(\?ަNO@@(\?^efff@(\@(\?+|'}@Q@(\?͒>@HzG@(\?w@q= ףp@(\?=l@@(\?ւ-@(\@(\?h@Q@(\?d[@Gz@(\?q@> ףp=@(\?\1333@ffffff@(\?HI@(\@(\?B_ `@Q@(\?Nuwww@zG@(\?8@ ףp= @(\?iO@333333@(\??!@[(\@(\?'}@Q@(\?>@Gz@(\?1H@ףp= @(\?l@@(\?~+؂-@)\(@(\?$oBDDD@SQ@(\?&Y[@{Gz@(\?ooq@p= ף@133333?&Y[@@գp= ?$oBDDD@(\@{Gz?~+؂-@Q@Q?l@GzG@(\?2H@233333@ffffff?>@ ףp= @ ףp= ?'}@zG@Gz??!@Q@Gz?Q?Q? ףp= ?Q?Q?ffffff?Q?Q?(\?Q?Q? Q?Q?Q?|Gz?Q?Q?֣p= ?Q?Q?433333?Q?Q?(\?Q?Q?Q?Q?Q?Q?HzG?HzG?Q?p= ף?p= ף?Q???Q?\(\?\(\?Q?Q?Q?Q?Gz?Gz?Q?q= ףp?q= ףp?Q???Q?)\(?)\(?Q?Q?Q?Q?zG?zG?Q?> ףp=?> ףp=?Q???Q?(\?(\?Q?RQ?RQ?Q?\(\?\(\?Q?Gz?Gz?Q???Q?Q?Q?Q?> ףp=?> ףp=?Q?(\?(\?Q?Gz?Gz?Q?ffffff?ffffff?Q?Q?Q?Q?ڣp= ?ڣp= ?Q?(\?(\?Q?JzG?JzG?Q???Q?t= ףp?t= ףp?Q?zG?zG?Q?TQ?TQ?(\?l@*\(@833333?Mo-؂-@RQ@p= ף?&DDDD@|Gz@0\(?Z[@p= ף@ףp= ??qq@Q@Q?M@(\@?I@gfffff@Q?1` @> ףp=@Gz?t@Gz@Q?|,8@Gz@Q?"O@= ףp=@Q?ț@ffffff@Q?nS'}'@(\@Q? >>@Q@Q?TUUU@zG@Q?`zkl@ ףp= @Q?2-؂@233333@Q?阙@[(\@Q?S[@Q@Q?Xq@Gz@Q?@֣p= @Q?FI@@Q? ` @(\(@Q?7!"""@RQ@Q?878@zGz@Q?ަNO@p= ף@Q?^efff@@Q?*|'}@(\@Q?͒>@Q@Q?v@FzG@Q?=l@p= ףp@Q?ւ-@@Q?h@(\@Q?d[@Q@Q?q@Gz@Q?Z1333@> ףp=@Q?HI@ffffff@Q?B_ `@(\@Q?Muwww@Q@Q?8@zG@Q?iO@ ףp= @Q?@!@233333@Q?'}@\(\@Q?>@Q@Q?2H@Gz@Q?l@֣p= @Q?~+؂-@@Q?$oBDDD@(\(@Q?&Y[@RQ@Q?ooq@zGz@(\?&Y[@p= ף@533333?$oBDDD@@ףp= ?+؂-@(\@}Gz?l@ Q@ Q?2H@433333@(\?>@ ףp= @hfffff?'}@zG@ ףp= ?@!@Q@Gz?iO@(\@Gz?HzG?HzG? ףp= ?HzG?HzG?ffffff?HzG?HzG?(\?HzG?HzG? Q?HzG?HzG?|Gz?HzG?HzG?ףp= ?HzG?HzG?333333?HzG?HzG?(\?HzG?HzG?Q?HzG?HzG?GzG?GzG?GzG?GzG?p= ף?p= ף?HzG???HzG?\(\?\(\?HzG?Q?Q?HzG?Gz?Gz?HzG?p= ףp?p= ףp?GzG???GzG?(\(?(\(?GzG?Q?Q?HzG?zG?zG?HzG?= ףp=?= ףp=?HzG???HzG?(\?(\?HzG?RQ?RQ?HzG?\(\?\(\?HzG?Gz?Gz?HzG???GzG?Q?Q?GzG?= ףp=?= ףp=?GzG?(\?(\?GzG?Gz?Gz?GzG?efffff?efffff?GzG?Q?Q?HzG?٣p= ?٣p= ?HzG?(\?(\?HzG?IzG?IzG?HzG???HzG?r= ףp?r= ףp?HzG?zG?zG?PQ?l@)\(@(\?Mo-؂-@RQ@833333?&DDDD@|Gz@p= ף?Z[@p= ף@0\(??qq@@ףp= ?M@Q@Q?I@(\@?0` @ffffff@Q?t@> ףp=@Gz?|,8@Gz@HzG?#O@Gz@HzG?ɛ@> ףp=@HzG?oS'}'@ffffff@HzG? >>@(\@HzG?TUUU@Q@HzG?bzkl@zG@HzG?2-؂@ ףp= @HzG?阙@333333@HzG?T[@\(\@HzG?Xq@Q@HzG?@Gz@HzG?FI@ףp= @HzG? ` @@HzG?7!"""@)\(@HzG?878@RQ@HzG?ަNO@{Gz@HzG?^efff@p= ף@HzG?*|'}@@HzG?͒>@(\@HzG?v@Q@HzG?=l@GzG@HzG?ւ-@p= ףp@HzG?h@@HzG?d[@(\@HzG?q@Q@HzG?[1333@Gz@HzG?HI@> ףp=@HzG?B_ `@gfffff@HzG?Muwww@(\@HzG?8@Q@HzG?iO@zG@HzG?@!@ ףp= @HzG?'}@333333@HzG?>@\(\@HzG?2H@Q@HzG?l@Gz@HzG?~+؂-@ףp= @HzG?$oBDDD@@HzG?&Y[@)\(@HzG?poq@RQ@Q?&Y[@{Gz@(\?$oBDDD@p= ף@433333?~+؂-@@أp= ?l@(\@~Gz?2H@Q@ Q?>@ ףp= @(\?'}@zG@hfffff??!@Q@ ףp= ?iO@(\@Gz?8@ffffff@Gz?p= ף?p= ף? ףp= ?p= ף?p= ף?ffffff?p= ף?p= ף?(\?p= ף?p= ף? Q?p= ף?p= ף?|Gz?p= ף?p= ף?ףp= ?p= ף?p= ף?333333?p= ף?p= ף?(\?p= ף?p= ף?Q?p= ף?p= ף?GzG?p= ף?p= ף?p= ף?p= ף?p= ף?p= ף???p= ף?\(\?\(\?p= ף?Q?Q?p= ף?Gz?Gz?p= ף?p= ףp?p= ףp?p= ף???p= ף?(\(?(\(?p= ף?Q?Q?p= ף?zG?zG?p= ף?= ףp=?= ףp=?p= ף???p= ף?(\?(\?p= ף?RQ?RQ?p= ף?\(\?\(\?p= ף?Gz?Gz?p= ף???p= ף?Q?Q?p= ף?= ףp=?= ףp=?p= ף?(\?(\?p= ף?Gz?Gz?p= ף?efffff?efffff?p= ף?Q?Q?p= ף?٣p= ?٣p= ?p= ף?(\?(\?p= ף?IzG?IzG?p= ף???p= ף?r= ףp?r= ףp?zG?l@)\(@PQ?Lo-؂-@RQ@(\?&DDDD@|Gz@833333?Z[@p= ף@p= ף??qq@@ \(?M@zG@ףp= ?I@Q@Q?1` @(\@?t@ffffff@Q?},8@> ףp=@Gz?#O@Gz@p= ף?ɛ@Gz@p= ף?oS'}'@> ףp=@p= ף? >>@ffffff@p= ף?TUUU@(\@p= ף?azkl@Q@p= ף?2-؂@zG@p= ף?阙@ ףp= @p= ף?T[@333333@p= ף?Xq@\(\@p= ף?@Q@p= ף?FI@Gz@p= ף? ` @ףp= @p= ף?7!"""@@p= ף?878@)\(@p= ף?ަNO@RQ@p= ף?^efff@{Gz@p= ף?*|'}@p= ף@p= ף?͒>@@p= ף?v@(\@p= ף?=l@Q@p= ף?ւ-@GzG@p= ף?h@p= ףp@p= ף?d[@@p= ף?q@(\@p= ף?[1333@Q@p= ף?HI@Gz@p= ף?B_ `@> ףp=@p= ף?Muwww@gfffff@p= ף?8@(\@p= ף?iO@Q@p= ף??!@zG@p= ף?'}@ ףp= @p= ף?>@333333@p= ף?2H@\(\@p= ף?l@Q@p= ף?~+؂-@Gz@p= ף?$oBDDD@ףp= @p= ף?&Y[@@p= ף?poq@)\(@IzG?&Y[@SQ@Q?$oBDDD@zGz@(\?~+؂-@p= ף@333333?l@@٣p= ?2H@(\@|Gz?>@ ףp= @ Q?'}@zG@(\?@!@Q@hfffff?iO@(\@ ףp= ?8@ffffff@Gz?Muwww@= ףp=@Gz??? ףp= ???ffffff???(\???Q???{Gz???ףp= ???433333???(\???Q???HzG???p= ף???????\(\?\(\??Q?Q??Gz?Gz??p= ףp?p= ףp?????)\(?)\(??Q?Q??zG?zG??> ףp=?> ףp=?????(\?(\??RQ?RQ??\(\?\(\??Gz?Gz?????Q?Q??@ ףp=?@ ףp=??(\?(\??Gz?Gz??hfffff?hfffff?? Q? Q??أp= ?أp= ??(\?(\??HzG?HzG????p= ףp?l@)\(@zG?Lo-؂-@RQ@PQ?&DDDD@{Gz@(\?Z[@p= ף@833333??qq@@p= ף?M@(\@\(?I@zG@֣p= ?1` @Q@Q?t@(\@?|,8@ffffff@Q?#O@> ףp=@Gz?ț@Gz@?oS'}'@Gz@? >>@> ףp=@?TUUU@ffffff@?bzkl@(\@?2-؂@Q@?阙@zG@?T[@ ףp= @?Xq@333333@?@\(\@?FI@Q@? ` @Gz@?7!"""@ףp= @?878@@?ަNO@)\(@?^efff@RQ@?*|'}@{Gz@?͒>@p= ף@?v@@?=l@(\@?ւ-@Q@?h@HzG@?d[@p= ףp@?q@@?[1333@(\@?HI@Q@?B_ `@Gz@?Muwww@= ףp=@?8@gfffff@?iO@(\@??!@Q@?'}@zG@?>@ ףp= @?2H@333333@?l@\(\@?~+؂-@Q@?$oBDDD@Gz@?&Y[@ףp= @?poq@@p= ף?&Y[@)\(@HzG?$oBDDD@RQ@Q?}+؂-@{Gz@(\?l@p= ף@433333?2H@@أp= ?>@(\@xGz?'}@zG@ Q??!@Q@(\?iO@(\@hfffff?8@ffffff@ףp= ?Nuwww@> ףp=@Gz?B_ `@Gz@Gz?\(\?\(\? ףp= ?\(\?\(\?ffffff?\(\?\(\?(\?\(\?\(\?Q?\(\?\(\?{Gz?\(\?\(\?ףp= ?\(\?\(\?433333?](\?](\?(\?](\?](\?Q?\(\?\(\?HzG?\(\?\(\?p= ף?\(\?\(\??\(\?\(\?\(\?\(\?\(\?\(\?Q?Q?\(\?Gz?Gz?\(\?p= ףp?p= ףp?\(\???\(\?)\(?)\(?\(\?Q?Q?\(\?zG?zG?\(\?> ףp=?> ףp=?\(\???\(\?(\?(\?\(\?RQ?RQ?\(\?\(\?\(\?\(\?Gz?Gz?\(\???](\?Q?Q?](\?@ ףp=?@ ףp=?](\?(\?(\?](\?Gz?Gz?](\?hfffff?hfffff?](\? Q? Q?\(\?أp= ?أp= ?\(\?(\?(\?\(\?HzG?HzG??l@)\(@p= ףp?Lo-؂-@RQ@zG?&DDDD@{Gz@PQ?Z[@p= ף@(\??qq@@833333?M@(\@p= ף?I@ ףp= @"\(?0` @zG@ףp= ?t@Q@Q?},8@(\@?"O@ffffff@Q?ɛ@> ףp=@Gz?oS'}'@Gz@\(\? >>@Gz@\(\?TUUU@> ףp=@\(\?azkl@ffffff@\(\?2-؂@(\@\(\?阙@Q@\(\?T[@zG@\(\?Xq@ ףp= @\(\?@333333@\(\?FI@\(\@\(\? ` @Q@\(\?7!"""@Gz@\(\?878@ףp= @\(\?ަNO@@\(\?^efff@)\(@\(\?*|'}@RQ@\(\?͒>@{Gz@\(\?v@p= ף@\(\?=l@@\(\?ւ-@(\@\(\?h@Q@\(\?d[@HzG@\(\?q@p= ףp@\(\?Z1333@@\(\?HI@(\@\(\?B_ `@Q@\(\?Muwww@Gz@\(\?8@= ףp=@\(\?iO@gfffff@\(\??!@(\@\(\?'}@Q@\(\?>@zG@\(\?2H@ ףp= @\(\?l@333333@\(\?~+؂-@\(\@\(\?$oBDDD@Q@\(\?&Y[@Gz@\(\?poq@ףp= @?&Y[@@p= ף?#oBDDD@*\(@GzG?~+؂-@QQ@Q?l@|Gz@(\?1H@p= ף@533333?>@@أp= ?'}@zG@xGz??!@Q@ Q?iO@(\@(\?8@efffff@hfffff?Nuwww@? ףp=@ףp= ?B_ `@Gz@Gz?HI@Q@Gz?Q?Q? ףp= ?Q?Q?gfffff?Q?Q?(\?Q?Q?Q?Q?Q?zGz?Q?Q?֣p= ?Q?Q?433333?Q?Q?(\?Q?Q?Q?Q?Q?IzG?Q?Q?p= ף?Q?Q??Q?Q?\(\?Q?Q?Q?Q?Q?Q?Gz?Gz?Q?q= ףp?q= ףp?Q???Q?)\(?)\(?Q?Q?Q?Q?zG?zG?Q?= ףp=?= ףp=?Q???Q?(\?(\?Q?SQ?SQ?Q?^(\?^(\?Q?Gz?Gz?Q???Q?Q?Q?Q?> ףp=?> ףp=?Q?(\?(\?Q?Gz?Gz?Q?ffffff?ffffff?Q?Q?Q?Q?ףp= ?ףp= ?Q?(\?(\?HzG?l@)\(@?Lo-؂-@RQ@p= ףp?&DDDD@{Gz@zG?Z[@p= ף@PQ?>qq@@(\?M@(\@133333?I@Q@p= ף?0` @ ףp= @,\(?t@zG@ ףp= ?|,8@Q@Q?"O@(\@?ț@ffffff@Q?nS'}'@= ףp=@_Gz? >>@Gz@Q?TUUU@Gz@Q?bzkl@> ףp=@Q?2-؂@gfffff@Q?阙@(\@Q?T[@Q@Q?Xq@zG@Q?@ ףp= @Q?FI@433333@Q? ` @\(\@Q?7!"""@Q@Q?878@Gz@Q?ަNO@أp= @Q?^efff@@Q?+|'}@*\(@Q?͒>@RQ@Q?x@|Gz@Q?=l@p= ף@Q?ւ-@@Q?j@(\@Q?d[@ Q@Q?q@HzG@Q?\1333@r= ףp@Q?HI@@Q?B_ `@(\@Q?Nuwww@Q@Q?8@Gz@Q?iO@> ףp=@Q?@!@ffffff@Q?'}@(\@Q?>@Q@Q?2H@zG@Q?l@ ףp= @Q?~+؂-@433333@Q?$oBDDD@\(\@Q?&Y[@Q@Q?poq@Gz@](\?&Y[@أp= @?$oBDDD@@p= ף?}+؂-@*\(@IzG?l@SQ@Q?2H@|Gz@(\?>@p= ף@533333?'}@@ڣp= ?@!@Q@zGz?iO@(\@Q?8@gfffff@(\?Nuwww@? ףp=@ffffff?B_ `@Gz@ ףp= ?HI@Q@Gz?\1333@(\@Gz?Gz?Gz? ףp= ?Gz?Gz?gfffff?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?zGz?Gz?Gz?֣p= ?Gz?Gz?433333?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?HzG?Gz?Gz?p= ף?Gz?Gz??Gz?Gz?\(\?Gz?Gz?Q?Gz?Gz?Gz?Gz?Gz?Gz?q= ףp?q= ףp?Gz???Gz?)\(?)\(?Gz?Q?Q?Gz?zG?zG?Gz?= ףp=?= ףp=?Gz???Gz?(\?(\?Gz?SQ?SQ?Gz?^(\?^(\?Gz?Gz?Gz?Gz???Gz?Q?Q?Gz?> ףp=?> ףp=?Gz?(\?(\?Gz?Gz?Gz?Gz?ffffff?ffffff?Gz?Q?Q?Gz?أp= ?أp= ?(\?l@)\(@HzG?Lo-؂-@RQ@?&DDDD@{Gz@p= ףp?Z[@p= ף@zG?>qq@@PQ?M@(\@(\?I@Q@633333?0` @333333@p= ף?t@ ףp= @,\(?|,8@zG@ ףp= ?"O@Q@Q?ɛ@(\@?oS'}'@ffffff@Q? >>@= ףp=@_Gz?TUUU@Gz@Gz?bzkl@Gz@Gz?2-؂@> ףp=@Gz?阙@gfffff@Gz?T[@(\@Gz?Xq@Q@Gz?@zG@Gz?FI@ ףp= @Gz? ` @433333@Gz?7!"""@\(\@Gz?878@Q@Gz?ަNO@Gz@Gz?^efff@أp= @Gz?*|'}@@Gz?͒>@*\(@Gz?w@RQ@Gz?=l@|Gz@Gz?ւ-@p= ף@Gz?j@@Gz?d[@(\@Gz?q@ Q@Gz?\1333@HzG@Gz?HI@r= ףp@Gz?B_ `@@Gz?Nuwww@(\@Gz?8@Q@Gz?iO@Gz@Gz?@!@> ףp=@Gz?'}@ffffff@Gz?>@(\@Gz?2H@Q@Gz?l@zG@Gz?~+؂-@ ףp= @Gz?$oBDDD@433333@Gz?&Y[@\(\@Gz?poq@Q@Q?&Y[@Gz@\(\?$oBDDD@ףp= @?}+؂-@@p= ף?l@)\(@IzG?1H@RQ@Q?>@{Gz@(\?'}@p= ף@633333?@!@Q@ڣp= ?iO@(\@xGz?8@gfffff@Q?Muwww@> ףp=@(\?B_ `@Gz@efffff?HI@Q@ ףp= ?[1333@(\@Gz?q@@Gz?p= ףp?p= ףp? ףp= ?q= ףp?q= ףp?gfffff?q= ףp?q= ףp?(\?p= ףp?p= ףp?Q?p= ףp?p= ףp?zGz?p= ףp?p= ףp?֣p= ?p= ףp?p= ףp?433333?q= ףp?q= ףp?(\?q= ףp?q= ףp?Q?p= ףp?p= ףp?HzG?q= ףp?q= ףp?p= ף?q= ףp?q= ףp??p= ףp?p= ףp?\(\?p= ףp?p= ףp?Q?r= ףp?r= ףp?Gz?r= ףp?r= ףp?q= ףp?q= ףp?q= ףp?p= ףp???p= ףp?)\(?)\(?p= ףp?Q?Q?p= ףp?zG?zG?p= ףp?= ףp=?= ףp=?p= ףp???r= ףp?(\?(\?r= ףp?SQ?SQ?r= ףp?^(\?^(\?r= ףp?Gz?Gz?r= ףp???p= ףp?Q?Q?p= ףp?> ףp=?> ףp=?p= ףp?(\?(\?p= ףp?Gz?Gz?p= ףp?ffffff?ffffff?r= ףp? Q? Q?أp= ?l@)\(@(\?Lo-؂-@RQ@HzG?&DDDD@{Gz@?Z[@p= ף@p= ףp?>qq@@zG?M@(\@PQ?I@Q@(\?1` @HzG@633333?t@333333@p= ף?|,8@ ףp= @,\(?"O@zG@ ףp= ?ț@Q@Q?oS'}'@(\@? >>@ffffff@Q?TUUU@= ףp=@_Gz?azkl@Gz@r= ףp?2-؂@Gz@r= ףp?阙@> ףp=@r= ףp?T[@gfffff@r= ףp?Xq@(\@r= ףp?@Q@r= ףp?FI@zG@r= ףp? ` @ ףp= @r= ףp?7!"""@433333@r= ףp?878@\(\@r= ףp?ަNO@Q@r= ףp?^efff@Gz@r= ףp?*|'}@أp= @r= ףp?͒>@@r= ףp?w@*\(@r= ףp?=l@RQ@r= ףp?ւ-@|Gz@r= ףp?j@p= ף@r= ףp?d[@@r= ףp?q@(\@r= ףp?\1333@ Q@r= ףp?HI@HzG@r= ףp?B_ `@r= ףp@r= ףp?Nuwww@@r= ףp?8@(\@r= ףp?iO@Q@q= ףp?@!@Gz@q= ףp?'}@> ףp=@q= ףp?>@ffffff@q= ףp?2H@(\@q= ףp?l@Q@q= ףp?~+؂-@zG@q= ףp?$oBDDD@ ףp= @q= ףp?&Y[@433333@q= ףp?poq@\(\@Gz?&Y[@Q@Q?$oBDDD@Gz@](\?~+؂-@أp= @?l@@p= ף?1H@*\(@IzG?>@SQ@Q?'}@|Gz@(\??!@p= ף@633333?iO@(\@ڣp= ?8@gfffff@zGz?Nuwww@? ףp=@Q?B_ `@Gz@(\?HI@Q@ffffff?\1333@(\@ ףp= ?q@@Gz?d[@p= ףp@Gz??? ףp= ???gfffff???(\???Q???{Gz???ףp= ???433333???(\???Q???HzG???p= ף??????\(\???Q???Gz???q= ףp???????*\(?*\(??Q?Q??zG?zG??> ףp=?> ףp=?????(\?(\??RQ?RQ??\(\?\(\??Gz?Gz?????Q?Q??= ףp=?= ףp=??(\?(\??Gz?Gz??hfffff?hfffff? Q?l@*\(@أp= ?Lo-؂-@RQ@(\?&DDDD@{Gz@HzG?Z[@p= ף@?>qq@@p= ףp?M@(\@zG?I@Q@PQ?1` @HzG@(\?t@\(\@433333?|,8@333333@p= ף?#O@ ףp= @(\(?ɛ@zG@ףp= ?oS'}'@Q@Q? >>@(\@?TUUU@ffffff@Q?bzkl@> ףp=@Gz?2-؂@Gz@?阙@Gz@?S[@= ףp=@?Xq@ffffff@?@(\@?FI@Q@? ` @zG@?7!"""@ ףp= @?878@433333@?ަNO@\(\@?^efff@Q@?*|'}@Gz@?͒>@أp= @?v@@?=l@*\(@?ւ-@SQ@?i@|Gz@?d[@p= ף@?q@@?[1333@(\@?HI@Q@?B_ `@HzG@?Muwww@p= ףp@?8@@?iO@(\@??!@Q@?'}@Gz@?>@> ףp=@?1H@ffffff@?l@(\@?~+؂-@Q@?$oBDDD@zG@?&Y[@ ףp= @?poq@333333@q= ףp?&Y[@\(\@Gz?$oBDDD@Q@Q?~+؂-@Gz@`(\?l@أp= @?2H@@p= ף?>@)\(@JzG?'}@SQ@Q??!@{Gz@(\?iO@(\@233333?8@efffff@ڣp= ?Muwww@> ףp=@zGz?B_ `@Gz@Q?HI@Q@(\?[1333@(\@ffffff?q@@ ףp= ?d[@q= ףp@Gz?i@IzG@Gz?)\(?)\(? ףp= ?*\(?*\(?gfffff?*\(?*\(?(\?*\(?*\(?Q?(\(?(\(?{Gz?)\(?)\(?ףp= ?)\(?)\(?433333?*\(?*\(?(\?*\(?*\(?Q?(\(?(\(?HzG?)\(?)\(?p= ף?)\(?)\(??)\(?)\(?\(\?)\(?)\(?Q?*\(?*\(?Gz?*\(?*\(?q= ףp?*\(?*\(??*\(?*\(?*\(?*\(?*\(?*\(?Q?Q?*\(?zG?zG?*\(?> ףp=?> ףp=?*\(???)\(?(\?(\?)\(?RQ?RQ?)\(?\(\?\(\?)\(?Gz?Gz?)\(???)\(?Q?Q?)\(?= ףp=?= ףp=?)\(?(\?(\?*\(?Gz?Gz?hfffff?l@*\(@ Q?Mo-؂-@RQ@أp= ?&DDDD@{Gz@(\?Z[@p= ף@HzG?>qq@@?M@(\@p= ףp?I@Q@zG?1` @HzG@PQ?t@q= ףp@(\?|,8@\(\@433333?#O@333333@p= ף?ɛ@ ףp= @(\(?oS'}'@zG@ףp= ? >>@Q@Q?TUUU@(\@?azkl@ffffff@Q?2-؂@> ףp=@Gz?阙@Gz@)\(?T[@Gz@)\(?Xq@= ףp=@)\(?@ffffff@)\(?FI@(\@)\(? ` @Q@)\(?7!"""@zG@*\(?878@ ףp= @*\(?ަNO@433333@*\(?^efff@\(\@*\(?*|'}@Q@*\(?͒>@Gz@*\(?v@أp= @*\(?=l@@*\(?ւ-@*\(@*\(?i@SQ@*\(?d[@|Gz@*\(?q@p= ף@*\(?[1333@@*\(?HI@(\@)\(?B_ `@Q@)\(?Muwww@HzG@)\(?8@p= ףp@)\(?iO@@)\(??!@(\@)\(?'}@Q@)\(?>@Gz@)\(?2H@> ףp=@)\(?l@ffffff@)\(?~+؂-@(\@)\(?$oBDDD@Q@)\(?&Y[@zG@*\(?poq@ ףp= @?&Y[@333333@r= ףp?$oBDDD@](\@Gz?~+؂-@Q@Q?l@Gz@^(\?1H@ףp= @?>@@p= ף?'}@*\(@KzG??!@SQ@Q?iO@|Gz@(\?8@gfffff@233333?Luwww@< ףp=@ڣp= ?B_ `@Gz@yGz?HI@Q@Q?\1333@(\@(\?q@@ffffff?d[@q= ףp@ ףp= ?i@HzG@Gz?ւ-@ Q@Gz?Q?Q? ףp= ?Q?Q?gfffff?Q?Q?(\?Q?Q?Q?Q?Q?{Gz?Q?Q?ףp= ?Q?Q?433333?Q?Q?(\?Q?Q?Q?Q?Q?HzG?Q?Q?p= ף?Q?Q??Q?Q?\(\?Q?Q?Q?Q?Q?Gz?Q?Q?q= ףp?Q?Q??Q?Q?*\(?Q?Q?Q?Q?Q?Q?zG?zG?Q?> ףp=?> ףp=?Q???Q?(\?(\?Q?RQ?RQ?Q?\(\?\(\?Q?Gz?Gz?Q???Q?Q?Q?Q?= ףp=?= ףp=?Q?(\?(\?Gz?l@*\(@hfffff?Mo-؂-@RQ@ Q?&DDDD@|Gz@أp= ?Z[@p= ף@(\?>qq@@HzG?M@(\@?I@Q@p= ףp?1` @HzG@zG?t@q= ףp@LQ?},8@Q@(\?#O@\(\@433333?ɛ@333333@p= ף?oS'}'@ ףp= @(\(? >>@zG@ףp= ?TUUU@Q@Q?azkl@(\@?2-؂@ffffff@Q?阙@> ףp=@Gz?T[@Gz@Q?Xq@Gz@Q?@= ףp=@Q?FI@ffffff@Q? ` @(\@Q?7!"""@Q@Q?878@zG@Q?ަNO@ ףp= @Q?^efff@333333@Q?*|'}@\(\@Q?͒>@Q@Q?v@Gz@Q?=l@ףp= @Q?ւ-@@Q?i@)\(@Q?d[@RQ@Q?q@{Gz@Q?[1333@p= ף@Q?HI@@Q?B_ `@(\@Q?Muwww@Q@Q?8@HzG@Q?iO@p= ףp@Q??!@@Q?'}@(\@Q?>@Q@Q?2H@Gz@Q?l@> ףp=@Q?~+؂-@ffffff@Q?$oBDDD@(\@Q?&Y[@Q@Q?poq@zG@,\(?&Y[@ ףp= @?$oBDDD@433333@t= ףp?~+؂-@](\@Gz?l@Q@Q?2H@Gz@](\?>@ףp= @?'}@@p= ף?@!@*\(@KzG?iO@RQ@Q?8@efffff@(\?Muwww@> ףp=@233333?B_ `@Gz@٣p= ?HI@Q@yGz?[1333@(\@Q?q@@(\?d[@q= ףp@ffffff?i@IzG@ ףp= ?ւ-@Q@Gz?=l@(\@Gz?zG?zG? ףp= ?zG?zG?ffffff?zG?zG?(\?zG?zG? Q?zG?zG?zGz?zG?zG?أp= ?zG?zG?433333?zG?zG?(\?zG?zG?Q?zG?zG?HzG?zG?zG?p= ף?zG?zG??zG?zG?\(\?zG?zG?Q?zG?zG?Gz?zG?zG?p= ףp?zG?zG??zG?zG?*\(?zG?zG?Q?zG?zG?zG?zG?zG?zG?= ףp=?= ףp=?zG???zG?(\?(\?zG?RQ?RQ?zG?[(\?[(\?zG?Gz?Gz?zG???zG?Q?Q?zG?> ףp=?> ףp=?(\?l@*\(@Gz?Mo-؂-@RQ@hfffff?&DDDD@|Gz@ Q?Z[@p= ף@أp= ?>qq@@(\?M@(\@HzG?I@Q@?0` @HzG@p= ףp?t@p= ףp@zG?},8@@PQ?#O@Q@(\?ɛ@\(\@233333?oS'}'@433333@p= ף? >>@ ףp= @$\(?TUUU@zG@ףp= ?bzkl@Q@Q?2-؂@(\@?阙@gfffff@Q?T[@> ףp=@Gz?Xq@Gz@zG?@Gz@zG?EI@= ףp=@zG? ` @ffffff@zG?7!"""@(\@zG?778@Q@zG?ݦNO@zG@zG?^efff@ ףp= @zG?)|'}@233333@zG?͒>@[(\@zG?u@Q@zG?=l@Gz@zG?ւ-@֣p= @zG?g@@zG?d[@(\(@zG?q@QQ@zG?Z1333@zGz@zG?HI@p= ף@zG?B_ `@@zG?Luwww@(\@zG?8@Q@zG?iO@FzG@zG?>!@o= ףp@zG?'}@@zG?>@(\@zG?0H@Q@zG?l@Gz@zG?|+؂-@= ףp=@zG?"oBDDD@ffffff@zG?&Y[@(\@zG?ooq@Q@Q?&Y[@zG@,\(?$oBDDD@ ףp= @?~+؂-@533333@t= ףp?l@^(\@Gz?2H@Q@Q?>@Gz@`(\?'}@أp= @??!@@p= ף?iO@*\(@LzG?8@SQ@Q?Muwww@= ףp=@(\?B_ `@Gz@533333?HI@Q@أp= ?[1333@(\@zGz?q@@ Q?d[@q= ףp@(\?i@HzG@hfffff?ւ-@ Q@ ףp= ?=l@(\@Gz?w@@Gz?> ףp=?> ףp=? ףp= ?> ףp=?> ףp=?ffffff?> ףp=?> ףp=?(\?> ףp=?> ףp=? Q?> ףp=?> ףp=?zGz?= ףp=?= ףp=?أp= ?> ףp=?> ףp=?433333?> ףp=?> ףp=?(\?> ףp=?> ףp=?Q?= ףp=?= ףp=?HzG?= ףp=?= ףp=?p= ף?= ףp=?= ףp=??> ףp=?> ףp=?\(\?> ףp=?> ףp=?Q?> ףp=?> ףp=?Gz?> ףp=?> ףp=?p= ףp?> ףp=?> ףp=??> ףp=?> ףp=?*\(?> ףp=?> ףp=?Q?> ףp=?> ףp=?zG?= ףp=?= ףp=?= ףp=?= ףp=?= ףp=?= ףp=???= ףp=?(\?(\?= ףp=?RQ?RQ?> ףp=?[(\?[(\?> ףp=?Gz?Gz?> ףp=???> ףp=?Q?Q?@ ףp=?l@*\(@(\?Mo-؂-@RQ@Gz?&DDDD@|Gz@hfffff?Z[@p= ף@ Q??qq@@أp= ?M@(\@(\?I@Q@HzG?0` @HzG@?t@p= ףp@p= ףp?},8@@zG?#O@Gz@PQ?ɛ@Q@(\?oS'}'@\(\@233333? >>@433333@p= ף?TUUU@ ףp= @$\(?bzkl@zG@ףp= ?2-؂@Q@Q?阙@(\@?T[@gfffff@Q?Xq@> ףp=@Gz?@Gz@= ףp=?EI@Gz@= ףp=? ` @= ףp=@= ףp=?7!"""@ffffff@= ףp=?878@(\@= ףp=?ަNO@Q@= ףp=?^efff@zG@= ףp=?*|'}@ ףp= @= ףp=?͒>@233333@= ףp=?v@[(\@= ףp=?=l@Q@> ףp=?ւ-@Gz@> ףp=?h@֣p= @> ףp=?d[@@> ףp=?q@(\(@> ףp=?Z1333@QQ@> ףp=?HI@zGz@> ףp=?B_ `@p= ף@> ףp=?Luwww@@> ףp=?8@(\@= ףp=?iO@Q@= ףp=?>!@FzG@= ףp=?'}@o= ףp@= ףp=?>@@= ףp=?0H@(\@= ףp=?l@Q@> ףp=?}+؂-@Gz@> ףp=?#oBDDD@= ףp=@> ףp=?&Y[@ffffff@> ףp=?ooq@(\@zG?&Y[@Q@Q?$oBDDD@zG@*\(?~+؂-@ ףp= @?l@433333@s= ףp?2H@](\@Gz?>@Q@Q?'}@Gz@_(\?@!@أp= @?iO@@p= ף?8@)\(@IzG?Muwww@= ףp=@Q?B_ `@Gz@(\?HI@Q@433333?[1333@(\@أp= ?q@@zGz?d[@q= ףp@ Q?i@IzG@(\?ւ-@Q@hfffff?=l@(\@ ףp= ?v@@Gz?͒>@p= ף@Gz??? ףp= ???ffffff???(\??? Q???zGz???أp= ???433333???(\???Q???HzG???p= ף??????\(\???Q???Gz???p= ףp??????*\(???Q???zG???= ףp=???????(\?(\??RQ?RQ??[(\?[(\??Gz?Gz????Q?l@*\(@@ ףp=?Mo-؂-@RQ@(\?&DDDD@|Gz@Gz?Z[@p= ף@hfffff??qq@@ Q?M@(\@أp= ?I@Q@(\?0` @HzG@HzG?t@p= ףp@?},8@@p= ףp?#O@(\@zG?ɛ@Gz@PQ?pS'}'@Q@(\? >>@\(\@233333?TUUU@433333@p= ף?bzkl@ ףp= @$\(?2-؂@zG@ףp= ?阙@Q@Q?T[@(\@?Xq@gfffff@Q?@> ףp=@Gz?FI@Gz@? ` @Gz@?7!"""@= ףp=@?878@ffffff@?ަNO@(\@?^efff@Q@?*|'}@zG@?͒>@ ףp= @?v@233333@?=l@[(\@?ւ-@Q@?h@Gz@?d[@֣p= @?q@@?Z1333@(\(@?HI@QQ@?B_ `@zGz@?Luwww@p= ף@?8@@?iO@(\@?>!@Q@?'}@FzG@?>@o= ףp@?0H@@?l@(\@?|+؂-@Q@?#oBDDD@Gz@?&Y[@= ףp=@?ooq@ffffff@= ףp=?&Y[@(\@zG?$oBDDD@Q@Q?~+؂-@zG@,\(?l@ ףp= @?2H@533333@t= ףp?>@^(\@Gz?'}@Q@Q?@!@Gz@`(\?iO@أp= @?8@@p= ף?Muwww@*\(@HzG?B_ `@Gz@Q?HI@Q@(\?[1333@(\@433333?q@@أp= ?d[@o= ףp@zGz?i@HzG@ Q?ւ-@ Q@(\?=l@(\@hfffff?w@@ ףp= ?͒>@p= ף@Gz?+|'}@|Gz@Gz?(\?(\? ףp= ?(\?(\?gfffff?(\?(\?(\?(\?(\?Q?(\?(\?{Gz?(\?(\?ףp= ?(\?(\?433333?(\?(\?(\?(\?(\?Q?(\?(\?HzG?(\?(\?p= ף?(\?(\??(\?(\?\(\?(\?(\?Q?(\?(\?Gz?(\?(\?p= ףp?(\?(\??(\?(\?)\(?(\?(\?Q?(\?(\?zG?(\?(\?= ףp=?(\?(\??(\?(\?(\?(\?(\?(\?RQ?RQ?(\?\(\?\(\?(\?Gz?Gz??l@)\(@Q?Mo-؂-@RQ@@ ףp=?&DDDD@|Gz@(\?Z[@p= ף@Gz??qq@@hfffff?M@(\@ Q?I@ Q@أp= ?0` @HzG@(\?t@p= ףp@HzG?},8@@?#O@(\@r= ףp?ɛ@أp= @zG?nS'}'@Gz@RQ? >>@Q@(\?TUUU@\(\@733333?bzkl@433333@p= ף?2-؂@ ףp= @.\(?阙@zG@ףp= ?T[@Q@Q?Xq@(\@?@ffffff@Q?FI@> ףp=@}Gz? ` @Gz@(\?7!"""@Gz@(\?878@> ףp=@(\?ަNO@ffffff@(\?^efff@(\@(\?*|'}@Q@(\?͒>@zG@(\?v@ ףp= @(\?=l@433333@(\?ւ-@\(\@(\?i@Q@(\?d[@Gz@(\?q@ףp= @(\?[1333@@(\?HI@)\(@(\?B_ `@RQ@(\?Muwww@{Gz@(\?8@p= ף@(\?iO@@(\?@!@(\@(\?'}@Q@(\?>@GzG@(\?2H@q= ףp@(\?l@@(\?~+؂-@(\@(\?$oBDDD@Q@(\?&Y[@Gz@(\?ooq@= ףp=@?&Y[@gfffff@= ףp=?#oBDDD@(\@zG?~+؂-@Q@Q?l@zG@#\(?1H@ףp= @?>@433333@k= ףp?'}@[(\@Gz?@!@Q@Q?iO@Gz@](\?8@ףp= @?Muwww@@p= ף?B_ `@Gz@HzG?HI@Q@Q?Z1333@(\@(\?q@@433333?d[@o= ףp@أp= ?i@HzG@|Gz?ւ-@ Q@Q?=l@(\@(\?v@@dfffff?͒>@p= ף@ ףp= ?*|'}@{Gz@Gz?^efff@RQ@Gz?RQ?RQ? ףp= ?RQ?RQ?gfffff?SQ?SQ?(\?RQ?RQ?Q?RQ?RQ?{Gz?RQ?RQ?ףp= ?RQ?RQ?433333?RQ?RQ?(\?RQ?RQ?Q?RQ?RQ?HzG?RQ?RQ?p= ף?RQ?RQ??RQ?RQ?\(\?RQ?RQ?Q?RQ?RQ?Gz?RQ?RQ?q= ףp?SQ?SQ??RQ?RQ?)\(?RQ?RQ?Q?RQ?RQ?zG?RQ?RQ?= ףp=?RQ?RQ??RQ?RQ?(\?RQ?RQ?RQ?RQ?RQ?RQ?\(\?\(\?Gz?l@)\(@?Lo-؂-@RQ@Q?&DDDD@|Gz@@ ףp=?Z[@p= ף@(\??qq@@Gz?M@(\@hfffff?I@ Q@ Q?2` @HzG@أp= ?t@p= ףp@(\?},8@@HzG?#O@(\@?ɛ@Q@n= ףp?pS'}'@ףp= @zG? >>@Gz@NQ?TUUU@Q@(\?bzkl@\(\@733333?2-؂@433333@p= ף?阙@ ףp= @.\(?T[@zG@ףp= ?Xq@Q@Q?@(\@?FI@ffffff@Q? ` @= ףp=@Gz?7!"""@Gz@SQ?878@Gz@SQ?ަNO@> ףp=@SQ?^efff@ffffff@SQ?*|'}@(\@SQ?͒>@Q@SQ?v@zG@SQ?=l@ ףp= @SQ?ւ-@333333@SQ?i@\(\@SQ?d[@Q@SQ?q@Gz@SQ?[1333@ףp= @SQ?HI@@SQ?B_ `@)\(@SQ?Muwww@RQ@SQ?8@{Gz@SQ?iO@p= ף@SQ?@!@@SQ?'}@(\@SQ?>@Q@SQ?1H@GzG@SQ?l@q= ףp@SQ?}+؂-@@SQ?#oBDDD@(\@SQ?&Y[@Q@SQ?poq@Gz@(\?&Y[@= ףp=@?$oBDDD@ffffff@= ףp=?}+؂-@(\@zG?l@Q@Q?2H@zG@-\(?>@ ףp= @?'}@333333@u= ףp?@!@](\@Gz?iO@Q@Q?8@Gz@[(\?Nuwww@ףp= @?B_ `@@p= ף?HI@Q@HzG?Z1333@(\@Q?q@@(\?d[@o= ףp@433333?i@HzG@أp= ?ւ-@Q@{Gz?=l@(\@Q?w@@(\?͒>@p= ף@dfffff?+|'}@{Gz@ ףp= ?^efff@RQ@Gz?ަNO@)\(@Gz?\(\?\(\? ףp= ?\(\?\(\?gfffff?](\?](\?(\?[(\?[(\?Q?\(\?\(\?{Gz?\(\?\(\?ףp= ?\(\?\(\?433333?](\?](\?(\?](\?](\?Q?](\?](\?HzG?\(\?\(\?p= ף?\(\?\(\??\(\?\(\?\(\?\(\?\(\?Q?](\?](\?Gz?^(\?^(\?q= ףp?^(\?^(\??\(\?\(\?)\(?\(\?\(\?Q?\(\?\(\?zG?[(\?[(\?= ףp=?[(\?[(\??[(\?[(\?(\?\(\?\(\?RQ?\(\?\(\?\(\?l@)\(@Gz?Lo-؂-@RQ@?&DDDD@{Gz@Q?Z[@p= ף@@ ףp=??qq@@(\?M@(\@Gz?I@ Q@hfffff?2` @HzG@ Q?t@r= ףp@أp= ?},8@@(\?#O@(\@HzG?ɛ@Q@?nS'}'@@r= ףp? >>@ףp= @zG?TUUU@Gz@RQ?azkl@Q@(\?2-؂@\(\@733333?阙@433333@p= ף?T[@ ףp= @.\(?Xq@zG@ףp= ?@Q@Q?FI@(\@? ` @ffffff@Q?7!"""@> ףp=@}Gz?878@Gz@Z(\?ަNO@Gz@Z(\?^efff@> ףp=@Z(\?*|'}@ffffff@Z(\?͒>@(\@Z(\?v@Q@Z(\?=l@zG@Z(\?ւ-@ ףp= @Z(\?h@333333@Z(\?d[@\(\@Z(\?q@Q@Z(\?[1333@Gz@Z(\?HI@ףp= @Z(\?B_ `@@Z(\?Muwww@)\(@Z(\?8@RQ@Z(\?iO@{Gz@Z(\??!@p= ף@Z(\?'}@@Z(\?>@(\@Z(\?1H@Q@Z(\?l@GzG@Z(\?~+؂-@q= ףp@Z(\?$oBDDD@@Z(\?&Y[@(\@^(\?poq@Q@SQ?&Y[@Gz@(\?$oBDDD@> ףp=@?~+؂-@gfffff@= ףp=?l@(\@zG?1H@Q@Q?>@zG@#\(?'}@ףp= @??!@333333@k= ףp?iO@[(\@Gz?8@Q@Q?Muwww@Gz@](\?B_ `@ףp= @?HI@Q@p= ף?Z1333@(\@HzG?q@@Q?d[@o= ףp@(\?i@HzG@433333?ւ-@Q@أp= ?=l@(\@{Gz?v@@Q?͒>@p= ף@(\?*|'}@{Gz@dfffff?^efff@RQ@ ףp= ?ަNO@)\(@Gz?878@@Gz?Gz?Gz? ףp= ?Gz?Gz?gfffff?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?{Gz?Gz?Gz?ףp= ?Gz?Gz?433333?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?HzG?Gz?Gz?p= ף?Gz?Gz??Gz?Gz?\(\?Gz?Gz?Q?Gz?Gz?Gz?Gz?Gz?q= ףp?Gz?Gz??Gz?Gz?)\(?Gz?Gz?Q?Gz?Gz?zG?Gz?Gz?= ףp=?Gz?Gz??Gz?Gz?(\?Gz?Gz?RQ?l@)\(@\(\?Lo-؂-@RQ@Gz?&DDDD@{Gz@?Z[@p= ף@Q??qq@@@ ףp=?M@(\@(\?I@ Q@Gz?2` @HzG@hfffff?t@r= ףp@ Q?~,8@@أp= ?#O@(\@(\?ɛ@Q@HzG?oS'}'@Gz@? >>@@n= ףp?TUUU@ףp= @zG?bzkl@Gz@NQ?2-؂@Q@(\?阙@\(\@733333?T[@433333@p= ף?Xq@ ףp= @.\(?@zG@ףp= ?FI@Q@Q? ` @(\@?7!"""@ffffff@Q?878@> ףp=@Gz?ަNO@Gz@Gz?^efff@Gz@Gz?*|'}@> ףp=@Gz?͒>@ffffff@Gz?v@(\@Gz?=l@Q@Gz?ւ-@zG@Gz?i@ ףp= @Gz?d[@333333@Gz?q@\(\@Gz?[1333@Q@Gz?HI@Gz@Gz?B_ `@ףp= @Gz?Muwww@@Gz?8@)\(@Gz?iO@RQ@Gz?@!@{Gz@Gz?'}@p= ף@Gz?>@@Gz?2H@(\@Gz?l@Q@Gz?}+؂-@GzG@Gz?#oBDDD@q= ףp@Gz?&Y[@@Gz?ooq@(\@Z(\?&Y[@Q@SQ?$oBDDD@Gz@(\?~+؂-@= ףp=@?l@ffffff@= ףp=?2H@(\@zG?>@Q@Q?'}@zG@-\(?@!@ ףp= @?iO@333333@u= ףp?8@](\@Gz?Muwww@Q@Q?B_ `@Gz@[(\?HI@ףp= @?Z1333@(\@p= ף?q@@HzG?d[@o= ףp@Q?i@HzG@(\?ւ-@Q@433333?=l@(\@أp= ?v@@{Gz?͒>@p= ף@Q?*|'}@{Gz@(\?^efff@RQ@dfffff?ަNO@)\(@ ףp= ?878@@Gz?7!"""@ףp= @Gz??? ףp= ???gfffff???(\???Q???{Gz???ףp= ???433333???(\???Q???HzG???p= ף??????\(\???Q???Gz???q= ףp??????)\(???Q???zG???= ףp=??????(\?l@)\(@RQ?Lo-؂-@RQ@\(\?&DDDD@{Gz@Gz?Z[@p= ף@?>qq@@Q?M@(\@@ ףp=?I@ Q@(\?2` @HzG@Gz?t@r= ףp@hfffff?~,8@@ Q?$O@(\@أp= ?ɛ@Q@(\?oS'}'@Gz@IzG? >>@)\(@?TUUU@@r= ףp?azkl@ףp= @zG?2-؂@Gz@RQ?阙@Q@(\?T[@\(\@733333?Xq@433333@p= ף?@ ףp= @.\(?FI@zG@ףp= ? ` @Q@Q?7!"""@(\@?878@ffffff@Q?ަNO@> ףp=@}Gz?^efff@Gz@?*|'}@Gz@?͒>@> ףp=@?w@ffffff@?=l@(\@?ւ-@Q@?i@zG@?d[@ ףp= @?q@333333@?[1333@\(\@?HI@Q@?B_ `@Gz@?Muwww@ףp= @?8@@?iO@)\(@?@!@RQ@?'}@{Gz@?>@p= ף@?2H@@?l@(\@?~+؂-@Q@?$oBDDD@GzG@?&Y[@q= ףp@?ooq@@Gz?&Y[@(\@Z(\?$oBDDD@Q@SQ?~+؂-@Gz@(\?l@> ףp=@?2H@ffffff@= ףp=?>@(\@zG?'}@Q@Q??!@zG@#\(?iO@ ףp= @?8@333333@k= ףp?Muwww@[(\@Gz?B_ `@Q@Q?HI@Gz@\(\?Z1333@(\@?q@@p= ף?d[@o= ףp@HzG?i@HzG@Q?ւ-@Q@(\?=l@(\@433333?v@@أp= ?͒>@p= ף@|Gz?*|'}@{Gz@Q?^efff@RQ@(\?ަNO@)\(@dfffff?878@@ ףp= ?7!"""@ףp= @Gz? ` @Gz@Gz?Q?Q? ףp= ?Q?Q?gfffff?Q?Q?(\?Q?Q?Q?Q?Q?{Gz?Q?Q?ףp= ?Q?Q?433333?Q?Q?(\?Q?Q?Q?Q?Q?HzG?Q?Q?p= ף?Q?Q??Q?Q?](\?Q?Q?Q?Q?Q?Gz?Q?Q?q= ףp?Q?Q??Q?Q?)\(?Q?Q?Q?Q?Q?zG?Q?Q?= ףp=?Q?Q??l@)\(@(\?Lo-؂-@RQ@SQ?&DDDD@{Gz@^(\?Z[@p= ף@Gz?>qq@@?M@(\@Q?I@Q@> ףp=?1` @HzG@(\?t@q= ףp@Gz?},8@@efffff?#O@(\@Q?ɛ@Q@أp= ?oS'}'@Gz@(\? >>@= ףp=@JzG?TUUU@)\(@?azkl@@t= ףp?2-؂@ףp= @zG?阙@Gz@TQ?T[@Q@(\?Xq@\(\@333333?@333333@p= ף?FI@ ףp= @&\(? ` @zG@ףp= ?7!"""@Q@Q?878@(\@?ަNO@ffffff@Q?^efff@> ףp=@@Gz?*|'}@Gz@Q?͒>@Gz@Q?v@> ףp=@Q?=l@gfffff@Q?ւ-@(\@Q?h@Q@Q?d[@zG@Q?q@ ףp= @Q?[1333@333333@Q?HI@\(\@Q?B_ `@Q@Q?Muwww@Gz@Q?8@ףp= @Q?iO@@Q??!@)\(@Q?'}@QQ@Q?>@{Gz@Q?1H@p= ף@Q?l@@Q?~+؂-@(\@Q?$oBDDD@Q@Q?&Y[@HzG@Q?poq@p= ףp@?&Y[@@Gz?$oBDDD@(\@Z(\?~+؂-@Q@SQ?l@Gz@(\?1H@= ףp=@?>@ffffff@= ףp=?'}@(\@zG??!@Q@Q?iO@zG@'\(?8@ ףp= @?Muwww@233333@o= ףp?B_ `@[(\@Gz?HI@Q@Q?[1333@Gz@](\?q@@?d[@q= ףp@p= ף?i@HzG@GzG?ւ-@Q@Q?=l@(\@(\?v@@633333?͒>@p= ף@֣p= ?*|'}@{Gz@zGz?^efff@RQ@Q?ަNO@*\(@(\?878@@ffffff?7!"""@أp= @ ףp= ? ` @Gz@Gz?FI@Q@Gz?> ףp=?> ףp=? ףp= ?@ ףp=?@ ףp=?gfffff?? ףp=?? ףp=?(\?? ףp=?? ףp=?Q?< ףp=?< ףp=?{Gz?> ףp=?> ףp=?ףp= ?> ףp=?> ףp=?433333?@ ףp=?@ ףp=?(\?@ ףp=?@ ףp=?Q?= ףp=?= ףp=?HzG?> ףp=?> ףp=?p= ף?> ףp=?> ףp=??@ ףp=?@ ףp=?](\?@ ףp=?@ ףp=?Q?? ףp=?? ףp=?Gz?? ףp=?? ףp=?q= ףp?? ףp=?? ףp=??= ףp=?= ףp=?)\(?= ףp=?= ףp=?Q?= ףp=?= ףp=?zG?= ףp=?= ףp=?> ףp=?l@)\(@?Lo-؂-@RQ@(\?&DDDD@{Gz@SQ?Z[@p= ף@^(\?>qq@@Gz?M@(\@?I@Q@Q?1` @HzG@= ףp=?t@q= ףp@(\?},8@@Gz?#O@(\@efffff?ɛ@Q@Q?oS'}'@Gz@أp= ? >>@= ףp=@(\?TUUU@RQ@JzG?azkl@)\(@?2-؂@@t= ףp?阙@ףp= @zG?T[@Gz@TQ?Xq@Q@(\?@\(\@333333?FI@333333@p= ף? ` @ ףp= @&\(?7!"""@zG@ףp= ?878@Q@Q?ަNO@(\@?^efff@ffffff@Q?*|'}@> ףp=@@Gz?͒>@Gz@> ףp=?v@Gz@> ףp=?=l@> ףp=@> ףp=?ւ-@gfffff@> ףp=?i@(\@> ףp=?d[@Q@> ףp=?q@zG@< ףp=?[1333@ ףp= @< ףp=?HI@333333@< ףp=?B_ `@\(\@< ףp=?Muwww@Q@< ףp=?8@Gz@< ףp=?iO@ףp= @< ףp=??!@@< ףp=?'}@)\(@< ףp=?>@QQ@< ףp=?1H@{Gz@; ףp=?l@p= ף@; ףp=?~+؂-@@; ףp=?$oBDDD@(\@> ףp=?&Y[@Q@> ףp=?poq@HzG@Q?&Y[@p= ףp@?$oBDDD@@Gz?~+؂-@(\@^(\?l@Q@QQ?1H@Gz@(\?>@> ףp=@?'}@ffffff@? ףp=??!@(\@zG?iO@Q@Q?8@zG@&\(?Nuwww@ ףp= @?B_ `@233333@n= ףp?HI@[(\@Gz?[1333@Q@Q?q@@](\?d[@q= ףp@?i@HzG@p= ף?ւ-@ Q@GzG?=l@(\@Q?w@@(\?͒>@p= ף@633333?+|'}@|Gz@֣p= ?^efff@RQ@{Gz?ߦNO@*\(@Q?878@@(\?7!"""@أp= @ffffff? ` @Gz@ ףp= ?FI@Q@Gz?@\(\@Gz?(\?(\? ףp= ?(\?(\?gfffff?(\?(\?(\?(\?(\?Q?(\?(\?{Gz?(\?(\?ףp= ?(\?(\?433333?(\?(\?(\?(\?(\?Q?(\?(\?HzG?(\?(\?p= ף?(\?(\??(\?(\?](\?(\?(\?Q?(\?(\?Gz?(\?(\?q= ףp?(\?(\??(\?(\?)\(?(\?(\?Q?(\?(\?zG?l@)\(@> ףp=?Lo-؂-@RQ@?&DDDD@{Gz@(\?Z[@p= ף@SQ?>qq@@^(\?M@(\@Gz?I@Q@?1` @HzG@Q?t@q= ףp@= ףp=?},8@@(\?#O@(\@Gz?ɛ@Q@efffff?oS'}'@Gz@Q? >>@> ףp=@أp= ?TUUU@ffffff@(\?azkl@RQ@JzG?2-؂@)\(@?阙@@t= ףp?T[@ףp= @zG?Xq@Gz@TQ?@Q@(\?FI@\(\@333333? ` @333333@p= ף?7!"""@ ףp= @&\(?878@zG@ףp= ?ަNO@Q@Q?^efff@(\@?*|'}@ffffff@Q?͒>@= ףp=@@Gz?v@Gz@(\?=l@Gz@(\?ւ-@> ףp=@(\?i@gfffff@(\?d[@(\@(\?q@Q@(\?[1333@zG@(\?HI@ ףp= @(\?B_ `@333333@(\?Muwww@\(\@(\?8@Q@(\?iO@Gz@(\??!@ףp= @(\?'}@@(\?>@)\(@(\?1H@QQ@(\?l@{Gz@(\?}+؂-@p= ף@(\?$oBDDD@@(\?&Y[@(\@(\?poq@Q@9 ףp=?&Y[@GzG@Q?$oBDDD@p= ףp@?~+؂-@@Gz?l@(\@Z(\?2H@Q@SQ?>@Gz@(\?'}@= ףp=@??!@ffffff@= ףp=?iO@(\@zG?8@Q@Q?Muwww@zG@$\(?B_ `@ ףp= @?HI@233333@l= ףp?Z1333@[(\@Gz?q@Q@Q?d[@q= ףp@](\?i@HzG@?ւ-@Q@p= ף?=l@(\@FzG?v@@Q?͒>@p= ף@(\?*|'}@{Gz@533333?^efff@RQ@֣p= ?ަNO@)\(@zGz?878@@Q?7!"""@أp= @(\? ` @Gz@ffffff?FI@Q@ ףp= ?@\(\@Gz?Xq@433333@Gz?Gz?Gz? ףp= ?Gz?Gz?gfffff?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?{Gz?Gz?Gz?ףp= ?Gz?Gz?433333?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?HzG?Gz?Gz?p= ף?Gz?Gz??Gz?Gz?](\?Gz?Gz?Q?Gz?Gz?Gz?Gz?Gz?q= ףp?Gz?Gz??Gz?Gz?)\(?Gz?Gz?Q?l@)\(@zG?Lo-؂-@RQ@> ףp=?&DDDD@{Gz@?Z[@p= ף@(\?>qq@@SQ?M@(\@^(\?I@Q@Gz?1` @HzG@?t@q= ףp@Q?},8@@= ףp=?#O@(\@(\?ɛ@Q@Gz?oS'}'@Gz@efffff? >>@> ףp=@Q?TUUU@gfffff@ڣp= ?azkl@{Gz@(\?2-؂@RQ@JzG?阙@)\(@?T[@@t= ףp?Xq@أp= @zG?@Gz@TQ?FI@Q@(\? ` @\(\@333333?7!"""@333333@p= ף?878@ ףp= @&\(?ަNO@zG@ףp= ?^efff@Q@Q?*|'}@(\@?͒>@ffffff@Q?v@= ףp=@@Gz?=l@Gz@Gz?ւ-@Gz@Gz?i@> ףp=@Gz?d[@gfffff@Gz?q@(\@Gz?[1333@Q@Gz?HI@zG@Gz?B_ `@ ףp= @Gz?Muwww@333333@Gz?8@\(\@Gz?iO@Q@Gz??!@Gz@Gz?'}@ףp= @Gz?>@@Gz?1H@)\(@Gz?l@QQ@Gz?}+؂-@{Gz@Gz?#oBDDD@p= ף@Gz?&Y[@@Gz?poq@(\@(\?&Y[@ Q@5 ףp=?$oBDDD@FzG@Q?~+؂-@r= ףp@?l@@Gz?2H@(\@V(\?>@Q@UQ?'}@Gz@(\?>!@< ףp=@?iO@hfffff@< ףp=?8@(\@zG?Muwww@Q@Q?B_ `@zG@&\(?HI@ ףp= @?[1333@233333@n= ףp?q@[(\@Gz?d[@q= ףp@Q?i@HzG@](\?ւ-@Q@?=l@(\@p= ף?w@@FzG?͒>@p= ף@Q?*|'}@{Gz@(\?^efff@RQ@633333?ަNO@*\(@֣p= ?878@@zGz?7!"""@أp= @Q? ` @Gz@(\?FI@Q@ffffff?@\(\@ ףp= ?Xq@433333@Gz?T[@ ףp= @Gz?ffffff?ffffff? ףp= ?hfffff?hfffff?gfffff?gfffff?gfffff?(\?gfffff?gfffff?Q?dfffff?dfffff?{Gz?ffffff?ffffff?ףp= ?ffffff?ffffff?433333?hfffff?hfffff?(\?hfffff?hfffff?Q?efffff?efffff?HzG?ffffff?ffffff?p= ף?efffff?efffff??hfffff?hfffff?](\?hfffff?hfffff?Q?gfffff?gfffff?Gz?gfffff?gfffff?q= ףp?gfffff?gfffff??ffffff?ffffff?)\(?l@)\(@Q?Mo-؂-@RQ@zG?&DDDD@{Gz@> ףp=?Z[@p= ף@??qq@@(\?M@(\@SQ?I@Q@^(\?1` @HzG@Gz?t@q= ףp@?},8@@Q?#O@(\@= ףp=?ɛ@Q@(\?pS'}'@Gz@Gz? >>@> ףp=@efffff?TUUU@gfffff@Q?bzkl@(\@ڣp= ?2-؂@{Gz@(\?阙@RQ@JzG?T[@*\(@?Xq@@t= ףp?@أp= @zG?FI@Gz@TQ? ` @Q@(\?7!"""@\(\@333333?878@333333@p= ף?ަNO@ ףp= @&\(?^efff@zG@ףp= ?*|'}@Q@Q?͒>@(\@?v@ffffff@Q?=l@= ףp=@@Gz?ւ-@Gz@ffffff?h@Gz@ffffff?d[@> ףp=@ffffff?q@gfffff@ffffff?[1333@(\@ffffff?HI@Q@ffffff?B_ `@zG@dfffff?Muwww@ ףp= @dfffff?8@333333@cfffff?iO@\(\@dfffff??!@Q@dfffff?'}@Gz@dfffff?>@ףp= @dfffff?2H@@cfffff?l@)\(@cfffff?~+؂-@QQ@cfffff?$oBDDD@{Gz@cfffff?&Y[@p= ף@ifffff?poq@@Gz?&Y[@(\@(\?$oBDDD@Q@9 ףp=?~+؂-@GzG@Q?l@p= ףp@?2H@@Gz?>@(\@Z(\?'}@Q@SQ??!@Gz@(\?iO@> ףp=@?8@ffffff@= ףp=?Luwww@(\@zG?B_ `@Q@Q?HI@zG@'\(?[1333@ ףp= @?q@233333@o= ףp?d[@[(\@Gz?i@HzG@Q?ւ-@Q@_(\?=l@(\@?v@@p= ף?͒>@p= ף@FzG?*|'}@{Gz@Q?^efff@SQ@(\?ަNO@)\(@533333?878@@֣p= ?7!"""@ףp= @zGz? ` @Gz@Q?FI@Q@(\?@\(\@gfffff?Xq@533333@ ףp= ?S[@ ףp= @Gz?阙@zG@Gz?Q?Q? ףp= ? Q? Q?gfffff?Q?Q?(\?Q?Q?Q?Q?Q?{Gz?Q?Q?ףp= ?Q?Q?433333? Q? Q?(\? Q? Q?Q?Q?Q?HzG?Q?Q?p= ף?Q?Q?? Q? Q?](\? Q? Q?Q?Q?Q?Gz?Q?Q?q= ףp?Q?Q??l@)\(@)\(?Mo-؂-@RQ@Q?&DDDD@{Gz@zG?Z[@p= ף@> ףp=??qq@@?M@(\@(\?I@Q@SQ?1` @HzG@^(\?t@q= ףp@Gz?},8@@?#O@(\@Q?ɛ@Q@> ףp=?pS'}'@Gz@(\? >>@> ףp=@Gz?TUUU@gfffff@efffff?bzkl@(\@Q?2-؂@p= ף@ڣp= ?阙@|Gz@(\?T[@RQ@JzG?Xq@*\(@?@@t= ףp?FI@أp= @zG? ` @Gz@TQ?7!"""@Q@(\?878@\(\@333333?ަNO@333333@p= ף?^efff@ ףp= @&\(?*|'}@zG@ףp= ?͒>@Q@Q?v@(\@?=l@ffffff@Q?ւ-@= ףp=@@Gz?h@Gz@Q?d[@Gz@Q?q@> ףp=@Q?[1333@gfffff@Q?HI@(\@Q?B_ `@Q@Q?Muwww@zG@Q?8@ ףp= @Q?iO@333333@Q??!@\(\@Q?'}@Q@Q?>@Gz@Q?1H@ףp= @Q?l@@Q?~+؂-@)\(@Q?$oBDDD@QQ@Q?&Y[@{Gz@!Q?poq@p= ף@efffff?&Y[@@Gz?$oBDDD@(\@(\?~+؂-@Q@> ףp=?l@HzG@Q?1H@p= ףp@?>@@Gz?'}@(\@^(\??!@Q@QQ?iO@Gz@(\?8@> ףp=@?Luwww@gfffff@? ףp=?B_ `@(\@zG?HI@Q@Q?Z1333@zG@&\(?q@ ףp= @?d[@233333@o= ףp?i@HzG@Gz?ւ-@ Q@Q?=l@(\@](\?v@@?͒>@p= ף@p= ף?+|'}@|Gz@FzG?^efff@RQ@Q?ަNO@)\(@(\?878@@633333?7!"""@أp= @֣p= ? ` @Gz@{Gz?FI@Q@Q?@\(\@(\?Xq@433333@ffffff?T[@ ףp= @ ףp= ?阙@zG@Gz?2-؂@Q@Gz?أp= ?أp= ? ףp= ?أp= ?أp= ?gfffff?٣p= ?٣p= ?(\?أp= ?أp= ?Q?أp= ?أp= ?{Gz?أp= ?أp= ?ףp= ?أp= ?أp= ?333333?أp= ?أp= ?(\?أp= ?أp= ?Q?٣p= ?٣p= ?HzG?أp= ?أp= ?p= ף?أp= ?أp= ??أp= ?أp= ?\(\?أp= ?أp= ?Q?ףp= ?ףp= ?Gz?ףp= ?ףp= ?p= ףp?l@)\(@?Lo-؂-@RQ@*\(?&DDDD@|Gz@Q?Z[@p= ף@zG?>qq@@> ףp=?M@(\@?I@Q@(\?0` @HzG@RQ?t@p= ףp@\(\?},8@@Gz?#O@(\@?ɛ@Q@Q?oS'}'@Gz@> ףp=? >>@> ףp=@(\?TUUU@ffffff@Gz?bzkl@(\@efffff?2-؂@Q@Q?阙@p= ף@أp= ?S[@{Gz@(\?Xq@RQ@HzG?@)\(@?FI@@p= ףp? ` @ףp= @zG?7!"""@Gz@PQ?878@Q@(\?ަNO@\(\@133333?^efff@433333@p= ף?*|'}@ ףp= @"\(?͒>@zG@ףp= ?v@Q@Q?=l@(\@?ւ-@gfffff@Q?h@= ףp=@Gz?d[@Gz@أp= ?q@Gz@أp= ?Z1333@> ףp=@أp= ?HI@ffffff@أp= ?B_ `@(\@أp= ?Muwww@Q@أp= ?8@zG@أp= ?iO@ ףp= @أp= ??!@333333@أp= ?'}@\(\@أp= ?>@Q@أp= ?1H@Gz@أp= ?l@ףp= @أp= ?~+؂-@@أp= ?$oBDDD@)\(@أp= ?&Y[@RQ@أp= ?poq@{Gz@Q?&Y[@p= ף@mfffff?$oBDDD@@Gz?~+؂-@(\@(\?l@ Q@6 ףp=?2H@FzG@Q?>@r= ףp@?'}@@Gz?@!@(\@V(\?iO@Q@UQ?8@Gz@(\?Muwww@= ףp=@?B_ `@gfffff@; ףp=?HI@(\@zG?[1333@Q@Q?q@zG@'\(?d[@ ףp= @?h@333333@p= ףp?ւ-@Q@Gz?=l@(\@Q?v@@^(\?͒>@p= ף@?*|'}@{Gz@p= ף?^efff@RQ@HzG?ަNO@)\(@Q?878@@(\?7!"""@ףp= @433333? ` @Gz@أp= ?FI@Q@xGz?@[(\@ Q?Xq@533333@(\?S[@ ףp= @hfffff?阙@zG@ףp= ?2-؂@Q@Gz?bzkl@(\@Gz?(\?(\? ףp= ?(\?(\?gfffff?(\?(\?(\?(\?(\?Q?(\?(\?{Gz?(\?(\?ףp= ?(\?(\?333333?(\?(\?(\?(\?(\?Q?(\?(\?HzG?(\?(\?p= ף?(\?(\??(\?(\?\(\?(\?(\?Q?(\?(\?Gz?l@)\(@p= ףp?Lo-؂-@RQ@?&DDDD@|Gz@*\(?Z[@p= ף@Q??qq@@zG?M@(\@> ףp=?I@Q@?1` @HzG@(\?t@p= ףp@RQ?},8@@\(\?#O@(\@Gz?ɛ@Q@?oS'}'@Gz@Q? >>@> ףp=@= ףp=?TUUU@ffffff@(\?bzkl@(\@Gz?2-؂@Q@ifffff?阙@@!Q?S[@p= ף@أp= ?Xq@{Gz@(\?@RQ@HzG?FI@)\(@? ` @@p= ףp?7!"""@ףp= @zG?878@Gz@PQ?ަNO@Q@(\?^efff@\(\@/33333?*|'}@233333@p= ף?͒>@ ףp= @\(?v@zG@֣p= ?=l@Q@Q?ւ-@(\@?h@gfffff@Q?d[@= ףp=@Gz?q@Gz@(\?[1333@Gz@(\?HI@> ףp=@(\?B_ `@ffffff@(\?Nuwww@(\@(\?8@Q@(\?iO@zG@(\??!@ ףp= @(\?'}@333333@(\?>@\(\@(\?2H@Q@(\?l@Gz@(\?~+؂-@ףp= @(\?$oBDDD@@(\?&Y[@)\(@(\?poq@RQ@أp= ?&Y[@{Gz@ Q?$oBDDD@p= ף@hfffff?~+؂-@@Gz?l@(\@(\?2H@Q@8 ףp=?>@GzG@Q?'}@q= ףp@?@!@@Gz?iO@(\@Z(\?8@Q@SQ?Nuwww@Gz@(\?B_ `@= ףp=@?HI@ffffff@> ףp=?[1333@(\@zG?q@Q@Q?d[@zG@(\(?i@ ףp= @?ւ-@Q@p= ףp?=l@(\@Gz?w@@Q?͒>@p= ף@[(\?*|'}@zGz@?^efff@RQ@p= ף?ަNO@*\(@HzG?878@@Q?7!"""@֣p= @(\? ` @Gz@433333?FI@Q@أp= ?@\(\@xGz?Xq@433333@ Q?T[@ ףp= @(\?阙@zG@hfffff?2-؂@Q@ףp= ?bzkl@(\@Gz?TUUU@ffffff@Gz?HzG?HzG? ףp= ?HzG?HzG?gfffff?IzG?IzG?(\?HzG?HzG?Q?HzG?HzG?{Gz?HzG?HzG?ףp= ?HzG?HzG?333333?HzG?HzG?(\?HzG?HzG?Q?IzG?IzG?HzG?HzG?HzG?p= ף?HzG?HzG??HzG?HzG?\(\?HzG?HzG?Q?l@)\(@Gz?Lo-؂-@RQ@p= ףp?&DDDD@{Gz@?Z[@p= ף@*\(??qq@@Q?M@(\@zG?I@Q@> ףp=?1` @HzG@?t@q= ףp@(\?},8@@RQ?#O@(\@\(\?ɛ@Q@Gz?oS'}'@Gz@? >>@> ףp=@Q?TUUU@ffffff@= ףp=?bzkl@(\@(\?2-؂@Q@Gz?阙@zG@gfffff?T[@@Q?Xq@p= ף@أp= ?@{Gz@(\?FI@RQ@HzG? ` @)\(@?7!"""@@p= ףp?878@ףp= @zG?ަNO@Gz@PQ?^efff@Q@(\?*|'}@\(\@133333?͒>@433333@p= ף?v@ ףp= @"\(?=l@zG@ףp= ?ւ-@Q@Q?h@(\@?d[@gfffff@Q?q@= ףp=@Gz?[1333@Gz@HzG?HI@Gz@HzG?B_ `@> ףp=@HzG?Muwww@ffffff@HzG?8@(\@HzG?iO@Q@HzG??!@zG@HzG?'}@ ףp= @HzG?>@333333@HzG?2H@\(\@HzG?l@Q@HzG?~+؂-@Gz@HzG?$oBDDD@ףp= @HzG?&Y[@@HzG?poq@)\(@(\?&Y[@SQ@ңp= ?$oBDDD@zGz@%Q?~+؂-@p= ף@cfffff?l@@Gz?2H@(\@(\?>@ Q@6 ףp=?'}@FzG@Q?@!@r= ףp@?iO@@Gz?8@(\@V(\?Nuwww@Q@UQ?B_ `@Gz@(\?HI@= ףp=@?Z1333@gfffff@; ףp=?q@(\@zG?d[@Q@Q?h@zG@)\(?ւ-@ ףp= @?=l@(\@p= ףp?v@@Gz?͒>@p= ף@Q?*|'}@{Gz@](\?^efff@RQ@?ަNO@)\(@p= ף?878@@HzG?7!"""@ףp= @Q? ` @Gz@(\?FI@Q@333333?@[(\@أp= ?Xq@333333@xGz?S[@ ףp= @ Q?阙@zG@(\?2-؂@Q@hfffff?bzkl@(\@ףp= ?TUUU@efffff@Gz? >>@? ףp=@Gz??? ףp= ???gfffff???(\???Q???{Gz???ףp= ???333333???(\???Q???HzG???p= ף??????\(\?l@)\(@Q?Lo-؂-@RQ@Gz?&DDDD@{Gz@p= ףp?Z[@p= ף@??qq@@*\(?M@(\@Q?I@ Q@zG?1` @HzG@> ףp=?t@q= ףp@?},8@@(\?#O@(\@RQ?ɛ@Q@\(\?oS'}'@Gz@Gz? >>@> ףp=@?TUUU@ffffff@Q?bzkl@(\@= ףp=?2-؂@Q@(\?阙@zG@Gz?S[@(\@ifffff?Xq@@!Q?@p= ף@أp= ?FI@{Gz@(\? ` @RQ@HzG?7!"""@)\(@?878@@p= ףp?ަNO@ףp= @zG?^efff@Gz@PQ?*|'}@Q@(\?͒>@\(\@/33333?v@233333@p= ף?=l@ ףp= @\(?ւ-@zG@֣p= ?i@Q@Q?d[@(\@?q@gfffff@Q?[1333@= ףp=@Gz?HI@Gz@?B_ `@Gz@?Muwww@> ףp=@?8@ffffff@?iO@(\@??!@Q@?'}@zG@?>@ ףp= @?2H@333333@?l@\(\@?~+؂-@Q@?$oBDDD@Gz@?&Y[@ףp= @?poq@@HzG?&Y[@)\(@(\?$oBDDD@RQ@أp= ?~+؂-@{Gz@ Q?l@p= ף@hfffff?2H@@Gz?>@(\@(\?'}@Q@B ףp=?@!@HzG@~Q?iO@p= ףp@?8@@Gz?Nuwww@(\@b(\?B_ `@Q@OQ?HI@Gz@(\?Z1333@> ףp=@?q@ffffff@A ףp=?d[@(\@zG?i@Q@Q?ւ-@zG@(\(?=l@(\@?v@@p= ףp?͒>@p= ף@Gz?+|'}@|Gz@Q?^efff@RQ@[(\?ަNO@(\(@?878@@p= ף?7!"""@أp= @HzG? ` @Gz@Q?FI@Q@(\?@\(\@433333?Xq@433333@أp= ?T[@ ףp= @xGz?阙@zG@ Q?2-؂@Q@(\?bzkl@(\@hfffff?TUUU@ffffff@ףp= ? >>@> ףp=@Gz?oS'}'@Gz@Gz?p= ףp?p= ףp? ףp= ?o= ףp?o= ףp?gfffff?r= ףp?r= ףp?(\?p= ףp?p= ףp?Q?q= ףp?q= ףp?{Gz?p= ףp?p= ףp?ףp= ?p= ףp?p= ףp?333333?o= ףp?o= ףp?(\?o= ףp?o= ףp?Q?r= ףp?r= ףp?HzG?q= ףp?q= ףp?p= ף?q= ףp?q= ףp??l@)\(@\(\?Lo-؂-@RQ@Q?&DDDD@{Gz@Gz?Z[@p= ף@p= ףp?>qq@@?M@(\@*\(?I@ Q@Q?2` @HzG@zG?t@q= ףp@> ףp=?},8@@?#O@(\@(\?ɛ@Q@RQ?oS'}'@Gz@\(\? >>@> ףp=@Gz?TUUU@ffffff@?azkl@(\@Q?2-؂@Q@= ףp=?阙@zG@(\?T[@ ףp= @Gz?Xq@(\@gfffff?@@Q?FI@p= ף@أp= ? ` @{Gz@(\?7!"""@RQ@HzG?878@)\(@?ަNO@@p= ףp?^efff@ףp= @zG?*|'}@Gz@PQ?͒>@Q@(\?v@\(\@133333?=l@433333@p= ף?ւ-@ ףp= @"\(?h@zG@ףp= ?d[@Q@Q?q@(\@?[1333@gfffff@Q?HI@= ףp=@Gz?B_ `@Gz@p= ףp?Muwww@Gz@p= ףp?8@> ףp=@p= ףp?iO@ffffff@p= ףp??!@(\@p= ףp?'}@Q@p= ףp?>@zG@p= ףp?1H@ ףp= @p= ףp?l@333333@p= ףp?~+؂-@\(\@p= ףp?$oBDDD@Q@p= ףp?&Y[@Gz@p= ףp?poq@ףp= @?&Y[@@NzG?$oBDDD@*\(@(\?~+؂-@RQ@ޣp= ?l@|Gz@Q?2H@p= ף@mfffff?>@@Gz?'}@(\@(\?@!@ Q@6 ףp=?iO@FzG@Q?8@q= ףp@?Nuwww@@Gz?B_ `@(\@V(\?HI@Q@UQ?Z1333@Gz@(\?q@= ףp=@?d[@gfffff@; ףp=?i@(\@zG?ւ-@Q@Q?=l@zG@(\(?v@@?͒>@p= ף@p= ףp?*|'}@{Gz@Gz?^efff@QQ@Q?ަNO@)\(@](\?878@@?7!"""@ףp= @p= ף? ` @Gz@HzG?FI@Q@Q?@](\@(\?Xq@333333@433333?S[@ ףp= @أp= ?阙@zG@xGz?2-؂@Q@ Q?bzkl@(\@(\?TUUU@efffff@hfffff? >>@? ףp=@ףp= ?nS'}'@Gz@Gz?ʛ@Q@Gz?zG?zG? ףp= ?zG?zG?gfffff?zG?zG?(\?zG?zG?Q?zG?zG?{Gz?zG?zG?ףp= ?zG?zG?333333?zG?zG?(\?zG?zG?Q?zG?zG?HzG?zG?zG?p= ף?l@)\(@?Lo-؂-@RQ@\(\?&DDDD@{Gz@Q?Z[@p= ף@Gz?>qq@@p= ףp?M@(\@?I@ Q@*\(?2` @HzG@Q?t@r= ףp@zG?},8@@> ףp=?#O@(\@?ɛ@Q@(\?oS'}'@Gz@RQ? >>@> ףp=@\(\?TUUU@ffffff@Gz?azkl@(\@?2-؂@Q@Q?阙@zG@= ףp=?T[@ ףp= @(\?Xq@ Q@Gz?@(\@ifffff?FI@@!Q? ` @p= ף@أp= ?7!"""@{Gz@(\?878@RQ@HzG?ަNO@)\(@?^efff@@p= ףp?*|'}@ףp= @zG?͒>@Gz@PQ?w@Q@(\?=l@\(\@/33333?ւ-@333333@p= ף?i@ ףp= @\(?d[@zG@֣p= ?q@Q@Q?[1333@(\@?HI@gfffff@Q?B_ `@= ףp=@Gz?Muwww@Gz@zG?8@Gz@zG?iO@> ףp=@zG??!@ffffff@zG?'}@(\@zG?>@Q@zG?1H@zG@zG?l@ ףp= @zG?~+؂-@333333@zG?$oBDDD@\(\@zG?&Y[@Q@zG?poq@Gz@p= ףp?&Y[@ףp= @?$oBDDD@@HzG?~+؂-@)\(@(\?l@RQ@أp= ?1H@{Gz@ Q?>@p= ף@hfffff?'}@@Gz??!@(\@(\?iO@Q@8 ףp=?8@GzG@Q?Muwww@q= ףp@?B_ `@@Gz?HI@(\@Z(\?[1333@Q@SQ?q@Gz@(\?d[@= ףp=@?i@ffffff@> ףp=?ւ-@(\@zG?=l@Q@Q?v@@(\(?͒>@p= ף@?*|'}@zGz@p= ףp?^efff@RQ@Gz?ަNO@*\(@Q?878@@[(\?7!"""@֣p= @? ` @Gz@p= ף?FI@Q@HzG?@\(\@Q?Xq@233333@(\?T[@ ףp= @433333?阙@zG@أp= ?2-؂@Q@xGz?bzkl@(\@ Q?TUUU@ffffff@(\? >>@> ףp=@hfffff?oS'}'@Gz@ףp= ?ɛ@Q@Gz?"O@(\@Gz?PQ?PQ? ףp= ?OQ?OQ?gfffff?RQ?RQ?(\?OQ?OQ?Q?QQ?QQ?{Gz?PQ?PQ?ףp= ?PQ?PQ?333333?OQ?OQ?(\?OQ?OQ?Q?RQ?RQ?HzG?l@)\(@p= ף?Lo-؂-@RQ@?&DDDD@{Gz@\(\?Z[@p= ף@Q?>qq@@Gz?M@(\@p= ףp?I@Q@?2` @HzG@*\(?t@r= ףp@Q?~,8@@zG?#O@(\@> ףp=?ɛ@Q@?oS'}'@Gz@(\? >>@> ףp=@RQ?TUUU@ffffff@\(\?azkl@(\@Gz?2-؂@Q@?阙@zG@Q?T[@ ףp= @= ףp=?Xq@433333@(\?@Q@Gz?FI@(\@gfffff? ` @@Q?7!"""@p= ף@أp= ?878@{Gz@(\?ަNO@RQ@HzG?^efff@)\(@?*|'}@@p= ףp?͒>@ףp= @zG?v@Gz@PQ?=l@Q@(\?ւ-@\(\@133333?h@333333@p= ף?d[@ ףp= @"\(?q@zG@ףp= ?Z1333@Q@Q?HI@(\@?B_ `@gfffff@Q?Nuwww@> ףp=@Gz?8@Gz@PQ?iO@Gz@PQ??!@> ףp=@PQ?'}@ffffff@PQ?>@(\@PQ?1H@Q@PQ?l@zG@PQ?}+؂-@ ףp= @PQ?$oBDDD@333333@PQ?&Y[@\(\@PQ?poq@Q@zG?&Y[@Gz@d= ףp?$oBDDD@֣p= @?~+؂-@@BzG?l@(\(@(\?2H@SQ@ңp= ?>@zGz@%Q?'}@p= ף@cfffff?@!@@Gz?iO@(\@(\?8@ Q@6 ףp=?Muwww@GzG@Q?B_ `@q= ףp@?HI@@Gz?[1333@(\@V(\?q@Q@UQ?d[@Gz@(\?i@= ףp=@?ւ-@gfffff@; ףp=?=l@(\@zG?v@Q@Q?͒>@p= ף@(\(?*|'}@{Gz@?^efff@RQ@p= ףp?ަNO@)\(@Gz?878@@Q?7!"""@ףp= @](\? ` @Gz@?FI@Q@p= ף?@[(\@HzG?Xq@333333@Q?T[@ ףp= @(\?阙@zG@433333?2-؂@Q@أp= ?azkl@(\@xGz?TUUU@efffff@ Q? >>@? ףp=@(\?nS'}'@Gz@hfffff?ʛ@Q@ףp= ?"O@(\@Gz?~,8@@Gz?(\?(\? ףp= ?(\?(\?gfffff?(\?(\?(\?(\?(\?Q?(\?(\?{Gz?(\?(\?أp= ?(\?(\?433333?(\?(\?(\?(\?(\?Q?l@)\(@HzG?Lo-؂-@RQ@p= ף?&DDDD@{Gz@?Z[@p= ף@\(\?>qq@@Q?M@(\@Gz?I@Q@p= ףp?0` @GzG@?t@q= ףp@*\(?~,8@@Q?$O@(\@zG?ɛ@Q@> ףp=?oS'}'@Gz@? >>@> ףp=@(\?TUUU@ffffff@QQ?bzkl@(\@Z(\?2-؂@Q@Gz?阙@zG@?T[@ ףp= @Q?Xq@433333@@ ףp=?@HzG@(\?FI@Q@Gz? ` @(\@gfffff?7!"""@@Q?878@p= ף@֣p= ?ަNO@zGz@(\?^efff@RQ@FzG?*|'}@(\(@?͒>@@l= ףp?v@֣p= @zG?=l@Gz@LQ?ւ-@Q@(\?h@\(\@433333?d[@433333@p= ף?q@ ףp= @(\(?[1333@zG@ףp= ?HI@Q@Q?B_ `@(\@?Muwww@ffffff@Q?8@> ףp=@Gz?iO@Gz@(\??!@Gz@(\?'}@= ףp=@(\?>@ffffff@(\?2H@(\@(\?l@Q@(\?~+؂-@zG@(\?#oBDDD@ ףp= @(\?&Y[@333333@(\?ooq@\(\@VQ?&Y[@Q@zG?$oBDDD@Gz@v= ףp?~+؂-@ףp= @?l@@KzG?2H@)\(@(\?>@RQ@ۣp= ?'}@{Gz@#Q?@!@p= ף@kfffff?iO@@Gz?8@(\@(\?Nuwww@Q@? ףp=?B_ `@HzG@Q?HI@p= ףp@?\1333@@Gz?q@(\@^(\?d[@Q@QQ?i@Gz@(\?ւ-@= ףp=@?=l@ffffff@? ףp=?w@(\@zG?͒>@p= ף@Q?*|'}@zGz@)\(?^efff@RQ@?ަNO@(\(@q= ףp?878@@Gz?7!"""@ףp= @Q? ` @Gz@Z(\?EI@Q@?@\(\@p= ף?Xq@433333@JzG?T[@ ףp= @Q?阙@zG@(\?2-؂@Q@333333?azkl@(\@ڣp= ?TUUU@ffffff@zGz? >>@> ףp=@Q?oS'}'@Gz@(\?ɛ@Q@efffff?"O@(\@ ףp= ?},8@@Gz?t@p= ףp@Gz?433333?433333? ףp= ?733333?733333?gfffff?633333?633333?(\?633333?633333?Q?133333?133333?{Gz?133333?133333?أp= ?833333?833333?433333?733333?733333?(\?l@)\(@Q?Lo-؂-@RQ@HzG?&DDDD@{Gz@p= ף?Z[@p= ף@?>qq@@\(\?M@(\@Q?I@Q@Gz?0` @GzG@p= ףp?t@p= ףp@?~,8@@*\(?$O@(\@Q?ʛ@Q@zG?oS'}'@Gz@> ףp=? >>@> ףp=@?TUUU@gfffff@(\?bzkl@(\@QQ?2-؂@Q@Z(\?阙@zG@Gz?T[@ ףp= @?Xq@433333@Q?@\(\@@ ףp=?FI@HzG@(\? ` @Q@Gz?7!"""@(\@gfffff?878@@Q?ަNO@p= ף@֣p= ?^efff@zGz@(\?*|'}@RQ@FzG?͒>@(\(@?v@@l= ףp?=l@֣p= @zG?ւ-@Gz@LQ?h@Q@(\?d[@\(\@433333?q@433333@p= ף?Z1333@ ףp= @(\(?HI@zG@ףp= ?B_ `@Q@Q?Nuwww@(\@?8@gfffff@Q?iO@> ףp=@Gz??!@Gz@433333?'}@Gz@433333?>@= ףp=@433333?2H@ffffff@433333?l@(\@433333?~+؂-@Q@433333?$oBDDD@zG@;33333?&Y[@ ףp= @;33333?poq@333333@(\?&Y[@\(\@bQ?$oBDDD@Q@zG?~+؂-@Gz@= ףp?l@أp= @?2H@@QzG?>@*\(@(\?'}@RQ@p= ?@!@|Gz@Q?iO@p= ף@qfffff?8@@Gz?Luwww@(\@(\?B_ `@Q@: ףp=?HI@HzG@Q?\1333@q= ףp@?q@@Gz?d[@(\@Z(\?i@Q@SQ?ւ-@Gz@(\?=l@> ףp=@?w@ffffff@= ףp=?͒>@(\@zG?*|'}@{Gz@Q?^efff@RQ@)\(?ަNO@)\(@?878@@r= ףp?7!"""@ףp= @Gz? ` @Gz@Q?FI@Q@\(\?@\(\@?Xq@333333@p= ף?S[@ ףp= @JzG?阙@zG@Q?2-؂@Q@(\?azkl@(\@233333?TUUU@dfffff@ڣp= ? >>@< ףp=@xGz?nS'}'@Gz@Q?ʛ@Q@(\?"O@(\@ffffff?~,8@@ ףp= ?t@o= ףp@Gz?1` @HzG@Gz?p= ף?p= ף? ףp= ?p= ף?p= ף?gfffff?p= ף?p= ף?(\?p= ף?p= ף?Q?p= ף?p= ף?{Gz?p= ף?p= ף?أp= ?p= ף?p= ף?433333?l@)\(@(\?Lo-؂-@RQ@Q?&DDDD@{Gz@HzG?Z[@p= ף@p= ף??qq@@?M@(\@\(\?I@Q@Q?0` @GzG@Gz?t@p= ףp@p= ףp?},8@@?$O@(\@*\(?ʛ@Q@Q?pS'}'@Gz@zG? >>@> ףp=@> ףp=?TUUU@gfffff@?azkl@(\@(\?2-؂@Q@QQ?阙@zG@Z(\?T[@ ףp= @Gz?Xq@433333@?@\(\@Q?FI@q= ףp@@ ףp=? ` @HzG@(\?7!"""@Q@Gz?878@(\@gfffff?ަNO@@Q?^efff@p= ף@֣p= ?*|'}@zGz@(\?͒>@RQ@FzG?v@(\(@?=l@@l= ףp?ւ-@֣p= @zG?h@Gz@LQ?d[@Q@(\?q@\(\@433333?Z1333@433333@p= ף?HI@ ףp= @(\(?B_ `@zG@ףp= ?Muwww@Q@Q?8@(\@?iO@gfffff@Q?@!@> ףp=@Gz?'}@Gz@p= ף?>@Gz@p= ף?2H@= ףp=@p= ף?l@ffffff@p= ף?~+؂-@(\@p= ף?$oBDDD@Q@p= ף?&Y[@zG@p= ף?poq@ ףp= @533333?&Y[@433333@(\?$oBDDD@\(\@VQ?~+؂-@Q@zG?l@Gz@v= ףp?2H@ףp= @?>@@KzG?'}@)\(@(\?@!@RQ@ۣp= ?iO@{Gz@#Q?8@p= ף@kfffff?Nuwww@@Gz?B_ `@(\@(\?HI@ Q@5 ףp=?[1333@FzG@Q?q@r= ףp@?d[@@Gz?j@(\@V(\?ւ-@Q@UQ?=l@Gz@(\?v@= ףp=@?͒>@gfffff@; ףp=?*|'}@zGz@zG?^efff@RQ@Q?ަNO@(\(@)\(?878@@?7!"""@֣p= @r= ףp? ` @Gz@Gz?FI@Q@Q?@\(\@Z(\?Xq@233333@?T[@ ףp= @p= ף?阙@zG@JzG?2-؂@Q@Q?`zkl@(\@(\?TUUU@ffffff@433333? >>@> ףp=@ڣp= ?nS'}'@Gz@zGz?ɛ@Q@Q?#O@(\@(\?},8@@ffffff?t@p= ףp@ ףp= ?1` @HzG@Gz?I@Q@Gz?'\(?'\(? ףp= ?.\(?.\(?gfffff?-\(?-\(?(\?,\(?,\(?Q?"\(?"\(?{Gz?)\(?)\(?أp= ?l@*\(@433333?Lo-؂-@RQ@(\?&DDDD@{Gz@Q?Z[@p= ף@HzG??qq@@p= ף?M@(\@?I@Q@\(\?1` @HzG@Q?t@p= ףp@Gz?},8@@p= ףp?#O@(\@?ʛ@Q@*\(?pS'}'@Gz@Q? >>@> ףp=@zG?TUUU@gfffff@> ףp=?azkl@(\@?2-؂@Q@(\?阙@zG@QQ?T[@ ףp= @Z(\?Xq@433333@Gz?@\(\@?FI@Q@Q? ` @q= ףp@@ ףp=?7!"""@HzG@(\?878@Q@Gz?ަNO@(\@gfffff?^efff@@Q?*|'}@p= ף@֣p= ?͒>@zGz@(\?v@RQ@FzG?=l@(\(@?ւ-@@l= ףp?h@֣p= @zG?d[@Gz@LQ?q@Q@(\?Z1333@\(\@433333?HI@433333@p= ף?B_ `@ ףp= @(\(?Muwww@zG@ףp= ?8@Q@Q?iO@(\@??!@gfffff@Q?'}@> ףp=@Gz?>@Gz@(\(?1H@Gz@(\(?l@= ףp=@(\(?~+؂-@ffffff@(\(?$oBDDD@(\@(\(?&Y[@Q@(\(?poq@zG@p= ף?&Y[@ ףp= @(33333?$oBDDD@233333@(\?~+؂-@](\@IQ?l@Q@zG?2H@Gz@j= ףp?>@ףp= @ ?'}@@EzG?@!@(\(@(\?iO@TQ@֣p= ?8@{Gz@)Q?Muwww@p= ף@ffffff?B_ `@@Gz?HI@(\@(\?[1333@Q@: ףp=?q@HzG@Q?d[@q= ףp@?i@@Gz?ւ-@(\@Z(\?=l@Q@SQ?w@Gz@(\?͒>@> ףp=@?+|'}@ffffff@? ףp=?^efff@RQ@zG?ަNO@(\(@Q?878@@)\(?7!"""@ףp= @? ` @Gz@r= ףp?FI@Q@Gz?@[(\@Q?Xq@333333@\(\?T[@ ףp= @?阙@zG@p= ף?2-؂@Q@JzG?azkl@(\@Q?TUUU@ffffff@(\? >>@< ףp=@233333?nS'}'@Gz@ڣp= ?ț@Q@yGz?"O@(\@Q?~,8@@(\?t@p= ףp@ffffff?1` @HzG@ ףp= ?I@Q@Gz?M@(\@Gz?ףp= ?ףp= ? ףp= ?ףp= ?ףp= ?gfffff? ףp= ? ףp= ?(\? ףp= ? ףp= ?Q? ףp= ? ףp= ?{Gz?l@)\(@أp= ?Lo-؂-@RQ@433333?&DDDD@{Gz@(\?Z[@p= ף@Q??qq@@HzG?M@(\@p= ף?I@Q@?1` @HzG@\(\?t@q= ףp@Q?},8@@Gz?#O@(\@p= ףp?ɛ@Q@?pS'}'@Gz@*\(? >>@> ףp=@Q?TUUU@gfffff@zG?azkl@(\@> ףp=?2-؂@Q@?阙@zG@(\?T[@ ףp= @QQ?Xq@433333@Z(\?@\(\@Gz?FI@Q@? ` @@Q?7!"""@q= ףp@@ ףp=?878@HzG@(\?ަNO@Q@Gz?^efff@(\@gfffff?*|'}@@Q?͒>@p= ף@֣p= ?v@zGz@(\?=l@RQ@FzG?ւ-@(\(@?h@@l= ףp?d[@֣p= @zG?q@Gz@LQ?[1333@Q@(\?HI@\(\@433333?B_ `@433333@p= ף?Muwww@ ףp= @(\(?8@zG@ףp= ?iO@Q@Q?@!@(\@?'}@gfffff@Q?>@> ףp=@Gz?1H@Gz@ףp= ?l@Gz@ףp= ?~+؂-@= ףp=@ףp= ?$oBDDD@ffffff@ףp= ?&Y[@(\@ףp= ?poq@Q@D\(?&Y[@zG@p= ף?$oBDDD@ ףp= @533333?~+؂-@433333@(\?l@\(\@VQ?2H@Q@zG?>@Gz@v= ףp?'}@ףp= @??!@@KzG?iO@)\(@(\?8@RQ@ۣp= ?Muwww@{Gz@#Q?B_ `@p= ף@kfffff?HI@@Gz?[1333@(\@(\?q@Q@? ףp=?d[@HzG@Q?i@p= ףp@?ւ-@@Gz?=l@(\@^(\?w@Q@QQ?͒>@Gz@(\?+|'}@= ףp=@?^efff@RQ@; ףp=?ަNO@(\(@zG?878@@Q?7!"""@֣p= @)\(? ` @Gz@?EI@Q@r= ףp?@\(\@Gz?Xq@433333@Q?T[@ ףp= @Y(\?阙@zG@?2-؂@Q@p= ף?azkl@(\@JzG?TUUU@ffffff@Q? >>@< ףp=@(\?nS'}'@Gz@233333?ɛ@Q@ڣp= ?"O@(\@zGz?},8@@Q?t@p= ףp@(\?1` @HzG@ffffff?I@Q@ ףp= ?M@(\@Gz?>qq@@Gz?Q?Q? ףp= ?Q?Q?gfffff?Q?Q?(\?Q?Q?Q?l@)\(@{Gz?Mo-؂-@RQ@أp= ?&DDDD@|Gz@433333?Z[@p= ף@(\??qq@@Q?M@(\@HzG?I@Q@p= ף?1` @HzG@?t@q= ףp@\(\?},8@@Q?#O@(\@Gz?ɛ@Q@p= ףp?oS'}'@Gz@? >>@> ףp=@*\(?TUUU@gfffff@Q?bzkl@(\@zG?2-؂@Q@> ףp=?阙@zG@?T[@ ףp= @(\?Xq@433333@QQ?@\(\@Z(\?FI@Q@Gz? ` @Gz@?7!"""@@Q?878@q= ףp@@ ףp=?ަNO@HzG@(\?^efff@Q@Gz?*|'}@(\@gfffff?͒>@@Q?v@p= ף@֣p= ?=l@zGz@(\?ւ-@RQ@FzG?h@(\(@?d[@@l= ףp?q@֣p= @zG?\1333@Gz@LQ?HI@Q@(\?B_ `@\(\@433333?Muwww@433333@p= ף?8@ ףp= @(\(?iO@zG@ףp= ?@!@Q@Q?'}@(\@?>@gfffff@Q?2H@> ףp=@Gz?l@Gz@Q?}+؂-@Gz@Q?$oBDDD@= ףp=@Q?&Y[@ffffff@Q?poq@(\@%ףp= ?&Y[@Q@(\(?$oBDDD@zG@p= ף?~+؂-@ ףp= @C33333?l@433333@(\?2H@[(\@bQ?>@Q@zG?'}@Gz@= ףp?@!@أp= @?iO@@QzG?8@*\(@(\?Muwww@RQ@p= ?B_ `@|Gz@Q?HI@p= ף@qfffff?[1333@@Gz?q@(\@(\?d[@Q@: ףp=?i@HzG@Q?ւ-@q= ףp@?=l@@Gz?w@(\@Z(\?͒>@Q@SQ?+|'}@Gz@(\?^efff@> ףp=@?ަNO@(\(@? ףp=?878@@zG?7!"""@֣p= @Q? ` @Gz@*\(?FI@Q@?@\(\@r= ףp?Xq@333333@Gz?S[@ ףp= @Q?阙@zG@\(\?2-؂@Q@?azkl@(\@p= ף?TUUU@dfffff@JzG? >>@< ףp=@Q?oS'}'@Gz@(\?ț@Q@233333?"O@(\@ڣp= ?|,8@@yGz?t@o= ףp@Q?2` @IzG@(\?I@Q@ffffff?M@(\@ ףp= ?=qq@@Gz?Z[@p= ף@Gz??? ףp= ???gfffff???(\?l@)\(@ Q?Mo-؂-@SQ@|Gz?&DDDD@|Gz@أp= ?Z[@p= ף@333333?>qq@@(\?M@(\@Q?I@Q@HzG?1` @HzG@p= ף?t@q= ףp@?},8@@\(\?#O@(\@Q?ț@Q@Gz?nS'}'@Gz@p= ףp? >>@= ףp=@?TUUU@gfffff@)\(?bzkl@(\@Q?2-؂@Q@zG?阙@zG@> ףp=?T[@ ףp= @?Xq@433333@(\?@\(\@RQ?FI@Q@\(\? ` @Gz@Gz?7!"""@(\@?878@@Q?ަNO@p= ףp@> ףp=?^efff@HzG@(\?*|'}@Q@Gz?͒>@(\@efffff?v@@Q?=l@p= ף@ףp= ?ւ-@{Gz@(\?i@RQ@GzG?d[@)\(@?q@@n= ףp?[1333@ףp= @zG?HI@Gz@NQ?B_ `@Q@(\?Muwww@](\@733333?8@433333@p= ף?iO@ ףp= @.\(?@!@zG@ףp= ?'}@Q@Q?>@(\@?1H@ffffff@Q?l@> ףp=@Gz?}+؂-@Gz@?#oBDDD@Gz@?&Y[@= ףp=@?ooq@ffffff@Q?&Y[@(\@ףp= ?%oBDDD@Q@\(?~+؂-@zG@p= ף?l@ ףp= @633333?2H@433333@(\?>@](\@UQ?'}@Q@zG?@!@Gz@t= ףp?iO@أp= @?8@@JzG?Nuwww@)\(@(\?B_ `@SQ@ڣp= ?HI@{Gz@'Q?[1333@p= ף@ifffff?q@@Gz?d[@(\@(\?i@Q@9 ףp=?ւ-@GzG@Q?=l@q= ףp@?v@@Gz?͒>@(\@Z(\?+|'}@Q@SQ?^efff@Gz@(\?ަNO@)\(@?878@@> ףp=?7!"""@ףp= @zG? ` @Gz@Q?FI@Q@(\(?@\(\@?Xq@633333@p= ףp?S[@ ףp= @Gz?阙@zG@Q?2-؂@Q@](\?bzkl@(\@?TUUU@ffffff@p= ף? >>@> ףp=@HzG?nS'}'@Gz@Q?ʛ@Q@(\?"O@(\@433333?},8@@أp= ?t@p= ףp@|Gz?1` @HzG@Q?I@ Q@(\?M@(\@efffff??qq@@ ףp= ?Z[@p= ף@Gz?&DDDD@{Gz@Gz?Q?Q? ףp= ?Q?Q?ffffff?l@)\(@(\?Lo-؂-@RQ@ Q?&DDDD@|Gz@|Gz?Z[@p= ף@أp= ??qq@@333333?M@(\@(\?I@Q@Q?0` @HzG@HzG?t@q= ףp@p= ף?},8@@?#O@(\@\(\?ɛ@Q@Q?nS'}'@Gz@Gz? >>@= ףp=@q= ףp?TUUU@ffffff@?bzkl@(\@)\(?2-؂@Q@Q?阙@zG@zG?T[@ ףp= @= ףp=?Xq@433333@?@](\@(\?FI@Q@RQ? ` @Gz@\(\?7!"""@ףp= @Gz?878@(\@?ަNO@@Q?^efff@p= ףp@> ףp=?*|'}@HzG@(\?͒>@Q@Gz?v@(\@efffff?=l@@Q?ւ-@p= ף@٣p= ?h@{Gz@(\?d[@RQ@IzG?q@)\(@?Z1333@@r= ףp?HI@ףp= @zG?B_ `@Gz@RQ?Muwww@Q@(\?8@](\@733333?iO@433333@p= ף?@!@ ףp= @.\(?'}@zG@ףp= ?>@Q@Q?1H@(\@?l@gfffff@Q?~+؂-@> ףp=@}Gz?#oBDDD@Gz@Q?&Y[@Gz@Q?poq@= ףp=@ۙ?&Y[@gfffff@Q?#oBDDD@(\@,ףp= ?~+؂-@Q@.\(?l@zG@p= ף?2H@ ףp= @*33333?>@233333@(\?'}@[(\@KQ??!@Q@zG?iO@Gz@l= ףp?8@֣p= @?Muwww@@FzG?B_ `@)\(@(\?HI@QQ@֣p= ?Z1333@{Gz@Q?q@p= ף@gfffff?d[@@Gz?i@(\@(\?ւ-@Q@: ףp=?=l@GzG@Q?v@p= ףp@?͒>@@Gz?*|'}@(\@Z(\?^efff@Q@SQ?ަNO@Gz@(\?878@@?7!"""@ףp= @> ףp=? ` @Gz@zG?FI@Q@Q?@\(\@(\(?Xq@433333@?T[@ ףp= @p= ףp?阙@zG@Gz?2-؂@Q@Q?bzkl@(\@^(\?TUUU@gfffff@? >>@> ףp=@p= ף?nS'}'@Gz@HzG?ʛ@Q@Q?#O@(\@(\?},8@@433333?t@o= ףp@أp= ?1` @HzG@|Gz?I@Q@Q?M@(\@(\?>qq@@efffff?Z[@p= ף@ ףp= ?&DDDD@{Gz@Gz?Lo-؂-@RQ@Gz?{Gz?{Gz? ףp= ?l@)\(@ffffff?Lo-؂-@RQ@(\?&DDDD@{Gz@ Q?Z[@p= ף@|Gz??qq@@أp= ?M@(\@333333?I@Q@(\?0` @HzG@Q?t@p= ףp@HzG?},8@@p= ף?#O@(\@?ɛ@Q@\(\?oS'}'@Gz@Q? >>@= ףp=@Gz?TUUU@ffffff@q= ףp?bzkl@(\@?2-؂@Q@)\(?阙@zG@Q?T[@ ףp= @zG?Xq@433333@= ףp=?@](\@?FI@Q@(\? ` @Gz@RQ?7!"""@ףp= @`(\?878@Q@Gz?ަNO@(\@?^efff@@Q?*|'}@p= ףp@> ףp=?͒>@HzG@(\?v@Q@Gz?=l@(\@efffff?ւ-@@Q?h@p= ף@ףp= ?d[@{Gz@(\?q@RQ@GzG?[1333@)\(@?HI@@n= ףp?B_ `@ףp= @zG?Muwww@Gz@NQ?8@Q@(\?iO@](\@733333?@!@433333@p= ף?'}@ ףp= @.\(?>@zG@ףp= ?1H@Q@Q?l@(\@?}+؂-@ffffff@Q?$oBDDD@> ףp=@Gz?&Y[@Gz@Gz?poq@Gz@FQ?&Y[@= ףp=@?$oBDDD@gfffff@Q?~+؂-@(\@ףp= ?l@Q@\(?2H@zG@p= ף?>@ ףp= @633333?'}@433333@(\?@!@](\@UQ?iO@Q@zG?8@Gz@t= ףp?Nuwww@ףp= @?B_ `@@JzG?HI@)\(@(\?Z1333@SQ@ڣp= ?q@{Gz@'Q?d[@p= ף@ifffff?i@@Gz?ւ-@(\@(\?=l@Q@9 ףp=?w@HzG@Q?͒>@q= ףp@?*|'}@@Gz?^efff@(\@Z(\?ަNO@Q@RQ?878@@(\?7!"""@ףp= @? ` @Gz@> ףp=?FI@Q@zG?@\(\@Q?Xq@433333@(\(?S[@ ףp= @?阙@zG@p= ףp?2-؂@Q@Gz?azkl@(\@Q?TUUU@ffffff@](\? >>@? ףp=@?nS'}'@Gz@p= ף?ɛ@Q@HzG?"O@(\@Q?~,8@@(\?t@p= ףp@433333?1` @HzG@أp= ?I@Q@|Gz?M@(\@Q??qq@@(\?Z[@p= ף@dfffff?&DDDD@{Gz@ ףp= ?Lo-؂-@RQ@Gz?l@)\(@Gz?l@)\(@ ףp= ?Lo-؂-@RQ@ffffff?&DDDD@{Gz@(\?Z[@p= ף@ Q??qq@@|Gz?M@(\@أp= ?I@ Q@333333?0` @HzG@(\?t@p= ףp@Q?},8@@HzG?#O@(\@p= ף?ɛ@Q@?oS'}'@Gz@\(\? >>@> ףp=@Q?TUUU@ffffff@Gz?bzkl@(\@q= ףp?2-؂@Q@?阙@zG@)\(?T[@ ףp= @Q?Xq@333333@zG?@](\@= ףp=?FI@Q@? ` @Gz@(\?7!"""@ףp= @RQ?878@@\(\?ަNO@Q@Gz?^efff@(\@?*|'}@@Q?͒>@p= ףp@> ףp=?v@HzG@(\?=l@Q@Gz?ւ-@(\@efffff?h@@Q?d[@p= ף@٣p= ?q@{Gz@(\?[1333@RQ@IzG?HI@)\(@?B_ `@@r= ףp?Muwww@ףp= @zG?8@Gz@RQ?iO@Q@(\?@!@](\@733333?'}@433333@p= ף?>@ ףp= @.\(?1H@zG@ףp= ?l@Q@Q?}+؂-@(\@?#oBDDD@ffffff@Q?&Y[@> ףp=@}Gz?poq@Gz@Gz?&Y[@Gz@Q?#oBDDD@= ףp=@b?~+؂-@ffffff@Q?l@(\@֣p= ?1H@Q@.\(?>@zG@p= ף?'}@ ףp= @*33333?@!@333333@(\?iO@[(\@KQ?8@Q@zG?Muwww@Gz@l= ףp?B_ `@ףp= @?HI@@FzG?Z1333@)\(@(\?q@QQ@֣p= ?d[@{Gz@Q?h@p= ף@gfffff?ւ-@@Gz?=l@(\@(\?v@Q@: ףp=?͒>@GzG@~Q?*|'}@o= ףp@?^efff@@Gz?ަNO@(\@Z(\?878@Q@RQ?7!"""@ףp= @(\? ` @Gz@?FI@Q@> ףp=?@\(\@zG?Xq@233333@Q?T[@ ףp= @(\(?阙@zG@?2-؂@Q@p= ףp?bzkl@(\@Gz?TUUU@dfffff@Q? >>@> ףp=@](\?oS'}'@Gz@?ʛ@Q@p= ף?"O@(\@HzG?},8@@Q?t@p= ףp@(\?1` @HzG@433333?I@Q@أp= ?M@(\@{Gz?>qq@@Q?Z[@p= ף@(\?&DDDD@zGz@dfffff?Lo-؂-@RQ@ ףp= ?l@)\(@Gz?@@Gz?Lo-؂-@RQ@ ףp= ?&DDDD@{Gz@ffffff?Z[@p= ף@(\?>qq@@ Q?M@(\@|Gz?I@ Q@أp= ?2` @HzG@333333?t@p= ףp@(\?},8@@Q?$O@(\@HzG?ɛ@Q@p= ף?oS'}'@Gz@? >>@> ףp=@\(\?TUUU@ffffff@Q?azkl@(\@Gz?2-؂@Q@q= ףp?阙@zG@?T[@ ףp= @)\(?Xq@333333@Q?@\(\@zG?FI@Q@= ףp=? ` @Gz@?7!"""@أp= @(\?878@@RQ?ަNO@Gz@`(\?^efff@Q@Gz?*|'}@(\@?͒>@@Q?v@p= ףp@> ףp=?=l@HzG@(\?ւ-@Q@Gz?h@(\@efffff?d[@@Q?q@p= ף@ףp= ?[1333@{Gz@(\?HI@RQ@GzG?B_ `@)\(@?Nuwww@@n= ףp?8@ףp= @zG?iO@Gz@NQ?@!@Q@(\?'}@](\@733333?>@433333@p= ף?2H@ ףp= @.\(?l@zG@ףp= ?}+؂-@Q@Q?#oBDDD@(\@?&Y[@gfffff@Q?poq@= ףp=@Q?&Y[@Gz@}Gz?$oBDDD@Gz@DQ?~+؂-@= ףp=@?l@hfffff@Q?1H@(\@Lףp= ?>@Q@\(?'}@zG@p= ף?@!@ ףp= @633333?iO@333333@(\?8@](\@UQ?Nuwww@Q@zG?B_ `@Gz@t= ףp?HI@ףp= @?[1333@@JzG?q@)\(@(\?d[@SQ@ڣp= ?i@{Gz@'Q?ւ-@p= ף@ifffff?=l@@Gz?v@(\@(\?͒>@Q@9 ףp=?*|'}@HzG@Q?^efff@r= ףp@?ߦNO@@Gz?878@(\@\(\?7!"""@ףp= @RQ? ` @Gz@(\?FI@Q@?@\(\@> ףp=?Xq@433333@zG?S[@ ףp= @Q?阙@zG@(\(?2-؂@Q@?bzkl@(\@p= ףp?TUUU@ffffff@Gz? >>@= ףp=@Q?nS'}'@Gz@](\?ʛ@Q@?"O@(\@p= ף?},8@@HzG?t@p= ףp@Q?1` @IzG@(\?I@Q@433333?M@(\@أp= ?>qq@@{Gz?Z[@p= ף@Q?&DDDD@|Gz@(\?Lo-؂-@RQ@dfffff?l@)\(@ ףp= ?@@Gz?Q?Q?Gz?&DDDD@{Gz@ ףp= ?Z[@p= ף@ffffff?>qq@@(\?M@(\@ Q?I@ Q@|Gz?2` @IzG@أp= ?t@r= ףp@333333?},8@@(\?#O@(\@Q?ʛ@Q@HzG?oS'}'@Gz@p= ף? >>@> ףp=@?TUUU@ffffff@\(\?azkl@(\@Q?2-؂@Q@Gz?阙@zG@p= ףp?T[@ ףp= @?Xq@433333@)\(?@\(\@Q?FI@Q@zG? ` @Gz@= ףp=?7!"""@أp= @?878@@(\?ަNO@)\(@PQ?^efff@Gz@\(\?*|'}@Q@Gz?͒>@(\@?v@@Q?=l@p= ףp@> ףp=?ւ-@GzG@(\?i@Q@Gz?d[@(\@efffff?q@@Q?[1333@p= ף@٣p= ?HI@{Gz@(\?B_ `@RQ@IzG?Muwww@)\(@?8@@r= ףp?iO@ףp= @zG??!@Gz@RQ?'}@Q@(\?>@](\@733333?2H@433333@p= ף?l@ ףp= @.\(?}+؂-@zG@ףp= ?#oBDDD@Q@Q?&Y[@(\@?ooq@ffffff@?&Y[@> ףp=@?$oBDDD@Gz@}Gz?}+؂-@Gz@Q?l@< ףp=@ۙ?1H@hfffff@Q?>@(\@,ףp= ?'}@Q@[(?>!@zG@p= ף?iO@ ףp= @*33333?8@333333@(\?Nuwww@\(\@KQ?B_ `@Q@zG?HI@Gz@l= ףp?Z1333@ףp= @?q@@FzG?d[@)\(@(\?h@QQ@֣p= ?ւ-@{Gz@Q?=l@p= ף@gfffff?v@@Gz?͒>@(\@(\?*|'}@Q@1 ףp=?^efff@FzG@Q?ަNO@q= ףp@?678@@Gz?7!"""@(\@\(\? ` @Gz@RQ?FI@Q@(\?@\(\@?Xq@233333@> ףp=?T[@ ףp= @zG?阙@zG@Q?2-؂@Q@(\(?bzkl@(\@?TUUU@ffffff@p= ףp? >>@> ףp=@Gz?nS'}'@Gz@Q?ʛ@Q@](\?#O@(\@?},8@@p= ף?t@n= ףp@HzG?1` @HzG@Q?I@Q@(\?M@(\@333333?=qq@@أp= ?Z[@p= ף@{Gz?&DDDD@{Gz@Q?Mo-؂-@SQ@(\?l@)\(@dfffff?@@ףp= ???Gz?@?@?Gz?Z[@p= ף@ ףp= ?>qq@@ffffff?M@(\@(\?I@Q@ Q?2` @IzG@|Gz?t@r= ףp@أp= ?~,8@@333333?#O@(\@(\?ɛ@Q@Q?pS'}'@Gz@HzG? >>@> ףp=@p= ף?TUUU@gfffff@?azkl@(\@\(\?2-؂@Q@Q?阙@zG@Gz?T[@ ףp= @p= ףp?Xq@433333@?@\(\@)\(?FI@Q@Q? ` @Gz@zG?7!"""@أp= @= ףp=?878@@?ަNO@*\(@(\?^efff@> ףp=@RQ?*|'}@Gz@`(\?͒>@Q@Gz?v@(\@?=l@@Q?ւ-@p= ףp@> ףp=?h@GzG@(\?d[@Q@Gz?q@(\@efffff?[1333@@Q?HI@p= ף@ףp= ?B_ `@{Gz@(\?Muwww@RQ@GzG?8@)\(@?iO@@n= ףp??!@ףp= @zG?'}@Gz@NQ?>@Q@(\?2H@](\@733333?l@333333@p= ף?~+؂-@ ףp= @.\(?#oBDDD@zG@ףp= ?&Y[@Q@Q?poq@(\@Q?&Y[@ffffff@Q?#oBDDD@= ףp=@Q?~+؂-@Gz@Gz?l@Gz@<Q?2H@? ףp=@?>@ffffff@R?'}@(\@ףp= ??!@Q@N\(?iO@zG@p= ף?8@ ףp= @633333?Nuwww@333333@(\?B_ `@\(\@UQ?HI@Q@zG?[1333@Gz@t= ףp?q@ףp= @?d[@@JzG?i@)\(@(\?ւ-@SQ@ڣp= ?=l@|Gz@'Q?v@p= ף@ifffff?͒>@@Gz?*|'}@(\@(\?^efff@Q@B ףp=?ަNO@IzG@Q?878@q= ףp@?7!"""@@Gz? ` @Gz@\(\?FI@Q@RQ?@\(\@(\?Xq@433333@?T[@ ףp= @> ףp=?阙@zG@zG?2-؂@Q@Q?bzkl@(\@(\(?TUUU@ffffff@? >>@? ףp=@p= ףp?nS'}'@Gz@Gz?ɛ@Q@Q?"O@(\@](\?~,8@@?t@p= ףp@p= ף?0` @HzG@HzG?I@Q@Q?M@(\@(\?>qq@@333333?Z[@p= ף@գp= ?&DDDD@yGz@{Gz?Lo-؂-@RQ@Q?l@)\(@(\?@@lfffff?dQ?dQ? ףp= ?\R?\R?Gz?Q?Q?Gz??qq@@ ףp= ?M@(\@ffffff?I@Q@(\?0` @HzG@ Q?t@r= ףp@|Gz?~,8@@أp= ?$O@(\@333333?ɛ@Q@(\?pS'}'@Gz@Q? >>@> ףp=@HzG?TUUU@gfffff@p= ף?azkl@(\@?2-؂@Q@\(\?阙@zG@Q?T[@ ףp= @Gz?Xq@433333@p= ףp?@\(\@?FI@Q@)\(? ` @Gz@Q?7!"""@ףp= @zG?878@@= ףp=?ަNO@*\(@?^efff@SQ@(\?*|'}@> ףp=@PQ?͒>@Gz@\(\?w@Q@Gz?=l@(\@?ւ-@@Q?i@p= ףp@> ףp=?d[@GzG@(\?q@Q@Gz?[1333@(\@efffff?HI@@Q?B_ `@p= ף@٣p= ?Muwww@{Gz@(\?8@RQ@IzG?iO@)\(@??!@@r= ףp?'}@ףp= @zG?>@Gz@RQ?1H@Q@(\?l@](\@733333?~+؂-@333333@p= ף?$oBDDD@ ףp= @.\(?&Y[@zG@ףp= ?poq@Q@ףp= ?&Y[@(\@ףp= ?$oBDDD@gfffff@<ףp= ?~+؂-@? ףp=@ףp= ?l@Gz@Gz?2H@Gz@Q?>@> ףp=@c?'}@efffff@Q??!@(\@֣p= ?iO@Q@.\(?8@zG@p= ף?Muwww@ ףp= @*33333?B_ `@233333@(\?HI@\(\@KQ?Z1333@Q@zG?q@Gz@l= ףp?d[@ףp= @?h@@FzG?ւ-@)\(@(\?=l@QQ@֣p= ?v@zGz@Q?͒>@p= ף@gfffff?*|'}@@Gz?^efff@(\@(\?ަNO@Q@: ףp=?878@GzG@~Q?7!"""@o= ףp@? ` @@Gz?FI@Q@\(\?@\(\@RQ?Xq@233333@(\?T[@ ףp= @?阙@zG@> ףp=?2-؂@Q@zG?azkl@(\@Q?TUUU@gfffff@(\(? >>@> ףp=@?oS'}'@Gz@p= ףp?ʛ@Q@Gz?"O@(\@Q?},8@@](\?t@p= ףp@?1` @HzG@p= ף?I@Q@HzG?M@(\@Q?>qq@@(\?Z[@p= ף@333333?&DDDD@yGz@ۣp= ?Mo-؂-@SQ@{Gz?l@)\(@Q?@@(\?[ףp= ?[ףp= ?dfffff?ףp= ?ףp= ?ףp= ?&ףp= ?&ףp= ?Gz?֣p= ?֣p= ?Gz?M@(\@ ףp= ?I@Q@ffffff?1` @HzG@(\?t@q= ףp@ Q?~,8@@|Gz?$O@(\@أp= ?ʛ@Q@433333?pS'}'@Gz@(\? >>@> ףp=@Q?TUUU@gfffff@HzG?azkl@(\@p= ף?2-؂@Q@?阙@zG@\(\?T[@ ףp= @Q?Xq@433333@Gz?@\(\@p= ףp?FI@Q@? ` @Gz@)\(?7!"""@ףp= @Q?878@@zG?ަNO@*\(@= ףp=?^efff@SQ@?*|'}@ffffff@(\?͒>@> ףp=@RQ?v@Gz@`(\?=l@Q@Gz?ւ-@(\@?i@@Q?d[@p= ףp@> ףp=?q@GzG@(\?[1333@Q@Gz?HI@(\@efffff?B_ `@@Q?Muwww@p= ף@ףp= ?8@{Gz@(\?iO@RQ@GzG?@!@)\(@?'}@@n= ףp?>@ףp= @zG?2H@Gz@NQ?l@Q@(\?~+؂-@\(\@733333?$oBDDD@333333@p= ף?&Y[@ ףp= @.\(?ooq@zG@\(?&Y[@Q@\(?$oBDDD@(\@>\(?~+؂-@gfffff@\(?l@< ףp=@>\(?2H@Gz@}Gz?>@Gz@DQ?'}@< ףp=@?@!@hfffff@Q?iO@(\@Mףp= ?8@Q@\(?Muwww@zG@p= ף?B_ `@ ףp= @633333?HI@433333@(\?[1333@\(\@UQ?q@Q@zG?d[@Gz@t= ףp?i@ףp= @?ւ-@@JzG?=l@)\(@(\?v@RQ@ڣp= ?͒>@|Gz@'Q?+|'}@p= ף@ifffff?^efff@@Gz?ަNO@(\@(\?978@ Q@9 ףp=?7!"""@GzG@Q? ` @r= ףp@?FI@Q@Gz?@\(\@\(\?Xq@433333@RQ?S[@ ףp= @(\?阙@zG@?2-؂@Q@> ףp=?bzkl@(\@zG?TUUU@efffff@Q? >>@= ףp=@(\(?nS'}'@Gz@?ʛ@Q@p= ףp?"O@(\@Gz?},8@@Q?t@p= ףp@](\?2` @JzG@?I@Q@p= ף?M@(\@EzG?>qq@@Q?Z[@p= ף@(\?&DDDD@zGz@333333?Lo-؂-@RQ@գp= ?l@'\(@|Gz?@@"Q?[(?[(?(\?R\(?R\(?jfffff?[(?[(? ףp= ?\(?\(?Gz?[(?[(?Gz?I@Q@ ףp= ?1` @GzG@ffffff?t@q= ףp@(\?},8@@ Q?$O@(\@|Gz?ʛ@Q@أp= ?oS'}'@Gz@433333? >>@> ףp=@(\?TUUU@ffffff@Q?azkl@(\@HzG?2-؂@Q@p= ף?阙@zG@?T[@ ףp= @\(\?Xq@333333@Q?@\(\@Gz?EI@Q@p= ףp? ` @Gz@?7!"""@أp= @*\(?878@@Q?ަNO@*\(@zG?^efff@RQ@> ףp=?*|'}@{Gz@?͒>@ffffff@(\?v@> ףp=@QQ?=l@Gz@^(\?ւ-@Q@Gz?h@(\@?d[@@Q?q@q= ףp@B ףp=?[1333@HzG@(\?HI@Q@Gz?B_ `@(\@ifffff?Muwww@@!Q?8@p= ף@أp= ?iO@{Gz@(\??!@RQ@HzG?'}@)\(@?>@@p= ףp?2H@ףp= @zG?l@Gz@PQ?~+؂-@Q@(\?#oBDDD@\(\@/33333?&Y[@333333@p= ף?ooq@ ףp= @p= ף?&Y[@zG@p= ף?$oBDDD@Q@p= ף?~+؂-@(\@p= ף?l@hfffff@p= ף?1H@< ףp=@p= ף?>@Gz@Gz?'}@Gz@Q??!@< ףp=@?iO@hfffff@Q?8@(\@:ףp= ?Nuwww@Q@\(?B_ `@zG@p= ף?HI@ ףp= @033333?[1333@333333@(\?q@\(\@PQ?d[@Q@zG?i@Gz@p= ףp?ւ-@ףp= @?=l@@HzG?v@)\(@(\?͒>@RQ@أp= ?*|'}@{Gz@ Q?^efff@p= ף@hfffff?ަNO@@Gz?878@(\@(\?7!"""@ Q@8 ףp=? ` @FzG@Q?FI@p= ףp@?@\(\@Gz?Xq@433333@`(\?T[@ ףp= @PQ?阙@zG@(\?2-؂@Q@?bzkl@(\@@ ףp=?TUUU@ffffff@zG? >>@> ףp=@Q?oS'}'@Gz@(\(?ɛ@Q@?#O@(\@p= ףp?},8@@Gz?t@r= ףp@Q?1` @HzG@Z(\?I@Q@?M@(\@p= ף??qq@@HzG?Z[@p= ף@Q?&DDDD@zGz@(\?Lo-؂-@RQ@133333?l@*\(@أp= ?@@yGz?p= ף?p= ף?!Q?p= ף?p= ף?(\?p= ף?p= ף?hfffff?|p= ף?|p= ף?ףp= ?p= ף?p= ף?Gz?\p= ף?\p= ף?Gz?1` @GzG@ ףp= ?t@q= ףp@ffffff?},8@@(\?#O@(\@ Q?ʛ@Q@|Gz?pS'}'@Gz@أp= ? >>@> ףp=@433333?TUUU@ffffff@(\?bzkl@(\@Q?2-؂@Q@HzG?阙@zG@p= ף?T[@ ףp= @?Xq@333333@\(\?@\(\@Q?EI@Q@Gz? ` @Gz@p= ףp?7!"""@ףp= @?878@@*\(?ަNO@*\(@Q?^efff@RQ@zG?*|'}@{Gz@= ףp=?͒>@(\@?v@ffffff@(\?=l@> ףp=@QQ?ւ-@Gz@^(\?h@Q@Gz?d[@(\@?q@@Q?[1333@q= ףp@> ףp=?HI@HzG@(\?B_ `@Q@Gz?Muwww@(\@gfffff?8@@Q?iO@p= ף@أp= ??!@{Gz@(\?'}@RQ@HzG?>@)\(@?2H@@p= ףp?l@ףp= @zG?~+؂-@Gz@PQ?$oBDDD@Q@(\?&Y[@\(\@133333?poq@333333@733333?&Y[@ ףp= @733333?$oBDDD@zG@733333?~+؂-@Q@733333?l@(\@033333?3H@gfffff@033333?>@= ףp=@033333?'}@Gz@Gz??!@Gz@Q?iO@= ףp=@?8@gfffff@Q?Nuwww@(\@ ףp= ?B_ `@Q@ \(?HI@zG@p= ף?\1333@ ףp= @"33333?q@233333@(\?d[@](\@CQ?i@Q@zG?ւ-@Gz@d= ףp?=l@֣p= @?v@@BzG?͒>@(\(@(\?*|'}@RQ@ңp= ?^efff@zGz@%Q?ަNO@p= ף@cfffff?878@@Gz?7!"""@(\@(\? ` @ Q@6 ףp=?FI@FzG@Q?@[(\@?Xq@533333@Gz?S[@ ףp= @`(\?阙@zG@PQ?2-؂@Q@(\?bzkl@(\@?TUUU@efffff@@ ףp=? >>@? ףp=@zG?nS'}'@Gz@Q?ʛ@Q@(\(?#O@(\@?},8@@p= ףp?t@q= ףp@Gz?0` @GzG@Q?I@Q@Z(\?M@(\@?>qq@@p= ף?Z[@p= ף@HzG?&DDDD@{Gz@Q?Mo-؂-@SQ@(\?l@)\(@833333?@@ԣp= ?_33333?_33333?~Gz?<33333?<33333?Q?33333?33333?(\?R33333?R33333?`fffff?(33333?(33333?ףp= ?<33333?<33333?Gz??33333??33333?Gz?t@q= ףp@ ףp= ?},8@@ffffff?#O@(\@(\?ɛ@Q@ Q?pS'}'@Gz@|Gz? >>@? ףp=@أp= ?TUUU@gfffff@433333?bzkl@(\@(\?2-؂@Q@Q?阙@zG@HzG?T[@ ףp= @p= ף?Xq@333333@?@\(\@\(\?FI@Q@Q? ` @Gz@Gz?7!"""@ףp= @p= ףp?878@@?ަNO@*\(@*\(?^efff@RQ@Q?+|'}@|Gz@zG?͒>@p= ף@= ףp=?v@(\@?=l@ffffff@(\?ւ-@= ףp=@QQ?i@Gz@^(\?d[@Q@Gz?q@(\@?[1333@@Q?HI@q= ףp@B ףp=?B_ `@HzG@(\?Muwww@ Q@Gz?8@(\@ifffff?iO@@!Q??!@p= ף@أp= ?'}@{Gz@(\?>@RQ@HzG?2H@)\(@?l@@p= ףp?~+؂-@ףp= @zG?$oBDDD@Gz@PQ?&Y[@Q@(\?ooq@\(\@(\?&Y[@333333@(\?$oBDDD@ ףp= @(\?~+؂-@zG@(\?l@Q@(\?2H@(\@(\?>@ffffff@(\?'}@> ףp=@(\??!@Gz@Gz?iO@Gz@Q?8@> ףp=@?Nuwww@ffffff@R?B_ `@(\@ףp= ?HI@Q@@\(?Z1333@zG@p= ף?q@ ףp= @033333?d[@333333@(\?h@\(\@PQ?ւ-@Q@zG?=l@Gz@p= ףp?v@ףp= @?͒>@@HzG?*|'}@)\(@(\?^efff@RQ@أp= ?ަNO@{Gz@ Q?878@p= ף@hfffff?7!"""@@Gz? ` @(\@(\?FI@Q@B ףp=?@HzG@Q?Xq@433333@?T[@ ףp= @Gz?阙@zG@`(\?2-؂@Q@PQ?bzkl@(\@(\?TUUU@ffffff@? >>@> ףp=@@ ףp=?oS'}'@Gz@zG?ɛ@Q@Q?"O@(\@(\(?},8@@?t@p= ףp@p= ףp?1` @HzG@Gz?I@ Q@Q?M@(\@^(\?>qq@@?Z[@p= ף@p= ף?&DDDD@|Gz@HzG?Lo-؂-@RQ@Q?l@(\(@(\?@@233333?(\?(\?ڣp= ?(\?(\?yGz?(\?(\?Q?(\?(\?(\?(\?(\?gfffff?(\?(\?ףp= ?(\?(\?Gz?(\?(\?Gz?},8@@ ףp= ?#O@(\@ffffff?ɛ@Q@(\?oS'}'@Gz@ Q? >>@? ףp=@|Gz?TUUU@gfffff@أp= ?bzkl@(\@433333?2-؂@Q@(\?阙@zG@Q?T[@ ףp= @HzG?Xq@333333@p= ף?@\(\@?FI@Q@\(\? ` @Gz@Q?7!"""@ףp= @Gz?878@@p= ףp?ަNO@)\(@?^efff@RQ@*\(?+|'}@|Gz@Q?͒>@p= ף@zG?v@Q@= ףp=?=l@(\@?ւ-@gfffff@(\?i@= ףp=@QQ?d[@Gz@^(\?q@Q@Gz?[1333@(\@?HI@@Q?B_ `@q= ףp@> ףp=?Muwww@HzG@(\?8@Q@Gz?iO@(\@gfffff?@!@@Q?'}@p= ף@أp= ?>@{Gz@(\?2H@RQ@HzG?l@)\(@?~+؂-@@p= ףp?$oBDDD@ףp= @zG?&Y[@Gz@PQ?poq@Q@WQ?&Y[@](\@WQ?$oBDDD@433333@VQ?~+؂-@ ףp= @VQ?l@zG@VQ?2H@Q@VQ?>@(\@PQ?'}@gfffff@PQ??!@= ףp=@PQ?iO@Gz@Gz?8@Gz@Q?Nuwww@= ףp=@?B_ `@gfffff@Q?HI@(\@ ףp= ?[1333@Q@ \(?q@zG@p= ף?d[@ ףp= @>33333?i@433333@(\?ւ-@[(\@]Q?=l@Q@zG?v@Gz@|= ףp?͒>@أp= @?*|'}@@NzG?^efff@*\(@(\?ަNO@RQ@ޣp= ?878@|Gz@Q?7!"""@p= ף@mfffff? ` @@Gz?EI@(\@(\?@ Q@@ ףp=?Xq@533333@Q?S[@ ףp= @?阙@zG@Gz?2-؂@Q@`(\?bzkl@(\@PQ?TUUU@efffff@(\? >>@? ףp=@?nS'}'@Gz@@ ףp=?ʛ@Q@zG?"O@(\@Q?~,8@@(\(?t@q= ףp@?1` @HzG@p= ףp?I@Q@Gz?M@(\@Q?>qq@@Z(\?Z[@p= ף@?&DDDD@{Gz@p= ף?Lo-؂-@QQ@HzG?l@)\(@Q?@@(\?`Q?`Q?433333?FQ?FQ?p= ?MQ?MQ?~Gz?ZQ?ZQ?Q?-Q?-Q?(\?mQ?mQ?afffff?AQ?AQ?ףp= ?ZQ?ZQ?Gz?UQ?UQ?Gz?#O@(\@ ףp= ?ɛ@Q@ffffff?oS'}'@Gz@(\? >>@= ףp=@ Q?TUUU@gfffff@|Gz?bzkl@(\@أp= ?2-؂@Q@333333?阙@zG@(\?T[@ ףp= @Q?Xq@333333@HzG?@\(\@p= ף?FI@Q@? ` @Gz@\(\?7!"""@ףp= @Q?878@@Gz?ަNO@)\(@p= ףp?^efff@RQ@?+|'}@|Gz@*\(?͒>@p= ף@Q?w@@zG?=l@Q@= ףp=?ւ-@(\@?h@gfffff@(\?d[@= ףp=@QQ?q@Gz@^(\?[1333@Q@Gz?HI@(\@?B_ `@@Q?Muwww@q= ףp@B ףp=?8@IzG@(\?iO@Q@Gz??!@(\@ifffff?'}@@!Q?>@p= ף@أp= ?1H@{Gz@(\?l@RQ@HzG?~+؂-@)\(@?$oBDDD@@p= ףp?&Y[@ףp= @zG?poq@Gz@zG?&Y[@Q@zG?$oBDDD@\(\@zG?~+؂-@333333@zG?l@ ףp= @zG?2H@zG@zG?>@Q@zG?'}@(\@zG?@!@hfffff@zG?iO@< ףp=@zG?8@Gz@Gz?Nuwww@Gz@Q?B_ `@< ףp=@?HI@hfffff@Q?Z1333@(\@:ףp= ?q@Q@\(?d[@zG@p= ף?i@ ףp= @033333?ւ-@333333@(\?=l@\(\@PQ?w@Q@zG?͒>@Gz@p= ףp?*|'}@ףp= @?^efff@@HzG?ަNO@)\(@(\?878@RQ@أp= ?7!"""@{Gz@ Q? ` @p= ף@hfffff?EI@@Gz?@(\@(\?Xq@Q@@ ףp=?T[@ ףp= @Q?阙@zG@?2-؂@Q@Gz?bzkl@(\@`(\?TUUU@ffffff@PQ? >>@> ףp=@(\?oS'}'@Gz@?ɛ@Q@@ ףp=?"O@(\@zG?},8@@Q?t@p= ףp@(\(?1` @HzG@?I@Q@p= ףp?M@(\@Gz??qq@@Q?Z[@p= ף@^(\?&DDDD@zGz@?Lo-؂-@RQ@p= ף?l@*\(@HzG?@@Q?zG?zG?(\?zG?zG??33333?zG?zG?֣p= ?zG?zG?vGz?{G?{G? Q?zG?zG?(\?zG?zG?hfffff?zG?zG?ףp= ?{G?{G?Gz?zG?zG?Gz?ɛ@Q@ ףp= ?oS'}'@Gz@ffffff? >>@= ףp=@(\?TUUU@gfffff@ Q?bzkl@(\@|Gz?2-؂@Q@أp= ?阙@zG@333333?T[@ ףp= @(\?Xq@433333@Q?@\(\@HzG?FI@Q@p= ף? ` @Gz@?7!"""@ףp= @\(\?878@@Q?ަNO@)\(@Gz?^efff@RQ@p= ףp?*|'}@{Gz@?͒>@p= ף@*\(?w@@Q?=l@zG@zG?ւ-@Q@= ףp=?h@(\@?d[@gfffff@(\?q@= ףp=@QQ?[1333@Gz@^(\?HI@Q@Gz?B_ `@(\@?Muwww@@Q?8@q= ףp@> ףp=?iO@GzG@(\??!@Q@Gz?'}@(\@gfffff?>@@Q?2H@p= ף@أp= ?l@{Gz@(\?~+؂-@RQ@HzG?$oBDDD@)\(@?&Y[@@p= ףp?poq@ףp= @p= ףp?&Y[@Gz@p= ףp?$oBDDD@Q@v= ףp?~+؂-@](\@v= ףp?l@433333@v= ףp?2H@ ףp= @v= ףp?>@zG@v= ףp?'}@Q@v= ףp?@!@(\@p= ףp?iO@gfffff@p= ףp?8@= ףp=@p= ףp?Muwww@Gz@Gz?B_ `@Gz@Q?HI@= ףp=@?Z1333@gfffff@Q?q@(\@ ףp= ?d[@Q@ \(?i@zG@p= ף?ւ-@ ףp= @"33333?=l@233333@(\?w@](\@CQ?͒>@Q@zG?*|'}@Gz@d= ףp?^efff@֣p= @?ަNO@@BzG?878@(\(@(\?7!"""@SQ@ңp= ? ` @zGz@%Q?FI@p= ף@cfffff?@@Gz?Xq@(\@(\?S[@ ףp= @@ ףp=?阙@zG@Q?2-؂@Q@?bzkl@(\@Gz?TUUU@efffff@`(\? >>@? ףp=@PQ?nS'}'@Gz@(\?ʛ@Q@?"O@(\@@ ףp=?~,8@@zG?t@o= ףp@Q?1` @IzG@(\(?I@Q@?M@(\@p= ףp?>qq@@Gz?Z[@p= ף@Q?&DDDD@{Gz@Z(\?Mo-؂-@SQ@?l@)\(@p= ף?@@BzG?e= ףp?e= ףp?Q?= ףp?= ףp?(\?q= ףp?q= ףp?433333?b= ףp?b= ףp?֣p= ?= ףp?= ףp?Gz?t= ףp?t= ףp?Q?b= ףp?b= ףp?(\?= ףp?= ףp?`fffff?t= ףp?t= ףp?ףp= ?t= ףp?t= ףp?Gz?= ףp?= ףp?Gz?oS'}'@Gz@ ףp= ? >>@= ףp=@ffffff?TUUU@gfffff@(\?azkl@(\@ Q? 2-؂@Q@|Gz?阙@zG@ףp= ?T[@ ףp= @333333?Xq@433333@(\?@\(\@Q?FI@Q@HzG? ` @Gz@p= ף?7!"""@ףp= @?878@@\(\?ަNO@)\(@Q?^efff@RQ@Gz?*|'}@{Gz@p= ףp?͒>@p= ף@?w@@*\(?=l@(\@Q?ւ-@zG@zG?i@Q@= ףp=?d[@(\@?q@gfffff@(\?[1333@= ףp=@QQ?HI@Gz@^(\?B_ `@Q@Gz?Muwww@(\@?8@@Q?iO@q= ףp@B ףp=??!@IzG@(\?'}@Q@Gz?>@(\@ifffff?1H@@!Q?l@p= ף@أp= ?~+؂-@{Gz@(\?$oBDDD@RQ@HzG?&Y[@)\(@?poq@@?&Y[@ףp= @?$oBDDD@Gz@?~+؂-@Q@?l@\(\@?3H@333333@?>@ ףp= @?'}@zG@?@!@Q@?iO@(\@?8@ffffff@?Muwww@> ףp=@?B_ `@Gz@Gz?HI@Gz@Q?Z1333@> ףp=@?q@ffffff@R?d[@(\@ףp= ?i@Q@@\(?ւ-@zG@p= ף?=l@ ףp= @033333?v@333333@(\?͒>@\(\@PQ?*|'}@Q@zG?^efff@Gz@p= ףp?ަNO@ףp= @?878@@HzG?7!"""@)\(@(\? ` @RQ@أp= ?EI@{Gz@ Q?@p= ף@hfffff?Xq@@Gz?S[@(\@(\?阙@zG@@ ףp=?2-؂@Q@Q?bzkl@(\@?TUUU@ffffff@Gz? >>@> ףp=@`(\?oS'}'@Gz@PQ?ɛ@Q@(\?"O@(\@?},8@@@ ףp=?t@p= ףp@zG?1` @HzG@Q?I@Q@(\(?M@(\@?>qq@@p= ףp?Z[@p= ף@Gz?&DDDD@|Gz@Q?Lo-؂-@RQ@^(\?l@(\(@?@@p= ף???HzG???Q???(\???033333? ? ?أp= ???xGz??? Q???(\???hfffff???ףp= ???Gz???Gz? >>@= ףp=@ ףp= ?TUUU@gfffff@ffffff?azkl@(\@(\?2-؂@Q@ Q?阙@zG@|Gz?U[@ ףp= @ףp= ?Xq@433333@333333?@\(\@(\?FI@Q@Q? ` @Gz@HzG?7!"""@ףp= @p= ף?878@@?ަNO@)\(@\(\?^efff@RQ@Q?*|'}@{Gz@Gz?͒>@p= ף@p= ףp?v@@?=l@(\@)\(?ւ-@ ףp= @Q?h@zG@zG?d[@Q@= ףp=?q@(\@?[1333@gfffff@(\?HI@= ףp=@QQ?B_ `@Gz@^(\?Muwww@Q@Gz?8@(\@?iO@@Q??!@q= ףp@> ףp=?'}@GzG@(\?>@Q@Gz?2H@(\@gfffff?l@@Q?~+؂-@p= ף@أp= ?$oBDDD@{Gz@(\?&Y[@RQ@HzG?poq@)\(@HzG?&Y[@@HzG?$oBDDD@ףp= @HzG?~+؂-@Gz@HzG?l@Q@KzG?2H@](\@KzG?>@433333@KzG?'}@ ףp= @KzG?@!@zG@KzG?iO@Q@KzG?8@(\@HzG?Nuwww@gfffff@HzG?B_ `@= ףp=@HzG?HI@Gz@Gz?Z1333@Gz@Q?q@= ףp=@?d[@gfffff@Q?h@(\@ ףp= ?ւ-@Q@ \(?=l@zG@p= ף?v@ ףp= @>33333?͒>@433333@(\?*|'}@[(\@]Q?^efff@Q@zG?ަNO@Gz@|= ףp?878@أp= @?7!"""@@NzG? ` @*\(@(\?EI@QQ@ޣp= ?@|Gz@Q?Xq@p= ף@mfffff?T[@@Gz?阙@zG@(\?2-؂@Q@@ ףp=?bzkl@(\@Q?TUUU@efffff@? >>@? ףp=@Gz?nS'}'@Gz@`(\?ʛ@Q@PQ?"O@(\@(\?~,8@@?t@o= ףp@@ ףp=?1` @IzG@zG?I@Q@Q?M@(\@(\(?>qq@@?Z[@p= ף@p= ףp?&DDDD@{Gz@Gz?Lo-؂-@QQ@Q?l@)\(@Z(\?@@?EzG?EzG?p= ף?VzG?VzG?GzG?@zG?@zG?Q?MzG?MzG?(\?EzG?EzG?633333?>zG?>zG?ޣp= ?NzG?NzG?Gz?FzG?FzG?Q??zG??zG?(\?MzG?MzG?`fffff?FzG?FzG?ףp= ?FzG?FzG?Gz?NzG?NzG?Gz?TUUU@gfffff@ ףp= ?azkl@(\@ffffff?2-؂@Q@(\?阙@zG@ Q?U[@ ףp= @|Gz?Xq@533333@ףp= ?@](\@333333?FI@Q@(\? ` @Gz@Q?7!"""@ףp= @HzG?878@@p= ף?ަNO@)\(@?^efff@RQ@\(\?*|'}@{Gz@Q?͒>@p= ף@Gz?v@@p= ףp?=l@(\@?ւ-@ Q@'\(?i@ ףp= @Q?d[@zG@zG?q@Q@= ףp=?[1333@(\@?HI@gfffff@(\?B_ `@= ףp=@QQ?Muwww@Gz@^(\?8@Q@Gz?iO@(\@?@!@@Q?'}@q= ףp@B ףp=?>@IzG@(\?2H@Q@Gz?l@(\@ifffff?~+؂-@@!Q?$oBDDD@p= ף@أp= ?&Y[@{Gz@(\?poq@RQ@(\?&Y[@)\(@(\?$oBDDD@@(\?~+؂-@ףp= @(\?l@Gz@(\?2H@Q@(\?>@\(\@(\?'}@333333@(\?@!@ ףp= @(\?iO@zG@(\?8@Q@(\?Muwww@(\@(\?B_ `@hfffff@(\?HI@< ףp=@(\?\1333@Gz@Gz?q@Gz@Q?d[@< ףp=@?i@hfffff@Q?ւ-@(\@:ףp= ?=l@Q@\(?v@zG@p= ף?͒>@ ףp= @033333?*|'}@333333@(\?^efff@\(\@PQ?ަNO@Q@zG?878@Gz@p= ףp?7!"""@ףp= @? ` @@HzG?FI@)\(@(\?@RQ@أp= ?Xq@{Gz@ Q?S[@p= ף@hfffff?阙@@Gz?2-؂@Q@(\?bzkl@(\@@ ףp=?TUUU@ffffff@Q? >>@> ףp=@?oS'}'@Gz@Gz?ɛ@Q@`(\?"O@(\@PQ?},8@@(\?t@p= ףp@?1` @HzG@@ ףp=?I@Q@zG?M@(\@Q?>qq@@(\(?Z[@p= ף@?&DDDD@zGz@p= ףp?Lo-؂-@RQ@Gz?l@*\(@Q?@@V(\?(\?(\??(\?(\?p= ף?t(\?t(\?JzG?(\?(\?Q?(\?(\?(\?(\?(\?B33333?(\?(\?٣p= ?|(\?|(\?xGz?(\?(\?!Q?(\?(\?(\?(\?(\?hfffff?{(\?{(\? ףp= ?(\?(\?Gz?l(\?l(\?Gz?azkl@(\@ ףp= ?2-؂@Q@ffffff?阙@zG@(\?T[@ ףp= @ Q?Xq@533333@|Gz?@](\@ףp= ?FI@Q@333333? ` @Gz@(\?7!"""@أp= @Q?878@@HzG?ަNO@)\(@p= ף?^efff@RQ@?*|'}@{Gz@\(\?͒>@p= ף@Q?v@@Gz?=l@(\@p= ףp?ւ-@Q@?h@333333@)\(?d[@ ףp= @Q?q@zG@zG?[1333@Q@= ףp=?HI@(\@?B_ `@gfffff@(\?Muwww@= ףp=@QQ?8@Gz@^(\?iO@Q@Gz??!@(\@?'}@@Q?>@q= ףp@> ףp=?1H@GzG@(\?l@Q@Gz?~+؂-@(\@gfffff?$oBDDD@@Q?&Y[@p= ף@أp= ?poq@{Gz@أp= ?&Y[@RQ@أp= ?$oBDDD@)\(@أp= ?~+؂-@@أp= ?l@ףp= @أp= ?1H@Gz@أp= ?>@Q@ۣp= ?'}@](\@ۣp= ?@!@433333@ۣp= ?iO@ ףp= @ڣp= ?8@zG@ۣp= ?Nuwww@Q@ۣp= ?B_ `@(\@أp= ?HI@gfffff@أp= ?[1333@= ףp=@أp= ?q@Gz@Gz?d[@Gz@Q?i@= ףp=@?ւ-@gfffff@Q?=l@(\@ ףp= ?v@Q@ \(?͒>@zG@p= ף?*|'}@ ףp= @"33333?^efff@233333@(\?ަNO@](\@CQ?878@Q@zG?7!"""@Gz@d= ףp? ` @֣p= @?FI@@BzG?@(\(@(\?Xq@SQ@ңp= ?S[@zGz@%Q?阙@p= ף@cfffff?2-؂@Q@Gz?azkl@(\@(\?TUUU@efffff@@ ףp=? >>@? ףp=@Q?nS'}'@Gz@?ʛ@Q@Gz?"O@(\@`(\?~,8@@PQ?t@o= ףp@(\?1` @IzG@?I@Q@@ ףp=?M@(\@zG?=qq@@Q?Z[@p= ף@(\(?&DDDD@{Gz@?Mo-؂-@SQ@p= ףp?l@)\(@Gz?@@Q?p= ?p= ?\(\?p= ?p= ??ԣp= ?ԣp= ?p= ף?p= ?p= ??zG?ڣp= ?ڣp= ?Q?ףp= ?ףp= ?(\?ԣp= ?ԣp= ?233333?ޣp= ?ޣp= ?ӣp= ?p= ?p= ?~Gz?p= ?p= ?Q?ˣp= ?ˣp= ?(\?p= ?p= ?`fffff?ѣp= ?ѣp= ?ףp= ?p= ?p= ?Gz?֣p= ?֣p= ?Gz?2-؂@Q@ ףp= ?阙@zG@ffffff?T[@ ףp= @(\?Xq@333333@ Q?@](\@|Gz?GI@Q@ףp= ? ` @Gz@333333?7!"""@أp= @(\?878@@Q?ަNO@)\(@HzG?^efff@RQ@p= ף?+|'}@{Gz@?͒>@p= ף@\(\?v@@Q?=l@(\@Gz?ւ-@Q@p= ףp?i@HzG@?d[@333333@'\(?q@ ףp= @Q?[1333@zG@zG?HI@Q@= ףp=?B_ `@(\@?Muwww@gfffff@(\?8@= ףp=@QQ?iO@Gz@^(\??!@Q@Gz?'}@(\@?>@@Q?1H@q= ףp@B ףp=?l@IzG@(\?~+؂-@Q@Gz?$oBDDD@(\@ifffff?&Y[@@Q?poq@p= ף@ Q?&Y[@{Gz@ Q?$oBDDD@RQ@ Q?~+؂-@)\(@ Q?l@@ Q?2H@ףp= @ Q?>@Gz@ Q?'}@Q@Q??!@\(\@Q?iO@333333@Q?8@ ףp= @Q?Luwww@zG@Q?B_ `@Q@Q?HI@(\@Q?[1333@ffffff@&Q?q@> ףp=@Q?d[@Gz@Gz?i@Gz@Q?ւ-@> ףp=@?=l@ffffff@R?v@(\@ףp= ?͒>@Q@@\(?*|'}@zG@p= ף?^efff@ ףp= @033333?ަNO@333333@(\?878@\(\@PQ?7!"""@Q@zG? ` @Gz@p= ףp?FI@ףp= @?@@HzG?Xq@)\(@(\?S[@RQ@أp= ?阙@{Gz@ Q?2-؂@p= ף@jfffff?bzkl@(\@Gz?TUUU@ffffff@(\? >>@> ףp=@@ ףp=?oS'}'@Gz@Q?ɛ@Q@?"O@(\@Gz?},8@@`(\?t@p= ףp@PQ?1` @HzG@(\?I@Q@?M@(\@@ ףp=?>qq@@zG?Z[@p= ף@Q?&DDDD@zGz@(\(?Lo-؂-@RQ@?l@(\(@p= ףp?@@Gz?5Q?5Q?Q?Q?Q?`(\? Q? Q??Q?Q?p= ף? Q? Q?DzG?:Q?:Q?Q? Q? Q?(\?Q?Q?233333?5Q?5Q?֣p= ?Q?Q?zGz?&Q?&Q?Q?*Q?*Q?(\?Q?Q?hfffff?Q?Q?ףp= ?%Q?%Q?Gz? Q? Q?Gz?阙@zG@ ףp= ?T[@ ףp= @ffffff?Xq@333333@(\?@](\@ Q?GI@Q@|Gz? ` @Gz@ףp= ?7!"""@أp= @433333?878@@(\?ަNO@*\(@Q?^efff@RQ@HzG?+|'}@{Gz@p= ף?͒>@p= ף@?v@@\(\?=l@(\@Q?ւ-@Q@Gz?i@HzG@p= ףp?d[@\(\@?q@333333@)\(?Z1333@ ףp= @Q?HI@zG@zG?B_ `@Q@= ףp=?Muwww@(\@?8@gfffff@(\?iO@= ףp=@QQ?@!@Gz@^(\?'}@Q@Gz?>@(\@?2H@@Q?l@q= ףp@> ףp=?~+؂-@GzG@(\?$oBDDD@Q@Gz?&Y[@(\@dfffff?poq@@gfffff?&Y[@p= ף@hfffff?$oBDDD@{Gz@hfffff?~+؂-@RQ@hfffff?l@)\(@hfffff?2H@@hfffff?>@ףp= @hfffff?'}@Gz@hfffff??!@Q@kfffff?iO@](\@jfffff?8@433333@jfffff?Nuwww@ ףp= @jfffff?B_ `@zG@kfffff?HI@Q@kfffff?\1333@(\@hfffff?q@gfffff@hfffff?d[@= ףp=@hfffff?i@Gz@Gz?ւ-@Gz@Q?=l@= ףp=@?v@gfffff@Q?͒>@(\@ ףp= ?*|'}@Q@ \(?^efff@zG@p= ף?ަNO@ ףp= @>33333?878@433333@(\?7!"""@[(\@]Q? ` @Q@zG?FI@Gz@|= ףp?@أp= @?Xq@@NzG?T[@*\(@(\?阙@QQ@ޣp= ?2-؂@|Gz@ Q?azkl@(\@ffffff?TUUU@ffffff@Gz? >>@= ףp=@(\?nS'}'@Gz@@ ףp=?ʛ@Q@Q?"O@(\@?~,8@@Gz?t@o= ףp@`(\?1` @IzG@PQ?I@Q@(\?M@(\@?=qq@@@ ףp=?Z[@p= ף@zG?&DDDD@yGz@Q?Mo-؂-@SQ@(\(?l@)\(@?@@m= ףp?dfffff?dfffff?Gz?Pfffff?Pfffff?Q?`fffff?`fffff?T(\?\fffff?\fffff??efffff?efffff?p= ף?efffff?efffff?IzG?Vfffff?Vfffff?Q?pfffff?pfffff?(\?dfffff?dfffff?833333?Pfffff?Pfffff?p= ?`fffff?`fffff?~Gz?kfffff?kfffff?Q?Pfffff?Pfffff?(\?pfffff?pfffff?`fffff?Tfffff?Tfffff?ףp= ?kfffff?kfffff?Gz?Yfffff?Yfffff?Gz?T[@ ףp= @ ףp= ?Xq@333333@ffffff?@](\@(\?FI@Q@ Q? ` @Gz@|Gz?7!"""@٣p= @أp= ?978@@433333?ަNO@*\(@(\?^efff@RQ@Q?+|'}@{Gz@HzG?͒>@p= ף@p= ף?w@@?=l@(\@\(\?ւ-@Q@Q?i@HzG@Gz?d[@q= ףp@p= ףp?q@\(\@?[1333@333333@'\(?HI@ ףp= @Q?B_ `@zG@zG?Muwww@Q@= ףp=?8@(\@?iO@gfffff@(\?@!@= ףp=@QQ?'}@Gz@^(\?>@Q@Gz?2H@(\@?l@@Q?~+؂-@q= ףp@B ףp=?$oBDDD@IzG@(\?&Y[@Q@Gz?poq@(\@Gz?&Y[@@Gz?$oBDDD@p= ף@Gz?~+؂-@{Gz@Gz?l@RQ@Gz?2H@)\(@Gz?>@@Gz?'}@ףp= @Gz??!@Gz@Gz?iO@Q@Gz?8@\(\@Gz?Muwww@333333@Gz?B_ `@ ףp= @Gz?HI@zG@Gz?Z1333@Q@Gz?q@(\@Gz?d[@hfffff@Gz?h@< ףp=@Gz?ւ-@Gz@Gz?=l@Gz@Q?v@< ףp=@?͒>@hfffff@Q?)|'}@(\@:ףp= ?^efff@Q@\(?ݦNO@zG@p= ף?878@ ףp= @033333?7!"""@333333@(\? ` @\(\@PQ?FI@Q@zG?@Gz@p= ףp?Xq@ףp= @?S[@@HzG?阙@)\(@(\?2-؂@RQ@أp= ?azkl@{Gz@ Q?TUUU@ffffff@kfffff? >>@> ףp=@Gz?oS'}'@Gz@(\?ɛ@Q@@ ףp=?"O@(\@Q?},8@@?t@p= ףp@Gz?1` @HzG@`(\?I@Q@PQ?M@(\@(\?>qq@@?Z[@p= ף@@ ףp=?&DDDD@zGz@zG?Lo-؂-@RQ@Q?l@(\(@(\(?@@?Gz?Gz?n= ףp?Gz?Gz?Gz?Gz?Gz?Q?Gz?Gz?Z(\?Gz?Gz??Gz?Gz?p= ף?Gz?Gz?FzG?Gz?Gz?Q?Gz?Gz?(\?Gz?Gz?:33333?Gz?Gz?ףp= ?Gz?Gz?wGz?Gz?Gz?Q?Gz?Gz?(\?Gz?Gz?hfffff?Gz?Gz?ףp= ?Gz?Gz?Gz?Gz?Gz?Gz?Xq@333333@ ףp= ?@\(\@ffffff?EI@Q@(\? ` @Gz@ Q?7!"""@أp= @|Gz?978@@أp= ?ަNO@*\(@433333?^efff@RQ@(\?*|'}@zGz@Q?͒>@p= ף@HzG?v@@p= ף?=l@(\@?ւ-@Q@\(\?h@HzG@Q?d[@p= ףp@Gz?q@Q@q= ףp?Z1333@\(\@?HI@433333@)\(?B_ `@ ףp= @Q?Muwww@zG@zG?8@Q@< ףp=?iO@(\@??!@gfffff@(\?'}@= ףp=@PQ?>@Gz@\(\?2H@Q@Gz?l@(\@?}+؂-@@Q?$oBDDD@p= ףp@> ףp=?&Y[@HzG@(\?poq@Q@(\?&Y[@(\@(\?$oBDDD@@(\?~+؂-@p= ף@(\?l@|Gz@(\?2H@RQ@(\?>@*\(@(\?'}@@(\?@!@أp= @(\?iO@Gz@(\?8@Q@(\?Muwww@\(\@(\?B_ `@433333@(\?HI@ ףp= @(\?Z1333@zG@(\?q@Q@(\?d[@(\@(\?i@hfffff@(\?ւ-@= ףp=@(\?=l@Gz@}Gz?w@Gz@Q?͒>@; ףp=@ݙ?*|'}@hfffff@Q?^efff@(\@-ףp= ?ަNO@Q@[(?878@zG@p= ף?7!"""@ ףp= @*33333? ` @233333@(\?FI@\(\@KQ?@Q@zG?Xq@Gz@l= ףp?R[@֣p= @?阙@@FzG?2-؂@)\(@(\?bzkl@QQ@ޣp= ?TUUU@ffffff@Q? >>@> ףp=@cfffff?nS'}'@Gz@Gz?ʛ@Q@(\?#O@(\@= ףp=?},8@@Q?t@p= ףp@?1` @HzG@Gz?I@Q@\(\?M@(\@RQ?>qq@@(\?Z[@p= ף@?&DDDD@{Gz@> ףp=?Lo-؂-@RQ@zG?l@)\(@Q?@@\(?)\?)\??(\?(\?q= ףp?(\?(\?Gz?(\?(\?Q?(\?(\?T(\?(\?(\??(\?(\?p= ף?(\?(\?@zG?)\?)\?Q?(\?(\?(\?(\?(\?:33333?(\?(\?ңp= ?(\?(\?vGz?(\?(\?Q?(\?(\?(\?(\?(\?dfffff?(\?(\?ףp= ?(\?(\?Gz?(\?(\?Gz?@\(\@ ףp= ?EI@Q@ffffff? ` @Gz@(\?7!"""@ףp= @ Q?978@@|Gz?ߦNO@*\(@أp= ?^efff@RQ@433333?*|'}@zGz@(\?͒>@p= ף@Q?v@@HzG?=l@(\@p= ף?ւ-@Q@?h@HzG@\(\?d[@q= ףp@Q?q@@Gz?[1333@Q@q= ףp?HI@\(\@?B_ `@433333@)\(?Muwww@ ףp= @Q?8@zG@zG?iO@Q@< ףp=??!@(\@?'}@gfffff@(\?>@> ףp=@RQ?1H@Gz@`(\?l@Q@Gz?}+؂-@(\@?$oBDDD@@Q?&Y[@p= ףp@> ףp=?poq@HzG@> ףp=?&Y[@Q@= ףp=?$oBDDD@(\@= ףp=?~+؂-@@= ףp=?l@p= ף@: ףp=?1H@zGz@: ףp=?>@RQ@: ףp=?'}@(\(@: ףp=??!@@: ףp=?iO@֣p= @: ףp=?8@Gz@: ףp=?Muwww@Q@8 ףp=?B_ `@\(\@7 ףp=?HI@233333@7 ףp=?Z1333@ ףp= @7 ףp=?q@zG@8 ףp=?d[@Q@8 ףp=?h@(\@5 ףp=?ւ-@ffffff@> ףp=?=l@= ףp=@5 ףp=?w@Gz@Gz?͒>@Gz@<Q?*|'}@> ףp=@?^efff@ffffff@R?ަNO@(\@ףp= ?878@Q@L\(?7!"""@zG@p= ף? ` @ ףp= @633333?GI@433333@(\?@\(\@UQ?Xq@Q@zG?S[@Gz@t= ףp?阙@ףp= @?2-؂@@JzG?bzkl@)\(@(\?TUUU@SQ@ޣp= ? >>@? ףp=@"Q?oS'}'@Gz@cfffff?ʛ@Q@Gz?"O@(\@(\?},8@@< ףp=?t@r= ףp@Q?0` @HzG@?I@ Q@Gz?M@(\@\(\?>qq@@RQ?Z[@p= ף@(\?&DDDD@{Gz@?Lo-؂-@RQ@> ףp=?l@)\(@zG?@@Q?2 ףp=?2 ףp=?/\(?Q ףp=?Q ףp=??< ףp=?< ףp=?w= ףp?B ףp=?B ףp=? Gz?G ףp=?G ףp=?Q?1 ףp=?1 ףp=?b(\?N ףp=?N ףp=??B ףp=?B ףp=?p= ף?4 ףp=?4 ףp=?NzG?R ףp=?R ףp=?Q?< ףp=?< ףp=?(\?B ףp=?B ףp=?:33333?G ףp=?G ףp=?ޣp= ?2 ףp=?2 ףp=?|Gz?Q ףp=?Q ףp=?%Q?B ףp=?B ףp=?(\?B ףp=?B ףp=?lfffff?1 ףp=?1 ףp=? ףp= ?R ףp=?R ףp=?Gz?" ףp=?" ףp=?Gz?EI@Q@ ףp= ? ` @Gz@ffffff?7!"""@ףp= @(\?878@@ Q?ߦNO@*\(@|Gz?^efff@SQ@أp= ?*|'}@|Gz@433333?͒>@p= ף@(\?v@@Q?=l@(\@HzG?ւ-@Q@p= ף?i@HzG@?d[@q= ףp@\(\?q@@Q?[1333@Gz@Gz?HI@Q@q= ףp?B_ `@\(\@?Muwww@433333@)\(?8@ ףp= @Q?iO@zG@zG??!@Q@< ףp=?'}@(\@?>@ffffff@(\?1H@= ףp=@PQ?l@Gz@\(\?~+؂-@Q@Gz?#oBDDD@(\@?&Y[@@Q?poq@p= ףp@Q?&Y[@HzG@Q?$oBDDD@ Q@Q?~+؂-@(\@Q?l@@Q?2H@p= ף@Q?>@|Gz@Q?'}@RQ@Q?@!@*\(@Q?iO@@Q?8@ףp= @Q?Muwww@Gz@Q?B_ `@Q@Q?HI@\(\@Q?[1333@433333@Q?q@ ףp= @Q?d[@zG@Q?h@Q@Q?ւ-@(\@Q?=l@gfffff@Q?w@> ףp=@Q?͒>@Gz@Gz?*|'}@Gz@Q?^efff@> ףp=@c?ަNO@ffffff@Q?878@(\@֣p= ?7!"""@Q@,\(? ` @zG@p= ף?FI@ ףp= @*33333?@233333@(\?Xq@\(\@KQ?S[@Q@zG?阙@Gz@l= ףp?2-؂@ףp= @?azkl@@FzG?TUUU@)\(@(\? >>@? ףp=@ޣp= ?oS'}'@Gz@Q?ɛ@Q@cfffff?"O@(\@Gz?~,8@@(\?t@p= ףp@< ףp=?1` @HzG@Q?I@Q@?M@(\@Gz?>qq@@\(\?Z[@p= ף@RQ?&DDDD@{Gz@(\?Lo-؂-@RQ@?l@)\(@> ףp=?@@zG?Q?Q?Q?oQ?oQ?!\(?Q?Q??_Q?_Q?i= ףp?~Q?~Q? Gz?Q?Q?Q?oQ?oQ?^(\?rQ?rQ??Q?Q?p= ף?Q?Q?BzG?Q?Q?Q?_Q?_Q?(\?~Q?~Q?*33333?Q?Q?ӣp= ?oQ?oQ?sGz?Q?Q?Q?~Q?~Q?(\?Q?Q?dfffff?oQ?oQ?ףp= ?Q?Q?Gz?_Q?_Q?Gz? ` @Gz@ ףp= ?7!"""@ףp= @ffffff?878@@(\?ަNO@)\(@ Q?^efff@SQ@|Gz?+|'}@|Gz@أp= ?͒>@p= ף@433333?v@@(\?=l@(\@Q?ւ-@Q@HzG?i@HzG@p= ף?d[@q= ףp@?q@@\(\?[1333@(\@Q?HI@Gz@Gz?B_ `@Q@q= ףp?Muwww@\(\@?8@433333@)\(?iO@ ףp= @Q?@!@zG@zG?'}@Q@< ףp=?>@(\@?1H@ffffff@(\?l@> ףp=@RQ?}+؂-@Gz@`(\?$oBDDD@Q@Gz?&Y[@(\@?ooq@@?&Y[@p= ףp@?$oBDDD@HzG@?~+؂-@Q@?l@(\@?1H@@?>@p= ף@?'}@zGz@??!@RQ@?iO@(\(@?8@@?Muwww@ףp= @?B_ `@Gz@?HI@Q@?[1333@\(\@?q@233333@?d[@ ףp= @?h@zG@?ւ-@Q@?=l@(\@?w@gfffff@?͒>@< ףp=@?+|'}@Gz@}Gz?^efff@Gz@DQ?ަNO@< ףp=@?878@hfffff@Q?7!"""@(\@Mףp= ? ` @Q@\(?FI@zG@p= ף?@ ףp= @633333?Xq@433333@(\?T[@\(\@UQ?阙@Q@zG?2-؂@Gz@t= ףp?bzkl@ףp= @?TUUU@@JzG? >>@)\(@(\?nS'}'@Gz@ޣp= ?ʛ@Q@#Q?#O@(\@bfffff?},8@@Gz?t@o= ףp@(\?0` @HzG@= ףp=?I@ Q@Q?M@(\@?>qq@@Gz?Z[@p= ף@\(\?&DDDD@{Gz@RQ?Lo-؂-@RQ@(\?l@)\(@?@@= ףp=???zG???Q???&\(??????y= ףp???Gz???Q???](\??????p= ף???HzG???Q???(\???B33333???ڣp= ???~Gz???#Q???(\???lfffff??? ףp= ???Gz???Gz?7!"""@ףp= @ ףp= ?878@@ffffff?ަNO@)\(@(\?^efff@QQ@ Q?+|'}@|Gz@|Gz?͒>@p= ף@أp= ?v@@333333?=l@(\@(\?ւ-@Q@Q?h@HzG@HzG?d[@q= ףp@p= ף?q@@?[1333@(\@](\?HI@ףp= @Q?B_ `@Gz@Gz?Muwww@Q@q= ףp?8@\(\@?iO@433333@)\(?@!@ ףp= @Q?'}@zG@zG?>@Q@< ףp=?1H@(\@?l@ffffff@(\?~+؂-@= ףp=@PQ?#oBDDD@Gz@\(\?&Y[@Q@Gz?ooq@(\@Gz?&Y[@@Gz?$oBDDD@r= ףp@Gz?~+؂-@HzG@Gz?l@ Q@Gz?2H@(\@Gz?>@@Gz?'}@p= ף@Gz?@!@|Gz@Gz?iO@RQ@Gz?8@)\(@Gz?Muwww@@Gz?B_ `@ףp= @Gz?HI@Gz@Gz?[1333@Q@Gz?q@\(\@Gz?d[@433333@Gz?i@ ףp= @Gz?ւ-@zG@Gz?=l@Q@Gz?v@(\@Gz?͒>@hfffff@Gz?*|'}@= ףp=@Gz?^efff@Gz@}Gz?ަNO@Gz@Q?778@< ףp=@ݙ?7!"""@hfffff@Q? ` @(\@-ףp= ?FI@Q@[(?@zG@p= ף?Xq@ ףp= @*33333?S[@233333@(\?阙@[(\@KQ?2-؂@Q@zG?azkl@Gz@l= ףp?TUUU@ףp= @? >>@@BzG?nS'}'@Gz@(\?ʛ@Q@ݣp= ?#O@(\@Q?},8@@cfffff?t@o= ףp@Gz?2` @IzG@(\?I@Q@= ףp=?M@(\@Q?>qq@@?Z[@p= ף@Gz?&DDDD@{Gz@\(\?Lo-؂-@RQ@RQ?l@)\(@(\?@@?Gz?Gz?< ףp=?Gz?Gz?zG?Gz?Gz?Q?Gz?Gz?$\(?Gz?Gz??Gz?Gz?l= ףp?Gz?Gz?Gz?Gz?Gz?Q?Gz?Gz?P(\?Gz?Gz??Gz?Gz?p= ף?Gz?Gz?DzG?Gz?Gz?Q?Gz?Gz?(\?Gz?Gz?<33333?Gz?Gz?ԣp= ?Gz?Gz?tGz?Gz?Gz?Q?Gz?Gz?(\?Gz?Gz?dfffff?Gz?Gz?ףp= ?Gz?Gz?Gz?Gz?Gz?Gz?878@@ ףp= ?ަNO@)\(@ffffff?^efff@RQ@(\?*|'}@{Gz@ Q?͒>@p= ף@|Gz?w@@أp= ?=l@(\@333333?ւ-@Q@(\?h@HzG@Q?d[@p= ףp@HzG?q@@p= ף?[1333@(\@?HI@Q@[(\?B_ `@ףp= @Q?Muwww@Gz@Gz?8@Q@q= ףp?iO@\(\@??!@433333@)\(?'}@ ףp= @Q?>@zG@zG?2H@Q@< ףp=?l@(\@?~+؂-@ffffff@(\?$oBDDD@> ףp=@RQ?&Y[@Gz@\(\?poq@Q@V(\?&Y[@(\@^(\?$oBDDD@@^(\?~+؂-@p= ףp@^(\?l@HzG@^(\?2H@Q@^(\?>@(\@^(\?'}@@^(\??!@p= ף@Z(\?iO@zGz@Z(\?8@RQ@Z(\?Muwww@)\(@Z(\?B_ `@@Z(\?HI@ףp= @Z(\?[1333@Gz@Z(\?q@Q@X(\?d[@\(\@X(\?h@333333@X(\?ւ-@ ףp= @X(\?=l@zG@X(\?v@Q@X(\?͒>@(\@V(\?*|'}@ffffff@^(\?^efff@= ףp=@V(\?ަNO@Gz@Gz?878@Gz@<Q?7!"""@> ףp=@? ` @ffffff@R?FI@(\@ףp= ?@Q@L\(?Xq@zG@p= ף?S[@ ףp= @633333?阙@333333@(\?2-؂@](\@UQ?bzkl@Q@zG?TUUU@Gz@t= ףp? >>@ףp= @?pS'}'@@BzG?ɛ@Q@(\?"O@(\@ޣp= ?~,8@@"Q?t@p= ףp@cfffff?1` @HzG@Gz?I@Q@(\?M@(\@= ףp=??qq@@Q?Z[@p= ף@?&DDDD@{Gz@Gz?Lo-؂-@RQ@\(\?l@)\(@TQ?@@(\?`(\?`(\??(\?(\?A ףp=?p(\?p(\?zG?p(\?p(\?Q?`(\?`(\?)\(?(\?(\??P(\?P(\?r= ףp?p(\?p(\?Gz?(\?(\?Q?`(\?`(\?f(\?`(\?`(\??p(\?p(\?p= ף?(\?(\?JzG?(\?(\?Q?P(\?P(\?(\?p(\?p(\?333333?(\?(\?ۣp= ?`(\?`(\?{Gz?(\?(\?$Q?p(\?p(\?(\?p(\?p(\?lfffff?`(\?`(\? ףp= ?(\?(\?Gz?P(\?P(\?Gz?ަNO@)\(@ ףp= ?^efff@RQ@ffffff?+|'}@{Gz@(\?͒>@p= ף@ Q?w@@|Gz?=l@(\@أp= ?ւ-@ Q@333333?h@HzG@(\?d[@p= ףp@Q?q@@HzG?[1333@(\@p= ף?HI@Q@?B_ `@@](\?Muwww@ףp= @Q?8@Gz@Gz?iO@Q@q= ףp??!@\(\@?'}@433333@)\(?>@ ףp= @Q?2H@zG@zG?l@Q@< ףp=?~+؂-@(\@?$oBDDD@ffffff@(\?&Y[@= ףp=@PQ?poq@Gz@UQ?&Y[@Q@QQ?$oBDDD@(\@UQ?~+؂-@@UQ?l@r= ףp@UQ?2H@HzG@UQ?>@ Q@UQ?'}@(\@UQ?@!@@UQ?iO@p= ף@SQ?8@{Gz@SQ?Muwww@RQ@SQ?B_ `@)\(@SQ?HI@@SQ?[1333@ףp= @SQ?q@Gz@SQ?d[@Q@RQ?i@\(\@RQ?ւ-@433333@RQ?=l@ ףp= @RQ?v@zG@RQ?͒>@Q@RQ?*|'}@(\@QQ?^efff@gfffff@UQ?ߦNO@> ףp=@QQ?878@Gz@Gz?7!"""@Gz@Q? ` @> ףp=@c?FI@efffff@Q?@(\@֣p= ?Xq@Q@,\(?S[@zG@p= ף?阙@ ףp= @*33333?2-؂@333333@(\?bzkl@[(\@KQ?TUUU@Q@zG? >>@Gz@l= ףp?oS'}'@ףp= @?ɛ@Q@YzG?#O@(\@(\?},8@@ޣp= ?t@p= ףp@Q?1` @HzG@xfffff?I@ Q@Gz?M@(\@(\?@qq@@< ףp=?Z[@p= ף@Q?&DDDD@|Gz@?Lo-؂-@RQ@Gz?l@*\(@\(\?@@XQ?PQ?PQ?(\?@Q?@Q??PQ?PQ?@ ףp=?HQ?HQ?zG?HQ?HQ?Q?@Q?@Q?'\(?PQ?PQ??8Q?8Q?o= ףp?HQ?HQ?Gz?XQ?XQ?Q?@Q?@Q?c(\?@Q?@Q??HQ?HQ?p= ף?PQ?PQ?FzG?PQ?PQ?Q?8Q?8Q?(\?HQ?HQ?.33333?XQ?XQ?գp= ?@Q?@Q?uGz?PQ?PQ?Q?HQ?HQ?(\?HQ?HQ?dfffff?@Q?@Q?ףp= ?PQ?PQ?Gz?8Q?8Q?Gz?^efff@RQ@ ףp= ?+|'}@{Gz@ffffff?͒>@p= ף@(\?w@@ Q?=l@(\@|Gz?ւ-@ Q@أp= ?j@HzG@333333?d[@p= ףp@(\?q@@Q?\1333@(\@HzG?HI@Q@p= ף?B_ `@Gz@?Nuwww@@[(\?8@ףp= @Q?iO@Gz@Gz??!@Q@q= ףp?'}@\(\@?>@433333@)\(?2H@ ףp= @Q?l@zG@zG?}+؂-@Q@< ףp=?$oBDDD@(\@?&Y[@ffffff@(\?ooq@= ףp=@(\?&Y[@Gz@(\?$oBDDD@Q@(\?~+؂-@(\@(\?l@@(\?2H@p= ףp@(\?>@HzG@(\?'}@Q@(\??!@(\@(\?iO@@(\?8@p= ף@(\?Muwww@{Gz@(\?B_ `@RQ@(\?HI@)\(@(\?[1333@@(\?q@ףp= @(\?d[@Gz@(\?i@Q@(\?ւ-@\(\@(\?=l@333333@(\?v@ ףp= @(\?͒>@zG@(\?*|'}@Q@(\?^efff@(\@(\?ߦNO@gfffff@(\?778@< ףp=@(\?7!"""@Gz@}Gz? ` @Gz@DQ?EI@< ףp=@?@ifffff@Q?Xq@(\@Mףp= ?U[@Q@\(?阙@zG@p= ף?2-؂@ ףp= @633333?bzkl@333333@(\?TUUU@](\@UQ? >>@Q@zG?oS'}'@Gz@t= ףp?ʛ@ףp= @?"O@(\@BzG?},8@@(\?t@o= ףp@ȣp= ?0` @FzG@"Q?I@Q@cfffff?M@(\@Gz?>qq@@(\?Z[@p= ף@4 ףp=?&DDDD@zGz@Q?Lo-؂-@RQ@?l@(\(@Gz?@@(\?|(\?|(\?(\?>Q?>Q?(\?(\?(\??)\?)\?D ףp=?(\?(\?zG?(\?(\?Q?(\?(\?,\(?)\?)\??(\?(\?t= ףp?(\?(\?$Gz?(\?(\?Q?(\?(\?Y(\?)\?)\??(\?(\?p= ף?(\?(\?LzG?)\?)\?Q?(\?(\?(\?(\?(\?D33333?(\?(\?ܣp= ?(\?(\?|Gz?)\?)\?$Q?(\?(\?(\?(\?(\?lfffff?(\?(\? ףp= ?)\?)\?Gz?(\?(\?Gz?+|'}@{Gz@ ףp= ?͒>@p= ף@ffffff?w@@(\?=l@(\@ Q?ւ-@ Q@|Gz?j@IzG@أp= ?d[@r= ףp@333333?q@@(\?\1333@(\@Q?HI@Q@HzG?B_ `@Gz@p= ף?Muwww@)\(@?8@@](\?iO@ףp= @Q??!@Gz@Gz?'}@Q@q= ףp?>@\(\@?2H@433333@)\(?l@ ףp= @Q?~+؂-@zG@zG?$oBDDD@Q@< ףp=?&Y[@(\@?ooq@ffffff@?&Y[@> ףp=@?$oBDDD@Gz@?~+؂-@Q@?l@(\@?1H@@?>@r= ףp@?'}@HzG@??!@ Q@?iO@(\@?8@@?Muwww@p= ף@?B_ `@{Gz@?HI@RQ@?[1333@)\(@?q@@?d[@ףp= @?i@Gz@?ւ-@Q@?=l@\(\@?v@433333@?͒>@ ףp= @?*|'}@zG@?^efff@Q@?ަNO@(\@?978@hfffff@?7!"""@= ףp=@? ` @Gz@}Gz?FI@Gz@Q?@; ףp=@ݙ?Xq@hfffff@Q?R[@(\@-ףp= ?阙@Q@[(?2-؂@zG@p= ף?azkl@ ףp= @*33333?TUUU@333333@(\? >>@[(\@KQ?nS'}'@Q@zG?ɛ@Gz@|= ףp?#O@(\@?},8@@YzG?t@r= ףp@(\?2` @IzG@ޣp= ?I@Q@Q?M@(\@xfffff??qq@@Gz?Z[@p= ף@(\?&DDDD@|Gz@= ףp=?Lo-؂-@RQ@Q?l@*\(@?@@?$Gz?$Gz??d(\?d(\??RQ?RQ??(\?(\????5 ףp=???zG???}Q???*\(??????g= ףp???Gz???Q???T(\??????p= ף???GzG???Q???(\???:33333???ףp= ???vGz???Q???(\???dfffff???ףp= ???Gz???Gz?͒>@p= ף@ ףp= ?w@@ffffff?=l@(\@(\?ւ-@Q@ Q?j@IzG@|Gz?d[@r= ףp@أp= ?q@@333333?\1333@(\@(\?HI@Q@Q?B_ `@Gz@HzG?Muwww@= ףp=@p= ף?8@)\(@?iO@@[(\?@!@ףp= @Q?'}@Gz@Gz?>@Q@q= ףp?1H@\(\@?l@433333@)\(?~+؂-@ ףp= @Q?$oBDDD@zG@zG?&Y[@Q@< ףp=?poq@(\@= ףp=?&Y[@ffffff@? ףp=?$oBDDD@> ףp=@; ףp=?~+؂-@Gz@? ףp=?l@Q@< ףp=?2H@(\@? ףp=?>@@? ףp=?'}@p= ףp@? ףp=??!@HzG@? ףp=?iO@Q@? ףp=?8@(\@? ףp=?Luwww@@? ףp=?B_ `@p= ף@= ףp=?HI@{Gz@= ףp=?[1333@RQ@= ףp=?q@)\(@= ףp=?d[@@= ףp=?i@ףp= @= ףp=?ւ-@Gz@> ףp=?=l@Q@< ףp=?v@\(\@< ףp=?͒>@233333@< ףp=?*|'}@ ףp= @< ףp=?^efff@zG@< ףp=?ަNO@Q@< ףp=?878@(\@; ףp=?7!"""@ffffff@? ףp=? ` @= ףp=@; ףp=?FI@Gz@Gz?@Gz@<Q?Xq@? ףp=@?T[@ffffff@R?阙@(\@ףp= ?2-؂@Q@L\(?azkl@zG@p= ף?TUUU@ ףp= @633333? >>@333333@(\?oS'}'@](\@UQ?ʛ@Q@zG?#O@Gz@L= ףp?|,8@@?t@p= ףp@BzG?1` @HzG@(\?I@Q@ȣp= ?M@(\@#Q?>qq@@cfffff?Z[@p= ף@Gz?&DDDD@yGz@(\?Lo-؂-@RQ@3 ףp=?l@(\(@Q?@@7 ףp=???> ףp=? Gz? Gz?0 ףp=?k(\?k(\?> ףp=?FQ?FQ?7 ףp=?(\?(\?> ףp=???H ףp=?@ ףp=?@ ףp=?zG?? ףp=?? ףp=?Q?8 ףp=?8 ףp=?0\(?H ףp=?H ףp=??/ ףp=?/ ףp=?w= ףp?@ ףp=?@ ףp=?Gz?P ףp=?P ףp=?Q?8 ףp=?8 ףp=?j(\?6 ףp=?6 ףp=??@ ףp=?@ ףp=?p= ף?H ףp=?H ףp=?MzG?G ףp=?G ףp=?Q?0 ףp=?0 ףp=?(\?@ ףp=?@ ףp=?533333?O ףp=?O ףp=?ޣp= ?8 ףp=?8 ףp=?~Gz?H ףp=?H ףp=?$Q?? ףp=?? ףp=?(\?@ ףp=?@ ףp=?mfffff?8 ףp=?8 ףp=? ףp= ?H ףp=?H ףp=?Gz?0 ףp=?0 ףp=?Gz?w@@ ףp= ?=l@(\@ffffff?ւ-@Q@(\?i@HzG@ Q?d[@r= ףp@|Gz?q@@أp= ?\1333@(\@333333?HI@Q@(\?B_ `@Gz@Q?Nuwww@> ףp=@FzG?8@QQ@p= ף?iO@)\(@?@!@@](\?'}@ףp= @Q?>@Gz@Gz?1H@Q@q= ףp?l@\(\@?~+؂-@433333@)\(?$oBDDD@ ףp= @Q?&Y[@zG@zG?poq@Q@zG?&Y[@(\@zG?$oBDDD@hfffff@zG?~+؂-@> ףp=@zG?l@Gz@zG?1H@Q@zG?>@(\@zG?'}@@zG??!@r= ףp@zG?iO@HzG@zG?8@Q@zG?Luwww@(\@zG?B_ `@@zG?HI@p= ף@zG?[1333@{Gz@zG?q@RQ@zG?d[@)\(@zG?i@@zG?ւ-@ףp= @zG?=l@Gz@zG?v@Q@zG?͒>@\(\@zG?*|'}@433333@zG?^efff@ ףp= @zG?ަNO@zG@zG?878@Q@zG?7!"""@(\@zG? ` @gfffff@zG?GI@> ףp=@zG?@Gz@Gz?Xq@Gz@Q?S[@> ףp=@c?阙@ffffff@Q?2-؂@(\@֣p= ?azkl@Q@,\(?TUUU@zG@p= ף? >>@ ףp= @*33333?nS'}'@333333@(\?ɛ@[(\@KQ?"O@Q@zG?},8@@|= ףp?t@p= ףp@?1` @HzG@ZzG?I@ Q@(\?M@(\@ޣp= ?>qq@@Q?Z[@p= ף@xfffff?&DDDD@|Gz@Gz?Mo-؂-@SQ@(\?l@*\(@= ףp=?@@zG?sQ?sQ?zG???zG?Gz?Gz?zG?t(\?t(\?zG?JQ?JQ?zG?(\?(\?zG???zG?: ףp=?: ףp=?zG?zG?zG?Q?zG?zG?!\(?zG?zG??zG?zG?i= ףp?zG?zG?Gz?zG?zG?Q?zG?zG?U(\?zG?zG??zG?zG?p= ף?zG?zG?BzG?zG?zG?Q?zG?zG?(\?zG?zG?033333?zG?zG?ңp= ?zG?zG?tGz?zG?zG?Q?zG?zG?(\?zG?zG?cfffff?zG?zG?ףp= ?zG?zG?Gz?zG?zG?Gz?=l@(\@ ףp= ?ւ-@Q@ffffff?i@HzG@(\?d[@q= ףp@ Q?q@@|Gz?\1333@(\@أp= ?HI@Q@333333?B_ `@Gz@(\?Nuwww@> ףp=@Q?8@hfffff@FzG?iO@QQ@p= ף?@!@)\(@?'}@@[(\?>@ףp= @Q?1H@Gz@Gz?l@Q@q= ףp?}+؂-@\(\@?$oBDDD@433333@)\(?&Y[@ ףp= @Q?poq@zG@Q?&Y[@Q@Q?$oBDDD@(\@Q?~+؂-@ffffff@Q?l@> ףp=@Q?2H@Gz@Q?>@Q@Q?'}@(\@Q?>!@@Q?iO@p= ףp@Q?8@HzG@Q?Muwww@Q@Q?B_ `@(\@Q?HI@@Q?Z1333@p= ף@Q?q@{Gz@Q?d[@RQ@Q?h@)\(@Q?ւ-@@Q?=l@ףp= @Q?v@Gz@Q?͒>@Q@Q?*|'}@\(\@Q?^efff@333333@Q?ަNO@ ףp= @Q?878@zG@Q?7!"""@Q@Q? ` @(\@Q?FI@gfffff@Q?@< ףp=@Q?Xq@Gz@}Gz?T[@Gz@DQ?阙@= ףp=@?2-؂@hfffff@Q?azkl@(\@Mףp= ?TUUU@Q@\(? >>@zG@p= ף?oS'}'@ ףp= @633333?ʛ@333333@(\?#O@](\@UQ?~,8@Q@zG?t@p= ףp@L= ףp?0` @FzG@?I@Q@BzG?M@(\@(\?>qq@@ȣp= ?Z[@p= ף@"Q?&DDDD@zGz@bfffff?Lo-؂-@RQ@Gz?l@'\(@(\?@@Q? ףp=? ףp=?Q?Q?Q?Q???Q?Gz?Gz?Q?\(\?\(\?Q?NQ?NQ?Q?(\?(\?Q???Q?/ ףp=?/ ףp=?Q?zG?zG?Q?Q?Q?2\(?Q?Q??Q?Q?o= ףp?Q?Q?Gz?Q?Q?Q?zQ?zQ?\(\?Q?Q??Q?Q?p= ף?Q?Q?OzG?Q?Q?Q?Q?Q?(\?Q?Q?B33333?Q?Q?ޣp= ?zQ?zQ?~Gz?Q?Q?#Q?Q?Q?(\?Q?Q?lfffff?Q?Q? ףp= ?Q?Q?Gz?Q?Q?Gz?ւ-@Q@ ףp= ?i@GzG@ffffff?d[@q= ףp@(\?q@@ Q?\1333@(\@|Gz?HI@Q@ףp= ?B_ `@Gz@333333?Nuwww@> ףp=@(\?8@ffffff@Q?iO@{Gz@HzG??!@RQ@p= ף?'}@)\(@?>@@\(\?2H@ףp= @Q?l@Gz@Gz?~+؂-@Q@p= ףp?$oBDDD@\(\@?&Y[@333333@'\(?poq@ ףp= @'\(?&Y[@zG@(\(?$oBDDD@Q@(\(?~+؂-@(\@(\(?l@gfffff@*\(?2H@> ףp=@&\(?>@Gz@*\(?'}@Q@'\(?@!@(\@)\(?iO@@*\(?8@q= ףp@*\(?Muwww@HzG@*\(?B_ `@Q@*\(?HI@(\@*\(?Z1333@@*\(?q@p= ף@(\(?d[@{Gz@(\(?i@RQ@(\(?ւ-@)\(@(\(?=l@@(\(?v@ףp= @(\(?͒>@Gz@(\(?*|'}@Q@'\(?^efff@\(\@'\(?ަNO@333333@'\(?878@ ףp= @'\(?7!"""@zG@'\(? ` @Q@'\(?EI@(\@*\(?@hfffff@&\(?Xq@< ףp=@*\(?U[@Gz@Gz?阙@Gz@Q?2-؂@< ףp=@?czkl@hfffff@Q?TUUU@(\@:ףp= ? >>@Q@\(?nS'}'@zG@p= ף?ɛ@ ףp= @033333?#O@333333@(\?},8@\(\@ZQ?t@r= ףp@zG?1` @HzG@g= ףp?I@Q@?M@(\@LzG??qq@@(\?Z[@p= ף@գp= ?&DDDD@zGz@ Q?Lo-؂-@RQ@kfffff?l@*\(@Gz?@@&\(?(\?(\?*\(?M ףp=?M ףp=?&\(?Q?Q?$\(???&\(?Gz?Gz?\(?p(\?p(\?&\(?HQ?HQ?$\(?)\?)\?&\(???*\(?I ףp=?I ףp=?&\(?zG?zG?$\(?Q?Q?+\(?+\(?+\(??\(?\(?s= ףp?#\(?#\(?Gz?7\(?7\(?Q?\(?\(?_(\?\(?\(??#\(?#\(?p= ף?1\(?1\(?JzG?+\(?+\(?Q?\(?\(?(\?#\(?#\(?<33333?7\(?7\(?٣p= ?\(?\(?yGz?+\(?+\(?!Q?#\(?#\(?(\?$\(?$\(?hfffff?\(?\(?ףp= ?+\(?+\(?Gz?\(?\(?Gz?i@GzG@ ףp= ?d[@q= ףp@ffffff?q@@(\?[1333@(\@ Q?HI@Q@|Gz?B_ `@Gz@ףp= ?Nuwww@> ףp=@333333?8@ffffff@(\?iO@(\@Q??!@{Gz@HzG?'}@RQ@p= ף?>@)\(@?2H@@\(\?l@ףp= @Q?~+؂-@Gz@Gz?$oBDDD@Q@p= ףp?&Y[@\(\@?ooq@333333@?&Y[@ ףp= @?$oBDDD@zG@?~+؂-@Q@?l@(\@?2H@gfffff@?>@= ףp=@?'}@Gz@??!@Q@?iO@(\@?8@@?Nuwww@p= ףp@?B_ `@GzG@?HI@Q@?[1333@(\@?q@@?d[@p= ף@?i@|Gz@?ւ-@QQ@?=l@*\(@?v@@?͒>@أp= @?*|'}@Gz@?^efff@Q@?ߦNO@](\@?978@433333@?7!"""@ ףp= @? ` @zG@?GI@Q@?@(\@?Xq@gfffff@?S[@= ףp=@?阙@Gz@Gz?2-؂@Gz@Q?bzkl@= ףp=@?TUUU@gfffff@Q? >>@(\@ ףp= ?oS'}'@Q@ \(?ɛ@zG@p= ף?$O@ ףp= @"33333?},8@233333@(\?t@](\@FQ?0` @GzG@zG?I@Q@x= ףp?M@(\@?>qq@@DzG?Z[@p= ף@(\?&DDDD@{Gz@ۣp= ?Mo-؂-@SQ@ Q?l@)\(@pfffff?@@?Gz?Gz??(\?(\??9 ףp=?9 ףp=??yQ?yQ?????Gz?Gz??x(\?x(\??<Q?<Q??(\?(\?????; ףp=?; ףp=??zG?zG??Q?Q??\(?\(????r= ףp???Gz???Q???S(\??????p= ף???@zG???Q???(\???833333???ԣp= ???Gz???Q???(\???`fffff???ףp= ???Gz???Gz?d[@q= ףp@ ףp= ?q@@ffffff?[1333@(\@(\?HI@Q@Q?B_ `@Gz@|Gz?Nuwww@> ףp=@ףp= ?8@gfffff@333333?iO@(\@(\?@!@p= ף@Q?'}@{Gz@HzG?>@RQ@p= ף?1H@)\(@?l@@\(\?~+؂-@ףp= @Q?$oBDDD@Gz@Gz?&Y[@Q@p= ףp?poq@\(\@o= ףp?&Y[@333333@o= ףp?$oBDDD@ ףp= @o= ףp?~+؂-@zG@p= ףp?l@Q@p= ףp?2H@(\@p= ףp?>@gfffff@r= ףp?'}@> ףp=@o= ףp?@!@Gz@q= ףp?iO@Q@o= ףp?8@(\@q= ףp?Luwww@@r= ףp?B_ `@q= ףp@r= ףp?HI@HzG@r= ףp?[1333@Q@r= ףp?q@(\@r= ףp?d[@@r= ףp?h@p= ף@p= ףp?ւ-@{Gz@p= ףp?=l@RQ@p= ףp?v@)\(@p= ףp?͒>@@p= ףp?*|'}@ףp= @p= ףp?^efff@Gz@p= ףp?ަNO@Q@o= ףp?878@\(\@o= ףp?7!"""@333333@o= ףp? ` @ ףp= @o= ףp?FI@zG@o= ףp?@Q@o= ףp?Xq@(\@n= ףp?S[@ffffff@q= ףp?阙@> ףp=@o= ףp?2-؂@Gz@Gz?bzkl@Gz@Q?TUUU@> ףp=@? >>@ffffff@R?oS'}'@(\@ףp= ?ɛ@Q@@\(?#O@zG@p= ף?},8@ ףp= @033333?t@333333@(\?1` @HzG@ZQ?I@ Q@zG?M@(\@g= ףp?>qq@@?Z[@p= ף@LzG?&DDDD@|Gz@(\?Lo-؂-@RQ@գp= ?l@(\(@ Q?@@m= ףp?dfffff?dfffff?f= ףp?Gz?Gz?p= ףp?)\?)\?p= ףp? ףp=? ףp=?p= ףp?Q?Q?p= ףp???p= ףp? Gz? Gz?p= ףp?`(\?`(\?p= ףp?PQ?PQ?p= ףp?(\?(\?p= ףp???p= ףp?0 ףp=?0 ףp=?p= ףp?zG?zG?p= ףp?Q?Q?p= ףp?0\(?0\(?s= ףp???u= ףp?e= ףp?e= ףp?Gz?u= ףp?u= ףp?Q?^= ףp?^= ףp?`(\?p= ףp?p= ףp??z= ףp?z= ףp?p= ף?p= ףp?p= ףp?KzG?m= ףp?m= ףp?Q?j= ףp?j= ףp?(\?{= ףp?{= ףp?333333?v= ףp?v= ףp?ڣp= ?]= ףp?]= ףp?zGz?m= ףp?m= ףp?Q?z= ףp?z= ףp?(\?e= ףp?e= ףp?hfffff?s= ףp?s= ףp? ףp= ?n= ףp?n= ףp?Gz?k= ףp?k= ףp?Gz?q@@ ףp= ?[1333@(\@ffffff?HI@Q@(\?B_ `@Gz@Q?Muwww@= ףp=@|Gz?8@hfffff@ףp= ?iO@(\@333333?@!@Q@(\?'}@p= ף@Q?>@{Gz@HzG?1H@RQ@p= ף?l@)\(@?~+؂-@@\(\?$oBDDD@ףp= @Q?&Y[@Gz@Gz?poq@Q@Gz?&Y[@\(\@Gz?$oBDDD@433333@Gz?~+؂-@ ףp= @Gz?l@zG@Gz?2H@Q@Gz?>@(\@Gz?'}@gfffff@Gz??!@= ףp=@Gz?iO@Gz@Gz?8@Q@Gz?Muwww@(\@Gz?B_ `@@Gz?HI@p= ףp@Gz?Z1333@GzG@Gz?q@Q@Gz?d[@(\@Gz?h@@Gz?ւ-@p= ף@Gz?=l@zGz@Gz?w@SQ@Gz?͒>@(\(@Gz?+|'}@@Gz?^efff@֣p= @Gz?ߦNO@Gz@Gz?878@Q@Gz?7!"""@](\@Gz? ` @433333@Gz?FI@ ףp= @Gz?@zG@Gz?Xq@Q@Gz?T[@(\@Gz?阙@gfffff@Gz?2-؂@= ףp=@Gz?azkl@Gz@Gz?TUUU@Gz@Q? >>@= ףp=@?oS'}'@gfffff@Q?ɛ@(\@ ףp= ?#O@Q@ \(?},8@zG@p= ף?t@ ףp= @>33333?1` @433333@(\?I@Q@FQ?M@(\@zG?>qq@@x= ףp?Z[@p= ף@?&DDDD@{Gz@DzG?Lo-؂-@QQ@(\?l@)\(@ۣp= ?@@Gz?%Q?%Q?Gz?_fffff?_fffff?Gz?Gz?Gz?Gz?(\?(\?Gz?F ףp=?F ףp=?Gz?Q?Q?Gz???Gz?Gz?Gz?Gz?(\?(\?Gz?DQ?DQ?Gz?)\?)\?Gz???Gz?D ףp=?D ףp=?Gz?zG?zG?Gz?Q?Q?Gz?%\(?%\(?Gz???Gz?v= ףp?v= ףp?Gz?Gz?Gz?Q?Gz?Gz?\(\?Gz?Gz?? Gz? Gz?p= ף?#Gz?#Gz?IzG?Gz?Gz?Q?Gz?Gz?(\? Gz? Gz?333333?Gz?Gz?p= ?Gz?Gz?~Gz?Gz?Gz?Q?Gz?Gz?(\?Gz?Gz?afffff?Gz?Gz?ףp= ?Gz?Gz?Gz?Gz?Gz?Gz?[1333@(\@ ףp= ?HI@Q@ffffff?B_ `@Gz@(\?Muwww@= ףp=@Q?8@ffffff@|Gz?iO@(\@ףp= ?@!@Q@633333?'}@@(\?>@p= ף@Q?1H@{Gz@HzG?l@RQ@p= ף?~+؂-@)\(@?$oBDDD@@\(\?&Y[@ףp= @Q?poq@Gz@Q?&Y[@Q@Q?$oBDDD@\(\@Q?~+؂-@333333@Q?l@ ףp= @Q?2H@zG@Q?>@Q@Q?'}@(\@Q?@!@gfffff@Q?iO@> ףp=@Q?8@Gz@Q?Muwww@Q@Q?B_ `@(\@Q?HI@@Q?[1333@q= ףp@Q?q@HzG@Q?d[@Q@Q?h@(\@Q?ւ-@@Q?=l@p= ף@Q?v@{Gz@Q?͒>@RQ@Q?*|'}@)\(@Q?^efff@@Q?ަNO@ףp= @Q?878@Gz@Q?7!"""@Q@Q? ` @\(\@Q?FI@333333@Q?@ ףp= @Q?Xq@zG@Q?S[@Q@Q?阙@(\@Q?2-؂@hfffff@Q?`zkl@< ףp=@Q?TUUU@Gz@Gz? >>@Gz@Q?nS'}'@< ףp=@?ʛ@hfffff@Q?"O@(\@:ףp= ?~,8@Q@\(?t@zG@p= ף?0` @ ףp= @$33333?I@Q@(\?M@(\@ZQ??qq@@zG?Z[@p= ף@g= ףp?&DDDD@zGz@?Lo-؂-@RQ@LzG?l@*\(@(\?@@Q?ƣp= ?ƣp= ?Q?Q?Q?Q?[fffff?[fffff?Q?Gz?Gz?Q?(\?(\?Q?- ףp=?- ףp=?Q?rQ?rQ?Q???Q?Gz?Gz?Q?p(\?p(\?Q?8Q?8Q?Q?(\?(\?Q???Q?9 ףp=?9 ףp=?Q?zG?zG?Q?Q?Q?Q?\(?\(?Q???Q?f= ףp?f= ףp?Q?Gz?Gz?Q?Q?Q?R(\?Q?Q??Q?Q?p= ף?Q?Q?GzG?Q?Q?Q?Q?Q?(\?Q?Q?>33333?Q?Q?ףp= ?Q?Q?xGz?Q?Q? Q?Q?Q?(\?Q?Q?gfffff?Q?Q?ףp= ?Q?Q?Gz?Q?Q?Gz?HI@Q@ ףp= ?B_ `@Gz@ffffff?Muwww@= ףp=@(\?8@gfffff@Q?iO@(\@|Gz?@!@Q@ףp= ?'}@zG@633333?>@@(\?2H@p= ף@Q?l@{Gz@HzG?~+؂-@RQ@p= ף?$oBDDD@)\(@?&Y[@@\(\?poq@ףp= @\(\?&Y[@Gz@\(\?$oBDDD@Q@\(\?~+؂-@\(\@](\?l@433333@](\?2H@ ףp= @](\?>@zG@\(\?'}@Q@\(\?@!@(\@\(\?iO@gfffff@\(\?8@= ףp=@\(\?Nuwww@Gz@\(\?B_ `@Q@\(\?HI@(\@\(\?Z1333@@\(\?q@p= ףp@](\?d[@GzG@](\?h@Q@](\?ւ-@(\@](\?=l@@](\?v@p= ף@^(\?͒>@|Gz@[(\?*|'}@QQ@](\?^efff@*\(@[(\?ަNO@@](\?878@أp= @[(\?7!"""@Gz@](\? ` @Q@](\?FI@](\@](\?@433333@](\?Xq@ ףp= @](\?T[@zG@](\?阙@Q@](\?2-؂@(\@\(\?bzkl@gfffff@\(\?TUUU@= ףp=@\(\? >>@Gz@Gz?oS'}'@Gz@Q?ɛ@= ףp=@?"O@gfffff@Q?},8@(\@ ףp= ?t@Q@ \(?1` @zG@p= ף?I@ ףp= @;33333?M@(\@(\?>qq@@FQ?Z[@p= ף@zG?&DDDD@{Gz@y= ףp?Mo-؂-@SQ@?l@)\(@PzG?@@Z(\?(\?(\?V(\?p= ?p= ?^(\?Q?Q?^(\?Ufffff?Ufffff?R(\?Gz?Gz?Z(\?(\?(\?Z(\? ףp=? ףp=?Z(\?Q?Q?Z(\???Z(\?Gz?Gz?Z(\?X(\?X(\?Z(\?LQ?LQ?Z(\?(\?(\?Z(\???Z(\?, ףp=?, ףp=?Z(\?zG?zG?Z(\?|Q?|Q?Z(\?+\(?+\(?V(\???^(\?`= ףp?`= ףp?^(\?Gz?Gz?R(\?Q?Q?X(\?X(\?X(\??p(\?p(\?p= ף?h(\?h(\?DzG?U(\?U(\?Q?\(\?\(\?(\?p(\?p(\?633333?d(\?d(\?֣p= ?M(\?M(\?Gz?`(\?`(\?Q?d(\?d(\?(\?\(\?\(\?`fffff?`(\?`(\?ףp= ?`(\?`(\?Gz?\(\?\(\?Gz?B_ `@Gz@ ףp= ?Muwww@= ףp=@ffffff?8@gfffff@(\?iO@(\@Q??!@Q@|Gz?'}@zG@ڣp= ?>@(\@633333?2H@@(\?l@p= ף@Q?}+؂-@{Gz@HzG?$oBDDD@RQ@p= ף?&Y[@)\(@?poq@@?&Y[@ףp= @?$oBDDD@Gz@?~+؂-@Q@?l@\(\@?2H@433333@?>@ ףp= @?'}@zG@?@!@Q@?iO@(\@?8@gfffff@?Muwww@> ףp=@?B_ `@Gz@?HI@Q@?[1333@(\@?q@@?d[@q= ףp@?i@HzG@?ւ-@Q@?=l@(\@?v@@?͒>@p= ף@?*|'}@{Gz@?^efff@RQ@?ަNO@)\(@?878@@?7!"""@ףp= @? ` @Gz@?FI@Q@?@\(\@?Xq@333333@?T[@ ףp= @?阙@zG@?2-؂@Q@?azkl@(\@?TUUU@ffffff@? >>@> ףp=@?oS'}'@Gz@Gz?ʛ@Gz@Q?#O@> ףp=@?},8@ffffff@R?t@(\@ףp= ?1` @Q@@\(?I@zG@p= ף?M@(\@$33333?>qq@@(\?Z[@p= ף@ZQ?&DDDD@|Gz@zG?Lo-؂-@RQ@h= ףp?l@(\(@?@@?0zG?0zG??(\?(\??p= ?p= ?? Q? Q??Pfffff?Pfffff??Gz?Gz??(\?(\??@ ףp=?@ ףp=??Q?Q?????Gz?Gz??(\?(\??@Q?@Q??)\?)\?????@ ףp=?@ ףp=??zG?zG??Q?Q?? \(? \(?????p= ףp?p= ףp??Gz?Gz??Q?Q??h(\?h(\????p= ף? ? ?HzG???Q???(\???033333???أp= ???xGz??? Q???(\???hfffff???ףp= ???Gz???Gz?Muwww@= ףp=@ ףp= ?8@gfffff@ffffff?iO@(\@(\??!@Q@Q?'}@zG@zGz?>@ ףp= @֣p= ?1H@(\@633333?l@@(\?+؂-@p= ף@Q?$oBDDD@{Gz@HzG?&Y[@RQ@p= ף?poq@)\(@p= ף?&Y[@@p= ף?$oBDDD@ףp= @p= ף?~+؂-@Gz@p= ף?l@Q@p= ף?2H@\(\@p= ף?>@433333@p= ף?'}@ ףp= @p= ף?@!@zG@p= ף?iO@Q@p= ף?8@(\@p= ף?Nuwww@gfffff@p= ף?B_ `@= ףp=@p= ף?HI@Gz@p= ף?Z1333@Q@p= ף?q@(\@p= ף?d[@@p= ף?h@p= ףp@p= ף?ւ-@GzG@p= ף?=l@Q@p= ף?v@(\@p= ף?͒>@@p= ף?*|'}@p= ף@p= ף?^efff@zGz@p= ף?ަNO@SQ@p= ף?878@(\(@p= ף?7!"""@@p= ף? ` @֣p= @p= ף?FI@Gz@p= ף?@Q@p= ף?Xq@](\@p= ף?T[@433333@p= ף?阙@ ףp= @p= ף?2-؂@zG@p= ף?bzkl@Q@p= ף?TUUU@(\@p= ף? >>@gfffff@p= ף?oS'}'@= ףp=@p= ף?ɛ@Gz@Gz?#O@Gz@Q?},8@= ףp=@?t@gfffff@Q?1` @(\@ ףp= ?I@Q@ \(?M@zG@p= ף?>qq@@<33333?Z[@p= ף@(\?&DDDD@{Gz@FQ?Ko-؂-@QQ@zG?l@)\(@y= ףp?@@p= ף? ? ?p= ף?=zG?=zG?p= ף?(\?(\?p= ף?p= ?p= ?p= ף?0Q?0Q?p= ף?afffff?afffff?p= ף?Gz?Gz?p= ף?)\?)\?p= ף?& ףp=?& ףp=?p= ף?Q?Q?p= ף???p= ף?(Gz?(Gz?p= ף?h(\?h(\?p= ף?TQ?TQ?p= ף?(\?(\?p= ף???p= ף?4 ףp=?4 ףp=?p= ף?zG?zG?p= ף?Q?Q?p= ף?!\(?!\(?p= ף???p= ף?= ףp?= ףp?p= ף?Gz?Gz?p= ף?Q?Q?p= ף?b(\?b(\?p= ף???p= ף?p= ף?p= ף?FzG?p= ף?p= ף?Q?p= ף?p= ף?(\?p= ף?p= ף?533333?p= ף?p= ף?ޣp= ?p= ף?p= ף?Gz?p= ף?p= ף?Q?p= ף?p= ף?(\?p= ף?p= ף?`fffff?p= ף?p= ף?ףp= ?p= ף?p= ף?Gz?p= ף?p= ף?Gz?8@gfffff@ ףp= ?iO@(\@ffffff??!@Q@(\?'}@zG@Q?>@ ףp= @zGz?1H@Q@ڣp= ?l@(\@633333?~+؂-@@(\?%oBDDD@p= ף@Q?&Y[@{Gz@HzG?poq@RQ@HzG?&Y[@)\(@HzG?$oBDDD@@HzG?~+؂-@ףp= @HzG?l@Gz@HzG?1H@Q@HzG?>@\(\@GzG?'}@433333@GzG??!@ ףp= @GzG?iO@zG@HzG?8@Q@HzG?Muwww@(\@HzG?B_ `@gfffff@IzG?HI@> ףp=@GzG?[1333@Gz@IzG?q@Q@HzG?d[@(\@HzG?h@@JzG?ւ-@q= ףp@JzG?=l@HzG@JzG?v@Q@JzG?͒>@(\@JzG?*|'}@@JzG?^efff@p= ף@HzG?ަNO@{Gz@HzG?878@RQ@HzG?7!"""@)\(@HzG? ` @@HzG?FI@ףp= @HzG?@Gz@HzG?Xq@Q@GzG?S[@\(\@GzG?阙@333333@GzG?2-؂@ ףp= @GzG?azkl@zG@GzG?TUUU@Q@GzG? >>@(\@IzG?pS'}'@hfffff@GzG?ț@< ףp=@IzG?$O@Gz@Gz?~,8@Gz@Q?t@< ףp=@?2` @hfffff@Q?I@(\@:ףp= ?M@Q@@\(?>qq@@p= ף?Z[@p= ף@%33333?&DDDD@zGz@(\?Lo-؂-@RQ@ZQ?l@*\(@zG?@@LzG?= ףp?= ףp?MzG???DzG?XzG?XzG?DzG?(\?(\?LzG?p= ?p= ?LzG?*Q?*Q?DzG?{fffff?{fffff?DzG?Gz?Gz?CzG?(\?(\?LzG?M ףp=?M ףp=?CzG?Q?Q?LzG???DzG?Gz?Gz?MzG?(\?(\?CzG?HQ?HQ?LzG?)\?)\?DzG???MzG?I ףp=?I ףp=?DzG?zG?zG?MzG?Q?Q?DzG?&\(?&\(?LzG???LzG?{= ףp?{= ףp?DzG?#Gz?#Gz?DzG?Q?Q?LzG?](\?](\?MzG? ? ?DzG?p= ף?p= ף?IzG?IzG?IzG?Q?BzG?BzG?(\?BzG?BzG?B33333?BzG?BzG?٣p= ?:zG?:zG?yGz?JzG?JzG? Q?AzG?AzG?(\?BzG?BzG?gfffff?6zG?6zG?ףp= ?JzG?JzG?Gz?2zG?2zG?Gz?iO@(\@ ףp= ??!@Q@ffffff?'}@zG@(\?>@ ףp= @Q?1H@333333@zGz?l@Q@֣p= ?~+؂-@(\@633333?%oBDDD@@(\?&Y[@p= ף@Q?poq@{Gz@Q?&Y[@RQ@Q?$oBDDD@)\(@Q?~+؂-@@Q?l@ףp= @Q?2H@Gz@Q?>@Q@Q?'}@\(\@Q?@!@433333@Q?iO@ ףp= @Q?8@zG@Q?Muwww@Q@Q?B_ `@(\@Q?HI@gfffff@Q?[1333@= ףp=@Q?q@Gz@Q?d[@Q@Q?i@(\@Q?ւ-@@Q?=l@p= ףp@Q?v@GzG@Q?͒>@Q@Q?*|'}@(\@Q?^efff@@Q?ަNO@p= ף@Q?878@|Gz@Q?7!"""@QQ@Q? ` @*\(@Q?EI@@Q?@أp= @Q?Xq@Gz@Q?T[@Q@Q?阙@](\@Q?2-؂@433333@Q?bzkl@ ףp= @Q?TUUU@zG@Q? >>@Q@Q?pS'}'@(\@Q?ʛ@gfffff@Q?#O@= ףp=@Q?},8@Gz@Gz?t@Gz@Q?1` @= ףp=@?I@gfffff@Q?M@(\@ ףp= ?>qq@Q@@\(?Z[@p= ף@p= ף?&DDDD@{Gz@;33333?Mo-؂-@SQ@(\?l@)\(@`Q?@@Q?zG?zG?Q?v= ףp?v= ףp?Q???Q?:zG?:zG?Q?|(\?|(\?Q?ߣp= ?ߣp= ?Q?Q?Q?Q?Tfffff?Tfffff?Q?Gz?Gz?Q?(\?(\?Q?: ףp=?: ףp=?Q?yQ?yQ?Q???Q?Gz?Gz?Q?x(\?x(\?Q?<Q?<Q?Q?(\?(\?Q???Q?< ףp=?< ףp=?Q?zG?zG?Q?Q?Q?Q?\(?\(?Q???Q?j= ףp?j= ףp?Q?Gz?Gz?Q?Q?Q?Q?j(\?j(\?Q???Q?p= ף?p= ף?Q?:zG?:zG?Q?Q?Q?(\?Q?Q?333333?R?R?ԣp= ?Q?Q?~Gz?Q?Q?Q?Q?Q?(\?Q?Q?afffff?Q?Q?ףp= ?Q?Q?Gz?Q?Q?Gz??!@Q@ ףp= ?'}@zG@ffffff?>@ ףp= @(\?2H@333333@Q?l@GzG@zGz?~+؂-@Q@ڣp= ?$oBDDD@(\@633333?&Y[@@(\?ooq@p= ף@(\?&Y[@{Gz@(\?$oBDDD@RQ@(\?~+؂-@)\(@(\?l@@(\?2H@ףp= @(\?>@Gz@(\?'}@Q@(\??!@\(\@(\?iO@333333@(\?8@ ףp= @(\?Muwww@zG@(\?B_ `@Q@(\?HI@(\@(\?[1333@gfffff@(\?q@> ףp=@(\?d[@Gz@(\?h@Q@(\?ւ-@(\@(\?=l@@(\?v@q= ףp@(\?͒>@HzG@(\?*|'}@Q@(\?^efff@(\@(\?ަNO@@(\?878@p= ף@(\?7!"""@{Gz@(\? ` @RQ@(\?FI@)\(@(\?@@(\?Xq@ףp= @(\?S[@Gz@(\?阙@Q@(\?2-؂@\(\@(\?azkl@333333@(\?TUUU@ ףp= @(\? >>@zG@(\?nS'}'@Q@(\?ɛ@(\@(\?#O@ffffff@(\?},8@> ףp=@(\?t@Gz@Gz?1` @Gz@Q?I@> ףp=@?M@ffffff@R?>qq@(\@ףp= ?Z[@p= ף@@\(?&DDDD@zGz@p= ף?Lo-؂-@RQ@%33333?l@(\(@(\?@@(\?TQ?TQ?(\?zG?zG?(\?o= ףp?o= ףp?(\???(\?GzG?GzG?(\?(\?(\?(\?ڣp= ?ڣp= ?(\?*Q?*Q?(\?dfffff?dfffff?(\?Gz?Gz?(\?)\?)\?(\? ףp=? ףp=?(\?Q?Q?(\???(\? Gz? Gz?(\?`(\?`(\?(\?PQ?PQ?(\?(\?(\?(\???(\?0 ףp=?0 ףp=?(\?zG?zG?(\?Q?Q?(\?\(?\(?(\???(\?{= ףp?{= ףp?(\? Gz? Gz?(\?Q?Q?(\?d(\?d(\?(\? ? ?(\?p= ף?p= ף?(\?6zG?6zG?(\?Q?Q?(\?(\?(\?233333?(\?(\?֣p= ?(\?(\?yGz?(\?(\?Q?(\?(\?(\?(\?(\?hfffff?(\?(\? ףp= ?(\?(\?Gz?(\?(\?Gz?'}@zG@ ףp= ?>@ ףp= @ffffff?2H@333333@(\?l@](\@Q?~+؂-@GzG@zGz?$oBDDD@Q@֣p= ?&Y[@(\@233333?ooq@@633333?&Y[@p= ף@433333?$oBDDD@{Gz@433333?~+؂-@RQ@433333?l@)\(@433333?2H@@433333?>@ףp= @433333?'}@Gz@433333??!@Q@433333?iO@\(\@433333?8@433333@433333?Nuwww@ ףp= @433333?B_ `@zG@433333?HI@Q@433333?[1333@(\@433333?q@gfffff@433333?d[@= ףp=@433333?i@Gz@433333?ւ-@Q@433333?=l@(\@433333?v@@533333?͒>@p= ףp@633333?*|'}@GzG@633333?^efff@Q@633333?ަNO@(\@633333?878@@633333?7!"""@p= ף@333333? ` @zGz@533333?FI@SQ@333333?@(\(@533333?Xq@@333333?S[@֣p= @433333?阙@Gz@433333?2-؂@Q@533333?bzkl@](\@533333?TUUU@433333@533333? >>@ ףp= @533333?pS'}'@zG@433333?ɛ@Q@433333?#O@(\@433333?},8@gfffff@433333?t@= ףp=@433333?1` @Gz@Gz?I@Gz@Q?M@= ףp=@?>qq@gfffff@Q?Z[@(\@ףp= ?&DDDD@yGz@@\(?Mo-؂-@SQ@p= ף?l@)\(@;33333?@@633333?(\?(\?.33333?fQ?fQ?733333?{G?{G??33333?h= ףp?h= ףp?733333???/33333?TzG?TzG?633333?(\?(\?>33333?գp= ?գp= ?733333?&Q?&Q?/33333?vfffff?vfffff?733333?Gz?Gz?733333?(\?(\?733333?F ףp=?F ףp=?733333?Q?Q?733333???733333?Gz?Gz?733333?(\?(\?733333?DQ?DQ?733333?)\?)\?733333???733333?E ףp=?E ףp=?733333?zG?zG?733333?Q?Q?733333?%\(?%\(??33333???733333?u= ףp?u= ףp?.33333?Gz?Gz?633333?Q?Q??33333?^(\?^(\?733333???/33333?p= ף?p= ף?733333?FzG?FzG?>33333?Q?Q?633333?(\?(\?833333?(33333?(33333?p= ?033333?033333?Gz?>33333?>33333?Q?(33333?(33333?(\?833333?833333?`fffff?"33333?"33333?ףp= ?@33333?@33333?Gz?33333?33333?Gz?>@ ףp= @ ףp= ?2H@333333@ffffff?l@](\@(\?~+؂-@q= ףp@Q?%oBDDD@GzG@zGz?&Y[@Q@֣p= ?ooq@(\@ڣp= ?&Y[@@ڣp= ?#oBDDD@p= ף@أp= ?}+؂-@{Gz@أp= ?l@RQ@أp= ?2H@)\(@أp= ?>@@أp= ?'}@ףp= @أp= ?@!@Gz@أp= ?iO@Q@أp= ?8@\(\@أp= ?Muwww@433333@أp= ?B_ `@ ףp= @أp= ?HI@zG@أp= ?[1333@Q@أp= ?q@(\@أp= ?d[@gfffff@أp= ?h@> ףp=@أp= ?ւ-@Gz@أp= ?=l@Q@أp= ?v@(\@أp= ?͒>@@ڣp= ?*|'}@q= ףp@ڣp= ?^efff@HzG@ڣp= ?ަNO@Q@ڣp= ?878@(\@ڣp= ?7!"""@@ڣp= ? ` @p= ף@أp= ?FI@{Gz@أp= ?@RQ@أp= ?Xq@)\(@أp= ?T[@@أp= ?阙@ףp= @أp= ?2-؂@Gz@أp= ?azkl@Q@ףp= ?TUUU@\(\@ףp= ? >>@333333@ףp= ?nS'}'@ ףp= @أp= ?ɛ@zG@أp= ?#O@Q@أp= ?},8@(\@٣p= ?t@hfffff@ףp= ?/` @< ףp=@٣p= ?I@Gz@Gz?M@Gz@Q?=qq@< ףp=@?Z[@hfffff@R?&DDDD@zGz@ףp= ?Lo-؂-@RQ@@\(?l@(\(@p= ף?@@֣p= ?)33333?)33333?ңp= ?(\?(\?ڣp= ?Q?Q?ޣp= ?zG?zG?֣p= ?n= ףp?n= ףp?ңp= ???ڣp= ?(zG?(zG?ޣp= ?i(\?i(\?֣p= ?ڣp= ?ڣp= ?ңp= ?Q?Q?ڣp= ?Efffff?Efffff?ޣp= ?Gz?Gz?ңp= ?(\?(\?ޣp= ?- ףp=?- ףp=?ңp= ?rQ?rQ?ޣp= ???ңp= ?Gz?Gz?ޣp= ?p(\?p(\?ңp= ?8Q?8Q?ޣp= ?(\?(\?ңp= ???ޣp= ?9 ףp=?9 ףp=?ңp= ?zG?zG?ޣp= ?Q?Q?ޣp= ?\(?\(?֣p= ???ңp= ?e= ףp?e= ףp?ڣp= ?Gz?Gz?ޣp= ?Q?Q?֣p= ?d(\?d(\?ңp= ???ڣp= ?p= ף?p= ף?ޣp= ?=zG?=zG?֣p= ?Q?Q?ңp= ?(\?(\?أp= ?33333?33333?ףp= ?ףp= ?ףp= ?uGz?p= ?p= ?Q?ߣp= ?ߣp= ?(\?ޣp= ?ޣp= ?gfffff?գp= ?գp= ?ףp= ?p= ?p= ?Gz?ϣp= ?ϣp= ?Gz?1H@333333@ ףp= ?l@\(\@ffffff?~+؂-@Q@(\?$oBDDD@q= ףp@ Q?&Y[@HzG@|Gz?poq@Q@|Gz?&Y[@(\@|Gz?$oBDDD@@|Gz?}+؂-@p= ף@zGz?l@{Gz@zGz?1H@RQ@zGz?>@)\(@zGz?'}@@{Gz?@!@أp= @{Gz?iO@Gz@zGz?8@Q@zGz?Muwww@\(\@zGz?B_ `@433333@zGz?HI@ ףp= @zGz?[1333@zG@zGz?q@Q@zGz?d[@(\@zGz?i@hfffff@{Gz?ւ-@> ףp=@zGz?=l@Gz@zGz?v@Q@zGz?͒>@(\@{Gz?*|'}@@|Gz?^efff@r= ףp@|Gz?ަNO@HzG@|Gz?878@ Q@|Gz?7!"""@(\@|Gz? ` @@|Gz?FI@p= ף@zGz?@|Gz@zGz?Xq@RQ@zGz?T[@*\(@zGz?阙@@zGz?2-؂@أp= @zGz?bzkl@Gz@zGz?TUUU@Q@zGz? >>@\(\@zGz?oS'}'@433333@zGz?ɛ@ ףp= @zGz?#O@zG@zGz?},8@Q@zGz?t@(\@{Gz?2` @hfffff@zGz?I@= ףp=@{Gz?M@Gz@}Gz??qq@Gz@Q?Z[@< ףp=@ݙ?&DDDD@gfffff@Q?Lo-؂-@RQ@=ףp= ?l@*\(@<\(?@@Gz?p= ף?p= ף?|Gz?C33333?C33333?tGz?(\?(\?|Gz?DQ?DQ?Gz?zG?zG?|Gz?= ףp?= ףp?tGz???|Gz?BzG?BzG?Gz?(\?(\?|Gz?p= ?p= ?tGz?3Q?3Q?|Gz?cfffff?cfffff?Gz?Gz?Gz?uGz?)\?)\?Gz? ףp=? ףp=?uGz?Q?Q?Gz???uGz?$Gz?$Gz?Gz?d(\?d(\?uGz?RQ?RQ?Gz?(\?(\?uGz???Gz?3 ףp=?3 ףp=?uGz?zG?zG?Gz?Q?Q?Gz?!\(?!\(?|Gz???tGz?z= ףp?z= ףp?|Gz?Gz?Gz?Gz?Q?Q?|Gz?b(\?b(\?tGz? ? ?|Gz?p= ף?p= ף?Gz??zG??zG?|Gz?Q?Q?tGz?(\?(\?|Gz?433333?433333?Gz?٣p= ?٣p= ?tGz?lGz?lGz?Q?Gz?Gz?(\?fGz?fGz?cfffff?yGz?yGz?ףp= ?nGz?nGz?Gz?rGz?rGz?Gz?l@\(\@ ףp= ?~+؂-@Q@jfffff?$oBDDD@@(\?&Y[@q= ףp@ Q?poq@HzG@Q?&Y[@Q@ Q?$oBDDD@(\@ Q?~+؂-@@ Q?l@p= ף@Q?2H@{Gz@Q?>@RQ@Q?'}@)\(@Q??!@@Q?iO@֣p= @Q?8@Gz@Q?Muwww@Q@Q?B_ `@[(\@Q?HI@433333@Q?[1333@ ףp= @Q?q@zG@Q?d[@Q@Q?i@(\@Q?ւ-@ffffff@Q?=l@> ףp=@Q?v@Gz@Q?͒>@Q@Q?*|'}@(\@Q?^efff@@ Q?ަNO@p= ףp@ Q?878@HzG@ Q?7!"""@Q@ Q? ` @(\@ Q?FI@@ Q?@p= ף@Q?Xq@zGz@Q?S[@RQ@Q?阙@(\(@Q?2-؂@@Q?azkl@֣p= @Q?TUUU@Gz@Q? >>@Q@Q?oS'}'@\(\@Q?ț@233333@Q?"O@ ףp= @Q?|,8@zG@Q?t@Q@Q?0` @(\@Q?I@ffffff@Q?M@= ףp=@Q?>qq@Gz@Gz?Z[@Gz@<Q?&DDDD@> ףp=@?Lo-؂-@RQ@Q?l@(\(@ ףp= ?@@Q?\(?\(?Q?p= ף?p= ף?Q?j33333?j33333?Q?(\?(\?"Q?UQ?UQ?Q?{G?{G?Q?= ףp?= ףp?Q???"Q?OzG?OzG?Q?(\?(\?Q?p= ?p= ?Q?-Q?-Q?"Q?tfffff?tfffff?Q?Gz?Gz?Q?(\?(\?Q?L ףp=?L ףp=?Q?Q?Q?Q???Q? Gz? Gz?Q?(\?(\?Q?FQ?FQ?Q?)\?)\?Q???Q?F ףp=?F ףp=?Q?zG?zG?Q?Q?Q?Q?&\(?&\(?Q???Q?v= ףp?v= ףp?"Q?$Gz?$Gz?Q?Q?Q?Q?V(\?V(\?Q???"Q?p= ף?p= ף?Q?EzG?EzG?Q?Q?Q?Q?(\?(\?#Q?J33333?J33333?Q?p= ?p= ?Q?uGz?uGz?$Q?Q?Q?(\?Q?Q?mfffff?Q?Q? ףp= ?%Q?%Q?Gz? Q? Q?Gz?~+؂-@Q@ ףp= ?$oBDDD@Gz@jfffff?&Y[@@(\?poq@q= ףp@(\?&Y[@GzG@(\?#oBDDD@Q@(\?~+؂-@(\@(\?l@@(\?1H@p= ף@(\?>@{Gz@(\?'}@RQ@(\??!@)\(@(\?iO@@(\?8@أp= @(\?Nuwww@Gz@(\?B_ `@Q@(\?HI@](\@(\?\1333@433333@(\?q@ ףp= @(\?d[@zG@(\?j@Q@(\?ւ-@(\@(\?=l@hfffff@(\?w@> ףp=@(\?͒>@Gz@(\?*|'}@Q@(\?^efff@(\@(\?ަNO@@(\?878@r= ףp@(\?7!"""@HzG@(\? ` @ Q@(\?FI@(\@(\?@@(\?Xq@p= ף@(\?T[@|Gz@(\?阙@RQ@(\?2-؂@*\(@(\?bzkl@@(\?TUUU@أp= @(\? >>@Gz@(\?pS'}'@Q@(\?ɛ@\(\@(\?#O@433333@(\?},8@ ףp= @(\?t@zG@(\?1` @Q@(\?I@(\@(\?M@gfffff@(\??qq@? ףp=@(\?Z[@Gz@Gz?&DDDD@Gz@Q?Lo-؂-@= ףp=@?l@*\(@Q?@@(\?֣p= ?֣p= ?(\?D\(?D\(?(\?bp= ף?bp= ף?(\?B33333?B33333?(\?(\?(\?(\?Q?Q?(\?zG?zG?(\?= ףp?= ףp?(\???(\?"zG?"zG?(\?r(\?r(\?(\?p= ?p= ?(\?Q?Q?(\?Bfffff?Bfffff?(\?Gz?Gz?(\?(\?(\?(\?4 ףp=?4 ףp=?(\?tQ?tQ?(\???(\?Gz?Gz?(\?t(\?t(\?(\?:Q?:Q?(\?(\?(\?(\???(\?: ףp=?: ףp=?(\?zG?zG?(\?Q?Q?(\?\(?\(?(\???(\?j= ףp?j= ףp?(\?Gz?Gz?(\?Q?Q?(\?c(\?c(\?(\???(\?p= ף?p= ף?(\?;zG?;zG?(\?Q?Q?(\?(\?(\?(\?!33333?!33333?(\?ۣp= ?ۣp= ?(\?kGz?kGz?(\?Q?Q?(\?(\?(\?efffff?(\?(\?ףp= ?(\?(\?Gz?(\?(\?Gz?$oBDDD@Gz@ףp= ?&Y[@(\@ffffff?poq@@ffffff?&Y[@p= ףp@ffffff?$oBDDD@GzG@ffffff?~+؂-@Q@hfffff?l@(\@hfffff?2H@@hfffff?>@p= ף@ffffff?'}@{Gz@ffffff?@!@RQ@ffffff?iO@)\(@ffffff?8@@ffffff?Muwww@֣p= @ffffff?B_ `@Gz@ffffff?HI@Q@ffffff?Z1333@[(\@ffffff?q@433333@ffffff?d[@ ףp= @ffffff?h@zG@ffffff?ւ-@Q@ffffff?=l@(\@ffffff?w@ffffff@ffffff?͒>@> ףp=@ffffff?*|'}@Gz@ffffff?^efff@Q@ffffff?ަNO@(\@ffffff?778@@hfffff?7!"""@p= ףp@hfffff? ` @HzG@hfffff?FI@Q@hfffff?@(\@hfffff?Xq@@hfffff?S[@p= ף@ffffff?阙@zGz@ffffff?2-؂@RQ@ffffff?azkl@(\(@ffffff?TUUU@@ffffff? >>@֣p= @ffffff?oS'}'@Gz@ffffff?ɛ@Q@ffffff?#O@\(\@ffffff?|,8@233333@ffffff?t@ ףp= @ffffff?0` @zG@efffff?I@Q@ffffff?M@(\@ffffff??qq@gfffff@ffffff?Z[@< ףp=@ffffff?&DDDD@Gz@}Gz?Mo-؂-@Gz@Q?l@(\(@?@@lfffff?GQ?GQ?]fffff?ףp= ?ףp= ?lfffff?[(?[(?^fffff?p= ף?p= ף?efffff?>33333?>33333?jfffff?(\?(\?dfffff?=Q?=Q?]fffff?zG?zG?dfffff?|= ףp?|= ףp?kfffff???dfffff?>zG?>zG?_fffff?(\?(\?dfffff?ޣp= ?ޣp= ?jfffff?#Q?#Q?efffff?^fffff?^fffff?]fffff?Gz?Gz?]fffff?)\?)\?kfffff? ףp=? ףp=?]fffff?Q?Q?lfffff???]fffff?Gz?Gz?lfffff?\(\?\(\?]fffff?NQ?NQ?lfffff?(\?(\?]fffff???kfffff?/ ףp=?/ ףp=?]fffff?zG?zG?lfffff?Q?Q?_fffff?\(?\(?efffff???jfffff?z= ףp?z= ףp?dfffff? Gz? Gz?]fffff?Q?Q?dfffff?](\?](\?kfffff? ? ?dfffff?p= ף?p= ף?]fffff?(zG?(zG?dfffff?Q?Q?kfffff?(\?(\?efffff?-33333?-33333?]fffff?ǣp= ?ǣp= ?]fffff?vGz?vGz?kfffff?Q?Q?]fffff?(\?(\?kfffff?cfffff?cfffff? ףp= ?efffff?efffff?Gz?kfffff?kfffff?Gz?&Y[@ףp= @ ףp= ?poq@(\@ ףp= ?&Y[@@ ףp= ?#oBDDD@p= ףp@ ףp= ?~+؂-@GzG@ ףp= ?l@Q@ ףp= ?2H@(\@ ףp= ?>@@ ףp= ?'}@p= ף@ ףp= ??!@{Gz@ ףp= ?iO@RQ@ ףp= ?8@)\(@ ףp= ?Muwww@@ ףp= ?B_ `@أp= @ ףp= ?HI@Gz@ ףp= ?\1333@Q@ ףp= ?q@](\@ ףp= ?d[@433333@ ףp= ?i@ ףp= @ ףp= ?ւ-@zG@ ףp= ?=l@Q@ ףp= ?w@(\@ ףp= ?͒>@hfffff@ ףp= ?+|'}@> ףp=@ ףp= ?^efff@Gz@ ףp= ?ަNO@Q@ ףp= ?878@(\@ ףp= ?7!"""@@ ףp= ? ` @r= ףp@ ףp= ?FI@HzG@ ףp= ?@ Q@ ףp= ?Xq@(\@ ףp= ?T[@@ ףp= ?阙@p= ף@ ףp= ?2-؂@|Gz@ ףp= ?bzkl@RQ@ ףp= ?TUUU@*\(@ ףp= ? >>@@ ףp= ?oS'}'@أp= @ ףp= ?ɛ@Gz@ ףp= ?#O@Q@ ףp= ?},8@\(\@ ףp= ?t@433333@ ףp= ?1` @ ףp= @ ףp= ?I@zG@ ףp= ?M@Q@ ףp= ?>qq@(\@ ףp= ?Z[@hfffff@ ףp= ?&DDDD@= ףp=@ ףp= ?Mo-؂-@Gz@}Gz?l@Gz@Q?@@ףp= ?v?v?ףp= ?Q?Q?ףp= ?7ףp= ?7ףp= ?ףp= ?R\(?R\(?ףp= ?p= ף?p= ף? ףp= ?833333?833333?ףp= ?(\?(\? ףp= ?OQ?OQ?ףp= ?{G?{G? ףp= ?t= ףp?t= ףp?ףp= ??? ףp= ?KzG?KzG?ףp= ?(\?(\? ףp= ?أp= ?أp= ?ףp= ?Q?Q? ףp= ?nfffff?nfffff?ףp= ?Gz?Gz?ףp= ?(\?(\?ףp= ?@ ףp=?@ ףp=?ףp= ?Q?Q?ףp= ???ףp= ?Gz?Gz?ףp= ?(\?(\?ףp= ?BQ?BQ?ףp= ?)\?)\?ףp= ???ףp= ?D ףp=?D ףp=?ףp= ?zG?zG?ףp= ?Q?Q?ףp= ?!\(?!\(? ףp= ???ףp= ?v= ףp?v= ףp? ףp= ?Gz?Gz?ףp= ?Q?Q? ףp= ?X(\?X(\?ףp= ??? ףp= ?p= ף?p= ף?ףp= ??zG??zG? ףp= ?Q?Q?ףp= ?(\?(\? ףp= ?C33333?C33333?ףp= ?ޣp= ?ޣp= ?ףp= ?nGz?nGz?ףp= ?+Q?+Q?ףp= ?(\?(\?ףp= ?{fffff?{fffff?ףp= ?֣p= ?֣p= ?Gz?֣p= ?֣p= ?Gz?poq@Q@Gz?&Y[@(\@Gz?#oBDDD@@Gz?}+؂-@p= ףp@Gz?l@GzG@Gz?1H@Q@Gz?>@(\@Gz?'}@@Gz??!@p= ף@Gz?iO@{Gz@Gz?8@RQ@Gz?Muwww@)\(@Gz?B_ `@@Gz?HI@֣p= @Gz?Z1333@Gz@Gz?q@Q@Gz?d[@[(\@Gz?i@433333@Gz?ւ-@ ףp= @Gz?=l@zG@Gz?v@Q@Gz?͒>@(\@Gz?*|'}@ffffff@Gz?^efff@> ףp=@Gz?ަNO@Gz@Gz?878@Q@Gz?7!"""@(\@Gz? ` @@Gz?FI@p= ףp@Gz?@HzG@Gz?Xq@Q@Gz?S[@(\@Gz?阙@@Gz?2-؂@p= ף@Gz?azkl@zGz@Gz?TUUU@RQ@Gz? >>@(\(@Gz?nS'}'@@Gz?ɛ@ףp= @Gz?#O@Gz@Gz?},8@Q@Gz?t@\(\@Gz?0` @233333@Gz?I@ ףp= @Gz?M@zG@Gz?>qq@Q@Gz?Z[@(\@Gz?&DDDD@ffffff@Gz?Lo-؂-@= ףp=@Gz?l@Gz@Gz?@@Gz?Q?Q?Gz?|?|?Gz?Q?Q?Gz?֣p= ?֣p= ?Gz?=\(?=\(?Gz?^p= ף?^p= ף?Gz?33333?33333?Gz?(\?(\?Gz?%Q?%Q?Gz?zG?zG?Gz?Z= ףp?Z= ףp?Gz???Gz?,zG?,zG?Gz?n(\?n(\?Gz?ңp= ?ңp= ?Gz? Q? Q?Gz?Gfffff?Gfffff?Gz?Gz?Gz?Gz?(\?(\?Gz?- ףp=?- ףp=?Gz?mQ?mQ?Gz???Gz?Gz?Gz?Gz?l(\?l(\?Gz?6Q?6Q?Gz?(\?(\?Gz???Gz?6 ףp=?6 ףp=?Gz?zG?zG?Gz?Q?Q?Gz?\(?\(?Gz???Gz?e= ףp?e= ףp?Gz?Gz?Gz?Gz?Q?Q?Gz?d(\?d(\?Gz???Gz?p= ף?p= ף?Gz?5zG?5zG?Gz?Q?Q?Gz?(\?(\?Gz?33333?33333?Gz?գp= ?գp= ?Gz?dGz?dGz?Gz?Q?Q?Gz?(\?(\?Gz?dfffff?dfffff?Gz?֣p= ?֣p= ?Gz?Gz?Gz?Gz?Gz?Gz?Gz? ףp= ? ףp= ?Gz?ffffff?ffffff?Gz?(\?(\?Gz?Q?Q?Gz?{Gz?{Gz?Gz?ףp= ?ףp= ?Gz?433333?433333?Gz?(\?(\?Gz?Q?Q?Gz?HzG?HzG?Gz?p= ף?p= ף?Gz???Gz?\(\?\(\?Gz?Q?Q?Gz?Gz?Gz?Gz?p= ףp?p= ףp?Gz???Gz?)\(?)\(?Gz?Q?Q?Gz?zG?zG?Gz?> ףp=?> ףp=?Gz???Gz?(\?(\?Gz?RQ?RQ?Gz?\(\?\(\?Gz?Gz?Gz?Gz???Gz?Q?Q?Gz?> ףp=?> ףp=?Gz?(\?(\?Gz?Gz?Gz?Gz?ffffff?ffffff?Gz?Q?Q?Gz?أp= ?أp= ?Gz?(\?(\?Gz?HzG?HzG?Gz???Gz?p= ףp?p= ףp?Gz?zG?zG?Gz?PQ?PQ?Gz?(\?(\?Gz?433333?433333?Gz?p= ף?p= ף?Gz?&\(?&\(?Gz?ףp= ?ףp= ?Gz?Q?Q?Gz???Gz?Q?Q?Gz?Gz?Gz?Gz?{o^M@Gz@Gz?"߼@> ףp=@Gz?pN@gfffff@Gz?Ey5@(\@Gz?fׂ-؂@Q@Gz?h6@zG@Gz?Z @ ףp= @Gz?Ջ|j@333333@Gz?PQ@\(\@Gz?ˮ[@Q@Gz?F@S@Gz@Gz?:m@أp= @Gz?;c@@Gz?*;@)\(@Gz?0@RQ@Gz? @{Gz@Gz?&gE#@p= ף@Gz?: ףp@@Gz?̊F@(\@Gz?] ` @Q@Gz?%X@HzG@Gz? @p= ףp@Gz?|@@Gz? t@@(\@Gz?48@Q@Gz?vS@Gz@Gz?W(@> ףp=@Gz?l2Tv@ffffff@Gz?z@(\@Gz?`  @Q@Gz?ܝo^ @zG@Gz?U/ͫ @ ףp= @Gz?_, @333333@Gz?JRϊF @\(\@Gz?> @Q@Gz?@uG @Gz@Gz?. @ףp= @Gz?6| @@Gz?)b @)\(@Gz?,l @SQ@Gz?Ld @{Gz@Gz?!K~ @p= ף@Gz?o @@Gz?*;L @(\@Gz? @Q@Gz? $ @HzG@Gz?xV4@q= ףp@Gz?G贁@@Gz?|؛W@(\@Gz?iq@Q@Gz? ףp= ? ףp= ? ףp= ? ףp= ? ףp= ? ףp= ?gfffff?gfffff? ףp= ?(\?(\? ףp= ?Q?Q? ףp= ?|Gz?|Gz? ףp= ?أp= ?أp= ? ףp= ?433333?433333? ףp= ?(\?(\? ףp= ?Q?Q? ףp= ?HzG?HzG? ףp= ?p= ף?p= ף? ףp= ??? ףp= ?\(\?\(\? ףp= ?Q?Q? ףp= ?Gz?Gz? ףp= ?q= ףp?q= ףp? ףp= ??? ףp= ?*\(?*\(? ףp= ?Q?Q? ףp= ?zG?zG? ףp= ?> ףp=?> ףp=? ףp= ??? ףp= ?(\?(\? ףp= ?RQ?RQ? ףp= ?\(\?\(\? ףp= ?Gz?Gz? ףp= ??? ףp= ?Q?Q? ףp= ?@ ףp=?@ ףp=? ףp= ?(\?(\? ףp= ?Gz?Gz? ףp= ?hfffff?hfffff? ףp= ? Q? Q? ףp= ?أp= ?أp= ? ףp= ?(\?(\? ףp= ?GzG?GzG? ףp= ??? ףp= ?n= ףp?n= ףp? ףp= ?zG?zG? ףp= ?NQ?NQ? ףp= ?(\?(\? ףp= ?633333?633333? ףp= ?p= ף?p= ף? ףp= ?,\(?,\(? ףp= ? ףp= ? ףp= ? ףp= ?Q?Q? ףp= ??? ףp= ?Q?Q?pGz?zo^M@Gz@ ףp= ?"߼@Gz@ ףp= ?pN@> ףp=@ ףp= ?Ey5@gfffff@ ףp= ?fׂ-؂@(\@ ףp= ?h6@Q@ ףp= ?\ @zG@ ףp= ?֋|j@ ףp= @ ףp= ?PQ@333333@ ףp= ?ˮ[@\(\@ ףp= ?F@S@Q@ ףp= ?:m@Gz@ ףp= ?;c@ףp= @ ףp= ?*;@@ ףp= ?0@)\(@ ףp= ? @RQ@ ףp= ?&gE#@{Gz@ ףp= ?: ףp@p= ף@ ףp= ?̊F@@ ףp= ?] ` @(\@ ףp= ?%X@Q@ ףp= ? @HzG@ ףp= ?|@p= ףp@ ףp= ? t@@@ ףp= ?48@(\@ ףp= ?vS@Q@ ףp= ?W(@Gz@ ףp= ?l2Tv@> ףp=@ ףp= ?z@gfffff@ ףp= ?b  @(\@ ףp= ?ܝo^ @Q@ ףp= ?V/ͫ @zG@ ףp= ?_, @ ףp= @ ףp= ?LRϊF @433333@ ףp= ?> @\(\@ ףp= ?@uG @Q@ ףp= ?. @Gz@ ףp= ?6| @أp= @ ףp= ?)b @@ ףp= ?,l @*\(@ ףp= ?Ld @SQ@ ףp= ?"K~ @{Gz@ ףp= ?o @p= ף@ ףp= ?*;L @@ ףp= ? @(\@ ףp= ? $ @Q@ ףp= ?xV4@HzG@ ףp= ?G贁@q= ףp@ ףp= ?|؛W@@ ףp= ?iq@(\@Gz?z؛W@ףp= @Gz?ffffff?ffffff? ףp= ?gfffff?gfffff?gfffff?gfffff?gfffff?ffffff?(\?(\?efffff?Q?Q?efffff?zGz?zGz?gfffff?أp= ?أp= ?gfffff?433333?433333?gfffff?(\?(\?ffffff?Q?Q?hfffff?IzG?IzG?hfffff?p= ף?p= ף?ffffff???ffffff?\(\?\(\?ffffff?Q?Q?ffffff?Gz?Gz?ffffff?q= ףp?q= ףp?ffffff???ffffff?(\(?(\(?ffffff?Q?Q?ffffff?zG?zG?ffffff?> ףp=?> ףp=?gfffff???ffffff?(\?(\?ffffff?RQ?RQ?ffffff?\(\?\(\?ffffff?Gz?Gz?hfffff???hfffff?Q?Q?hfffff?? ףp=?? ףp=?hfffff?(\?(\?hfffff?Gz?Gz?hfffff?gfffff?gfffff?hfffff?Q?Q?hfffff?٣p= ?٣p= ?hfffff?(\?(\?hfffff?IzG?IzG?hfffff???hfffff?r= ףp?r= ףp?hfffff?zG?zG?hfffff?RQ?RQ?gfffff?(\?(\?gfffff?533333?533333?gfffff?p= ף?p= ף?gfffff?*\(?*\(?gfffff? ףp= ? ףp= ?gfffff?Q?Q?gfffff???Q?{o^M@)\(@hGz?"߼@Gz@ffffff?pN@Gz@ffffff?Ey5@> ףp=@ffffff?fׂ-؂@gfffff@ffffff?h6@(\@ffffff?\ @Q@ffffff?֋|j@zG@ffffff?PQ@ ףp= @ffffff?ʮ[@333333@ffffff?E@S@\(\@ffffff?:m@Q@ffffff?:c@Gz@ffffff?*;@ףp= @ffffff?0@@ffffff? @)\(@ffffff?&gE#@RQ@ffffff?: ףp@{Gz@ffffff?̊F@p= ף@ffffff?] ` @@ffffff?%X@(\@ffffff? @Q@ffffff?|@HzG@ffffff? t@@p= ףp@ffffff?48@@ffffff?vS@(\@ffffff?W(@Q@gfffff?l2Tv@Gz@gfffff?z@> ףp=@gfffff?b  @gfffff@gfffff?ݝo^ @(\@ffffff?W/ͫ @Q@ffffff?_, @zG@ffffff?LRϊF @ ףp= @ffffff?> @433333@ffffff?AuG @](\@ffffff?. @Q@ffffff?7| @Gz@ffffff?)b @أp= @gfffff?-l @@gfffff?Ld @*\(@gfffff?"K~ @TQ@gfffff?o @|Gz@gfffff?*;L @p= ף@gfffff? @@gfffff? $ @(\@gfffff?xV4@Q@gfffff?G贁@HzG@gfffff?|؛W@q= ףp@gfffff?iq@@ףp= ?z؛W@(\@Gz?G贁@Gz@Gz?(\?(\? ףp= ?(\?(\?ffffff?(\?(\?(\?(\?(\?(\? Q? Q?(\?|Gz?|Gz?(\?أp= ?أp= ?(\?433333?433333?(\?(\?(\?(\?Q?Q?(\?HzG?HzG?(\?p= ף?p= ף?(\???(\?](\?](\?(\?Q?Q?(\?Gz?Gz?(\?r= ףp?r= ףp?(\???(\?*\(?*\(?(\?Q?Q?(\?zG?zG?(\?> ףp=?> ףp=?(\???(\?(\?(\?(\?RQ?RQ?(\?\(\?\(\?(\?Gz?Gz?(\???(\?Q?Q?(\?> ףp=?> ףp=?(\?(\?(\?(\?Gz?Gz?(\?ffffff?ffffff?(\?Q?Q?(\?٣p= ?٣p= ?(\?(\?(\?(\?IzG?IzG?(\???(\?r= ףp?r= ףp?(\?zG?zG?(\?QQ?QQ?(\?(\?(\?(\?433333?433333?(\?p= ף?p= ף?(\?(\(?(\(?(\?ףp= ?ףp= ?(\?Q?Q??{o^M@)\(@Q?"߼@= ףp=@`Gz?oN@Gz@(\?Ey5@Gz@(\?fׂ-؂@> ףp=@(\?h6@gfffff@(\?\ @(\@(\?׋|j@Q@(\?RQ@zG@(\?̮[@ ףp= @(\?F@S@433333@(\?:m@](\@(\? ףp=@(\?ܝo^ @gfffff@(\?W/ͫ @(\@(\?_, @Q@(\?LRϊF @zG@(\?> @ ףp= @(\?AuG @433333@(\?. @](\@(\?6| @Q@(\?)b @Gz@(\?,l @أp= @(\?Ld @@(\?"K~ @*\(@(\?o @TQ@(\?*;L @|Gz@(\? @p= ף@(\? $ @@(\?xV4@(\@(\?G贁@ Q@(\?}؛W@IzG@(\?iq@r= ףp@efffff?{؛W@@ ףp= ?G贁@Gz@Gz?xV4@Q@Gz?Q?Q? ףp= ?Q?Q?ffffff?Q?Q?(\?Q?Q?Q?Q?Q?Q?zGz?zGz?Q?֣p= ?֣p= ?Q?333333?333333?Q?(\?(\?Q?Q?Q?Q?GzG?GzG?Q?p= ף?p= ף?Q???Q?\(\?\(\?Q?Q?Q?Q?Gz?Gz?Q?p= ףp?p= ףp?Q???Q?(\(?(\(?Q?Q?Q?Q?zG?zG?Q?= ףp=?= ףp=?Q???Q?(\?(\?Q?RQ?RQ?Q?\(\?\(\?Q?Gz?Gz?Q???Q?Q?Q?Q?< ףp=?< ףp=?Q?(\?(\?Q?Gz?Gz?Q?dfffff?dfffff?Q?Q?Q?Q?٣p= ?٣p= ?Q?(\?(\?Q?IzG?IzG?Q???Q?r= ףp?r= ףp?Q?zG?zG?Q?RQ?RQ?Q?(\?(\?Q?233333?233333?Q?p= ף?p= ף?Q?$\(?$\(?Q?ףp= ?ףp= ?Q?{o^M@)\(@?"߼@RQ@Q?oN@> ףp=@PGz?Ey5@Gz@Q?fׂ-؂@Gz@Q?h6@> ףp=@Q?[ @gfffff@Q?֋|j@(\@Q?QQ@Q@Q?̮[@zG@Q?F@S@ ףp= @Q?:m@333333@Q?;c@\(\@Q?*;@Q@Q?0@Gz@Q? @ףp= @Q?&gE#@@Q?: ףp@*\(@Q?̊F@RQ@Q?] ` @|Gz@Q?%X@p= ף@Q? @@Q?|@(\@Q? t@@Q@Q?48@GzG@Q?vS@p= ףp@Q?W(@@Q?l2Tv@(\@Q?z@Q@Q?a  @Gz@Q?ܝo^ @= ףp=@Q?W/ͫ @ffffff@Q?_, @(\@Q?LRϊF @Q@Q?> @zG@Q?AuG @ ףp= @Q?. @433333@Q?6| @](\@Q?)b @Q@Q?,l @Gz@Q?Ld @أp= @Q?!K~ @@Q?o @*\(@Q?*;L @SQ@Q? @|Gz@Q? $ @p= ף@Q?xV4@@Q?G贁@(\@Q?|؛W@Q@ Q?iq@HzG@(\?{؛W@q= ףp@gfffff?G贁@@ ףp= ?xV4@Q@Gz? $ @\(\@Gz?{Gz?{Gz? ףp= ?|Gz?|Gz?ffffff?{Gz?{Gz?(\?|Gz?|Gz?Q?{Gz?{Gz?{Gz?{Gz?{Gz?{Gz?ףp= ?ףp= ?{Gz?333333?333333?{Gz?(\?(\?|Gz?Q?Q?{Gz?HzG?HzG?{Gz?p= ף?p= ף?{Gz???{Gz?\(\?\(\?zGz?Q?Q?zGz?Gz?Gz?zGz?p= ףp?p= ףp?{Gz???{Gz?)\(?)\(?{Gz?Q?Q?zGz?zG?zG?zGz?= ףp=?= ףp=?zGz???{Gz?(\?(\?{Gz?RQ?RQ?{Gz?\(\?\(\?{Gz?Gz?Gz?{Gz???{Gz?Q?Q?{Gz?> ףp=?> ףp=?{Gz?(\?(\?{Gz?Gz?Gz?{Gz?ffffff?ffffff?{Gz?Q?Q?{Gz?أp= ?أp= ?{Gz?(\?(\?{Gz?HzG?HzG?{Gz???{Gz?q= ףp?q= ףp?{Gz?zG?zG?{Gz?QQ?QQ?{Gz?(\?(\?{Gz?233333?233333?{Gz?p= ף?p= ף?zGz?.\(?.\(?ףp= ?{o^M@)\(@Q?"߼@RQ@?oN@ffffff@Q?Ey5@> ףp=@HGz?dׂ-؂@Gz@{Gz?h6@Gz@{Gz?[ @> ףp=@{Gz?֋|j@gfffff@{Gz?QQ@(\@{Gz?̮[@Q@{Gz?F@S@zG@{Gz?:m@ ףp= @{Gz?;c@333333@{Gz?*;@\(\@{Gz?0@Q@{Gz? @Gz@{Gz?&gE#@ףp= @{Gz?: ףp@@{Gz?̊F@*\(@{Gz?] ` @RQ@{Gz?%X@|Gz@{Gz? @p= ף@{Gz?|@@{Gz? t@@(\@{Gz?48@Q@{Gz?vS@HzG@{Gz?W(@p= ףp@{Gz?k2Tv@@{Gz?z@(\@{Gz?a  @Q@{Gz?ܝo^ @Gz@{Gz?V/ͫ @> ףp=@{Gz?_, @ffffff@{Gz?LRϊF @(\@{Gz?> @Q@{Gz?AuG @zG@{Gz?. @ ףp= @{Gz?6| @433333@{Gz?)b @](\@{Gz?+l @Q@{Gz?Ld @Gz@{Gz?!K~ @أp= @{Gz?o @@{Gz?*;L @*\(@{Gz? @SQ@{Gz? $ @|Gz@{Gz?xV4@p= ף@{Gz?G贁@@{Gz?|؛W@(\@{Gz?iq@Q@Q?|؛W@HzG@(\?G贁@q= ףp@ffffff?xV4@Q@ ףp= ? $ @\(\@Gz? @233333@Gz?ףp= ?ףp= ? ףp= ?أp= ?أp= ?gfffff?أp= ?أp= ?(\?أp= ?أp= ?Q?ףp= ?ףp= ?{Gz?ףp= ?ףp= ?ףp= ?ףp= ?ףp= ?ףp= ?333333?333333?ףp= ?(\?(\?أp= ?Q?Q?ףp= ?HzG?HzG?ףp= ?p= ף?p= ף?ףp= ???ףp= ?\(\?\(\?֣p= ?Q?Q?֣p= ?Gz?Gz?֣p= ?p= ףp?p= ףp?ףp= ???ףp= ?)\(?)\(?ףp= ?Q?Q?أp= ?zG?zG?أp= ?> ףp=?> ףp=?أp= ???ףp= ?(\?(\?ףp= ?RQ?RQ?ףp= ?\(\?\(\?ףp= ?Gz?Gz?ףp= ???ףp= ?Q?Q?ףp= ?> ףp=?> ףp=?ףp= ?(\?(\?ףp= ?Gz?Gz?ףp= ?ffffff?ffffff?ףp= ?Q?Q?ףp= ?أp= ?أp= ?ףp= ?(\?(\?ףp= ?HzG?HzG?ףp= ???ףp= ?p= ףp?p= ףp?ףp= ?zG?zG?ףp= ?PQ?PQ?أp= ?(\?(\?أp= ?833333?833333?أp= ?p= ף?p= ף?!\(?{o^M@)\(@ףp= ?"߼@RQ@Q?pN@{Gz@?Ey5@ffffff@Q?eׂ-؂@> ףp=@@Gz?h6@Gz@ףp= ?[ @Gz@ףp= ?֋|j@> ףp=@ףp= ?QQ@gfffff@ףp= ?̮[@(\@ףp= ?F@S@Q@ףp= ?:m@zG@֣p= ?:c@ ףp= @֣p= ?*;@233333@֣p= ?0@\(\@֣p= ? @Q@֣p= ?&gE#@Gz@֣p= ?: ףp@֣p= @֣p= ?̊F@@֣p= ?] ` @(\(@֣p= ?%X@RQ@֣p= ? @zGz@֣p= ?|@p= ף@֣p= ? t@@@֣p= ?48@(\@ףp= ?vS@Q@ףp= ?W(@HzG@ףp= ?l2Tv@p= ףp@ףp= ?z@@ףp= ?a  @(\@ףp= ?ܝo^ @Q@ףp= ?V/ͫ @Gz@ףp= ?_, @> ףp=@ףp= ?LRϊF @ffffff@ףp= ?> @(\@ףp= ?AuG @Q@ףp= ?. @zG@֣p= ?5| @ ףp= @֣p= ?)b @333333@֣p= ?*l @\(\@֣p= ?Ld @Q@֣p= ? K~ @Gz@֣p= ?o @ףp= @ףp= ?*;L @@ףp= ? @*\(@ףp= ? $ @SQ@ףp= ?xV4@|Gz@ףp= ?G贁@p= ף@ףp= ?|؛W@@ףp= ?iq@(\@{Gz?{؛W@Q@Q?G贁@GzG@(\?xV4@p= ףp@ffffff? $ @[(\@ ףp= ? @333333@Gz?*;L @ ףp= @Gz?433333?433333? ףp= ?433333?433333?gfffff?433333?433333?(\?433333?433333?Q?333333?333333?{Gz?333333?333333?ףp= ?333333?333333?433333?433333?433333?433333?(\?(\?433333?Q?Q?333333?HzG?HzG?333333?p= ף?p= ף?333333???333333?\(\?\(\?433333?Q?Q?433333?Gz?Gz?433333?q= ףp?q= ףp?433333???433333?*\(?*\(?433333?Q?Q?433333?zG?zG?433333?> ףp=?> ףp=?433333???333333?(\?(\?333333?RQ?RQ?333333?\(\?\(\?333333?Gz?Gz?333333???433333?Q?Q?433333?@ ףp=?@ ףp=?433333?(\?(\?433333?Gz?Gz?433333?gfffff?gfffff?433333?Q?Q?333333?ףp= ?ףp= ?333333?(\?(\?333333?GzG?GzG?333333???333333?n= ףp?n= ףp?333333?zG?zG?333333?NQ?NQ?433333?(\?(\?433333?633333?633333?p= ף?{o^M@*\(@0\(?"߼@RQ@ףp= ?qN@|Gz@Q?Ey5@(\@?eׂ-؂@ffffff@Q?h6@> ףp=@Gz?Z @Gz@333333?֋|j@Gz@333333?PQ@> ףp=@333333?ˮ[@ffffff@333333?F@S@(\@333333?:m@Q@233333?;c@zG@233333?*;@ ףp= @233333?0@233333@233333? @\(\@333333?&gE#@Q@333333?: ףp@Gz@333333?̊F@ףp= @333333?] ` @@233333?%X@(\(@233333? @RQ@233333?|@zGz@233333? t@@p= ף@333333?48@@333333?vS@(\@433333?W(@Q@433333?k2Tv@HzG@433333?z@q= ףp@433333?a  @@433333?ܝo^ @(\@333333?V/ͫ @Q@333333?_, @Gz@333333?LRϊF @> ףp=@333333?> @ffffff@333333?BuG @(\@333333?. @Q@333333?6| @zG@333333?)b @ ףp= @333333?,l @433333@333333?Ld @[(\@333333? K~ @Q@333333?o @Gz@333333?*;L @ףp= @433333? @@433333? $ @)\(@433333?xV4@SQ@433333?G贁@{Gz@433333?|؛W@p= ף@233333?iq@@֣p= ?z؛W@(\@{Gz?G贁@Q@Q?xV4@GzG@(\? $ @[(\@ffffff? @233333@ ףp= ?*;L @ ףp= @Gz?o @zG@Gz?(\?(\? ףp= ?(\?(\?gfffff?(\?(\?(\?(\?(\?Q?(\?(\?{Gz?(\?(\?ףp= ?(\?(\?433333?(\?(\?(\?(\?(\?(\?Q?Q?(\?HzG?HzG?(\?p= ף?p= ף?(\???(\?\(\?\(\?(\?Q?Q?(\?Gz?Gz?(\?q= ףp?q= ףp?(\???(\?*\(?*\(?(\?Q?Q?(\?zG?zG?(\?> ףp=?> ףp=?(\???(\?(\?(\?(\?RQ?RQ?(\?\(\?\(\?(\?Gz?Gz?(\???(\?Q?Q?(\?? ףp=?? ףp=?(\?(\?(\?(\?Gz?Gz?(\?gfffff?gfffff?(\?Q?Q?(\?ףp= ?ףp= ?(\?(\?(\?(\?GzG?GzG?(\???(\?n= ףp?n= ףp?(\?zG?zG?(\?NQ?NQ?(\?(\?(\?833333?{o^M@*\(@p= ף?"߼@RQ@0\(?qN@|Gz@ףp= ?Ey5@p= ף@Q?fׂ-؂@(\@?h6@ffffff@Q?[ @> ףp=@Gz?Ջ|j@Gz@(\?PQ@Gz@(\?ˮ[@> ףp=@(\?F@S@ffffff@(\?:m@(\@(\?:c@Q@(\?*;@zG@(\?0@ ףp= @(\? @233333@(\?%gE#@\(\@(\?: ףp@Q@(\?̊F@Gz@(\?] ` @֣p= @(\?%X@@(\? @(\(@(\?|@RQ@(\? t@@zGz@(\?48@p= ף@(\?vS@@(\?W(@(\@(\?k2Tv@Q@(\?z@HzG@(\?a  @q= ףp@(\?۝o^ @@(\?V/ͫ @(\@(\?_, @Q@(\?LRϊF @Gz@(\?> @> ףp=@(\?@uG @ffffff@(\?. @(\@(\?6| @Q@(\?)b @zG@(\?,l @ ףp= @(\?Ld @333333@(\?K~ @[(\@(\?o @Q@(\?*;L @Gz@(\? @ףp= @(\? $ @@(\?xV4@)\(@(\?G贁@SQ@(\?}؛W@{Gz@(\?iq@p= ף@133333?z؛W@@գp= ?F贁@(\@{Gz?xV4@Q@Q? $ @GzG@(\? @233333@ffffff?*;L @ ףp= @ ףp= ?o @zG@Gz?K~ @Q@Gz?Q?Q? ףp= ?Q?Q?ffffff?Q?Q?(\?Q?Q? Q?Q?Q?|Gz?Q?Q?֣p= ?Q?Q?433333?Q?Q?(\?Q?Q?Q?Q?Q?Q?HzG?HzG?Q?p= ף?p= ף?Q???Q?\(\?\(\?Q?Q?Q?Q?Gz?Gz?Q?q= ףp?q= ףp?Q???Q?)\(?)\(?Q?Q?Q?Q?zG?zG?Q?> ףp=?> ףp=?Q???Q?(\?(\?Q?RQ?RQ?Q?\(\?\(\?Q?Gz?Gz?Q???Q?Q?Q?Q?> ףp=?> ףp=?Q?(\?(\?Q?Gz?Gz?Q?ffffff?ffffff?Q?Q?Q?Q?ڣp= ?ڣp= ?Q?(\?(\?Q?JzG?JzG?Q???Q?t= ףp?t= ףp?Q?zG?zG?Q?TQ?TQ?(\?{o^M@*\(@833333?"߼@RQ@p= ף?qN@|Gz@0\(?Ey5@p= ף@ףp= ?fׂ-؂@Q@Q?h6@(\@?Z @gfffff@Q?֋|j@> ףp=@Gz?PQ@Gz@Q?ʮ[@Gz@Q?E@S@= ףp=@Q?:m@ffffff@Q?:c@(\@Q?*;@Q@Q?0@zG@Q? @ ףp= @Q?$gE#@233333@Q?: ףp@[(\@Q?̊F@Q@Q?] ` @Gz@Q?%X@֣p= @Q? @@Q?|@(\(@Q? t@@RQ@Q?48@zGz@Q?vS@p= ף@Q?W(@@Q?j2Tv@(\@Q?z@Q@Q?`  @FzG@Q?ڝo^ @p= ףp@Q?T/ͫ @@Q?_, @(\@Q?JRϊF @Q@Q?> @Gz@Q?@uG @> ףp=@Q?. @ffffff@Q?6| @(\@Q?)b @Q@Q?,l @zG@Q?Ld @ ףp= @Q? K~ @233333@Q?o @\(\@Q?*;L @Q@Q? @Gz@Q? $ @֣p= @Q?xV4@@Q?G贁@(\(@Q?|؛W@RQ@Q?iq@zGz@(\?|؛W@p= ף@533333?G贁@@ףp= ?xV4@(\@}Gz? $ @ Q@ Q? @433333@(\?*;L @ ףp= @hfffff?o @zG@ ףp= ? K~ @Q@Gz?Ld @(\@Gz?HzG?HzG? ףp= ?HzG?HzG?ffffff?HzG?HzG?(\?HzG?HzG? Q?HzG?HzG?|Gz?HzG?HzG?ףp= ?HzG?HzG?333333?HzG?HzG?(\?HzG?HzG?Q?HzG?HzG?GzG?GzG?GzG?GzG?p= ף?p= ף?HzG???HzG?\(\?\(\?HzG?Q?Q?HzG?Gz?Gz?HzG?p= ףp?p= ףp?GzG???GzG?(\(?(\(?GzG?Q?Q?HzG?zG?zG?HzG?= ףp=?= ףp=?HzG???HzG?(\?(\?HzG?RQ?RQ?HzG?\(\?\(\?HzG?Gz?Gz?HzG???GzG?Q?Q?GzG?= ףp=?= ףp=?GzG?(\?(\?GzG?Gz?Gz?GzG?efffff?efffff?GzG?Q?Q?HzG?٣p= ?٣p= ?HzG?(\?(\?HzG?IzG?IzG?HzG???HzG?r= ףp?r= ףp?HzG?zG?zG?PQ?{o^M@)\(@(\?"߼@RQ@833333?qN@|Gz@p= ף?Ey5@p= ף@0\(?fׂ-؂@@ףp= ?h6@Q@Q?\ @(\@?Ջ|j@ffffff@Q?PQ@> ףp=@Gz?ʮ[@Gz@HzG?F@S@Gz@HzG?:m@> ףp=@HzG?;c@ffffff@HzG?*;@(\@HzG?0@Q@HzG? @zG@HzG?%gE#@ ףp= @HzG?: ףp@333333@HzG?̊F@\(\@HzG?] ` @Q@HzG?%X@Gz@HzG? @ףp= @HzG?|@@HzG? t@@)\(@HzG?48@RQ@HzG?vS@{Gz@HzG?W(@p= ף@HzG?l2Tv@@HzG?z@(\@HzG?`  @Q@HzG?۝o^ @GzG@HzG?V/ͫ @p= ףp@HzG?_, @@HzG?KRϊF @(\@HzG?> @Q@HzG?AuG @Gz@HzG?. @> ףp=@HzG?6| @gfffff@HzG?)b @(\@HzG?+l @Q@HzG?Ld @zG@HzG? K~ @ ףp= @HzG?o @333333@HzG?*;L @\(\@HzG? @Q@HzG? $ @Gz@HzG?xV4@ףp= @HzG?G贁@@HzG?{؛W@)\(@HzG?iq@RQ@Q?|؛W@{Gz@(\?G贁@p= ף@433333?xV4@@أp= ? $ @(\@~Gz? @Q@ Q?*;L @ ףp= @(\?o @zG@hfffff? K~ @Q@ ףp= ?Ld @(\@Gz?*l @ffffff@Gz?p= ף?p= ף? ףp= ?p= ף?p= ף?ffffff?p= ף?p= ף?(\?p= ף?p= ף? Q?p= ף?p= ף?|Gz?p= ף?p= ף?ףp= ?p= ף?p= ף?333333?p= ף?p= ף?(\?p= ף?p= ף?Q?p= ף?p= ף?GzG?p= ף?p= ף?p= ף?p= ף?p= ף?p= ף???p= ף?\(\?\(\?p= ף?Q?Q?p= ף?Gz?Gz?p= ף?p= ףp?p= ףp?p= ף???p= ף?(\(?(\(?p= ף?Q?Q?p= ף?zG?zG?p= ף?= ףp=?= ףp=?p= ף???p= ף?(\?(\?p= ף?RQ?RQ?p= ף?\(\?\(\?p= ף?Gz?Gz?p= ף???p= ף?Q?Q?p= ף?= ףp=?= ףp=?p= ף?(\?(\?p= ף?Gz?Gz?p= ף?efffff?efffff?p= ף?Q?Q?p= ף?٣p= ?٣p= ?p= ף?(\?(\?p= ף?IzG?IzG?p= ף???p= ף?r= ףp?r= ףp?zG?{o^M@)\(@PQ?"߼@RQ@(\?qN@|Gz@833333?Ey5@p= ף@p= ף?fׂ-؂@@ \(?h6@zG@ףp= ?[ @Q@Q?֋|j@(\@?PQ@ffffff@Q?ˮ[@> ףp=@Gz?E@S@Gz@p= ף?:m@Gz@p= ף?;c@> ףp=@p= ף?*;@ffffff@p= ף?0@(\@p= ף? @Q@p= ף?&gE#@zG@p= ף?: ףp@ ףp= @p= ף?̊F@333333@p= ף?] ` @\(\@p= ף?%X@Q@p= ף? @Gz@p= ף?|@ףp= @p= ף? t@@@p= ף?48@)\(@p= ף?vS@RQ@p= ף?W(@{Gz@p= ף?l2Tv@p= ף@p= ף?z@@p= ף?a  @(\@p= ף?۝o^ @Q@p= ף?U/ͫ @GzG@p= ף?_, @p= ףp@p= ף?KRϊF @@p= ף?> @(\@p= ף?AuG @Q@p= ף?. @Gz@p= ף?6| @> ףp=@p= ף?)b @gfffff@p= ף?,l @(\@p= ף?Ld @Q@p= ף?!K~ @zG@p= ף?o @ ףp= @p= ף?*;L @333333@p= ף? @\(\@p= ף? $ @Q@p= ף?xV4@Gz@p= ף?G贁@ףp= @p= ף?{؛W@@p= ף?iq@)\(@IzG?|؛W@SQ@Q?G贁@zGz@(\?xV4@p= ף@333333? $ @@٣p= ? @(\@|Gz?*;L @ ףp= @ Q?o @zG@(\? K~ @Q@hfffff?Ld @(\@ ףp= ?*l @ffffff@Gz?)b @= ףp=@Gz??? ףp= ???ffffff???(\???Q???{Gz???ףp= ???433333???(\???Q???HzG???p= ף???????\(\?\(\??Q?Q??Gz?Gz??p= ףp?p= ףp?????)\(?)\(??Q?Q??zG?zG??> ףp=?> ףp=?????(\?(\??RQ?RQ??\(\?\(\??Gz?Gz?????Q?Q??@ ףp=?@ ףp=??(\?(\??Gz?Gz??hfffff?hfffff?? Q? Q??أp= ?أp= ??(\?(\??HzG?HzG????p= ףp?{o^M@)\(@zG?"߼@RQ@PQ?pN@{Gz@(\?Ey5@p= ף@833333?fׂ-؂@@p= ף?h6@(\@\(?[ @zG@֣p= ?֋|j@Q@Q?PQ@(\@?ʮ[@ffffff@Q?F@S@> ףp=@Gz?:m@Gz@?;c@Gz@?*;@> ףp=@?0@ffffff@? @(\@?&gE#@Q@?: ףp@zG@?̊F@ ףp= @?] ` @333333@?%X@\(\@? @Q@?|@Gz@? t@@ףp= @?48@@?vS@)\(@?W(@RQ@?l2Tv@{Gz@?z@p= ף@?a  @@?ܝo^ @(\@?U/ͫ @Q@?_, @HzG@?KRϊF @p= ףp@?> @@?AuG @(\@?. @Q@?6| @Gz@?)b @= ףp=@?+l @gfffff@?Ld @(\@?!K~ @Q@?o @zG@?*;L @ ףp= @? @333333@? $ @\(\@?xV4@Q@?G贁@Gz@?|؛W@ףp= @?iq@@p= ף?{؛W@)\(@HzG?G贁@RQ@Q?xV4@{Gz@(\? $ @p= ף@433333? @@أp= ?*;L @(\@xGz?o @zG@ Q? K~ @Q@(\?Ld @(\@hfffff?*l @ffffff@ףp= ?)b @> ףp=@Gz?5| @Gz@Gz?\(\?\(\? ףp= ?\(\?\(\?ffffff?\(\?\(\?(\?\(\?\(\?Q?\(\?\(\?{Gz?\(\?\(\?ףp= ?\(\?\(\?433333?](\?](\?(\?](\?](\?Q?\(\?\(\?HzG?\(\?\(\?p= ף?\(\?\(\??\(\?\(\?\(\?\(\?\(\?\(\?Q?Q?\(\?Gz?Gz?\(\?p= ףp?p= ףp?\(\???\(\?)\(?)\(?\(\?Q?Q?\(\?zG?zG?\(\?> ףp=?> ףp=?\(\???\(\?(\?(\?\(\?RQ?RQ?\(\?\(\?\(\?\(\?Gz?Gz?\(\???](\?Q?Q?](\?@ ףp=?@ ףp=?](\?(\?(\?](\?Gz?Gz?](\?hfffff?hfffff?](\? Q? Q?\(\?أp= ?أp= ?\(\?(\?(\?\(\?HzG?HzG??{o^M@)\(@p= ףp?"߼@RQ@zG?pN@{Gz@PQ?Ey5@p= ף@(\?fׂ-؂@@833333?h6@(\@p= ף?Z @ ףp= @"\(?ԋ|j@zG@ףp= ?OQ@Q@Q?ʮ[@(\@?E@S@ffffff@Q?:m@> ףp=@Gz?;c@Gz@\(\?*;@Gz@\(\?0@> ףp=@\(\? @ffffff@\(\?&gE#@(\@\(\?: ףp@Q@\(\?̊F@zG@\(\?] ` @ ףp= @\(\?%X@333333@\(\? @\(\@\(\?|@Q@\(\? t@@Gz@\(\?48@ףp= @\(\?vS@@\(\?W(@)\(@\(\?l2Tv@RQ@\(\?z@{Gz@\(\?a  @p= ף@\(\?ܝo^ @@\(\?V/ͫ @(\@\(\?_, @Q@\(\?KRϊF @HzG@\(\?> @p= ףp@\(\?AuG @@\(\?. @(\@\(\?6| @Q@\(\?)b @Gz@\(\?+l @= ףp=@\(\?Ld @gfffff@\(\?!K~ @(\@\(\?o @Q@\(\?*;L @zG@\(\? @ ףp= @\(\? $ @333333@\(\?xV4@\(\@\(\?G贁@Q@\(\?|؛W@Gz@\(\?iq@ףp= @?|؛W@@p= ף?G贁@*\(@GzG?xV4@QQ@Q? $ @|Gz@(\? @p= ף@533333?*;L @@أp= ?o @zG@xGz?K~ @Q@ Q?Ld @(\@(\?)l @efffff@hfffff?)b @? ףp=@ףp= ?3| @Gz@Gz?. @Q@Gz?Q?Q? ףp= ?Q?Q?gfffff?Q?Q?(\?Q?Q?Q?Q?Q?zGz?Q?Q?֣p= ?Q?Q?433333?Q?Q?(\?Q?Q?Q?Q?Q?IzG?Q?Q?p= ף?Q?Q??Q?Q?\(\?Q?Q?Q?Q?Q?Q?Gz?Gz?Q?q= ףp?q= ףp?Q???Q?)\(?)\(?Q?Q?Q?Q?zG?zG?Q?= ףp=?= ףp=?Q???Q?(\?(\?Q?SQ?SQ?Q?^(\?^(\?Q?Gz?Gz?Q???Q?Q?Q?Q?> ףp=?> ףp=?Q?(\?(\?Q?Gz?Gz?Q?ffffff?ffffff?Q?Q?Q?Q?ףp= ?ףp= ?Q?(\?(\?HzG?{o^M@)\(@?"߼@RQ@p= ףp?pN@{Gz@zG?Ey5@p= ף@PQ?fׂ-؂@@(\?h6@(\@133333?Z @Q@p= ף?Ջ|j@ ףp= @,\(?PQ@zG@ ףp= ?ʮ[@Q@Q?E@S@(\@?:m@ffffff@Q?:c@= ףp=@_Gz?*;@Gz@Q?1@Gz@Q? @> ףp=@Q?&gE#@gfffff@Q?: ףp@(\@Q?̊F@Q@Q?] ` @zG@Q?%X@ ףp= @Q? @433333@Q?|@\(\@Q? t@@Q@Q?48@Gz@Q?vS@أp= @Q?W(@@Q?l2Tv@*\(@Q?z@RQ@Q?b  @|Gz@Q?ܝo^ @p= ף@Q?V/ͫ @@Q?_, @(\@Q?LRϊF @ Q@Q?> @HzG@Q?BuG @r= ףp@Q?. @@Q?6| @(\@Q?)b @Q@Q?,l @Gz@Q?Ld @> ףp=@Q?"K~ @ffffff@Q?o @(\@Q?*;L @Q@Q? @zG@Q? $ @ ףp= @Q?xV4@433333@Q?G贁@\(\@Q?z؛W@Q@Q?iq@Gz@](\?|؛W@أp= @?G贁@@p= ף?xV4@*\(@IzG? $ @SQ@Q? @|Gz@(\?*;L @p= ף@533333?o @@ڣp= ?!K~ @Q@zGz?Ld @(\@Q?,l @gfffff@(\?)b @? ףp=@ffffff?7| @Gz@ ףp= ?. @Q@Gz?BuG @(\@Gz?Gz?Gz? ףp= ?Gz?Gz?gfffff?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?zGz?Gz?Gz?֣p= ?Gz?Gz?433333?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?HzG?Gz?Gz?p= ף?Gz?Gz??Gz?Gz?\(\?Gz?Gz?Q?Gz?Gz?Gz?Gz?Gz?Gz?q= ףp?q= ףp?Gz???Gz?)\(?)\(?Gz?Q?Q?Gz?zG?zG?Gz?= ףp=?= ףp=?Gz???Gz?(\?(\?Gz?SQ?SQ?Gz?^(\?^(\?Gz?Gz?Gz?Gz???Gz?Q?Q?Gz?> ףp=?> ףp=?Gz?(\?(\?Gz?Gz?Gz?Gz?ffffff?ffffff?Gz?Q?Q?Gz?أp= ?أp= ?(\?{o^M@)\(@HzG?"߼@RQ@?pN@{Gz@p= ףp?Ey5@p= ף@zG?fׂ-؂@@PQ?h6@(\@(\?Z @Q@633333?Ջ|j@333333@p= ף?PQ@ ףp= @,\(?ʮ[@zG@ ףp= ?E@S@Q@Q?:m@(\@?:c@ffffff@Q?*;@= ףp=@_Gz?0@Gz@Gz? @Gz@Gz?&gE#@> ףp=@Gz?: ףp@gfffff@Gz?̊F@(\@Gz?] ` @Q@Gz?%X@zG@Gz? @ ףp= @Gz?|@433333@Gz? t@@\(\@Gz?48@Q@Gz?vS@Gz@Gz?W(@أp= @Gz?l2Tv@@Gz?z@*\(@Gz?b  @RQ@Gz?ܝo^ @|Gz@Gz?V/ͫ @p= ף@Gz?_, @@Gz?LRϊF @(\@Gz?> @ Q@Gz?BuG @HzG@Gz?. @r= ףp@Gz?6| @@Gz?)b @(\@Gz?,l @Q@Gz?Ld @Gz@Gz?"K~ @> ףp=@Gz?o @ffffff@Gz?*;L @(\@Gz? @Q@Gz? $ @zG@Gz?xV4@ ףp= @Gz?G贁@433333@Gz?z؛W@\(\@Gz?iq@Q@Q?z؛W@Gz@\(\?G贁@ףp= @?xV4@@p= ף? $ @)\(@IzG? @RQ@Q?*;L @{Gz@(\?o @p= ף@633333?!K~ @Q@ڣp= ?Ld @(\@xGz?,l @gfffff@Q?)b @> ףp=@(\?7| @Gz@efffff?. @Q@ ףp= ?@uG @(\@Gz?> @@Gz?p= ףp?p= ףp? ףp= ?q= ףp?q= ףp?gfffff?q= ףp?q= ףp?(\?p= ףp?p= ףp?Q?p= ףp?p= ףp?zGz?p= ףp?p= ףp?֣p= ?p= ףp?p= ףp?433333?q= ףp?q= ףp?(\?q= ףp?q= ףp?Q?p= ףp?p= ףp?HzG?q= ףp?q= ףp?p= ף?q= ףp?q= ףp??p= ףp?p= ףp?\(\?p= ףp?p= ףp?Q?r= ףp?r= ףp?Gz?r= ףp?r= ףp?q= ףp?q= ףp?q= ףp?p= ףp???p= ףp?)\(?)\(?p= ףp?Q?Q?p= ףp?zG?zG?p= ףp?= ףp=?= ףp=?p= ףp???r= ףp?(\?(\?r= ףp?SQ?SQ?r= ףp?^(\?^(\?r= ףp?Gz?Gz?r= ףp???p= ףp?Q?Q?p= ףp?> ףp=?> ףp=?p= ףp?(\?(\?p= ףp?Gz?Gz?p= ףp?ffffff?ffffff?r= ףp? Q? Q?أp= ?{o^M@)\(@(\?"߼@RQ@HzG?pN@{Gz@?Ey5@p= ף@p= ףp?fׂ-؂@@zG?h6@(\@PQ?[ @Q@(\?Ջ|j@HzG@633333?PQ@333333@p= ף?ʮ[@ ףp= @,\(?E@S@zG@ ףp= ?:m@Q@Q?:c@(\@?*;@ffffff@Q?0@= ףp=@_Gz? @Gz@r= ףp?&gE#@Gz@r= ףp?: ףp@> ףp=@r= ףp?̊F@gfffff@r= ףp?] ` @(\@r= ףp?%X@Q@r= ףp? @zG@r= ףp?|@ ףp= @r= ףp? t@@433333@r= ףp?48@\(\@r= ףp?vS@Q@r= ףp?W(@Gz@r= ףp?l2Tv@أp= @r= ףp?z@@r= ףp?`  @*\(@r= ףp?ܝo^ @RQ@r= ףp?V/ͫ @|Gz@r= ףp?_, @p= ף@r= ףp?LRϊF @@r= ףp?> @(\@r= ףp?BuG @ Q@r= ףp?. @HzG@r= ףp?6| @r= ףp@r= ףp?)b @@r= ףp?,l @(\@r= ףp?Ld @Q@q= ףp?"K~ @Gz@q= ףp?o @> ףp=@q= ףp?*;L @ffffff@q= ףp? @(\@q= ףp? $ @Q@q= ףp?xV4@zG@q= ףp?G贁@ ףp= @q= ףp?|؛W@433333@q= ףp?iq@\(\@Gz?|؛W@Q@Q?G贁@Gz@](\?xV4@أp= @? $ @@p= ף? @*\(@IzG?*;L @SQ@Q?o @|Gz@(\? K~ @p= ף@633333?Ld @(\@ڣp= ?+l @gfffff@zGz?)b @? ףp=@Q?7| @Gz@(\?. @Q@ffffff?BuG @(\@ ףp= ?> @@Gz?LRϊF @p= ףp@Gz??? ףp= ???gfffff???(\???Q???{Gz???ףp= ???433333???(\???Q???HzG???p= ף??????\(\???Q???Gz???q= ףp???????*\(?*\(??Q?Q??zG?zG??> ףp=?> ףp=?????(\?(\??RQ?RQ??\(\?\(\??Gz?Gz?????Q?Q??= ףp=?= ףp=??(\?(\??Gz?Gz??hfffff?hfffff? Q?{o^M@*\(@أp= ?"߼@RQ@(\?pN@{Gz@HzG?Ey5@p= ף@?fׂ-؂@@p= ףp?h6@(\@zG?[ @Q@PQ?֋|j@HzG@(\?PQ@\(\@433333?ʮ[@333333@p= ף?E@S@ ףp= @(\(?:m@zG@ףp= ?:c@Q@Q?*;@(\@?0@ffffff@Q? @> ףp=@Gz?&gE#@Gz@?: ףp@Gz@?̊F@= ףp=@?] ` @ffffff@?%X@(\@? @Q@?|@zG@? t@@ ףp= @?48@433333@?vS@\(\@?W(@Q@?j2Tv@Gz@?z@أp= @?`  @@?ڝo^ @*\(@?W/ͫ @SQ@?_, @|Gz@?LRϊF @p= ף@?> @@?AuG @(\@?. @Q@?5| @HzG@?)b @p= ףp@?*l @@?Ld @(\@? K~ @Q@?o @Gz@?*;L @> ףp=@? @ffffff@? $ @(\@?xV4@Q@?G贁@zG@?{؛W@ ףp= @?iq@333333@q= ףp?|؛W@\(\@Gz?G贁@Q@Q?xV4@Gz@`(\? $ @أp= @? @@p= ף?*;L @)\(@JzG?o @SQ@Q?!K~ @{Gz@(\?Ld @(\@233333?)l @efffff@ڣp= ?)b @> ףp=@zGz?8| @Gz@Q?. @Q@(\?AuG @(\@ffffff?> @@ ףp= ?LRϊF @q= ףp@Gz?_, @IzG@Gz?)\(?)\(? ףp= ?*\(?*\(?gfffff?*\(?*\(?(\?*\(?*\(?Q?(\(?(\(?{Gz?)\(?)\(?ףp= ?)\(?)\(?433333?*\(?*\(?(\?*\(?*\(?Q?(\(?(\(?HzG?)\(?)\(?p= ף?)\(?)\(??)\(?)\(?\(\?)\(?)\(?Q?*\(?*\(?Gz?*\(?*\(?q= ףp?*\(?*\(??*\(?*\(?*\(?*\(?*\(?*\(?Q?Q?*\(?zG?zG?*\(?> ףp=?> ףp=?*\(???)\(?(\?(\?)\(?RQ?RQ?)\(?\(\?\(\?)\(?Gz?Gz?)\(???)\(?Q?Q?)\(?= ףp=?= ףp=?)\(?(\?(\?*\(?Gz?Gz?hfffff?{o^M@*\(@ Q?"߼@RQ@أp= ?pN@{Gz@(\?Ey5@p= ף@HzG?fׂ-؂@@?h6@(\@p= ףp?[ @Q@zG?֋|j@HzG@PQ?QQ@q= ףp@(\?ʮ[@\(\@433333?E@S@333333@p= ף?:m@ ףp= @(\(?:c@zG@ףp= ?*;@Q@Q?0@(\@? @ffffff@Q?&gE#@> ףp=@Gz?: ףp@Gz@)\(?̊F@Gz@)\(?] ` @= ףp=@)\(?%X@ffffff@)\(? @(\@)\(?|@Q@)\(? t@@zG@*\(?48@ ףp= @*\(?vS@433333@*\(?W(@\(\@*\(?j2Tv@Q@*\(?z@Gz@*\(?`  @أp= @*\(?ڝo^ @@*\(?V/ͫ @*\(@*\(?_, @SQ@*\(?LRϊF @|Gz@*\(?> @p= ף@*\(?AuG @@*\(?. @(\@)\(?5| @Q@)\(?)b @HzG@)\(?*l @p= ףp@)\(?Ld @@)\(? K~ @(\@)\(?o @Q@)\(?*;L @Gz@)\(? @> ףp=@)\(? $ @ffffff@)\(?xV4@(\@)\(?G贁@Q@)\(?{؛W@zG@*\(?iq@ ףp= @?|؛W@333333@r= ףp?G贁@](\@Gz?xV4@Q@Q? $ @Gz@^(\? @ףp= @?*;L @@p= ף?o @*\(@KzG?!K~ @SQ@Q?Ld @|Gz@(\?+l @gfffff@233333?)b @< ףp=@ڣp= ?6| @Gz@yGz?. @Q@Q?BuG @(\@(\?> @@ffffff?MRϊF @q= ףp@ ףp= ?_, @HzG@Gz?W/ͫ @ Q@Gz?Q?Q? ףp= ?Q?Q?gfffff?Q?Q?(\?Q?Q?Q?Q?Q?{Gz?Q?Q?ףp= ?Q?Q?433333?Q?Q?(\?Q?Q?Q?Q?Q?HzG?Q?Q?p= ף?Q?Q??Q?Q?\(\?Q?Q?Q?Q?Q?Gz?Q?Q?q= ףp?Q?Q??Q?Q?*\(?Q?Q?Q?Q?Q?Q?zG?zG?Q?> ףp=?> ףp=?Q???Q?(\?(\?Q?RQ?RQ?Q?\(\?\(\?Q?Gz?Gz?Q???Q?Q?Q?Q?= ףp=?= ףp=?Q?(\?(\?Gz?{o^M@*\(@hfffff?"߼@RQ@ Q?qN@|Gz@أp= ?Ey5@p= ף@(\?fׂ-؂@@HzG?h6@(\@?[ @Q@p= ףp?֋|j@HzG@zG?QQ@q= ףp@LQ?ˮ[@Q@(\?E@S@\(\@433333?:m@333333@p= ף?:c@ ףp= @(\(?*;@zG@ףp= ?0@Q@Q? @(\@?&gE#@ffffff@Q?: ףp@> ףp=@Gz?̊F@Gz@Q?] ` @Gz@Q?%X@= ףp=@Q? @ffffff@Q?|@(\@Q? t@@Q@Q?48@zG@Q?vS@ ףp= @Q?W(@333333@Q?j2Tv@\(\@Q?z@Q@Q?`  @Gz@Q?ڝo^ @ףp= @Q?V/ͫ @@Q?_, @)\(@Q?KRϊF @RQ@Q?> @{Gz@Q?AuG @p= ף@Q?. @@Q?7| @(\@Q?)b @Q@Q?*l @HzG@Q?Ld @p= ףp@Q? K~ @@Q?o @(\@Q?*;L @Q@Q? @Gz@Q? $ @> ףp=@Q?xV4@ffffff@Q?G贁@(\@Q?{؛W@Q@Q?iq@zG@,\(?}؛W@ ףp= @?G贁@433333@t= ףp?xV4@](\@Gz? $ @Q@Q? @Gz@](\?*;L @ףp= @?o @@p= ף? K~ @*\(@KzG?Ld @RQ@Q?)l @efffff@(\?)b @> ףp=@233333?3| @Gz@٣p= ?. @Q@yGz?AuG @(\@Q?> @@(\?LRϊF @q= ףp@ffffff?_, @IzG@ ףp= ?V/ͫ @Q@Gz?ܝo^ @(\@Gz?zG?zG? ףp= ?zG?zG?ffffff?zG?zG?(\?zG?zG? Q?zG?zG?zGz?zG?zG?أp= ?zG?zG?433333?zG?zG?(\?zG?zG?Q?zG?zG?HzG?zG?zG?p= ף?zG?zG??zG?zG?\(\?zG?zG?Q?zG?zG?Gz?zG?zG?p= ףp?zG?zG??zG?zG?*\(?zG?zG?Q?zG?zG?zG?zG?zG?zG?= ףp=?= ףp=?zG???zG?(\?(\?zG?RQ?RQ?zG?[(\?[(\?zG?Gz?Gz?zG???zG?Q?Q?zG?> ףp=?> ףp=?(\?{o^M@*\(@Gz?"߼@RQ@hfffff?qN@|Gz@ Q?Ey5@p= ף@أp= ?eׂ-؂@@(\?h6@(\@HzG?Z @Q@?Ջ|j@HzG@p= ףp?PQ@p= ףp@zG?ʮ[@@PQ?F@S@Q@(\?:m@\(\@233333?:c@433333@p= ף?*;@ ףp= @$\(?0@zG@ףp= ? @Q@Q?&gE#@(\@?: ףp@gfffff@Q?̊F@> ףp=@Gz?] ` @Gz@zG?%X@Gz@zG? @= ףp=@zG?|@ffffff@zG? t@@(\@zG?48@Q@zG?tS@zG@zG?W(@ ףp= @zG?i2Tv@233333@zG?z@[(\@zG?^  @Q@zG?ٝo^ @Gz@zG?T/ͫ @֣p= @zG?_, @@zG?IRϊF @(\(@zG?> @QQ@zG??uG @zGz@zG?. @p= ף@zG?5| @@zG?)b @(\@zG?(l @Q@zG?Ld @FzG@zG?K~ @o= ףp@zG?o @@zG?*;L @(\@zG? @Q@zG? $ @Gz@zG?xV4@= ףp=@zG?F贁@ffffff@zG?z؛W@(\@zG?iq@Q@Q?|؛W@zG@,\(?G贁@ ףp= @?xV4@533333@t= ףp? $ @^(\@Gz? @Q@Q?*;L @Gz@`(\?o @أp= @?!K~ @@p= ף?Ld @*\(@LzG?,l @SQ@Q?)b @= ףp=@(\?4| @Gz@533333?. @Q@أp= ?>uG @(\@zGz?> @@ Q?MRϊF @q= ףp@(\?_, @HzG@hfffff?W/ͫ @ Q@ ףp= ?ܝo^ @(\@Gz?b  @@Gz?> ףp=?> ףp=? ףp= ?> ףp=?> ףp=?ffffff?> ףp=?> ףp=?(\?> ףp=?> ףp=? Q?> ףp=?> ףp=?zGz?= ףp=?= ףp=?أp= ?> ףp=?> ףp=?433333?> ףp=?> ףp=?(\?> ףp=?> ףp=?Q?= ףp=?= ףp=?HzG?= ףp=?= ףp=?p= ף?= ףp=?= ףp=??> ףp=?> ףp=?\(\?> ףp=?> ףp=?Q?> ףp=?> ףp=?Gz?> ףp=?> ףp=?p= ףp?> ףp=?> ףp=??> ףp=?> ףp=?*\(?> ףp=?> ףp=?Q?> ףp=?> ףp=?zG?= ףp=?= ףp=?= ףp=?= ףp=?= ףp=?= ףp=???= ףp=?(\?(\?= ףp=?RQ?RQ?> ףp=?[(\?[(\?> ףp=?Gz?Gz?> ףp=???> ףp=?Q?Q?@ ףp=?{o^M@*\(@(\?"߼@RQ@Gz?qN@|Gz@hfffff?Ey5@p= ף@ Q?fׂ-؂@@أp= ?h6@(\@(\?Z @Q@HzG?Ջ|j@HzG@?PQ@p= ףp@p= ףp?ʮ[@@zG?F@S@Gz@PQ?:m@Q@(\?:c@\(\@233333?*;@433333@p= ף?0@ ףp= @$\(? @zG@ףp= ?&gE#@Q@Q?: ףp@(\@?̊F@gfffff@Q?] ` @> ףp=@Gz?%X@Gz@= ףp=? @Gz@= ףp=?|@= ףp=@= ףp=? t@@ffffff@= ףp=?48@(\@= ףp=?uS@Q@= ףp=?W(@zG@= ףp=?i2Tv@ ףp= @= ףp=?z@233333@= ףp=?_  @[(\@= ףp=?ٝo^ @Q@> ףp=?T/ͫ @Gz@> ףp=?_, @֣p= @> ףp=?IRϊF @@> ףp=?> @(\(@> ףp=??uG @QQ@> ףp=?. @zGz@> ףp=?5| @p= ף@> ףp=?)b @@> ףp=?*l @(\@= ףp=?Ld @Q@= ףp=?K~ @FzG@= ףp=?o @o= ףp@= ףp=?*;L @@= ףp=? @(\@= ףp=? $ @Q@> ףp=?xV4@Gz@> ףp=?F贁@= ףp=@> ףp=?z؛W@ffffff@> ףp=?iq@(\@zG?{؛W@Q@Q?G贁@zG@*\(?xV4@ ףp= @?$ @433333@s= ףp? @](\@Gz?*;L @Q@Q?o @Gz@_(\?"K~ @أp= @?Ld @@p= ף?+l @)\(@IzG?)b @= ףp=@Q?4| @Gz@(\?. @Q@433333?>uG @(\@أp= ?> @@zGz?LRϊF @q= ףp@ Q?_, @IzG@(\?V/ͫ @Q@hfffff?ܝo^ @(\@ ףp= ?a  @@Gz?z@p= ף@Gz??? ףp= ???ffffff???(\??? Q???zGz???أp= ???433333???(\???Q???HzG???p= ף??????\(\???Q???Gz???p= ףp??????*\(???Q???zG???= ףp=???????(\?(\??RQ?RQ??[(\?[(\??Gz?Gz????Q?{o^M@*\(@@ ףp=?"߼@RQ@(\?qN@|Gz@Gz?Ey5@p= ף@hfffff?fׂ-؂@@ Q?h6@(\@أp= ?Z @Q@(\?Ջ|j@HzG@HzG?PQ@p= ףp@?ʮ[@@p= ףp?E@S@(\@zG?:m@Gz@PQ? ףp=@Gz? @Gz@?|@Gz@? t@@= ףp=@?48@ffffff@?tS@(\@?W(@Q@?j2Tv@zG@?z@ ףp= @?_  @233333@?ٝo^ @[(\@?T/ͫ @Q@?_, @Gz@?IRϊF @֣p= @?> @@??uG @(\(@?. @QQ@?5| @zGz@?)b @p= ף@?*l @@?Ld @(\@?K~ @Q@?o @FzG@?*;L @o= ףp@? @@? $ @(\@?xV4@Q@?F贁@Gz@?z؛W@= ףp=@?iq@ffffff@= ףp=?}؛W@(\@zG?G贁@Q@Q?xV4@zG@,\(?$ @ ףp= @? @533333@t= ףp?*;L @^(\@Gz?o @Q@Q?"K~ @Gz@`(\?Ld @أp= @?,l @@p= ף?)b @*\(@HzG?4| @Gz@Q?. @Q@(\?>uG @(\@433333?> @@أp= ?IRϊF @o= ףp@zGz?_, @HzG@ Q?W/ͫ @ Q@(\?ܝo^ @(\@hfffff?b  @@ ףp= ?z@p= ף@Gz?l2Tv@|Gz@Gz?(\?(\? ףp= ?(\?(\?gfffff?(\?(\?(\?(\?(\?Q?(\?(\?{Gz?(\?(\?ףp= ?(\?(\?433333?(\?(\?(\?(\?(\?Q?(\?(\?HzG?(\?(\?p= ף?(\?(\??(\?(\?\(\?(\?(\?Q?(\?(\?Gz?(\?(\?p= ףp?(\?(\??(\?(\?)\(?(\?(\?Q?(\?(\?zG?(\?(\?= ףp=?(\?(\??(\?(\?(\?(\?(\?(\?RQ?RQ?(\?\(\?\(\?(\?Gz?Gz??{o^M@)\(@Q?"߼@RQ@@ ףp=?qN@|Gz@(\?Ey5@p= ף@Gz?fׂ-؂@@hfffff?h6@(\@ Q?\ @ Q@أp= ?Ջ|j@HzG@(\?PQ@p= ףp@HzG?ʮ[@@?E@S@(\@r= ףp?:m@أp= @zG?:c@Gz@RQ?*;@Q@(\?0@\(\@733333? @433333@p= ף?&gE#@ ףp= @.\(?: ףp@zG@ףp= ?̊F@Q@Q?] ` @(\@?%X@ffffff@Q? @> ףp=@}Gz?|@Gz@(\? t@@Gz@(\?48@> ףp=@(\?vS@ffffff@(\?W(@(\@(\?l2Tv@Q@(\?z@zG@(\?`  @ ףp= @(\?ܝo^ @433333@(\?V/ͫ @\(\@(\?_, @Q@(\?LRϊF @Gz@(\?> @ףp= @(\?AuG @@(\?. @)\(@(\?6| @RQ@(\?)b @{Gz@(\?,l @p= ף@(\?Ld @@(\?"K~ @(\@(\?o @Q@(\?*;L @GzG@(\? @q= ףp@(\? $ @@(\?xV4@(\@(\?G贁@Q@(\?}؛W@Gz@(\?iq@= ףp=@?{؛W@gfffff@= ףp=?G贁@(\@zG?xV4@Q@Q? $ @zG@#\(? @ףp= @?*;L @433333@k= ףp?o @[(\@Gz?K~ @Q@Q?Ld @Gz@](\?*l @ףp= @?)b @@p= ף?4| @Gz@HzG?. @Q@Q?>uG @(\@(\?> @@433333?IRϊF @o= ףp@أp= ?_, @HzG@|Gz?W/ͫ @ Q@Q?ܝo^ @(\@(\?a  @@dfffff?z@p= ף@ ףp= ?k2Tv@{Gz@Gz?W(@RQ@Gz?RQ?RQ? ףp= ?RQ?RQ?gfffff?SQ?SQ?(\?RQ?RQ?Q?RQ?RQ?{Gz?RQ?RQ?ףp= ?RQ?RQ?433333?RQ?RQ?(\?RQ?RQ?Q?RQ?RQ?HzG?RQ?RQ?p= ף?RQ?RQ??RQ?RQ?\(\?RQ?RQ?Q?RQ?RQ?Gz?RQ?RQ?q= ףp?SQ?SQ??RQ?RQ?)\(?RQ?RQ?Q?RQ?RQ?zG?RQ?RQ?= ףp=?RQ?RQ??RQ?RQ?(\?RQ?RQ?RQ?RQ?RQ?RQ?\(\?\(\?Gz?{o^M@)\(@?"߼@RQ@Q?qN@|Gz@@ ףp=?Ey5@p= ף@(\?fׂ-؂@@Gz?h6@(\@hfffff?\ @ Q@ Q?֋|j@HzG@أp= ?PQ@p= ףp@(\?ʮ[@@HzG?E@S@(\@?:m@Q@n= ףp? ףp=@SQ?W(@ffffff@SQ?j2Tv@(\@SQ?z@Q@SQ?`  @zG@SQ?ڝo^ @ ףp= @SQ?U/ͫ @333333@SQ?_, @\(\@SQ?JRϊF @Q@SQ?> @Gz@SQ?@uG @ףp= @SQ?. @@SQ?6| @)\(@SQ?)b @RQ@SQ?+l @{Gz@SQ?Ld @p= ף@SQ? K~ @@SQ?o @(\@SQ?*;L @Q@SQ? @GzG@SQ? $ @q= ףp@SQ?xV4@@SQ?F贁@(\@SQ?z؛W@Q@SQ?iq@Gz@(\?|؛W@= ףp=@?G贁@ffffff@= ףp=?xV4@(\@zG? $ @Q@Q? @zG@-\(?*;L @ ףp= @?o @333333@u= ףp?!K~ @](\@Gz?Ld @Q@Q?,l @Gz@[(\?)b @ףp= @?7| @@p= ף?. @Q@HzG?>uG @(\@Q?> @@(\?IRϊF @o= ףp@433333?_, @HzG@أp= ?S/ͫ @Q@{Gz?ܝo^ @(\@Q?a  @@(\?z@p= ף@dfffff?k2Tv@{Gz@ ףp= ?W(@RQ@Gz?vS@)\(@Gz?\(\?\(\? ףp= ?\(\?\(\?gfffff?](\?](\?(\?[(\?[(\?Q?\(\?\(\?{Gz?\(\?\(\?ףp= ?\(\?\(\?433333?](\?](\?(\?](\?](\?Q?](\?](\?HzG?\(\?\(\?p= ף?\(\?\(\??\(\?\(\?\(\?\(\?\(\?Q?](\?](\?Gz?^(\?^(\?q= ףp?^(\?^(\??\(\?\(\?)\(?\(\?\(\?Q?\(\?\(\?zG?[(\?[(\?= ףp=?[(\?[(\??[(\?[(\?(\?\(\?\(\?RQ?\(\?\(\?\(\?{o^M@)\(@Gz?"߼@RQ@?pN@{Gz@Q?Ey5@p= ף@@ ףp=?fׂ-؂@@(\?h6@(\@Gz?\ @ Q@hfffff?֋|j@HzG@ Q?QQ@r= ףp@أp= ?ʮ[@@(\?E@S@(\@HzG?:m@Q@?:c@@r= ףp?*;@ףp= @zG?0@Gz@RQ? @Q@(\?&gE#@\(\@733333?: ףp@433333@p= ף?̊F@ ףp= @.\(?] ` @zG@ףp= ?%X@Q@Q? @(\@?|@ffffff@Q? t@@> ףp=@}Gz?48@Gz@Z(\?vS@Gz@Z(\?W(@> ףp=@Z(\?l2Tv@ffffff@Z(\?z@(\@Z(\?b  @Q@Z(\?ܝo^ @zG@Z(\?V/ͫ @ ףp= @Z(\?_, @333333@Z(\?KRϊF @\(\@Z(\?> @Q@Z(\?AuG @Gz@Z(\?. @ףp= @Z(\?6| @@Z(\?)b @)\(@Z(\?,l @RQ@Z(\?Ld @{Gz@Z(\?"K~ @p= ף@Z(\?o @@Z(\?*;L @(\@Z(\? @Q@Z(\? $ @GzG@Z(\?xV4@q= ףp@Z(\?G贁@@Z(\?}؛W@(\@^(\?iq@Q@SQ?z؛W@Gz@(\?G贁@> ףp=@?xV4@gfffff@= ףp=? $ @(\@zG? @Q@Q?*;L @zG@#\(?o @ףp= @? K~ @333333@k= ףp?Ld @[(\@Gz?*l @Q@Q?)b @Gz@](\?5| @ףp= @?. @Q@p= ף?>uG @(\@HzG?> @@Q?IRϊF @o= ףp@(\?_, @HzG@433333?S/ͫ @Q@أp= ?ܝo^ @(\@{Gz?a  @@Q?z@p= ף@(\?k2Tv@{Gz@dfffff?W(@RQ@ ףp= ?vS@)\(@Gz?48@@Gz?Gz?Gz? ףp= ?Gz?Gz?gfffff?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?{Gz?Gz?Gz?ףp= ?Gz?Gz?433333?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?HzG?Gz?Gz?p= ף?Gz?Gz??Gz?Gz?\(\?Gz?Gz?Q?Gz?Gz?Gz?Gz?Gz?q= ףp?Gz?Gz??Gz?Gz?)\(?Gz?Gz?Q?Gz?Gz?zG?Gz?Gz?= ףp=?Gz?Gz??Gz?Gz?(\?Gz?Gz?RQ?{o^M@)\(@\(\?"߼@RQ@Gz?pN@{Gz@?Ey5@p= ף@Q?fׂ-؂@@@ ףp=?h6@(\@(\?\ @ Q@Gz?֋|j@HzG@hfffff?RQ@r= ףp@ Q?̮[@@أp= ?E@S@(\@(\?:m@Q@HzG?;c@Gz@?*;@@n= ףp?2@ףp= @zG? @Gz@NQ?&gE#@Q@(\?: ףp@\(\@733333?̊F@433333@p= ף?] ` @ ףp= @.\(?%X@zG@ףp= ? @Q@Q?|@(\@? t@@ffffff@Q?48@> ףp=@Gz?uS@Gz@Gz?W(@Gz@Gz?j2Tv@> ףp=@Gz?z@ffffff@Gz?`  @(\@Gz?ڝo^ @Q@Gz?V/ͫ @zG@Gz?_, @ ףp= @Gz?JRϊF @333333@Gz?> @\(\@Gz?@uG @Q@Gz?. @Gz@Gz?5| @ףp= @Gz?)b @@Gz?+l @)\(@Gz?Ld @RQ@Gz? K~ @{Gz@Gz?o @p= ף@Gz?*;L @@Gz? @(\@Gz? $ @Q@Gz?xV4@GzG@Gz?F贁@q= ףp@Gz?z؛W@@Gz?iq@(\@Z(\?|؛W@Q@SQ?G贁@Gz@(\?xV4@= ףp=@? $ @ffffff@= ףp=? @(\@zG?*;L @Q@Q?o @zG@-\(?"K~ @ ףp= @?Ld @333333@u= ףp?,l @](\@Gz?)b @Q@Q?6| @Gz@[(\?. @ףp= @?>uG @(\@p= ף?> @@HzG?IRϊF @o= ףp@Q?_, @HzG@(\?S/ͫ @Q@433333?ܝo^ @(\@أp= ?^  @@{Gz?z@p= ף@Q?k2Tv@{Gz@(\?W(@RQ@dfffff?vS@)\(@ ףp= ?48@@Gz? t@@ףp= @Gz??? ףp= ???gfffff???(\???Q???{Gz???ףp= ???433333???(\???Q???HzG???p= ף??????\(\???Q???Gz???q= ףp??????)\(???Q???zG???= ףp=??????(\?{o^M@)\(@RQ?"߼@RQ@\(\?pN@{Gz@Gz?Ey5@p= ף@?fׂ-؂@@Q?h6@(\@@ ףp=?\ @ Q@(\?֋|j@HzG@Gz?RQ@r= ףp@hfffff?̮[@@ Q?F@S@(\@أp= ?:m@Q@(\?;c@Gz@IzG?*;@)\(@?0@@r= ףp? @ףp= @zG?&gE#@Gz@RQ?: ףp@Q@(\?̊F@\(\@733333?] ` @433333@p= ף?%X@ ףp= @.\(? @zG@ףp= ?|@Q@Q? t@@(\@?48@ffffff@Q?wS@> ףp=@}Gz?W(@Gz@?l2Tv@Gz@?z@> ףp=@?b  @ffffff@?ܝo^ @(\@?W/ͫ @Q@?_, @zG@?KRϊF @ ףp= @?> @333333@?AuG @\(\@?. @Q@?6| @Gz@?)b @ףp= @?,l @@?Ld @)\(@?"K~ @RQ@?o @{Gz@?*;L @p= ף@? @@? $ @(\@?xV4@Q@?G贁@GzG@?}؛W@q= ףp@?iq@@Gz?z؛W@(\@Z(\?G贁@Q@SQ?xV4@Gz@(\? $ @> ףp=@? @ffffff@= ףp=?*;L @(\@zG?o @Q@Q?K~ @zG@#\(?Ld @ ףp= @?*l @333333@k= ףp?)b @[(\@Gz?5| @Q@Q?. @Gz@\(\?>uG @(\@?> @@p= ף?IRϊF @o= ףp@HzG?_, @HzG@Q?S/ͫ @Q@(\?ܝo^ @(\@433333?^  @@أp= ?z@p= ף@|Gz?k2Tv@{Gz@Q?W(@RQ@(\?vS@)\(@dfffff?48@@ ףp= ? t@@ףp= @Gz?|@Gz@Gz?Q?Q? ףp= ?Q?Q?gfffff?Q?Q?(\?Q?Q?Q?Q?Q?{Gz?Q?Q?ףp= ?Q?Q?433333?Q?Q?(\?Q?Q?Q?Q?Q?HzG?Q?Q?p= ף?Q?Q??Q?Q?](\?Q?Q?Q?Q?Q?Gz?Q?Q?q= ףp?Q?Q??Q?Q?)\(?Q?Q?Q?Q?Q?zG?Q?Q?= ףp=?Q?Q??{o^M@)\(@(\?"߼@RQ@SQ?pN@{Gz@^(\?Ey5@p= ף@Gz?fׂ-؂@@?h6@(\@Q?[ @Q@> ףp=?֋|j@HzG@(\?PQ@q= ףp@Gz?ˮ[@@efffff?F@S@(\@Q?:m@Q@أp= ?:c@Gz@(\?*;@= ףp=@JzG?0@)\(@? @@t= ףp?&gE#@ףp= @zG?: ףp@Gz@TQ?̊F@Q@(\?] ` @\(\@333333?%X@333333@p= ף? @ ףp= @&\(?|@zG@ףp= ? t@@Q@Q?48@(\@?tS@ffffff@Q?W(@> ףp=@@Gz?j2Tv@Gz@Q?z@Gz@Q?a  @> ףp=@Q?ܝo^ @gfffff@Q?W/ͫ @(\@Q?_, @Q@Q?LRϊF @zG@Q?> @ ףp= @Q?@uG @333333@Q?. @\(\@Q?6| @Q@Q?)b @Gz@Q?+l @ףp= @Q?Ld @@Q?!K~ @)\(@Q?o @QQ@Q?*;L @{Gz@Q? @p= ף@Q? $ @@Q?xV4@(\@Q?G贁@Q@Q?|؛W@HzG@Q?iq@p= ףp@?{؛W@@Gz?G贁@(\@Z(\?xV4@Q@SQ? $ @Gz@(\? @= ףp=@?*;L @ffffff@= ףp=?o @(\@zG? K~ @Q@Q?Ld @zG@'\(?+l @ ףp= @?)b @233333@o= ףp?7| @[(\@Gz?. @Q@Q?AuG @Gz@](\?> @@?LRϊF @q= ףp@p= ף?_, @HzG@GzG?V/ͫ @Q@Q?ܝo^ @(\@(\?a  @@633333?z@p= ף@֣p= ?k2Tv@{Gz@zGz?W(@RQ@Q?xS@*\(@(\?48@@ffffff? t@@أp= @ ףp= ?|@Gz@Gz? @Q@Gz?> ףp=?> ףp=? ףp= ?@ ףp=?@ ףp=?gfffff?? ףp=?? ףp=?(\?? ףp=?? ףp=?Q?< ףp=?< ףp=?{Gz?> ףp=?> ףp=?ףp= ?> ףp=?> ףp=?433333?@ ףp=?@ ףp=?(\?@ ףp=?@ ףp=?Q?= ףp=?= ףp=?HzG?> ףp=?> ףp=?p= ף?> ףp=?> ףp=??@ ףp=?@ ףp=?](\?@ ףp=?@ ףp=?Q?? ףp=?? ףp=?Gz?? ףp=?? ףp=?q= ףp?? ףp=?? ףp=??= ףp=?= ףp=?)\(?= ףp=?= ףp=?Q?= ףp=?= ףp=?zG?= ףp=?= ףp=?> ףp=?{o^M@)\(@?"߼@RQ@(\?pN@{Gz@SQ?Ey5@p= ף@^(\?fׂ-؂@@Gz?h6@(\@?\ @Q@Q?֋|j@HzG@= ףp=?PQ@q= ףp@(\?ˮ[@@Gz?F@S@(\@efffff?:m@Q@Q? ףp=@@Gz?z@Gz@> ףp=?`  @Gz@> ףp=?۝o^ @> ףp=@> ףp=?V/ͫ @gfffff@> ףp=?_, @(\@> ףp=?KRϊF @Q@> ףp=?> @zG@< ףp=??uG @ ףp= @< ףp=?. @333333@< ףp=?5| @\(\@< ףp=?)b @Q@< ףp=?+l @Gz@< ףp=?Ld @ףp= @< ףp=? K~ @@< ףp=?o @)\(@< ףp=?*;L @QQ@< ףp=? @{Gz@; ףp=? $ @p= ף@; ףp=?xV4@@; ףp=?G贁@(\@> ףp=?{؛W@Q@> ףp=?iq@HzG@Q?{؛W@p= ףp@?G贁@@Gz?xV4@(\@^(\? $ @Q@QQ? @Gz@(\?*;L @> ףp=@?o @ffffff@? ףp=? K~ @(\@zG?Ld @Q@Q?*l @zG@&\(?)b @ ףp= @?7| @233333@n= ףp?. @[(\@Gz?AuG @Q@Q?> @@](\?MRϊF @q= ףp@?_, @HzG@p= ף?W/ͫ @ Q@GzG?ܝo^ @(\@Q?b  @@(\?z@p= ף@633333?m2Tv@|Gz@֣p= ?W(@RQ@{Gz?wS@*\(@Q?48@@(\? t@@أp= @ffffff?|@Gz@ ףp= ? @Q@Gz?%X@\(\@Gz?(\?(\? ףp= ?(\?(\?gfffff?(\?(\?(\?(\?(\?Q?(\?(\?{Gz?(\?(\?ףp= ?(\?(\?433333?(\?(\?(\?(\?(\?Q?(\?(\?HzG?(\?(\?p= ף?(\?(\??(\?(\?](\?(\?(\?Q?(\?(\?Gz?(\?(\?q= ףp?(\?(\??(\?(\?)\(?(\?(\?Q?(\?(\?zG?{o^M@)\(@> ףp=?"߼@RQ@?pN@{Gz@(\?Ey5@p= ף@SQ?fׂ-؂@@^(\?h6@(\@Gz?\ @Q@?֋|j@HzG@Q?PQ@q= ףp@= ףp=?ˮ[@@(\?F@S@(\@Gz?:m@Q@efffff? ףp=@أp= ?0@ffffff@(\? @RQ@JzG?&gE#@)\(@?: ףp@@t= ףp?̊F@ףp= @zG?] ` @Gz@TQ?%X@Q@(\? @\(\@333333?|@333333@p= ף? t@@ ףp= @&\(?48@zG@ףp= ?vS@Q@Q?W(@(\@?j2Tv@ffffff@Q?z@= ףp=@@Gz?^  @Gz@(\?۝o^ @Gz@(\?V/ͫ @> ףp=@(\?_, @gfffff@(\?LRϊF @(\@(\?> @Q@(\?AuG @zG@(\?. @ ףp= @(\?5| @333333@(\?)b @\(\@(\?+l @Q@(\?Ld @Gz@(\? K~ @ףp= @(\?o @@(\?*;L @)\(@(\? @QQ@(\? $ @{Gz@(\?xV4@p= ף@(\?G贁@@(\?{؛W@(\@(\?iq@Q@9 ףp=?{؛W@GzG@Q?G贁@p= ףp@?xV4@@Gz? $ @(\@Z(\? @Q@SQ?*;L @Gz@(\?o @= ףp=@? K~ @ffffff@= ףp=?Ld @(\@zG?*l @Q@Q?)b @zG@$\(?5| @ ףp= @?. @233333@l= ףp?@uG @[(\@Gz?> @Q@Q?LRϊF @q= ףp@](\?_, @HzG@?V/ͫ @Q@p= ף?ܝo^ @(\@FzG?a  @@Q?z@p= ף@(\?k2Tv@{Gz@533333?W(@RQ@֣p= ?vS@)\(@zGz?48@@Q? t@@أp= @(\?|@Gz@ffffff? @Q@ ףp= ?%X@\(\@Gz?] ` @433333@Gz?Gz?Gz? ףp= ?Gz?Gz?gfffff?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?{Gz?Gz?Gz?ףp= ?Gz?Gz?433333?Gz?Gz?(\?Gz?Gz?Q?Gz?Gz?HzG?Gz?Gz?p= ף?Gz?Gz??Gz?Gz?](\?Gz?Gz?Q?Gz?Gz?Gz?Gz?Gz?q= ףp?Gz?Gz??Gz?Gz?)\(?Gz?Gz?Q?{o^M@)\(@zG?"߼@RQ@> ףp=?pN@{Gz@?Ey5@p= ף@(\?fׂ-؂@@SQ?h6@(\@^(\?\ @Q@Gz?֋|j@HzG@?PQ@q= ףp@Q?ˮ[@@= ףp=?F@S@(\@(\?:m@Q@Gz? ףp=@Q?1@gfffff@ڣp= ? @{Gz@(\?&gE#@RQ@JzG?: ףp@)\(@?̊F@@t= ףp?] ` @أp= @zG?%X@Gz@TQ? @Q@(\?|@\(\@333333? t@@333333@p= ף?48@ ףp= @&\(?vS@zG@ףp= ?W(@Q@Q?k2Tv@(\@?z@ffffff@Q?`  @= ףp=@@Gz?ڝo^ @Gz@Gz?V/ͫ @Gz@Gz?_, @> ףp=@Gz?LRϊF @gfffff@Gz?> @(\@Gz?AuG @Q@Gz?. @zG@Gz?5| @ ףp= @Gz?)b @333333@Gz?+l @\(\@Gz?Ld @Q@Gz?!K~ @Gz@Gz?o @ףp= @Gz?*;L @@Gz? @)\(@Gz? $ @QQ@Gz?xV4@{Gz@Gz?G贁@p= ף@Gz?|؛W@@Gz?iq@(\@(\?{؛W@ Q@5 ףp=?G贁@FzG@Q?xV4@r= ףp@? $ @@Gz? @(\@V(\?*;L @Q@UQ?o @Gz@(\? K~ @< ףp=@?Ld @hfffff@< ףp=?*l @(\@zG?)b @Q@Q?4| @zG@&\(?. @ ףp= @?BuG @233333@n= ףp?> @[(\@Gz?MRϊF @q= ףp@Q?_, @HzG@](\?W/ͫ @Q@?ܝo^ @(\@p= ף?c  @@FzG?z@p= ף@Q?k2Tv@{Gz@(\?W(@RQ@633333?xS@*\(@֣p= ?48@@zGz? t@@أp= @Q?|@Gz@(\? @Q@ffffff?%X@\(\@ ףp= ?] ` @433333@Gz?̊F@ ףp= @Gz?ffffff?ffffff? ףp= ?hfffff?hfffff?gfffff?gfffff?gfffff?(\?gfffff?gfffff?Q?dfffff?dfffff?{Gz?ffffff?ffffff?ףp= ?ffffff?ffffff?433333?hfffff?hfffff?(\?hfffff?hfffff?Q?efffff?efffff?HzG?ffffff?ffffff?p= ף?efffff?efffff??hfffff?hfffff?](\?hfffff?hfffff?Q?gfffff?gfffff?Gz?gfffff?gfffff?q= ףp?gfffff?gfffff??ffffff?ffffff?)\(?{o^M@)\(@Q?"߼@RQ@zG?pN@{Gz@> ףp=?Ey5@p= ף@?fׂ-؂@@(\?h6@(\@SQ?\ @Q@^(\?֋|j@HzG@Gz?PQ@q= ףp@?̮[@@Q?F@S@(\@= ףp=?:m@Q@(\? ףp=@efffff?1@gfffff@Q? @(\@ڣp= ?%gE#@{Gz@(\?: ףp@RQ@JzG?̊F@*\(@?] ` @@t= ףp?%X@أp= @zG? @Gz@TQ?|@Q@(\? t@@\(\@333333?48@333333@p= ף?vS@ ףp= @&\(?W(@zG@ףp= ?k2Tv@Q@Q?z@(\@?`  @ffffff@Q?ڝo^ @= ףp=@@Gz?T/ͫ @Gz@ffffff?_, @Gz@ffffff?LRϊF @> ףp=@ffffff?> @gfffff@ffffff?BuG @(\@ffffff?. @Q@ffffff?7| @zG@dfffff?)b @ ףp= @dfffff?+l @333333@cfffff?Ld @\(\@dfffff?!K~ @Q@dfffff?o @Gz@dfffff?*;L @ףp= @dfffff? @@cfffff? $ @)\(@cfffff?xV4@QQ@cfffff?G贁@{Gz@cfffff?|؛W@p= ף@ifffff?iq@@Gz?z؛W@(\@(\?G贁@Q@9 ףp=?xV4@GzG@Q? $ @p= ףp@? @@Gz?*;L @(\@Z(\?o @Q@SQ? K~ @Gz@(\?Ld @> ףp=@?*l @ffffff@= ףp=?)b @(\@zG?4| @Q@Q?. @zG@'\(?AuG @ ףp= @?> @233333@o= ףp?LRϊF @[(\@Gz?_, @HzG@Q?V/ͫ @Q@_(\?ݝo^ @(\@?a  @@p= ף?z@p= ף@FzG?k2Tv@{Gz@Q?W(@SQ@(\?vS@)\(@533333?48@@֣p= ? t@@ףp= @zGz?|@Gz@Q? @Q@(\?%X@\(\@gfffff?] ` @533333@ ףp= ?̊F@ ףp= @Gz?: ףp@zG@Gz?Q?Q? ףp= ? Q? Q?gfffff?Q?Q?(\?Q?Q?Q?Q?Q?{Gz?Q?Q?ףp= ?Q?Q?433333? Q? Q?(\? Q? Q?Q?Q?Q?HzG?Q?Q?p= ף?Q?Q?? Q? Q?](\? Q? Q?Q?Q?Q?Gz?Q?Q?q= ףp?Q?Q??{o^M@)\(@)\(?"߼@RQ@Q?qN@{Gz@zG?Ey5@p= ף@> ףp=?fׂ-؂@@?h6@(\@(\?[ @Q@SQ?֋|j@HzG@^(\?PQ@q= ףp@Gz?̮[@@?F@S@(\@Q?:m@Q@> ףp=? ףp=@Gz?1@gfffff@efffff? @(\@Q?&gE#@p= ף@ڣp= ?: ףp@|Gz@(\?̊F@RQ@JzG?] ` @*\(@?%X@@t= ףp? @أp= @zG?|@Gz@TQ? t@@Q@(\?48@\(\@333333?vS@333333@p= ף?W(@ ףp= @&\(?k2Tv@zG@ףp= ?z@Q@Q?`  @(\@?ڝo^ @ffffff@Q?U/ͫ @= ףp=@@Gz?_, @Gz@Q?KRϊF @Gz@Q?> @> ףp=@Q?AuG @gfffff@Q?. @(\@Q?6| @Q@Q?)b @zG@Q?*l @ ףp= @Q?Ld @333333@Q? K~ @\(\@Q?o @Q@Q?*;L @Gz@Q? @ףp= @Q? $ @@Q?xV4@)\(@Q?G贁@QQ@Q?{؛W@{Gz@!Q?iq@p= ף@efffff?{؛W@@Gz?G贁@(\@(\?xV4@Q@> ףp=? $ @HzG@Q? @p= ףp@?*;L @@Gz?o @(\@^(\? K~ @Q@QQ?Ld @Gz@(\?*l @> ףp=@?)b @gfffff@? ףp=?5| @(\@zG?. @Q@Q??uG @zG@&\(?> @ ףp= @?LRϊF @233333@o= ףp?_, @HzG@Gz?X/ͫ @ Q@Q?ܝo^ @(\@](\?b  @@?z@p= ף@p= ף?m2Tv@|Gz@FzG?W(@RQ@Q?vS@)\(@(\?48@@633333? t@@أp= @֣p= ?|@Gz@{Gz? @Q@Q?%X@\(\@(\?] ` @433333@ffffff?̊F@ ףp= @ ףp= ?: ףp@zG@Gz?&gE#@Q@Gz?أp= ?أp= ? ףp= ?أp= ?أp= ?gfffff?٣p= ?٣p= ?(\?أp= ?أp= ?Q?أp= ?أp= ?{Gz?أp= ?أp= ?ףp= ?أp= ?أp= ?333333?أp= ?أp= ?(\?أp= ?أp= ?Q?٣p= ?٣p= ?HzG?أp= ?أp= ?p= ף?أp= ?أp= ??أp= ?أp= ?\(\?أp= ?أp= ?Q?ףp= ?ףp= ?Gz?ףp= ?ףp= ?p= ףp?{o^M@)\(@?"߼@RQ@*\(?pN@|Gz@Q?Ey5@p= ף@zG?eׂ-؂@@> ףp=?h6@(\@?Z @Q@(\?Ջ|j@HzG@RQ?PQ@p= ףp@\(\?ʮ[@@Gz?E@S@(\@?:m@Q@Q?:c@Gz@> ףp=?*;@> ףp=@(\?0@ffffff@Gz? @(\@efffff?&gE#@Q@Q?: ףp@p= ף@أp= ?̊F@{Gz@(\?] ` @RQ@HzG?%X@)\(@? @@p= ףp?|@ףp= @zG? t@@Gz@PQ?48@Q@(\?tS@\(\@133333?W(@433333@p= ף?j2Tv@ ףp= @"\(?z@zG@ףp= ?`  @Q@Q?ڝo^ @(\@?U/ͫ @gfffff@Q?_, @= ףp=@Gz?KRϊF @Gz@أp= ?> @Gz@أp= ?@uG @> ףp=@أp= ?. @ffffff@أp= ?6| @(\@أp= ?)b @Q@أp= ?+l @zG@أp= ?Ld @ ףp= @أp= ? K~ @333333@أp= ?o @\(\@أp= ?*;L @Q@أp= ? @Gz@أp= ? $ @ףp= @أp= ?xV4@@أp= ?G贁@)\(@أp= ?{؛W@RQ@أp= ?iq@{Gz@Q?|؛W@p= ף@mfffff?G贁@@Gz?xV4@(\@(\? $ @ Q@6 ףp=? @FzG@Q?*;L @r= ףp@?o @@Gz? K~ @(\@V(\?Ld @Q@UQ?,l @Gz@(\?)b @= ףp=@?6| @gfffff@; ףp=?. @(\@zG??uG @Q@Q?> @zG@'\(?LRϊF @ ףp= @?_, @333333@p= ףp?V/ͫ @Q@Gz?ڝo^ @(\@Q?a  @@^(\?z@p= ף@?k2Tv@{Gz@p= ף?W(@RQ@HzG?vS@)\(@Q?48@@(\? t@@ףp= @433333?|@Gz@أp= ? @Q@xGz?%X@[(\@ Q?] ` @533333@(\?̊F@ ףp= @hfffff?: ףp@zG@ףp= ?#gE#@Q@Gz? @(\@Gz?(\?(\? ףp= ?(\?(\?gfffff?(\?(\?(\?(\?(\?Q?(\?(\?{Gz?(\?(\?ףp= ?(\?(\?333333?(\?(\?(\?(\?(\?Q?(\?(\?HzG?(\?(\?p= ף?(\?(\??(\?(\?\(\?(\?(\?Q?(\?(\?Gz?{o^M@)\(@p= ףp?"߼@RQ@?pN@|Gz@*\(?Ey5@p= ף@Q?fׂ-؂@@zG?h6@(\@> ףp=?Z @Q@?Ջ|j@HzG@(\?PQ@p= ףp@RQ?ʮ[@@\(\?E@S@(\@Gz?:m@Q@?;c@Gz@Q?*;@> ףp=@= ףp=?0@ffffff@(\? @(\@Gz?&gE#@Q@ifffff?: ףp@@!Q?̊F@p= ף@أp= ?] ` @{Gz@(\?%X@RQ@HzG? @)\(@?|@@p= ףp? t@@ףp= @zG?48@Gz@PQ?vS@Q@(\?W(@\(\@/33333?j2Tv@233333@p= ף?z@ ףp= @\(?`  @zG@֣p= ?ڝo^ @Q@Q?W/ͫ @(\@?_, @gfffff@Q?KRϊF @= ףp=@Gz?> @Gz@(\?@uG @Gz@(\?. @> ףp=@(\?6| @ffffff@(\?)b @(\@(\?+l @Q@(\?Ld @zG@(\? K~ @ ףp= @(\?o @333333@(\?*;L @\(\@(\? @Q@(\? $ @Gz@(\?xV4@ףp= @(\?G贁@@(\?|؛W@)\(@(\?iq@RQ@أp= ?{؛W@{Gz@ Q?G贁@p= ף@hfffff?xV4@@Gz? $ @(\@(\? @Q@8 ףp=?*;L @GzG@Q?o @q= ףp@? K~ @@Gz?Ld @(\@Z(\?+l @Q@SQ?)b @Gz@(\?4| @= ףp=@?. @ffffff@> ףp=??uG @(\@zG?> @Q@Q?JRϊF @zG@(\(?_, @ ףp= @?V/ͫ @Q@p= ףp?ܝo^ @(\@Gz?b  @@Q?z@p= ף@[(\?j2Tv@zGz@?W(@RQ@p= ף?wS@*\(@HzG?48@@Q? t@@֣p= @(\?|@Gz@433333? @Q@أp= ?%X@\(\@xGz?] ` @433333@ Q?̊F@ ףp= @(\?: ףp@zG@hfffff?%gE#@Q@ףp= ? @(\@Gz?0@ffffff@Gz?HzG?HzG? ףp= ?HzG?HzG?gfffff?IzG?IzG?(\?HzG?HzG?Q?HzG?HzG?{Gz?HzG?HzG?ףp= ?HzG?HzG?333333?HzG?HzG?(\?HzG?HzG?Q?IzG?IzG?HzG?HzG?HzG?p= ף?HzG?HzG??HzG?HzG?\(\?HzG?HzG?Q?{o^M@)\(@Gz?"߼@RQ@p= ףp?pN@{Gz@?Ey5@p= ף@*\(?fׂ-؂@@Q?h6@(\@zG?Z @Q@> ףp=?Ջ|j@HzG@?PQ@q= ףp@(\?ʮ[@@RQ?E@S@(\@\(\?:m@Q@Gz?;c@Gz@?*;@> ףp=@Q?0@ffffff@= ףp=? @(\@(\?&gE#@Q@Gz?: ףp@zG@gfffff?̊F@@Q?] ` @p= ף@أp= ?%X@{Gz@(\? @RQ@HzG?|@)\(@? t@@@p= ףp?48@ףp= @zG?vS@Gz@PQ?W(@Q@(\?j2Tv@\(\@133333?z@433333@p= ף?`  @ ףp= @"\(?ڝo^ @zG@ףp= ?U/ͫ @Q@Q?_, @(\@?KRϊF @gfffff@Q?> @= ףp=@Gz?@uG @Gz@HzG?. @Gz@HzG?6| @> ףp=@HzG?)b @ffffff@HzG?,l @(\@HzG?Ld @Q@HzG?!K~ @zG@HzG?o @ ףp= @HzG?*;L @333333@HzG? @\(\@HzG? $ @Q@HzG?xV4@Gz@HzG?G贁@ףp= @HzG?|؛W@@HzG?iq@)\(@(\?{؛W@SQ@ңp= ?G贁@zGz@%Q?xV4@p= ף@cfffff? $ @@Gz? @(\@(\?*;L @ Q@6 ףp=?o @FzG@Q?!K~ @r= ףp@?Ld @@Gz?+l @(\@V(\?)b @Q@UQ?6| @Gz@(\?. @= ףp=@?@uG @gfffff@; ףp=?> @(\@zG?JRϊF @Q@Q?_, @zG@)\(?V/ͫ @ ףp= @?ܝo^ @(\@p= ףp?a  @@Gz?z@p= ף@Q?k2Tv@{Gz@](\?W(@RQ@?vS@)\(@p= ף?48@@HzG? t@@ףp= @Q?|@Gz@(\? @Q@333333?%X@[(\@أp= ?] ` @333333@xGz?̊F@ ףp= @ Q?: ףp@zG@(\?#gE#@Q@hfffff? @(\@ףp= ?.@efffff@Gz?*;@? ףp=@Gz??? ףp= ???gfffff???(\???Q???{Gz???ףp= ???333333???(\???Q???HzG???p= ף??????\(\?{o^M@)\(@Q?"߼@RQ@Gz?pN@{Gz@p= ףp?Ey5@p= ף@?fׂ-؂@@*\(?h6@(\@Q?\ @ Q@zG?Ջ|j@HzG@> ףp=?PQ@q= ףp@?ˮ[@@(\?E@S@(\@RQ?:m@Q@\(\?;c@Gz@Gz?*;@> ףp=@?0@ffffff@Q? @(\@= ףp=?&gE#@Q@(\?: ףp@zG@Gz?̊F@(\@ifffff?] ` @@!Q?%X@p= ף@أp= ? @{Gz@(\?|@RQ@HzG? t@@)\(@?48@@p= ףp?vS@ףp= @zG?W(@Gz@PQ?k2Tv@Q@(\?z@\(\@/33333?`  @233333@p= ף?ڝo^ @ ףp= @\(?V/ͫ @zG@֣p= ?_, @Q@Q?LRϊF @(\@?> @gfffff@Q?@uG @= ףp=@Gz?. @Gz@?5| @Gz@?)b @> ףp=@?+l @ffffff@?Ld @(\@? K~ @Q@?o @zG@?*;L @ ףp= @? @333333@? $ @\(\@?xV4@Q@?G贁@Gz@?{؛W@ףp= @?iq@@HzG?|؛W@)\(@(\?G贁@RQ@أp= ?xV4@{Gz@ Q? $ @p= ף@hfffff? @@Gz?*;L @(\@(\?o @Q@B ףp=?!K~ @HzG@~Q?Ld @p= ףp@?+l @@Gz?)b @(\@b(\?6| @Q@OQ?. @Gz@(\?@uG @> ףp=@?> @ffffff@A ףp=?KRϊF @(\@zG?_, @Q@Q?T/ͫ @zG@(\(?ܝo^ @(\@?`  @@p= ףp?z@p= ף@Gz?l2Tv@|Gz@Q?W(@RQ@[(\?uS@(\(@?48@@p= ף? t@@أp= @HzG?|@Gz@Q? @Q@(\?%X@\(\@433333?] ` @433333@أp= ?̊F@ ףp= @xGz?: ףp@zG@ Q?%gE#@Q@(\? @(\@hfffff?0@ffffff@ףp= ?*;@> ףp=@Gz?:c@Gz@Gz?p= ףp?p= ףp? ףp= ?o= ףp?o= ףp?gfffff?r= ףp?r= ףp?(\?p= ףp?p= ףp?Q?q= ףp?q= ףp?{Gz?p= ףp?p= ףp?ףp= ?p= ףp?p= ףp?333333?o= ףp?o= ףp?(\?o= ףp?o= ףp?Q?r= ףp?r= ףp?HzG?q= ףp?q= ףp?p= ף?q= ףp?q= ףp??{o^M@)\(@\(\?"߼@RQ@Q?pN@{Gz@Gz?Ey5@p= ף@p= ףp?fׂ-؂@@?h6@(\@*\(?\ @ Q@Q?֋|j@HzG@zG?PQ@q= ףp@> ףp=?ˮ[@@?E@S@(\@(\?:m@Q@RQ?;c@Gz@\(\?*;@> ףp=@Gz?0@ffffff@? @(\@Q?&gE#@Q@= ףp=?: ףp@zG@(\?̊F@ ףp= @Gz?] ` @(\@gfffff?%X@@Q? @p= ף@أp= ?|@{Gz@(\? t@@RQ@HzG?48@)\(@?vS@@p= ףp?W(@ףp= @zG?l2Tv@Gz@PQ?z@Q@(\?`  @\(\@133333?ڝo^ @433333@p= ף?U/ͫ @ ףp= @"\(?_, @zG@ףp= ?JRϊF @Q@Q?> @(\@?AuG @gfffff@Q?. @= ףp=@Gz?6| @Gz@p= ףp?)b @Gz@p= ףp?+l @> ףp=@p= ףp?Ld @ffffff@p= ףp?!K~ @(\@p= ףp?o @Q@p= ףp?*;L @zG@p= ףp? @ ףp= @p= ףp? $ @333333@p= ףp?xV4@\(\@p= ףp?G贁@Q@p= ףp?{؛W@Gz@p= ףp?iq@ףp= @?|؛W@@NzG?G贁@*\(@(\?xV4@RQ@ޣp= ? $ @|Gz@Q? @p= ף@mfffff?*;L @@Gz?o @(\@(\?!K~ @ Q@6 ףp=?Ld @FzG@Q?,l @q= ףp@?)b @@Gz?6| @(\@V(\?. @Q@UQ?@uG @Gz@(\?> @= ףp=@?JRϊF @gfffff@; ףp=?_, @(\@zG?T/ͫ @Q@Q?ڝo^ @zG@(\(?a  @@?z@p= ף@p= ףp?k2Tv@{Gz@Gz?W(@QQ@Q?vS@)\(@](\?48@@? t@@ףp= @p= ף?|@Gz@HzG? @Q@Q?%X@](\@(\?] ` @333333@433333?̊F@ ףp= @أp= ?: ףp@zG@xGz?#gE#@Q@ Q? @(\@(\?.@efffff@hfffff?*;@? ףp=@ףp= ?8c@Gz@Gz?:m@Q@Gz?zG?zG? ףp= ?zG?zG?gfffff?zG?zG?(\?zG?zG?Q?zG?zG?{Gz?zG?zG?ףp= ?zG?zG?333333?zG?zG?(\?zG?zG?Q?zG?zG?HzG?zG?zG?p= ף?{o^M@)\(@?"߼@RQ@\(\?pN@{Gz@Q?Ey5@p= ף@Gz?fׂ-؂@@p= ףp?h6@(\@?\ @ Q@*\(?֋|j@HzG@Q?RQ@r= ףp@zG?ˮ[@@> ףp=?E@S@(\@?:m@Q@(\?;c@Gz@RQ?*;@> ףp=@\(\?0@ffffff@Gz? @(\@?&gE#@Q@Q?: ףp@zG@= ףp=?̊F@ ףp= @(\?] ` @ Q@Gz?%X@(\@ifffff? @@!Q?|@p= ף@أp= ? t@@{Gz@(\?48@RQ@HzG?vS@)\(@?W(@@p= ףp?k2Tv@ףp= @zG?z@Gz@PQ?a  @Q@(\?ڝo^ @\(\@/33333?V/ͫ @333333@p= ף?_, @ ףp= @\(?LRϊF @zG@֣p= ?> @Q@Q?BuG @(\@?. @gfffff@Q?6| @= ףp=@Gz?)b @Gz@zG?+l @Gz@zG?Ld @> ףp=@zG?!K~ @ffffff@zG?o @(\@zG?*;L @Q@zG? @zG@zG? $ @ ףp= @zG?xV4@333333@zG?G贁@\(\@zG?{؛W@Q@zG?iq@Gz@p= ףp?{؛W@ףp= @?G贁@@HzG?xV4@)\(@(\? $ @RQ@أp= ? @{Gz@ Q?*;L @p= ף@hfffff?o @@Gz? K~ @(\@(\?Ld @Q@8 ףp=?*l @GzG@Q?)b @q= ףp@?5| @@Gz?. @(\@Z(\??uG @Q@SQ?> @Gz@(\?IRϊF @= ףp=@?_, @ffffff@> ףp=?T/ͫ @(\@zG?ܝo^ @Q@Q?_  @@(\(?z@p= ף@?j2Tv@zGz@p= ףp?W(@RQ@Gz?wS@*\(@Q?48@@[(\? t@@֣p= @?|@Gz@p= ף? @Q@HzG?%X@\(\@Q?] ` @233333@(\?̊F@ ףp= @433333?: ףp@zG@أp= ?%gE#@Q@xGz? @(\@ Q?0@ffffff@(\?*;@> ףp=@hfffff?:c@Gz@ףp= ?:m@Q@Gz?D@S@(\@Gz?PQ?PQ? ףp= ?OQ?OQ?gfffff?RQ?RQ?(\?OQ?OQ?Q?QQ?QQ?{Gz?PQ?PQ?ףp= ?PQ?PQ?333333?OQ?OQ?(\?OQ?OQ?Q?RQ?RQ?HzG?{o^M@)\(@p= ף?"߼@RQ@?pN@{Gz@\(\?Ey5@p= ף@Q?fׂ-؂@@Gz?h6@(\@p= ףp?[ @Q@?֋|j@HzG@*\(?RQ@r= ףp@Q?̮[@@zG?E@S@(\@> ףp=?:m@Q@?;c@Gz@(\?*;@> ףp=@RQ?0@ffffff@\(\? @(\@Gz?&gE#@Q@?: ףp@zG@Q?̊F@ ףp= @= ףp=?] ` @433333@(\?%X@Q@Gz? @(\@gfffff?|@@Q? t@@p= ף@أp= ?48@{Gz@(\?vS@RQ@HzG?W(@)\(@?l2Tv@@p= ףp?z@ףp= @zG?a  @Gz@PQ?ܝo^ @Q@(\?U/ͫ @\(\@133333?_, @333333@p= ף?JRϊF @ ףp= @"\(?> @zG@ףp= ?@uG @Q@Q?. @(\@?6| @gfffff@Q?)b @> ףp=@Gz?*l @Gz@PQ?Ld @Gz@PQ?!K~ @> ףp=@PQ?o @ffffff@PQ?*;L @(\@PQ? @Q@PQ? $ @zG@PQ?xV4@ ףp= @PQ?G贁@333333@PQ?|؛W@\(\@PQ?iq@Q@zG?{؛W@Gz@d= ףp?G贁@֣p= @?xV4@@BzG? $ @(\(@(\? @SQ@ңp= ?*;L @zGz@%Q?o @p= ף@cfffff? K~ @@Gz?Ld @(\@(\?,l @ Q@6 ףp=?)b @GzG@Q?6| @q= ףp@?. @@Gz?@uG @(\@V(\?> @Q@UQ?KRϊF @Gz@(\?_, @= ףp=@?U/ͫ @gfffff@; ףp=?ܝo^ @(\@zG?_  @Q@Q?z@p= ף@(\(?k2Tv@{Gz@?W(@RQ@p= ףp?vS@)\(@Gz?48@@Q? t@@ףp= @](\?|@Gz@? @Q@p= ף?%X@[(\@HzG?] ` @333333@Q?̊F@ ףp= @(\?: ףp@zG@433333?$gE#@Q@أp= ? @(\@xGz?.@efffff@ Q?*;@? ףp=@(\?8c@Gz@hfffff?:m@Q@ףp= ?B@S@(\@Gz?ή[@@Gz?(\?(\? ףp= ?(\?(\?gfffff?(\?(\?(\?(\?(\?Q?(\?(\?{Gz?(\?(\?أp= ?(\?(\?433333?(\?(\?(\?(\?(\?Q?{o^M@)\(@HzG?"߼@RQ@p= ף?pN@{Gz@?Ey5@p= ף@\(\?fׂ-؂@@Q?h6@(\@Gz?Z @Q@p= ףp?Ջ|j@GzG@?PQ@q= ףp@*\(?ˮ[@@Q?F@S@(\@zG?:m@Q@> ףp=?;c@Gz@?*;@> ףp=@(\?0@ffffff@QQ? @(\@Z(\?%gE#@Q@Gz?: ףp@zG@?̊F@ ףp= @Q?] ` @433333@@ ףp=?%X@HzG@(\? @Q@Gz?|@(\@gfffff? t@@@Q?48@p= ף@֣p= ?vS@zGz@(\?W(@RQ@FzG?j2Tv@(\(@?z@@l= ףp?`  @֣p= @zG?ڝo^ @Gz@LQ?V/ͫ @Q@(\?_, @\(\@433333?JRϊF @433333@p= ף?> @ ףp= @(\(?@uG @zG@ףp= ?. @Q@Q?6| @(\@?)b @ffffff@Q?,l @> ףp=@Gz?Ld @Gz@(\? K~ @Gz@(\?o @= ףp=@(\?*;L @ffffff@(\? @(\@(\? $ @Q@(\?xV4@zG@(\?G贁@ ףp= @(\?{؛W@333333@(\?iq@\(\@VQ?}؛W@Q@zG?G贁@Gz@v= ףp?xV4@ףp= @? $ @@KzG? @)\(@(\?*;L @RQ@ۣp= ?o @{Gz@#Q? K~ @p= ף@kfffff?Ld @@Gz?,l @(\@(\?)b @Q@? ףp=?6| @HzG@Q?. @p= ףp@?BuG @@Gz?> @(\@^(\?LRϊF @Q@QQ?_, @Gz@(\?W/ͫ @= ףp=@?ܝo^ @ffffff@? ףp=?b  @(\@zG?z@p= ף@Q?j2Tv@zGz@)\(?W(@RQ@?tS@(\(@q= ףp?48@@Gz? t@@ףp= @Q?|@Gz@Z(\? @Q@?%X@\(\@p= ף?] ` @433333@JzG?̊F@ ףp= @Q?: ףp@zG@(\?$gE#@Q@333333? @(\@ڣp= ?/@ffffff@zGz?*;@> ףp=@Q?:c@Gz@(\?:m@Q@efffff?D@S@(\@ ףp= ?̮[@@Gz?OQ@p= ףp@Gz?433333?433333? ףp= ?733333?733333?gfffff?633333?633333?(\?633333?633333?Q?133333?133333?{Gz?133333?133333?أp= ?833333?833333?433333?733333?733333?(\?{o^M@)\(@Q?"߼@RQ@HzG?pN@{Gz@p= ף?Ey5@p= ף@?fׂ-؂@@\(\?h6@(\@Q?Z @Q@Gz?Ջ|j@GzG@p= ףp?PQ@p= ףp@?ˮ[@@*\(?F@S@(\@Q?:m@Q@zG?;c@Gz@> ףp=?*;@> ףp=@?1@gfffff@(\? @(\@QQ?%gE#@Q@Z(\?: ףp@zG@Gz?̊F@ ףp= @?] ` @433333@Q?%X@\(\@@ ףp=? @HzG@(\?|@Q@Gz? t@@(\@gfffff?48@@Q?vS@p= ף@֣p= ?W(@zGz@(\?j2Tv@RQ@FzG?z@(\(@?`  @@l= ףp?ܝo^ @֣p= @zG?V/ͫ @Gz@LQ?_, @Q@(\?JRϊF @\(\@433333?> @433333@p= ף?@uG @ ףp= @(\(?. @zG@ףp= ?4| @Q@Q?)b @(\@?+l @gfffff@Q?Ld @> ףp=@Gz? K~ @Gz@433333?o @Gz@433333?*;L @= ףp=@433333? @ffffff@433333? $ @(\@433333?xV4@Q@433333?G贁@zG@;33333?z؛W@ ףp= @;33333?iq@333333@(\?|؛W@\(\@bQ?G贁@Q@zG?xV4@Gz@= ףp? $ @أp= @? @@QzG?*;L @*\(@(\?o @RQ@p= ?!K~ @|Gz@Q?Ld @p= ף@qfffff?+l @@Gz?)b @(\@(\?8| @Q@: ףp=?. @HzG@Q?CuG @q= ףp@?> @@Gz?LRϊF @(\@Z(\?_, @Q@SQ?X/ͫ @Gz@(\?۝o^ @> ףp=@?b  @ffffff@= ףp=?z@(\@zG?j2Tv@{Gz@Q?W(@RQ@)\(?vS@)\(@?48@@r= ףp? t@@ףp= @Gz?|@Gz@Q? @Q@\(\?%X@\(\@?] ` @333333@p= ף?̊F@ ףp= @JzG?: ףp@zG@Q?&gE#@Q@(\? @(\@233333?-@dfffff@ڣp= ?*;@< ףp=@xGz?8c@Gz@Q?:m@Q@(\?C@S@(\@ffffff?ͮ[@@ ףp= ?MQ@o= ףp@Gz?׋|j@HzG@Gz?p= ף?p= ף? ףp= ?p= ף?p= ף?gfffff?p= ף?p= ף?(\?p= ף?p= ף?Q?p= ף?p= ף?{Gz?p= ף?p= ף?أp= ?p= ף?p= ף?433333?{o^M@)\(@(\?"߼@RQ@Q?pN@{Gz@HzG?Ey5@p= ף@p= ף?fׂ-؂@@?h6@(\@\(\?[ @Q@Q?Ջ|j@GzG@Gz?PQ@p= ףp@p= ףp?ʮ[@@?F@S@(\@*\(?:m@Q@Q? ףp=@> ףp=?1@gfffff@? @(\@(\?&gE#@Q@QQ?: ףp@zG@Z(\?̊F@ ףp= @Gz?] ` @433333@?%X@\(\@Q? @q= ףp@@ ףp=?|@HzG@(\? t@@Q@Gz?48@(\@gfffff?vS@@Q?W(@p= ף@֣p= ?j2Tv@zGz@(\?z@RQ@FzG?`  @(\(@?ڝo^ @@l= ףp?V/ͫ @֣p= @zG?_, @Gz@LQ?LRϊF @Q@(\?> @\(\@433333?@uG @433333@p= ף?. @ ףp= @(\(?6| @zG@ףp= ?)b @Q@Q?,l @(\@?Ld @gfffff@Q?"K~ @> ףp=@Gz?o @Gz@p= ף?*;L @Gz@p= ף? @= ףp=@p= ף? $ @ffffff@p= ף?xV4@(\@p= ף?G贁@Q@p= ף?{؛W@zG@p= ף?iq@ ףp= @533333?|؛W@433333@(\?G贁@\(\@VQ?xV4@Q@zG? $ @Gz@v= ףp? @ףp= @?*;L @@KzG?o @)\(@(\? K~ @RQ@ۣp= ?Ld @{Gz@#Q?+l @p= ף@kfffff?)b @@Gz?6| @(\@(\?. @ Q@5 ףp=?@uG @FzG@Q?> @r= ףp@?LRϊF @@Gz?_, @(\@V(\?U/ͫ @Q@UQ?ݝo^ @Gz@(\?`  @= ףp=@?z@gfffff@; ףp=?j2Tv@zGz@zG?W(@RQ@Q?uS@(\(@)\(?48@@?~ t@@֣p= @r= ףp?|@Gz@Gz? @Q@Q?%X@\(\@Z(\?] ` @233333@?̊F@ ףp= @p= ף?: ףp@zG@JzG?$gE#@Q@Q? @(\@(\?/@ffffff@433333?*;@> ףp=@ڣp= ?:c@Gz@zGz?:m@Q@Q?E@S@(\@(\?ˮ[@@ffffff?OQ@p= ףp@ ףp= ?֋|j@HzG@Gz?Y @Q@Gz?'\(?'\(? ףp= ?.\(?.\(?gfffff?-\(?-\(?(\?,\(?,\(?Q?"\(?"\(?{Gz?)\(?)\(?أp= ?|o^M@*\(@433333?"߼@RQ@(\?pN@{Gz@Q?Ey5@p= ף@HzG?fׂ-؂@@p= ף?h6@(\@?[ @Q@\(\?֋|j@HzG@Q?PQ@p= ףp@Gz?ʮ[@@p= ףp?E@S@(\@?:m@Q@*\(? ףp=@zG?1@gfffff@> ףp=? @(\@?&gE#@Q@(\?: ףp@zG@QQ?̊F@ ףp= @Z(\?] ` @433333@Gz?%X@\(\@? @Q@Q?|@q= ףp@@ ףp=? t@@HzG@(\?48@Q@Gz?vS@(\@gfffff?W(@@Q?k2Tv@p= ף@֣p= ?z@zGz@(\?`  @RQ@FzG?ܝo^ @(\(@?V/ͫ @@l= ףp?_, @֣p= @zG?LRϊF @Gz@LQ?> @Q@(\?@uG @\(\@433333?. @433333@p= ף?6| @ ףp= @(\(?)b @zG@ףp= ?*l @Q@Q?Ld @(\@? K~ @gfffff@Q?o @> ףp=@Gz?*;L @Gz@(\(? @Gz@(\(? $ @= ףp=@(\(?xV4@ffffff@(\(?G贁@(\@(\(?{؛W@Q@(\(?iq@zG@p= ף?|؛W@ ףp= @(33333?G贁@233333@(\?xV4@](\@IQ? $ @Q@zG? @Gz@j= ףp?*;L @ףp= @ ?o @@EzG? K~ @(\(@(\?Ld @TQ@֣p= ?+l @{Gz@)Q?)b @p= ף@ffffff?5| @@Gz?. @(\@(\?BuG @Q@: ףp=?> @HzG@Q?LRϊF @q= ףp@?_, @@Gz?W/ͫ @(\@Z(\?ܝo^ @Q@SQ?b  @Gz@(\?z@> ףp=@?l2Tv@ffffff@? ףp=?W(@RQ@zG?tS@(\(@Q?48@@)\(? t@@ףp= @?|@Gz@r= ףp? @Q@Gz?%X@[(\@Q?] ` @333333@\(\?̊F@ ףp= @?: ףp@zG@p= ף?"gE#@Q@JzG? @(\@Q?0@ffffff@(\?*;@< ףp=@233333?8c@Gz@ڣp= ?:m@Q@yGz?C@S@(\@Q?ͮ[@@(\?NQ@p= ףp@ffffff?׋|j@HzG@ ףp= ?X @Q@Gz?h6@(\@Gz?ףp= ?ףp= ? ףp= ?ףp= ?ףp= ?gfffff? ףp= ? ףp= ?(\? ףp= ? ףp= ?Q? ףp= ? ףp= ?{Gz?{o^M@)\(@أp= ?"߼@RQ@433333?pN@{Gz@(\?Ey5@p= ף@Q?fׂ-؂@@HzG?h6@(\@p= ף?[ @Q@?֋|j@HzG@\(\?QQ@q= ףp@Q?ʮ[@@Gz?E@S@(\@p= ףp?:m@Q@? ףp=@Q?1@gfffff@zG? @(\@> ףp=?&gE#@Q@?: ףp@zG@(\?̊F@ ףp= @QQ?] ` @433333@Z(\?%X@\(\@Gz? @Q@?|@@Q? t@@q= ףp@@ ףp=?48@HzG@(\?vS@Q@Gz?W(@(\@gfffff?l2Tv@@Q?z@p= ף@֣p= ?`  @zGz@(\?ڝo^ @RQ@FzG?V/ͫ @(\(@?_, @@l= ףp?LRϊF @֣p= @zG?> @Gz@LQ?@uG @Q@(\?. @\(\@433333?6| @433333@p= ף?)b @ ףp= @(\(?*l @zG@ףp= ?Ld @Q@Q? K~ @(\@?o @gfffff@Q?*;L @> ףp=@Gz? @Gz@ףp= ? $ @Gz@ףp= ?xV4@= ףp=@ףp= ?G贁@ffffff@ףp= ?|؛W@(\@ףp= ?iq@Q@D\(?|؛W@zG@p= ף?G贁@ ףp= @533333?xV4@433333@(\? $ @\(\@VQ? @Q@zG?*;L @Gz@v= ףp?o @ףp= @? K~ @@KzG?Ld @)\(@(\?,l @RQ@ۣp= ?)b @{Gz@#Q?6| @p= ף@kfffff?. @@Gz?@uG @(\@(\?> @Q@? ףp=?LRϊF @HzG@Q?_, @p= ףp@?V/ͫ @@Gz?ܝo^ @(\@^(\?b  @Q@QQ?z@Gz@(\?l2Tv@= ףp=@?W(@RQ@; ףp=?uS@(\(@zG?48@@Q? t@@֣p= @)\(?|@Gz@? @Q@r= ףp?%X@\(\@Gz?] ` @433333@Q?̊F@ ףp= @Y(\?: ףp@zG@?$gE#@Q@p= ף? @(\@JzG?/@ffffff@Q?*;@< ףp=@(\?:c@Gz@233333?:m@Q@ڣp= ?D@S@(\@zGz?ˮ[@@Q?OQ@p= ףp@(\?֋|j@HzG@ffffff?Y @Q@ ףp= ?h6@(\@Gz?dׂ-؂@@Gz?Q?Q? ףp= ?Q?Q?gfffff?Q?Q?(\?Q?Q?Q?{o^M@)\(@{Gz?"߼@RQ@أp= ?pN@|Gz@433333?Ey5@p= ף@(\?fׂ-؂@@Q?h6@(\@HzG?[ @Q@p= ף?֋|j@HzG@?QQ@q= ףp@\(\?̮[@@Q?E@S@(\@Gz?:m@Q@p= ףp?;c@Gz@?*;@> ףp=@*\(?1@gfffff@Q? @(\@zG?&gE#@Q@> ףp=?: ףp@zG@?̊F@ ףp= @(\?] ` @433333@QQ?%X@\(\@Z(\? @Q@Gz?|@Gz@? t@@@Q?48@q= ףp@@ ףp=?vS@HzG@(\?W(@Q@Gz?k2Tv@(\@gfffff?z@@Q?`  @p= ף@֣p= ?ܝo^ @zGz@(\?V/ͫ @RQ@FzG?_, @(\(@?LRϊF @@l= ףp?> @֣p= @zG?@uG @Gz@LQ?. @Q@(\?6| @\(\@433333?)b @433333@p= ף?*l @ ףp= @(\(?Ld @zG@ףp= ? K~ @Q@Q?o @(\@?*;L @gfffff@Q? @> ףp=@Gz? $ @Gz@Q?xV4@Gz@Q?G贁@= ףp=@Q?{؛W@ffffff@Q?iq@(\@%ףp= ?{؛W@Q@(\(?G贁@zG@p= ף?xV4@ ףp= @C33333? $ @433333@(\? @[(\@bQ?*;L @Q@zG?o @Gz@= ףp?!K~ @أp= @?Ld @@QzG?+l @*\(@(\?)b @RQ@p= ?6| @|Gz@Q?. @p= ף@qfffff?@uG @@Gz?> @(\@(\?MRϊF @Q@: ףp=?_, @HzG@Q?W/ͫ @q= ףp@?ܝo^ @@Gz?b  @(\@Z(\?z@Q@SQ?m2Tv@Gz@(\?W(@> ףp=@?tS@(\(@? ףp=?48@@zG?~ t@@֣p= @Q?|@Gz@*\(? @Q@?%X@\(\@r= ףp?] ` @333333@Gz?̊F@ ףp= @Q?: ףp@zG@\(\?&gE#@Q@? @(\@p= ף?-@dfffff@JzG?*;@< ףp=@Q?;c@Gz@(\?:m@Q@233333?B@S@(\@ڣp= ?ɮ[@@yGz?MQ@o= ףp@Q?؋|j@IzG@(\?X @Q@ffffff?h6@(\@ ףp= ?bׂ-؂@@Gz?Ey5@p= ף@Gz??? ףp= ???gfffff???(\?zo^M@)\(@ Q?"߼@SQ@|Gz?qN@|Gz@أp= ?Ey5@p= ף@333333?eׂ-؂@@(\?h6@(\@Q?Z @Q@HzG?Ջ|j@HzG@p= ף?PQ@q= ףp@?ʮ[@@\(\?E@S@(\@Q?:m@Q@Gz?:c@Gz@p= ףp?*;@= ףp=@?0@gfffff@)\(? @(\@Q?&gE#@Q@zG?: ףp@zG@> ףp=?̊F@ ףp= @?] ` @433333@(\?%X@\(\@RQ? @Q@\(\?|@Gz@Gz? t@@(\@?48@@Q?vS@p= ףp@> ףp=?W(@HzG@(\?l2Tv@Q@Gz?z@(\@efffff?`  @@Q?ڝo^ @p= ף@ףp= ?W/ͫ @{Gz@(\?_, @RQ@GzG?KRϊF @)\(@?> @@n= ףp?AuG @ףp= @zG?. @Gz@NQ?7| @Q@(\?)b @](\@733333?,l @433333@p= ף?Ld @ ףp= @.\(?!K~ @zG@ףp= ?o @Q@Q?*;L @(\@? @ffffff@Q? $ @> ףp=@Gz?xV4@Gz@?F贁@Gz@?z؛W@= ףp=@?iq@ffffff@Q?{؛W@(\@ףp= ?G贁@Q@\(?xV4@zG@p= ף? $ @ ףp= @633333? @433333@(\?*;L @](\@UQ?o @Q@zG?"K~ @Gz@t= ףp?Ld @أp= @?,l @@JzG?)b @)\(@(\?6| @SQ@ڣp= ?. @{Gz@'Q?AuG @p= ף@ifffff?> @@Gz?KRϊF @(\@(\?_, @Q@9 ףp=?V/ͫ @GzG@Q?ݝo^ @q= ףp@?a  @@Gz?z@(\@Z(\?k2Tv@Q@SQ?W(@Gz@(\?vS@)\(@?48@@> ףp=? t@@ףp= @zG?|@Gz@Q? @Q@(\(?%X@\(\@?] ` @633333@p= ףp?̊F@ ףp= @Gz?: ףp@zG@Q?$gE#@Q@](\? @(\@?.@ffffff@p= ף?*;@> ףp=@HzG?9c@Gz@Q?:m@Q@(\?D@S@(\@433333?ˮ[@@أp= ?NQ@p= ףp@|Gz?֋|j@HzG@Q?\ @ Q@(\?h6@(\@efffff?gׂ-؂@@ ףp= ?Ey5@p= ף@Gz?qN@{Gz@Gz?Q?Q? ףp= ?Q?Q?ffffff?{o^M@)\(@(\?"߼@RQ@ Q?qN@|Gz@|Gz?Ey5@p= ף@أp= ?fׂ-؂@@333333?h6@(\@(\?Z @Q@Q?֋|j@HzG@HzG?PQ@q= ףp@p= ף?ˮ[@@?E@S@(\@\(\?:m@Q@Q?:c@Gz@Gz?*;@= ףp=@q= ףp?0@ffffff@? @(\@)\(?&gE#@Q@Q?: ףp@zG@zG?̊F@ ףp= @= ףp=?] ` @433333@?%X@](\@(\? @Q@RQ?|@Gz@\(\? t@@ףp= @Gz?48@(\@?wS@@Q?W(@p= ףp@> ףp=?j2Tv@HzG@(\?z@Q@Gz?`  @(\@efffff?ڝo^ @@Q?V/ͫ @p= ף@٣p= ?_, @{Gz@(\?KRϊF @RQ@IzG?> @)\(@?@uG @@r= ףp?. @ףp= @zG?5| @Gz@RQ?)b @Q@(\?+l @](\@733333?Ld @433333@p= ף?!K~ @ ףp= @.\(?o @zG@ףp= ?*;L @Q@Q? @(\@? $ @gfffff@Q?xV4@> ףp=@}Gz?F贁@Gz@Q?|؛W@Gz@Q?iq@= ףp=@ۙ?z؛W@gfffff@Q?G贁@(\@,ףp= ?xV4@Q@.\(? $ @zG@p= ף? @ ףp= @*33333?*;L @233333@(\?o @[(\@KQ? K~ @Q@zG?Ld @Gz@l= ףp?*l @֣p= @?)b @@FzG?4| @)\(@(\?. @QQ@֣p= ?>uG @{Gz@Q?> @p= ף@gfffff?JRϊF @@Gz?_, @(\@(\?V/ͫ @Q@: ףp=?ڝo^ @GzG@Q?b  @p= ףp@?z@@Gz?l2Tv@(\@Z(\?W(@Q@SQ?vS@Gz@(\?48@@? t@@ףp= @> ףp=?|@Gz@zG? @Q@Q?%X@\(\@(\(?] ` @433333@?̊F@ ףp= @p= ףp?: ףp@zG@Gz?"gE#@Q@Q? @(\@^(\?0@gfffff@?*;@> ףp=@p= ף?8c@Gz@HzG?:m@Q@Q?E@S@(\@(\?̮[@@433333?MQ@o= ףp@أp= ?׋|j@HzG@|Gz?[ @Q@Q?h6@(\@(\?eׂ-؂@@efffff?Ey5@p= ף@ ףp= ?pN@{Gz@Gz?"߼@RQ@Gz?{Gz?{Gz? ףp= ?{o^M@)\(@ffffff?"߼@RQ@(\?pN@{Gz@ Q?Ey5@p= ף@|Gz?gׂ-؂@@أp= ?h6@(\@333333?Z @Q@(\?֋|j@HzG@Q?PQ@p= ףp@HzG?ˮ[@@p= ף?E@S@(\@?:m@Q@\(\?;c@Gz@Q?*;@= ףp=@Gz?0@ffffff@q= ףp? @(\@?&gE#@Q@)\(?: ףp@zG@Q?̊F@ ףp= @zG?] ` @433333@= ףp=?%X@](\@? @Q@(\?|@Gz@RQ? t@@ףp= @`(\?48@Q@Gz?uS@(\@?W(@@Q?l2Tv@p= ףp@> ףp=?z@HzG@(\?`  @Q@Gz?ڝo^ @(\@efffff?V/ͫ @@Q?_, @p= ף@ףp= ?LRϊF @{Gz@(\?> @RQ@GzG?AuG @)\(@?. @@n= ףp?6| @ףp= @zG?)b @Gz@NQ?,l @Q@(\?Ld @](\@733333? K~ @433333@p= ף?o @ ףp= @.\(?*;L @zG@ףp= ? @Q@Q? $ @(\@?xV4@ffffff@Q?G贁@> ףp=@Gz?z؛W@Gz@Gz?iq@Gz@FQ?|؛W@= ףp=@?G贁@gfffff@Q?xV4@(\@ףp= ? $ @Q@\(? @zG@p= ף?*;L @ ףp= @633333?o @433333@(\?"K~ @](\@UQ?Ld @Q@zG?+l @Gz@t= ףp?)b @ףp= @?6| @@JzG?. @)\(@(\?@uG @SQ@ڣp= ?> @{Gz@'Q?LRϊF @p= ף@ifffff?_, @@Gz?V/ͫ @(\@(\?ݝo^ @Q@9 ףp=?`  @HzG@Q?z@q= ףp@?k2Tv@@Gz?W(@(\@Z(\?uS@Q@RQ?48@@(\? t@@ףp= @?|@Gz@> ףp=? @Q@zG?%X@\(\@Q?] ` @433333@(\(?̊F@ ףp= @?: ףp@zG@p= ףp?$gE#@Q@Gz? @(\@Q?.@ffffff@](\?*;@? ףp=@?9c@Gz@p= ף?:m@Q@HzG?D@S@(\@Q?ή[@@(\?NQ@p= ףp@433333?֋|j@HzG@أp= ?X @Q@|Gz?h6@(\@Q?gׂ-؂@@(\?Ey5@p= ף@dfffff?qN@{Gz@ ףp= ?"߼@RQ@Gz?|o^M@)\(@Gz?{o^M@)\(@ ףp= ?"߼@RQ@ffffff?pN@{Gz@(\?Ey5@p= ף@ Q?gׂ-؂@@|Gz?h6@(\@أp= ?\ @ Q@333333?֋|j@HzG@(\?PQ@p= ףp@Q?ˮ[@@HzG?E@S@(\@p= ף?:m@Q@?;c@Gz@\(\?*;@> ףp=@Q?0@ffffff@Gz? @(\@q= ףp?&gE#@Q@?: ףp@zG@)\(?̊F@ ףp= @Q?] ` @333333@zG?%X@](\@= ףp=? @Q@?|@Gz@(\? t@@ףp= @RQ?48@@\(\?wS@Q@Gz?W(@(\@?l2Tv@@Q?z@p= ףp@> ףp=?`  @HzG@(\?ܝo^ @Q@Gz?U/ͫ @(\@efffff?_, @@Q?KRϊF @p= ף@٣p= ?> @{Gz@(\?@uG @RQ@IzG?. @)\(@?5| @@r= ףp?)b @ףp= @zG?+l @Gz@RQ?Ld @Q@(\?!K~ @](\@733333?o @433333@p= ף?*;L @ ףp= @.\(? @zG@ףp= ? $ @Q@Q?xV4@(\@?G贁@ffffff@Q?|؛W@> ףp=@}Gz?iq@Gz@Gz?z؛W@Gz@Q?F贁@= ףp=@b?xV4@ffffff@Q? $ @(\@֣p= ? @Q@.\(?*;L @zG@p= ף?o @ ףp= @*33333? K~ @333333@(\?Ld @[(\@KQ?*l @Q@zG?)b @Gz@l= ףp?4| @ףp= @?. @@FzG??uG @)\(@(\?> @QQ@֣p= ?JRϊF @{Gz@Q?_, @p= ף@gfffff?T/ͫ @@Gz?ڝo^ @(\@(\?`  @Q@: ףp=?z@GzG@~Q?j2Tv@o= ףp@?W(@@Gz?tS@(\@Z(\?48@Q@RQ? t@@ףp= @(\?|@Gz@? @Q@> ףp=?%X@\(\@zG?] ` @233333@Q?̊F@ ףp= @(\(?: ףp@zG@?&gE#@Q@p= ףp? @(\@Gz?,@dfffff@Q?*;@> ףp=@](\?:c@Gz@?:m@Q@p= ף?B@S@(\@HzG?̮[@@Q?OQ@p= ףp@(\?׋|j@HzG@433333?W @Q@أp= ?h6@(\@{Gz?eׂ-؂@@Q?Ey5@p= ף@(\?oN@zGz@dfffff?"߼@RQ@ ףp= ?zo^M@)\(@Gz?@@Gz?"߼@RQ@ ףp= ?pN@{Gz@ffffff?Ey5@p= ף@(\?eׂ-؂@@ Q?h6@(\@|Gz?\ @ Q@أp= ?֋|j@HzG@333333?PQ@p= ףp@(\?ʮ[@@Q?F@S@(\@HzG?:m@Q@p= ף?;c@Gz@?*;@> ףp=@\(\?0@ffffff@Q? @(\@Gz?&gE#@Q@q= ףp?: ףp@zG@?̊F@ ףp= @)\(?] ` @333333@Q?%X@\(\@zG? @Q@= ףp=?|@Gz@? t@@أp= @(\?48@@RQ?uS@Gz@`(\?W(@Q@Gz?k2Tv@(\@?z@@Q?`  @p= ףp@> ףp=?ܝo^ @HzG@(\?V/ͫ @Q@Gz?_, @(\@efffff?KRϊF @@Q?> @p= ף@ףp= ?AuG @{Gz@(\?. @RQ@GzG?7| @)\(@?)b @@n= ףp?,l @ףp= @zG?Ld @Gz@NQ?"K~ @Q@(\?o @](\@733333?*;L @433333@p= ף? @ ףp= @.\(? $ @zG@ףp= ?xV4@Q@Q?G贁@(\@?z؛W@gfffff@Q?iq@= ףp=@Q?|؛W@Gz@}Gz?G贁@Gz@DQ?xV4@= ףp=@? $ @hfffff@Q? @(\@Lףp= ?*;L @Q@\(?o @zG@p= ף?"K~ @ ףp= @633333?Ld @333333@(\?,l @](\@UQ?)b @Q@zG?6| @Gz@t= ףp?. @ףp= @?AuG @@JzG?> @)\(@(\?LRϊF @SQ@ڣp= ?_, @{Gz@'Q?V/ͫ @p= ף@ifffff?ܝo^ @@Gz?`  @(\@(\?z@Q@9 ףp=?k2Tv@HzG@Q?W(@r= ףp@?uS@@Gz?48@(\@\(\? t@@ףp= @RQ?|@Gz@(\? @Q@?%X@\(\@> ףp=?] ` @433333@zG?̊F@ ףp= @Q?: ףp@zG@(\(?$gE#@Q@? @(\@p= ףp?.@ffffff@Gz?*;@= ףp=@Q?9c@Gz@](\?:m@Q@?D@S@(\@p= ף?ˮ[@@HzG?NQ@p= ףp@Q?؋|j@IzG@(\?X @Q@433333?h6@(\@أp= ?cׂ-؂@@{Gz?Ey5@p= ף@Q?qN@|Gz@(\?"߼@RQ@dfffff?|o^M@)\(@ ףp= ?@@Gz?Q?Q?Gz?pN@{Gz@ ףp= ?Ey5@p= ף@ffffff?fׂ-؂@@(\?h6@(\@ Q?\ @ Q@|Gz?׋|j@IzG@أp= ?RQ@r= ףp@333333?ʮ[@@(\?F@S@(\@Q?:m@Q@HzG?;c@Gz@p= ף?*;@> ףp=@?0@ffffff@\(\? @(\@Q?&gE#@Q@Gz?: ףp@zG@p= ףp?̊F@ ףp= @?] ` @433333@)\(?%X@\(\@Q? @Q@zG?|@Gz@= ףp=? t@@أp= @?48@@(\?vS@)\(@PQ?W(@Gz@\(\?l2Tv@Q@Gz?z@(\@?a  @@Q?ܝo^ @p= ףp@> ףp=?V/ͫ @GzG@(\?_, @Q@Gz?KRϊF @(\@efffff?> @@Q?AuG @p= ף@٣p= ?. @{Gz@(\?5| @RQ@IzG?)b @)\(@?+l @@r= ףp?Ld @ףp= @zG?!K~ @Gz@RQ?o @Q@(\?*;L @](\@733333? @433333@p= ף? $ @ ףp= @.\(?xV4@zG@ףp= ?G贁@Q@Q?|؛W@(\@?iq@ffffff@?z؛W@> ףp=@?G贁@Gz@}Gz?xV4@Gz@Q? $ @< ףp=@ۙ? @hfffff@Q?*;L @(\@,ףp= ?o @Q@[(?K~ @zG@p= ף?Ld @ ףp= @*33333?*l @333333@(\?)b @\(\@KQ?4| @Q@zG?. @Gz@l= ףp?>uG @ףp= @?> @@FzG?IRϊF @)\(@(\?_, @QQ@֣p= ?T/ͫ @{Gz@Q?ڝo^ @p= ף@gfffff?_  @@Gz?z@(\@(\?l2Tv@Q@1 ףp=?W(@FzG@Q?wS@q= ףp@?48@@Gz? t@@(\@\(\?|@Gz@RQ? @Q@(\?%X@\(\@?] ` @233333@> ףp=?̊F@ ףp= @zG?: ףp@zG@Q?&gE#@Q@(\(? @(\@?0@ffffff@p= ףp?*;@> ףp=@Gz?7c@Gz@Q?:m@Q@](\?E@S@(\@?̮[@@p= ף?LQ@n= ףp@HzG?׋|j@HzG@Q?Z @Q@(\?h6@(\@333333?aׂ-؂@@أp= ?Ey5@p= ף@{Gz?pN@{Gz@Q?"߼@SQ@(\?zo^M@)\(@dfffff?@@ףp= ???Gz?@?@?Gz?Ey5@p= ף@ ףp= ?fׂ-؂@@ffffff?h6@(\@(\?[ @Q@ Q?׋|j@IzG@|Gz?RQ@r= ףp@أp= ?̮[@@333333?F@S@(\@(\?:m@Q@Q? ףp=@p= ף?0@gfffff@? @(\@\(\?&gE#@Q@Q?: ףp@zG@Gz?̊F@ ףp= @p= ףp?] ` @433333@?%X@\(\@)\(? @Q@Q?|@Gz@zG? t@@أp= @= ףp=?48@@?wS@*\(@(\?W(@> ףp=@RQ?j2Tv@Gz@`(\?z@Q@Gz?`  @(\@?ܝo^ @@Q?V/ͫ @p= ףp@> ףp=?_, @GzG@(\?LRϊF @Q@Gz?> @(\@efffff?@uG @@Q?. @p= ף@ףp= ?7| @{Gz@(\?)b @RQ@GzG?,l @)\(@?Ld @@n= ףp?!K~ @ףp= @zG?o @Gz@NQ?*;L @Q@(\? @](\@733333? $ @333333@p= ף?xV4@ ףp= @.\(?G贁@zG@ףp= ?{؛W@Q@Q?iq@(\@Q?|؛W@ffffff@Q?F贁@= ףp=@Q?xV4@Gz@Gz? $ @Gz@<Q? @? ףp=@?*;L @ffffff@R?o @(\@ףp= ? K~ @Q@N\(?Ld @zG@p= ף?,l @ ףp= @633333?)b @333333@(\?6| @\(\@UQ?. @Q@zG?@uG @Gz@t= ףp?> @ףp= @?KRϊF @@JzG?_, @)\(@(\?V/ͫ @SQ@ڣp= ?ܝo^ @|Gz@'Q?`  @p= ף@ifffff?z@@Gz?j2Tv@(\@(\?W(@Q@B ףp=?wS@IzG@Q?48@q= ףp@? t@@@Gz?|@Gz@\(\? @Q@RQ?%X@\(\@(\?] ` @433333@?̊F@ ףp= @> ףp=?: ףp@zG@zG?$gE#@Q@Q? @(\@(\(?.@ffffff@?*;@? ףp=@p= ףp?9c@Gz@Gz?:m@Q@Q?D@S@(\@](\?ή[@@?NQ@p= ףp@p= ף?Ջ|j@HzG@HzG?X @Q@Q?h6@(\@(\?cׂ-؂@@333333?Ey5@p= ף@գp= ?mN@yGz@{Gz?"߼@RQ@Q?|o^M@)\(@(\?@@lfffff?dQ?dQ? ףp= ?\R?\R?Gz?Q?Q?Gz?fׂ-؂@@ ףp= ?h6@(\@ffffff?[ @Q@(\?֋|j@HzG@ Q?RQ@r= ףp@|Gz?ͮ[@@أp= ?F@S@(\@333333?:m@Q@(\? ףp=@HzG?0@gfffff@p= ף? @(\@?&gE#@Q@\(\?: ףp@zG@Q?̊F@ ףp= @Gz?] ` @433333@p= ףp?%X@\(\@? @Q@)\(?|@Gz@Q? t@@ףp= @zG?48@@= ףp=?wS@*\(@?W(@SQ@(\?l2Tv@> ףp=@PQ?z@Gz@\(\?b  @Q@Gz?۝o^ @(\@?W/ͫ @@Q?_, @p= ףp@> ףp=?KRϊF @GzG@(\?> @Q@Gz?@uG @(\@efffff?. @@Q?6| @p= ף@٣p= ?)b @{Gz@(\?+l @RQ@IzG?Ld @)\(@? K~ @@r= ףp?o @ףp= @zG?*;L @Gz@RQ? @Q@(\? $ @](\@733333?xV4@333333@p= ף?G贁@ ףp= @.\(?{؛W@zG@ףp= ?iq@Q@ףp= ?|؛W@(\@ףp= ?G贁@gfffff@<ףp= ?xV4@? ףp=@ףp= ? $ @Gz@Gz? @Gz@Q?*;L @> ףp=@c?o @efffff@Q?K~ @(\@֣p= ?Ld @Q@.\(?*l @zG@p= ף?)b @ ףp= @*33333?4| @233333@(\?. @\(\@KQ?>uG @Q@zG?> @Gz@l= ףp?JRϊF @ףp= @?_, @@FzG?T/ͫ @)\(@(\?ڝo^ @QQ@֣p= ?^  @zGz@Q?z@p= ף@gfffff?i2Tv@@Gz?W(@(\@(\?uS@Q@: ףp=?48@GzG@~Q? t@@o= ףp@?|@@Gz? @Q@\(\?%X@\(\@RQ?] ` @233333@(\?̊F@ ףp= @?: ףp@zG@> ףp=?&gE#@Q@zG? @(\@Q?0@gfffff@(\(?*;@> ףp=@?:c@Gz@p= ףp?:m@Q@Gz?B@S@(\@Q?̮[@@](\?PQ@p= ףp@?׋|j@HzG@p= ף?W @Q@HzG?h6@(\@Q?dׂ-؂@@(\?Ey5@p= ף@333333?lN@yGz@ۣp= ?"߼@SQ@{Gz?zo^M@)\(@Q?@@(\?[ףp= ?[ףp= ?dfffff?ףp= ?ףp= ?ףp= ?&ףp= ?&ףp= ?Gz?֣p= ?֣p= ?Gz?h6@(\@ ףp= ?[ @Q@ffffff?֋|j@HzG@(\?QQ@q= ףp@ Q?ͮ[@@|Gz?G@S@(\@أp= ?:m@Q@433333? ףp=@Q?0@gfffff@HzG? @(\@p= ף?&gE#@Q@?: ףp@zG@\(\?̊F@ ףp= @Q?] ` @433333@Gz?%X@\(\@p= ףp? @Q@?|@Gz@)\(? t@@ףp= @Q?48@@zG?wS@*\(@= ףp=?W(@SQ@?k2Tv@ffffff@(\?z@> ףp=@RQ?`  @Gz@`(\?ܝo^ @Q@Gz?U/ͫ @(\@?_, @@Q?LRϊF @p= ףp@> ףp=?> @GzG@(\?AuG @Q@Gz?. @(\@efffff?6| @@Q?)b @p= ף@ףp= ?,l @{Gz@(\?Ld @RQ@GzG?"K~ @)\(@?o @@n= ףp?*;L @ףp= @zG? @Gz@NQ? $ @Q@(\?xV4@\(\@733333?G贁@333333@p= ף?{؛W@ ףp= @.\(?iq@zG@\(?|؛W@Q@\(?G贁@(\@>\(?xV4@gfffff@\(? $ @< ףp=@>\(? @Gz@}Gz?*;L @Gz@DQ?o @< ףp=@?"K~ @hfffff@Q?Ld @(\@Mףp= ?,l @Q@\(?)b @zG@p= ף?7| @ ףp= @633333?. @433333@(\?AuG @\(\@UQ?> @Q@zG?LRϊF @Gz@t= ףp?_, @ףp= @?V/ͫ @@JzG?ܝo^ @)\(@(\?`  @RQ@ڣp= ?z@|Gz@'Q?k2Tv@p= ף@ifffff?W(@@Gz?tS@(\@(\?48@ Q@9 ףp=? t@@GzG@Q?|@r= ףp@? @Q@Gz?%X@\(\@\(\?] ` @433333@RQ?̊F@ ףp= @(\?: ףp@zG@?%gE#@Q@> ףp=? @(\@zG?.@efffff@Q?*;@= ףp=@(\(?9c@Gz@?:m@Q@p= ףp?D@S@(\@Gz?ˮ[@@Q?NQ@p= ףp@](\?ً|j@JzG@?X @Q@p= ף?h6@(\@EzG?cׂ-؂@@Q?Ey5@p= ף@(\?nN@zGz@333333?"߼@RQ@գp= ?xo^M@'\(@|Gz?@@"Q?[(?[(?(\?R\(?R\(?jfffff?[(?[(? ףp= ?\(?\(?Gz?[(?[(?Gz?Z @Q@ ףp= ?Ջ|j@GzG@ffffff?PQ@q= ףp@(\?ˮ[@@ Q?F@S@(\@|Gz?:m@Q@أp= ?;c@Gz@433333?*;@> ףp=@(\?0@ffffff@Q? @(\@HzG?%gE#@Q@p= ף?: ףp@zG@?̊F@ ףp= @\(\?] ` @333333@Q?%X@\(\@Gz? @Q@p= ףp?|@Gz@? t@@أp= @*\(?48@@Q?vS@*\(@zG?W(@RQ@> ףp=?j2Tv@{Gz@?z@ffffff@(\?`  @> ףp=@QQ?ڝo^ @Gz@^(\?V/ͫ @Q@Gz?_, @(\@?KRϊF @@Q?> @q= ףp@B ףp=?@uG @HzG@(\?. @Q@Gz?5| @(\@ifffff?)b @@!Q?+l @p= ף@أp= ?Ld @{Gz@(\? K~ @RQ@HzG?o @)\(@?*;L @@p= ףp? @ףp= @zG? $ @Gz@PQ?xV4@Q@(\?F贁@\(\@/33333?{؛W@333333@p= ף?iq@ ףp= @p= ף?|؛W@zG@p= ף?G贁@Q@p= ף?xV4@(\@p= ף?$ @hfffff@p= ף? @< ףp=@p= ף?*;L @Gz@Gz?o @Gz@Q?K~ @< ףp=@?Ld @hfffff@Q?*l @(\@:ףp= ?)b @Q@\(?4| @zG@p= ף?. @ ףp= @033333?BuG @333333@(\?> @\(\@PQ?LRϊF @Q@zG?_, @Gz@p= ףp?V/ͫ @ףp= @?ܝo^ @@HzG?a  @)\(@(\?z@RQ@أp= ?l2Tv@{Gz@ Q?W(@p= ף@hfffff?vS@@Gz?48@(\@(\? t@@ Q@8 ףp=?|@FzG@Q? @p= ףp@?%X@\(\@Gz?] ` @433333@`(\?̊F@ ףp= @PQ?: ףp@zG@(\?%gE#@Q@? @(\@@ ףp=?0@ffffff@zG?*;@> ףp=@Q?:c@Gz@(\(?:m@Q@?E@S@(\@p= ףp?̮[@@Gz?RQ@r= ףp@Q?֋|j@HzG@Z(\?Z @Q@?h6@(\@p= ף?gׂ-؂@@HzG?Ey5@p= ף@Q?oN@zGz@(\?"߼@RQ@133333?|o^M@*\(@أp= ?@@yGz?p= ף?p= ף?!Q?p= ף?p= ף?(\?p= ף?p= ף?hfffff?|p= ף?|p= ף?ףp= ?p= ף?p= ף?Gz?\p= ף?\p= ף?Gz?Ջ|j@GzG@ ףp= ?PQ@q= ףp@ffffff?ˮ[@@(\?E@S@(\@ Q?:m@Q@|Gz? ףp=@433333?0@ffffff@(\? @(\@Q?%gE#@Q@HzG?: ףp@zG@p= ף?̊F@ ףp= @?] ` @333333@\(\?%X@\(\@Q? @Q@Gz?|@Gz@p= ףp? t@@ףp= @?48@@*\(?vS@*\(@Q?W(@RQ@zG?j2Tv@{Gz@= ףp=?z@(\@?`  @ffffff@(\?ܝo^ @> ףp=@QQ?U/ͫ @Gz@^(\?_, @Q@Gz?JRϊF @(\@?> @@Q?AuG @q= ףp@> ףp=?. @HzG@(\?7| @Q@Gz?)b @(\@gfffff?,l @@Q?Ld @p= ף@أp= ? K~ @{Gz@(\?o @RQ@HzG?*;L @)\(@? @@p= ףp? $ @ףp= @zG?xV4@Gz@PQ?G贁@Q@(\?{؛W@\(\@133333?iq@333333@733333?|؛W@ ףp= @733333?G贁@zG@733333?xV4@Q@733333? $ @(\@033333? @gfffff@033333?*;L @= ףp=@033333?o @Gz@Gz? K~ @Gz@Q?Ld @= ףp=@?+l @gfffff@Q?)b @(\@ ףp= ?6| @Q@ \(?. @zG@p= ף?BuG @ ףp= @"33333?> @233333@(\?LRϊF @](\@CQ?_, @Q@zG?X/ͫ @Gz@d= ףp?ڝo^ @֣p= @?b  @@BzG?z@(\(@(\?l2Tv@RQ@ңp= ?W(@zGz@%Q?vS@p= ף@cfffff?48@@Gz? t@@(\@(\?|@ Q@6 ףp=? @FzG@Q?%X@[(\@?] ` @533333@Gz?̊F@ ףp= @`(\?: ףp@zG@PQ?#gE#@Q@(\? @(\@?.@efffff@@ ףp=?*;@? ףp=@zG?8c@Gz@Q?:m@Q@(\(?F@S@(\@?ͮ[@@p= ףp?QQ@q= ףp@Gz?ԋ|j@GzG@Q?[ @Q@Z(\?h6@(\@?fׂ-؂@@p= ף?Ey5@p= ף@HzG?pN@{Gz@Q?"߼@SQ@(\?{o^M@)\(@833333?@@ԣp= ?_33333?_33333?~Gz?<33333?<33333?Q?33333?33333?(\?R33333?R33333?`fffff?(33333?(33333?ףp= ?<33333?<33333?Gz??33333??33333?Gz?PQ@q= ףp@ ףp= ?ˮ[@@ffffff?E@S@(\@(\?:m@Q@ Q? @(\@?@uG @@Q?. @q= ףp@B ףp=?5| @HzG@(\?)b @ Q@Gz?+l @(\@ifffff?Ld @@!Q?!K~ @p= ף@أp= ?o @{Gz@(\?*;L @RQ@HzG? @)\(@? $ @@p= ףp?xV4@ףp= @zG?G贁@Gz@PQ?{؛W@Q@(\?iq@\(\@(\?|؛W@333333@(\?G贁@ ףp= @(\?xV4@zG@(\? $ @Q@(\? @(\@(\?*;L @ffffff@(\?o @> ףp=@(\? K~ @Gz@Gz?Ld @Gz@Q?*l @> ףp=@?)b @ffffff@R?4| @(\@ףp= ?. @Q@@\(??uG @zG@p= ף?> @ ףp= @033333?LRϊF @333333@(\?_, @\(\@PQ?V/ͫ @Q@zG?ܝo^ @Gz@p= ףp?a  @ףp= @?z@@HzG?l2Tv@)\(@(\?W(@RQ@أp= ?vS@{Gz@ Q?48@p= ף@hfffff? t@@@Gz?|@(\@(\? @Q@B ףp=?%X@HzG@Q?] ` @433333@?̊F@ ףp= @Gz?: ףp@zG@`(\?%gE#@Q@PQ? @(\@(\?0@ffffff@?*;@> ףp=@@ ףp=?:c@Gz@zG?:m@Q@Q?D@S@(\@(\(?̮[@@?PQ@p= ףp@p= ףp?֋|j@HzG@Gz?] @ Q@Q?h6@(\@^(\?dׂ-؂@@?Ey5@p= ף@p= ף?rN@|Gz@HzG?"߼@RQ@Q?yo^M@(\(@(\?@@233333?(\?(\?ڣp= ?(\?(\?yGz?(\?(\?Q?(\?(\?(\?(\?(\?gfffff?(\?(\?ףp= ?(\?(\?Gz?(\?(\?Gz?ˮ[@@ ףp= ?E@S@(\@ffffff?:m@Q@(\?;c@Gz@ Q?*;@? ףp=@|Gz?1@gfffff@أp= ? @(\@433333?&gE#@Q@(\?: ףp@zG@Q?̊F@ ףp= @HzG?] ` @333333@p= ף?%X@\(\@? @Q@\(\?|@Gz@Q? t@@ףp= @Gz?48@@p= ףp?vS@)\(@?W(@RQ@*\(?l2Tv@|Gz@Q?z@p= ף@zG?^  @Q@= ףp=?ڝo^ @(\@?U/ͫ @gfffff@(\?_, @= ףp=@QQ?KRϊF @Gz@^(\?> @Q@Gz?@uG @(\@?. @@Q?7| @q= ףp@> ףp=?)b @HzG@(\?+l @Q@Gz?Ld @(\@gfffff?!K~ @@Q?o @p= ף@أp= ?*;L @{Gz@(\? @RQ@HzG? $ @)\(@?xV4@@p= ףp?G贁@ףp= @zG?{؛W@Gz@PQ?iq@Q@WQ?|؛W@](\@WQ?G贁@433333@VQ?xV4@ ףp= @VQ? $ @zG@VQ? @Q@VQ?*;L @(\@PQ?o @gfffff@PQ? K~ @= ףp=@PQ?Ld @Gz@Gz?*l @Gz@Q?)b @= ףp=@?5| @gfffff@Q?. @(\@ ףp= ?@uG @Q@ \(?> @zG@p= ף?KRϊF @ ףp= @>33333?_, @433333@(\?V/ͫ @[(\@]Q?ݝo^ @Q@zG?`  @Gz@|= ףp?z@أp= @?j2Tv@@NzG?W(@*\(@(\?uS@RQ@ޣp= ?48@|Gz@Q? t@@p= ף@mfffff?|@@Gz? @(\@(\?%X@ Q@@ ףp=?] ` @533333@Q?̊F@ ףp= @?: ףp@zG@Gz?#gE#@Q@`(\? @(\@PQ?.@efffff@(\?*;@? ףp=@?8c@Gz@@ ףp=?:m@Q@zG?B@S@(\@Q?ή[@@(\(?QQ@q= ףp@?׋|j@HzG@p= ףp?[ @Q@Gz?h6@(\@Q?fׂ-؂@@Z(\?Ey5@p= ף@?pN@{Gz@p= ף?"߼@QQ@HzG?{o^M@)\(@Q?@@(\?`Q?`Q?433333?FQ?FQ?p= ?MQ?MQ?~Gz?ZQ?ZQ?Q?-Q?-Q?(\?mQ?mQ?afffff?AQ?AQ?ףp= ?ZQ?ZQ?Gz?UQ?UQ?Gz?E@S@(\@ ףp= ?:m@Q@ffffff?;c@Gz@(\?*;@= ףp=@ Q?1@gfffff@|Gz? @(\@أp= ?&gE#@Q@333333?: ףp@zG@(\?̊F@ ףp= @Q?] ` @333333@HzG?%X@\(\@p= ף? @Q@?|@Gz@\(\? t@@ףp= @Q?48@@Gz?vS@)\(@p= ףp?W(@RQ@?l2Tv@|Gz@*\(?z@p= ף@Q?b  @@zG?ڝo^ @Q@= ףp=?V/ͫ @(\@?_, @gfffff@(\?KRϊF @= ףp=@QQ?> @Gz@^(\?AuG @Q@Gz?. @(\@?6| @@Q?)b @q= ףp@B ףp=?+l @IzG@(\?Ld @Q@Gz?!K~ @(\@ifffff?o @@!Q?*;L @p= ף@أp= ? @{Gz@(\? $ @RQ@HzG?xV4@)\(@?G贁@@p= ףp?{؛W@ףp= @zG?iq@Gz@zG?{؛W@Q@zG?G贁@\(\@zG?xV4@333333@zG? $ @ ףp= @zG? @zG@zG?*;L @Q@zG?o @(\@zG?#K~ @hfffff@zG?Ld @< ףp=@zG?-l @Gz@Gz?)b @Gz@Q?4| @< ףp=@?. @hfffff@Q?>uG @(\@:ףp= ?> @Q@\(?JRϊF @zG@p= ף?_, @ ףp= @033333?V/ͫ @333333@(\?ܝo^ @\(\@PQ?`  @Q@zG?z@Gz@p= ףp?l2Tv@ףp= @?W(@@HzG?vS@)\(@(\?48@RQ@أp= ? t@@{Gz@ Q?|@p= ף@hfffff? @@Gz?%X@(\@(\?] ` @Q@@ ףp=?̊F@ ףp= @Q?: ףp@zG@?%gE#@Q@Gz? @(\@`(\?0@ffffff@PQ?*;@> ףp=@(\?:c@Gz@?:m@Q@@ ףp=?D@S@(\@zG?̮[@@Q?OQ@p= ףp@(\(?֋|j@HzG@?Z @Q@p= ףp?h6@(\@Gz?gׂ-؂@@Q?Ey5@p= ף@^(\?nN@zGz@?"߼@RQ@p= ף?|o^M@*\(@HzG?@@Q?zG?zG?(\?zG?zG??33333?zG?zG?֣p= ?zG?zG?vGz?{G?{G? Q?zG?zG?(\?zG?zG?hfffff?zG?zG?ףp= ?{G?{G?Gz?zG?zG?Gz?:m@Q@ ףp= ?;c@Gz@ffffff?*;@= ףp=@(\?1@gfffff@ Q? @(\@|Gz?'gE#@Q@أp= ?: ףp@zG@333333?̊F@ ףp= @(\?] ` @433333@Q?%X@\(\@HzG? @Q@p= ף?|@Gz@? t@@ףp= @\(\?48@@Q?vS@)\(@Gz?W(@RQ@p= ףp?k2Tv@{Gz@?z@p= ף@*\(?b  @@Q?ڝo^ @zG@zG?U/ͫ @Q@= ףp=?_, @(\@?KRϊF @gfffff@(\?> @= ףp=@QQ?@uG @Gz@^(\?. @Q@Gz?6| @(\@?)b @@Q?,l @q= ףp@> ףp=?Ld @GzG@(\?!K~ @Q@Gz?o @(\@gfffff?*;L @@Q? @p= ף@أp= ? $ @{Gz@(\?xV4@RQ@HzG?G贁@)\(@?|؛W@@p= ףp?iq@ףp= @p= ףp?{؛W@Gz@p= ףp?G贁@Q@v= ףp?xV4@](\@v= ףp? $ @433333@v= ףp? @ ףp= @v= ףp?*;L @zG@v= ףp?o @Q@v= ףp? K~ @(\@p= ףp?Ld @gfffff@p= ףp?*l @= ףp=@p= ףp?)b @Gz@Gz?5| @Gz@Q?. @= ףp=@?@uG @gfffff@Q?> @(\@ ףp= ?JRϊF @Q@ \(?_, @zG@p= ף?W/ͫ @ ףp= @"33333?۝o^ @233333@(\?b  @](\@CQ?z@Q@zG?l2Tv@Gz@d= ףp?W(@֣p= @?wS@@BzG?48@(\(@(\? t@@SQ@ңp= ?|@zGz@%Q? @p= ף@cfffff?%X@@Gz?] ` @(\@(\?̊F@ ףp= @@ ףp=?: ףp@zG@Q?#gE#@Q@? @(\@Gz?.@efffff@`(\?*;@? ףp=@PQ?8c@Gz@(\?:m@Q@?B@S@(\@@ ףp=?ή[@@zG?MQ@o= ףp@Q?؋|j@IzG@(\(?[ @Q@?h6@(\@p= ףp?fׂ-؂@@Gz?Ey5@p= ף@Q?pN@{Gz@Z(\?"߼@SQ@?{o^M@)\(@p= ף?@@BzG?e= ףp?e= ףp?Q?= ףp?= ףp?(\?q= ףp?q= ףp?433333?b= ףp?b= ףp?֣p= ?= ףp?= ףp?Gz?t= ףp?t= ףp?Q?b= ףp?b= ףp?(\?= ףp?= ףp?`fffff?t= ףp?t= ףp?ףp= ?t= ףp?t= ףp?Gz?= ףp?= ףp?Gz?;c@Gz@ ףp= ?*;@= ףp=@ffffff?1@gfffff@(\? @(\@ Q?'gE#@Q@|Gz?: ףp@zG@ףp= ?̊F@ ףp= @333333?] ` @433333@(\?%X@\(\@Q? @Q@HzG?|@Gz@p= ף? t@@ףp= @?48@@\(\?vS@)\(@Q?W(@RQ@Gz?k2Tv@{Gz@p= ףp?z@p= ף@?b  @@*\(?ܝo^ @(\@Q?V/ͫ @zG@zG?_, @Q@= ףp=?KRϊF @(\@?> @gfffff@(\?AuG @= ףp=@QQ?. @Gz@^(\?6| @Q@Gz?)b @(\@?,l @@Q?Ld @q= ףp@B ףp=?!K~ @IzG@(\?o @Q@Gz?*;L @(\@ifffff? @@!Q? $ @p= ף@أp= ?xV4@{Gz@(\?G贁@RQ@HzG?|؛W@)\(@?iq@@?{؛W@ףp= @?G贁@Gz@?xV4@Q@? $ @\(\@? @333333@?*;L @ ףp= @?o @zG@?!K~ @Q@?Ld @(\@?+l @ffffff@?)b @> ףp=@?6| @Gz@Gz?. @Gz@Q??uG @> ףp=@?> @ffffff@R?JRϊF @(\@ףp= ?_, @Q@@\(?T/ͫ @zG@p= ף?ܝo^ @ ףp= @033333?a  @333333@(\?z@\(\@PQ?k2Tv@Q@zG?W(@Gz@p= ףp?vS@ףp= @?48@@HzG? t@@)\(@(\?|@RQ@أp= ? @{Gz@ Q?%X@p= ף@hfffff?] ` @@Gz?̊F@(\@(\?: ףp@zG@@ ףp=?%gE#@Q@Q? @(\@?0@ffffff@Gz?*;@> ףp=@`(\?:c@Gz@PQ?:m@Q@(\?D@S@(\@?̮[@@@ ףp=?OQ@p= ףp@zG?֋|j@HzG@Q?Z @Q@(\(?h6@(\@?dׂ-؂@@p= ףp?Ey5@p= ף@Gz?rN@|Gz@Q?"߼@RQ@^(\?yo^M@(\(@?@@p= ף???HzG???Q???(\???033333? ? ?أp= ???xGz??? Q???(\???hfffff???ףp= ???Gz???Gz?*;@= ףp=@ ףp= ?1@gfffff@ffffff? @(\@(\?&gE#@Q@ Q?: ףp@zG@|Gz?̊F@ ףp= @ףp= ?] ` @433333@333333?%X@\(\@(\? @Q@Q?|@Gz@HzG? t@@ףp= @p= ף?48@@?vS@)\(@\(\?W(@RQ@Q?k2Tv@{Gz@Gz?z@p= ף@p= ףp?a  @@?ܝo^ @(\@)\(?T/ͫ @ ףp= @Q?_, @zG@zG?JRϊF @Q@= ףp=?> @(\@?@uG @gfffff@(\?. @= ףp=@QQ?6| @Gz@^(\?)b @Q@Gz?+l @(\@?Ld @@Q?!K~ @q= ףp@> ףp=?o @GzG@(\?*;L @Q@Gz? @(\@gfffff? $ @@Q?xV4@p= ף@أp= ?G贁@{Gz@(\?{؛W@RQ@HzG?iq@)\(@HzG?|؛W@@HzG?G贁@ףp= @HzG?xV4@Gz@HzG? $ @Q@KzG? @](\@KzG?*;L @433333@KzG?o @ ףp= @KzG?"K~ @zG@KzG?Ld @Q@KzG?,l @(\@HzG?)b @gfffff@HzG?5| @= ףp=@HzG?. @Gz@Gz??uG @Gz@Q?> @= ףp=@?JRϊF @gfffff@Q?_, @(\@ ףp= ?T/ͫ @Q@ \(?ܝo^ @zG@p= ף?`  @ ףp= @>33333?z@433333@(\?j2Tv@[(\@]Q?W(@Q@zG?tS@Gz@|= ףp?48@أp= @? t@@@NzG?|@*\(@(\? @QQ@ޣp= ?%X@|Gz@Q?] ` @p= ף@mfffff?̊F@@Gz?: ףp@zG@(\?#gE#@Q@@ ףp=? @(\@Q?.@efffff@?*;@? ףp=@Gz?8c@Gz@`(\?:m@Q@PQ?B@S@(\@(\?ή[@@?MQ@o= ףp@@ ףp=?؋|j@IzG@zG?X @Q@Q?h6@(\@(\(?fׂ-؂@@?Ey5@p= ף@p= ףp?pN@{Gz@Gz?"߼@QQ@Q?{o^M@)\(@Z(\?@@?EzG?EzG?p= ף?VzG?VzG?GzG?@zG?@zG?Q?MzG?MzG?(\?EzG?EzG?633333?>zG?>zG?ޣp= ?NzG?NzG?Gz?FzG?FzG?Q??zG??zG?(\?MzG?MzG?`fffff?FzG?FzG?ףp= ?FzG?FzG?Gz?NzG?NzG?Gz?1@gfffff@ ףp= ? @(\@ffffff?&gE#@Q@(\?: ףp@zG@ Q?̊F@ ףp= @|Gz?] ` @533333@ףp= ?%X@](\@333333? @Q@(\?|@Gz@Q? t@@ףp= @HzG?48@@p= ף?vS@)\(@?W(@RQ@\(\?k2Tv@{Gz@Q?z@p= ף@Gz?a  @@p= ףp?ܝo^ @(\@?V/ͫ @ Q@'\(?_, @ ףp= @Q?KRϊF @zG@zG?> @Q@= ףp=?@uG @(\@?. @gfffff@(\?6| @= ףp=@QQ?)b @Gz@^(\?,l @Q@Gz?Ld @(\@?"K~ @@Q?o @q= ףp@B ףp=?*;L @IzG@(\? @Q@Gz? $ @(\@ifffff?xV4@@!Q?G贁@p= ף@أp= ?{؛W@{Gz@(\?iq@RQ@(\?|؛W@)\(@(\?G贁@@(\?xV4@ףp= @(\? $ @Gz@(\? @Q@(\?*;L @\(\@(\?o @333333@(\? K~ @ ףp= @(\?Ld @zG@(\?+l @Q@(\?)b @(\@(\?8| @hfffff@(\?. @< ףp=@(\?BuG @Gz@Gz?> @Gz@Q?IRϊF @< ףp=@?_, @hfffff@Q?T/ͫ @(\@:ףp= ?ݝo^ @Q@\(?]  @zG@p= ף?z@ ףp= @033333?l2Tv@333333@(\?W(@\(\@PQ?vS@Q@zG?48@Gz@p= ףp? t@@ףp= @?|@@HzG? @)\(@(\?%X@RQ@أp= ?] ` @{Gz@ Q?̊F@p= ף@hfffff?: ףp@@Gz?%gE#@Q@(\? @(\@@ ףp=?0@ffffff@Q?*;@> ףp=@?:c@Gz@Gz?:m@Q@`(\?D@S@(\@PQ?̮[@@(\?OQ@p= ףp@?֋|j@HzG@@ ףp=?Z @Q@zG?h6@(\@Q?dׂ-؂@@(\(?Ey5@p= ף@?oN@zGz@p= ףp?"߼@RQ@Gz?|o^M@*\(@Q?@@V(\?(\?(\??(\?(\?p= ף?t(\?t(\?JzG?(\?(\?Q?(\?(\?(\?(\?(\?B33333?(\?(\?٣p= ?|(\?|(\?xGz?(\?(\?!Q?(\?(\?(\?(\?(\?hfffff?{(\?{(\? ףp= ?(\?(\?Gz?l(\?l(\?Gz? @(\@ ףp= ?&gE#@Q@ffffff?: ףp@zG@(\?̊F@ ףp= @ Q?] ` @533333@|Gz?%X@](\@ףp= ? @Q@333333?|@Gz@(\? t@@أp= @Q?48@@HzG?vS@)\(@p= ף?W(@RQ@?k2Tv@{Gz@\(\?z@p= ף@Q?a  @@Gz?ܝo^ @(\@p= ףp?V/ͫ @Q@?_, @333333@)\(?JRϊF @ ףp= @Q?> @zG@zG?@uG @Q@= ףp=?. @(\@?6| @gfffff@(\?)b @= ףp=@QQ?+l @Gz@^(\?Ld @Q@Gz? K~ @(\@?o @@Q?*;L @q= ףp@> ףp=? @GzG@(\? $ @Q@Gz?xV4@(\@gfffff?G贁@@Q?}؛W@p= ף@أp= ?iq@{Gz@أp= ?{؛W@RQ@أp= ?G贁@)\(@أp= ?xV4@@أp= ? $ @ףp= @أp= ? @Gz@أp= ?*;L @Q@ۣp= ?o @](\@ۣp= ?!K~ @433333@ۣp= ?Ld @ ףp= @ڣp= ?,l @zG@ۣp= ?)b @Q@ۣp= ?6| @(\@أp= ?. @gfffff@أp= ??uG @= ףp=@أp= ?> @Gz@Gz?JRϊF @Gz@Q?_, @= ףp=@?U/ͫ @gfffff@Q?ܝo^ @(\@ ףp= ?_  @Q@ \(?z@zG@p= ף?l2Tv@ ףp= @"33333?W(@233333@(\?wS@](\@CQ?48@Q@zG? t@@Gz@d= ףp?|@֣p= @? @@BzG?%X@(\(@(\?] ` @SQ@ңp= ?̊F@zGz@%Q?: ףp@p= ף@cfffff?$gE#@Q@Gz? @(\@(\?.@efffff@@ ףp=?*;@? ףp=@Q?8c@Gz@?:m@Q@Gz?B@S@(\@`(\?ή[@@PQ?MQ@o= ףp@(\?؋|j@IzG@?X @Q@@ ףp=?h6@(\@zG?bׂ-؂@@Q?Ey5@p= ף@(\(?pN@{Gz@?"߼@SQ@p= ףp?{o^M@)\(@Gz?@@Q?p= ?p= ?\(\?p= ?p= ??ԣp= ?ԣp= ?p= ף?p= ?p= ??zG?ڣp= ?ڣp= ?Q?ףp= ?ףp= ?(\?ԣp= ?ԣp= ?233333?ޣp= ?ޣp= ?ӣp= ?p= ?p= ?~Gz?p= ?p= ?Q?ˣp= ?ˣp= ?(\?p= ?p= ?`fffff?ѣp= ?ѣp= ?ףp= ?p= ?p= ?Gz?֣p= ?֣p= ?Gz?&gE#@Q@ ףp= ?: ףp@zG@ffffff?̊F@ ףp= @(\?] ` @333333@ Q?%X@](\@|Gz? @Q@ףp= ?|@Gz@333333? t@@أp= @(\?48@@Q?vS@)\(@HzG?W(@RQ@p= ף?k2Tv@{Gz@?z@p= ף@\(\?a  @@Q?ܝo^ @(\@Gz?V/ͫ @Q@p= ףp?_, @HzG@?LRϊF @333333@'\(?> @ ףp= @Q?AuG @zG@zG?. @Q@= ףp=?6| @(\@?)b @gfffff@(\?,l @= ףp=@QQ?Ld @Gz@^(\?"K~ @Q@Gz?o @(\@?*;L @@Q? @q= ףp@B ףp=? $ @IzG@(\?xV4@Q@Gz?G贁@(\@ifffff?|؛W@@Q?iq@p= ף@ Q?{؛W@{Gz@ Q?G贁@RQ@ Q?xV4@)\(@ Q? $ @@ Q? @ףp= @ Q?*;L @Gz@ Q?o @Q@Q? K~ @\(\@Q?Ld @333333@Q?*l @ ףp= @Q?)b @zG@Q?5| @Q@Q?. @(\@Q?@uG @ffffff@&Q?> @> ףp=@Q?KRϊF @Gz@Gz?_, @Gz@Q?T/ͫ @> ףp=@?ܝo^ @ffffff@R?^  @(\@ףp= ?z@Q@@\(?j2Tv@zG@p= ף?W(@ ףp= @033333?vS@333333@(\?48@\(\@PQ? t@@Q@zG?|@Gz@p= ףp? @ףp= @?%X@@HzG?] ` @)\(@(\?̊F@RQ@أp= ?: ףp@{Gz@ Q?$gE#@p= ף@jfffff? @(\@Gz?0@ffffff@(\?*;@> ףp=@@ ףp=?:c@Gz@Q?:m@Q@?D@S@(\@Gz?̮[@@`(\?OQ@p= ףp@PQ?֋|j@HzG@(\?Z @Q@?h6@(\@@ ףp=?dׂ-؂@@zG?Ey5@p= ף@Q?nN@zGz@(\(?"߼@RQ@?yo^M@(\(@p= ףp?@@Gz?5Q?5Q?Q?Q?Q?`(\? Q? Q??Q?Q?p= ף? Q? Q?DzG?:Q?:Q?Q? Q? Q?(\?Q?Q?233333?5Q?5Q?֣p= ?Q?Q?zGz?&Q?&Q?Q?*Q?*Q?(\?Q?Q?hfffff?Q?Q?ףp= ?%Q?%Q?Gz? Q? Q?Gz?: ףp@zG@ ףp= ?̊F@ ףp= @ffffff?] ` @333333@(\?%X@](\@ Q? @Q@|Gz?|@Gz@ףp= ? t@@أp= @433333?48@@(\?vS@*\(@Q?W(@RQ@HzG?k2Tv@{Gz@p= ף?z@p= ף@?a  @@\(\?ܝo^ @(\@Q?V/ͫ @Q@Gz?_, @HzG@p= ףp?KRϊF @\(\@?> @333333@)\(?@uG @ ףp= @Q?. @zG@zG?5| @Q@= ףp=?)b @(\@?+l @gfffff@(\?Ld @= ףp=@QQ? K~ @Gz@^(\?o @Q@Gz?*;L @(\@? @@Q? $ @q= ףp@> ףp=?xV4@GzG@(\?G贁@Q@Gz?}؛W@(\@dfffff?iq@@gfffff?{؛W@p= ף@hfffff?G贁@{Gz@hfffff?xV4@RQ@hfffff? $ @)\(@hfffff? @@hfffff?*;L @ףp= @hfffff?o @Gz@hfffff? K~ @Q@kfffff?Ld @](\@jfffff?,l @433333@jfffff?)b @ ףp= @jfffff?7| @zG@kfffff?. @Q@kfffff?BuG @(\@hfffff?> @gfffff@hfffff?JRϊF @= ףp=@hfffff?_, @Gz@Gz?T/ͫ @Gz@Q?۝o^ @= ףp=@?_  @gfffff@Q?z@(\@ ףp= ?j2Tv@Q@ \(?W(@zG@p= ף?tS@ ףp= @>33333?48@433333@(\?~ t@@[(\@]Q?|@Q@zG? @Gz@|= ףp?%X@أp= @?] ` @@NzG?̊F@*\(@(\?: ףp@QQ@ޣp= ?&gE#@|Gz@ Q? @(\@ffffff?.@ffffff@Gz?*;@= ףp=@(\?8c@Gz@@ ףp=?:m@Q@Q?B@S@(\@?ή[@@Gz?MQ@o= ףp@`(\?؋|j@IzG@PQ?X @Q@(\?h6@(\@?bׂ-؂@@@ ףp=?Ey5@p= ף@zG?lN@yGz@Q?"߼@SQ@(\(?{o^M@)\(@?@@m= ףp?dfffff?dfffff?Gz?Pfffff?Pfffff?Q?`fffff?`fffff?T(\?\fffff?\fffff??efffff?efffff?p= ף?efffff?efffff?IzG?Vfffff?Vfffff?Q?pfffff?pfffff?(\?dfffff?dfffff?833333?Pfffff?Pfffff?p= ?`fffff?`fffff?~Gz?kfffff?kfffff?Q?Pfffff?Pfffff?(\?pfffff?pfffff?`fffff?Tfffff?Tfffff?ףp= ?kfffff?kfffff?Gz?Yfffff?Yfffff?Gz?̊F@ ףp= @ ףp= ?] ` @333333@ffffff?%X@](\@(\? @Q@ Q?|@Gz@|Gz? t@@٣p= @أp= ?48@@433333?vS@*\(@(\?W(@RQ@Q?k2Tv@{Gz@HzG?z@p= ף@p= ף?a  @@?ܝo^ @(\@\(\?V/ͫ @Q@Q?_, @HzG@Gz?LRϊF @q= ףp@p= ףp?> @\(\@?AuG @333333@'\(?. @ ףp= @Q?6| @zG@zG?)b @Q@= ףp=?+l @(\@?Ld @gfffff@(\?"K~ @= ףp=@QQ?o @Gz@^(\?*;L @Q@Gz? @(\@? $ @@Q?xV4@q= ףp@B ףp=?G贁@IzG@(\?|؛W@Q@Gz?iq@(\@Gz?z؛W@@Gz?G贁@p= ף@Gz?xV4@{Gz@Gz? $ @RQ@Gz? @)\(@Gz?*;L @@Gz?o @ףp= @Gz? K~ @Gz@Gz?Ld @Q@Gz?+l @\(\@Gz?)b @333333@Gz?5| @ ףp= @Gz?. @zG@Gz?@uG @Q@Gz?> @(\@Gz?LRϊF @hfffff@Gz?_, @< ףp=@Gz?V/ͫ @Gz@Gz?ݝo^ @Gz@Q?^  @< ףp=@?z@hfffff@Q?h2Tv@(\@:ףp= ?W(@Q@\(?rS@zG@p= ף?48@ ףp= @033333? t@@333333@(\?|@\(\@PQ? @Q@zG?%X@Gz@p= ףp?] ` @ףp= @?̊F@@HzG?: ףp@)\(@(\?%gE#@RQ@أp= ? @{Gz@ Q?0@ffffff@kfffff?*;@> ףp=@Gz?:c@Gz@(\?:m@Q@@ ףp=?D@S@(\@Q?̮[@@?OQ@p= ףp@Gz?֋|j@HzG@`(\?Z @Q@PQ?h6@(\@(\?dׂ-؂@@?Ey5@p= ף@@ ףp=?nN@zGz@zG?"߼@RQ@Q?yo^M@(\(@(\(?@@?Gz?Gz?n= ףp?Gz?Gz?Gz?Gz?Gz?Q?Gz?Gz?Z(\?Gz?Gz??Gz?Gz?p= ף?Gz?Gz?FzG?Gz?Gz?Q?Gz?Gz?(\?Gz?Gz?:33333?Gz?Gz?ףp= ?Gz?Gz?wGz?Gz?Gz?Q?Gz?Gz?(\?Gz?Gz?hfffff?Gz?Gz?ףp= ?Gz?Gz?Gz?Gz?Gz?Gz?] ` @333333@ ףp= ?%X@\(\@ffffff? @Q@(\?|@Gz@ Q? t@@أp= @|Gz?48@@أp= ?vS@*\(@433333?W(@RQ@(\?j2Tv@zGz@Q?z@p= ף@HzG?`  @@p= ף?۝o^ @(\@?U/ͫ @Q@\(\?_, @HzG@Q?JRϊF @p= ףp@Gz?> @Q@q= ףp??uG @\(\@?. @433333@)\(?6| @ ףp= @Q?)b @zG@zG?*l @Q@< ףp=?Ld @(\@? K~ @gfffff@(\?o @= ףp=@PQ?*;L @Gz@\(\? @Q@Gz? $ @(\@?xV4@@Q?G贁@p= ףp@> ףp=?|؛W@HzG@(\?iq@Q@(\?|؛W@(\@(\?G贁@@(\?xV4@p= ף@(\? $ @|Gz@(\? @RQ@(\?*;L @*\(@(\?o @@(\?!K~ @أp= @(\?Ld @Gz@(\?,l @Q@(\?)b @\(\@(\?7| @433333@(\?. @ ףp= @(\?AuG @zG@(\?> @Q@(\?LRϊF @(\@(\?_, @hfffff@(\?T/ͫ @= ףp=@(\?ݝo^ @Gz@}Gz?b  @Gz@Q?z@; ףp=@ݙ?l2Tv@hfffff@Q?W(@(\@-ףp= ?wS@Q@[(?48@zG@p= ף? t@@ ףp= @*33333?|@233333@(\? @\(\@KQ?%X@Q@zG?] ` @Gz@l= ףp?̊F@֣p= @?: ףp@@FzG?$gE#@)\(@(\? @QQ@ޣp= ?0@ffffff@Q?*;@> ףp=@cfffff?8c@Gz@Gz?:m@Q@(\?F@S@(\@= ףp=?̮[@@Q?PQ@p= ףp@?׋|j@HzG@Gz?[ @Q@\(\?h6@(\@RQ?eׂ-؂@@(\?Ey5@p= ף@?pN@{Gz@> ףp=?"߼@RQ@zG?zo^M@)\(@Q?@@\(?)\?)\??(\?(\?q= ףp?(\?(\?Gz?(\?(\?Q?(\?(\?T(\?(\?(\??(\?(\?p= ף?(\?(\?@zG?)\?)\?Q?(\?(\?(\?(\?(\?:33333?(\?(\?ңp= ?(\?(\?vGz?(\?(\?Q?(\?(\?(\?(\?(\?dfffff?(\?(\?ףp= ?(\?(\?Gz?(\?(\?Gz?%X@\(\@ ףp= ? @Q@ffffff?|@Gz@(\? t@@ףp= @ Q?48@@|Gz?wS@*\(@أp= ?W(@RQ@433333?j2Tv@zGz@(\?z@p= ף@Q?`  @@HzG?۝o^ @(\@p= ף?U/ͫ @Q@?_, @HzG@\(\?KRϊF @q= ףp@Q?> @@Gz??uG @Q@q= ףp?. @\(\@?6| @433333@)\(?)b @ ףp= @Q?+l @zG@zG?Ld @Q@< ףp=?!K~ @(\@?o @gfffff@(\?*;L @> ףp=@RQ? @Gz@`(\? $ @Q@Gz?xV4@(\@?G贁@@Q?|؛W@p= ףp@> ףp=?iq@HzG@> ףp=?{؛W@Q@= ףp=?G贁@(\@= ףp=?xV4@@= ףp=? $ @p= ף@: ףp=? @zGz@: ףp=?*;L @RQ@: ףp=?o @(\(@: ףp=? K~ @@: ףp=?Ld @֣p= @: ףp=?*l @Gz@: ףp=?)b @Q@8 ףp=?5| @\(\@7 ףp=?. @233333@7 ףp=?@uG @ ףp= @7 ףp=?> @zG@8 ףp=?KRϊF @Q@8 ףp=?_, @(\@5 ףp=?V/ͫ @ffffff@> ףp=?۝o^ @= ףp=@5 ףp=?a  @Gz@Gz?z@Gz@<Q?l2Tv@> ףp=@?W(@ffffff@R?vS@(\@ףp= ?48@Q@L\(? t@@zG@p= ף?|@ ףp= @633333? @433333@(\?%X@\(\@UQ?] ` @Q@zG?̊F@Gz@t= ףp?: ףp@ףp= @?&gE#@@JzG? @)\(@(\?1@SQ@ޣp= ?*;@? ףp=@"Q?:c@Gz@cfffff?:m@Q@Gz?B@S@(\@(\?ˮ[@@< ףp=?RQ@r= ףp@Q?Ջ|j@HzG@?\ @ Q@Gz?h6@(\@\(\?fׂ-؂@@RQ?Ey5@p= ף@(\?qN@{Gz@?"߼@RQ@> ףp=?|o^M@)\(@zG?@@Q?2 ףp=?2 ףp=?/\(?Q ףp=?Q ףp=??< ףp=?< ףp=?w= ףp?B ףp=?B ףp=? Gz?G ףp=?G ףp=?Q?1 ףp=?1 ףp=?b(\?N ףp=?N ףp=??B ףp=?B ףp=?p= ף?4 ףp=?4 ףp=?NzG?R ףp=?R ףp=?Q?< ףp=?< ףp=?(\?B ףp=?B ףp=?:33333?G ףp=?G ףp=?ޣp= ?2 ףp=?2 ףp=?|Gz?Q ףp=?Q ףp=?%Q?B ףp=?B ףp=?(\?B ףp=?B ףp=?lfffff?1 ףp=?1 ףp=? ףp= ?R ףp=?R ףp=?Gz?" ףp=?" ףp=?Gz? @Q@ ףp= ?|@Gz@ffffff? t@@ףp= @(\?48@@ Q?wS@*\(@|Gz?W(@SQ@أp= ?l2Tv@|Gz@433333?z@p= ף@(\?`  @@Q?ܝo^ @(\@HzG?U/ͫ @Q@p= ף?_, @HzG@?KRϊF @q= ףp@\(\?> @@Q??uG @Gz@Gz?. @Q@q= ףp?5| @\(\@?)b @433333@)\(?+l @ ףp= @Q?Ld @zG@zG?K~ @Q@< ףp=?o @(\@?*;L @ffffff@(\? @= ףp=@PQ? $ @Gz@\(\?xV4@Q@Gz?F贁@(\@?|؛W@@Q?iq@p= ףp@Q?|؛W@HzG@Q?G贁@ Q@Q?xV4@(\@Q? $ @@Q? @p= ף@Q?*;L @|Gz@Q?o @RQ@Q?"K~ @*\(@Q?Ld @@Q?,l @ףp= @Q?)b @Gz@Q?7| @Q@Q?. @\(\@Q?BuG @433333@Q?> @ ףp= @Q?LRϊF @zG@Q?_, @Q@Q?W/ͫ @(\@Q?ܝo^ @gfffff@Q?b  @> ףp=@Q?z@Gz@Gz?j2Tv@Gz@Q?W(@> ףp=@c?uS@ffffff@Q?48@(\@֣p= ? t@@Q@,\(?|@zG@p= ף? @ ףp= @*33333?%X@233333@(\?] ` @\(\@KQ?̊F@Q@zG?: ףp@Gz@l= ףp?$gE#@ףp= @? @@FzG?.@)\(@(\?*;@? ףp=@ޣp= ?:c@Gz@Q?:m@Q@cfffff?B@S@(\@Gz?ͮ[@@(\?PQ@p= ףp@< ףp=?׋|j@HzG@Q?Z @Q@?h6@(\@Gz?eׂ-؂@@\(\?Ey5@p= ף@RQ?pN@{Gz@(\?"߼@RQ@?zo^M@)\(@> ףp=?@@zG?Q?Q?Q?oQ?oQ?!\(?Q?Q??_Q?_Q?i= ףp?~Q?~Q? Gz?Q?Q?Q?oQ?oQ?^(\?rQ?rQ??Q?Q?p= ף?Q?Q?BzG?Q?Q?Q?_Q?_Q?(\?~Q?~Q?*33333?Q?Q?ӣp= ?oQ?oQ?sGz?Q?Q?Q?~Q?~Q?(\?Q?Q?dfffff?oQ?oQ?ףp= ?Q?Q?Gz?_Q?_Q?Gz?|@Gz@ ףp= ? t@@ףp= @ffffff?48@@(\?uS@)\(@ Q?W(@SQ@|Gz?l2Tv@|Gz@أp= ?z@p= ף@433333?`  @@(\?ܝo^ @(\@Q?V/ͫ @Q@HzG?_, @HzG@p= ף?KRϊF @q= ףp@?> @@\(\?@uG @(\@Q?. @Gz@Gz?5| @Q@q= ףp?)b @\(\@?,l @433333@)\(?Ld @ ףp= @Q? K~ @zG@zG?o @Q@< ףp=?*;L @(\@? @ffffff@(\? $ @> ףp=@RQ?xV4@Gz@`(\?G贁@Q@Gz?{؛W@(\@?iq@@?|؛W@p= ףp@?G贁@HzG@?xV4@Q@? $ @(\@? @@?*;L @p= ף@?o @zGz@? K~ @RQ@?Ld @(\(@?+l @@?)b @ףp= @?6| @Gz@?. @Q@?AuG @\(\@?> @233333@?LRϊF @ ףp= @?_, @zG@?V/ͫ @Q@?۝o^ @(\@?b  @gfffff@?z@< ףp=@?l2Tv@Gz@}Gz?W(@Gz@DQ?uS@< ףp=@?48@hfffff@Q? t@@(\@Mףp= ?|@Q@\(? @zG@p= ף?%X@ ףp= @633333?] ` @433333@(\?̊F@\(\@UQ?: ףp@Q@zG?&gE#@Gz@t= ףp? @ףp= @?0@@JzG?*;@)\(@(\?8c@Gz@ޣp= ?:m@Q@#Q?D@S@(\@bfffff?ˮ[@@Gz?MQ@o= ףp@(\?Ջ|j@HzG@= ףp=?\ @ Q@Q?h6@(\@?fׂ-؂@@Gz?Ey5@p= ף@\(\?qN@{Gz@RQ?"߼@RQ@(\?|o^M@)\(@?@@= ףp=???zG???Q???&\(??????y= ףp???Gz???Q???](\??????p= ף???HzG???Q???(\???B33333???ڣp= ???~Gz???#Q???(\???lfffff??? ףp= ???Gz???Gz? t@@ףp= @ ףp= ?48@@ffffff?vS@)\(@(\?W(@QQ@ Q?l2Tv@|Gz@|Gz?z@p= ף@أp= ?b  @@333333?ܝo^ @(\@(\?V/ͫ @Q@Q?_, @HzG@HzG?KRϊF @q= ףp@p= ף?> @@?@uG @(\@](\?. @ףp= @Q?5| @Gz@Gz?)b @Q@q= ףp?*l @\(\@?Ld @433333@)\(? K~ @ ףp= @Q?o @zG@zG?*;L @Q@< ףp=? @(\@? $ @ffffff@(\?xV4@= ףp=@PQ?F贁@Gz@\(\?|؛W@Q@Gz?iq@(\@Gz?z؛W@@Gz?G贁@r= ףp@Gz?xV4@HzG@Gz? $ @ Q@Gz? @(\@Gz?*;L @@Gz?o @p= ף@Gz?!K~ @|Gz@Gz?Ld @RQ@Gz?,l @)\(@Gz?)b @@Gz?6| @ףp= @Gz?. @Gz@Gz?AuG @Q@Gz?> @\(\@Gz?LRϊF @433333@Gz?_, @ ףp= @Gz?V/ͫ @zG@Gz?۝o^ @Q@Gz?a  @(\@Gz?z@hfffff@Gz?i2Tv@= ףp=@Gz?W(@Gz@}Gz?wS@Gz@Q?48@< ףp=@ݙ? t@@hfffff@Q?|@(\@-ףp= ? @Q@[(? %X@zG@p= ף?] ` @ ףp= @*33333?̊F@233333@(\?: ףp@[(\@KQ?$gE#@Q@zG? @Gz@l= ףp?.@ףp= @?*;@@BzG?8c@Gz@(\?:m@Q@ݣp= ?E@S@(\@Q?̮[@@cfffff?MQ@o= ףp@Gz?؋|j@IzG@(\?Z @Q@= ףp=?h6@(\@Q?eׂ-؂@@?Ey5@p= ף@Gz?pN@{Gz@\(\?"߼@RQ@RQ?zo^M@)\(@(\?@@?Gz?Gz?< ףp=?Gz?Gz?zG?Gz?Gz?Q?Gz?Gz?$\(?Gz?Gz??Gz?Gz?l= ףp?Gz?Gz?Gz?Gz?Gz?Q?Gz?Gz?P(\?Gz?Gz??Gz?Gz?p= ף?Gz?Gz?DzG?Gz?Gz?Q?Gz?Gz?(\?Gz?Gz?<33333?Gz?Gz?ԣp= ?Gz?Gz?tGz?Gz?Gz?Q?Gz?Gz?(\?Gz?Gz?dfffff?Gz?Gz?ףp= ?Gz?Gz?Gz?Gz?Gz?Gz?48@@ ףp= ?vS@)\(@ffffff?W(@RQ@(\?k2Tv@{Gz@ Q?z@p= ף@|Gz?b  @@أp= ?ܝo^ @(\@333333?V/ͫ @Q@(\?_, @HzG@Q?LRϊF @p= ףp@HzG?> @@p= ף?@uG @(\@?. @Q@[(\?7| @ףp= @Q?)b @Gz@Gz?+l @Q@q= ףp?Ld @\(\@?!K~ @433333@)\(?o @ ףp= @Q?*;L @zG@zG? @Q@< ףp=? $ @(\@?xV4@ffffff@(\?G贁@> ףp=@RQ?{؛W@Gz@\(\?iq@Q@V(\?}؛W@(\@^(\?F贁@@^(\?xV4@p= ףp@^(\? $ @HzG@^(\? @Q@^(\?*;L @(\@^(\?o @@^(\? K~ @p= ף@Z(\?Ld @zGz@Z(\?,l @RQ@Z(\?)b @)\(@Z(\?6| @@Z(\?. @ףp= @Z(\?@uG @Gz@Z(\?> @Q@X(\?KRϊF @\(\@X(\?_, @333333@X(\?V/ͫ @ ףp= @X(\?۝o^ @zG@X(\?`  @Q@X(\?z@(\@V(\?k2Tv@ffffff@^(\?W(@= ףp=@V(\?vS@Gz@Gz?48@Gz@<Q? t@@> ףp=@?|@ffffff@R? @(\@ףp= ?%X@Q@L\(?] ` @zG@p= ף?̊F@ ףp= @633333?: ףp@333333@(\?&gE#@](\@UQ? @Q@zG?0@Gz@t= ףp?*;@ףp= @?;c@@BzG?:m@Q@(\?C@S@(\@ޣp= ?ή[@@"Q?OQ@p= ףp@cfffff?֋|j@HzG@Gz?X @Q@(\?h6@(\@= ףp=?gׂ-؂@@Q?Ey5@p= ף@?qN@{Gz@Gz?"߼@RQ@\(\?|o^M@)\(@TQ?@@(\?`(\?`(\??(\?(\?A ףp=?p(\?p(\?zG?p(\?p(\?Q?`(\?`(\?)\(?(\?(\??P(\?P(\?r= ףp?p(\?p(\?Gz?(\?(\?Q?`(\?`(\?f(\?`(\?`(\??p(\?p(\?p= ף?(\?(\?JzG?(\?(\?Q?P(\?P(\?(\?p(\?p(\?333333?(\?(\?ۣp= ?`(\?`(\?{Gz?(\?(\?$Q?p(\?p(\?(\?p(\?p(\?lfffff?`(\?`(\? ףp= ?(\?(\?Gz?P(\?P(\?Gz?vS@)\(@ ףp= ?W(@RQ@ffffff?k2Tv@{Gz@(\?z@p= ף@ Q?b  @@|Gz?ݝo^ @(\@أp= ?V/ͫ @ Q@333333?_, @HzG@(\?LRϊF @p= ףp@Q?> @@HzG?@uG @(\@p= ף?. @Q@?5| @@](\?)b @ףp= @Q?+l @Gz@Gz?Ld @Q@q= ףp? K~ @\(\@?o @433333@)\(?*;L @ ףp= @Q? @zG@zG? $ @Q@< ףp=?xV4@(\@?G贁@ffffff@(\?|؛W@= ףp=@PQ?iq@Gz@UQ?z؛W@Q@QQ?G贁@(\@UQ?xV4@@UQ? $ @r= ףp@UQ? @HzG@UQ?*;L @ Q@UQ?o @(\@UQ? K~ @@UQ?Ld @p= ף@SQ?+l @{Gz@SQ?)b @RQ@SQ?6| @)\(@SQ?. @@SQ?@uG @ףp= @SQ?> @Gz@SQ?KRϊF @Q@RQ?_, @\(\@RQ?V/ͫ @433333@RQ?۝o^ @ ףp= @RQ?`  @zG@RQ?z@Q@RQ?k2Tv@(\@QQ?W(@gfffff@UQ?vS@> ףp=@QQ?48@Gz@Gz? t@@Gz@Q?|@> ףp=@c? @efffff@Q?%X@(\@֣p= ?] ` @Q@,\(?̊F@zG@p= ף?: ףp@ ףp= @*33333?$gE#@333333@(\? @[(\@KQ?/@Q@zG?*;@Gz@l= ףp?8c@ףp= @?:m@Q@YzG?H@S@(\@(\?ͮ[@@ޣp= ?OQ@p= ףp@Q?֋|j@HzG@xfffff?^ @ Q@Gz?h6@(\@(\?iׂ-؂@@< ףp=?Ey5@p= ף@Q?sN@|Gz@?"߼@RQ@Gz?~o^M@*\(@\(\?@@XQ?PQ?PQ?(\?@Q?@Q??PQ?PQ?@ ףp=?HQ?HQ?zG?HQ?HQ?Q?@Q?@Q?'\(?PQ?PQ??8Q?8Q?o= ףp?HQ?HQ?Gz?XQ?XQ?Q?@Q?@Q?c(\?@Q?@Q??HQ?HQ?p= ף?PQ?PQ?FzG?PQ?PQ?Q?8Q?8Q?(\?HQ?HQ?.33333?XQ?XQ?գp= ?@Q?@Q?uGz?PQ?PQ?Q?HQ?HQ?(\?HQ?HQ?dfffff?@Q?@Q?ףp= ?PQ?PQ?Gz?8Q?8Q?Gz?W(@RQ@ ףp= ?k2Tv@{Gz@ffffff?z@p= ף@(\?a  @@ Q?ݝo^ @(\@|Gz?W/ͫ @ Q@أp= ?_, @HzG@333333?LRϊF @p= ףp@(\?> @@Q?@uG @(\@HzG?. @Q@p= ף?6| @Gz@?)b @@[(\?-l @ףp= @Q?Ld @Gz@Gz? K~ @Q@q= ףp?o @\(\@?*;L @433333@)\(? @ ףp= @Q? $ @zG@zG?xV4@Q@< ףp=?G贁@(\@?{؛W@ffffff@(\?iq@= ףp=@(\?|؛W@Gz@(\?G贁@Q@(\?xV4@(\@(\? $ @@(\? @p= ףp@(\?*;L @HzG@(\?o @Q@(\? K~ @(\@(\?Ld @@(\?*l @p= ף@(\?)b @{Gz@(\?6| @RQ@(\?. @)\(@(\?@uG @@(\?> @ףp= @(\?JRϊF @Gz@(\?_, @Q@(\?V/ͫ @\(\@(\?۝o^ @333333@(\?`  @ ףp= @(\?z@zG@(\?k2Tv@Q@(\?W(@(\@(\?xS@gfffff@(\?48@< ףp=@(\? t@@Gz@}Gz?|@Gz@DQ? @< ףp=@?%X@ifffff@Q?] ` @(\@Mףp= ?̊F@Q@\(?: ףp@zG@p= ף?&gE#@ ףp= @633333? @333333@(\?0@](\@UQ?*;@Q@zG?:c@Gz@t= ףp?:m@ףp= @?D@S@(\@BzG?ˮ[@@(\?MQ@o= ףp@ȣp= ?ҋ|j@FzG@"Q?Y @Q@cfffff?h6@(\@Gz?cׂ-؂@@(\?Ey5@p= ף@4 ףp=?nN@zGz@Q?"߼@RQ@?xo^M@(\(@Gz?@@(\?|(\?|(\?(\?>Q?>Q?(\?(\?(\??)\?)\?D ףp=?(\?(\?zG?(\?(\?Q?(\?(\?,\(?)\?)\??(\?(\?t= ףp?(\?(\?$Gz?(\?(\?Q?(\?(\?Y(\?)\?)\??(\?(\?p= ף?(\?(\?LzG?)\?)\?Q?(\?(\?(\?(\?(\?D33333?(\?(\?ܣp= ?(\?(\?|Gz?)\?)\?$Q?(\?(\?(\?(\?(\?lfffff?(\?(\? ףp= ?)\?)\?Gz?(\?(\?Gz?k2Tv@{Gz@ ףp= ?z@p= ף@ffffff?a  @@(\?ܝo^ @(\@ Q?W/ͫ @ Q@|Gz?_, @IzG@أp= ?LRϊF @r= ףp@333333?> @@(\?@uG @(\@Q?. @Q@HzG?6| @Gz@p= ף?)b @)\(@?+l @@](\?Ld @ףp= @Q? K~ @Gz@Gz?o @Q@q= ףp?*;L @\(\@? @433333@)\(? $ @ ףp= @Q?xV4@zG@zG?F贁@Q@< ףp=?{؛W@(\@?iq@ffffff@?z؛W@> ףp=@?G贁@Gz@?xV4@Q@? $ @(\@? @@?*;L @r= ףp@?o @HzG@? K~ @ Q@?Ld @(\@?*l @@?)b @p= ף@?6| @{Gz@?. @RQ@?@uG @)\(@?> @@?KRϊF @ףp= @?_, @Gz@?V/ͫ @Q@?ܝo^ @\(\@?a  @433333@?z@ ףp= @?k2Tv@zG@?W(@Q@?vS@(\@?48@hfffff@? t@@= ףp=@?|@Gz@}Gz? @Gz@Q? %X@; ףp=@ݙ?] ` @hfffff@Q?̊F@(\@-ףp= ?: ףp@Q@[(?"gE#@zG@p= ף? @ ףp= @*33333?.@333333@(\?*;@[(\@KQ?9c@Q@zG?:m@Gz@|= ףp?E@S@(\@?̮[@@YzG?RQ@r= ףp@(\?؋|j@IzG@ޣp= ?Z @Q@Q?h6@(\@xfffff?hׂ-؂@@Gz?Ey5@p= ף@(\?sN@|Gz@= ףp=?"߼@RQ@Q?~o^M@*\(@?@@?$Gz?$Gz??d(\?d(\??RQ?RQ??(\?(\????5 ףp=???zG???}Q???*\(??????g= ףp???Gz???Q???T(\??????p= ף???GzG???Q???(\???:33333???ףp= ???vGz???Q???(\???dfffff???ףp= ???Gz???Gz?z@p= ף@ ףp= ?a  @@ffffff?ܝo^ @(\@(\?V/ͫ @Q@ Q?_, @IzG@|Gz?MRϊF @r= ףp@أp= ?> @@333333?@uG @(\@(\?. @Q@Q?8| @Gz@HzG?)b @= ףp=@p= ף?+l @)\(@?Ld @@[(\?"K~ @ףp= @Q?o @Gz@Gz?*;L @Q@q= ףp? @\(\@? $ @433333@)\(?xV4@ ףp= @Q?G贁@zG@zG?{؛W@Q@< ףp=?iq@(\@= ףp=?|؛W@ffffff@? ףp=?G贁@> ףp=@; ףp=?xV4@Gz@? ףp=? $ @Q@< ףp=? @(\@? ףp=?*;L @@? ףp=?o @p= ףp@? ףp=? K~ @HzG@? ףp=?Ld @Q@? ףp=?*l @(\@? ףp=?)b @@? ףp=?5| @p= ף@= ףp=?. @{Gz@= ףp=?@uG @RQ@= ףp=?> @)\(@= ףp=?KRϊF @@= ףp=?_, @ףp= @= ףp=?V/ͫ @Gz@> ףp=?۝o^ @Q@< ףp=?`  @\(\@< ףp=?z@233333@< ףp=?j2Tv@ ףp= @< ףp=?W(@zG@< ףp=?uS@Q@< ףp=?48@(\@; ףp=? t@@ffffff@? ףp=?|@= ףp=@; ףp=? @Gz@Gz?%X@Gz@<Q?] ` @? ףp=@?̊F@ffffff@R?: ףp@(\@ףp= ?&gE#@Q@L\(? @zG@p= ף?0@ ףp= @633333?*;@333333@(\?;c@](\@UQ?:m@Q@zG?E@S@Gz@L= ףp?Ȯ[@@?NQ@p= ףp@BzG?֋|j@HzG@(\?X @Q@ȣp= ?h6@(\@#Q?cׂ-؂@@cfffff?Ey5@p= ף@Gz?mN@yGz@(\?"߼@RQ@3 ףp=?xo^M@(\(@Q?@@7 ףp=???> ףp=? Gz? Gz?0 ףp=?k(\?k(\?> ףp=?FQ?FQ?7 ףp=?(\?(\?> ףp=???H ףp=?@ ףp=?@ ףp=?zG?? ףp=?? ףp=?Q?8 ףp=?8 ףp=?0\(?H ףp=?H ףp=??/ ףp=?/ ףp=?w= ףp?@ ףp=?@ ףp=?Gz?P ףp=?P ףp=?Q?8 ףp=?8 ףp=?j(\?6 ףp=?6 ףp=??@ ףp=?@ ףp=?p= ף?H ףp=?H ףp=?MzG?G ףp=?G ףp=?Q?0 ףp=?0 ףp=?(\?@ ףp=?@ ףp=?533333?O ףp=?O ףp=?ޣp= ?8 ףp=?8 ףp=?~Gz?H ףp=?H ףp=?$Q?? ףp=?? ףp=?(\?@ ףp=?@ ףp=?mfffff?8 ףp=?8 ףp=? ףp= ?H ףp=?H ףp=?Gz?0 ףp=?0 ףp=?Gz?a  @@ ףp= ?ܝo^ @(\@ffffff?V/ͫ @Q@(\?_, @HzG@ Q?MRϊF @r= ףp@|Gz?> @@أp= ?BuG @(\@333333?. @Q@(\?6| @Gz@Q?)b @> ףp=@FzG?+l @QQ@p= ף?Ld @)\(@?!K~ @@](\?o @ףp= @Q?*;L @Gz@Gz? @Q@q= ףp? $ @\(\@?xV4@433333@)\(?G贁@ ףp= @Q?|؛W@zG@zG?iq@Q@zG?|؛W@(\@zG?G贁@hfffff@zG?xV4@> ףp=@zG? $ @Gz@zG? @Q@zG?*;L @(\@zG?o @@zG? K~ @r= ףp@zG?Ld @HzG@zG?*l @Q@zG?)b @(\@zG?4| @@zG?. @p= ף@zG?@uG @{Gz@zG?> @RQ@zG?JRϊF @)\(@zG?_, @@zG?U/ͫ @ףp= @zG?ڝo^ @Gz@zG?`  @Q@zG?z@\(\@zG?l2Tv@433333@zG?W(@ ףp= @zG?vS@zG@zG?48@Q@zG? t@@(\@zG?|@gfffff@zG? @> ףp=@zG?%X@Gz@Gz?] ` @Gz@Q?̊F@> ףp=@c?: ףp@ffffff@Q?$gE#@(\@֣p= ? @Q@,\(?.@zG@p= ף?*;@ ףp= @*33333?9c@333333@(\?:m@[(\@KQ?C@S@Q@zG?ͮ[@@|= ףp?PQ@p= ףp@?׋|j@HzG@ZzG?^ @ Q@(\?h6@(\@ޣp= ?dׂ-؂@@Q?Ey5@p= ף@xfffff?rN@|Gz@Gz?"߼@SQ@(\?~o^M@*\(@= ףp=?@@zG?sQ?sQ?zG???zG?Gz?Gz?zG?t(\?t(\?zG?JQ?JQ?zG?(\?(\?zG???zG?: ףp=?: ףp=?zG?zG?zG?Q?zG?zG?!\(?zG?zG??zG?zG?i= ףp?zG?zG?Gz?zG?zG?Q?zG?zG?U(\?zG?zG??zG?zG?p= ף?zG?zG?BzG?zG?zG?Q?zG?zG?(\?zG?zG?033333?zG?zG?ңp= ?zG?zG?tGz?zG?zG?Q?zG?zG?(\?zG?zG?cfffff?zG?zG?ףp= ?zG?zG?Gz?zG?zG?Gz?ܝo^ @(\@ ףp= ?V/ͫ @Q@ffffff?_, @HzG@(\?LRϊF @q= ףp@ Q?> @@|Gz?BuG @(\@أp= ?. @Q@333333?6| @Gz@(\?)b @> ףp=@Q?,l @hfffff@FzG?Ld @QQ@p= ף?!K~ @)\(@?o @@[(\?*;L @ףp= @Q? @Gz@Gz? $ @Q@q= ףp?xV4@\(\@?G贁@433333@)\(?|؛W@ ףp= @Q?iq@zG@Q?|؛W@Q@Q?G贁@(\@Q?xV4@ffffff@Q? $ @> ףp=@Q? @Gz@Q?*;L @Q@Q?o @(\@Q?K~ @@Q?Ld @p= ףp@Q?*l @HzG@Q?)b @Q@Q?4| @(\@Q?. @@Q??uG @p= ף@Q?> @{Gz@Q?JRϊF @RQ@Q?_, @)\(@Q?U/ͫ @@Q?۝o^ @ףp= @Q?`  @Gz@Q?z@Q@Q?k2Tv@\(\@Q?W(@333333@Q?vS@ ףp= @Q?48@zG@Q? t@@Q@Q?|@(\@Q? @gfffff@Q?%X@< ףp=@Q?] ` @Gz@}Gz?̊F@Gz@DQ?: ףp@= ףp=@?(gE#@hfffff@Q? @(\@Mףp= ?3@Q@\(?*;@zG@p= ף?:c@ ףp= @633333?:m@333333@(\?E@S@](\@UQ?ͮ[@Q@zG?NQ@p= ףp@L= ףp?ҋ|j@FzG@?Y @Q@BzG?h6@(\@(\?cׂ-؂@@ȣp= ?Ey5@p= ף@"Q?nN@zGz@bfffff?"߼@RQ@Gz?xo^M@'\(@(\?@@Q? ףp=? ףp=?Q?Q?Q?Q???Q?Gz?Gz?Q?\(\?\(\?Q?NQ?NQ?Q?(\?(\?Q???Q?/ ףp=?/ ףp=?Q?zG?zG?Q?Q?Q?2\(?Q?Q??Q?Q?o= ףp?Q?Q?Gz?Q?Q?Q?zQ?zQ?\(\?Q?Q??Q?Q?p= ף?Q?Q?OzG?Q?Q?Q?Q?Q?(\?Q?Q?B33333?Q?Q?ޣp= ?zQ?zQ?~Gz?Q?Q?#Q?Q?Q?(\?Q?Q?lfffff?Q?Q? ףp= ?Q?Q?Gz?Q?Q?Gz?U/ͫ @Q@ ףp= ?_, @GzG@ffffff?KRϊF @q= ףp@(\?> @@ Q?AuG @(\@|Gz?. @Q@ףp= ?6| @Gz@333333?)b @> ףp=@(\?*l @ffffff@Q?Ld @{Gz@HzG? K~ @RQ@p= ף?o @)\(@?*;L @@\(\? @ףp= @Q? $ @Gz@Gz?xV4@Q@p= ףp?G贁@\(\@?{؛W@333333@'\(?iq@ ףp= @'\(?|؛W@zG@(\(?G贁@Q@(\(?xV4@(\@(\(? $ @gfffff@*\(? @> ףp=@&\(?*;L @Gz@*\(?o @Q@'\(?"K~ @(\@)\(?Ld @@*\(?,l @q= ףp@*\(?)b @HzG@*\(?6| @Q@*\(?. @(\@*\(?@uG @@*\(?> @p= ף@(\(?LRϊF @{Gz@(\(?_, @RQ@(\(?V/ͫ @)\(@(\(?ܝo^ @@(\(?a  @ףp= @(\(?z@Gz@(\(?k2Tv@Q@'\(?W(@\(\@'\(?vS@333333@'\(?48@ ףp= @'\(? t@@zG@'\(?|@Q@'\(? @(\@*\(?%X@hfffff@&\(?] ` @< ףp=@*\(?̊F@Gz@Gz?: ףp@Gz@Q?"gE#@< ףp=@? @hfffff@Q?,@(\@:ףp= ?*;@Q@\(?7c@zG@p= ף?:m@ ףp= @033333?G@S@333333@(\?ˮ[@\(\@ZQ?RQ@r= ףp@zG?֋|j@HzG@g= ףp?Z @Q@?h6@(\@LzG?gׂ-؂@@(\?Ey5@p= ף@գp= ?oN@zGz@ Q?"߼@RQ@kfffff?|o^M@*\(@Gz?@@&\(?(\?(\?*\(?M ףp=?M ףp=?&\(?Q?Q?$\(???&\(?Gz?Gz?\(?p(\?p(\?&\(?HQ?HQ?$\(?)\?)\?&\(???*\(?I ףp=?I ףp=?&\(?zG?zG?$\(?Q?Q?+\(?+\(?+\(??\(?\(?s= ףp?#\(?#\(?Gz?7\(?7\(?Q?\(?\(?_(\?\(?\(??#\(?#\(?p= ף?1\(?1\(?JzG?+\(?+\(?Q?\(?\(?(\?#\(?#\(?<33333?7\(?7\(?٣p= ?\(?\(?yGz?+\(?+\(?!Q?#\(?#\(?(\?$\(?$\(?hfffff?\(?\(?ףp= ?+\(?+\(?Gz?\(?\(?Gz?_, @GzG@ ףp= ?KRϊF @q= ףp@ffffff?> @@(\?@uG @(\@ Q?. @Q@|Gz?7| @Gz@ףp= ?)b @> ףp=@333333?*l @ffffff@(\?Ld @(\@Q? K~ @{Gz@HzG?o @RQ@p= ף?*;L @)\(@? @@\(\? $ @ףp= @Q?xV4@Gz@Gz?G贁@Q@p= ףp?{؛W@\(\@?iq@333333@?}؛W@ ףp= @?G贁@zG@?xV4@Q@?$ @(\@? @gfffff@?*;L @= ףp=@?o @Gz@? K~ @Q@?Ld @(\@?*l @@?)b @p= ףp@?6| @GzG@?. @Q@?@uG @(\@?> @@?KRϊF @p= ף@?_, @|Gz@?U/ͫ @QQ@?ܝo^ @*\(@?`  @@?z@أp= @?k2Tv@Gz@?W(@Q@?xS@](\@?48@433333@? t@@ ףp= @?|@zG@? @Q@?%X@(\@?] ` @gfffff@?̊F@= ףp=@?: ףp@Gz@Gz?%gE#@Gz@Q? @= ףp=@?0@gfffff@Q?*;@(\@ ףp= ?:c@Q@ \(?:m@zG@p= ף?H@S@ ףp= @"33333?ʮ[@233333@(\?RQ@](\@FQ?ԋ|j@GzG@zG?[ @Q@x= ףp?h6@(\@?fׂ-؂@@DzG?Ey5@p= ף@(\?pN@{Gz@ۣp= ?"߼@SQ@ Q?{o^M@)\(@pfffff?@@?Gz?Gz??(\?(\??9 ףp=?9 ףp=??yQ?yQ?????Gz?Gz??x(\?x(\??<Q?<Q??(\?(\?????; ףp=?; ףp=??zG?zG??Q?Q??\(?\(????r= ףp???Gz???Q???S(\??????p= ף???@zG???Q???(\???833333???ԣp= ???Gz???Q???(\???`fffff???ףp= ???Gz???Gz?KRϊF @q= ףp@ ףp= ?> @@ffffff?@uG @(\@(\?. @Q@Q?5| @Gz@|Gz?)b @> ףp=@ףp= ?+l @gfffff@333333?Ld @(\@(\? K~ @p= ף@Q?o @{Gz@HzG?*;L @RQ@p= ף? @)\(@? $ @@\(\?xV4@ףp= @Q?G贁@Gz@Gz?{؛W@Q@p= ףp?iq@\(\@o= ףp?|؛W@333333@o= ףp?G贁@ ףp= @o= ףp?xV4@zG@p= ףp? $ @Q@p= ףp? @(\@p= ףp?*;L @gfffff@r= ףp?o @> ףp=@o= ףp?"K~ @Gz@q= ףp?Ld @Q@o= ףp?,l @(\@q= ףp?)b @@r= ףp?6| @q= ףp@r= ףp?. @HzG@r= ףp?@uG @Q@r= ףp?> @(\@r= ףp?KRϊF @@r= ףp?_, @p= ף@p= ףp?V/ͫ @{Gz@p= ףp?۝o^ @RQ@p= ףp?`  @)\(@p= ףp?z@@p= ףp?k2Tv@ףp= @p= ףp?W(@Gz@p= ףp?uS@Q@o= ףp?48@\(\@o= ףp? t@@333333@o= ףp?|@ ףp= @o= ףp? @zG@o= ףp?%X@Q@o= ףp?] ` @(\@n= ףp?̊F@ffffff@q= ףp?: ףp@> ףp=@o= ףp?&gE#@Gz@Gz? @Gz@Q?0@> ףp=@?*;@ffffff@R?:c@(\@ףp= ?:m@Q@@\(?D@S@zG@p= ף?̮[@ ףp= @033333?QQ@333333@(\?֋|j@HzG@ZQ?] @ Q@zG?h6@(\@g= ףp?dׂ-؂@@?Ey5@p= ף@LzG?rN@|Gz@(\?"߼@RQ@գp= ?yo^M@(\(@ Q?@@m= ףp?dfffff?dfffff?f= ףp?Gz?Gz?p= ףp?)\?)\?p= ףp? ףp=? ףp=?p= ףp?Q?Q?p= ףp???p= ףp? Gz? Gz?p= ףp?`(\?`(\?p= ףp?PQ?PQ?p= ףp?(\?(\?p= ףp???p= ףp?0 ףp=?0 ףp=?p= ףp?zG?zG?p= ףp?Q?Q?p= ףp?0\(?0\(?s= ףp???u= ףp?e= ףp?e= ףp?Gz?u= ףp?u= ףp?Q?^= ףp?^= ףp?`(\?p= ףp?p= ףp??z= ףp?z= ףp?p= ף?p= ףp?p= ףp?KzG?m= ףp?m= ףp?Q?j= ףp?j= ףp?(\?{= ףp?{= ףp?333333?v= ףp?v= ףp?ڣp= ?]= ףp?]= ףp?zGz?m= ףp?m= ףp?Q?z= ףp?z= ףp?(\?e= ףp?e= ףp?hfffff?s= ףp?s= ףp? ףp= ?n= ףp?n= ףp?Gz?k= ףp?k= ףp?Gz?> @@ ףp= ?@uG @(\@ffffff?. @Q@(\?6| @Gz@Q?)b @= ףp=@|Gz?,l @hfffff@ףp= ?Ld @(\@333333? K~ @Q@(\?o @p= ף@Q?*;L @{Gz@HzG? @RQ@p= ף? $ @)\(@?xV4@@\(\?G贁@ףp= @Q?|؛W@Gz@Gz?iq@Q@Gz?{؛W@\(\@Gz?G贁@433333@Gz?xV4@ ףp= @Gz? $ @zG@Gz? @Q@Gz?*;L @(\@Gz?o @gfffff@Gz? K~ @= ףp=@Gz?Ld @Gz@Gz?*l @Q@Gz?)b @(\@Gz?5| @@Gz?. @p= ףp@Gz?@uG @GzG@Gz?> @Q@Gz?JRϊF @(\@Gz?_, @@Gz?U/ͫ @p= ף@Gz?ڝo^ @zGz@Gz?b  @SQ@Gz?z@(\(@Gz?l2Tv@@Gz?W(@֣p= @Gz?wS@Gz@Gz?48@Q@Gz? t@@](\@Gz?|@433333@Gz? @ ףp= @Gz?%X@zG@Gz?] ` @Q@Gz?̊F@(\@Gz?: ףp@gfffff@Gz?%gE#@= ףp=@Gz? @Gz@Gz?0@Gz@Q?*;@= ףp=@?:c@gfffff@Q?:m@(\@ ףp= ?D@S@Q@ \(?ˮ[@zG@p= ף?OQ@ ףp= @>33333?؋|j@433333@(\?[ @Q@FQ?h6@(\@zG?fׂ-؂@@x= ףp?Ey5@p= ף@?pN@{Gz@DzG?"߼@QQ@(\?{o^M@)\(@ۣp= ?@@Gz?%Q?%Q?Gz?_fffff?_fffff?Gz?Gz?Gz?Gz?(\?(\?Gz?F ףp=?F ףp=?Gz?Q?Q?Gz???Gz?Gz?Gz?Gz?(\?(\?Gz?DQ?DQ?Gz?)\?)\?Gz???Gz?D ףp=?D ףp=?Gz?zG?zG?Gz?Q?Q?Gz?%\(?%\(?Gz???Gz?v= ףp?v= ףp?Gz?Gz?Gz?Q?Gz?Gz?\(\?Gz?Gz?? Gz? Gz?p= ף?#Gz?#Gz?IzG?Gz?Gz?Q?Gz?Gz?(\? Gz? Gz?333333?Gz?Gz?p= ?Gz?Gz?~Gz?Gz?Gz?Q?Gz?Gz?(\?Gz?Gz?afffff?Gz?Gz?ףp= ?Gz?Gz?Gz?Gz?Gz?Gz?@uG @(\@ ףp= ?. @Q@ffffff?6| @Gz@(\?)b @= ףp=@Q?*l @ffffff@|Gz?Ld @(\@ףp= ?!K~ @Q@633333?o @@(\?*;L @p= ף@Q? @{Gz@HzG? $ @RQ@p= ף?xV4@)\(@?G贁@@\(\?|؛W@ףp= @Q?iq@Gz@Q?{؛W@Q@Q?G贁@\(\@Q?xV4@333333@Q? $ @ ףp= @Q? @zG@Q?*;L @Q@Q?o @(\@Q?"K~ @gfffff@Q?Ld @> ףp=@Q?,l @Gz@Q?)b @Q@Q?6| @(\@Q?. @@Q?@uG @q= ףp@Q?> @HzG@Q?KRϊF @Q@Q?_, @(\@Q?V/ͫ @@Q?ڝo^ @p= ף@Q?a  @{Gz@Q?z@RQ@Q?l2Tv@)\(@Q?W(@@Q?vS@ףp= @Q?48@Gz@Q? t@@Q@Q?|@\(\@Q? @333333@Q?%X@ ףp= @Q?] ` @zG@Q?̊F@Q@Q?: ףp@(\@Q?(gE#@hfffff@Q? @< ףp=@Q?2@Gz@Gz?*;@Gz@Q?7c@< ףp=@?:m@hfffff@Q?A@S@(\@:ףp= ?Ϯ[@Q@\(?LQ@zG@p= ף?֋|j@ ףp= @$33333?Z @Q@(\?h6@(\@ZQ?gׂ-؂@@zG?Ey5@p= ף@g= ףp?nN@zGz@?"߼@RQ@LzG?|o^M@*\(@(\?@@Q?ƣp= ?ƣp= ?Q?Q?Q?Q?[fffff?[fffff?Q?Gz?Gz?Q?(\?(\?Q?- ףp=?- ףp=?Q?rQ?rQ?Q???Q?Gz?Gz?Q?p(\?p(\?Q?8Q?8Q?Q?(\?(\?Q???Q?9 ףp=?9 ףp=?Q?zG?zG?Q?Q?Q?Q?\(?\(?Q???Q?f= ףp?f= ףp?Q?Gz?Gz?Q?Q?Q?R(\?Q?Q??Q?Q?p= ף?Q?Q?GzG?Q?Q?Q?Q?Q?(\?Q?Q?>33333?Q?Q?ףp= ?Q?Q?xGz?Q?Q? Q?Q?Q?(\?Q?Q?gfffff?Q?Q?ףp= ?Q?Q?Gz?Q?Q?Gz?. @Q@ ףp= ?6| @Gz@ffffff?)b @= ףp=@(\?+l @gfffff@Q?Ld @(\@|Gz?"K~ @Q@ףp= ?o @zG@633333?*;L @@(\? @p= ף@Q? $ @{Gz@HzG?xV4@RQ@p= ף?G贁@)\(@?{؛W@@\(\?iq@ףp= @\(\?|؛W@Gz@\(\?G贁@Q@\(\?xV4@\(\@](\? $ @433333@](\? @ ףp= @](\?*;L @zG@\(\?o @Q@\(\?"K~ @(\@\(\?Ld @gfffff@\(\?*l @= ףp=@\(\?)b @Gz@\(\?5| @Q@\(\?. @(\@\(\?@uG @@\(\?> @p= ףp@](\?JRϊF @GzG@](\?_, @Q@](\?T/ͫ @(\@](\?ڝo^ @@](\?_  @p= ף@^(\?z@|Gz@[(\?j2Tv@QQ@](\?W(@*\(@[(\?tS@@](\?48@أp= @[(\? t@@Gz@](\?|@Q@](\? @](\@](\?%X@433333@](\?] ` @ ףp= @](\?̊F@zG@](\?: ףp@Q@](\?&gE#@(\@\(\? @gfffff@\(\?/@= ףp=@\(\?*;@Gz@Gz?:c@Gz@Q?:m@= ףp=@?D@S@gfffff@Q?̮[@(\@ ףp= ?OQ@Q@ \(?֋|j@zG@p= ף?] @ ףp= @;33333?h6@(\@(\?fׂ-؂@@FQ?Ey5@p= ף@zG?pN@{Gz@y= ףp?"߼@SQ@?{o^M@)\(@PzG?@@Z(\?(\?(\?V(\?p= ?p= ?^(\?Q?Q?^(\?Ufffff?Ufffff?R(\?Gz?Gz?Z(\?(\?(\?Z(\? ףp=? ףp=?Z(\?Q?Q?Z(\???Z(\?Gz?Gz?Z(\?X(\?X(\?Z(\?LQ?LQ?Z(\?(\?(\?Z(\???Z(\?, ףp=?, ףp=?Z(\?zG?zG?Z(\?|Q?|Q?Z(\?+\(?+\(?V(\???^(\?`= ףp?`= ףp?^(\?Gz?Gz?R(\?Q?Q?X(\?X(\?X(\??p(\?p(\?p= ף?h(\?h(\?DzG?U(\?U(\?Q?\(\?\(\?(\?p(\?p(\?633333?d(\?d(\?֣p= ?M(\?M(\?Gz?`(\?`(\?Q?d(\?d(\?(\?\(\?\(\?`fffff?`(\?`(\?ףp= ?`(\?`(\?Gz?\(\?\(\?Gz?6| @Gz@ ףp= ?)b @= ףp=@ffffff?+l @gfffff@(\?Ld @(\@Q? K~ @Q@|Gz?o @zG@ڣp= ?*;L @(\@633333? @@(\? $ @p= ף@Q?xV4@{Gz@HzG?G贁@RQ@p= ף?{؛W@)\(@?iq@@?|؛W@ףp= @?G贁@Gz@?xV4@Q@? $ @\(\@? @433333@?*;L @ ףp= @?o @zG@?"K~ @Q@?Ld @(\@?,l @gfffff@?)b @> ףp=@?6| @Gz@?. @Q@?AuG @(\@?> @@?KRϊF @q= ףp@?_, @HzG@?V/ͫ @Q@?ڝo^ @(\@?`  @@?z@p= ף@?l2Tv@{Gz@?W(@RQ@?vS@)\(@?48@@? t@@ףp= @?|@Gz@? @Q@?%X@\(\@?] ` @333333@?̊F@ ףp= @?: ףp@zG@?&gE#@Q@? @(\@?0@ffffff@?*;@> ףp=@?:c@Gz@Gz?:m@Gz@Q?D@S@> ףp=@?̮[@ffffff@R?OQ@(\@ףp= ?׋|j@Q@@\(?Y @zG@p= ף?h6@(\@$33333?dׂ-؂@@(\?Ey5@p= ף@ZQ?rN@|Gz@zG?"߼@RQ@h= ףp?yo^M@(\(@?@@?0zG?0zG??(\?(\??p= ?p= ?? Q? Q??Pfffff?Pfffff??Gz?Gz??(\?(\??@ ףp=?@ ףp=??Q?Q?????Gz?Gz??(\?(\??@Q?@Q??)\?)\?????@ ףp=?@ ףp=??zG?zG??Q?Q?? \(? \(?????p= ףp?p= ףp??Gz?Gz??Q?Q??h(\?h(\????p= ף? ? ?HzG???Q???(\???033333???أp= ???xGz??? Q???(\???hfffff???ףp= ???Gz???Gz?)b @= ףp=@ ףp= ?+l @gfffff@ffffff?Ld @(\@(\?!K~ @Q@Q?o @zG@zGz?*;L @ ףp= @֣p= ? @(\@633333? $ @@(\?xV4@p= ף@Q?G贁@{Gz@HzG?{؛W@RQ@p= ף?iq@)\(@p= ף?{؛W@@p= ף?G贁@ףp= @p= ף?xV4@Gz@p= ף? $ @Q@p= ף? @\(\@p= ף?*;L @433333@p= ף?o @ ףp= @p= ף?!K~ @zG@p= ף?Ld @Q@p= ף?-l @(\@p= ף?)b @gfffff@p= ף?5| @= ףp=@p= ף?. @Gz@p= ף?@uG @Q@p= ף?> @(\@p= ף?JRϊF @@p= ף?_, @p= ףp@p= ף?T/ͫ @GzG@p= ף?ڝo^ @Q@p= ף?_  @(\@p= ף?z@@p= ף?j2Tv@p= ף@p= ף?W(@zGz@p= ף?wS@SQ@p= ף?48@(\(@p= ף? t@@@p= ף?|@֣p= @p= ף? @Gz@p= ף?%X@Q@p= ף?] ` @](\@p= ף?̊F@433333@p= ף?: ףp@ ףp= @p= ף?'gE#@zG@p= ף? @Q@p= ף?2@(\@p= ף?*;@gfffff@p= ף?:c@= ףp=@p= ף?:m@Gz@Gz?D@S@Gz@Q?ˮ[@= ףp=@?OQ@gfffff@Q?֋|j@(\@ ףp= ?Y @Q@ \(?h6@zG@p= ף?fׂ-؂@@<33333?Ey5@p= ף@(\?pN@{Gz@FQ?"߼@QQ@zG?{o^M@)\(@y= ףp?@@p= ף? ? ?p= ף?=zG?=zG?p= ף?(\?(\?p= ף?p= ?p= ?p= ף?0Q?0Q?p= ף?afffff?afffff?p= ף?Gz?Gz?p= ף?)\?)\?p= ף?& ףp=?& ףp=?p= ף?Q?Q?p= ף???p= ף?(Gz?(Gz?p= ף?h(\?h(\?p= ף?TQ?TQ?p= ף?(\?(\?p= ף???p= ף?4 ףp=?4 ףp=?p= ף?zG?zG?p= ף?Q?Q?p= ף?!\(?!\(?p= ף???p= ף?= ףp?= ףp?p= ף?Gz?Gz?p= ף?Q?Q?p= ף?b(\?b(\?p= ף???p= ף?p= ף?p= ף?FzG?p= ף?p= ף?Q?p= ף?p= ף?(\?p= ף?p= ף?533333?p= ף?p= ף?ޣp= ?p= ף?p= ף?Gz?p= ף?p= ף?Q?p= ף?p= ף?(\?p= ף?p= ף?`fffff?p= ף?p= ף?ףp= ?p= ף?p= ף?Gz?p= ף?p= ף?Gz?+l @gfffff@ ףp= ?Ld @(\@ffffff?!K~ @Q@(\?o @zG@Q?*;L @ ףp= @zGz? @Q@ڣp= ? $ @(\@633333?xV4@@(\?G贁@p= ף@Q?|؛W@{Gz@HzG?iq@RQ@HzG?{؛W@)\(@HzG?G贁@@HzG?xV4@ףp= @HzG? $ @Gz@HzG? @Q@HzG?*;L @\(\@GzG?o @433333@GzG? K~ @ ףp= @GzG?Ld @zG@HzG?,l @Q@HzG?)b @(\@HzG?7| @gfffff@IzG?. @> ףp=@GzG?@uG @Gz@IzG?> @Q@HzG?LRϊF @(\@HzG?_, @@JzG?V/ͫ @q= ףp@JzG?ڝo^ @HzG@JzG?`  @Q@JzG?z@(\@JzG?j2Tv@@JzG?W(@p= ף@HzG?vS@{Gz@HzG?48@RQ@HzG? t@@)\(@HzG?|@@HzG? @ףp= @HzG?%X@Gz@HzG?] ` @Q@GzG?̊F@\(\@GzG?: ףp@333333@GzG?%gE#@ ףp= @GzG? @zG@GzG?0@Q@GzG?*;@(\@IzG?>c@hfffff@GzG?:m@< ףp=@IzG?H@S@Gz@Gz?Ϯ[@Gz@Q?LQ@< ףp=@?ً|j@hfffff@Q?V @(\@:ףp= ?h6@Q@@\(?dׂ-؂@@p= ף?Ey5@p= ף@%33333?oN@zGz@(\?"߼@RQ@ZQ?}o^M@*\(@zG?@@LzG?= ףp?= ףp?MzG???DzG?XzG?XzG?DzG?(\?(\?LzG?p= ?p= ?LzG?*Q?*Q?DzG?{fffff?{fffff?DzG?Gz?Gz?CzG?(\?(\?LzG?M ףp=?M ףp=?CzG?Q?Q?LzG???DzG?Gz?Gz?MzG?(\?(\?CzG?HQ?HQ?LzG?)\?)\?DzG???MzG?I ףp=?I ףp=?DzG?zG?zG?MzG?Q?Q?DzG?&\(?&\(?LzG???LzG?{= ףp?{= ףp?DzG?#Gz?#Gz?DzG?Q?Q?LzG?](\?](\?MzG? ? ?DzG?p= ף?p= ף?IzG?IzG?IzG?Q?BzG?BzG?(\?BzG?BzG?B33333?BzG?BzG?٣p= ?:zG?:zG?yGz?JzG?JzG? Q?AzG?AzG?(\?BzG?BzG?gfffff?6zG?6zG?ףp= ?JzG?JzG?Gz?2zG?2zG?Gz?Ld @(\@ ףp= ?!K~ @Q@ffffff?o @zG@(\?*;L @ ףp= @Q? @333333@zGz? $ @Q@֣p= ?xV4@(\@633333?G贁@@(\?|؛W@p= ף@Q?iq@{Gz@Q?{؛W@RQ@Q?G贁@)\(@Q?xV4@@Q? $ @ףp= @Q? @Gz@Q?*;L @Q@Q?o @\(\@Q?"K~ @433333@Q?Ld @ ףp= @Q?,l @zG@Q?)b @Q@Q?7| @(\@Q?. @gfffff@Q??uG @= ףp=@Q?> @Gz@Q?JRϊF @Q@Q?_, @(\@Q?T/ͫ @@Q?ڝo^ @p= ףp@Q?_  @GzG@Q?z@Q@Q?j2Tv@(\@Q?W(@@Q?tS@p= ף@Q?48@|Gz@Q? t@@QQ@Q?|@*\(@Q? @@Q?%X@أp= @Q?] ` @Gz@Q?̊F@Q@Q?: ףp@](\@Q?&gE#@433333@Q? @ ףp= @Q?1@zG@Q?*;@Q@Q? @> ףp=@(\?LRϊF @Gz@(\?_, @Q@(\?V/ͫ @(\@(\?ڝo^ @@(\?`  @q= ףp@(\?z@HzG@(\?j2Tv@Q@(\?W(@(\@(\?uS@@(\?48@p= ף@(\? t@@{Gz@(\?|@RQ@(\? @)\(@(\?%X@@(\?] ` @ףp= @(\?̊F@Gz@(\?: ףp@Q@(\?%gE#@\(\@(\? @333333@(\?/@ ףp= @(\?*;@zG@(\?:c@Q@(\?:m@(\@(\?E@S@ffffff@(\?ˮ[@> ףp=@(\?OQ@Gz@Gz?֋|j@Gz@Q?Y @> ףp=@?h6@ffffff@R?dׂ-؂@(\@ףp= ?Ey5@p= ף@@\(?nN@zGz@p= ף?"߼@RQ@%33333?yo^M@(\(@(\?@@(\?TQ?TQ?(\?zG?zG?(\?o= ףp?o= ףp?(\???(\?GzG?GzG?(\?(\?(\?(\?ڣp= ?ڣp= ?(\?*Q?*Q?(\?dfffff?dfffff?(\?Gz?Gz?(\?)\?)\?(\? ףp=? ףp=?(\?Q?Q?(\???(\? Gz? Gz?(\?`(\?`(\?(\?PQ?PQ?(\?(\?(\?(\???(\?0 ףp=?0 ףp=?(\?zG?zG?(\?Q?Q?(\?\(?\(?(\???(\?{= ףp?{= ףp?(\? Gz? Gz?(\?Q?Q?(\?d(\?d(\?(\? ? ?(\?p= ף?p= ף?(\?6zG?6zG?(\?Q?Q?(\?(\?(\?233333?(\?(\?֣p= ?(\?(\?yGz?(\?(\?Q?(\?(\?(\?(\?(\?hfffff?(\?(\? ףp= ?(\?(\?Gz?(\?(\?Gz?o @zG@ ףp= ?*;L @ ףp= @ffffff? @333333@(\? $ @](\@Q?xV4@GzG@zGz?G贁@Q@֣p= ?|؛W@(\@233333?iq@@633333?{؛W@p= ף@433333?G贁@{Gz@433333?xV4@RQ@433333? $ @)\(@433333? @@433333?*;L @ףp= @433333?o @Gz@433333? K~ @Q@433333?Ld @\(\@433333?,l @433333@433333?)b @ ףp= @433333?6| @zG@433333?. @Q@433333?BuG @(\@433333?> @gfffff@433333?JRϊF @= ףp=@433333?_, @Gz@433333?T/ͫ @Q@433333?ܝo^ @(\@433333?_  @@533333?z@p= ףp@633333?i2Tv@GzG@633333?W(@Q@633333?tS@(\@633333?48@@633333?~ t@@p= ף@333333?|@zGz@533333? @SQ@333333?%X@(\(@533333?] ` @@333333?̊F@֣p= @433333?: ףp@Gz@433333?$gE#@Q@533333? @](\@533333?1@433333@533333?*;@ ףp= @533333?33333?գp= ?գp= ?733333?&Q?&Q?/33333?vfffff?vfffff?733333?Gz?Gz?733333?(\?(\?733333?F ףp=?F ףp=?733333?Q?Q?733333???733333?Gz?Gz?733333?(\?(\?733333?DQ?DQ?733333?)\?)\?733333???733333?E ףp=?E ףp=?733333?zG?zG?733333?Q?Q?733333?%\(?%\(??33333???733333?u= ףp?u= ףp?.33333?Gz?Gz?633333?Q?Q??33333?^(\?^(\?733333???/33333?p= ף?p= ף?733333?FzG?FzG?>33333?Q?Q?633333?(\?(\?833333?(33333?(33333?p= ?033333?033333?Gz?>33333?>33333?Q?(33333?(33333?(\?833333?833333?`fffff?"33333?"33333?ףp= ?@33333?@33333?Gz?33333?33333?Gz?*;L @ ףp= @ ףp= ? @333333@ffffff? $ @](\@(\?xV4@q= ףp@Q?G贁@GzG@zGz?|؛W@Q@֣p= ?iq@(\@ڣp= ?z؛W@@ڣp= ?G贁@p= ף@أp= ?xV4@{Gz@أp= ? $ @RQ@أp= ? @)\(@أp= ?*;L @@أp= ?o @ףp= @أp= ? K~ @Gz@أp= ?Ld @Q@أp= ?*l @\(\@أp= ?)b @433333@أp= ?6| @ ףp= @أp= ?. @zG@أp= ?AuG @Q@أp= ?> @(\@أp= ?LRϊF @gfffff@أp= ?_, @> ףp=@أp= ?V/ͫ @Gz@أp= ?ڝo^ @Q@أp= ?`  @(\@أp= ?z@@ڣp= ?j2Tv@q= ףp@ڣp= ?W(@HzG@ڣp= ?uS@Q@ڣp= ?48@(\@ڣp= ? t@@@ڣp= ?|@p= ף@أp= ? @{Gz@أp= ?%X@RQ@أp= ?] ` @)\(@أp= ?̊F@@أp= ?: ףp@ףp= @أp= ?%gE#@Gz@أp= ? @Q@ףp= ?0@\(\@ףp= ?*;@333333@ףp= ?:c@ ףp= @أp= ?:m@zG@أp= ?E@S@Q@أp= ?ʮ[@(\@٣p= ?SQ@hfffff@ףp= ?ҋ|j@< ףp=@٣p= ?] @Gz@Gz?h6@Gz@Q?`ׂ-؂@< ףp=@?Ey5@hfffff@R?nN@zGz@ףp= ?"߼@RQ@@\(?yo^M@(\(@p= ף?@@֣p= ?)33333?)33333?ңp= ?(\?(\?ڣp= ?Q?Q?ޣp= ?zG?zG?֣p= ?n= ףp?n= ףp?ңp= ???ڣp= ?(zG?(zG?ޣp= ?i(\?i(\?֣p= ?ڣp= ?ڣp= ?ңp= ?Q?Q?ڣp= ?Efffff?Efffff?ޣp= ?Gz?Gz?ңp= ?(\?(\?ޣp= ?- ףp=?- ףp=?ңp= ?rQ?rQ?ޣp= ???ңp= ?Gz?Gz?ޣp= ?p(\?p(\?ңp= ?8Q?8Q?ޣp= ?(\?(\?ңp= ???ޣp= ?9 ףp=?9 ףp=?ңp= ?zG?zG?ޣp= ?Q?Q?ޣp= ?\(?\(?֣p= ???ңp= ?e= ףp?e= ףp?ڣp= ?Gz?Gz?ޣp= ?Q?Q?֣p= ?d(\?d(\?ңp= ???ڣp= ?p= ף?p= ף?ޣp= ?=zG?=zG?֣p= ?Q?Q?ңp= ?(\?(\?أp= ?33333?33333?ףp= ?ףp= ?ףp= ?uGz?p= ?p= ?Q?ߣp= ?ߣp= ?(\?ޣp= ?ޣp= ?gfffff?գp= ?գp= ?ףp= ?p= ?p= ?Gz?ϣp= ?ϣp= ?Gz? @333333@ ףp= ? $ @\(\@ffffff?xV4@Q@(\?G贁@q= ףp@ Q?|؛W@HzG@|Gz?iq@Q@|Gz?{؛W@(\@|Gz?G贁@@|Gz?xV4@p= ף@zGz? $ @{Gz@zGz? @RQ@zGz?*;L @)\(@zGz?o @@{Gz?"K~ @أp= @{Gz?Ld @Gz@zGz?,l @Q@zGz?)b @\(\@zGz?7| @433333@zGz?. @ ףp= @zGz?AuG @zG@zGz?> @Q@zGz?NRϊF @(\@zGz?_, @hfffff@{Gz?V/ͫ @> ףp=@zGz?ܝo^ @Gz@zGz?`  @Q@zGz?z@(\@{Gz?l2Tv@@|Gz?W(@r= ףp@|Gz?vS@HzG@|Gz?48@ Q@|Gz? t@@(\@|Gz?|@@|Gz? @p= ף@zGz?%X@|Gz@zGz?] ` @RQ@zGz?̊F@*\(@zGz?: ףp@@zGz?'gE#@أp= @zGz? @Gz@zGz?1@Q@zGz?*;@\(\@zGz? @zG@Q?LRϊF @Q@Q?_, @(\@Q?W/ͫ @ffffff@Q?۝o^ @> ףp=@Q?`  @Gz@Q?z@Q@Q?k2Tv@(\@Q?W(@@ Q?vS@p= ףp@ Q?48@HzG@ Q? t@@Q@ Q?|@(\@ Q? @@ Q?%X@p= ף@Q?] ` @zGz@Q?̊F@RQ@Q?: ףp@(\(@Q?%gE#@@Q? @֣p= @Q?0@Gz@Q?*;@Q@Q?:c@\(\@Q?:m@233333@Q?D@S@ ףp= @Q?ʮ[@zG@Q?OQ@Q@Q?ԋ|j@(\@Q?Y @ffffff@Q?h6@= ףp=@Q?dׂ-؂@Gz@Gz?Ey5@Gz@<Q?rN@> ףp=@?"߼@RQ@Q?xo^M@(\(@ ףp= ?@@Q?\(?\(?Q?p= ף?p= ף?Q?j33333?j33333?Q?(\?(\?"Q?UQ?UQ?Q?{G?{G?Q?= ףp?= ףp?Q???"Q?OzG?OzG?Q?(\?(\?Q?p= ?p= ?Q?-Q?-Q?"Q?tfffff?tfffff?Q?Gz?Gz?Q?(\?(\?Q?L ףp=?L ףp=?Q?Q?Q?Q???Q? Gz? Gz?Q?(\?(\?Q?FQ?FQ?Q?)\?)\?Q???Q?F ףp=?F ףp=?Q?zG?zG?Q?Q?Q?Q?&\(?&\(?Q???Q?v= ףp?v= ףp?"Q?$Gz?$Gz?Q?Q?Q?Q?V(\?V(\?Q???"Q?p= ף?p= ף?Q?EzG?EzG?Q?Q?Q?Q?(\?(\?#Q?J33333?J33333?Q?p= ?p= ?Q?uGz?uGz?$Q?Q?Q?(\?Q?Q?mfffff?Q?Q? ףp= ?%Q?%Q?Gz? Q? Q?Gz?xV4@Q@ ףp= ?G贁@Gz@jfffff?|؛W@@(\?iq@q= ףp@(\?|؛W@GzG@(\?G贁@Q@(\?xV4@(\@(\? $ @@(\? @p= ף@(\?*;L @{Gz@(\?o @RQ@(\? K~ @)\(@(\?Ld @@(\?,l @أp= @(\?)b @Gz@(\?6| @Q@(\?. @](\@(\?BuG @433333@(\?> @ ףp= @(\?LRϊF @zG@(\?_, @Q@(\?X/ͫ @(\@(\?ݝo^ @hfffff@(\?a  @> ףp=@(\?z@Gz@(\?l2Tv@Q@(\?W(@(\@(\?vS@@(\?48@r= ףp@(\? t@@HzG@(\?|@ Q@(\? @(\@(\?%X@@(\?] ` @p= ף@(\?̊F@|Gz@(\?: ףp@RQ@(\?&gE#@*\(@(\? @@(\?1@أp= @(\?*;@Gz@(\? @433333@ffffff?LRϊF @ ףp= @ffffff?_, @zG@ffffff?V/ͫ @Q@ffffff?ܝo^ @(\@ffffff?b  @ffffff@ffffff?z@> ףp=@ffffff?k2Tv@Gz@ffffff?W(@Q@ffffff?wS@(\@ffffff?48@@hfffff? t@@p= ףp@hfffff?|@HzG@hfffff? @Q@hfffff?%X@(\@hfffff?] ` @@hfffff?̊F@p= ף@ffffff?: ףp@zGz@ffffff?%gE#@RQ@ffffff? @(\(@ffffff?0@@ffffff?*;@֣p= @ffffff?:c@Gz@ffffff?:m@Q@ffffff?E@S@\(\@ffffff?ʮ[@233333@ffffff?OQ@ ףp= @ffffff?ԋ|j@zG@efffff?Y @Q@ffffff?h6@(\@ffffff?hׂ-؂@gfffff@ffffff?Ey5@< ףp=@ffffff?rN@Gz@}Gz?"߼@Gz@Q?xo^M@(\(@?@@lfffff?GQ?GQ?]fffff?ףp= ?ףp= ?lfffff?[(?[(?^fffff?p= ף?p= ף?efffff?>33333?>33333?jfffff?(\?(\?dfffff?=Q?=Q?]fffff?zG?zG?dfffff?|= ףp?|= ףp?kfffff???dfffff?>zG?>zG?_fffff?(\?(\?dfffff?ޣp= ?ޣp= ?jfffff?#Q?#Q?efffff?^fffff?^fffff?]fffff?Gz?Gz?]fffff?)\?)\?kfffff? ףp=? ףp=?]fffff?Q?Q?lfffff???]fffff?Gz?Gz?lfffff?\(\?\(\?]fffff?NQ?NQ?lfffff?(\?(\?]fffff???kfffff?/ ףp=?/ ףp=?]fffff?zG?zG?lfffff?Q?Q?_fffff?\(?\(?efffff???jfffff?z= ףp?z= ףp?dfffff? Gz? Gz?]fffff?Q?Q?dfffff?](\?](\?kfffff? ? ?dfffff?p= ף?p= ף?]fffff?(zG?(zG?dfffff?Q?Q?kfffff?(\?(\?efffff?-33333?-33333?]fffff?ǣp= ?ǣp= ?]fffff?vGz?vGz?kfffff?Q?Q?]fffff?(\?(\?kfffff?cfffff?cfffff? ףp= ?efffff?efffff?Gz?kfffff?kfffff?Gz?{؛W@ףp= @ ףp= ?iq@(\@ ףp= ?z؛W@@ ףp= ?F贁@p= ףp@ ףp= ?xV4@GzG@ ףp= ? $ @Q@ ףp= ? @(\@ ףp= ?*;L @@ ףp= ?o @p= ף@ ףp= ?!K~ @{Gz@ ףp= ?Ld @RQ@ ףp= ?+l @)\(@ ףp= ?)b @@ ףp= ?7| @أp= @ ףp= ?. @Gz@ ףp= ?AuG @Q@ ףp= ?> @](\@ ףp= ?LRϊF @433333@ ףp= ?_, @ ףp= @ ףp= ?V/ͫ @zG@ ףp= ?ܝo^ @Q@ ףp= ?b  @(\@ ףp= ?z@hfffff@ ףp= ?l2Tv@> ףp=@ ףp= ?W(@Gz@ ףp= ?vS@Q@ ףp= ?48@(\@ ףp= ?~ t@@@ ףp= ?|@r= ףp@ ףp= ? @HzG@ ףp= ?%X@ Q@ ףp= ?] ` @(\@ ףp= ?̊F@@ ףp= ?: ףp@p= ף@ ףp= ?&gE#@|Gz@ ףp= ? @RQ@ ףp= ?1@*\(@ ףp= ?*;@@ ףp= ? @Q@Gz?KRϊF @[(\@Gz?_, @433333@Gz?V/ͫ @ ףp= @Gz?ڝo^ @zG@Gz?a  @Q@Gz?z@(\@Gz?l2Tv@ffffff@Gz?W(@> ףp=@Gz?vS@Gz@Gz?48@Q@Gz? t@@(\@Gz?|@@Gz? @p= ףp@Gz?%X@HzG@Gz?] ` @Q@Gz?̊F@(\@Gz?: ףp@@Gz?$gE#@p= ף@Gz? @zGz@Gz?0@RQ@Gz?*;@(\(@Gz?:c@@Gz?:m@ףp= @Gz?E@S@Gz@Gz?ʮ[@Q@Gz?OQ@\(\@Gz?ԋ|j@233333@Gz?Z @ ףp= @Gz?h6@zG@Gz?dׂ-؂@Q@Gz?Ey5@(\@Gz?oN@ffffff@Gz?"߼@= ףp=@Gz?yo^M@Gz@Gz?@@Gz?Q?Q?Gz?|?|?Gz?Q?Q?Gz?֣p= ?֣p= ?Gz?=\(?=\(?Gz?^p= ף?^p= ף?Gz?33333?33333?Gz?(\?(\?Gz?%Q?%Q?Gz?zG?zG?Gz?Z= ףp?Z= ףp?Gz???Gz?,zG?,zG?Gz?n(\?n(\?Gz?ңp= ?ңp= ?Gz? Q? Q?Gz?Gfffff?Gfffff?Gz?Gz?Gz?Gz?(\?(\?Gz?- ףp=?- ףp=?Gz?mQ?mQ?Gz???Gz?Gz?Gz?Gz?l(\?l(\?Gz?6Q?6Q?Gz?(\?(\?Gz???Gz?6 ףp=?6 ףp=?Gz?zG?zG?Gz?Q?Q?Gz?\(?\(?Gz???Gz?e= ףp?e= ףp?Gz?Gz?Gz?Gz?Q?Q?Gz?d(\?d(\?Gz???Gz?p= ף?p= ף?Gz?5zG?5zG?Gz?Q?Q?Gz?(\?(\?Gz?33333?33333?Gz?գp= ?գp= ?Gz?dGz?dGz?Gz?Q?Q?Gz?(\?(\?Gz?dfffff?dfffff?Gz?֣p= ?֣p= ?Gz?Gz?Gz?teem-1.11.0~svn6057/data/test/trandhisto.pgm0000664000175000017500000364155612202606415020403 0ustar domibeldomibelP5 1000 1000 # NRRD>content: dhisto(histo(???,1000),1000) # NRRD>axis mins: -4.3845973014831543 162 # NRRD>axis maxs: 4.0000205039978027 0 # NRRD>centerings: cell cell # NRRD>labels: "histo(???,1000)" "" # min value: -4.3846 # max value: 4.00002 # max hits: 162, in bin 489, around value -0.280327 255 teem-1.11.0~svn6057/data/test/trandhisto.nrrd0000664000175000017500000001032612202606415020543 0ustar domibeldomibelNRRD0003 # Complete NRRD file format specification at: # http://teem.sourceforge.net/nrrd/format.html content: histo(???,1000) type: int dimension: 1 sizes: 1000 axis mins: -4.3845973014831543 axis maxs: 4.0000205039978027 centerings: cell kinds: domain labels: "histo(???,1000)" endian: little encoding: raw                 &"!! $(%!&&+((,'&*4$.,&*4#4*3%(6&51,/.@2CA4?//=4?-3I0<A:/9=<7;I=LGD=JN@CRJP`JPLU=LVQM]QNTMAUfT^Rj?cWM^M`]`^j\nZ]g\n\jkliooiaxt^engokhtar|znvxqixd~}foush|toxtj}{}|jozwvxt}|zxwh}{{w~{~u{qv{bkr{|rxsmgn~otwq`{k`xejrtrbmMqrRpaz]e[dcgogbY]YZP_eUR\]^WKI]NU[KGPF?C]SR[FK=KBQRUGK6J>AFG<<>7@B<>FE13B9-3685.0495-*<*155&&-.8*-*#.,/.%,$'!(+#0 (#"!$##"                   teem-1.11.0~svn6057/data/test/probeSclAns.nrrd0000664000175000017500000333325712013064513020612 0ustar domibeldomibelNRRD0001 # Complete NRRD file format specification at: # http://teem.sourceforge.net/nrrd/format.html type: double dimension: 3 sizes: 13 5 1728 endian: little encoding: raw  lw?V/N[ÿWL?Nk;N?V6⡫7ɏ?8J?cz`0'?J?$pdz`0'?p9H_?vSnX?ZE[+EZE?zmIf[?X)Av ?f[? yqs鿲X)Av ?s{ J?/Rz"?muuj c?'Lҿz //?@%?y //?wj'?טx@%?טx,?Q<(?ty5E}΍ h?X%ѿVV?Y}?VV?m%ˢ?!Y}?!ߘ%? Vȝ?ߨɿw%?(-L?/]+?Z̥pPCۖ?ЂU?w0~?#޲?-fK?#޲?{@Iڿ-fK?Hڿ@r+ @C=?)ۆImVkƧ?QGaXr?rܫ'b?1}x?{ Cp?2}x?koI6?Mؿz Cp?Mؿ5?;+}E?Ǫ]?U??)O?D2?`?D2?QM;?+#ʿ`?+#ʿ ?b؇F?DSooL? w?շon/?=}?!v? fųT!v?7q<@;@nտV?XR^?̦t埱k0ClhWTVy"TVyI+z\?"\?$Ob`?_dsf?@&=;UؓţjìIdţjìmB!uٺ翏?f?Id?f?C*WI??X?~UMYkuQFk տX1@ֿFk տ?\ O?@ti?X1@ֿAti?Вc̿z:UxJ?ųa?~݂ 9<,o6տ_>;ֿo6տ`* ?g?_>;ֿg?ǿr-ቤ?U8̾տoO sÿ4t?/MZʿ;?Rc?PSEm 4ɿR.9?wB f@R.9?Sm @F%?wB f@@F%?S@:^%?4M#=$¿l?DYiwVݿ,rͭ?A?,rͭ?I vlz?A?vlz?JBp@k?,o=RNO헿STw+Vݿv\?W?v\?qtD,_?W?_?7Af?25 ̣oϰz#sݿŰ)?Jb?Ű)?[\m߲@'?Jb?@'?1ͺX?ǼF1u?][A?R b? ekd?@? DL^?| ?[e,ҿ{G*{6Eb?O?G*O?-r?En?pɥ?USRT?r1bG?5ֿL-9M-P5?8Cy?98Cy?"3?+e`N?͢֎?1چ?ih?7/g:LH=PD 1Gп:LHmsv?Vߙ?=PD 1GпUߙ?Dȋ?GJ+zM?o ժO?)M߉?"l?i y#&VG{BL&п#&VGq%:a?:6i?{BL&п96i?bu ?M!7?P_*ʿ^?cZhrxC?+>ǘř?K ڴ )[S?W/?S?9i?q-]jW/?r-]j> O7Ë\2?hfߖ?_Ę?65,?5Ŗӿz?uϒ?z?C:?vWt?uϒ?wWt?kWg$@?DLUJ'gؒ?]~2!}rp<'?ʧ?<'?%l?]?ʧ?]?Xh^O?$#A?(K5r~Y*?y ˝x!^j 7vy s?|ɮ?vy s?@I?WS?|ɮ?WS?C ?V?pa߸.hÿ\e,v?XnF??BXg:mwKV!04?V>?sˉ٦?G>04sˉ٦^Q?C&?q;??= ?ϫK=?#$+Gؿti*?ζЭti*?\043?19JͰζЭ19JͰ1, s? ?Ս?wFkR?~"?fF$ ?]tk?R!@'fduh?'fdH-8?Ŋ?/*i ?J?P)E??n?эL??n?/3$?ޛֿэL?ޛֿ!n?Wb7?N炿\BB)?/^M p? {ҿ]Yp.?Ot?]Yp.?g? 7㷱Ot?7㷱|QZ?3q5?4pn^Ж?yoݑ? "Ϳ\ ?sK"`_C?\ ?{bx?[Z(ѴsK"`_C?[Z(Ѵ'"Ρ?o,?; I?)t)?bB_}?;Nqu?`-d~f?4V:q9kv?\]'?ooڷ?#~Eooڷ?EZ@"v oӿ#~E"v oӿZr?73JH: >r?82?ƿ73JHƿ슇S? .s?g2?Ntt?5Pe?k'¨1?4pB?1?J?Tg4pB?TgU/? @?MM?m-wAみ@?WU< ?MB¿<|%?@oz/=|%?"?Oտ@oz/Oտ@F?ܞ?6 ÿxn?xHЊ?w>?Qb±W<ױ?z?53B?AոH.?Aոt?J {$H.?J {$e7 t @A;?ҮIC(GƮ?4ZU?\ͪ?di3j?G ?di3j?F5C?X`F ?X`꿢phQ*?'O?`!68Ť?? C?5Z V8oRϿ5Z Vds?+=ϔѿ8oRϿ,=ϔѿ\hO8 H?bj(ݘ*?Jo???TO=̿TOBXr?zѿ=̿zѿ|8 䰕%?eܝi?ڟWL^?PoxR?jU?j?s=N g:_Kãm_KR'af @H{< ?ģmI{< ?j8Km?\?yg]K??3eM iv鿋>YD⿌>. cP?Ohdk?YDOhdk?RwD7~16?F X>[?Inw?X0!0nN !޿z,_V¿oN !޿ue?:i; ?z,_V¿:i; ?M,Q<ǿ )5?a#?k0w?P4g%`Hp5ܿ^޿ s¿^޿iV}4?&? s¿&?Z;¿B28|?pο"%P? ?/IJpq?` Fб4!|1Z?R^b?'(G?/?'(G?2cS@9bLr/?9bLr0Mi?^*N?ǏiV'hB Ժ įm#OX%c?yC?X%c?T/? R?yC? R?jڬx`ҡ?-zԪ6.1ԏ} n!vO17ս}D? :w ĺ?}D? hDg?L1-? :w ĺ?L1-?謀?rlӡ?q:[~Z*݂_v@Ws `3ul?].?ul?*?^'?].?^'?Uז?޳?҅٫?܉ͣ꼱?{kbȡ?rZѕ?H嚧(??VrN?5I ʵcէ5I L*5"mboʵcէ"mborpP4?IQq^?2?fMš s ?գ*=?LWz=M忆LWz$DTRyA,?=MQyA,?KIs;?鍊? %?9U&?hAQ^y?| ɽ?3HY#ſkɻ2HY#ſG\ Ͽ>g7?kɻ>g7?vPvu)?}b=}?:S?/X?fz?1|.?Z2ſ}rZ2ſ3Bѿ##'?~r##'?= yu?IRDT?f BAV?C>k0Ѣ?$(3kQ}=??!h_IX` \7@:=? \7@dx&?:=?&?H_Iᾆ?tĂ?46rǔ&Fr<?ecS4L9?<?^Q=?ecS4L9?Q=?/Ϳ &dޡ?oph?}ⳍ?[٭j?.OÿQ{6?l?R{6?{KZ~ѿ}Oh?l?}Oh??]?f9d?Bv)?cl?-i¿ #h?J/՟? #h?FeҿG# ͧw?P,^ſCLKȿP,^ſΰ(@@icCLKȿjc q` ͡?,cT`S?z+Ѻ?Mk*#-m?L?feQa+&澿feQa؞?0?g<+&澿1?g<f#bB?x Lf?x;p?EU/u?o"H?mม?|0c:ƿe z|0c:ƿV0"rN)ʿe z"rN)ʿ-JҤ?͎@a?Q_?*$c?L?,D?Te:Ŀf4}Ue:ĿbL8|%lxɿf4}%lxɿ)?z#]?PQukk?CFx?K) j ?~?p?Lk?Eщ&D#?OI?&D,ʿ<%J?#?OI?<%J?sTii?+P/?+{EvΠ?oZoп](?u1>oпf+ѿN](?N6Jaa?sٸ?@˿uՂm?K9s[?wK_wol#BG6?o{a?8iؿ?8iؿྉ, v8{??w8{?KF_?tG{-N*?t?&yKy^[o?*tBr0?My8tпt:?My8tпmNm9>&?t:?9>&?v}1c?ʢ?ba7ELζ$?_˗?;AhsMa?h^ZQ׿BMI?sMa?BMI? V?@2} Ţ?UjۅgT8Z?p?'rFuvX`:ﵿ[tc~?tvX`:ﵿR+ؕٿN _?[tc~?N _?@.mA?|?>?DE΍?Zٖ?$N?gd?Q1?Gz8ޙ?){ڧ2@dl?~bWfٿdl?#]Bbۤ?~bWfٿbۤ?pQ?37?H&R?(F(?ڕ ?X?FfPO?jp9OԿEfPO?>E ڿZ?jp9OԿZ??)p?pH?3`5??n5/o?I?08I?N_Xz? ?08 ?%?KGGDq?Q¾"?Mv¸?-/ё?s<@֢?D\b7?nS n軿D\b7?}:?F|N?nS n軿F|N?r7F?>?*0-?|}t≥?҇"&6l?3P@ +rF!{߿+r迅!tswF!{߿sw3r+ܿvʐ?^gk3D?Y{ac 0O-''zؿړuտ'zؿ!`'ړuտ'%5?8$ ?d.x?pn ?=?[׿E5L?}F{1fE5L?n3bĿI 6ʉ}F{1fH 6ʉ+1Ƚ?cW~?qQI?}^ ?j>σ?wY ؿ](a_?ȹM](a_?n+Vnſ|=ȹM|=|p,O?R=IM? 3ǿ?T]iU?^P>YN}?Ԩ?sQ?oqӘўq*4?nC dοm=ʛnC dο/3??m=ʛ?j[C,?4mؔ?:0Y?z-CbX^?2ZmAU?2Z/|Q>?7?mAU?6?}8|ׅ/9-? \ctWB%?T)xd?v`˿dl"?hudl"?oKxj̿deW9-vѿhueeW9-vѿO4וN$?g|Qd*G۠?s'b?\ ȿA?PAA?qp9˿mzyпPAmzyпS6ؽ Ȣ?*Q;7I?l3W`?]1?IŖ.?snv?UB?xT6?"rR?xT6? OW)ȿ"rR?ȿhӣ?[?:쮲-?ɬ晿 'S?D["`?b?h?('M?h?lп('M?lп:nGǢ?0.oQOe?xQr?`ʄ`KD 5ҿmFE*ϿʄmFE*Ͽ57D9п=Ģ?Q͐^j(?oŅR-?[k̋Ep@4[k̋rfqӿޝ0ͿEp@4ޝ0Ϳ rd˿ۺ}?ML?ͼԢ=Xr,BIߣ?Iǖ@gd|C M,"-OX~*,?-OX~id$'1=ο),?'1=οwCfyѿƚjJs?D5^(Y$Y ?UVKpj)%^p?pjpGQgw<)%^p?gw<(M ٿ|=>?IKb"9Q9D?)f,r2ؿֿW?ֿN:"zbud?W?bud?m&̿unE?B%!-?X:Cc/E׿!5׿O5|?!5׿Wؐ^a3`N5|?^a3`s;؉ǿf٤y!?Mqd^gaؿB:=?p?*M/|q? _N:s8?*M/|:s8?|kMXӿM˾,?ɉʬ>)Ah}Z?ro@O?7MԘI/?ro@O?I7?*6Lh57MԘI/?+6Lh5BO(2+|?m.9PtcS?;??`Y??7S;?.z`Y?.z.c.X?\e?7V9qu4EU?|^?[?-?[?Hgs? ς-? ςR*ͬ?(x[?т1aw:8Qm? 䫫?ֶ4@ҟhdZ?In?lu뼎?lu鿴dT?EW6?뼎?EW6?_m?}?̞ǭCX9u:NҜ?̍2?R{޿x{?Q{޿Ӕ^_?:!?x{?:!?-?a?Yj?Ymp$npH:s?k?o5CǿOl?o5CǿWi?˻.ENl?˻.Epxj?a&8Inpu$r~c6? =v?3Hƿq*γ?3HƿP4?3\p*γ?4\+nU|?/[շ?B5}Ϳ+M? *?-'`?3gH??E=ޯϿl}ϵοޯϿo^@Hf?l}ϵο^@Hf? $@53?C{ ?lKɸ'?xA@P8R޿HӍ?zkF跿HӍ?sݿ *?zkF跿 *?x<˃?T?>oК?aJlW7ݏ?Qa?krz?A r?krz?&9?$sx6›??je?F>@je?hPGyl+?E>+?$BοZգ?+ "?Ϗ>ۧ?ku-i3tB?>Dgi3tB?>5g-s?>Dg-s?s\b~ ?ypCwx? QG?p c?)v&nǿ۸|=@~)ƿ۸|xk促pMH=@~)ƿpMH rmQ;?:e5?fuw?%_?c_7a?mƿ~ h5>uſ~ hPrF5>uſF.{?7Uk]uPLhl?$gadii#U왯Gs:Կ]~DYx?Gs:Կt_NI?D`f]ۣ?[/[t"j?-P?w+3k-?w+3ῦIhPHk-?hPHĮfbfÿ^ٖ?vM%IXMÍ?W(ӑlYɬ?ԯ)迒Puԯ)_~>Q H_a¿習Pu H_a¿ާMڿd0TF?.jyeJg?a$O㲩?S+\]8<)GvS+\]h*=G8膗8<)Gv8膗OnVֿ.]?Ɗ%p+?O*C-bM\74?wO?(>phB ">~?A<࿸">~?|q ; ?fcRAi8?>H5ļ?x CD ?=;?EǕ扱?=;?{nwÊ_fEǕ扱?Š_f}鿜 X/?(0@К-Qځ?Mb< +?N6Jф?N?N6Jф?XhB8[P۩HٿN?\P۩HٿT{ǿ|uC?,fؿgqE?5`" gqE?3ڂ2P4`" 4ڂ2Pg?^e?:9e(F)?ux?5Wt?_ࡴ̿s?SSN^񿌮h*`ࡴ̿h*7s4c5?]熿\(KЅ?]N+NS?Memr?!D=!Z{C?!D=Ő.?W۷!Z{C?W۷u PD$'I?6{<ڄn#Bo?BI/a?m˂r? 6j?IN? 6j?>.^?j*ޭs¿IN?j*ޭs¿Vek !.(?9G$?T"?TS$?X*?ď*?r\ð?c<;?d @h5=k[?h5 9㗔@B㥝=k[?B㥝L>Z'M@w?͐U{?Z`Z!?1S?"œ?Awhrw?AῩ ?p@xhrw?p@|?z?Gٌ廛?BCVp?/ok|?pK]!2?¿)??¿2c?:ѿ)??:ѿ-)Z[F8"f?md6?6B Ru?tٺ~?Vڨ?P%.JvȖ?P%.JHY\? qAҿvȖ? qAҿve?Ĥ?l%}ȥϓb)O_?U"Y?AZx؛;,ȍ;VNف6^f,ȍځ6^f俓Xu"f!?ܰ7$t]O"5䱔@%~*l9T~*mk$oTȘտk9T$oTȘտ;~ٿ@D?c^ vEnk͏>"^-ls-ܿG+ѿ #7G+ѿS|]ʿ(ř #7(řDֆ?s h ?+x?8u+˦ݢKmik*!Zۿ 1Zѿ 'mT 1ZѿcȿA 'mTA?a6iI$?_?XbcL̖T7ÿduo?CU~)e vcG`)ƿ^?$GfO}=?`C>)ƿO}=?CymB?/TW[? Ў|ҝ4abtZ7!?r?_Ga.%1*?_G[^8ֿV&?a.%1*?V&?Qly?QKvc? ׫=}ݫ:?jmu?A Mɱ?A MɱR{ۿkt??kt?Z( %?F?s%ÿg?+S1Ǣ xŧ?bI2tbY:#?Mӿc%Rm?>Mӿ2E9ϐ (c%Rm?9ϐ (i:u?6T]?|-=?rVAnߦ5?<'?pbu?>6_ÿpbu?c6ĿTӿ>6_ÿTӿO?bcV? ݓ ?}7 ٴ?ù?v?/6@8ÿv?}֜ƿaCuԿ/6@8ÿaCuԿ_)?0ug?1 |?vl%Y&Z?4Z?z6H?`jt`? 'D @vȒfw濟vȒ?ޑٿfwޑٿ?قJ?kk?\o9FC|?؝r7?E#d_Ŀ/F⿧E#d_Ŀkb? yҿ/F⿦ yҿK?XQ@Ǝ?H;|? T_EM|]{aV/?2q"?*ֈ3ӿ2q"?BF-j+ֈ3ӿ-j/c}?R`?-2EiOh` DȔ?~NM?w[%[ҿ}NM?| asw[%[ҿ asrn-㻿-}nĦ?xtn@o?Y)=替]j* ?hr?)}Ҏ퐢fĦB1B~}T{ɸ ? )P4T{ɸ ?䊽X "x:?)P4 "x:?sO7y?c_њ?Y!4 d .ݙz?iE/'ݿz?T鿌9?hE/'ݿ9?!ԍ꿏`?F_?܆q./_%ۓ_[k1ܿ^/?z?tF˿^/?iEĿkLٕ?z?tF˿kLٕ?Է$]*տS@WjY?pB>rU;?VUzfrxXeٖO߿/qng?5犉ʿ/qng?ϓƿ"?5犉ʿ"? Dmӿ[m7a?_t{?洿o` ?KJTcw|D3?=E(3g?ô؄W+a _xx? _Αaʿ-mkA'?xx?.mkA'?5roĿbRH?iQc/V??bЋSPAf|?5ǢuW?f[OJ?/-V?(_㿅ųqzɿ(_ظjf?rϒ?ųqzɿrϒ?߼fV1=`?@_}JE8G?CB闿C8nt?3Oտw]u&Ϳ3OտRtb^?&tI?w]u&Ϳ&tI?QVտZ^11?~es?wjy^[|:?ј0?{И0?tMG}]k?{~]k?;}fj4?Ҝi? lxe6?e7?LNcb*e7?_-фDC?MNcb*C?{'I,V?[Ρ6-A^9e2?TX5hI{1|E1u!B? 6jۿ~ۿ 6jۿ-q(?~ۿq(?{>^^ɿYwmޠ?O6C}>U̥N nb?&?#lvοcOKeP3'?#lvοdP3'?"6e?qkL?K5pTr[?&2{O,/C?g?>?Rj?Rj?ߊp?#% ?"% ?.z߉3?d$“?P"s]?O4ʩz_Fg?A?$EyM?̞~$EyM?e/U:?3?̞~3? bu?D7t?D4[?1֒Jv)f!稢?Ó~; ?k& f:.S濇1&Gٍ?x-d?1&Gٍ?_B?΅L?x-d?΅L?7|- '?yޮ?٭y*eQݗudSyF?l9|ttdSyF?[c?xgր?l9|txgր?X3k 5nӿn\d}?پM?t? ""澛/=ѿrT4?&OrT4?<8񷊥?)iV?&O)iV?c#?@8v'z?l.?t$?Bbи ?\@Iп$!??$!?Pje?[.??[.?lC?('w ?8/¿5*:?<')؉Nj%?P6=~׵ Za=w?ï,ig^nF!tyA?ůjz?tyA?.E;?ym?įjz?ym?j/^0Zhס?4•x&#)é?ۂh,u?L@?: 8^? <2|?: 8^? W?+=Q? <2|?*=Q?jCA5?7h_?U:3y3R?;?vͿ8)z&mO,?8)zȀ$?<?&mO,?<?b?Z?Xm`io? P?}Fh0 ɿ+U?ԓ3eߝ{?!]sl>?!L?6TVS?ߒUgRs?G>Rs?z!@pmPǿGpmPǿ6bz?^==?qg#5?Vj.1ʸ?fx*?g <ҿfx*? 7?1kȿg <ҿ1kȿpb]?Nv?,Zs"w?B|ჿ?X;h`8yW;hvej?V#~̿`8yV#~̿%b?RJNc?C큈P?_y?Xڑ@c ?GJq  CZ?8.˿GJq 8.˿d`?d!?JP=?j}*U~?Ɛs?su@? .,Q\R:5VCݩ?I?oL!>k lث?j\?Qܠ?$MrLạ;?/jb??Xſ?+HqYⱿXſYⱿΣW"I?j?ƣ?Sz0ɼie}xwwy?D?GS?ż$GGS?k|}jż$GjQ?CE?-n M? :S֊ZFzs"9̿zf?ѱu񴿩zf?K7.;8u*;?ѱu񴿼8u*;?˗zĿFk?Ȯx' ?'33r6/CQRƿ+t?C]+t?Btw?C]w?ÿ-Mݦ?4*c?T?p[Aܥ?5zO?\:0ɫ?>%$& z]dH?61޻z]dH?JV4 u?fI$տ61޻fI$տ6ɹjkt?>B?>6)|?d%|U_w8?uQZԿw8?K{+|?Y0[ÿuQZԿY0[ÿRU(IDOu?TvBrѵ q؞-呿#HIF;?QEwIF;?cIƿ"X ?QEw"X ?O`*οG偤?~A;kdV^L=㿝J?3[Y?J?Zvbÿi?3[Y?i?ɚ˿ƼE_?O"U@Ng?UVԴ?U&?LOvԂS\- ?V3?!޿pO? ޿xBM? 0cgYpO? 0cgYzs?wڏ?9V^gGl~D?$hW?A;ſ!'"?A;ſ@?l4gI!'"?l4gIi>t?7"(?ekZwrGK#?y@=鍿PbG?y@=鍿۵? a`uPbG? a`u(¿xU&?~hCyP^a uKm?`(6?9AVw}5?9AVw}5Ŵ?9[7V5?9[7V(^d?3 }?KŜyֽ~Nl?eLc&?2 As{?Gz+Wޚ?,# DR?O-?;kAfֿO-?@?7l3?:kAfֿ7l3?Eg_?M\?(?ZA-㏿54lV?<?g6P?95пg6P?$<'t?"Ħ1?:5п"Ħ1?(?#Ln?Z.u?Y4ˀ?IhiP9?TC?|A嗿TC? tx(|A嗿x(4''ſ͞?4mt?~'C?{ T̄#^ ?f'6p?/>͹/ f'6p?E? $ k?/>͹/ $ k?xE+A}:Na ?)d\?Qeĵ?nXȬ ?jH(? VV?JnH{f?Ca?{@k+!?r&օ?k+!?҆m?ŧCPr&օ?ŧCPXf??J?؈ ?Nw?irB%?u2U@X_?7n?Y_?׋9 ?SFֿ7n?SFֿؓn?^"5S?-Y??%R*?Ec&?Ȱw?7W?:r?7W?Z_U?}enPX:r?}enPXbo'?ɱV'?Q?RG_? @z2# ?^&ᚌ?w?i`?w?7Z#?qXqmi`?qXqmO])?Z=?k7?x)8? Ar?'k?Ss᥯?Mb?*q?CfPC ٭l;?OC 5R,F)޿٭l;?R,F)޿%ð?il?oȭ?ɜvV?ܟӄҢ??̕DlJ*-$使Ō-)7_Z܄?A5.>-Z܄?X`K7?52Ōn?!kwU?0zs_? L?pۿ(n;òdx(n;0`~Q9?òdxQ9?`Q'?\~C?U1,9#?`X?$f?/Z/<7裌?Ug|?J)ѿ*jCɿPL?+jCɿXZEݵ!vPL?ݵ!v>7F/? la_?C5ˤ/>Q?E??{M˅ZĨS_?=h?[ĨS_?J'nyI=h?nyI?\0I ?I"Đ7?Hy?od-Dm,?<)}?.Dm,?M9Xֿknl?<)}?knl?2?n?_ HǐIu?Rd=M?#Z _Z?k? _Z?cKADؿL-7#3ik?L-7#3iV?Rme?(1?r{ه|?nIW?*?ExǍ?R ?IEk? b?A}[?ybqS^A}[?V#ﵿybqS^࿽V#ﵿE$X?o;??^?FmvG ?Ytȟ?hIH`]?AA.U?}|mӿBA.U?3G;o1}|mӿo1 _?$p^n?FG sq?ĕ?+QĦ嶿=s?]-T=s?Z̿̈xQ]-T̈xQ=/ ?Wt|?#n> R?̩ ?5r H5?K&ҝ= H5?ajxEѿJgK&ҝ=Jg:?"6v?zu֓ iGtjV?.]i?/p[?େ?Qm=s@!?,IV!s@!? ? ?,IV! ?5?.XӠ6?jå<^?M?a}6` M?A¿a M?bRƓ$ {퍿A¿$ {퍿4;a?wM١?s`4KŽ.?&?x)@ʿ7GC?W'i&7GC?cIy ȿV'i&𸿐 ȿ% ?Rd?qG5n `;?68?R?xjͿ5v\9?yw9C5v\9?@`Vnǿyw9Cnǿ=$?#f6?2^?_s?7C(&?VJsZ³?0zoj?!@NXˉ?Լ?NXˉ?;?O$GԼ?O$G6k?=|kb?wزC3V9_?ZP?+Д?[?D?[?I:?Vu %UѿD?Vu %Uѿy4"?[mZ֠?s?R8ӝMz?3jLl?g?In?Nm瑿In?^?ѿNm瑿ѿ[?hߠ?18W}j͕?LɁh?o A?E?L!)WE?Lu?YB`ѿL!)WYB`ѿ;H?,Kƥ?o+}?聐H?Jj ?Fz_Fa|6ȓ񲿲 BO?f刔 鿲 BO?jIKVAŕP?f刔 鿗AŕP?r#|f=E?$c}ʦEuJ?%E/::ȿho?mho?d՗p?mp?UXoZῬtG?/m!ᆿX;_?yy?_8NJ.x0o!x0o*D \Z ÿ!Z ÿmlƿwp߸A?CxP@y? x狨jDڿb7,DڿVu}过S y:ÿb7,S y:ÿΑHĿ{[, ?6FNANɋ?9ɾ{=L?2B ?-Zɫdb׿|OYr!{?|O忮Iʍ0{V?Yr!{?1{V?=5ݤ?ƥhDe Xqۥb -YpֿC?pֿm*~`!? C?~`!?)1Ýu#?4㬢Ye"䒿F%ֽ?M.zqQm"M.zq.a>gR县Qm">gR县Dg1WlȿG?И7-- [xdjXSr??H ~ndz$8m:Փ?EcECW`?EcECN<ͿxQ?W`?xQ?ѳ3{пm%?Te%o4hlmi` ?u gM>ʷ?usiO̿2? gM>ʷ?2?-F0Ϳ!Eå?lb??aۏ#o|?sāL?WWO3P $$%&ܿ!@x-&ܿ!@MN c\bFx-c\bFċc!f^?D?RL آڌy^|'gK?IYaz 'gK?.}2΢L%}߿JYaz K%}߿W=OD?2,jH?fÍȢBgRd?PLl?ѥ j˿QLl?{ڸ^8`“྿ѥ j˿`“྿QZYʿ9?辬?%ܷeNW!i3?'?- ʿ'?2BVw7- ʿVw7xFÿ윕W^g?KH??*]o˿0?_28m)?(g线(FrQd?^&?Qk $3'?d?^&?%3'?hwO(eg]jc?0zӣ_ M?kQYm׆29ib,?׆29Ǘ+%m#n\!?ib,?n#n\!?ow-I?K5?srdxJ쩗h\uR=q"l wX]{? wXpnlݿd9pп]{?d9pп-蠢?4?$?kyk[ ?3?Jf͡?Uޡ+?W,11QKH?N J?)bSտ_?)bSտ& Կ fwؿ_? fwؿ[H?I[ݡ?Z,ay?9Ge{я ?44-?ϻe-^^e?ϻe-\fDɿ/%ym^^e?0%ym5~?-|sj>?ʮnlǃ s?**:z?RE\z?˻Tb?]='?˻Tb?ם>]?48]='?48P=a ?Pn?y>X|?AH3`?<&`s?{K*?<&`s?4O?{K*?]J?H [AE:h?lH@?(ȎN]ku?cp?\s{t$0#? }~ ݷ?t$0#?iѩѮ{U? }~ ݷ?Ю{U?R4H?!ڡ?˫l1?Hs?X>*࿷ND?|F?ND?G+5!|F?*5!RC? *N ͬ?Q{pUp V?>F%ʋ???3F [??1?3F [? ">?3h^l?1?3h^l:̨'cN?1(qKf?4j?7:Ty?A$$I?0H}oX?A$$I?M}ϰ?|}g0H}oX?|}gƠ6d (l? ܜs֠F]?S?}ZX}P?aSs7?k%pL?Gܝ_RA?,(K??_RA?!ڿwj?-(K??wj?j~?6s?o;?hCw?KM;?K?dCU?Tpw?dCU?dYWRXN?Tpw?N?2`>?ƞN?!?S麂? AD?&XUA AAsβUA *e:?\EC-AAsβ\EC-?̐?Rmփ?S R?KLo?cÿ)ֲ 㼨uղ 㼨 ,^׹?b"qQub"qQw72?B $\?_ȗy?^=З?rS9?Ӻc?C?3PHV?ˁ)ژ?NzMn?h]c?WS@?[Γ?WS@?y?s>೿[Γ?s>೿لe)?EK\?6:ÿpq?JSH$0֣?:yிߴϓ\SHz3=ɯ?HzG?TCh3=ɯ?TChG}ڿٔdģ?9u"/o$ :3?!kέcUJp.w?灿m?灿za?E?m?E? nƿڈ ?Xd&X * 2?j~e?oIxX?l7y_.?WV^?S)ѿ+BۿS)ѿ.?D=?+BۿD=?K?No]?‰qN?| ؙ?&Ci`?g?`ZFοg?c=i?dG?`ZFοdG?< ?n(?)î1mG$0EÕ+ W?~Rc?nX?9?nX?tUv۹?^o?:?]o?h.>`pr?h蚌w0?t:-hW?'rZ֤?3-L?F.G[?3-L?| YO4ͻ?kƲ?F.G[?kƲ?=uf\5<_?@qa?j>E,1?AtY> 65rk>:@RoI?7¿RoI?0XG]X?7¿F]X? rTcۓ?; !hʙSprW$dI?|5]k,Z)k[?s,Z)k[?glF2^?slF2^?`/?cѠsLS政 <%0uLk/2ƿMmi#L^вMmivg_?'Q?#L^в'Q?+ԁ -7ڍ?T"0C8TU76eDͿ.X_@$4"᰿.X_@Օ3'? a?%4"᰿ a?7Ψ&?Ԭ?jn|/?z+㠿+0?M@- е?U󬝿fȟRTS?,p+,q5;@W p+V hr^R9:%v?,]w?&]qD7Xp?ce^7c׳fR@Ɖх-Ͽe^7Ɖх-Ͽ DMu?W9֪=?V0/!(RA+l?pqjQ̿2{ɿoqjQ̿xa?Zv]?2{ɿZv]? b /t?ہ⌎?Ɓ^.UߡC?vS[̿;jlʿvS[̿w$?F7?:jlʿF7?R)iA-P_ ?-b>m Ը;h?֩7@NH6*{\1NdW ?p! ?NdW ?`cH7?~.6p! ?~.6 Y~YȐ?q[u')]yyc;>m@v}? QM\Ir?@v}?޳,?x .?!QM\Ir?w .? #%9Sբ?I ?Jըr5C6œwv oVڿnbl{ABpn?nbl{GV  ?[Os?ABpn?\Os?p2ѓͪiڢ?ra%D*4haTlp-Hަ5{?, a?N;Bl?Hަ5{?O;Bl?>Y?ª#nZ.?m?"P"?&˫֣à`8?s2?  r?Qψ?=B %?Qψ?"O3?YK=B %?XK?>+?nA?m`׉?b?beը?@| *?beը?T ?vXVۿ@| *?vXVۿI8ע?da?tswPno;?e?LLj끄*냲?u*냲?4 ӔMuM ~Dž?5-O8]?I$onߡyy?:y[f?[L{x?iKȽ?/jKȽ?ĕm+-IJ/-IJjAZ"?KC?⧽B|?L&@RT??D˱?7V%~?wN?A}Ճ(?l< #h >Կ<QU^ѿr|!#h >Կr|=_Hb?~iq?VNm6eׁ?P;^ژ?ՐCt?Gg . Ej^Jyÿ. Ej^m\S5JyÿS51xJ˜D(HlU?Pmݓc? [K?5,\ L?GJ?=x?GJ?SƔQŪ(RĿ=x?ƪ(RĿk@XrE0U?Tzۦb? !?쵸mD?gA/? 1?u2[;? 1?x[ Dsſv2[;?DsſXDl^?r>n"?_W⊷-ǂxG J?hiYTmdՕJ׏v?k_ZC@zſkQeC@zſ##?kQe#?בh?[V?;9^L΀a~n~?鿑jL?2ۿjL?jBhZט^{A?2ۿט^{A?. Z}?+w|d~*" y?;{?O⡿&eNh?TS&eNh?ǿAl?TSAl?  L\ո8?ٽ! }Wnz?d B{?w֭|~6?t36׭|~6?;Ubiȿրy?t36րy?ʫlw?j%˿}?!?R?d p?[(V_7ΰ?كA??'o@6X6?gN5"?6X6?eKF?_!˿fN5"?_!˿M??i|?FM'i?O!ʅY?"3:?1HCu?_?1HCu?tv?_ӗ_?_ӗ|T?gU? }yQ?1=?Qj?~qk?3K?J3K?|O5@!T~J@!T~eaǍ ?CmAU?T>I?)? ok?%gD}9O?P@=?Bw䴎P@=?r{gTRʢ|Bw䴎SRʢ|b}?$v)S? /`? C?C6'i?LJN>Z?aX??cn>R?Уbn>R?!4Ϳ7̹У7̹WSU?L {?g$HXEDoj3,tv?G$J^׿[ZN? /%?[ZN?WP e远b /%?bl?x'ې?ՋW}{P=rZ!Kw?k6ο5j?G=T8?5j?1sԿ}OÿG=T8?}Oÿa?"%/f5q?$?7 IN?"AV.0r2Q- r?dW`?Z?2|?dW`?2QUK?) ʵY?2|?( ʵ /Cÿau?^BU:?n`魘N9g*?bwR?%?jk?bwR?%?OyqZ?viWjk?wiWZ glLTV6?@޵G`Y?PI??=l+Zro d?c?ZpU?$Wʿַelm?$WʿA @ېַelm?ې''@@<2?Pڛ~vBa?V?C?IYS6?IYSEi1Of?9ÿ6?:ÿ)N ?wC@A{r?iS)(`.(U?cfm8?>³/f?:H9W?/f?f|?S49H9W?S4<_)7p?8oeNK|Ĉ? b?p@f1ˬ?`9?f1ˬ?it?G /`9?G /FSS>%?u)?P*z?);?HA?2˖n\? gP; [/2?et?x Oɔ?ZatpgeJ?v[?Ltuc?ڛ#AALtuc?_[?8=M̢ڛ#AA8=M̢͞b?ť|eE?#u?qH< U"%?+4w? o`?,Ƃ` o`?Pʞ*?SH?,Ƃ`SH?J?u&?^,3"Svs?vR`}?+Z嶽?J*"itYb?J Mz?֍и?H~uVw櫿)?H~uVw櫿K##?w?)?w?٠?`"=?.bwPw?T?KU?i{*W k?:c?̣BI忛ot-h?A(^?ot-h?=ʮ?(>)?A(^?(>)?f|V=@P%()?ORs<:'Ą? bB'?Z&@յV?R1z[?յV?6hz?{[.?R1z[?z[.?":'?Y(~?B#%_?a_*?U*;U0?$ZeE?U0?t?&z?$ZeE?&z?S%ƣ?`?Q&·%p>L?ѐJ?4P?¿Mot@?\pU]?Mot@?[YO~?hO?\pU]?hO?g9!?x+V?G#y??:jX1^ﯿӿ`(?ڦY?ow>~gfx+9(K?r?跪Vr??PM4?ejX?跪VejX?:th!?6䶖?V^ӠzyjLz/?z?ڗٿz?t?)Cڗٿ)CA'eHпQL?H?Q5b{bv_{q;׆i:6#?ev¿:6#?փFy?-r2ev¿-r2Z ? XH?8?=x`X" {Q~ﱿLI)?KMI)?O TY?B%yz޳KB%yz޳K:?!!s[?ްT1-4봿M͓IŠ?Mm_?v3C6_.dr?6A9?r?/hпHP?6A9?HP?2T:L|!Eߢ?1s?i02_Sܝ*׿i0X?7hX?i0X?~Ck?s1Ac$/?7hX?s1Ac$/?)࿷؜լ?/Dn[v?-&c?%{hٽs*бmu[0?})A>?nu[0?4kL܋u?u(})A>?u(R¿o]?D!Ru?.s^?`b󗃋hh%к?_ɛ ?h%к?ڶj?nD_ɛ ?nDny$+A$?eUR~??Wpn?Wt?4͛{See?N4?e5b? ~:?wo? ~:??7@AU=q`wo?AU=q`~f5@P2^&:?{yry1?mK?7V?Or?>?Or? B@)>?)?[ ?e%Ì?8flo/lR`?[vhv?/lR`?#|l?#o1̿[vhv?$o1̿_KÿBh?ٶ Qej8E?i[X!I?_ i:?}?i:?8!#?}  ˿}?}  ˿tR'`'lTU?.&?3`?olY>?8ޡ?-?C\۲?#2!? ?4 V~Rʿ4n0@]6 V~Rʿ]6迦7 ?n9?9o?yi?<9?O!R?O?KS??ԃ+U?)M{ޤLbVnտ{ޤLUL?'?bVnտ'?Jd?- ]?yͬN?qwr ?G?+ǿ忔CW;   οCW;$ʎ?'c1?   ο&c1?PG?z8?5]?~6,iS]s?ՠlտ"Iek򖿓lտk,5̿{{ Ӂ?!Iek{{ Ӂ?wV;?9a ^?iII%!Gu?z捲B[տ>jxwB[տDưɿ?>jxw?{\ߺXoݢ?Oʿ)sH?01?lvO}R`FdP?c7#?MȾ,ҦMȾ,XgY?ҦgY?ɞj?R?"xfPV.H ?SNhT?yGRt RyGRHҸxοa1E?t Ra1E?&@?_cFO?zKxkWmzZEҭ0+m?(I}{`v ٿ"5U`v ٿؿ&A?"5U&A?P|tbs?KɈhokxy;꼇{p?A2[Xe ٿ Y ٿ2KտOBOO?YNBOO?mlM?j~Ÿ?JVI?? 7<ԫlִ<?VRoL?4e$ (0d?Hܢ*畤k?Hܢ\SS\F@]/e?)畤k?\/e?='俹{ߘ?^p ?g a8ꉿ~2_?s\? j?s\?zW @gA(? j?fA(?Q˿_?+U2??; hw?io?\}f?͋?\}f?=¯X?R@́?͋?R@́?ض /??Tv?0$넿/3o?5ReR!?;ooQ&=ÿ X?uai? X? zi?XĦH?uai?XĦH?KYB ƿRsƢ?3?e{j?9Qqk*hw jĿ#܀??#܀?29?هaLs??هaLs?PY`Zr¿ ϊI?NMs-J?J@Vw?N?ת~@-WƉ?#D?Dwj?\6 }?j?E>\6 }?>?q? #96?NM|E)g=?kq?2#*D?m8D2?*D?NML*v|pn8D2?|p,AW?s!iX?q^ vT?jh?ͺe@~?Ɲ'jť,թAS?ĥ,թew_|%!AS?`|%!bW躄oP?]S?!c?ߢi~?0´L]w*,;?L]wA3j" j*,;?i" j(zMa+?W)9V?6b$?nX?Lj?? :S?I?vޟ?Vjу.ؿU80.LN-|U80.L^ӆ#YV=׿N-|$YV=׿ *k?kU[%?j,= K?@*:?]x`ĿTSTS@Lj4:&Կ4:&Կ ?R~Ѣ?w,F+[d?xU!?j,s'฿9Y ҿ7y9Y ҿtXWԿQqik7yRqik\?5W͢?:ƻ0 xk?[T?S*v> ҿygIh9O> ҿ܎jտtKygIh9OtKIUX{?pF𪞢?rQC?sE?]l.}˼ \ ?I$藒?,y?8 .ʀ?ce¿Vx?ce¿RD&쿅Vx?D&y\MM?1پ?Fǀrsh?S`=Su5x?>3ÿOr?>3ÿ~TVbvm27Or?wm27俪C,,ۿȌӡ?Y%hfIvp| Q^g? ?)٦u\g% ?)٦u\g\Uqտy% ?yj?Fįӡ?[L(cQ`?7+?퉵fgu?퉵"տ_z+afgu?_z+a S֙?r q֡?MuM˧Ψ?U?Fz?WϿ6ǥƓB?/t?{Bٿ0?BEd?0?TQ-r{5BEd?{5un?"(?ž\%g~?2Tk?p? n@hfV5_}^h4Bv?9Bv?F0~Fj^?9Dj^?Rʱ?rÆa?ko?p3?Z@iH> ? ۖ?,?[Cb1у>?.,޿O!?.,޿eiؿέGR޿O!?έGR޿[DKȃ Ųv?}$ 4?~KZt?YTǝI)?hY ?hYmg6Ԟؿ!FcͿ ?!FcͿ]c{qǢ?Fj?=Nsl?fuXʮSܱX~6_?=H_W~6_?!Rdhpdy_n?=H_ody_n?d¿Ƣ?WQL?Za_m?8gk/吿k5ӱFv ?kY Fv ?;v,re?kY ,re?f<OH?DkO s?U?"ƿpfT) #w0?Bp. ?賿3?2U< (?z ?,D]z ?shOcݿ,D]OcݿV,ߣ?C(L=}?nc?=䪼)GԊU6C}ܿ)GԊU6SP4bPῆC}ܿbP84.a?.Sy_F5X?8Rv8 rTY}\Bک$OgJ?Cک$SOǖrſOgJ?ǖrſ ο x?_z.?~@?'Fm;0?]eLQI?@[Cp!DF?;|jѿf]ÿWd/?f]ÿ 'f?b]?Wd/?b]?t)?=̽?(poSǕ?J?VĖ?4.tL"?OL@0Hu?dQb |?0Hu?qLlf?#~njٿdQb |?#~njٿsU?Y ?9' 57i?ÿ?N{>?8G?M{>?`.i3?":ÿ8G?":ÿ@z??Z?y)ΟZ蜿hfa?)?i(Ͽ<>ZdԮ?i(ϿG p?nܲ?<>ZdԮ?nܲ?p~?)|?X("כ]?oЍ?cbWͿ0İ?cbWͿ/?mGz?0İ?mGz?ݎ+Ͽ?eţ?ȸL;1w[? >w[?go`o^ Ϳgo`o+za?^ Ϳza?_.@\?/}?ueP?&U՘?Qo@&yV?gRӿп$ s?пdɭӿ6C2c?$ s?5C2c?Mu8:T˝?ٛ?˨?Wz^W?B-ԿF TѿT`?F Tѿ1ӿz>?T`?z>?HJ vUE?fq d/ݮ?;9Ȧf䁭MQ'T}i3\ܿoj0?\ܿbW+* ?pj0?* ?HJ??*MtJOflxp6 s䂷:&r?ȓoN?Nr IqJ??IqJ?Ƕ`?s.q?Ħ@,<+Cr?ԜA5O?BQW濵XFxտ@\e?XFxտjI߿bv'a?@\e?bv'a?0?DtXz6.n?T[Q}?Wj!翲BԿiQ!?BԿ7za6̕^Uۼ?iQ!?͕^Uۼ? Aְl`?r۳꼿RqάU>!?tt.&Ģ?^9:%D;??MZG?XQ8Dɺ׿XQ`ñ8h??8Dɺ׿8h??`{?Nע?O!ϖU+ ?eAW?'Ϳ-\}>'Ϳ{B'ۿOؑLP?-\}>NؑLP?T?#{FӢ?,w^\NRO݂?AU̿Ao>U̿Pc&Ah&ܿ&?Ao>&?N?{n4_?qr!vxҒ]X`ÿݙZ$9?bu?^ljaմlZ ȸ&D?`CtӿCrbW?`CtӿX}LǿTQ?CrbW?TQ?yؖ{?]gm p?wR(F|P?e0*ȿD?e0*ȿ x#x^|?D?x^|?XBbܿ;eK?Qaƒ]D0f?dPU&ͫdPUVn#пz L&ͫz L MN?HOqQ?a}タ}zjqyj7vh{?Oׯ1駿d/gY4oOׯ1駿}uѿ=wkd/gY4o=wkP1?r6k?lvu chdz-*N?Hr5à1&-0B?lA䩶?[e9û0ȿlA䩶?/Y!zؿ\e9û0ȿY!zؿ-@;,/?/vXI` 5Y\d(g;+?V~3?x,݈>?V~3?r3\eݿ6 ;;Kſx,݈>?6 ;;Kſi֮cٿ3j?,$DFPu]M-ƚ_NG?OOg?L?jǺf?L?~JɿcO.jǺf?cO.L?Tj| ?Qj`(CiH?[ˊ?~lf?oUo?~lf?7Mʿ0vToUo?1vT`#qCխ?0[?DBƿӒv¿"(r $Nb?HI7붿ګt }ȬI|쿰[!)?}ȬI|yJPp?Cm?[!)?Cm?-!xۨF&?Ab7^V~$Ppxݎn#4P3˿5P_/,ܿ㳧O?3˿㳧O?MP9\^hJˡ?(A n ?^Y>\ ]l;?*e ?#JSȿ ?D)һÿ8S?#JSȿ8S?}w\9? ҡ?iyOژ? VQ"ꖄ?gwd!Vi?ƹ6ȿd!Vi?*pſ gB?ƹ6ȿ gB?Xߨ&?AMqԢ?z4dr?{kATE >Bˢ?KPT? N9U  Ʈ?puM?nX”ϿpuM?L֖?}C?nX”Ͽ}C?d>oNt|Ѣ?DMjp?wrMגrδ̒=?qp?:3]Aʿqp?oCq!??:3]Aʿ?rѿZZ*?u23P?nxj?-ؼxWO6Lſ Ӈ?СbʿӇ?c>jk?&p?Сbʿ&p?mvoؒ?}-ڢ?0j.t?g^c?÷KuxPVJ0ſ^Bj?>uɿ^Bj?C,?R?>uɿR?|?WZ? \ҝ'?]C"1osmrU?xXnkOx?Iuĥr4?wD^p;Ϳv#6?^p;ͿV-"^U?F%?v#6?F%?X)ſ"ɲ?y׵;E7 u#TjɫX?詽W?ö"R;NÞIЄ(?Q?s+V?G?r+V?HE?q5ad?G?q5ad?Ac]ٿ׹,?eqoXd9OvGxٚ?dz$?*Rh\?ez$?˼OG?m>?*Rh\?m>?`(Z?eM^̯4}bq>w?VK|B?VKs]?U弳?|B?U弳?hf $Q?P\N!W˄a܏BpT?N _r/+ W?N _r?>? ?/+ W? ?& Պ_?@|?fq=T?wZ?o;6?i$v? X?a.?iA [?$jҿ [?8;P \GS?$jҿ \GS?\pd?n?Jؔ~?Mӌ?Huu>?ۇkE?ԓmZCjE?${ rr||e?ՓmZCrr||e?Im??F5١?$7N fRǒSj?X[9,ٿ6ˌ'} ?6ˌ'V9< `S≘?} ?S≘?"̴?Pۡ?Mbї!D>@?)lۿbrUk?br(ǣD1̗?Uk?D1̗?aT=M?J?A?B6͈)3?sPys}e%;z?eBo2 @U}L뿐 tҿT}L뿛tV[[ҿ tҿ[[ҿoU?EĬ? mh첿QڋӘ̋?zaV?86|Ryſ76Wv\Lrkeſ}Ryſrkeſ옳?Glվ? O<븣Rnu"khd{K?AB+?i5?hĿ}r-H?i5?hĿb:''I{#?}r-H?''I{#?*?Y.0à?d*4yƣ_\|BS:}?!`s ?񥙻ſJ?񥙻ſ2WU¿~=?J?~=?Oc?M5?љ:i5_/???!\i֠U|?*?p=?ɕ ?p=?P?-?ɕ ?-?+VPQ?\U綣?V?"yגlYs?sD8?hC??gC?eĿT&??T&??d?:{?2?_fb[?8If!?i"*-{m-*?i"*n,?f࿈9?-{m-*?9?0dnuӿs`m? BÔ?uH#?!r?;j}ж?nq`m7ax ?nq`m76r_ڿt?bx ?t?_ҿ|: ?1z?n??64y)' wT\@ư?:c:ƵۿPihc:Ƶۿ]'?$?Pih꿆$?~hq@dӘ B?2\ 1*?́$ k׿H~ѿ$ k׿"?g]?H~ѿg]??xLi ?ֲՁ?k2[?( ]M?E/wݿU#(JͿZ?U#(JͿҿO!y?Z?O!y?=V$r{{?Խց?pD?yMf3?KD|Aݿ4Ncʿ^!+?4NcʿI.RϿqȊ .?^!+?qȊ .?Y?㧿F0K7?>(Kl?G_`?d?۩ϿS4^g򢿝Ĕ?龊cfg?G( ?cfg?ů.m?.?G( ?.?v3?Iϣ?be,ǡ۠|`?2cr; IO??6?r; IO??m??6?m? ]q)?܌?_Dh]A(:[{?̜ 2㿱#`˿s`D;#`˿&&q?1 ?t`D;1 ?x6?W;?oj}F'HwAv?m[CȿCȿY^???7?jRμ?Z?+ ÿ,Oʲ?{1?#91kr9ʚœ?]Yn@% +kSϿr2% +kSϿM;S?'0ɿr2'0ɿ@=?Z~K~o?ՠyMItžO?0?+vh翲+?vVvhvVJhD??h_]DD'ށ?}PK{?d"'1ȿd"'s?*$ ٢?1ȿ*$ ٢?a'R?{?Ŀu'ϖlCڣ{?cH ?Lzy4{C}ȿLzy4򽶨^?_?{C}ȿ_?ea$?6I@?oSk"FIrDZ?bW8arԙۊfۖ{?E,ҿHj?E,ҿ|N,Y{?8zHj?8zpx뿹烫Ɵ? HXICدP%;? 2WLÿs,?2WLÿ3 ?Y'd5?s,?Y'd5?&7ɿTToP?bS7NJm7Y?U?ȷ"_٪?U?73`?#Y?ȷ"_٪?#Y?My??mHZ?<^B6ٚM͏vX z?F*iHy&c?6S?F*iHy&c?O3P?ۍ??6S?ۍ??ӧ[?tU?`S? ?Ud?4G пTd?G?Δ?IڎF?4G пIڎF?? 5?OD?6? ޥ>x~!?G[p?K,? zпK,?6?RV )? zпRV )?*e? ՞C??P0\f?4=?KD?\n?msɰ!?db?* Q?`?xi+>&?֓K9>#~u{#\¿$~u{ ]΃?#\¿΃?%tJr?oO?ƂI IAſkݕAſs""ܿ k?kݕ k?"#~ڑڿ.G(?d݊ kۄ?Rܜ,jrɎϿv1⤘?v1=G?&N6?⤘?&N6?ne5ĿJyb!?,'8^/A? W҆1^ʿP3 Eq[~?P3$)U?Cg7r? Eq[~?Cg7r?FAYpkÿNzʼ?R^?W픞?*} ?(y?fgnۘ?rzK}?% Ц7?nؖdT?mؖ.nKE~?dT?E~?˩?Vŝ? @bv?sݘÎ?CЦu?!0?<O˿K#j?<O˿P~ĿSHXvƱ?J#j?SHXvƱ?m2Y?N!1.?$ߎgƕL$b@ճ8?JLƿ+?JLƿCRɓ?pk w?,?pk w?'%L]d4y&?`ΘM[ QY4CQz"?5gƿt#i?5gƿ؆0?ܦ~j?s#i?ܦ~j?~a+VnM?pPR}?Y3Ī?r?f ?AL!N>C,㒫F~?3M ~ Sþ~ ĚӍ?9Sþ9>?4$&??jSģ=-5?ؑZIZ<[?Z<U]?) 3֐[?) 3֐@?8e c?#Deҋ-?Hl޿oi̿Z7?oi̿,Vɟ~?zw?Z7?zw?ő#?[Je?`ʡQth'2ŋ?Ҧ#࿝)οͥ?)οX0?aH寝?ͥ?aH寝?yp9?|8B?>`+2?3:?\3P'3}?@Z?|&\BG|29oſ?Fŧ?Lf L?Fŧ?m[3?nN޿Lf L?nN޿l6o1?)q F@3:!v?T!<>?ՖB?N{?<]c?N{?*%z?J\C¿<]c?J\C¿C c߅ؿ JN?Xs &u?U?hٸ?OR-?BV?OR-?@Ny1?{KÿBV?{KÿcDڿ§HM?߿?eb?)!*i?2A{?A4?#I"A?>C?@}/޿Mtdk}Mtd")Nb?ك貿k}ك貿!^ hGѢ?%?J?x"Vx?i5\ Eeڦ0\ E迅F?/οeڦ0񿛨/οQw!2 ?9B캻k'?fV,|>?Wſ!]^jkQĿ!]^j:d?dyɿkQĿdyɿyf :m:K ?E#p<Ռ?tJa?1PZQɿ=zſ=z]#?>/ɿzſ>/ɿj%|y=oF?& íWҬ?6Is[ﶂS? \J︿]eཎ?>"x>,?BK ,? Q?ޔw%BK ޔw%⿺jb3Jӡ?Y+|nm: ;?GjŔV?n %On?LUMPѿ %On?.r?d֕G:տLUMPѿc֕G:տQ){fW/0?U<{?ϖ;X?`?(( Gkfٺѿ((o.<?yPMƿ GkfٺѿyPMƿf~ӿ0]8?Q5!?^?*\B|?AvVPM)ѿAvVP1 ĉ?NvQyĿM)ѿOvQyĿJ>Կ;?dq+q?!F?DaPwrcL ?E/e?e}7vw? @"s?w @-Xs俦7z-Xs-?S#?7zS#?(wM2@=XꟌ?7[c߮?Cs |?PK#*QV?eVךݿ~%;eVךݿ 9"?+Qu?~%;+Qu?cV @e5G?њ?sKw?Žք༕`‡xkHſ`‡3 ?a7?xkHſa7?n?rM?ޛ?C34t?Bg-M*?]7A~;¿zMNǿ\7A~;¿9+?>wp&r?zMNǿ=wp&r?a&A?-%?#}^LlpP?ͼԗ_',9c?[Gc>k1?$ ݋ ڌ濊V?IQ ?V?c5x:?^*K-y?IQ ?_*K-y?XzzL?y6?v^♿oίre?Ǩ֪Z9/翪k䦲?@ ?k䦲?֠yG?WVn?@ ?WVn?ZH?9ǡ?lXO0m=?ebǂ6lѿ6[?h ?6[?3F?(?h ?(?^A|O?rʡ?XY.5#?Ҷc2~$mK+'ӿiZ2?UnW?jZ2?F?{?^?UnW?{?^?mU?RF/-?ZB?HU?Ss,OH?YKx?M)r&?A-[ݤEʱvƿuxBNuxBVb?_ih ?N_ih ?&kWH_O?~-;r??R?Lu\|ſ$޿|8翦$޿: H?Acٱ?|8Acٱ?55LJ?!m5u?TҤM?+*5i{?X3E*?CAmzto?4s3yto?zE"?`4s3`_ ճ?2t?]T?men:?Bʯ}?,Yo?Psr?N gF࿄wХIWB_( ?wХI8\?D"?WB_( ?D"?L'?)VN??ǼÒY|?ޛ=o?b[X#YܿZ=7"֯?Z=7"ƜxO?+&z?֯?+&z?4?5M?ە?YϨK?L}r?s0v;N??>ÿػM睘??>ÿ?ǍƄ?ػM睘?ǍƄ?;U}?Y?ەhʕ?ބ?y?^nû?gEgaĿYg?fEgaĿGֲjm9?:⅘?Yg?:⅘?6? qr\?^,ұ;8ͣ"s?9OK?ZulWmr1pq?xLտ-r4?էm܊?,r4?.Bu{濢`| ɿէm܊?`| ɿ.orި;=?5Ё?6~R)g7?"{C˿ٺ?xе:?ٺ?0)$:?DbpIxе:?DbpI&Sſk'P?&!?ZdW?ROH|?QC[hH?uRhH?n˰3?6uR6lM?eKeE?E&$0? b?yͼ-{gz1k 툤-{gz1kPz>?6hg 툤6hgt6SFޠ$\m?o?`Tg\Z?%qNǿ0Y?Ystú0Y?y+,?%QYstú%Q9ݭV?8ܨ?)J26?<[ ?DDXN?>e]?@ ȿ>e]?+RvW K?4Κ?~t?woQ㚿FV8?nM ?pō? 뭿pō?E4@W5d ֿ 뭿V5d ֿ4c"?m@?bP ?ܩiCk?9)+#?l6[?u\|\k6[?ߟ!P?}}*ſv\|\}}*ſ,G_Yř?.hܹ?0p?.B?bd8?J)}?NכJ)}?\vr%?ڿ7ĿNכڿ7Ŀ^i/cݯ&$5?xN@t?=|L;"?6N.?u6?U_?ʀz?k;AI%Ϳn?I%ͿCz`#_¿-pv?n?-pv?w?]?i]ޥ?Zۑ?=K%? ŕ]2xl5? X ?xl5?(xzT?G%l? X ?G%l?hT%D3~Pۢ?ꉟae?C.c?@?Jl"пK? ΌK?q?ƨt>̿ ΌŨt>̿O79yͿfXբ?CV'KY`?G?–?oXпtao?& 7\tao?ř?[MF˿& 7\[MF˿kZA1GϿ:+?y%^R곿Xڄ۠?D???reX 1Xcƒ,m?kIqxB3yB35=>Y(s?(s?X>̿S*ۆV?*Og DW]?^TCq?GdL㿸ԢͿt؏KԢͿ/613VΜt؏K3VΜ*Rh?C^ʒ|C ?7A?3wӿ5phT.&7Fʿ5phTO*|ӿ-&7Fʿ)|ӿ]?׿?%qȑlj)A?4XXw?|'ӿWے.URʿWے^4kYҿ.URʿ^4kYҿ "׿S?"?I?# Skʿ?I5鏿jvSVpr[ Z^ԥ@(R>?9Dl)R>?8xw޿(vrX?9Dl(vrX?._ ħJ9?ſˣ=u?NS$VX|@"z s?,ٿz s?!χ;K{XK#Կ,ٿXK#Կ?hx6Jj?Q{sg?ꠣM!8J8cF?p,?,dsʿo,?{]H?eӿ,dsʿeӿfQEi&ѿH}E¡?Ҍ!h`?iOYJ/1XC?$ze?Mɿ$ze?LX?~eVӿMɿ~eVӿW+ѿQLҧ"?9>ėJbOS?~B_e>)?4ߔϢ?%?sao?nJ? |nJ?q?|gʫ? ||gʫ?C#?R)?,ɢ?-44i?t=Χ??c"g{@c*Ü?;M?"g{;M?R;$j?>{/? U?kقɕ?hBS"`oд+NοjG/?+Nο?%DjG/?%D,u ބ?pQɠ?Hz^[z^? uCͿ1=ڋ?ԻJkI>^[z^?ԻJkI/aN?=;?hTW: }1Y&p?)T?{h#~5&j\?K#✑?˲_ؿ3rB{"~=?3rB{ᅯgqɑu ?"~=?pɑu ?6? Q?@|eyg?=@?aݫ{Ϳy}⿤77U?y}| տul?77U?ul?Te?vVš?d 1|n}WaVw?%FQG@;?G@`>J/, Tf?;?, Tf?b ?R<.ʡ?g D32X@v?3`᣿$Gh}LRC?$GhS9R__B?~LRC?_B?'Հt?}wɮ?aQIEqR iK>3i10?T勵/p.k/z+n엿\?1E@?sJW`bU?1E@?tdush?sJW`bU?ush?!MmqZ?(tl䅐1teI9?Jlݘ?Ɉ?Jlݘ?FmP3?Ɉ?FmP3?-࿃|佡?Z]^ct?Y*k?N“{?iRO?>UGM?Ie?=UGM?J|rͿ);?Ie?);?rG;S?1A?Ro )r?n}q? |L7y?FHB?џ,%?(N6nқ\?szT?nқ\?t3I˿SGl?szT?TGl?X3s?Ί}q?EG~?YZ64?=Lh]1?O#)6?%o?6?d.̿%mG(?%o?%mG(?]'?jz?g[SW i ?2"DJ-?ƶiFޡ?RqI k镃w??{Կ>Nsۿ})?>Nsۿ5o?LN?})?LN?Nn?Ɛ±? |??lJ>h?.)?9]?eMo욼+>ƣ?dMo욼3]?Q릿+>ƣ?Q릿ԧ~N?rCt?Ca?,??;a$?;˿ƇR?;˿0zƿƇR?ƿWӽ+?qr?)W8?"UΠ? Ea ?y F,?勁ʿNS /?勁ʿL,eL7ĿNS /?L7ĿW%-a ?ԒX?|斞e?o~y?Adj??&a?, L?<ꦹ?[:ӕa?}ⷉ?&2?}ⷉ?p?%'2?%5y?,{ӫޢ?]?Kv?Svh?42P?(D?[eϹ(D?ĨT?Nsqۿ\eϹNsqۿG2}?$,?ʲ[?AStl?~JKXY?L Mÿ.hMMÿ!ْſb}Ͽ.hMa}Ͽ&ٷCNʧ?;oc?E?*= ?Z2J¿wS¿WgSƿߔK+οwSߔK+ο2=vtc?ƒ3z?:(2hҼ?q\؋?}p)I?oa?Lg?2Z?vcݿ@_⿔.2D@_Ti6B?4yl?.2D4yl?+7?Jv%?g܍h?C1?H+?|M࿝vU ׿ c𿝠vU ׿SIEEѿ_ÊpO? c_ÊpO?߹?թLgK?Kxv?&K?>]?D pƿ9oL ~49oLեARdͿx¿ ~4x¿#uhD?*h2~??}؊?Bu3ǿݟ ,2cݟ cLοt,2ctm{ۿQD?&PhE^?.踿y5?5wXM>?/LX-?dO}!wbD$ݿ5bD$ݿ1v0 ?5w0 ?zXuA?գmv!r?O4sHa;?1lNпo|H?1lNп87Mmu,L?o|H?u,L?Z`ɉp~=_?Urw4V'A?_a!?UM:zxϢp'Aև?Тp'A=[ƿ;Kqև?;KqzFƿ<ސW?U=E-WH.PU?+6?kI F̚Ρ0?F̚G~b^ɿ7ὩΡ0?6Ὡ*ĿR婈?X?OEʴ\?yb<y?9rۭ?QgV?a\?,`|1@1)MA? lצ?1)MA?^J?:m? lצ?:m?چI{?&N?1ڜ?a.*nj?'?G#Mvh?F#"!^?[ݻ_9Mvh?[ݻ_9G/ELQ`?Jr&,?1oa+?%@W?x bo|,[0#XBKs+[0# >9?&BǿXBKs&BǿÚeοLUqe?_Z)P?ۗ?iZm?\h瀿c̘ȳc̘iw?_\bzƿȳ_\bzƿ{6nп3~Ā?N.aAv ? ͼ?ѱ1?^c V?״q#g5?}nϱ?e͞? 񏰿?P7Iڿ͞??P7Iڿ]Z5?f'?mxEc ;i?˦?XvMF<@5 ?(?<@RODʿ@J'5 ?(?@J'9?r#zp?wW7m?XS2?d쩓w?fmۿTRٓw/K¿TRٓwvۡȠ߲Kӿ/K¿ɠ߲KӿV9[Ϳ%-nk?lg?bH?GgRy?t`ۿR1óxR1졙>A5 ҿóx>A5 ҿ=O$Ͽ8tj\?Ϊ?o:?hA?#S$?=ݶv}?qg?9ٜ?[ݛ?=&?PI=&?}pU=?PIyr?rC?e;e\?z"ITj? Zb?m`I?_Ͼt?UDB_Ͼt? 4?F{5DUDBG{5D16P5?0'?D EdSՈ?GxIJNgͿ4@+Ϳ4@]sǿ5H8ֿ+Ϳ5H8ֿX%̿VR?=$7?ixv6ȿ쿦]5Ϳ쿦]ƿgGBֿ5ͿgGBֿB(̿t~?4tq?p}?$Ŀ$b.?8|D̆yP?=E!宝ܿ 宝ܿ[ہ?]w ]w1e%|Мu\?Q?-kc᯲?ˇ&Á)q;dw?-;dw1Yt?+F?-翀+FÒ `?㟃ݩc?ZK/K??Q)ÿ?X\9ƿnѿQ)ÿnѿ^I<ĿS%b?0(E@^f?0,l,2?]B?oc+on¿]B?a 9.|ǿ3*ѿoc+on¿2*ѿӽec ſT1m3?u+ ?j{u.tN$1??7MAج&INދ+Sxj:?Nދ+6OFc5/2K_8Sxj:?/2K_8td;ev]?9?VCӳ=M50[zx*濝T\?zx*&!f;;3⿝T\?;;3lwjҿN??{j SA{?@-u4g$@¿U<@Ϳg2 ?U<@ͿCC-g2 ?-VmL&?BB&?9({h60py?W(EmiL$b?!ژ5zV ?`.?5Lz,S?-\ѿ6Lz,S? mw?? /ҿ-\ѿ /ҿ!x?V`3?9⨿.ddony?H?- ?7>m?- ?*?ޚ滿7>m?ޚ滿pT ?Gj?kbU24jZxbomj?^p?}eXA.?}eo-?BN;ˠXA.?BN;ˠ ?饡?\c0BKH y&mJ?Yd{?s]ijaq?s]ij棩F?,9ќaq?-9ќH?l?F㓦 #H>AɫOܜߠ?eͿӊ2 ?~>ͿX/@ܰK v"?vq)y#LI Z3HЇ:M~?ܠrWʿ ?ܠrWʿ ( ?xEx+ǿ ?xEx+ǿDoZQ} ?Hl?P{Y=24{?T/ ?h̔:.C?h̔i⃿~9.C?~b-?z8\ ? dh?&+z' {?c@(뻙?>$q?>c@࿽q? {uC?G?dLi.Ȝk0? #t.̂z??ߩ¿t.̂z?6BC\I ??ߩ¿BC\I ?1s4Ey?h9K?C@$7Q<[?Px|0O?.* 7?/O?ܿmj-".* 7?mj-"ID% ?EbKԜ?8OjA?) ÿ,F0?KGq>?,F0?ǀs ߿bXo;וKGq>?bXo;וW }' ? !G\o׶?/|?N!Z?,s2d?L+?&Չ2ֿM:ItpE3"M:IYZj&OtpE3"&O#,#?'1ԣ? oMCiI?N?9`^ƿ(K꿹V(KA:yD+迹VyD+迗 ?&6?+Cpco?Oc?>Gf'IxTȿ9|>q/IxTȿ`z⼵˿9|>q/㼵˿HYlɥ?yO1?^>m9M}?/ڡC? { -*ǿ?ŭ!-*ǿ,NKzoʿ?ŭKzoʿ/;&?@?4i& ¿? ?N(b?ȏF^?J4k?fr`'yq?er`4oZ'yq?nZYۏE@Ap.S=?Hv?Ordkip?%Zg ?~&)K?~&RII{8)K?I{8c=o?{$?3Hh?T*# ?X!?ieGg? "j}4r?!3?ǿ&}/0?ǿaBJtQQƿ&}/0?tQQƿKĘ۪?x?I{kӸ?.l*oF! W?)K.?oH␯Ԡ? z/ڲ*k?wD~9*?mZ激9*? d:mZ濨:JH^?^*:?U0R?| 0ا̋?>9-cC?zcC?QlRVa׿{׿Tzl&?bb9Y?TIh~?#ΒkΈ5[?h !Yw?%b?!Yw?7$iۿ?&b??N٘忿;R?(Ez?^d(t\p?]/ y?tPͫ0? y?l5/ܿv7Ѕe(?sPͫ0?w7Ѕe(?<ﭽ_O>?AY|"W?/{Y,ң?QGUw R?E"p?W-F?>F/*?W-F?._??=F/*??%8e俐ԣ?dKLŒ͡ʬ<˹?Kl ?#%?7P2?#%?C?vqn?7P2?vqn?yo+ ⿃Q{?mWJ?dlz/Р?k&;[H|1S?" ^?|1S?l) 5ڿE/?" ^?E/?CѾҿ Dul?e'D@|!`\?29XBųqز?} l?qز?cֿdqC?} l?dqC?i?ISѿbdC?7%?3;Eu`pG9?],VA?Ût~?-dIps?o]?jLvΰg?B_:0kLB_:0K`E k?z1-?v&ޔ?~UEA?'O?/翿@˿/ qFӿ(: ?@˿': ?ҼQD.?+?CEX#U(?VGp?&.ulɿLʏ}ʿlɿtDٿߘLʏ}ʿߘ!I)(o?3?췃?r?er?$KȶLXLȿqNʿLXLȿ:w׿.3+RrNʿ/3+Rr㿚(gp?WVB ?jJCe-tk؉[+G?m￸[+G?Zb*z?m*z?~F,z?%d&TI0O酿O"GӨ7ʮowI?C'aʮowI?.XaϿ΢쿕C'a忴΢쿙]',hϙԣ?H1*{ `g?e.Պ;|0ܖԿEN~70ӿFN~V^a+yпLʿ70ӿLʿ~CֿR4Oͣ?@C RzgdyoZe?(m#ˉ=Կ绍ֲr0[ӿ绍G5ݨϿ)y8}ɿֲr0[ӿ)y8}ɿ@׿ 4L?E%U?쇦 ?3h*Wƿ[Xj? Һn?7C?Gڷ۷n0?_ se5?ֿ&-` s&-roh?}h?7d/ ,Wr_\?N/(Da?X?) Կi X?) ԿIU\i 濡\qX;oOX\? JI`vSN?%DsG_uX?[C߅ǿuX??yGBҿ[C߅ǿ?yGBҿ!j4ſ{W?C\U?s̄T`Si?V7CƿKu ?Kb'ѿC`a?vҊUQ+Qb^u?`Wx?$?`Wx?s?R.ê?$?R.ê?YƢ ?C f~!lrS ɸ?ʕ㡪?BYն?ʕ㡪?{5 >?BYն? >?&s)SI}M?6[ϼq?1v?롿ger?!3U?y?!3U?tlE~ ̇ƉĿy? ̇ƉĿ{螴?L [J?d^ww?ض}^?q?}^?kwiп/V#?q?/V#?W Y??fz]3E?^ ?Tp?ȓn?,G?ȓn?!b*Ͼ?#?,G?#? B? x?! ৃUdc?ȇ u?4aYN?tȝŎ?vU߮?tȝŎ?ǎڰ?vͭvU߮?vͭ>pgz?TK?4ɂ`1j?)|Dv?xRE?+?V?+?0s|?BL{ V?CL{ J\N? =t?%}{y?ű*3Ec3?4+v~?2F I? ]ٿ4AC ]ٿjԿ3AC Կa 6ƈ")?Lz-^\?}uƸ%,\?jZ;ٿ޿jZ;ٿuQ0Gп޿0GпKNxUZ?^ƤYzRk?˔3???xCfƿWh?{ ?xCfƿ 0h?!jWh?{ ?!jDT?ɫ:?"%{Np?Ȇ<}D?L?íbeEĿQ`i:?íbeEĿ5Ś?z%OfQ`i:?z%OfU$ ?Gb;?Y?K/$<ǿve糿ҪWR?@\?:BuS70({Wi%{}(]GU?}(tT ?c]GU?cq /?S?l1ܯŸU[wg?|exUdʿ.k⿖uSs?.k⿼?_xuSs?_x򿕓A/5)/??*V3ŒՁ"p[v?7.c?uSĿՑ?uSĿXQ ?1PпՑ?1PпkT1~PY2?~?xD%qv?y6?&ދ|ĿA%?&ދ|Ŀd;aZ? <$ϿA%? <$Ͽ&*1즿1?qp!$!&7^쿢_?п_?I.9?Z?пZ?O?p?贻$غ}Ȧ #?rɁ`ҿ^Ƨ^LT)Rc,VO?Ƨc,VO?jM>?Vڢ?_xart1򴷓 X4?X:6п6<9AW6~UJ~AS;9AW~AS G ?$bܞ?jr6a{Aytd?uz+f?39L@q>8P7`ib@'B?Iy?'B?]^@E9H?Iy?E9H?]2@ܚ$+?GSXP2r@Lhwj?q`"Q?Lhwj?ta?I?q`"Q?I?'ҧ\?" s?pTk({˹Tr??6& 8z/@R8?6& 8z/A{I?t ?@R8?s ?ItSPڛ?Em] ?RPڛ?Zt+3?)ɈT?|y|?kڣ?Phnkhx@? צn?RW;I2?RW;s?J2?s?b ?Y7}?Wg?Z +!`,"?aՔjؿ^{=,ƿl?^{=,ƿߙ*x]?l?]?҇@?EI+?Z, 4!c?lrʽ:Pަ?t>$iڷ^$:u4?iڷ^@WS+/ܿ1b?$:u4?2b?` Ŀd-?i|2Y?׆&УÙ?^Z>R/ ?>R/ښ޿U/rH? ?U/rH?`')0ÿDգ?fH0=g7ʿQδ?\%Kƣ?]۴Q5¿,Pu?.5F1?9C?1?3}H{I?9C?H{I?"俛q*^?i9a b?'#|b+?"K??b+?F|n 7 XPE?"K?? XPE?~π꿝 z ?r:\%Fz?g۠2nt?B6??d?]6/ȿ?d?J׺P/p?\6/ȿP/p?[п~b ?ʇʡ/w?M"Π䡖}r?Z"%3?xYt*?)B[{ƿxYt*?mCjea@?*B[{ƿa@?;rqѿʆ?8/ o3?G\,Dy?vg?_"|8?آB󿸅KB?JYS?'tKN?>B'tKN?`^뿛o?›u?\3yۢ4iLWf?mb?Jefomb?,:{B?\?Jefo\?ZB41志X1R?bց? Zw,V(᰿rq?FKڿrq?)<ڝ (_?FKڿڝ (_?s~*пF=N?<}a?K&ԑvrHƾsN/F? wٿF?SISH? wٿSH? aп̋ף?yb@?3"x? blJi?<\+U;?f9&v<{>ᅵFP?OU:N鿜FP?2?Sp?NU:NTp?}7E?~ۣ? l=]4?ngIq?*lDMA6Y?դ9Tn??դ9Tn?-56ɦ?B ??B ?Hbrn??Ng8? p_ ?O.xWD?Ѱ{O:ì݀?{?ì݀?'9Oʮ˃=e{?˃=e.%LU6sD?4_?[,1j ;K*??嶊?K*?Baɿy-X0տ?嶊?x-X0տ5<ؿ"?ӵ?ˍ/ė"?bC xƿLRbò.j?LRbò#2ǿ} 1 6?.j?~ 1 6?yoHk?-_{E[W߉?d+ofS XxÿFSgە/?FSgەHy1 >?/? >?;S9#l4ao2?c:&βK? do?zC?%魜ʞl֝?t? C?I$lqJ |0ה>^f? DZN|0 DZNZ~?[G?6fi1Ʉ?9XJi3{͒.?86zv۔?D$C?86zv۔?Jޑ?q꿺C$C?q꿺X?Oe\?O$8^?nS?{L?{?{L?fMʰ? {? $?Uh?ЛqLuny?uh??FNԠzS?w?a9ѿ7QCm?XB-+jٿ7QCm?}vſ^K\?XB-+jٿ^K\?sF?mE-Ρ?fڛ|PÂ?4R?K(?w gO?W)*̿w gO?(VĿ M'C?X)*̿ M'C?П+?Z?4}i>'?|ĩ?d3? ᷱ? fT\ ᷱ?]"? fT\?lqqBEz:?'h7T,?GEW? 76a ?N `?U'r3鷿N `?\ PD?V'r3鷿PD?ԥ"gL?4-?~iJ?Se?+?zJ?tS ܓ??$˜?>㿫v?):v?3s*@E<հ?):E<հ?H&Q+?D >?$ړ&?޼* ?^3`?F)mؿf?Af?`L? hNn?A hNn?|Q?~|?HMR?5qB?)->}Si-=۠m?89GtĿ۠m?( ?]|?89GtĿ]|?5ɥ  S~?T[ʺ;?^D٘?12>cy ^/z?ԠB3Iſ/z?V?faˁ?ԠB3Iſfaˁ??`&R?}ˆbÄEr?L?T}?lę=O[~?67X?iA? fMV 翙3? fMV 翄dG=@+5࿘3?+5nչj?/D? ҺHRU?|=i?{jy?Ggտa)?Ggտ7OL?Wa)?Wdx/4?[t?7}܄?)P?sTJYƿ ǘ]1i?h`?? ǘ]1i?׆>?UQI_?h`??UQI_?#x '0 k?v f)6?Λ ?0ÿ,%pG'?,%p.w?F?G'?F?C뫈dz?뫈)T?w7?dz?w7?uaÿ߄9?@6l^]W?o ?s&[?[\DſD?[\Dſ2žj?5'AǿD?5'Aǿ!h&?k?k۞} f*u A?_%j?dOIÿĚi?dOIÿ(jX%?&`hɿĚi?&`hɿDclV?#۫ ?xsU٠?$?F?T ړ? q=f?M? f?f[?Q |ۿkby?Q |ۿ_Lp?J-ô?kby?I-ô?C&@̧iʝx?.Dl:O?Hx?鳟?;??l<6}˿ #뷿l<6}˿y?2:y}? #뷿1:y}?va^]@)ƙ?9-~? Ӫpn*TG`?jg?Y7:˿Y70q?h:˿h0E9?B#T?] ?Iwl_RT>?Of?o>spɿo>J)?ՕUspɿՕU^&A>? H5? ?|Q烟YQq_?N<[?*M??Q}40'V?=H9J?76g66`jP?_Jݷ.g_Jݷ. :M\@sr=?Q-g<8?9 pH3SuH?ƿx?#㿟^}ܿ#㿦*33?4<⿟^}ܿ4<4F?}?:)ʗ?-L7Ha7]s!3?l%/;tOrԿl%/\DžXտY^n?\DžXտQgTi.?wHS ?X&YѢdDYty5⌊6;? 6њǿ7;?C}>蕞wϗ? 6њǿ畞wϗ?/VQb??7{U]n_n=g/ ſjb?<˿jb? a$ɿ~B?<˿~B?#"Y¿Hh_P\?W?XZg&ke]ÿ${d?Bձȿ${d?g2rʿYL?BձȿYL?w/?n3櫿.mBJ??r'?ټ8?yԫȅ?cЂ'U?.?_q`?igz_q`?W#@1 &tfGֿhgz &tfGֿㆤrz?3C?`XnuKenG ?y@,пBA?.tQֿBA?-Qο1C1?.tQֿ1C1?#?̣?ZkPB?R$w?hC/ٿ7.w?P`?8.w?. @Ya˿FTǣ?q\@_S?[z?1\P׿o ?&?o ?+ȿ[o?&?[o?C¶rܝai7?"% B@Ϳ540F?rZK򁜾j}s?;Ox}>@?;Ox鿄Pƿ0?}>@?0?m?0k(w?h7"q~yZWh #?Hr<㿘ϕq ?Hr<ʯ鿿;?ϕq ?;?;#򿯋?Y賖"8?xi~"U<[4H,?vp;4_k9?wp;47OI?mA?_k9?mA?2iv*?!}? rU/e?2ۭfjUfh?!h$8?!hCȾ?D?$8?D?r#Zn?p:>?ydL.q)/G)]-㰿Z爯?5펿\}o\ŴfZ$|?^|[$A@!|˿^|[$A@?/V;࿱!|˿/V;쒫? jU ?~kCK+7 ?{@?}pϿ{@?Ҥῑäɿ}pϿäɿd򿜗|n?$$. i?rׯz?V!irQ>?*h?F$U*h?w`e?F)?F$UF)???k+?*A]q?JЊ?DO|? d? TF?F9? TF?,G?̥_?F9?̥_??LOlT?)L?Db"$|? =і?q.? տo6?G&^zhWÆ?eYܘW?'vAl3 o%?Mt'O? o%?y(<ٿᇴEMt'O?⇴E|u}տR?Q{Cy̒?,|?]<}?lRꀴ+q?5в?el)Kι?0&?l)Kι?q9?0&?q9?g >ֿc7?Ě`VaE-V?wga?1y.6 ?AՄ9 ?5 ? 藡 ꄺ?AՄ9 ?藡 ꄺ?hu'⿍| N?9Ћ+24?E7?f3}οV^b?m(?V^b?D?lhöѿ}칿m(?}칿==ͿW~OpPK?OIw⊿w?`X?8%=п7?NJ3?7?{arlrԿדxNJ3?דxA+aA{jο#Ot2?6 5mqe?/~x?3>h?%GͰy_Ȅ5?#"FR?{ݿo?Ă鿒o?P.翟y8?Ă鿠y8?%W?5?Sb[H7| ݤ?!?찒Bؿ1i?:)ӿ2i?H45?:)ӿ45?2ƃ6?fRž.Sǽ?h?\?)ēwf~L?R巻?L?iZ(¿k'(ƿR巻?k'(ƿ[z? g ?|萢g#?S"2~?6M.Yc?@{ A?';?>na¿';?]>~٣W(HĿ>na¿أW(HĿ0#+ ?d4>?LnCx狿$|- GÓ`rK?S'?/L*IS'?|>ӿ /L*I3ֱ?c @f?#iaJ_d-E]"|F?_?#|F?On˞GcfM ޿_?GcfM ޿1O @!d 8?091V?2:~$/_.?sgD?YQuD?YQui]S'$?œD?œPe1? f?ͯi?rb[}Oa ?ws91ts?1tWVu}?2^cs?1^cTx8h?3S?n8ڞ?yP 9[m@W1?V.π?8K簉#Bi 8?'xҿ@e'xҿyʏk?oB X;Yݿ0[Γ}hVZ?0[ΓZAп_e޿}hVZ?_e޿p/m}[B?n @?$v?C=Jh~?|eͯ?U?BY)`?U?z ?+ͺBY)`?+ͺ^+²;,$?u9Xy?9Kv?Q Ȣ|?\b?JXW??JXW?9?r? N? N,\iȥ?+Gm?s?#?_W]?k^%?d Ox?{ ?ѣ3?Ip:?3?-a]ٿS'kIp:?S'k}E5=?u?F_ck?[?GJdm習]^=9?Gо?]^=9?2 ӿyv7ݿGо?yv7ݿԕP"sf?8zfz?Պb ?/{$? ɿ@"?3b\@"?j)=KflS3b\flS)4˿d?{?WZђ?Kl'?& ʿ4rW0?~*4rW0?rϵ~*ϵ>cHοugl_%?cGD'ϕ.t+p#?l׭5}iʒl49u?#SD}@pT؛?p ȿ` ?T؛ῐ` ?W˾?%~h:d>Mo?{uw?2~I.4RԿZڿI.4RԿQmhy?Zڿnhy?T@߿'|)C?qe?H¥x? ɿ:Ɵu/|?] E?>u/|?x㨧DE?Ϸ ?:\aN龜aE??'B0?vH?zwh?vH?}?ohr?zwh?ohr?#3y?? :LxJ?r<}0?H[F?x`?l'?x`?V$gЃ?v28O&i킿>[RґoRKFU*ҿoRK}BJ쿎l$dFU*ҿl$ds.B3|W!?wKA,Z'vaQR0 5_؉_4=Iӿ'\؉'\ 28wU?BS/Oqua<hŗÿ(ԕa9dJz?(ԕakU ?m1?8dJz?m1?$eƿ _NS&?Gi؃ :uߝK7kr~ƿ38\/ y?38\񿿰 ?c#%?1 y?c#%?؈;lƿCH?:'D0F/?=?D#SB0mF1x]p ƿ(>FP*"d?6G?2x]p ƿ6G?P\W?:? ?xXAmw[+xؿg<ҿbFH@"?g<ҿJ5ʱ ??bFH@"??Y:S?EX^P?kJT?SܙD4C쀿s(¿w\[ƹ?w\ ? 84?[ƹ? 84?TF@?,F9[?mD^`?\͌?~vmǿ?oX?]Z` ?x??oX?x?:|&?mwG?TIAkƩ>Ifˢ?˝ ʦGCϝe?9x}??23Wп23W￧$!1x}?п1x}?G@X]p?_ݞ h?j}‚?/?O ݰZrKLPO ݰZ8ēyUڿ=,` ?rKLP=,` ?2ۿE,,tt?)$fvk uz?qI՞R? &BvҏOBvҏUÿb$BOb$B7S j2q?!uu'[(z?/X? Z~|1v/񗮇|1v/񗮇Of/D?3(q_EC.i#JǿjUMt?\MZz^Bʲe ?.҉R7 ?BOQ;d?ķx;=.?Z:iUJӿiKr?UJӿ*F?]Կ;fȿiKr?<fȿyiEK?rS_w?? JL?jZ0'x# F>?x# FqW?D g|?pm?=?z?/iO?Er? 4?Er?@T+_?-?+ue?m?ŷ;?C*&{?u(?~ٚ?l ۿZ.O?bJ̓Z.O?Fٿz߶übJ̓y߶üK?ǁ? Ԍ3?3^?8ЫL?*`ᘹܿb]?.pb]?Ljz ӿ#< 7.p#< 7꿙1 ?5ufR̢?&IA ?'v/T??in|ذxF?a:soxF?Ľ5п_:ƿa:so_:ƿ|aTϹ?$d1Hʢ?tܢ?֌ؗ?q?dU+檤$?g?|+檤$?HϿT Pȿg?|T Pȿۯ1?nȲ!?z?ž?u'Q3KԘ?'=iΈ?WZAOȋz]?} ?$xP1?} ?PP[o5?NJO?*︉?Тȡ?RWZ?S]J:b’:@?՚?o7?՚?$?ۧZ?o7?ۧZ?dcm? Uϡ?=?\ ?ڒw8ʳm>Ɍ?"?g?"?ę ?iyF?g?iyF?ר?Ġ?4Q~)? ?:R7*G*?qԅ?V0c]0V ڿٶ??)W% R?ڶ??]q@nBҿ)W% R?nBҿ~ =a訹|? ٟ?AAi*T2EBK?B+=?K?(G?w&mEԿB+=?w&mEԿFٯQ޿!q??8c#ߴ?4p5Ά6*?yq?Qalu?yq?ϝ?j-}Vw?Qalu?j-}Vw?ۺ5qc/A?"4%Α?n#qtIu?ɸ?~?7?ɸ?8b:?3!|?~?7?3!|? mthF?@I7?")?IPo0?!?O$#Y| [=fNu,`lڿƄWT?iZ?ƄWT?{0 K@)=YiZ?)=Y+P#5 cd?m&}?լq wxPo? ՍNܿup?Z?up?i?8߿Z?8߿ wR/$¢?̾|?Aq?:?J#\ҿH Z?Q{F?H Z?NK)Y?/rR{F?/rPvf&ٿYKP?+r?7s@|?~xH"MMdҿ[ G?o?[ G?-h*$O?mzo?mz51ٿFrb`?OiO;vE}ߢ?BP9ShH?jiz*$?.Ɔ-k? V>˿H"?2*K??W?&T'߿? M@?{6n?p!4? M@q!4?_.W\ V?- 1ɉ.H?I$?Mm˿<@ڿ[s?0m?<@ڿ[s?&Y}?]`? ?0m?]`? ?:a%ҿwg%8?B=.l83?U+r?ەΚ̿ c5x??? c5x? l|y?ۉH???ۉH?Aҿ[. ?ŦiD#p&ĿBPu 9?-~F?|R?uA6/ mϬ?mvm?'mſ(?'mſ@ Ly/?(?Ly/?Y$q0˿\l?PG?)FÒd+9Ǣ?!d?``:ߚC\RϮG?``:ߚ&`C"c?C\RϮG?C"c?vOQ?*͔ ? {B_S?v42:? &I>?щotՠ )? ?gMgw/n]K疩ա?Mg?D[$:򥬢˘?mY?iXmY?:ӻÿ ]࿕iX ]~UH`?)hH"A1?09Ń_?qi5D3C8ݓ?3ol9?@ .3ol9?ev" ?b}D!ꪶ@ .c}D!ꪶ{=!?{4?#'P`^?wrqSBlB>?09(?i(4c09(?ӂ:˳?' Di(4c' D=,?}?xچ$ ˮ?9dt4S^:?靏X?Ns?!KuhCPޫ׿Q5?Pޫ׿b?4Q5?4N%v?C?N$F/a?j0\b-LO_ /LĿ_?6w? /LĿ"JnY?A꿸_?6w?AeYH?Wbz?#Iw 8J2/XC!`SD?F$?`SD?P3X墿iHÿG$?iHÿw[_lM0D?7ph?յ~?>7ph?뫹ѡꚿj^aaAĿյ~?j^aaAĿEc.T? k? ?V3C[Z $?CQ`?`V`?kP?tj?n$){Zҿtj?6j?n$){Zҿj?ʍ\a\L?n* R?F(?;H?y;a?_?caſ_?w`̿yDUÿdaſyDUÿAW#?ȡN?ӻvb? 5`i{ oDpX0R6n`3ٺ? ?a3ٺ?CLϐ^г ?ϐ^г(%ǿȏ?6"d`?&8QTDbtp߲VSN?!4?M,錟?"4?,8}R 鷁)M,錟?鷁); ǿ'W?Ѝ?!8R#?znig/P;kv?yu?)z`h?ia $昅\ci^!lѼ?!>Ѽ? (y@Y?Y?F.?1V?}??|ڀ;L? u#X?Kkr?^f/Ê? V2L0]f/Ê?k"?OY? V2L0OY?-ajY_?!"iY_?nhL?ʿ!"ʿİ?^#?x(C?$v?٨P ?494?i2494?-801?g"j2g"p,&Q:?na"?m?epc ꂷUH_2Mg?zqal®H_2Mg?y]?2#{vyqal®3#{vM}"%?? І?Gv?/;J쑏y?*5쑏y?Nn?v20KЬ*5v20KЬV]}#/?^>sպ? 4?g7K͐???L?`Vֿ=.Þ?Luᬮ? 5ɛ? Y??5ܵ2F˛w?.;#?OkBhZw!c?h,R㲊?Zw!c?㲊?.} ;v6?C6Xկ,Z=қ?X?8B/ˇLT?B/ˇ;aK `ϽLT?L `Ͻ(sZvE.?މ̈́RbE< z?PÖLz?_ӿ!l-ϿS7'¦?!l-Ͽ1{XϿYeS7'¦?Ye,/Ϳt)?^|gUX&?OFU{?r տgjϿ. ?gjϿxtϿM9. ?M9͊fͿU7D?!d?9kWͬ?-oqr? ?l#鴥?ޢnԅ?Z?m# ?_??_ĴU׿_??pJ ?$C kƿ`ĴU׿#C kƿ]@~' s?ܺ?Qǖtܬ0?~s̡?+C?[%0_̿+C?)_1?1 ɿ[%0_̿1 ɿt^??b-u._?݄1)?(s'¿ZV(s'¿;xg׶տ/\OZV/\O?۰?c./?HLr؄Wʴ?GQ#?a/8BCR< a/8,HӿJm2BCR< Jm2cl?̳j?I?uuyz|:["?E4(?oL\-G' ?$Fb;וS1?;ו࿡K^hLS1?^hL4п iI͢? 0p>lxJ-Y*?=h>3%ҿfx?%11Ρ?J?@`f9?lO?$o1ӿsyӿ$o1ӿG]U?пsyӿп6?.; @ϧz?VO?<^?M?19Կp Ŀ;ſp ĿӪ?y5;ſy52 9G? ?ۣ~?0>9t?@1q?a`UȿJzEW?i*pc[JzEW?e ?x*tI[?i*pc[w*tI[?MO:?? dh?. ?,+b>?l&?^A٠?xhj?LN@j?y<o?bB?sC{?B?;f?@VV@㿮*WUV@dA(W?~I?*W~I?gO??XW^?06$nj?Vg$JJ?N8ӿ꿾N8ӿqa}?Se5?Se5?i?9?+[\r \?pƪQ?8g5м?mź}[`Ϳlź}o?UQ)h?[`ͿUQ)h?[m?7v=?2Om3JTͿ2Om3\?M-}?JTͿM-}?0?q".,?2H.|ѩGDPyƿium?. k?;^F3bfT5^?V ۿH/׿?V ۿHP?cwJ(?I/׿?cwJ(?a>ԅ/?w $?-;P}NH𘰿laD2?@(9oܿѪ?A(9oܿ3\W( RѪ?( R}#?DšW?+1^?T}Pw@\ V.? X~sſ;%D,!? X~sſfkV?ARf̵;%D,!?ARf̵{PR?g Pa]?S-M?C77E'"Χ61{?e:0ƿYT?e:0ƿkW`?C͹ZT?D͹AW?oL?߿GEE?;Ժ D_)\JEa? tV@D?Gk06D#W(=p_=f?1#Dn?X?1#Dn?XwĻ۬ؿX?۬ؿDdØ?ru)q?'/ߠ?w?d|$ r)oȾ*?%i{?ɾ*?X묿H?%n%i{?G?%nO$VS b%w?Wt? Hs,x`6`N?Bbg[W9?ZN"Ur/7[?HX5pS7?}H-?5pS7?RAG?ViK?}H-?WiK?W3+?v4``xHH?Ȳh?ag3Կ*dR*;ҭ?*dR`k*:v>?*;ҭ?v>?XǿyV@?dUy?A"ڍ"d?]ۓ>}տV,V,:9?V,V,^0[y𪨲?:9?y𪨲?'3iɿ(ûtu?e%q?KQ)v? է͹?'AWn|2a1"$F?* @2?NI??2?jNI?f\<{?NI??e\<{?,7U ԡ?0 ]4?#i:w S?%?)e?)eqCne?/ ??/ ?| ?œ\ 6yܐ?{K]?9m@ƿDmT?9m@ƿ.LL?>?EmT?>?tY+lKF?>?$@Ϭ9k4?&Ԏ?n1ſ$Fy?n1ſigM?fu?%Fy?fu?2Y7QChG?gGnر?ܘʎu?_ˢ?@E?NQK?zE ? e>5劄? Cy?8g῕ Cy?ڬԲ x 79gx 7?{)ءx?Af?i(>{앿~?y]?7<î?y]?)@0ÿ!/{:b4V?Tx?+_p_?Y%bb7;b]C;`¿L*{q?w"?xD ?×~ҿuɶꁛA?uɶz)nbKꁛA?nbKXBֿhEm?{?6?-?EevHlS=fӿN?eD.?N? &~˼Oȑx?fD.?ʼOȑx?ȕ׿ԓuDL}?[rZBo?cvt+KۇOme?R?*1X]zdnh)On~.wδ?n~.aAyY(N0՞B?wδ?M0՞B? "t"?3v?ϙQ?T˕fR%`^gt?Q%`^!tFϷ῁E?gt?E?A2_,•^~?FoU^>??B}khyeQI濆r3"ؿQ<?r3"ؿhܭϿ?Q<??XnտeFV ?>|OZ/8OD?tS AzSַ\׿uJ?ַ\׿aϿlU|?uJ?lU|?8"տj 嗤?ËzzsO(ͫ*x'<-7?f0ѴŬ?u4?9 #g8?}I=_?g8?ӆ ⿵d}I=_?dU=Lan?絨 봿wwPK#?vB˿L?B˿X'2俅w?L?w?q濰a@/?OQ6 g?)+A ^˿,]I _տع̾?,]I _տb+ҿYf?ع̾?Yf?{M(!-?ʭ3UȤVTؖ?-Ͽ]3ԿO?]3Կׁӿr#?O?r#?K԰룿nO=X?$HQ, 2; ?;Y!ʠ?8_CK?IR50"?X]qE?FXE'߿[@¿FXE'߿#:=r?[@¿=r?.J0@^T?w?Liu腘?䭳\E?Ay Q &4-¿Ay Q_}O&Y? &4-¿O&Y?&9?Iި*#q?a5cnuC $'?!-\?FI!ƽvoAFI!$}u¿ p?ƽvoA p?H%?=)ռu? k_܄۲?푐Uֿ>܄۲?yg. ?G_5?푐UֿG_5?;G@ԩ?O?k%m?eӔ?fScZĿ?.*5ֿ?$?f^?-*5ֿf^?:n>? 1ؠ?k~~<2 ?ʸ2?1[?q񮿒):?U[pE>Z?P俿Ӌq?P[ԅ=_?|b?Ӌq?|b?W-u?q\zn?-ť0d?uw>)C+?e[%n& 0J?e[%nLY?){ l?& 0J?*{ l?l0>&?~*fH?<nYRt?hAߪ|'5?!2βѿ.7\LͶ?!2βѿ$?S[[.7\LͶ?S[[?;Z?8:+[H?^MаҝAu?oȓIVє?6ѿ87y?6ѿ W S?ԅC?87y?ԅC?kż?Q?`Q?&ỳ7k0?W 6?AgFBK? n?61x?g%?kpBHM?k?4!+?pBHM?4!+?XRL.?[g?& e?.mUo?T.unr?%?YnCY?Ynl4?*rl˲?CY?*rl˲?=`l?F#!#?;yp~s?ҀNNm|֧W?21GV?21G F?"#ExV?"#Ex{M1,k?Tt=s?ݹ9g~$m a7&?1!¶.mL?1!¶@;?.+Lz/mL?.+Lzc6ﳿ?%VU#?\p?Q{_.aUY6?yZG]u ?_^/?Ӻq=?ER׷?&1?׷?]'%Q&1?Q_xgaʱz?3΅?vczˈ?w 8?7K*plm"O7/?K*plm8?ry 1ی&ÿ"O7/?ی&ÿɛ>- [.?n9_ֈPGxJMu? Eݿ (5[S$? (5"afz[S$?fz)|uϿqo.?!Gr?c?>r?ΑTؿъ3ѿc?ъ3ѿGyC[oҢ?y>E?f[r?NH J&J?9fl?m1f?9fl?$Sm~/ԿʹͿm1f?ʹͿx']?m<Ͽչ.B?:+?k?t!/?ЩM i΃e?M GWkhqhH? i΃e?iqhH?4:D0s ?D=??}[fe?")?B]CwA^e4ؿy5?A^e4ؿmH~U?y5?~U?SU6FL1?!.>? ;S S=?mx~EC忶6O?6&}տ@4?O?@4?MƩHKA%?TΓ?6q7?^sˆC`w Jvdd ? JpMiӿ"NL?vdd ?"NL?<&I)Lǯ?{U#*? vI? 1?༻n!cŏHaXK?.ǃ?ۦ/?o=C@ۦ/?'yL?[?o=C@[?nD!ֿRs?3{7;ph?^$ΧɿP(k>h?(9z[u)J?7瘿8 _pg?T2?EkLOnѿJ?nѿ&Dub\~?J?b\~?./wտ׵P&:?y$lZԂ}d?3?Uq?Q0X迥]#Cп`1?]#Cп=m񷟤Wk[q?`1?Wk[q?Pm_տm`_?U%ϑ ǿq?ʈ.΄?O=_ шÿ__Z$X_xx?6̳ +x}? +x^x<$N?),$?}?),$?JKKQҿWux?|Cġ9b86.?0vV(~v翆ˏ:?LK?V(~LK?z5!?+*?t(=k PO2K?I3:暊FϿK?;暊FϿ*?g#?K?g#?ix?q7?98mv}zdWe?ӥ:Ͽ`%?:ϿsE/-?]G ?`%?]G ?cA{?@?J!?$?،H4?VvH?x11A?9K:!n$y9&G?ps??ps? 8?Qqlv??Qqlv?3Ҥ?9Ϗ?z|?6YD?fԿV0U?Yؽ ?V0U?ԈD?uH|?Xؽ ?uH|?KӬ?Y$?jBbن doy+?4VB~Y?C?+оD?< q?ύ?+оύ?%˲?%?(FwpoIh8?Wn??yo?̴9U?yo?IOdj?:/$1?̴9U9/$1?4 ?pKMs9?aӛ?"2̘?'2 ],?vRw?h_k?sW|?Tќ]?sW|?H&5u?Tќ]?u?D?kz?h]!ސG?jzԘ?PP?@9T?IBC?@9T?N`_qN/n?ʡ/n?!24Կ@gI?qɣ??%0G#C?(F#C?/HDd$?(Ed$?`є3'%\o?Hx G?mn??~>ٿ14ܼ?A|b࿤14ܼ?$OJ_ǿ*&܈ ?A|b*&܈ ?{(jAѿG"eKݿ`W?ݿ Ο?̰ò?U?SA4>}?>WEWO6?Ec}Tҿ\?Ec}TҿDT–3)ҿ\?–3)ҿ"ߊ?}{~IʿN>C*kf>?*S~ǿ6Ni ?Z4scm?뽼|ڡ?JsΫ?Z\?{5h]?muIRlyDTumuIRl࿳|ŎvfyDTuwf2?:H?/Zd?Aj벡?DjHڿe?^DjHڿ2̿ gܿe?^ gܿ49udL"&?ڥ=wd>ry;״k?2 ɿL?2 ɿw3?=9&?L?=9&?Mo4ӿo"?k&npvf==0[_?{șȿҫ&:b?{șȿ|] ?>kuҫ&:b?>kuKokӿNd?B'?mWu)?~usw}?Qb~׉[?'W9ȉ?$8zD\90j2?D\97է^Z?Q·~i?0j2?Q·~i?Sʍ#?vd֑Y)\F` ?k 5|"ױݿ't?"ױݿ)#l? 8?'t? 8?ֱH8{?Uv}3 }4踪b?CC5޿1ĿW4D ?1ĿiiX.?nW4D ?mC{Kڿ~8?昝xV䌿>Vxg?2bf$ֳrſ'Lj?$ֳrſk@P?qԴ'Lj?qԴܝ?ۿ?zIy?˛0?Z nkK+`$?b@o5?C a4?o5?S?bnR?B a4?bnR?]ݗϻ/?I@Xdl??3?|Ƒ_֠G ?h3r?}?A4n?}?;eoi?=wW?A4n?=wW?>?H?HΠ?.lsᶖ`zt??e*f./6 ?d*f.J?w?/6 ?w?CY<ѿ Ƞ?d'*ATﳣ? )ן?T^?TEǮ?'` ?^?'` ?J;ѿ *B?iZDÿ*8Zſt%{z ?^\+~6͐>f ?1`f ?VY?-Pfɿ>VY?&Z:Y-Pfɿ:Yt*p"ݿ%Tڣ? }GKQA)Nք?@MD?ޘ)TȿMD?KIߌD\?ޘ)TȿߌD\?'9O?ܿAz?ۉˣ?MnMW?Dÿ \Ϣ?UPI?ו?[<rchX@˫5Y?JdAV?˫5Y?f?tZ ?JdAV?tZ ?*x q-[M?#i`˅?Zx>*6IIJP?B%w?FC?A%w?*?XT?FC?XT?H1"<5?RrzQ?%Ƚ&? ǵ?޾⋋'?W4¿޾⋋'?̽g??|4AnW4¿|4Anu\޿/G ?<2?GfM?+:|A+G|?TNֿ?F$뾿SNֿ? Q?ͳ AF$뾿ͳ Aq5T޿PLu?zqÐ?z,?yMqX? -oj?kU ?u)4rD[0=/?rDY?Y[0=/?YHlm࿳)?ڜÒ*p)qz?|Eei3D xLTW7?xLTWe ?RVEѿ7?QVEѿ/0ϰ~bm?`ʼ՝NEsz?`pӘu[ƿv5@ିw21v5@ି]RFV?.Wgw21.Wg ٿ m?.eܗ"tx?._6٣s_P }ϿR﫿m=&鐿R﫿Rf?89m=&鐿89޹ؿPuMm4?o$ſF|0d:z ?Z0Y`OO=w~(޹ t{@#$0$Z?'Η?#$0$Z?T\%ҿ9|~?'Η?9|~?l@?ַН?DU~/r)URF/?5i?;I?5i?k|}뷼Giw?;I?Giw?xo2?սV/?~:,WØh<(?Gs ?c4$lG?smG?ȶ?GzMb?sGzMb?|fa7V??lOC??OH?aճZkOC?aճZNP\^ϡ? $/9込zHtz?9?EY?Ko?0zc Q?Ko?>“?x.1zc Q?w.ș㦰Ρ?Wu`چaO`c? dD>?krt?Hp~d0v?H?t `鯙Ŵ? rt3?Ŵ?zw?wngt rt3?vngtv* ٿ5K?p99?N?[k޳D n? ?E n?c-? OEؿ ? OEؿW׿??ΐzs?5 ?]pV?W/:"|C?]d?.:"|C?8M?(e.]d?(e._/hG?J痵r?iqN`?˫+N!?Uw?Y ?V#e?Y ? "Hv? )fV#e? )fļRuZ?2?I~]d?J?~?uK? ˩i$?}j?f!?WAyI"Xw`,j?jAyI"j必1+em?4vK7?}?_D? rL?nE?$Z*ڿs m0%Z*ڿv?~Mҿt m0~Mҿ>p{?.b?FWVǐ?b뗽?1Bew?%Jfe/?dt9¿*̿dt9¿Ksp?9O*̿9OI`ÿFɆP?{ٝhӐ?^^V?=\2o?Fi~ө?]¿˿]¿"U($?:ŚxP˿:ŚxPH"ƿ^ՈG?!?E=g\%"=|?PF[?V[q 2UW ?n5<࿱ ?ݜ&ſ.Al7?n5<࿡.Al7?b90 | Xs?Ìjn?SQ?wW|}N;-iۿM;-&??w#G?iۿw#G?[%Ҍ|F?#*zl?#f?q،܇?[LHr yW%Ocѿr yW0?׊0%Ocѿ׊0J-ԿH ?1]` @?g'М?WSzLj?8;ƿs}ѵg;пs}k3U?Eѵg;пEw4M\ؿF#{(?`iW [Ul]?)H\WV$q=4b?TAP)Կf׬bc?TAP)Կ0^U6;ؿ6|2?f׬bc?6|2?pGWe]/?vPԱʈ(W`p>y(U?^N"οxVv?^N"οt*ٻ? bxVv? b t9H,7?Ǡ5xHa4K ˱?e+?e4[9?*]e4[9?}a@ U?*]࿺ U?!,8+|Rb?ZY@?5RaLi?:v1?*R?kܿ*R?^vy6?f}@?kܿf}@? 忏"}Z0?$.~ j?& K.?,C78?_G?y}ǿ_G?~W7Ȩ?"{Oy}ǿ"{Oeu/K~ٿIam)?$)Af?hQ\jnc?Hgu =v?G6ƿ =v?>?$)G6ƿ$)o)ٿY:P?? `.?|F?'_A?F!]9?Ufv憎>{]9]~c/t?' ?UN?' ?Bav`?6jտUN?6jտrҎ??\rCsl@x?cKe{E??)j" ??i\?b2ÿ)j" ?b2ÿxt2P)?Ñ?MYIAyow?tzi7M\?m?ĵ#Q?n?dtǢ?ԶKÿĵ#Q?ԶKÿ7)K?d8/-?#h)?nM?<\@?Zh??Wڪw?"M?[Of?i PϏ?g ?bF?j PϏ?^ ??PApfg ?bF?QApf{V??Q?{?)NG?)NJ90?JGĵ?a?$\?00Gb?(}ܦC?}֞&&ߋ2o@MӿܦC?1o@MӿJ uC?~?D?G+b?[—j0?B{_`?XD}nC?XD}koգ $$RҿnC?$$Rҿ%?VoD?oz?GGN c[?jz]?~(Ij=?3t?ҋh),?ԡ(4<|I/0Bٿ;|C=@>}pݿH/0Bٿ>}pݿ ?`%ų?|Xw~yGs?n׿?= ]1 Uο> ]\[SKᅬxVwϿ1 UοxVwϿ-?*J?e\t/xƵ"Bj3>?]lhſWENϔ?v#WENϔ?t Ϳ *xv# *xha? OK?؞NT ?Ց53?+;ɿ0?{̀䣿0?oyvͿ[<19{̀䣿[<19cT?R'? 8臿p ٦b\?5?x˘rZ?XD""ox0?\.__i?ӜeC6c?aig?ӜeC6c?Fـ7"?aig?"?ZN?: ? !K?0a6`F#?pQ?i?7մ?i?Oq޿ -(?6մ? -(?EHܫ?Iy# ?yP:8?P +v?Olj?j?m ?5D?m ?<^4=jOX:?5D?ƿcy?|=?cy?ROslJu?{6K?w"?Y ?֕?A<̿t濂A<̿@LۿZjO㿅tZjO㿑/#Gdb/?/ fp?[{?n ¦v? (М?"R-KvϿ 2ƿ"R-KvϿ<QE0=ʿq;gп 2ƿq;gпcȿCK)?4Yh6r?wB?U.(ē?0'Y?8̿[sſ8̿c$leʿoKϿ[sſoKϿ$)˿D0? d>?xUcu?  0C?3x ?Mex ?Q!|Q~9}Eϵ?yBݿEϵ? 3?brSxBݿbrSUnAfUR?k?1[?Ai~5 iJъr3L~޿Jъr+&ؿ`_3Ucڿ3L~޿`_3UcڿNpTXP@T?p:2%ۍl?YXpjx?p?DIS*aSoFLEQп*aSaJϿJ<\'ÿoFLEQпJ<\'ÿ+Az]\ѿJU?sdrYp?r^̃|?I+eHYVr?ЩŶ"՛ fyο"՛Ͽ^6ÿ fyο^6ÿ8lտ:9e?þ$δ?kXLZ>렿aǹף??3, a?O*Y+$k?MWfeR?MW{ y]?bfeR?b+S Jݣ?7"꠿ (N?B3.$͛(V?J5I˿.[n?K5I˿nOBbDv..[n?v.㿸\rOWhi?\5yf?%HFp?tCw39?=ޱ}f? \<ޱ}f?Gj-/㾿nwf \owf.Jֿx ci?n6DKc?v8Fu?Jv~r,E?}5i\}\'l|5i\}: \߽\'l \߽iJa+ڿ‘q?B-mѴ?4-x?M?&ES7Ԕ?2?K? ^'b+8R?9\\ ?+8R?,:?#Q9\\ ?#QSP˿=Gfˣ?& ?ÕGӠ?ORT/?x߼.Yx$`?/Yx% ?0H e$`?0H e +sMv?Y{?Ͳf?(( \Iy?|ã?QPI?*@ ɿ|ã?*@ ɿyk߿*sҐ?ˊ5{?B@k?kGyۚ 1?? Ӹ !f?? Ӹb*ȿ!f?Pvj>ȿД̠hS7?пy?dŲF$UmE? ~?B/ ?BJU&ri9?k*-4@18?rۿ18? --ܿw@rۿw@ 3?r-&e ?$;$?МhZtю?wB;Pn?gq0ֿwB;Pn? fO\׿H hq0ֿH j֙k?^/2?Ӂ?n_X{?B [n9&ԁpw}D!ſ&ԁpwwϑ??[п}D!ſ?[п|L<̿).p?Yd]d?.Dy?eqq#J'8ﭓ3ࣱ͚eԿ?Sſ?N;=_d,S ?Sſd,S ?A7n!n?퀍z'@0䢍]2 ˿FY?*+?EY?cˠ?0X.?2]Gw; ?y/&Ͻ7[?ܝ{?CGݴ?۝{?Ux.Կi[l?CGݴ?i[l?,M?z?hY\Ip?7_}JV?Z?H}?Z? ZFq\MW÷H}?\MW÷ŏC??ޓ 7aq?ߜ ?A>/c/?<-?A>/c/?o?:N2xy,H<-?wy,H ??Ȋׅ?"AuƿtYH{Sf? 3K?[ 뾶=_8q"nm&h֕Y#ϐlm&h֕꿺؀k'^ſX#ϐlk'^ſ i?B g?y))?}#t.&E: H#GM Ӷ H[,Ϳ#GM ӶͿ ?MN?F?̣uhh#c?#)B?7gƿ?))?7gƿF U8ӽsſ?))?U8ӽsſz@?`T?6x#?@j.-`d? )?¼ݻǿf2_?¼ݻǿ+@ƞeuFĿf2_?euFĿ(c ??ݸ5ߡ?Oh)̸zj@c".}9?0>N?*tSWրtdz(p-?c+Eп7ThH?>VylVyl,Föu?5|u.tԿ=,Fö.tԿ?HF?k1K?c9yǴ?Eѿ4Ԙ?K4Ԙ?~jpMKlpMV*% 9Tkƿ#b86J[>*6J[V?](f?h8ct??].TſAe~?U(?2DY ˘el\{h-%?@{-?Ÿ^o??{-?O*@N8Ƹ^o?N8^%J?Hm?8ХnI#%3ip?R&?첗D?R&?0~?dl&SȿD?dl&SȿSS ?o7:c?R;Cٙrr5ԋjOD?Ft?PZX?Gt?H?OlFPZX?OlF?׬\sb?VY`Y嘿nJM!cԹ?;y|?(h8?;y|?E "z?x0뽿(h8?x0뽿!=?n@?5Swв CO?Rg?@AZ:+]w`y~??y ?o+2V-?y ?/FW?zLo+2V-?zLKj̱@ق ?$b냿0Fn=5_p?逩e?B`%?4{sj?C`%?'*?Rؿ4{sj?RؿuWh=@T0~F^?a;Ȇf".pc@.К?owm?5~?owm? {ׄ;5~?ׄ;o%?\?PE`gZTp[`KGt?6?(Õ ?6?+~0{(Õ ?3yѹ=?z`cJ?轟Sm 7q?&%ɽ'-䂠?#wm`JK# ?5>JR-?V㬡?9u?saZ q?0p]Nb? jt? jd'E?kL]Vt?kL]VtQ?Dv?ú?LRƵ? ?a?B- F?Dšj&? xNe?=?WZca*?Sw?ca*?c'?\Sw?\@X ￞- p ?`gՙ?Vx?LwdJ?@dQN*??N*?C6?F'ݿ?F'ݿ>7?63: \o?X-?\'Ҭ> ?:㦅z?L*8'?ǑU&UmǑU&=wJ?[ ֿUm[ ֿ?z@an&?hقJjD_1?7k.?s*?kGGߤѿO1ikGGߤѿ?RK5O1iRK5q,?5K&?g ?̿{Pt?5h1?k?6޿2[`\ ?H0l纪Q?)^ֿ)?Q?)^ֿ[@}D ?)?}D ?[d?i?[+?WE[`0s &KSv;(?MG4??ɦ?eGº?k o?J?k o?;jA?D|! )E?J?D|! )E?C ؞!-;??fZn?YIyj? ~'?c{|l?? =?c{|l?;+FM?:8#?? =?:8#?i,"鿌`?J:swj?}rV 6TL?.đ?FO?TʕO?GO?1}l:?Vޅf?TʕO?Vޅf?U)?wl?m>ӯj]/WU?df?1u-&?`-ٻ?1u-&?C 7?ĉ ?a-ٻ?ĉ ?حǿ5j?'Fy-?u?'U,?i6V/?@1<.T&?ՊOs)?7 ?l ?1?G+1?GoQ"Hg[]L?F+"Hg[]L?T !1bh?g?3y?x~?Z .?َL?-0ni[َL?퀚CHJ?,0ni[CHJ?jU?@_kW?rcψc95x?M! ?MN?~'?LN?lߦ?~'?lߦ?` e տ1S$?'U@?2$.t&w?aos?? ?d:?? ?uKgD!C?d:?D!C?&DLzٿsz"ݧ?J5]?ɿv~GH?v&? ?L=C)̥y¿^̥y¿mN] ɚ?^ ɚ?-9EHwHk?n??ɮ5?n '\դec;%HaC|ܿԤecW5]!0bK?:%HaC|ܿ!0bK?y@cU 0x?Lퟨ?;?[?'vͿR~??A tۿ`s?R~?`s? O18濖4Q9? .B?ߔ0<~?ᤖȿ lu󙠿}T? lu󙠿eqٿP??}T?P??}/OTʣ?:2jn Pi?a΄!oF?rlFf=cpjf~m?T~??`U2Cפ?T~Dפ?!U̢?>=;"?&VHLI䝦@]›u¿^SX?j-gG^SX?Хt]?#Yt^>t]?ǻ?C㐙?#YtC㐙?kѿY?o`@o _㒿B`fO?HIxfO?X4_?#?HIx#?w# |ͿXI?<)nVFҧI'F#-f? A<?M1K#o~?7~xy޼"߿SZ ?(S>߿SZ ?:=x?(S>߿Qz[D &l亢?؃}qB|{7Xߑ9#ke4.ٿب ?r?ب ? ?'ɿr?'ɿiԿY$r,?ļw{B\{޴c?i2p}x?X8ѿp}x?S?nm喿?X8ѿnm喿?l}CڲJJ(?oDDˏ}ڥ] )+[ӅM?QD#пԅM?_A?O_E?QD#пO_E?7f$o?)88WTߍA pB?h&hx;?&KJB?x˻Y?B+?y˻Y?&os?>RA+?>R@cw?I}̡?$^ʖ]Nx?P访T ?~5?XIx?~5?(r?Rx}M⿫XIx?Rx}M 9k@?@W)?_sb*Xا΁?Ќ*?w-nu?Sw-nu?a5oH?=S=tk?SBe/?'<)ӌ?QI7dR?nr߬?\2 Anr߬?wQ=)`?#]2 A#>I? -_4?4~8+? cQ>?ٱ@ ?h'JaD? I?쇿&'?SLǒJON?S⿣mP?^2lOLǒJON?^2lOayf?@+A(CB?vZxU?m?:&kIU9??Y ؿHa+?Y ؿGG?9X2ȿHa+?9X2ȿ}̽?..?Rl?L\?VC?jjmޟ&m?D\?& ?D\?ņ\/?h;ًڝ& ?i;ًڝ&4?!jD?D`?`l?1(N(N?ܬ,?P״Ԁ?ܬ,?\q.?,Y<ِP״Ԁ?,Y<ِ۫f9?bPn?q.kqj?2\{?000h?"<?iUNTJڈ:c츶?KJ?(>?KJ??O?P{}d?,m%?b !KͣZ]Έ?lѫ2?ͣZ]Έ?E?3(+!?lѫ2?2(+!?,? ?3hdґev¿ p()Qሆ?6kū}L սy՟n,Row$J"%?n,Roa@Wl7ÿx$J"%?Wl7ÿ>ܿV-Yq?Z.hlQ.u⡿En 5;uu+⿵-?;uu++h?N׿-?N׿S%ƅ^R%?Wέrě?̄6b?.Pg?)M,>?Pd4?*M,>?kW:?mDė?wNF?4KYi?MXWnM?D҅?MXWnM??^f?,?D҅?,?HD<?^&?>sG]1>?D&ÿT~m?}3d?D߬_O˿@cW?%?@cW?ik:!A%?A뿧#[;"Z?JQ\D?LZJ'ʳݖ ?ثtg*?8?ثtg*?4gs]~6࿣8?s]~6hf۪S&l?ZFV!?C?9k\?,ql?Q&,ql?"B'-G]R$/Q&H]R$/e?s0p?`` ??/pB?_1A?9,Q_1A?w3H.o9,QH.o&?nh%؏?wts?b9@孿w;?b?",nh-?`O0cLy8Y\N%Y?8Y\NJKr?d $Y?d &ց``E?G0[)Nʬ?4j9pי=ٿT>|ݿ'ُlT>|ݿt?5T (ُl6T y{d~za?3u?Py?2r7k?wO elʿwO񳿛yF?O¿ elʿO¿ З?jw4i?a[u? :?xq/,?g~' Sʿg~'.R ?01>¿ Sʿ01>¿+Հ?(pO?R1?E6r?ݒ3?DzQjΣ?9QL?CzYg?-{?IP tH?A2m?P tH? zt7?A2m? zt7?HfkSd?XH? ?OkFo6pI@s<$?s̭FCӿl;"$?ֈgd !qSّ?q%?0 pB?0꿌O?Ow pB?Ow翏K8ܿH{z?O>TA~Xe?LbX?LΨzM? S7ҿ+iC1? S7ҿߜ?X.+iC1?X.]ۿqVy?x}Ia`?h? )?4ҿ7ν?4ҿkT?Cʰ7ν?Cʰܿ%rTF?{e LpſfAz?.'u?M{MG&O݅y ԏ?X"A?x݅a?1|i?x݅a?R]%8nK@?Ȼ1|i??Ȼ^3$տ9I<̡?=q@8Aar`q*?uh?́?1N~?1N,Vܪ@a7Kտ~?`7Kտ|u^Qr%J?FZ_p}uU*?&"?̄,yĿ )U ?̄,yĿCV? m^h?)U ? m^h?OPiG?q-3_yv??f\Ņ¿kQ?f\Ņ¿l,?)[]kQ?)[]2BN 俷mwq?6f.`?d|?nZs?䀴7 ? 0܉?2WB?p 7?ꓩZ?2y5AY7F2y5A!mc?gzv@Y7Fgzv@Q9N#R$?mkTk|?=u?tcܶ?< UӴڰ?g [1GNg n_$?i\+?[1GNi\+?WnJB*_?^38_nh ʋe?U?݈!}!r?ÿ"`Կ}!r?ÿ2:.;?uO?"`ԿtO?Hhٿ6핢?|;~fH+2|^n?V?%b*]kUEVÿIҿkUEVÿ9>]N?kz*?Iҿkz*?e&@ڿwlz?Hs2O ;ʱH*a'9A?)գ9XòaVի?k2dzۿi2dzۿ;Ge?i2Fe?躝9>?S. }u?jjyhɿFEq*2xkݒFEq*@9俢2[?2xkݒ2[?fSGhNK"u?P9hok`%u9v ʄ6*?.$ b}Dۿ.$ܶ=fÿЕ5`x? b}DۿЕ5`x?e1wE̿:}x?2fZp1i\+H?KFWx[XڿKFW[OĿd|^@?x[Xڿd|^@?x/οj!B?d?bD&xs?CSe澿!J9P"??z?F mcjeՕgs23?v?57⿚v?Jy>ω(@57ω(@՝\#ۿ<?aGf׃?A9 jhfn4L3ϩ?Sm:? V׿Sm:?Z"r }X-? V׿ }X-?FAտؖT [?2l=ڢ?),=فob?C!G ??%H.??ap}?weM?%H.?weM?WPD?gi?#j]SWj-?2M`|̨ɀRF-?I&u[e?-7T?I&u[e?*WS P-?-7T? P-?xv9_?cYZ?!K+z?AH<?G}*ѐy!/c_?c|?c_?dXbj7[ټ?c|?[ټ?|%?ϗ ?J;?j?΍iR^nO`<Ȁ?3&n?`<Ȁ?0 cE?,q%?3&n?,q%?5?oJ?T vX`-񚠿qzU^.?#ک%*TcJW1eu?{s|Guز{sHy8d?ca?}Guز࿠ca?:?q"? Z80N0tz\=#- &?ՖU0dՖn?y4?%?V0d%?JVq?*vCx?A~?iS$w?NME?Nm*?I?J?I?q:C?Vz}}h?J?Wz}}h?O$W$Z@e}ģ?2dU?ox?ó?ڊ! N?q1M?v{e?q1M?Ȁ?YND?v{e?YND?D@i)?h?T L􅲿 v]?['T;F?z 3&T=?QI?bZ?QI?Zqa?0ĭ:?bZ?0ĭ:?UZi@U@%3`?_sBp?>v/t?p4Y,6H?ꚌOì?$h\*?.?xLňr?~ζ݃uY ¿/?vZ ¿n6Ɏm?.?vɎm?Lڱ%Xi}?9,Мbl$?9諿.ݗc$m■c `dJi:I?$mi:I? T\⿇k8Gw?q;;s?iS?̊og}sӿOX{Rœ?yOͿOX{Rœ?5&{ѿ W鸥?yOͿ W鸥?aJui/?~F^?,>i/?PF7B Iſ~F^? IſaB?<„? GYv?cR,?Z. ؉Ԫڷ?^?kU}?^?3&lͿS($rϠ?kU}?T($rϠ?fy.]ȿ5Z5?N#˻u?y)t?[ ͈f̉յ?6Qk?<?5Qk?FtJ'Ͽ/g??<?/g??8ڃȿN {?QW!F?,|?mi0H6?]D?u6;?(Nđ%HM !Q?@ŶR? !Q?Zke@ 3q?@ŶR?3q?M´ob?";x/M=9? 떱!kH7v=,?a1b?7v=,?)C2P쿮XC~?a1b?XC~?ߙ*N #ء?NMi=Y"]?"{apn п'4a??HZ?'4a?aZھο2xK ?HZ?3xK "տQ@+?;/h/I?`^'5ѿ"lW?P(/?"lW?ȳ٫пr³P(/?r³DpC+׿,)RO?N[!?Nt"ޛ?^WSl5ڭ?f^~E?R??HI |B3?HI 6quܿT=ӹ?|B3?T=ӹ?sx8"׿D-?VVߢaOIpP?T''?\)?`1Z?B5+ r?`1Z?Zt,r%0?B5+ r?u,r%0?fNldI?($֙?^{?eh n31xAT?ѡ?1xAT?/V龿g]˱ѡ?g]˱X׿s.YG?xu)gJT?ZT?a"-Cic%>U?C?hc%>U?{aĿd6KC?d6K nؿ/;B\,?ُBV?lL'?У?͔=?lR?>ab? 1s>+V`}?>Z?,V`}?Y)6 +?>Z? +?zb(_]g?"!? خ? |?9)M?N@p?)M? gp?=+Y !N@p?>+Y !PQ=NEע?7 Z傛&?$4$۟?"}9?K㐿k>=C?K㐿d1򢿿mdk>=C?mdUs٢?w@͂??6(Z?1y)u?-|W[`?-|W[4lusD`?usDtLn?nlZݠiUM]%?r3?UO' R˧ <{JOZa?[ ,@gq?#?@gq?MNLm޿#?NLm޿ꗞѿ:?~埿MɅuՠepB~?B ˉ_?9R?ˉ_?_Nh,Zҿ9R?ZҿXCͿ ;?A͈ݬ"0V$?@฿?`?S&??`?)@qܿUٔ1ĿS&?Uٔ1Ŀ߳LFjAD7?P?}jtw=g[?Eڌ?g[?WwEF>H޿E33ÿEڌ?E33ÿJX0n˞֩mY?d2''?SOV?[ǕY? Cn?fa ?Ec5?Αl?lӛd?cGſlӛd?-ܽ?P ^?cGſP ^?P?"QA?cv=O6qJ'%C?g?wԑ?ަEzwԑ?%ҿGR ަEzGR Xg?fˢ?㸘 8%10O䐿Dj"Qsן? к?d:'? к?vHb̿ Od:'? OjϙV?WK%̢? Ydچ0:R%?Ϲ??< o?Ϲ?-пum6=a3>< o?um6=a3ue>?|?<5L@ B?^Rw?b&?r!O!\?>>?+q*p?ҿ ?q*p?+MNAD^-ѿ ?D^-̔`E?ȥ/?ّaEH&?/o?0??ВO??Կdp㿪ВO?q{ %X?6 g?9.?6v؂j4(7?X?N_~~X?e*6}? ĿN_~~ Ŀ &k?6"(o?EЂ: 9S^gļk? 4?v`R= 4?0 io_Wÿv`R=_Wÿ-?(µ?"ExAx?ʱ?z=ӡ?Thf?~9kS?"iW?C9 ?FFIbƑ>FFIbM(2SǿOW NƑ>PW NpP?Bf?t5ne?}?e߼0 q߸S?t.a?8cat.a?Blȿ>0T쿼8ca>0T쿍$2?:˞S=?[ca?Co2M2.5(1\^?Yùf¿qOYùf¿؝7>?9p3rO9p39~?6D~²J?6f Xj?ԧwM;EכSy+? ID+ſgeQ? ID+ſS͍ ? V ÿgeQ? V ÿ·{?oXD??&=?Je/v?YPe^b?߃5B?^B̛!Kj?%wda\Ic?%wdwf?ZOa\Ic?ZOD?͇A慢?:Ϳ3K?Cqv?3K?>},]?.T?q%md?53w?:m? ĵ?/$탳?}/$탳?(i"e*?}i"e*?[3?.GU??e]? Xjw?ƺ?K1C?d ??|*d ??/X̠`瓽?|*`瓽?# I?ЄM?v*믿d{6,f3 ?H3X&?^c-H^/$t{8?2zFz˿s{8?Qe߿4Mc?2zFz˿3Mc?RsP/?fyyʬ?ya|.?`Dt %Ѹf쿸qRI?w^`ҿqRI?ڿH?v^`ҿH?g(QI?a}\?apm^?Tի2?(W?+6¿Y3?#TY3?Leīſ`Cb?#T_Cb?G|ۜ?Ÿg? ]=H?$ W?|,Tÿ8C?9_8C?.w ƿǺ,?9_Ǻ,?qt?3v?vi-?T)Ճ?vɻUj(?ML5WnHX?x19:},?x1࿙tֳ ?YM??$n?oݗ?_}:?Q?_}:?fbsпd+%Q?d+%㿽${~𿂹 ?juB'yv?DM?s ?!B+?%hA?r}Vo?%hA?oj8 cE8?r}Vo? cE8?twxs۝.?X!e7w|,?2!ڏ?ݢ<.?T?@?}/Ȣ?U_jcݺ"6,?ӽR'F?yc ijr܁ؿ5 [=a5 v ?s\R[=as\R52H l?{p?pMJWb?QCt\̿:YG:Nvꊁ?mlѿYG꿴mlѿ/ JE?z^;Hw?Z?NTi#?l~2R7373 LXͬ&\?Xͬ&\?Ƒ?|˩F? X2v?#.~M?lJ? .fJ?oqҺՄi;MȴoqҺՄ8|hk?eSr?j;MȴeSr?Vݙ堿Jm?AcU`f?#IL?Pp?Hs)蘿\6Oڀ?T`yZ8Zy8$g?8Zy8b!]@.o$g?.oc|W?‰V?WwۖȞLpcR¿Uũ?t?Uũ?:5˚?XU'3t?XU'3K~J?m??֨^Ȼ&-٤Fqqy]xd?_?&^?_?P=[?gG&^?gG@c?Yy?~H i鎲2KWQ@C?:4f?8^?:4f?s2?!,a8^?!,aM੒?!칞?.?/a782?f"9zz.l?:$3Cm?lԠ6?AÿlԠ6?t׻@@{^ Aÿ{^ _i?F?{; u?OoȜ^땿(xoå?mhU ?hМf?mhU ?|?WiМf?WI ?,8?A3gs?5OA}#G?;jH?Z?;jH?]J?Z?m@lҴ?ԇR?4f󊗛,.X?хĸBIۡ?`pt'D3|?"c"!9dQſpndQſWSy @^~?on^~?I?vxԷ?֤L?˵֬Z?_XhϨ}yl6$?!#mڿm6$?F+?T!uǿ!#mڿS!uǿs׀?~D&?(_xL?:~L \?`B?gű V>?OGԿ,/\a]?ePu?qDRHg?>qD_=?j;k_?|!rT+_"?& 7? =}?߼ AP?|??Lh7Ihs?kO䢸Dhs?kOJK@V䢸DVAjM@}eڡ?wОNF?QiX?V5?Aaҿ;DsY\d g7?}H*?PK Τ?Pni{@jL Τ?h{@jFC ?:׳ll?5"ի?`?L0 5 -?UA?-RV?:VUJLCZZE'ỿZZR]#տoY>{E'ỿoY>{]73?ATK:?v L0?LB2?p;nZn:8䂘7,ڿAϿ7,ڿxz5ƿ}) ˿AϿ}) ˿k/>'?Z?Q2G@_?bn鮋rUĖa=¿En2/UMU SĿEn2/B8_~#uEbATMU SĿ~#uEbAFL?g}?|?=^zHj?/t\kƋWDžTodǿ{v40#ÿ{v/T1f'_Ǩ:40#ÿ'_Ǩ:iHص?Vc]?}˿x : ~3(bO?ҺAȡ?r.d6fЗ0+#I?s@"9oѿsb_?"9oѿ-?{TK*ӿsb_?{TK*ӿ|^?u2ٍ"?#_ccꦒɶd?cy$?+pſL?+pſ2?Te:JѿL?Te:Jѿ`%K?W\?a?7 dE ?i(?JFU; ?i(?ƑjZFĿJFU; ?ZFĿVã#?w ӛ?ҋ\Ҏf5haWxG;jJT?%Ђ ??%Ђ ?f8թgÿ?gÿ.W?j_}?4cPt7?)SZti?u?)k# ھ?^ Rr c)?Tļ2w?O=?ż2w?cK-?O=??d?&[֢?'B?=}?>׿2&\%?Ƀc?1&\%?T⿉E lɃc?E l>~z?SYŢ??qQBu?9fIeq?'P5Aɽ?A2Ya?S9?A2Ya?>۞Iο]&)#ɿS9?]&)#ɿ{字)¢?VE߇?ks?J?IBL*%?-N?%fº?-N?:Ϳ4eȿ$fº?4eȿC Dn[Qz?}ʥd?Ȥȕ?Qh=?z5<Jk^?7?<*:?_h?ekA_h?'[VO'A@?fkA'A@?k!ݿFlmأ?gg[p$t?$\>i?5;-տIus?*)忓Ius?[8 dq?)) dq?iڿs, ?@ դ?Q#8?e?B"ʿ= ?V춺= ?MkտJ\jlȿV춺J\jlȿmP)?޴>b~?%pP*?7fH?ցIſqNyȲ?yGqNyȲ?f7տD[*ƿyGE[*ƿI5⻿iRʎ4?p2v?Oշ?J?ܹ^˻?k OG1 ? 57M?䪿ꢿ*t ׿,!^)t ׿R9@=O .7?+!^促=O .7?t'?D@?$"a? sx?W?4_C+? 5{п, pTMֿ 5{п>#ʺ?i*e?, pTMֿi*e??; -Q?Q/:Ok?'lEs?9Te?e;`p4?ӊ0H}F[Ųӊ0HK?:?}F[Ų:?xLU?߷yr?T~? /y`?MVIo ?S0ANS0A|ͽ?C}(?NC}(?ԧ?|]C?ΨB?&jpT#X!?J?8bd?q rRe6?z?nu)쿪nQnu)Uo?-?nQῂ-?۳?i?AM?0C-\AJ?Vgwq\?%SMr]޿%SMI7?Dr7d?r]޿Dr7d?ڍ9Y?M1}ء?N8Anޑ?wN KY?;K+m?< j?fc/ÿ= j?b'?О (?fc/ÿО (?z8? Tۡ?m(8n(?E~aU?f g]?Vr?3n¿Vr?05?u?4n¿u?l?ڇ($?]Bb<5?4{NJƽy7ݤ?;Λi?BU?JYc 3W=D--](?S|i--](?OE? VT|i  VKXgEM|?˶2? 2i"ixFݿdQ?d1㿸dQ?y+?7-ڿd17-ڿ.%}g? a?=ۇv/u?C7 2L̽¿#u%۽?_$u%۽?Aq?;e?_;e?ae?Ai?ɚr?+ eVÿLGBy?Ec4LGBy?¿m>X?~?Ec4~?`!%2z=?_’"௔s7 ?'Rh !I]~{'ڪž:m? #? ? #?HſX= ?X= ۊIʿ*Ũ?{L?qg?1ڦm?;QѠ?f"?vZ{?f"?D.CdbvZ{?Cdb ?Aɿh8?@rbX?y"K~?-P7~ ::Eſ[NȲ?){?[NȲ?"գ$P# ({?$P# AFw¿@TT?lN\?4+?(B~ ~i2ְ+ſ*(?"+5?*(?Wh!-_?"+5?g!-_? ʘ=?9aO RjH $\!SrU?ϬZʸ?`|@t4?<?utyOxutyODGS{'ܛ>x'ܛ>rtK,8?cq?WBntр?Y?*opv \*opvK?*nE7 \*nE7Gl@迁 ?p1S? * {?"="^ن?SH8? 0xe- 0Zb@ο跿xe- 跿aʿԬjߜ?sS?N}ۧw?T??d(S?dJ}Cև% dJ}IN̿tNDև% tNL#"οQ-h?hǰi2,T"sO"?y=ߐ ‘?F@2 Fd>?5z?d>?] _t5z? _tsu- 5a(?`T_?pn젿L-GK.~?|gGK.~?*sMκ|gMκm 5?YB}K?MTj?KD_ Ͽw9 cD?Q٘zw9 cD?NA̿)Q٘z)7+οV?3h=󷄿P汀?R uXk??pgѿZJ~?GMYJ~?~̿NGMN Jп$86\?9ɟ?!?ᆱt?}Ju(l?հkʿ VƷ?3Oʔ? VƷ? ,?†?3Oʔ?†?w.8?ћmF?7dt?')uwjS? ֢?lƇ2?v&Mj?dy )f ?PqAW}}?F{ְK2?'\`܋?ְK2?;tc?r9?&\`܋?r9?Ȓ ?n=kk?g񎣢?ا47ueMx-U[?bOǿ,U[?J\T?I4Ss?bOǿJ4Ss?e?1A_?( #u?y٢|0yh)=ZOSԭ:V?E6Udƿ:V?(kWz?E6UdƿWz?N퇋T?.)a?ո5Kt?mU9zR7LܐX @4?X/?efſ?X/?>upڬIY(?efſJY(?Kb?y?je!-?vW r ΄?ŴqfFs? @4?ݮ*?BS{k⿊ݮ*?->lS3!Lŧ?BS{k3!Lŧ?~c:W?G^?l1 ܂S`>?th 3H??X4%ο?0濝P@?W4%οP@?n?#2w?C3ZV`?feh>GK9w3?{!z?C|,w*{!z?u\oZ%w?C|,w*Z%w?<ȰmnЁ9r?I='H!Z?#jKcǝZv˄?5sRi?M޺ٰ5sRi?X1e֡?M޺ٰe֡?ᤥ=)pt?C>|M?<@ )0?RɗЮbR?[>;??Q['W9tc4ɿJ:D4M?K: {̿=,IP{ݿD4M?>,IP{ݿ&/ Y?A{?_Qڔے?#dt*|?/Vor$8h$[ ᅲ]"?h$[ .dпkٿ]"?kٿ+U%2?mO?6tJ}?E}U0xo_܅+C{˄?)!&o!Ez{?)!&o(#T?Pޭ"Ez{?PޭRB0 .ɿ.,d?c|0f}?k@) xlYTlM~?r.Y¿i~=鵮?s.Y¿jt̚Ȯi~=鵮?ȮVLoÿV$y? RǿC*?K>?bleU? Zڂߥ?ll}=b>G?}=:bNkҾ|Kb>G?NkҾ|K͉K?W[@o"? 4?Q\|7Z? :ۻ7Dk㿧'?7Dk| I߿8K?'?8K?5a^??Ϣ?cPwj?UHR,co.vR/:{~t@¿Y?{~t@¿ :U?I[?Y?I[?ͻ?ӿ͖Ǣ?, d?:DxztOt踿l`KÿV:+o?l`Kÿ5mc?]ߙu#?V:+o?\ߙu#?=.пى$?7*C>SP? ^?I2{ЈŜ?񦙿濲*ʼ?,`ſ*ʼ?b/?INV<տ,`ſINV<տPW? /6c?Ytyx$)SFfL?:?-\lһݬ!?.:<6׿ݬ!?KTfˠ -:<6׿fˠ ycI?(y?y](.E?ޮv#o?Uv?5T`9?c){?•^̿c){?2^ޛx'w0۹ſ•^̿'w0۹ſ*? F9?b">?&C [@l?zv?}?|.F[?f& [?iʿf& [?Xʿ=nƿiʿ=nƿ?p:?ٜa{?(C1I ?%B?%&E?lFT? G|y_?*hh?QVq8?!8?bͬgտ!8?"}?9?bͬgտ9?[ P?k\?ح ?Ó?L9>?F70mё?XH?" ?l8s" ??b4Eǿk8s翳b4Eǿ>s@s?dʀQ?=r!"r(?63?=m`?M#Ps>r=m`?:QN?| y?ga? œ?V$d2Zܼ?Q]i@2Zܼ? 5&4T?UH ƿQ]i@UH ƿ f?yȢ?_œU?*1}U? Y]tՌĿNx9?OPav?Nx9?ޟ?w`NPav?w`Ex?5Ǣ?aU{?J4?q!7Ŀb1?QSb1?B?S)QSS)Rу5?fѾ?]C=?Gt!?Op˄b%ԣ?'z?ߺ ?̳WBD(6T,ZE?6Ῑ4[} Ve.?U,ZE?} Ve.?w8mc?=QU??]$EY5 忺QMտ6?QMտޫfֿ`,d?6?`,d?@7ߨֿK*?H%1$@)x?$]=xCv+׿HđBĿO4U?HđBĿ{1E,?cE-i?)@S=ۅ?k/̿j/̿"b~?uۻ?Wsg:?E̙?9]sv?U_ֿ𿧙6bT_ֿa8N?f$Ŀ𿧙6bf$Ŀf:?ڨY?!蛂6bnz?J?i(?'뫗'lׂE?뫗 G\?>9[#?"X?D~?:?qY黿qY黿h֜?ЁЁ;6b?6%p6?" dh,7>c?^?4O?H+6nr .^ȭ?LݱUgտE#^Dgįȶ?E#^D[L?d?gįȶ?d?HŬ;@(Msq?ˍdz?9[ @X7?8^Ͽ!F]{'H=? F]{nS?d#x?(H=?d#x?3w!?nH`?@>Bkl?q" qj?^]K?5"f8@25"fn6?;Ln/9@2;Ln/&e[?)?ٕod?ثEv^g?#O*%?)5Sއ2>%?`E3?L)5SއLALy?bǡ?s^{?dSi;QV6D!?Y8ˬ?UC͈Y8ˬ?z??[X`{?UC͈[X`{?!]H?>>P9?8y?2)6zjmŜ?P \u?7Gxu}ĿP \u?h㦹Eyء?7Gxu}ĿEyء?Y"ܤ?.H2?%vv?E ~ z]?9ر?Qÿ9ر?Ɩ6MPF?QÿPF?ZA>?Tbڢ?Bgj?$_:7''>T?$56n\?85 (Įǣj31%"?4O8,?1%"?~M? JF뿳4O8,? JF" n?Ea?-J `?Ʃ!l7;sȟw Y?,2zb?w Y?<&5?88п,2zb?88пOb\s?rj?IW4o?{Wˑ˭Q%-ۍʢ?@b?-ۍʢ?g|TݴQ?@b?TݴQ?>ͿOHa?΋l?w|}$k¿gr9?%,?gr9?Σ7ЃU?%,?ЃU?+?;`ǿNO?°y? t~Ba?5,?v⛿v[&Ct?U9G?p3q7?~hS?yҽF?~hS?е8x?ݿyҽF?ݿ#'?q5?QI@Ν?e?i ?OE?a@?OE?ڒh?'F{s?a@?(F{s?_ ?PId?qӊp 3Z?! %aUԶ5` 7 ?UԶ5`!xd^o? 7 ?_o?>,ޖٿ΀W? 84 te5R6>T]?ZDs΀?sE@?bR?΀?bR?#6տep.)b?:l%[?=z7?e?p~@G`e)N? 6z?ssTY\y? 6z?hT.A ssTY\y?A 3S!Կ:úy?8?xj݊|^j.΅?!Q4?/:Ȑ-!Q4?;- =l/:Ȑ-=l#v҉?Ryd?ƨܟ?$ƚo(E Iӕ<_?O ?ro{O ?0$п8ܦ俥ro{8ܦ俏#vO_?k‡?F?ze2"3pD? +Ix?J +Ix?(NZѿ-T6qJ-T6q俒t ?X ?{bǹ?i_? -.Fӊ\p?^֫?-zb?0#_?NKAT? g+ĿNKAT?;t?j/ g+Ŀj/ v'0?`U.Ž?VNզ?`!69s?!oJIRJ)E?,{<޸RJ)E?_f?<Q,{<޸<Q6?ҧ?Uf!??yQ?g,¿ p H9?@LaNC p H9?uS}b%ثԿ?LaNCb%ثԿWR?>G"?A0?kƓ?)dJbſ kJf?.$kJf?: (wEsԿ.$wEsԿv?HEB?;ܠVg$`?7{xÿlҖ?큎NC/(?3KD{ބyU'>s^ڿyU'>𿁂(&?Z⿬s^ڿZ/iȿ<?qja?:Z?L:Ɇ?ֿhQ?R? FX|˗LܿYDjrU?YDjMkB=wH4?rU?H4?+$.? ?J#?*{?;`ڿ˜Er?˜EH{@֊!F?r?!F?r1k?D_E?>ĐF?.UwİF:Fr?Ce?(4bҺ\gUݑ> h⿫U@h͟7 I?-֠?U@-֠?qr?)T?"r0'hz/(~e>翯BA?e>%S?1<'?BA?1<'?Yzƀ?&W~?Ñ~@y?EC%?B%ۿuLֿ?uLֿr{V?Kءt?>V?2_L?rS?$r^a,7aتp?W!O?HҼHusu-??%;9 ѿH%;9 ѿ1zpYI8?H8?'W@W v2W?yhkOB*3~eȔ?>D0~?t\_^C꿭k}-?s\翮k}-?=}?Fbxʔ Zܗ!?Ώ?U8Eߗƿ+ĿU8Eߗƿ,¿!?+Ŀ ?@,JwT ?CT+RǓ?Gx?8 ſ%Ŀ8 ſ0C,rſUwo?%ĿUwo?ɋݩ31?UFܶDA6 Qƿ4xG?\x}?[F#]3.z&MJ?ZZǿ|=)P?|=،7?hI+x?)P?hI+x?k=c!? ?[4#߭(llFԄ?$ҿ!H#PH?Ѻn?!H#PH?vE\?? Ѻn??ʋRǿ~g@?iApf %;.GƋ? J\?E8?AW?t-) ?}1{Կ' C}1{ԿlՂ7ѿ' ClՂ7ѿZFᎹ0EQˡ?}n~?}rc\\ 1 ?ŔOɋŔluhxl:ǿOɋl:ǿ@w%?-mʡ?MV+{?؜<'4w?io^,{O io^,{%C֫j_ƿP j_ƿG*M?l?Ҫ?.ۨ?/x\eT:r%?nieP?O?2ʻWt=Gz9llʿGz9l`qhbXlʿX`?BɒӢ?%?da[cs܎k7L{K[R5iѿ=?L[R5iѿ~䣄ѿ=?~䣄ѿg?6?$eĞ?Au? Hlc| ϊ;%1̿Mn:KTj?Mn:KT\ ˿eqj?eq (пhP ?c_Is?@FyMK~슿mͿX[eT?X[edFF̿K*#T?K*# ο;Ģ?P+%?G`?zۢ?Kx3Uy?xm=1?1пq! ?1п|`>r5^?q! ?>r5^?*ιտ~?ϑHw PЀ'd? ?ITBſ% A?ITBſ-ۘH=% A?H=IؿKc-?~PIʓN`?縿ʡ0s?ɡ0s23ҿX?XE fgbf?̛T8ˮ[va?KBalS#¨JCKZ?"¨JηpOԿD/NCKZ?C/N0޿ʆ֫?IFi?O׿$n?&?)󴿀(Qο*D@@rq@_V?@rq@ @1>^V?1>*?v|??yxlbOƿz?^@Xy ?_?Xy ?剟?Q:_?Q:񿢙? 7?}~ ?mA ld᢭8xP?{\'T?G\*n?z\'T?';?&eۿG\*n?&eۿyOA?N@?'S?Ҏ+gKԘ`F? `?n]O? `? ?Q?}shڿn]O?}shڿL^? 9C?uiYп1H?U}+?+^b?1ްVſ-Rblz?tY)@blz?kb-m{%ܿtY)@m{%ܿj+sG|?#-7h?8)//?)G9gѤ ϕ{'-?`?Ε{'-?A+ᅢ%Nտ`?%NտA#z*1? bE?^9с?W*f<ȥѐ@N.?| l?N.?&~;ȿ^qÿ| l?^qÿ)?4·TU-?3>?fZLK?L}Pz]4>?d{Vs,?ۋMΘ?d{Vs,?E/Tƿ|¿ۋMΘ?|¿t~?g?Ye@b?,ȧB?k뀥?u=u:?y|4ߠ?F9_x8Y1 K'?sR@ K'?A?sR@A?= N֔j8?]m??*w@ h!gӿ"y:E?R6Cr?!y:E?yh '?R6Cr?'?o!y՟ E?gz?w9lC?|jy_ćLG 뿠"yu,z\Wv?"yu_QɿY?,z\Wv?Y?d(˿B*A3?`e{?Ee.?{>n=)U )z|b]? )z|tAhǿެv?b]?ެv?˿ޫw?D++3%? H(1?kRlǺn?<{۲?챲]t 0=@? 0=@+nY忾Ώ0??Ώ0?(7?d۱(?3Ȅ?S+گ?1]a/M@?a/6ui_&ȼ?M@?ȼ?n%v?(ē?ept̿fҿϫ: C?fҿ%oyn4ҿϫ: C?oyn4ҿ(Y7_;+$?N5V?m?l퐿v?"wѿ\۱YHv?\۱Y3 {QԿYпGv?Yп>g a??ݠ5.?TmFi?t?KƿV-B?#IdʿV-B?ngȰ#IdʿȰFha;Y?m(/lJ?x?MQc?p?yȿk?xQ{ȿk?pкLG_;0|gxQ{ȿ;0|gȈMæ?\LA/Ŀ!Q4mÿFXP?4ޓ.t ?f9p|)# I"?$J;߿I"? 2Sӿ$J;߿Sӿef$?>1DĽ?~Awt2 s9#?Es9#?"%7jUӿEjUӿTi.DL? A/kT ?c$P%j~׿a6M"}vӿa6M"ȿ^6̿}vӿ]6̿ZWj߿>RD?r3i/g?0 Ciֿ\Pӿ\Piwſˢjzx˿ӿʢjzx˿Um%࿖It?ɇ믿"I#[:o? y4D,KDQ酯\DESҥ?\D{"H?")uESҥ?!)u'4/*&%]ߝL/jk?`m!Ͽۯ3ҹ?`m!Ͽ z`H~sԿۯ3ҹ?~sԿ.Ď? =á?Z?9Pkc.bnQsA?cR=οRhp?cR=οTϦ,ӿRhp?Ц,ӿ M|ѯ?Ϟ?'v&km¿?cg}#CB%"`kp?f\Mʿ.vHg\MʿVBh@p6.vHp6+ƽ?HbΠ?z+'k?էkYP*?0v`˿"\!῜0v`˿.~i?= C"\!= CDe?a?H?9^E)C돿YB́2P$?}V¿[Q"~V¿ PHo?X2hhο[Q"W2hhο2+c?y,L?jGM75->㙿y &?ܖ]¿&ܖ]¿F5?w=Vο&w=Vο0(eل?zr/?Tx?aA+ѿ$5?g[{?Ni ^ J?p%? J?,!z?T(p%?T("q8ܿMLet?.K@֭?"Pcwe RijOɨ;?2Г?ɨ;?f.v ?i2>2Г?i2>ߡFӿ>_?bnK?%֝HEb˘؆JI˿z)oϼ~c\䥉H$ͿjKvI)觥ؑ?jKvI)Um,m?]Gǿ觥ؑ?]Gǿ&̱5οi|?_?I}}ܸ?=1z耓]>m]?m?Yh?B@Zh?D^?fB@fb'6iC?2wDDޛA ozGāE>?^;dp?l ]3?^;dp?v\D?sVm ]3?sVTv()?:s`ه9`!PsBo1%)as<]߬g?asXI?5Wt2?_A_ƿiU>XI?_A_ƿtt`係c-?@ۿ7_BԿ,-]??z$¿"J͸>˕4?m! +̇@q>%?Ͽ+̇@t^HZ2: a\:p>%?Ͽ a\:_; yĤ?N8EoxG2A`?1Vg:_uV?W$?9_uV?p.cd=ݿW$?d=ݿj~`V!?O6t}8E %m.yi1:I?@u?;I?@D淿OuAu?Ouf> Boˊ?ugv/p㮤}?(a,? 㮤}?xYSy}'a,?y} Im7,?jJ?.^i?F`4aܴnրR?DwiYd\>DwYp6?n߷?_Irh]m%ş<@B?a"?'?a"? Ud(Vж'?(Vж15YEү?B6? "riL\\X?iE5fl?XkL?gj<\e"?gjP1ǎϰ?<\e"?ǎϰ?L324u~Ӥ?tX?jr'3@ߤ1yl?H֊ی?4ݰ۹R!gB?SIֿ=PY8? k?=PY8? ~Ijt? k?~Ijt?Xݴ)siKBJ?ܰ?D$I^'2b?iH#Q똝?JrC?Q똝?7B迪ruSIrC?ruS%C8K~4j?ȡ!A?=Gim%? @տia.u?iBm}׫NT$ja.u?OT$jJIw濻d?$d;'?Q(Sd?uҿb ʱ1Aڮ?c ʱmd"广8õ1Aڮ?8õt~JG俷FqY?Wa3 Tƿ5?-?rK$y?ݟ?0m?ǝQ?BEAQ?o@a?AQ?7?\?o@a?[?nG#Q6?Ta5b?=hUvUn-,tEɉ?d轀?i^n1?d轀?_tT|Jw*i^n1?Jw*g/f2} 't??ڡ1.n@,ۈ?Lq!g?A񨑿~tjG3H&?qLt2^?mY_?t2^?'k{8?WʿmY_?{8?Wʿn 7-Rilo?gѐƥpS󙿂S:xmW:6 ǡ?at.s?erN9ʝ)iu\uɿ?J?5 ?R ?KR ?'v?WD?;J?.un hɑv"xr?|?DhYwu?DyBf? ?,]m`? ?t)?܃Z?-]m`?܃Z?ұl?[y4v?i?լ?YZunj?YÑ?11?YÑ?Nq =?~?11??~6Hk?{j?&1#?$} ? b? L ? b?[;1f?f!N? L ?f!N?ة E$Lk?C,'?}H6W?EO`e?oI?dz?`[Z{?oI?dz?Uw?1مp%?`[Z{?0مp%?б$? {?%?5?cyt-Z?zݜ?Jz'c4;?MElG$ᅩ?LElG$G/C3?$??$?1 op?ץ} |A?Zqާ+{?lvu?|N?UPr?|N`V?ʹ耒??UPr?ʹ耒?~Ȫ[ٿ>?M?8?ig C??c?~Ͽ)L)K8ÿcG.?)L)K8ÿ?ncG.?n?.ٿxU2?|lQ~?0G?B={?:SʿUiÿ@?Uiÿc '?@?U oؿ ć$?==򇠿IޚUT[O|?Є?Vܞr6'WN{?'9C?A^9C?[Pٿ,aA^,a F[0?5BD ?Q~(PJt{B=Y?svs.8ʋ?5ɋ?IiKM?%SJտ5%SJտ8|yuQ?")?pCkA}qp?R'oS?ƃT 6¿MӦ BnϿMӦ{?Œ BnϿŒ?\%?bm܅2\k?JF?7Kɿ#yФ\Yzο#yФTmA?G.ȭ\YzοG.ȭ`A^-HOtڥ?-H$c2[cOtڥ?[c??~y!DY? MܾTeƇrk࿿Z.aƇrk࿣8y%nXgZ.ay%nXg`MW?3Ћ?ܑnH:8"Pb;Mk'n&¿+ ?9㸟?+ ?o )?rsEr^9㸟?rsEr^<!Ŀ^3M?ŽY W`c#PhOFz5(?'G4c?5(?k Sk?h(G4c?h%Z:8e.7"/?1;ߥ*nZ ̨_?sVt?lԡ~+W5 H|1qԑ?gD?gD[H5p心ZUҿ?ZUҿ (? ?}"&choۙ.Sc_u? { ȿ6D? { ȿ26~п)P8ҿ6D?)P8ҿI{'?DF?~H8L &~õ۔.Ҁ?ܰ?.Ҁ? C(ɮʸܰ?ɮʸjcI6?fqK?pؓ@<ҌJ]66.h?w1/?.h?-l(`:w1/?(`:06d~Y?w@"?Vv}`?ʣ}W7?,Oh ?_U7&O}('2?:N6j?+?j?zc j}fq?+?fq?ϖ8pҿk8T?4e`bTFp;؛?;?ϲ`6?6?β`6?N ǿUS ?6?US ?Oz??ܻhC}ezRt?jy?>!ieJ?>!cLF%j`?){4ieJ?){4񠿂#X?+?b&L8{=iuއu?Ʊ?j PEC="t?j PE7<=mTl9 `C="t?9 `Q?o +Ȣ?\u?*]Q'2o{kDHb:?$f62?8_?ǃA1(n$A1Ǜp?Xؿ(n$XؿpJ ?vZ?\m]tЅ?eBI8!̿O樂qkXʿO樂 :&?iEj޾ӿqkXʿhEj޾ӿaҼۿ6?1P?~C$-D?c}%:4Mu]ug:4M}p:6?j8xﶿu]ugj8xﶿ QKҿ_}!7}?3۰?=YËoj ?*wHp฿!XFHp฿J$?qT<~!XFqT<~򵿝ޕѿ>1?WE*nn?jsn[?H6ɘ?ֳ^\?̐wǰcL}wę}*?V^L/[̻5%?/[̻76⿣xiuvݿ5%?xiuvݿ@b.QG?dT5ޏ XkDG?N g-b??:?-b?(DԿ/誾޿?:?/誾޿ZH鿆àc9?7p@^(2<ޔ?8#ڿ"?OW وz"?AAvP7*@`OW وz7*@`ٿOf2? q qCAT+DB? |ۿJ2D!? (q?J2D!?1g? 4OԽ (q?? 4OԽD~׿"I>?_?]*Q?Flx?h:@}?&?,~?{^lC=ҿ C?{^lC=ҿSg?E T? C?E T?5d3ܿp( ?dj/?`YI? eA!Qd?ҹ?28Qҹ?< *?~0k'?28Q~0k'?b26 ߿F?rm?fZ>b?wT?@q?}]P?q?®^M?+kY?}]P+kY?v q1տܼK?J[[?oT?lm2z?.u?鏭7ĭ?zVOȾ?:u7,f?E`6O{?D`2V.]뿍M%H?6O{?M%H?CjbMR?}$)f?vN{`?e֊:ҿBܿ\tg?Bܿ7q哋ڿmm.C>?\tg?mm.C>?P3q7"? =֒? 1ҕ?`;zy4P*QѼCPӤy#ɿ}='?ѼC}='?lBOޕl?-~?xfp?487y #6PhV݌*6Ph=/ɾȿJ1p?V݌*I1p?$wht?9ﶿCV8?FK?K ?EĮ_מ?{m?"a?0?ݚٿ0?^pMֿ# ERSݚٿ" ERSk9Qת?_~6𱕈?z|?޸!?l %?AQ/ÿk %?_ow5ĿAQ/ÿw5Ŀѿ^ʭ?0Yio@cq?" ?b꼘Ŀ)ƿ?)ƿq<ҿX7AĿ?X7AĿ$՛RԿ9 ဣ?=6i1s? XXJCy=4-%ĿUud?4-%Ŀ8t0ҿKlE¿Uud?KlE¿ /ӿRڋ?i[?u4?!AdLx ?Z&t r\mU?w/B-㿌,׿sw"f鿌,׿n Ey?u60sw"fu60*})?_f L+?3 TZ߳3q{h?I "O\޿WI?G) ߿WI?Ed ѧ3(G) ߿2(fM$?Vi,hD?ʙ,&2%r1MW#iֹ7s?{i?+<?{i?~-ǿc/gusE*<c/gusE0xh?B$@?k|N4*})Z@?1ZUkߞBҩ%c?.33ҩ%c?O-ǿ@B͕h.33@B͕hYЧ?c=Ӡ?;r4 [h ? j0T䥡?*WkQj?S)|٧ZA[i@L@@0?cr L@@0?nGu.?S?cr S?*ܼ@4Z? \P"V?Lm٘&?+ρPąҿ+ρPp?yI?ąҿyI??)?SSEڂp nd?8{~ ?*{Kr? qV8Ϳ*{Kr? M׊~q? qV8Ϳ~q?z,~?n)|ߢ?Fف{k?gX8ɚQ?,5eC [CF̿,5eC܃}? [CF̿}?f.MK[?6-?ʝ쓭?#7F0YÿF1I_?~鷣?b3\?4IkqK*$negѻݿ;UBnegѻݿ|ŲS??;UB?7\O?<1}?<‹/O9?v-M~hiK#ؿip0߿qn-rhp0߿*?ړ?qn-r濛ړ?-詽?}Q΢?+QKo?}pc UM0l?O!2Yi?pSwi}ſpSrpp?e^?wi}ſe^?06?g Ӣ?c7s?J\:ra`P?6Z9@0/%|ƿ6Z9@kʡ?qK+?//%|ƿrK+?t?i|4(?0OH^=`^?\` ?% p?<K4*Ry?4kU?WCg'PXYWCg|m翱aǕG @'PXYaǕG @#F?oqn?+u?eBARTU{S?(+jQX ٿikQX ٿ~pxۿ~?~i?~RbǺ1.Ƣ?.}n?Ki?<v89k3o¿MQ7kſMQ֯?Pq?7kſPq?ʿllâ?ct&Li?RjF/zy#̏Ŀzy+=኿?!" ?#̏Ŀ!" ?*@ȿBtp?L w\o/LR¿K)K?r>ʩdOXE!~.f5W2?FΒ }6W2?1Wq?a^Ӊe@?FΒ }a^Ӊe@?W3xg?20X^? lz Rx3p vz;ހ࿓9 "?fCn忓9 "?cNԹ?)2-?fCn)2-?a?.#?0:? l0ak {KClu&̿@qWT#e^g&뮿@qWTM]n?"e^g&뮿M]n?@|ss? O%A3ؤ]V++ŪPʿw Cwye|kc}[?sЭh@RmŭbSmR_?:?ŭb:?/t޿RVf?S?U^?쒿YFy=?ޠ?\Em:K\E[##XQ?e?l:Ke?a阨տCX?5zL?#MX/B^N?3e Tx?rM@Ŀ@p?rM@Ŀ+aRk?5S@p?5Sޏߠ|?Vd@"L?"SBk{?Ja|k?!yĿA?"yĿ|#Gjq?0^A?1^ s*㏣?iԃ4K7ʽJ?~C?iOp-K7?8=n_D6ܿgy ?o_D6ܿ|+ŝ˿1`?gy ?1`?L(?ܿT?&oeb,՘ʞ!w?Eː=v+O큿q&jo?v+O큿&BԶC9?q&jo?C9?ͦđوԢ?=ht?Z{EiF@h I;?1ѿ ,?J/E? ,?sȴ?@O?J/E?@O?&,KȿӢ?,=A:Hq?1n9Ɇr8t?]{SӿZ:?G&3j?Z:?:-a?_S?B̔kf.8#5[@ZA>_S?5[@ϵ /??gw$?՞?8E z?t/}, Rk?t/}`+jk\E 491?, Rk?D 491?8Aݢ?g7{?~2U? .ϒ?]r(e8?l}x&ʿ6BPm}x&ʿyȿ ?6BP ?,Bۿ,HVݢ?M6Sy?0 ??F%#?<4 ɿ.}ܻ<4 ɿ".h3Ϳ>?.}ܻ>?'^;mۿ s?+fʬܲyԋ~J.?E~\?Ss? m\RjcF }?U +ݷ?7=w+ݷ?iхyQF?7=wQF?:;KBӤ?c͞"a?}%t z2ˑT?m4$[$J(q[$J(}즎~%šnG?q%šnG?w.00wp?vĈ?6ҼkQxmƦcX̿5ĿfTп5Ŀ-( ߿qU?fTпqU?RMݿek?V? mv13~ea劣ؚ˿lU7T¿ˇ7пkU7T¿࿵6'?ˇ7п6'?~$}ݿ%" ?Mt𵿾4p?T?`BYs(rC#| ^ v? 0+n҇? 0迨TE0dP'?,n҇?P'?ീ[?nu]cog}2Y ?Y^$׿&c`#g?Y^$׿.17濉?ؿ&c`#g??ؿ\:/|ӿ'<S?{;E\ȫ`?vq?bw_ɮ|k?bw_Ȗh|%9Q^y};s?{?*?:[ӿ*?툘o?:[ӿo?QbZ?Kuhq֢?f{*'߄ʉ [ϤXfe?ʍޑN?3#ȗN?:>ۿ\,?3#ȗ\,?Xzsd?8J]?t U%}/ٿ>}/ٿ-c|ſ42*?濸42*?h4m?'2-?$Ӟ S?B ?i=&K?Y]! w?FƿY]! w?G?@ٰ?Fƿ@ٰ?=o?񔀥?-)\?Lz?{ M[c1?J@?UſJ@?ǃ,#»?I{}m?UſI{}m?wZ?A v?JҦ\\Tb?nh?:o*?6!p׵\?bQu?G&ÿ5&MluP?UiA;5&MluP?HݬJUiA;JŢ}俊 ?a9HJE?ty?0 ̿ #?-!ٿ #?_ja$?0c"N-!ٿ0c"N促OXr?qb *$~+N@8ƿ*Wkw~Z)W+stĿ=?kw~Z=?uQ?~b?s?̼n:zX% :ƿX޵ ҹxYX޵ ҹuw¿20?xY20?.h}b ?#]p3֡?48K*1?CӘ\j}y?oJ{!? 6#?i̞0O?wU?;Ӯ,`;cC${?Ӯ,`${?A/G?`W?c/ТCMp?MOo?Uh^Uhn{%Z{P?^%Z{P?+}fڿw)ռ?G|,, }{HġM mΌ?K`qs""/?K`qsO5MLAb-?""/?LAb-?*)B?;¡?7s<&pDJ?j9gAW?_)đ`urg?_)đ"K,r!?_urg?K,r!?hAT?74?{H%kՂp??p0z?voá3}/dz? (Tʵ?^ +?Kr}?^ +?t0:C ։rEȨ?Kr}?ՉrEȨ?)42lH?% d?wNr4i??O(RbG?ʦW?O(RbG?`$?ʦW?a$?x ޴@k?*ܵ3?&^wxPWc6?<#?KQ?տ-x?KQ?w sFg?տ-x?Fg?*# ?M&w?Ν?B?\/IyvBRє?ɩg?îq?ɩg?榄igJWR?®q?gJWR?7?Dm"ɡ?SJ10֨?.n?~>b?Iɗ0c?$?bk?kv^&甥*kv^&nl܁Mۿ甥*l܁Mۿ( l?M.~og Q?_J(?g?Nlh߿ZuP?Nlh߿Pnl.B? gԿZuP? gԿtGB?Ѷ ?ϙTp6g>䑬?b?#?DX+VD?DXFB?#= +VD?#=  u5ҹ?fݥ?5Gli?( {?UT>Wk$?Y4z?k$?qW+?nЬ?Y4z?nЬ?BA?5Et?|p?jUcz?-t?eݧGir?Jا?Gir?קx?e ?Jا?e ?-?3C?Q'Իp Ŀd?'?Qj&ʤOcȩ-[?JB?8~5_HIT9?8~5ݶ@-`HIT9?-6=@3?KΕO I(+79?*x ?=T ޿kիk}?=T ޿7Tq.?aݙfL ǿkիk}?aݙfL ǿ&7!?Iq!_47?2h?~pL9ѿ'_~pL9ѿ"X)]fOSg?'_OSg?׿@qY?YEωmVb1?Yl?W֦пk2BAW֦пuhZ ?k2BAZ ?ɑDԿͦ*O?NI|?Tǿ? û?c>?;}K淿-?WS?d'WdrU@'`W\?'W'`W\?3- 'آ?.(cI?jS񬞹@?\?=zKѿd'K=zKѿM7[?uIsU?e'KtIsU?cC@ʢ?l6U";&g ~?)n'"Iyǿuڿ"IyǿY I1/?O.D?uڿO.D?R-ο|Z,[ɢ?6ÞC?#HՐǗf݊'p%~? @ʢ`!,3Jǿs^kٿ`!,3Jǿuqj? m?s^kٿ m?"Ϳ[Ie?f`ʡuu+İet ?@*Dҩ?גoM(K]D觿 YƫR:zn?+[?``?+[?/N65K?aۻ!濈``?bۻ!濮|?B ?@k4#?[?c@?[?x6g %?IKٿc@?IKٿݖ,Q?7v?:}ȯØX'浉AI ?fUA;?G :?7gAͿ/B( V7?15/j| Z}[ޘDQuwпyIe}?c`?yIe}?ٓIA`'?fed`?fez*?z_#ٰ?L0?Bf?P~+h?;zS?egͿ;zS?`?d?c/10@d?bY @h383%@c/10@h383%@02@dʗiz?"地'?jz?wW? ?aXxi?j ŵ@`Xxi?[ @ET.8@j ŵ@ET.8@@II̍?C&И_!¿=|kѬP?+?gJV, DB)7? ߾G?YQڿ{?YQڿb0@4 ={?4 = %ZD?GZq?DP"PL-ciG?8iD^)?l2;?l2;4?$ohMV:?$ohMV=? ?0B2!!1_ ??⸠?jV?eG?=WOտeG?.?3G?=WOտ3G?!?9}%?_tv䕿Yd?s8?OIvV?H Jz?~RٿG Jz?QWT?|.C?~Rٿ|.C?,U'?]n?,> TjHնd!WI2?v9E ?lvc?O%_lvc?@BJ߿O%_BJ߿@ʹB$|?ֲW==zymV?3A¿P{?+ڿP{?l=?ZeԿ+ڿZeԿ%f$m?C'dQd?ҳ?F58M?ksv?_9 ?2["ьb7ӿ1[duw?VPqY"ьb7ӿVPqYMR?^Xn?eEhSA?eO=w?Nz?Zk" YԿZk"إ+? Mbp YԿ MbpB4~?!d/q ?#Ҳ?$?H5|˿Xt?4~?ՎWx?Ecs 0 ea=?jȨڿ ea=?ۯQ×2?jȨڿ—2?0 '?O_?'^?G|Zvۺ?;>_|:zJDU;?PVXٿJDU;?U,ҿN?PVXٿN?\r j.⻿̿yAS%4?|'G W |'G ?@XԐ|?W XԐ|?%K+ ?#N0NGTJ!XIU'?TJ!X"m`8n`?5$ZƮ:?*X?)Y "ο+ƿг\J?+ƿꌤ^ȿUd?г\J?Ud?w׿Db^?sQƝ%?Li~ T}ȿNhſ(}?NhſO1ƿ@W?(}?@W?6Zڿ%,,M?nɿ-Peu Xfw?7K?8u?[0(ΦƍCZu8п)Õ?8п@' 5Tա?4șݏ+q?r~sW[@)? ?;?kbDA? ?;?:SK¿G=?kbDA?G=?HY?)r? u?锿>2d?^B?"t??^B?ߔqnĿ\iR?"t??[iR?l4˼?Bsŋ ?G?g?q ~? m?7b>?Tl?tEʽ?pOT@ꁠ?Y @ꁠ?7L@Жs@Y @іs@U(@93?-Xo?Hm?D ?L9?"@w||i@"@{\.@_g/@w||i@_g/@4 ᎄ@AI?zP $?^`Q?s(? ¾?Q@*S?@Q@$(J@(1\@*S?@(1\@'@FXc?gr=?o??\4?}?@~7@ʧ1@@~7@~@k@ʧ1@k@@:5d?YV?Ÿ֮n? ?DŶz7?$t?Bh?vj?^_r?~ӗh?s8@?~ӗh?|1t @a&@s8@a&@7r$`0@OWx?98t?>}?-c12E?Qi_쿒Aو ?P>@Aو ? rw @X==!'@P>@X==!'@د`!?,]?TMB?|QL3.>?F$g?hMۓ?ё'R?Qezё'R?F%dHQezdH=?^ ub)?-7Q4h?l5?D\bٯ?al?f\g?@1"f\g?#5%AJNh@1"JNh@e?i,? ( ?,z?gOq?Ջ Ŀc\gՋ Ŀ#u?zH?c\gzH? xU @犠q?qsCÝ?R{~?P8?l̿@ll̿eTG-?ը+?Alը+?aU% @~=Y? 䛉{%OXy?:u?]`3?L"1r?~l:%r4?43Eqe4c*?dœVkRѰũ?4c*?QѰũ?s"?rtYl?jҝHesg?->i?uz5п.??5п{QWx\ .??Wx\ -7+?(Ԍd?^9ʷ{%?Dp/?E쁄rڣy߿rڣ۱\䶿%O?y߿%O?/I?ʤ?~1Kכn?/&4??6ew;;[ď6ew;;,?[ď⿆?}.?ʪ ?9a\G, ?z>?^!?;X+]x?3% ?lw?zLb/?orb/?=4'52?U?or񿃞U?Ks?{;?Y׌?%szE?"WN_GM(Կax'^?Ŝ>\?ax'^?~5L?A?Ŝ>\?忘A?p}? ?v?X?q;F ?89v|Ӓ89v|cǿ\I_?Ӓ\I_?YU?gAd ?#r?,_?,W悿OKfT~?sLvV.ĿsLvJſGey?V.ĿGey?ң=?>R-(?.Vî?Bs?ZC/EW?^E?q\?x!NԿ>1N?t˘?>1N?$uEXt˘?EX- ?=er+j?c1?0?M瑿65yOnտ p?|D? p?ǭ޿:NQQ2.9c.ɨMe?=V 9c=V p$.II˿ie8NDZ?PHn:ꆩĿ2?U4?kAJS?fK&񾿁&E!?O~QE!?U.U.vO~Q.U.vA<R8?@ ?Zpo?VU>۵}ć Ϳ 0,s?' 0,s?O\TL翤'TL習TvZ)B?w ȼ~Y?]ն].ȴ?h:}>ҹÿh:}(KR?VR*x>ҹÿVR*xlq; ao'?uv?b.W?]kdd'wĿ]ko`?Qbdd'wĿQb޿4)?!ԑ?s97ΑxIڛH?,٣ds?P9)O1aIP;J?Wb?)(+?Wb?S` QGeQȿ)(+?QGeQȿJվr?_r^?%x2g?VڊRߕXtcc0?:?P"?~:? sɉ쿽6aP"?6ayaQ?I ?hf?3򙿍{_唿W,?+}.?W,??oU0 Gi?+}.?/ Gi?2IڅJ?eg͊d?^!ǶU=^l\?'-?y?]?'-? P7Ehy[pٟ?y?y[pٟ?[O( 152?eKs7A?r0z?vD#?D ]-oIa|?7Jg?MԳA߿9$ɯwǿ8֤?9$ɯwǿk@}տD 8֤?C 俢t?3y?ZNΪ@8EzC)U?xNֿvaHJJo?vaHJ&-.ۿ&}˿Jo?%}˿A?6QV坢?M"tq(0啿yNhL (p|n?*ǿoVC?:m,,?oVC?,m]ٿ;šQ?9m,,?;šQ?lŶtŘ?^~lo政FtP1DƿАY@E?Ϲk8 ?АY@E?953ٿ5'P͹k8 ?5'PvFmJ?͌ 8S|s4_q?8H èZ}VOYY?=MTV4׿=M(/?俬TV4׿VO1?k2z?n$hd{-n?%[㳿˚ ׿>6п˚ ׿VMDƿetڿ>6пetڿ;Pp?~FuN?M>ؓN`QS?1p}H ?!Yxf}H ?>[ſKby#!YxfJby#Oqw?_RMQ?> 漓nm?s菿hJ|?6R gJ|?ĿEA6R EAPS>?2UOX? R}sQ`?D5FWJ?s,؊` F)'˷u?\1pك?m9 ?\1pك?ӿB}$mm9 ?C}$mBI;?aSI?R1h40i؆١I[?) ?>?Jn) ?>?>~տ9Lm?Jn9Lm?ǤjE?j6? S G?8 $p[Kp4)?)UJ)U#`?~=nRJ=nRB.?f5ٖ? :}q?y?29] C-[c q?bPȿ#:1E?bPȿiPX?F#:1E?F,pl?v/?;Y?-8Kۊw@͡?^%c?۲blw$ehvF@-C 949?-Cjq?` v 949?` v??)A?rf?~,9YtBh?Uǝ?*D*?+0?vJCWܿD*?vJCWܿ?J?O]Զ?☬COԂ$ z??k֮tG?6 @|9k֮tG?|9jɿ;+?ـ-?[(XW`˙?EL?9:9?9 @Սؑ濩:9?ԍؑX$|4Cӿ{r?NCte&ȿLFEl? oF?>ZĮ? t˿?c(?7o?7>{@Jeo?Jeo\ տvkҋ??KϿx7Y?Y7x?(8( _(?(80@O|J( _(?O|J}tizPa>.?OF)E?޿ߑܻ? p@A2z?AjfD&@LR2z?LRD`o@'?@?ڦγ߿j-?w¦@EELj_?EV.ˣ'%@iA8 ELj_?iA8 j9Bg14?J$ qnm?(E?*D?(E?/LAy]迎*D?y]迠i?ۖ1jv?:|-qSv?Om#?(7?%,Z?: ?&,Z? p%Կ: ?p%ԿS?:?E?Ж!n?څ6m(??.,Ŀ?w)^?Itֿ.,ĿItֿ@ݻ?kI7=?\y7qP܋v+H?se®?LfxS?))˴οLfxS?y;P?}ܿ))˴ο}ܿk`zZd%?sܥ?Io}1 *?}SlY(?-PΤ?ץZs?9@?h?i>N"ℳ2k&ѿO"ℳz{(ῡG?2k&ѿG?86ERMOz)?H?uwI?_豨?aoGѓ?rĀIͿnGѓ?YV h\ ?rĀIͿ h\ ?Jj䊛 ?a*U|cg?|?ٴ^#$̿xSd?%o=VxSd?YDž~qW{2?%o=VqW{2?DU/^&]"?]9OsaM)B?lpސ?<҃ɿZP&?1ZP&?CJݹ<)$R?1<)$R?ؼk6؜c{?D/A?^"Z޺?k ?uv?(?Nٲ?Geܕ?lJr&?)jʿ2D߻?)jʿSkפN[?2D߻?N[??c~ܢ?=;ډq??y?Õ{?Ѩhp?xDi_xDi4(|{=?_{{=?쀘뿛oR[?>v&iv~u/ڙ?8Rq?݃ٶ??/*>?@?T{U?h(s"9 Ɠ?Gg ]s?IJ`?QX@-񕹿QXXS}˿T?@-񕹿T??YSe8?6a%궿bX?Ԟ??QG 5?GHVÙ?%?ke*_8y˿ke*_g1ap6>?8y˿ap6>?$ Lq??eM``7?8ٿ?ni !?g4?g4N)O]??O]? 4S"? ?!0 h?4η|T?0]x?9>L>?+DȾqv5;+?*DȾqfgpƿȟ܍v5;+?ȟ܍xd*?\?V^5?W{?IV?wcfte{#h)?wcfteAYſ\"]ne{#h)?\"]ne#M"Ǩ3?T8*T?Z*2?ǰޝnG)|f?p $?&_r?f`ӓ?pn/s[bտ}|S?s[bտp=X9?C?}|S?C? ^f@?a&w? G ?\0G6?ﺨ߿?\#ǿVE??\#ǿبf?kbtҬ?VE?kbtҬ?̪ͅK?I1͢?"gr?B#*?5U?qPXk?Fv?A|U4?Fv?2U.¿6ܫ4.A|U4?6ܫ4.QD𺿐c9â?fj&|~?.`2&?D;8W?WXپ?_‚.k?g0=?_‚.k?Vޮrug0=?ruM֧?>Xp\?e|1 7? ?/w[yRn8#?7&Ԡ?C ~ V*?B ~_?moJ!ӿ V*?moJ!ӿ@6{ @7?\wAy;j}%J`?73(;H?]41q?]4NZL|?᛻:ӿ1q?᛻:ӿ7s@V?\YSo?zX?[?۵SwƵ+Z?wƵ+A[)WƿɴǿZ?ɴǿ~k)?>r?;4Geh?7}?Ʌߚ?Ծ4ÖmߴҐ' ?Ömߴ_H㦫i3U/PȿҐ' ?3U/Pȿ`4ˇs?~7o{?1zq;VN]JlÿoQ?1?oQ?=Y?:1?:XM\?x?1R{? :+y΂֐,Ŀ L&ߕ?&=^M? L&ߕ? g Fs?O!&=^M?O!*+d?L}p?j#:ﳿYU?P?4ӓ?Id0c/ ?zl3q#}¿Z?EWnwgϿZ?v?"@j iEWnwgϿj i-]7"?-&ܠ?1`Q!s5kZy 枵@e?(SQ{ȿ@e?o)(?B)SQ{ȿBp{ g?,0?Q&gRPaAJ1쇿O,tZ݌^B?*rfn^B?YV?@ƿ+rfn@ƿM?f?-o4#RP<#S4i8ۂ0fh3BS_?whxh3BS_?1?oNĿwhxoNĿ8hm?\*zQ?|Zatc?+6?ED?*=.4V?bmyDD ?Okep?@?܃UƂ?`D*?BR)9?`D*?Q]}Of @8FͿBR)9?8FͿ򽞹?Z?XEC?a[e-V?5?% n2I?1E ?% n2I?x>Ǽ?4B<1E ?4BI:m?ibGko?v9ÿ1tWϿ埜?1tWϿ@*FuR?f밿埜?f밿Mct`{g? 0?I23?P>?pm?N ӿʾC? ӿ(?rɾC?r]@%{i??xVYRĔ?(#ӳ(y?gr?D P[?}t"4U?;sY s?;s鿖Jd?3z?Y s?3z??I_?qh[P4 |cc{vp_vv}?菀+0Yw^?菀+sA? < `?0Yw^? < `??ڲ?_Ȣ?7xNg?7OrZ?IU?F鿧^ι?FH? ]Ro˿^ι? ]Ro˿ /UvTő;?jk&?7BUe+?CgK?.C?@KI7gL&7$@A @?-oԿ›ٿ?-oԿaQL@ySg›ٿySg뿅HǮ?:e"*ܡ?|q?C271 sV?zk*?39NO?39NmD?$ė⿆O?$ėa ?a?+{~ǩ'?'fi=y~?}#!?uwS |?Ҍζ?twS |?aƥſҌζ?ſ? ?;-?$N$Ϣ#?|Q7P{?cz?:"EۿcԖ?ܻ?cԖ?-vrο2{ܻ?2{13P?"iPz?R&}hI՛?8-4q#ڿ-?.?+`b?-?.?Z*mȿ $+`b? $r+0?&)p?Pev!g?7 ?-$"1W?סٗPgU.??n?IA&翳 IA&wJ<]?ݟmv ݟmvU0ڿ9x?&n:K~A'1n::,3?+5v8y?2Bmjܧ2B ?!`njܧ!`59闧]*e"բ?ZRng г(cUt?FB]a:?)xzZkѿ).Е ƿYOxzZkѿYOCδ?L[~֢?r)L&DJIeQ?dacdX?ieл(̣пjeл¿ZxTox(̣пZxToxa`6?y.p3?ͤpC Hf"?'B>pD?֬[y?;u?Iſ Ln1q?T:r)?:qNt?j`Ǩ?:qNt?*I.߿ّ_j`Ǩ?ّ_ZhEӗ?,u!q?ekJ//Gw2? ff8 4XGkϿB?9,ſff8A?9,ſsŲ,zϹ?i?N{("6m?6'}=?UXJ.y6옿VXJ.y0\̿Ŀ6옿Ŀ(=.b࿥?eI?01 T9g+Zí_ ?vB K?Az&43act+c?|\Zܿc?lt`hJQѿ|\ZܿhJQѿrtn ?m?$Mr?fE?O mпfE?b4d1)ؿO mп0)ؿ[]*WL?҃|}ɍ?$kcPѿs5C-\ѳ?va$=?-\ѳ?jFκ~ٿ]Y~ҿva$=?]Y~ҿa?N¿G?`+O?%[>ߤ%uɰ[? >aG(?7p?p2j?bsQ?p2j?2O@hasQ?@hKs?u-? 2@?4Pz?QJf?B^9?>b?B^9?IOtC^">b?tC^"T!?{*$H?21c?(Utt0JlߢϿI*?WbAH*?QZݿ39ֶڿWbA39ֶڿm$ÿ;ޒC?~Dih[?΂}M}D|FP\@9h8ɿ3,Ө? 9$a3,Ө?G^ymݿP!ڿ 9$aP!ڿ^Dt? q?&.Mێ?u֌¿P_?g?-?h/)$t;E?2?2Eƿbk??bk??d["p?RХ?ۑMƿA`ۑMƿzi k?A` k?Cz7|?i;zq?{Wr?Dܙ?/AW56j? NP? 5 Qſ򛢿 5 Qſ*U=?򛢿?]?iҙ?T{ ]~HcG?|8#?ZSZ]Ώ G?ΞIq?Ts r\ZX|eFۿZX| !(}(?eFۿ}(?q3y? &ߡ?Nʅli^?]/U?>o$N>EʸXhҝȿ>Eʸ񿉼p쿉Nv3?XhҝȿNv3?]ķA8>?+Yvx? 9F<ᇎ?xV aĖԡ?Ћmr[¿ЋV?oZ?i]?mr[¿i]?+D?g7O?;4A_y?#*L ?8az:?橵W橵~>~nj']?W']?SNӹ?<?wsxy#Cθ]k^D?C[蜉V姿Z󏥿??~pqȿ~-t[n࿛B)?pqȿB)?&X\?F~w?qꜛ:}uO\bz= YQ+@?@Ǿֿrx3W˿@Ǿֿˀοpc?rx3W˿pc?Y%?ܾۡ?8kՇ?W7%?:.#G? 1-PD?BJƓ 1-PD?i'l?g?BJƓ g?ɼp?ջߡ?%*?ŘJӈ?ق&悿)?9[֧?=&9[֧?ɑ'/? cE>_?=& cE>_?&?^Q6?Ox?YW(UA 8?Z7?ItWzލ%7? >h?He&? >h?( =2ȿ"?Ge&?#?D˜Is?Rq%?9F E?J/6@<ZCg?BY1?X5?AY1?pzZ?S?X5?S?<00?|e?Has?eV?<}ni64rnȮP&к?64rnm$;o?/,?ȮP&к?/,? ' ?Gc?]C&?d??PﺡF2{ьF?po?TZH?6rB^n8 ڢSDԿu8 ڢSDԿ"Z+@XC,>?uXC,>?n @8G?y?kMk?Bdb<?#쿭KtT¿ w`ڿKtT¿oz?b?!w`ڿb?ԁ#K?whI|?맇?ܒ8}?T~kf?/;7ϭK^BQ?8ϭ,wqxPA?܇O ?K^BQ?ۇO ?Mxɽ?qs~?g ?9M|?yV?P guM wu4dr?M wuy&V?RXǵ?4dr?RXǵ?]7?Mr2]ơ?8?1kُ?3tQ?Ӑ?c;_?4,(? {G?D9?}n?IU$~l(?IU⿡l˔?ٵ(?$~l(?ٵ(?㒀d?@?LO?$J?pl?NH?ϷW,e?tH?B{mbcmQ\?Efbg׿bcmQ\? ~ iXm9?Efbg׿ iXm9?B-?;d̡? Fz?Y,19?lEɋrlǡ?"s.?0?0J/d%6?b ~𿼕Qh?b ~Nƪ!?Ar?Qh?Ar?ZP1Kjgǡ?W6Qŵd?e8򢿲'5? sLj| ? s忶LUD?',?Lj| ?',??;Jf9?3+ũ+L?_Y4m#7 ?akˡ¿ 窾Tпakˡ¿#O-?ƬM?K>jȏb¿K>jCw¿px?ZI-㴰Xע? ?/mD^'p>w?gd{Կw?ԅ@/'?hd{Կ?/'?`!'7?K`?)%[]r,ѿ TR?5+-ѿ TR? 5X?ʫ|?5+-ѿʫ|?˵3#?5CS7m?ySԌn:BL K%8zjBL H#?WK%8zjWXE;_˿hI+)?c9j?mHn-(&!iJb-(&6(7[?s@.:!iJbt@.:ߡjoѿl}zu?嫘3)vZ5?6>-?ن4mv㤿w?bqą?/2X* ӿbqą?')S졲o?/2X* ӿ롲o?4ߵ>dD?Rlx(I )xcLq]?BST6q!?ZRAcÿq!?WaPy,?ZRAcÿaPy,?pY?QRXQAOI3"?\F+?ZGs\?xYGs\?65ß&S3?x&S3?Mӧ?-?SG SQnoа?2fbr?+?%C+?"A ?%CA ?&R+?""d?wA߯zoa[u.?~0?v!0/m[pK0ۀ kѨ?ĕB\?3ж῿ĕB\? #⿍m$Rj3жῌm$Rj  9?. ?(sPtwr,R>t?]IG?tsw?!NC?[X?4ef[X?? >Y?4ef >Y?1) ?)?c"{u?e~?cXu?i?Rn_?h#qͿSn_?_aվ|U?h#qͿ|U?ÝeE?[s ?ʂu ?;/[U'?}5Ps]?o*4?X_y?FTُ?>GoDoy?.҃lUr\?y/ٹ?Ur\?M+R濘Ruy/ٹ?Ru򼿀ŝ?*/?^f^q d>zLl[x?Z ¿4 @?:w%v?5 @? yvҿ 5k?:w%v? 5k? @?Wۗ?<Zx@Ox?qjʿ= ~?Z|!?= ~?)Y3x\AӢ?a19q?1J? owċ??POտBlcMj/Blc08Z[u(P Mj/v(P 3dq? ';?ߜO>?YAS1iyh"ߎ ?ɃQb=?܏)bpt3?1;MX @Ú p's(?Ú Wr`}{??sfq's(??sf p?;6T?S+q N@?xyNw?#IxܿEr@|?#Ixܿ(Ix5Fr@|?x5^ccU?˨?S+t 5w*tq?8?qƧUƿ=Ad?qƧUƿ9@.(п=Ad?.(п@vw 颢?;Q\t~yhv?88?u̵ſ$U?u̵ſd5q窿 bп$U? bп)\BbKƤ?qF|+exs?t= ?\pno9ᘿse?OtWJ MsܿXJ<\~ ǚF`? MsܿǚF`?gSΆ㶥?rz𿃻j?r^࿃j?*Su`r^`pt?SyfT:FoR?SSɿJ$?RǧeпJ$?*ZQvPӿ#9Rǧeп#9\_ᶿcйu?RS ΉpP&?C7sȿi/xJ?/e.3οi/xJ?RL-ԿZ/e.3οZCcKrլ?A^ P?.x?MXjA=Vٚ>mxSͦ?M+K?IJjIJ6 ǿ?i?\dsk8W?`]kq-ZQȷ?yC?DZ !]GԿDZ #Xbٿoy? !]GԿoy?Vn/}I?mllqó|sRu| VkS?: Cƺ?6s#eԿ: Cƺ?uпz?6s#eԿz?3@*?ꚟʈGlh@6X'zӥ?E?jA"(AӿE?Bп?jA"(Aӿ?C,7?ZXѹ?_o)-ׅ?ѰZ;?f$

BFxgy?xW ?!:UKwW ?t[?d=O ? :UKd=O ?<v[鿋ʡ?wc%E3erCU eLW!)?y ,珙?-Rƿx ,珙?dke'ѯ?-Rƿke'ѯ?D~%XYΡ?óG5j(u=tRЖ|?ZQ?; pƿZQ? Z0 m]Q?; pƿ m]Q? MM*Q?@?H9Iq נ?U*?,4=x9?U4?4>?z Zƿ3>?r(pp?{ Zƿ(pp?:@>?IJN?iAc7k?W??5?ݽPӿ?5?GAL鿚,-?ܽPӿ,-?hWyw4?f? vBU?P}rkE ?mH/+9?O#d?y8?]Ϳy8?(ӿy?]Ϳy?>r?K ՠ?QI_Tŋ?1KKg?iFM?M"?9&r?,j hk%, ?&TEhk%, ?@T:?2}?'TE鿤2}?9R.?p`>B?!nK)?۔UN?۰LyǯY?;x,+?LyǯY?KXt_Գ;x,+?t_ԳgbӒ?ͅ?P!?4ԕ}?3q(?|&f?e/^]?K?4D+?=ۂ  P? lGwIQ?AYN&?:SP?M&?>=3. Q?:SP?3. Q?h??Ϟ?̝7pϫRU{~HaKNa2?;|?KNa2?О#z/}i?;|?z/}i?+a?И`?duga>? :\a?B*KFS?c2?~dI?b2?%(_ǿ?}dI??yF$?lz?wF?,im=mP?c O0?HbQ?ٹYuϔ?HbQ?\2~Oɿ¾a?عYuϔ?¾a?!ԇz?y8?_@'B Mn?<[V?hZ-?+K[?uR2~D.DaU,aU,ܩ@6 ??XgiQ?<\?-"ÆG}zSo 4cڿ!^ǿ4cڿuj?~;"^ǿ~;Z@O޿)H?ubpc!L@a?*EWdL7˫uҿ.\¿ o3ѿ-\¿ p&-יDԿ o3ѿיDԿ3KܴLxX(+?po)Aΰ?]h?i$J.Z{T?&FNCÿ|:'8?&FNCÿ8\&Y?jݿ|:'8?jݿN?Ybg~?بu?3IWR ĿpCա?+?5NY*2_*a?ڏ=?~TUпڏ=?e?u9~TUпu9ݭA˿s{ܡ?ҍtb?I HiF?|i,rr?kdNǿ|i,rr?='7kdNǿ'7H̿S̡?F7>65B֎Iv?Av=1L硿*T) L硿Jr]eʿ*T) ]eʿI?^vϡ?//V͡p\Jr?~{. @F|@r2֣@F|@r2,^\)Dƅɿ֣[)Dƅɿ!_?~d|y?n~y&ԺU?.m͚uMa?bԗq32zTe政\haD?:%?>:%?,] q?\a>@\a̼SpC?0~ӍTd kQ!h?R,4V?ajɿR,4V?D[T$GܿajɿT$Gܿ*p28𖶁?%|rn=q\~ڶ?Fwٲ?u/ϿFwٲ?#¿2@I?u/Ͽ2@I??>ah?9Rb{t_Ǭp>S?QVد?2ExͿQVد?%Mÿ?2ExͿ?}D?쇁?8IA? $?bP?bt*?+oj̰?'].>3?mf}?GŸ&kJ?GŸ"j,п&kJ?j,п.sΡ?E?OW¡뚿p &??4֭޿:}9п4֭޿CSC:}9пCQ*d鿓V?g?؇9?@S3֚۷?*J? q%&ʿ q%Ee後ҿL?&ʿL?"+?}*k?foK?*9h6Ǔ?n覿߯2Jɿn覿 ̖hӿ)?߯2Jɿ(?|eS??Zu&Ae?Rly?/-n?GϲSx^g1)KOc~п2)K >9 <2POc~п<2P&\J k4?~{/Eܮl!- (QD)- (ClwܿmnQD)mnMb~? c?C9-?*0~_2Ah p2't*Ah pT8Q޿f}q?2't*f}q?:5n\ÿ>sI_?1HH?RӋ?8?|?¾]?!O_@g-"?ng-"?z|V/ @kA?nkA? vNXw@u+ڛ?2?txnG?M:@2ë+?%eP!q3ë+?9[$?iC9?%eP!qiC9?Ж⌛ @HBC?[2J#?JK%[I`ٕ?-;?c?ʶ)ܿc?{9B?E ?ʶ)ܿE ?W7#0F? ?+L? *?EMKJf F?=囧?A0pf?YgܿA0pf?<fY?%53?Zgܿ%53?%?@|?)'KF ?Ŀ#?g|>?^q?\ +ۖ?\,ۖ??m"Y׿\"Y׿¥XV@1١?%I 쥴?Ly |?@ĉ{?V>k0=ϿZ;?6ԿZ;?Mѡ&=(V߹6Կ'=(V߹t?L?4|Ae?ʊ/"pDã?8ظ?YwW!h?j:$D2a+? {k?ê>B Ŀ {k?L t wuê>B Ŀ wuA|?eVMṃ?T%xCp?q%6Ũ?rτ?24U? 4"w?24U?x|qA#;W迆 4"w?#;W ?"]٣?_o?>?B"۩:6l8IJ?eF?6l8IJ?L?ɿjWÿœeF?jWÿS? ǣ?T ۢ?4C#?Co|Rۿ5?R;?5?dzVUƿ @¿R;? @¿ yry?Y@l?Q8??G{?cd?MrDr$v?/g|-?N?"^)W98꼒Ѻa?)W98wW>%c꼒Ѻa?%cVbi)<*r?Zm%2x?pw[$?$M?1HI^.\^?HIs s_`ڿ^.\^?_`ڿz=uen?Gn쀿)kCۈ?'8?FyU} ;˿P ?} ;˿x^5ɿb y?P ?b y?+ѿbu]?a . X?9@?5KFǧpʅH˿mgT?ǧpʅH˿sƿX?mgT?X?E.i2lпl:?…Ψ̿h\'q?P?Oۆzʉ*\ \Ft?ƘEx@^JEǿX]O^JEǿoA??Y]O?"l@6[m?3^=nzr?sO<ؒ? q?/?F_/?-?dX̑?F_dX̑?>/?C?W=_s;R?XI5?qqmߧl&~*[f$/So߿>d o߿'LFc?п>d пvĞձ-d?jzۓrO_˲O㖿iaD￰Puտ[R =Puտyr ?n-x̿[R =o-x̿.$X1? Kt^uMDP?𔲝$Hv*ʿcqDٿcuO?P\m͘ϿqDٿP\m͘ϿJ7K(xTq.?>)'u N?KJ&:z9y̿l\=dŸٿm\=UAF?ǝm[ͿdŸٿǝm[Ϳ_:Rp):{Ң?AAn+ ^Pر=?]Ф R.?jvTUPw8xrW׿M?xrW׿B@ܿQ`还M?P`/Ìȿ4?_[ahSNg?|̲I٢ap濒i׎ȿ vc?i׎ȿJMʬeyP vc?eyPtfC?sZ6,?92v3[4{ĝ霿&LN%ϊ?&LxJI?6).׿N%ϊ?6).׿eɴ_?#>.?ܔl%zu(fS-Cæfٶ6/e?fٶ?;-?}ӓOֿ6/e?}ӓOֿ'd?Z_P"3?%@Kٿs}߸?>%@KٿZN?S) S?s}߸?S) S?ip1@3m !? "yq1 N'?e<\EMϿy?d<\EMϿ$?/v9(?y?.v9(?L R?Kkb ?|[E fKio vN??Ƿ)} Wj?ȷ)}ᓈ?ba- Wj?ba-f?So?I‰B"xkqv!+8o?R鸛I;n?R鸛P%?e.I;n?e.g6?;8?n#q?;']o?M?al?^H{s2҅6{ VϨ5?S2)Pjпz$¿jп's@+4b4?z$¿*4b4?0Jx?Z?Rϑ|yxQ+TJӂ?PJ|CIƿ)ZJ|CIƿoY?uYz ?)ZuYz ?n?% ?}{uzպw.t8zt< 횎?%Y 5$HP&~?~P?Y 5~P?T[? W?+׈U|(ɿB̔?j1%?)Rm Q)Rm 3+?oȑ+%?Qpȑ+%?{:`~7?"<?=%qPI^ ?auG?q}곿IwIB?G ڿ5)kzϿ P4)kzϿ,F@.N7u? P뿲.N7u?V8O?+0-wП? yTK[02SĶXO^ſ8-eϿE t8-eϿ|&?q%C?D tq%C?8?|n?_YzxrkfQ;*zNbygqѿNby#N?ފ]?gqѿފ]?e9?#?#LTx+r6}Z;!!BxlF;XпlF;<Y?#W:?Xп#W:?u;?$?ڴ~Ϳ4 Y?&; Am4lH}/I?rW-4ֿ7`lrW-4ֿX +p@,;Ʃo7`l,;ƩoZdk?mW?DTˁ  s'RKL?)iӿn@S9)iӿ?,0׿m@S9,0׿!s6? 90X?,r3w/0ѕa M?X}_.uRѸX}7ބM ?S*0~_.uRѸS*0~ Y?II \?)DAxHC (0xTNjgQ?$O6$O}S5/?VI\6VI\YgL?o?sn?ZBnţsǿ.y?W9[?#˚m!°6u@ .i?0t"? .i?˕7Tal5*?0t"?bl5*?M @6ZQu?~RP?h<ĜnWN㭿N*?N"v?|?N"v?MyO?|??2aK@KD@%?s,|N ?8Q@hzž ?3êo;? q?3êo;? 6LG(`T? q?G(`T?,6P?O%(?:?u젥[Hvk? ]Ax?, l? ]Ax?3A!]fq?, l?]fq?Is?K?j?U~Uꩿ"1Lބ?R?R՚ϤCpE 2Կn?2ԿSSZA^n?A^m?R I?Zdmge?L\9nŘ]o9Judg4c?dg5[bQ[MP5c?cQ[MPPn?4SQ? 8N,|?AaK[F[ҿŐ*?^Zo?Ő*?D<¿/콄^Zo?/콄9_?znm? 7?iPc9țB8-3/ Կ5E?c}S?6E?BMA<R /q}b}S?R /q}Di/? @?t)ѿ|L&?輿=W ;?C.00kofđ?1:KB\?#+@+%#+@=~]?:md-?+%;md-?`E`E@cuG?)WF(l?=̞e?ԅsG?]M?ԅsG?NtΊ?nɐ?]M?nɐ?V?V-?:@ yMr?φK;%p3o\-`ks?C?js?sQzk-l?C?-l? `9*}??SsIL|Үs?]n@s)!ɀ4?ݕND?r4?ܕND?~@];\?r4?;\?|?mh4?) S?. {?d@1(c ?K?2͉?HT (?6Pǂ?^?YJ9^կV?YJῸ(x @@ 1?9^կV?@ 1?v?g t ?=!*?@r_?ێX\7h#%?z˷ӿ(z˷ӿ#cK?}}~^?(}}~^?XC?ys?A?ۣKIɀ?9'?1 a?Sm紈?BT]?BгtBT]?7R5?I!{m?BгtI!{m?Y9\L]?v?H=?)A?<)h_?U?%G/?`ٳJ%G/?gP~?0a> ?`ٳJ0a> ?݋?g3?'Hc:R?G6_xQ?>i;P?mЕΝ?DVUUlЕΝ?~NܴΤ?F}?DVUU𿃖F}?b?8ˡ?2}@Bup{?NK?.ݔѨ[?`r4#0`re0?l.€?5#0k.€?N?f{7O?dU|Ό?r?e`?Vݓ S°_Y᾿P3ؿ_Y᾿+i`?e7lt?P3ؿe7lt?mHݽ?EQ?Y{?43.?va]Hkn](?ۻEq_1׿?ۻjd?Śm?Eq_1׿Śm?n7?8l? BZ(|+?B)!|ҿau?0.?Xy_?[6>Uƿ, ﴵ?~~Q߲?Ŕ?~~Q߲?ae?gKaH?Ŕ?hKaH?3?S(Ģ?@h?{ v9sKy;&.ĿT29"1?U29"pp:?CX?1?CX?uCx?"qբ?$ᡂ?X&u?Vu/8DaS?Ca+&?bO ?S?bO ?6Ǟ?nRբ?]}?0ňt?CSNKĿ|4ϧiX ?|4ϧH༦}?#7&?iX ?#7&?jf]?&Y?a0*Y!*Sڬ?#qj¿mkʍ3֠??_@Ԑ?4kHuOۿ@]?xE,?@]?ld ?<3AH ?xE,?<3AH ?6ry?}Z??ё`? 5TæZϿV?o^fE?W?+ k}?2w?o^fE?2w?@Z?A8bR?A oxw(:?]Nes~]#G?c?WDQ?c?Nm?WX]?WDQ?WX]?4ͻ?8\;V?t$vxH2?0?*-ܟg?}·B?ae?}·B?B*W?6!?ae?6!?i?/dχ?'H~?͸$˺?8¿a1?40? )?rIPd?8&W@h?"r ?/5 o+?o%@k3=so%@̮2|?r}jk3=sr}jcam?>ћ?ui?ܩ?1^xA!?$ p?ڶS1?']1?:dZ5Fݿ:dZmj?ƹ2&W̿5Fݿƹ2&W̿ȥ[̱Gm+?_9=?~Ԯ?[6?}Կy6hؿ~Կ'M?,?}(y6hؿ-?}(+ט?X]?4"/q?tA Ŀp~Ey!x"o~EyX*?GI#Y>?!x"FI#Y>?-51TJ\?>5?Ѿnr?wt`!h( 4y!hk ̛4?doXRK?' 4ydoXRK?':Xv?'|u{?0xcu ] ݳ?N<{?H + zoa%bъf MPކ`п0?Pކ`пetܿ5 hY?0?5 hY?&7|6=?WO_"^S5+ȖDD%.#w`?.#whNF"ѿP?a?P?&7a$F=NG?؎\~Exʇ_?nXۆ5⿩=?d3?=?vح ?.I é?d3?.I é?S(޿ RY? w`|FOZ?d(EID1? WB OcrϿ WB>V ,EU_?R yߪ?331<>\Ue?ztl?If.F?ztl?;M?7#FٕIf.F?7#Fٕz^]hYU?k/k J!?e"$ɾ{iN$?a"(?rְ>Нڗ?^oeI?"(?^oeI?%cȧ?@ژ?CR?EV[߼eՠ?rՎ1?ǩ/Q 2@Ĵvz࿈f?J=ɿĴvz\Gyo?~CU[ӿf?J=ɿ~CU[ӿʦ@P?()? ΑjT8=rd?ڿt憎ڿN*?=Ct憎=C7ښ?D?`vT? 3uf? ҉??U\ſ#vk ?T\ſ[:'?※?$vk ?ျ?rJ?]\Ǡ? L?`. V?SW:?]ÿgP>?]ÿ4?jMS ? gP>?jMS ?k?*;?:jVM߻D1;\?h,?jLd:L6)t!@<ѿjp?t!@<ѿu"?}|H?kp?}|H?^?g?Bu?͗֨-ٵ#ҿWQO?#ҿ 5s ?pK.3?WQO?pK.3? ?f3?.?kj>7}v ?ގՕI?]n?*>ՕI?#2˿oC?Ϲ衐??n'|?~rb?RlgBk@?Kن?Bk@?RDO4 ٿF?Kن?F?-=? x?ˆ?O kB}?Z?Ǔ<}K?y$?<}K?Moڿ4?z$?4?젩?lR۾+?Zg&iQ,y?EYc?DIRj.Hh?r܏yb[?G"W5?5KG"W5?G}?hML?5K࿑hML?/z'?W?yeA ?W}h?-?V뙔U⿇-?L??V뙔U⿤?]?4=8?ă?vC?L; T0~Ltq?IP<ӿZſs@Bh?JP<ӿr@Bh?A[u?6Ĥ?Pqyxh?`!,? RŎ?4s唿3oㅒӿ4s唿&ԩbſØv2?3oㅒӿØv2?ٷz9?g?Vx,M)ފ?c:Qc?AĀw ]o?]sf16俈[Ją鿈[Cا%?ʺs`?Ją鿙ʺs`?{Z,? b?) N??#?Љ([lO߿#YſA+b(? E?#Yſ E?o|? 9 ?>vi?y??+*Fљj`̬Y?j`^(S??̬Y?@?"g.ݣ?' g?l7HUg?'<y?aT鐿Z{ÿ fTyVNnkj? fTyVvn /饿N#֢?Nnkj?N#֢?e` c?ء?S!i?Fg?6d| ?vw?.~|?2N?b`?߿OF)p?Oc_- տ.e1?F)p?.e1?iGХ ~?7 \X?,Vv?8ANHi?Dƿ)q ?Ӊok?(q ?u@mh|əe?Ӊok?|əe?1Hnf?ye@?@l|5`lE!?D-2~:c?H?XɡH?nS?4V1Xɡ4V1b1;?;{?r!R&ah1? |xp?|?*蠠|? ݃b?= dد*蠠< dد":GL?T(?Lꬿ M~?hςA?sa@?y.̑.+"?rz ?*@?P2l&b꿣P2r2iWϿ}UB?v-K?=AZ\?nc{r__:'o}?2^EV?&o}? ZPYpп1^EV?Ypпe bgLy=?x:[Cϡ?ɤ/{o?slB?WDU?^~qr?0?^~qr? =W?nM?0?nM?n ?M{?]>?Iŀ?HÁ:?ZJ T?HÁ:?:7}?mvR?ZJ T?mvR?8,+{?niyqtIxǝ?(Q?̀V?]J?}:?]J?EŔ>?qDc?}:?pDc?mvB?L?q4ҁ=? q? ?NJH?g"?NJH?xJL?G=?f"?G=?csFH?6˗?T׎k:=?7ÿnu9?-<=u?2Ϋs׿ֻBX)M?ֻBX'7.k?=.%?)M?=.%? Y(@2=Es??l~{]F?0 @ϗ.Qʿbt >.uS:=?bt >ͻEb?6?.uS:=?6?E?5lw?ILYxٟg?@qCU~4VVأϿdHtŷ?أϿ?%C?dHtŷ?&C?>l?@u?BO#O N?y!PҘKgi%NodSϿޤC?NodSϿ}?7'l?ޤC?7'l?a),?뫹a&?~K?ROa? N!c1?pc#GXӨ?9uhQ?c迼2-^-?c|n?4ڌg?2-^-?5ڌg?ݣKv@0'䕠?p}i?Y;BuN?xu~&d?xu~\N?uZ?&d?uZ?&Tkn?}kxb?RVCd9q?`]|(w?l $Կ;9#?l $Կ ?[]M?;9#?[]M?NO?Me?9ra?y7PD?aӿmY5`?aӿ\b??Yܐ9?mY5`?Yܐ9?dvŦ?IwJ? Sq?Pƽ魿n;ҍ?GT?.Mct?ߥSf'U{4W?~ ?tcɥſt[Y? g^F?cɥſ g^F? A?9cg?Eb$d?S 󝕿Ccs?3g?0Z ŕ?0Z YslN=?W?ŕ?W?%q5?_so?K<?{L?޴LMQ?TRD?Yп"s?ZпPaq?^]c9?#s?^]c9?x#?@>r?WS}?J @? /.5S?;!VW?B<пAJ?B<пqNG? v}?AJ? v}?ET?h.?c$ z?B_y%ʿViy?5'W=S?٦?.ǡaa^?zC?7?] "A?7?/-?k(] "A?k(`C5?;r?oe?|,cF[?qMQQ?E{ ?/ 5qe?E{ ?*?EJ. 5qe?EJVv?IL?- ?rjⓆ7r?$V H?*r䰿m?*r䰿?㱿RV џm?RV џ ?[0v?a) qœ?9֪U&pd?KK? k pZ&? k p ʜ/lEZ&?/lE?{O?\?tc-z"W>?x$@?C#+Ѵ?c:+P?9'?O*@x1w?͝Rx1w?~['рQ-L?p64Q-L?b?[ٍТ?r]!-%?T)-Y|s`axO?ss?Cm̿s?BͿOc3?Cm̿Oc3?h?]ӆ΢?х?ц[x]?)jW* ?zm̿ ??iG2ϿͶ?zm̿Ͷ?}R?^6Ṛ?Ux$gǿa)t2YgGã?vԥ岿B?7?,C{iݿr\:ƿp`MKq\:ƿ"4%to`MK4%tc@?ňk?,u jHLy?k vPBi`ΙS< ?K\S< ? 9%ēK\%ē!W ?Vt ?8l?ic8us?디]h?sp#2]h?G?ܹI?sp#2ܹI?.⢕G?*?"Cβr?c+ʽf?PfWrO7I ﷿43Ἂ?Rnq53Ἂ?`?J?RnqJ?]!C?#]\ F,+)~5?ɲ(Kӎ栞]?>I-FnA?n.'Sܿ{*bbտn.'Sܿ(?C#C{*bbտD#C!!i@~5? я?CjֶFkDh?C%ڿ;ajkƿC%ڿ9W?4۔5@;ajkƿ4۔5@쿟烆?6gx?\ |7?W$8CkJ?4kl11@ƿZ1(4kl11@ƿV$D$%Rs˽Z1($%Rs˽Y?+4?!WF&~! f?ɯLCg4?ay(ȿ~ay(ȿϗZ@q~Aq:E?#?N柒sV񟟿}87O??[hR? @t IER.[:pfQ?i:pfQ?Ǎ0I\ۿi\ۿG1?%MeC?0}9զ>Qp?;|L[ܿ#9O?0ʿ#9O?;߿QF}F˿0ʿQF}F˿w(ڴ?o?d2 vZzh?ej?n?q0"ſ‹~3?n?‹~3?"z)?G"p?eiQ?Ud?zh?#1f?ga&̟Wu?g6UMĿ,< j?b&̟Wu?,< j?>?V'>S?ȕ?"1P?YEEV|?:Sq?:^C_󅿷* A]e?bӿq70X?aӿq}?xGEo?q70X?xGEo?k?etC?#mM`D?Ry̕_?|_?̳DMH?أ?l8~?s#J9RXVsT}?|T́ڿsT}?M6?]??|T́ڿ]??䛩Kk^4?C#&ʅ?i^ԍbMv;K?X4j)?Xj)?B[濩Y?Aߔ?TzK̀]H e>l4~/?$?~/?44 ƿUb ?$?Ub ?`c-?B"ǁ?kLuFvj}Z ÿPw?Ns?Pw?5,~fȿ/?Ns?/?kk.,c?2q$ÿ`Jy@S7?ĪZj_zIw:?dYX3a?eYX3)&6Կa?5Կ]6w?[!8K?v9vm3qVEbnz)LH?'m2ÿb.I2?'m2ÿ ݺ==Ϳb.I2?>=Ϳ+͛?r)'?Izob?(bFЅd?>?ާ7z?iߞ˄?7z? Nѿ?Uy?iߞ˄?>Uy? Vq?Ts(?)b?b?Y?Ls^8?mqh?^8?GFmӿtY?mqh?tY?_\N?"o?$)??TؓM?=أ?N]?oNеlG!?A>T3?pu?T3?' M7Tpu?M7Tg2L¿!?GJZ?A)c?q.h2?;`οu΋j?(?v΋j?~k{)c%(?k{)c%x?3Q?> 0>? YJm?ܑ?PN?-1%!?m?ؠ?m? ׿',`?ؠ?',`?ɕl9?` B?\q?2e?\/"?ŚwEU;?-'H?6( ?,'H?NՀ(ۿP?6( ?P?Fݬ{?{}k?hpN¤{p/7};4? ATo/kA aya_6X信5nd?n0ۿ5nd?O`tE44?n0ۿ44? oWA@."1?kn[ߏ3OW;ZM ѿ/۳e? y-~"ѿ/۳e?#DaO? y-~"ѿaO?8)?8[離?p%c؀pjqˑ? vC:?+-?;?'忙ԄI?+-?ԄI?54R 7?\T~$+?f uD4eoD龮?Z0:ԡ?oD龮?h`D&ܮ ?Z0:ԡ?ܮ ?i^cn?jg?o9 =?B9.μY-?<`h?σSjF?$91?d޺wQ?Eg]s?d޺wQ?X."?Eg]s?."?$g?Հ[?Zf*~]?C䃿r5ÿ<?74|?<?N;?84|?N;?`7&Cf?887?L߅ `2bHg?9 \G?&R?pRԕs|?pRԕxS`ڿewgbp?s|?ewgbp?DLP?z`74?H3qBh8ݒ?YY :?I1"D%pP?D%pؿgx?P?gx? 楓W?̓X?uhLsx?1 [?YM?~헴,lV?E,>^?t࿒Tf?4n?Tf?J.?4n?.?{m\?|*A]? yRmì?Km!YQ?q?=C?ȦAc]h??=C?]h?Gw6?E[*=?e\BK_?"2`g?U{:E?ƿ չ|軲?ƿYି{? չ|軲?{?&kq?aS?d!eX N?8g?}kP?P~Ŀ6h\ɴ?P~ĿD3 ?6h\ɴ?4 ?wd=)t?Aa?|jrܑq}H?RڳS9WL?A:"?<󉸭?C h?Ð^?)t|@/Q8r)t|@5foF?{ܿ/Q8r忆{ܿkj@`IǠ?uTk/?B ٠?M߸n?I?-߿[3%ѿ-߿~PG?drNsҿ[3%ѿcrNsҿH}j?<DӴ?z?5 f|*?xb?.?G`ÿ7^ H`ÿ2/?lz﬿7^ lz﬿:?UsH?؁;+? B6U??[?FJp?'Hž'H//-~@?E4džE4dՐք?,o}N?̫sviʧVԃM<"-Rt?.1~Ei>RJTF?K?Vi}鿁;wVi}]@V򿁄;wV獫?.@6?eo;Bg9'޼!?48? 7Q"%Wۿ 7Qῑ @?.viF#%Wۿ.viF{? iE?# ?i]=N?t?' '.?G?9JG?˱?Cڰ9JCڰ,¿Θ?3l?x`w?y?)̄L?F&n6S?eYF&n6S?y8]?6#>eY6#>h`¿x뎈@?3=4!5 8/?1uh u #y$8ƿ~?᥇?i dǣ᥇?C @6aȇAa@?LX n^"`ɿLX n࿖4:a?b3?]"`ɿc3?le#f_?Zoa?0k)$bן?tkqpm:?6!(CϿ6p?]qo?"(CϿ]qo?rO?gr?4?ڧ8?=Ɓp ?זC?!0Y?C5?i*?5Vl?X????(SZ?&T2y[&??&T2y[&?T.`D]d?p/Wp?wcp? 1v-w?BiW?]rR?pO?]rR?ד>?5$RUv?pO?4$RUv?MpŠ?ޔ?oi[\ƨA;/e?m^+wovgs?yrhjĿ ?yrhjĿ"P?z79M? ?z79M?9@?nP?:^\΢cr?_㧲dq^svߣ?uQƿ]vr?uQƿ_{8d'?Lv?\vr?Lv??IP? Gy?Ib?ڒ?\/?-= ? bns?T?:?Bh[3e翢Bh쿰:iwڂA?[3ewڂA?8ޔW ?HD0?U{ adn?rAݕ?=cZzٿ=῾F?hTE?%ɟ욿?ZTٿ%ɟ욿?.}?^h6ͿZTٿ^h6Ϳ\Kox0 @ $R(?Z? k?3 \y?{$"g|?6mTB2?yMtտ6mTB2?]ݐo?ېP?yMtտڐP?cXeOp?Brx?k."v?B;.k?}dRLb.n?w٤?Lb.n?u`J#?3C?w٤?3C?NE̠?FD?5gv?EY~p3G9?L!~s?5aV",?~s?%?X<˝?5aV",?X<˝?|`?M`m?TK+[ kCM}ۅC?0oc4B& mqa)?[hm.9?Ɣ],׿.9?|`^@~N@?Ɣ],׿~N@?Fv@?z( a?N`AtĘP?X 랗׿^i?쫧quǿ^i?m3i?d?쫧quǿd?:? R?)POM1?ț@ޮԙ?b4:?+}G?4:?&9eO ?mFm4?+}G?lFm4?wg?C ?Kb J?/#"ׄ?$ w[r&?R?[r&?rթ 5? ) ?R? ) ?nY0?+tՠ?gp{&?&,.թB,*d@m?QF1bc?nۄrEI5?bja?yN5?5i:ٿyN5?Yle?:?5i:ٿ:?S?`{?ʙW(?y}uC ??m;3z?p>5? W$οp>5?•y^o?LLy:@H? W$οMLy:@H?<?|a?~MпD?5TR?WI"?A{7t?̧?NJ̧?i͕?AˋFt?NJ@ˋFt?.C`l?@2d?0h0f?W(?J,L'?aO?#VΰaO?Kۧ7?UMqu?#VΰUMqu?ok=I?g]֯?š?] ? ?8?A?2◊? 3.?w?Z剨*ֿ7oը?Z剨*ֿ6 %̿ʴ{?7oը?ʴ{?FT1_4bRm? +?@?0Iۡ?*bχ?su:οgb|˿su:ο pJXǿ 2sdT?gb|˿ 2sdT?I8e_h?K!(e?3DŸ?R+??87_o?4M &87_o?-?Etr?4M &Etr?ފ8׿C4?Ji;?CvY?,Wl?0?+ @–>ߛ @7?–>ߛ @v0ֿЈӓ?7?Јӓ?2G_IXѤ?[M?ľaҹ?g Xe?Cv@nŽ @K?nŽ @w*wY@R|?K?R|?R]C u :?y"$Ӹ?9ʉD? 7~>ܰ? = @ˆ$1@"eԝK?̆$1@<^@1/t}?"eԝK?0/t}?1H= (xe?k#f6?e#??}Ѥ @DJ@f?EJ@`CV@_nj%?f?_nj%?[b՘!?[%"?{?`E?kO?s)ˬ?l?kԔd??oO\@ I$ )@@!I$ )@s`44@F b!@@F b!@f^de?.5.? ?#O?T'V@c)@ &!@c)@^>5@s.R!@ &!@s.R!@¹\l~bQ?_#y?1GTF??|']@(R09$@\$@(R09$@v??.0@R@\$@R@)#Ysp??DOG?JA?:R@hlǂn#@UZ@hlǂn#@_(k/@T2@UZ@T2@Nki,,3?܈ÿI|Ԧrѽ>j`?1Ō6pၥ?1b{?u<?ij`3i7Ͱ@y";@j`3y";@֧x@ԉQգ?}Hg|?#4?=QV?2r}W2rXLz@xu|@|Wxu|@"j @?CPF?KfͿ}խW?~Pݘ?:YҠ@+YPbh'p+YP n@ p@bh'p p@F'l6@$fwŪ?[߻Ϳi|y?q&4?Œ@nApB64nA5ִҞ@@pB64@pk|Me@pM!?)vZ? Rs?-(hO "?ơAD?ʋF&mԹ vGEuMRaJ?vGE Zi@,< 4uMRaJ?,< 4gV?j Ң?ܠ]hu[jtW?]Z᱉P1#ށ} b8 1} b8=t?X>2? 1X>2?is?ڕ?NA_\?z{??O]aNx̚{O]a#H??e'?Ox̚{>e'?icGP? M?ҿϥs*؞?nmٓ p?5)?)֑ ?\EТUJuYZ٤?X$?z&, X$?dUL@Ղ+Yz&, Ղ+YpJզտaX?Ki?ZkwEcP%?;4]>?.QL:4]>?mZ[?Xp~e6忕.QLXp~e6応 Ŀ0S?40D!9oZx? Fdޯn?ۇ#¿)/ɿ܇#¿:\?xA)/ɿxAܼ?$?טhN~6|~?TJqN?W$ƿ0` Q̿W$ƿuY?jhɪ0` Q̿jhɪS詮?v$ M?dV?֡iѨ?>**Q?ii!?S?E@ك^>?s9?]vW?s9?w$)r@uzrP?]vW?uzrP?Z," T:?p?\$?w5.>p?W~ѯi?0EuƗ?l}/g?Tq{6*?,x,_ ?,x,N@?PȺ` ?PȺpV?aLT|?jc?|>V?jc?+B?u5z\?ĭGE.`햿@;9V?! B:nп JN?:nпʐ?u JN?ui2b?=[?3}c虚q +pi ?:o: fпML? fпpȺ?biNML?aiNQq?tT?iLP?r{\?Il??`Ӧ?Vf츰?K?2{=?q\ၪd癨 齿p\ၪd }fvb晨 齿fvb~ &?Uv?Qݒ?AX=??crʖٿ{|WR?crʖٿ5@'%>?r?5@'%>?⟲bڡ?kQ >?vjq?h1ɔ? ϶?%?Dq[}?%?|, !?Oya1Dq[}?Oya1!@ޞ?o?﫴둳?}EW|?X6h?Ł?S?2e`?S?塌)?q;2e`?q; ?p?Q2?H?N;v5o?X@?d򯍐? ? ?c򉤬?"Q-=k#Q-{W߿࿓ׯ"߿=k쿓ׯ"߿?̹<'nҿy?qeN?O?X? O1?IhC.,ҿ.V%IhC.,ҿ8T„ҿ 9t1˿-V% 9t1˿y[Do;5?Bh?.7o?x ?lZ?3?,vl?3?$l?h3q?,vl?i3q?=Gu:׿?ǖ??Ev#?Wx??'TIe?pb?ea5?!@hK+@Kj@hK+@ KBy-@yW8 @Lj@yW8 @KLՆ &? @k)@ @Z?OҌ@o-@L@g0 @ ;&?@~,A@ ;&?@j)B@|D@~,A@|D@)Ip`?+aK@I|a@aI@G{ @%r0[6@/<@%r0[6@R)Tj=@R.[@@/<@R.[@@SH%ÊD׸?=1?´@(u&?UF`⿧ K!M@w)@ K!M@׏1Edb!@;l+@w)@;l+@w#>:hrb??s@@{U?Cxrܠ@;)@rܠ@Ru!@ B]*@;)@ B]*@.18B~'RZ?/c̿ E?7B?gծ??ӁQֿtz 9?arE5?*;d@]= @P' Y)]= Z#Y?@Ƌj`+@@P' Y)@Ƌj`+@k9]q4@<԰?ڿFs?3?NnP;! @( :}b*( :}?hU80@[?Uip+?*y"8?= @7R*Gtmx'7R*G-u @%,@tmx'%,@'y)Ƨ-@\ ?jt-踿y ;N~R?\Z! T/jϠf?Td1?Q8?<"_G_ < @2GF2G9X@?@F?@+tv@BI:?+U@Կm:?7³r? h @i_x}F#j_x}k5@IeX@F#IeX@5M@\-[?>ƔJ"&1܁?9kw6(?o+%%?<|m`MA?mH6?rmHZ(/? ?6?r ?§l忧[ ?@;=6?)l@߾mG?3Xdۿ:`}Ͽ3Xdۿ_.D?ʓ?:`}Ͽʓ?&ҿ  ?9d6Opq?H?<`y??`e>`Nsw?zW#?ezW#?V|f@S?ddTS?xǛ&/Sw%?T"ٜ?5? ާ<Lm ާ<$YjC?2I?Lm2I?'NY'?#ӡyEx?|%Ab?r;?{ex⭿se/B?ρXW??Ss#V7ݿi27?$V7ݿf14Iv)i27?Iv)4㿐E ?]w?/Fܚ?.0;{1忂0q?1}l˓1t«˿0?«˿G4.HZfп0?IZfп Z߿D |?0kMcRJ\r?jֿ0\x¿gOP)?0\x¿hـ?h@ʿfOP)?h@ʿ}s%?^cG?uGF7t?tRHmxAֿuc'o¿Dns?uc'o¿. ߾G8?6qȿDns?6qȿK4e?Y1?ݺC@WY?z椷?dA? a'z~ͼ?6Ÿ? ?jWpU1?wf?˗Z_wf?許m4Tz?˗Z_Tz?ƱR#D܈Z5?h*? 6.?[1?[j􁏜?\?K>/?\?@߲ݿZ$K>/?Z$wMctpZҡ?v(͎",Wh?d1y&8 ?8?61 Cy4?8?x,'?/=ҿ61 Cy4?/=ҿTqn?KSHFݡ?;zQݗ m?'v_? U&.?#\? U&.?fO#1A?ѿ#\?ѿP{ ?+w?5l ?%ܖ^LP㠄*?+X? i_dCdK@Z.e?pq A?Y.e?m̡?̮Ex?pq A?ͮEx?-`aOt;?OrS?'},)릿jG?DM?T5g?DM?/{,]?7{0o?T5g?6{0o?~=D1ѿ90?x?d딿Y6 EBrm?`?E?`?S[xE?xxm? a-i?;??/ڔOrAVɻ?Sz?HK9ާ?Tz?709hݷqs~HK9ާ?qs~aOj?t?f榿,5j?W?K@b?& uҗ?=uOy GNyw Lޱ?Nywῄyj?s&3? Lޱ?s&3?Gf7t ? K?d×}6kSƿ&ް?jSƿ4e{^?>qM?&ް?>qM?l;\D2?"ţ?.\lkGb)Xܿ>?.rm?>?<A{?@h[y>A{?HvMPX04?M\?^~&֡z?tR빠?Q? ȿi4̿ ȿj,Ծi1߿4qʯ?i4̿4qʯ?ԽשyݿǴY=?_Q?rO`!))&?b}KJb}K,'@?R?Jῦ?R?YYӿv(N?|kF?!Wp+~3e2ZUC?]_) \_ yȺ\@/K"u?* .K"u?00QJwտ`о?ۿ,4/J3fY3?DOAyslǜ^[x"=?@3( n2\3( KA @X䌃?n2\X䌃?"_?I? ڡ?x$A¿{Ug";@U, uU, ?q@PSan@uPSan@VYÿF\f#Ӫ?]h4\?3:5ݿ3nQ1Eu< @ Y5mU nAPL(@F @Y5mUF @YL W?f ?sv5Ir߿ل>= @iڀi#mC'@N4@ڀN4@e1 p?N?6T⎿9Nt ,?(q?+5sTX4&MȎăiM?\ $jҿiM?C>!]F[߿[ $jҿ[߿> -_x'#?5Syp[ɏ)XIp?Iw]bܪ\PAMi?ڿ|߿Mi?ڿz⿼mǤ?CFSi Am鸿?rd ?C=%M@Ж;(cC=%M@f^@xqЖ;(cwqc!?; 7A?ڰDʬ2-"?1I?jp8@`"뿌jp8@ܧI@xz`"wz>c?E ?a+y5YD?@c D+? _Rp?짢?m2XB$Z6pOzpY?0ĒݞpOzpY? *)п$0Ēݞ$;\Owd?8loy?c)?)j@ea3-?en-?Bſ2 jAen2 jAc`_ή8?5_1{!]B?)̕j@y#gOʩ?'4?K gǿ'4?!}?HٿK gǿHٿQ2[ſɁ?]yޠBd>jro?ZehܝM?co?WJ&#Ϳco?_K? \ɭܿWJ&#Ϳ \ɭܿhNmn\iKG?:2up}?BN?\?jOJstJR?W2n?x4kY쿤V'ۿц7㿤V'ۿ/pSN[ؿц7[ؿu0o7?@@4eFm`?ThH?Qn=㿘rҿ6S?%ޥƪ%ޥຐkA+VƪA+VWCHƿ@x`h?74ĿY$*K?eALa@Ρ?Q<e?GW_s_[/f=y??J=e:8>J=\Ғ?VlԿe:8VlԿ}w?Ɗ ?"hw/sĠ?}3롿gƜ:?|L%xO{f{L%ЛÿHWʿ%xO{f쿌HWʿvzͯ?GF)k?Vv g?p?_s]:r?߲=Ϳ^߲=Ϳʛж㊫rX&!{?^𶿆rX&!{?q/PIBv*m?kKa?z1a?Dp=?囋̿x+囋̿$s]ScIy?x+]ScIy?iⰿ m?SXV?O^l?Ŀ Т? ^w? (+i?_TvuQ?ڡШw쟴?١ШEÛc-[w쟴?c-[Yz?=O?ͦMR?\bρwkQ+ t ?\d/U?\b뿅aÿ v`wʊ'? v`C،#|?VL,tqX.5yo㿂s8Y?.5yo7=r1NuS?s8Y?MuS?YWj[CDWܣ?6Fژ?i&ok1Կф805?ф8.$0Կ}I񍵿/5?|I񍵿0^t@ѣ?,f?LI%^zLJexѿѹP-[б?ѹP-{ vѿgѰ[б?gѰy߿EMԝ??T?[jNul7?a&F ?&ӕ?GYsvZ?S^@E5pl?[?E5 n?aT?pl?[?aT?es!b?}3~o?Yj?CaY?JT?b?vIM^?v:&?d.$?IM^?d.$?1?M?^2^?DIHp?6Xe?%nr?Z˿?Z˿q=/?:o??:o?J90Ph?|ߐt?kӄ ag?xtԿǿo0rǿ."H?(D?o0r)D??@i-~h)3o?*{`w?\f?GAտؾ|ǿQaSؾ|ǿ ?1ުO?PaS1ުO?!F(Ġʹi.?̙֨EkWFSG/>8?7,u?쐙?7,u? ,*?L? ?Be?\Lў_Pn?)?_h.c?5-?[r/%?5+p?P?,p?lil̦|nP?|n`.i*?ӇSkU?*ǝ?O ˜}?(j?=ڒ/ֿոU?X?ոU?_u?չֻX?չֻ%{?BY?%t>?td_}u?:pZ?%y"_?=_֥>?%y"_?;?`CP=_֥>?`CPRBG$?4˚?l:? W9v?aW@?b_?+j}?b_? W&-?1˙E+j}?1˙EY_D׷??5??[ZZ@?8vx?`]Ţ?gn?׽'鍿kYvBί?H\)ePI?S>Z?dPI?4W0?_~S>Z?_~O4q?HDȢ?<ɬ?DŽ "8Χ?44EVL-1b?VL?ӻ?-1b?mg?=?Z^?xFJUG?=yQnĿ~/$Ƹ[ ?~/$Ƹ(p?qm\ ?qmce5MA?8k3+?|ĞCWnǾR'?EpDÿ$Na?3iJP97W9?g?(.?bmXF+(.?TCE1?%CIbmXF+%CI w`?[T x?lHs?ƔCrQ^uz?[=?=?q=?wO?4Y?q򿔜4Y?-G(ux)O?>z?E4 'Kw_?=px?g?N.?gihv?Sʼ̿hihv?k?n]8Tʼ̿o]8K SL?+qZ?ɔU!@o?1~Q??g&>?,ta^Ϳ?g&>?YME?fĴ,ta^ͿfĴnN-t?0?MIu%q@86]|?;lǻeƿ8\h{?fpa??'H~roV翲8?㼂?8?? ?㼂? ?ĕx?m?rha?H:hzJq\=dO?8]?Np?8]?.n(tG?Np??x$ۍſ}Σ?۫?!xV v׿+iE.?V,+iE.?\ɍ7,ʿRv~B?W,Rv~B?+տ!ã?9ȳ?=JcLkɁ(d]տ( /?6h( /?Gÿ~~h?6h~~h?ߴ*ӿZf>Y?Rr]?*N%?, oue6?;7sdpɦ? q?v,7_9v,7S.l@ɯ5?_9ȯ5?yi$xw"R?ܹm[p?ƾ ? ذ߿Ijҿ ذ߿:z?zTP?IjҿzTP?$ lĿW:D?0aڌ\ۡV??}UI?r{JmI˄${lJWlJSa?!erÿpt*uP!erÿ@Hߧʿے0?pt*uPے0?`k6Zh?p?b $&I'(Y89 ?fi}޹fiR3>ſ4?}޹4?DG?<0!?SB|??u{ο9"Š? ?ڡ#ig(A=@ ֿǍ2rͿ ֿߴC>@ ?Ǎ2rͿ ?.jz@Ʉ?gnX?M5EM!?cUգҿZlcUգҿ ]'g?Vp?ZlVp?}a@ϑ|?@8-?w16Дqyg$?&[ ſxOZg/'[ ſ*˿WF-?xOZg/WF-?Z@r?BC~?ZFR?wޓLQxWן?C0"Ŀ?bC0"Ŀ%ɿXJT??bXJT?%?":d?pua<ͿCXĸ\bv?0Ї?j>\bv?G@?-(HE?E"?4sY@_lizR̿WBo#i?WBo#ϿuJ?i?uJ?ego?M?W?[Ṷ˚hb<_#ǿyV?y(ȼDѿ%??V?%??Ӌӝu(?8N?4?a涿ri?nޅ?-ͬ?n=}h?^t?-ӃҠ0!BCԃҠG_P )m?0!BC )m?@I|uLǢ?\G֐i?Q^(?ǣv!hڿ M20ؿnޔݿ M20ؿBήXeֳ?nޔݿdֳ?`3NmڿﳏA?߼M_z"as4?i=?Yj~ؿÿ7?8D-ÿi Aȿj4*N7?8D-j4*N9ZL̿#=?ҡevv6?|("Г?'=-r,U_ײÿ)mĶ^ײÿ^4"ʿ i )mĶ i T(5ʿ& n?;_?8FND2?`mQ-?|呿Q!L<>?YRy G%?\ID\D{?IDD{?C?O]c?Tx5 ?#l? m??n ?|2Z߸?n Z߸?C*? ?']w8?T$?}~?VE9?e!t"l?_5.d!t"l?TئBx?&ӽ_5.&ӽttvנ<}?.ZOA*?)1, ?{g?׼$w0¿׼$w?# * 1¿ * u)rGݡ?*jE?(c EՈ\}Iuÿ|Pƿ\}Iuÿ!1v!?83 |Pƿ83 ]*>?C?vui?pM?Σ|p) d5?A¿r }qĿ?A¿'\?i6멀r }qĿi6멀Tð?'? z?mʘ\؁Lۨ:|??:Qq?)y㘿8~6}?5{>޿Al@5{>޿#j!?֪ ?Al@ת ?B?1J?{{?F?ҁti;w֥WN?Zұο;C?YұοhSg?L?^=I?;C?L?^=I?Ex?tcc)V?!^?J=8+w?{6y?l?W?vCo5x?LO$ ?p/HԿDᠥŔ??Dᠥܩ"RfŔ??Rfu}%տ,Š?7l`[? K<?7\ԌצQ)u?I)bj9Q? P(XX P~H5 *ֿ(XXH5 *ֿ! ?/z?6?10=A?6(xtuFϿ6(xtu g(ĺm;JҿFϿn;Jҿqdv? RJxO)? {F?8xXC7 m ˵1sdkֿC(?sdkֿdOo?C(?o?Ћ?Ju`d ?ЈG`u?6mYi0lX̎HyԿ}[?̎HyԿimXؿ ?}[? ?q,jҢȿqb~?A?4t}󀍚pz^_ſ֢7b¿SH=֢7b¿:2e*$?SH=e*$?xW$[ٿt?]Z?̂«}]ZwĿ¿NVl¿Fկ/p?NVl0p?c9s7׿V-?ƲP)̧iّѡ0)k?8@TcqH-&?؜1LHM?9͐?LHM?jӦ} ? ((Vf9͐? ((Vf7q?}SSj? 7''br'1Zʖ?Ϫ2U*?/qV?U*?DP?./qV?.T2?I譳?;pTbwnQ?v[!#ѿAMI̳?&%7?AMI̳?|̚S&]ſ&%7?S&]ſՙj п๙?ބ0=1y^Xs]?LO}пw?{c?:}6?Ơ>cr8#?.`oўp>:}6?.`oўp7 ?ݡ?ǫqM[L֘Oq f??`79?r1d?r92M?hu1d?hu*lm:?BAE[?e?$dmba0*?#jpq?XVxSQ?,t?fM.P?fM̗1?{(].P?|(]Nhr@L@B?<dMp?պE?H Qb?H?S)[l? Qb?T)[l?KF?P?Waz\凿g? XS?ܡ¿S $?ܡ¿?{Y?+]bw?S $?+]bw?@?[UY?xNL=1uIx=[?O;H핺?cvl:¿*r?dvl:¿`.`/?wM ?*r?wM ?UQ ?QlL?iY0$?c"j? 8?]3z?;rk4^?9C mcп&?[w?&?}5bg?I&:+¿[w?I&:+¿`/w@"/?Jr? Ӵ??, dSΧ?@ x?Χ?0?~nq#?@ x?nq#?D@&oK?|hEc? cT+?YVр?8âE[?8Ynk?\s?âE[?\s?1TV? \T?oV3.b?@vH497/&?41 ?k`LjT?k`L5zgs?-?jT?-?vg?u a?f oxfηA?bP=?-S?jCi%}D?;!m%Dv?0G?v?@b?^iڿ0G?^iڿ |E1¿?)?X5JhKK ?9^(ῼ? z˫o`?ca=?Lҿ z˫o`Lҿ c)?4e?Ҟ@#x*|?AJݍ?EÁLa'?8SRa'?;qjԿYAʾn8SRYAʾnGϿQ0Qߢ?=izv烿E{?fws?l짡s{?A;s{?T.ӿ0@A;0@z'o̿gqc?xqWW1T??V?S0˯-w`?S#y?7u?6?,@?\m,@?8V?u?\mu?H.50U?S`uyf{?J?`@m?#\?y!#\?2-dPA?.=Ōz!ῂ.=Ō%$qg?X$?Әf3WdH?8?rM(?+g? 5nmw+g?d˷ȿDR 5nmwDRV8Ҡɿ4 ?wcfy榊?.Â?b[N?%Ϝ?A9Ta%Ϝ?U!hĿTA9TaTX)ǿ4*?W1ml?EqT T"h/$?.mܭ?HU?'!)Ț fo]?7*}u?O 7*}u?su6~?T#:?O U#:?V{`?T׹W?;|B!?=3L?R\]?3hR\]?λj?ۘQ#?3hژQ#?w "޳¢%m͢?y ?,{4aќ?~}z<2f?XH?,^PʿWH?h? u¿Ա~G,^PʿԱ~G@WcӿRXȢ?S2Ҥ?nyTR?X5P!hU'h?Zx? ĮʿZx?=^41տ`岿2¿`岿=" V7ʠ2¿ V7ʠ3e!ԿМD;?9ƴhI?ye x B?Ѵ+B[̊v"?0)Я}@K0A|!n9q?K0A| V?t9$!n9q?t9$Y7ܰ8{?Y]!fbaM#?#w??Afb?hjiPs?Afb?[6?] hjiPs?\ gJrǿ_?1a)n?HxG?'+,yug?{)¢ȿ=?|)¢ȿ<ԍ?6ñ?=?6ñ?+W𸾿r2lk?Ѡk?)ɍ?*}yTVYl?S%$ǿt8_g?S%$ǿl _e?x?t8_g?x?mgcnw?p?($eVʿx#lfE3?mK ?'_itZɁeآ ?e2?g nآ ?g nwx?0}?=d?~,m%a~?Ux\cY?\c򿒁ؾ?Ν&:俇Y?Ν&:XڗiDcIF? D??lqк=?=&תڿDS{пJ<͟?DS{п/&:ł?J<͟?:ł?տ2ׁ&"C?ݶ?L)6rA ?1<ݿs-`lп?N?s-`lпBD;i̅R??N?;i̅R?ҫgӿa|"?84_bb?C⬲vƢf n]~]AԿX ? rl?X ?f|a}1Uu^?E=ߑlڧw?vKN㒿vKN̿2@ȡ?㒿2@ȡ?TH1MgpPQϿO7]`?AZRq?P7]`?2H=#t?AZRq?=#t?ܶ¿eڎ?'4~qQ;政 "wx?*[6;QsM7*ٍ:`?m,?#$ړm,?iwܿýgÝ?#$ړýgÝ?ďp ?iB&O 2?+Yt>?y`?j?y`?ؕAٿpUj?qUE?L̡?G.қ)^U1:xtgv? yS?Nλ?\@+V?Nλ?O Oj?8\@+V?8n|?%.С? mD.ZB$!Jy?l?ݸ?t^p?ݸ?Pdʑs ot^p?s ox?#Pu5}?~=+ ?-KE?]?>zG'#?L%?/$?{0?-]?{0?'x?-]?'x?Tl2?!&?~/Mwu?]1ٟ?YI?Gl9?ZT?Gl9?@zO?ZT?O?g>?21?c4X:*Uqub?S1 ?C?o= ݁IFSf?o= ݁I>Vܵ?*|Zu^FSf?+|Zu^j#kE?f;?W!U \?Nm/?k'H?ZoރNn ??ZoރNn{hK#?ܙW{ ??ܙW{$?l? V?QM3?6k?g9s?ĽK7?\Rz?nǿuԏ^?uԏCQ ¿6/)?^?6/)?QT)?qY?l'Uv?:̞n?"o ?X4Ŋ)F5dƿ٪ ?(F5dƿ=ALϔLO"6b٪ ?LO"6b4?I?_>lT?n1xb?A?J?i-ѿ?9f?i-ѿ?%{N?¢A49f?A4FTb ?hj?9stcO?d;GIW?0(W0?#?τٓl?xs_1Jc VZK>1Jc &.ڿӤ]ޢ?7wLc?E,Z?Ҵ= fr?4za?2?;\G?$¿2?;\G?C&˿:98$¿:982_V@׿U""?Cxq ?3m? Eu?߷`js?=j?l>؞?%*%9?^֓?c}ɘ?^}޵c}ɘ?)O 8?^}޵8?a JhO?|P ?0ğ? -#g?D ٿv`?h.4?'PU.ד?U) 2[࿦H2ˤ?ɰ½H2ˤ?+nb׿d$ʰ½d$[(ҳؿ2x!?fA?"-4?02m=Pe?~A?"R!?galŤz?4\I?@|Oײ?5\I?G^~J̑6c?@|Oײ?K̑6c?W+K=0OL?Z?b/?1L9lzd?8?,o?8?>$w̗z%?,o?̗z%?m]:j,?\ƿE"4qͿ[ۿ@9>,?[ۿ@˲@?M?o>@/ޘ`sMgcy?YD¿ ?YD¿*|ſZ_ ?Z_I?@]pyM?mʡL~yBD_2/Um{?\nN¿|Mi?[nN¿m0ƿs|Mi?s*Qिy?z $@_H%c?.&r?TF *mh};I(?:?>{? _ q/ _ W4Lcg2?q/Mcg2?B+kɨΒ|?˪j?fYз>?QP( ?c k?ɿ_޿Z?c k?ɿZ?,QobF?J.W4DnN<ڴ`O?aunK?aun"[wb!$=Wk?K?"$=Wk?}J"`H??>_Wbmôʝk:fڨ\fHi?bQ/6B,e_?bQ/6oz!<;X?B,e_?<;X? aO~!?:RBZȻvޥLĵ??+??7.Ќ7?EϙI>?_zm?8.Ќ7?ym?6.ſ5Gl?Txꄈ-?h! ?Wkס?|D'G€g?>zF?kO]>7DP]>K?}?6D}?p7TGɡ?\Rң"(?-A5?[&ݿ0|gʿ}״?0|gʿDz?AT6 w?}״?AT6 w?/9Ͽkoǡ?! nE\?ϭvq?%쿴qeRy?%v?"[ԤqeRy?"[ԤAe?=?!C?$*9"tw`;oͣ?nD?\`iٿ~AZTX?\`iٿ\7?4F]h?-q8?C@yYTW2-?>]h?UW2-?Ãԇ?l$?`ӕ O?B. NH?v|?8 6ԡ`J?8 6ԡpq\=a9{?`J?]=a9{?=?)?Gt~P?*`A(x^?i,3$*??Z暿Nr$4ز?Z暿j*[n}:?Nr$4ز?}:??!?ULӡ?jX?CWN=?%o?kԔF/× Z?3_aPt~NLi3_aP nc?Xk ڻ?s~NLiXk ڻ?ʔ0?uj?8eU?;<@/yݰǿ3zHpJ?s/z?K:5)Ls/z?MyTIi]?"J:5)L忾"|.w<&y?*uGh?f'rPA?~Ȅ=?7B?L8} Կ7B? L ?56?L8} Կ56?UPJ%Ytpr?SϏ-Mq?/Uc{b7s|f?_rd&e?7foʶ?qaԿ7foʶ?jnI? ϰ5?qaԿ ϰ5?ͱ ,i迖h?eknbL1 wV`_?eU' ܠ.e_x‹ܿPAel1?{ICjNcz ?ҭRj?kŤUϿҭRj?1F@ULkŤUϿULt?r^?]RKpO ?Զ `?6\ Ϳն `?jgDk6\ ͿDk *Eݿ?L'w? ?q?y #?(,ҢV~ )? פ\$O? פ\F&?39$O?39L)+Zڪӣ?Wt|6?񽳞)s?K!Pпo|?K!Pп4o ?m9bQݿo|?m9bQݿ=q)1Hm}?Ze?~ wspioU&3:4M?Luw.ſ3:4M?¿뒥dpǿKuw.ſ꒥dpǿ{/Uӿ?)?{? #w:$m荿ܣ&]>UL?%-[ÿ&]>UL?҅t_48ƿ%-[ÿ48ƿI+ҿ.bKG?b?2_} ?l:)cĦ6 Wܭk|??f`%'꿯?X??+"U׿g`%'+"U׿E ߿ޒo?pY"EٞNx7 Y1ڞjO?NxaڞjO?Cf?Eҙ{jԿNxaῌEҙ{jԿ I Zj>?JX"v?W[A()MivH.Ǒ¯?pJ¯?з˸ ʿp< ¿pJp< ¿3$4ӿ&Ik8?1_}^q?LISHo|ܕRww?"1Sm{?l`ǫ?"Rt:?"Rߠ_ĿP ?t:?P ?!N߿* q?`Wp?G|_?b/W:_3gQ!Jӷ?gI[^-ƿzRB7^-ƿo{5?o9?zRB7o9?`nbC?坟gt2s.es_X??鏳ٿcY벯'ٿcY53?f?겯'ٿf?$ 셜?;'.t?h!?86_2G<5_2 D^VY?G<VY?& _˿J^^.?+Wv,/Yn[=vBY2?BXGBXM l?-fx?G-fx?;XF_˿bĠ?J*Dqң1.o?F> L4TҠ!'?6 o'f]y7@6 o'MPX!?_'f]y7@_'`L`X`sx0?@􏧿1`-kzSCS[ ?M[տ%ʭ?M[տq)IL?X5uR࿣%ʭ?W5uR࿖`- ?ٸy?x 5`.s XBE?G ] Bʄ{?G ] R?䲻Cʄ{?䲻2ԕ!?(?FpfI󏖿EvփAs?KGs?s9?O7ɭ?r>s?O7ɭ?VbTRӿB0'?Jk\̉xڝ}?J 0?A2͊%j>?u?˼ڃ-w?Wv?-w?6aIrWv?aIr Y]m?tB塿_[?HM? l׷0ʰ?jlt?0ʰ?^x߿HiQٿjlt?HiQٿZF3ȡ?evM$N?Oѓ??\ Hп1ɿS|ȡ?Ea})F?gܖ?sZҿIlJ?(PP;+(m?Jg䨿;+(m?J@mѿ g3?ﮔW?;Ԙ9V#6?va/?#6?Nz GI@"Wua/?"WP?srKdɡ?ݥ?jB?6bF?տDW?t%?CW?\? }ٿt%? }ٿl!g?X4+Z?25?GA/?cL?~"m??r?m?jF3?"y?r?"yUTb??zR?Թ.?R?DV?WC͓qqm?{0 ?qqm?i|^?p} {0 ?q} hs?MK[?&l`䨿`ÞJ?uq&?w,a?UaU\?rK [?Y~?I? un3Hu?I? E@Xտtn3Hu?Xտ@ |e ?U;;xC$O?e2 "? F?$9ӅT?%9Ӆ/A}?%dT?%d! 7@!ĕR?,L)F6 ?/?$}xX8{a6TB\>˿Y_>6V4e?'_TB\>˿'_ ?Y$i?B L[bܜa?$FZ?GdvR?9ۏ>ώ?-Dǵ?3/s_UT?bCUT? K@v?bCv?v8?hS*?h ʰꁿ%h?#3lM?US5ۿ Bə?6@ y Bə?d?ȟc(,?5@ yȟc(,?mX??"?9Wz3iȞ?Er_~?t'Ԑ?4''xSؿ5''xw~#g?:Uh ɰ?Sؿ:Uh ɰ?g[6?@_?&̛pyEBW?6K~?0竭?nTM׿nDP?4]d?TM׿5]d?ҪS?dnB?+d'MnDa?'z~?PQ!B?h?3MsE!^^?`Ρ?.|@ Xy?d# Xy?FŰ?&*npÿd# &*npÿ;#޿LDߠ?&Kv;?~?n|W?d ?n>, (?&@n>, (?ͺ?7&@7_RxؿW+瀡? Tx?H"?+?o?,Ks LR?+KsLlC+Ʒ?iNa LR?iNa:!M3?R ?Kt?Jp???o ?ޘ뻭x?ޘ뻭_q'?OJnx?NJnwUy΅(?sËbV?SMhh?Y[?<즿rR??_?z%l?gkпz%l?|??hZqgkпhZqQB?š?/&6a?"@}?-sF?й5M?9M?^+vο9M?'b?QϿ]+vοQϿ^*b@?ҟ?wv?}EguL@?nW? = y§ :Hg_2pѿ}nlѿ_2pѿ,^s᪼ϫ?}nlѿ᪼ϫ?'#b@'͋?{+?`P˵(˅1C7(&ȿ jRȿB7(&ȿ`h򜩿EEr? jRȿEEr?D@3$?攻p?{,{w?O?… /6ƿqD?!"nRs7qD?^O4#?RJ?!"nRs7RJ?5?z*O?_Vn?`5?ҵ/пӳ?g\ӳ?Qx&:?.,?g\.,?<}|[?$QiQ?HQǽDij(.}?@ї?Nޡw-K6DŽS (?TZA?Tj?jz?Tj?_+Rٿm^޿jz?m^޿z?8e7EG?D?8e7EG? J=^?w 1E?w 1L瓗?&n~?MdBvCJ@_? w?n?<*oR?n?&̀?:I?d?Za8?(5?h?x@Ϳh?'=˿4-y@Ϳ4-͠4?H@Z?=l?KpT{ҧ?D&?KZDN?ĝ+KZDN?j܃Ue?IGjmֿĝ+JGjmֿȁ?*7*?Vb?Rn9%A?ql?f$ ?gJ[SſGf?gJ[Sſ\zg ?DſGf?Dſf #D?6?:*0?8U-?.U#ti?ٙJ!?]%9 5ÿ?]%9 5ÿ<~ģ{?Fņ¿?Fņ¿D?8kӴ?ˍS|?Yq"N^4˰?z鶠?K?*#hu0dI©?YyԿ?q?lȿ5plFfq5plFf &?PsΠ?%do?vX71d.?'ƿoz9\d?nz9\5(^/[ǿ<𳂪i?d?<𳂪i?áv? ?zV;?b.os]?>^ã?w]AS?w]A?|4?P !S?P !;L*?N?OG?ˈk3U~0y?C]?%-it`?%-*FA4?2it`?2àKQ?D筡?s`A?7܀PxM|ȡ?)[ץ?B@_b,!"?cȨ?_Ƙ?]_Ƙ?Zb&`Nv?]&`Nv?D[ͤ?|?*&?  kmm?`"AQ?>W?:.ڿ>W?Җ#aG迁ֆ?:.ڿֆ?TlNi9((?M!ҀĿ=lNi!ҀĿ )b?3c#?݊8?Z|7?UqIԝ?_ҿN,%?< 6?O,%?ŪX#psƿ< 6?sƿ30?竵? )m?@#2?7W?3п ?fi? ?y"4ƿfi?"4ƿ $v޼?KK? Jk MNOJkz?clt1R?Ȍ7Йy ve?ɔyǥ?? sXT>߿Ok;9m? sXT>߿ۭt?;ObOk;9m??;ObD.?[gZ?c"?X?( r\j?_ǿpE?_ǿ~5/y"ԿGoE?!ԿG['; ?VMH?_Z [,? xӈ*(?$,%>ij ?FCBÿ%>ij ?&޿k\*IοFCBÿk\*IοoIy?wQ!>?ѯh+?]?} O(Ī?z"m5 ¿O(Ī?Mt\a߿9f̿y"m5 ¿9f̿~r\?x?_헿y"ղBn?RF"X#7?co淂ɵ? ^?ph?3кph?v3к^>ʧ5̿ղѓ<;?3irCvy?l`v?Pm]e_ا?ӳqѿe_ا?+u~imvӳqѿimv[gBuĘѢ?\ߏn@?zǰ ~?M0Wش?9^%—i?(п9^%Gwܿr7—i?(пr7! t?332Ѣ?3aFȔ?^# ~?lƮ?7քs_Ͽ7ք$ۿ>Vs_Ͽ>V*$B?DuُС?uk?d|? e?gv С? 7~Mv?t$WO?ZBiT(t?:e?"/ٿ:e?迅VM ="/ٿVM =`J?f7L?~ H? :?qsYN#H_;ɸ7?3OL+ſ3`2 OL8f|ῐOL+ſL8f|῝]"҄¿RU?ֆ?Ӕtw?!go?ٶz?lgˣxc ?mgˣ|Ba#ͦ?xc ?̦?w:_rr?bO@Do-?;G9on?Xa`?Ynr55X̗?Ynr5%Ӭ?m]J5X̗??m]JtkېZw?TJ2pE9-ڪ?#j?W?o_ Tƚϡ?Q 4_PпDEm+?_Pп&in5e/鿾DEm+?4e/鿑,p7u(?B?d4>z>d?tl ]Ϳm<v?l ]Ϳ[ 忞on<v?o B!?B0IWzc?Ji?`-ճ0?A(mJƿo h^ __?o h^{PRe?"xǿ __?"xǿH(TI/(?%wi?mAݐi?)?Y|ǿ͢O/ʝ?͢jr[kƿO/ʝ?r[kƿt|01i?w2qa ?Ej[Ϧ?с ֠?bE읿>@7&??`N?T?C$(?pC$(?!x@ؿ}3d/⿝p}3d/ȵ-.?+p7 ,?S(%pe?TG?@O:?{}?Dέ Mz}?H[Pѳ|SLeEέ M俵|SLe_3;?j?_*x?4س?XRw_?1?wl1+?+}k^wl1+?U6?%:r?+}k^%:r?Ru?V V?A^;v?=6{y )?z1h `?ЖgB?u6M7?z}4u6M7?* n?TʸB?{}4TʸB?J?*9? P!;}?f9FX?,6b|eXS?_?Wsk?n]S.-iz?z῎8dx"?iEqN?8dx"?"'i?>ĵS?iEqN?>ĵS?Qu9@S#|?h^?R?]hΫ~?|K?tI?|K?cCw?l*+y?tI?m*+y?{vt?&bU|?=Ԉ?_?@%O>uc&e*`=C6?^ks?a=C6?+Ǫp/?g+ kk ?^ks?f+ kk ?6$?&MDy??!IEot8?(:ܡr?~R?-w?N5wm@(?b0d͛?(? q?@J?a0d͛?J?14|?p1 ?yVs?,rv?A7qɚOM?>A?K?>A?k ?b w?K?b w?þ-|?;(0?ḧ?}F?X?onk??(bd?z~Y>?(bd? I?vv?z~Y>?vv?LL? ^ܢ?l4?[8ll?Q "o?2Xor?dby6? ?cby6?ql/1?ʼn? ?ʼn?3&?>܁ p?v!?~ u?SB0?M,(?e&?gy8?K5HHO?QX?HO?}?D7/?QX?D7/?!Ӏ}Wܣ?efVӹ?_s???gL*T??GII?kL?gL*T?kL?iua_5(k?"(?F:c? |J?gͦ!4<~?L62?"4<~?>Lo66?K62?66?T"ONп,uVf?W]w? K᭫?#pǍ?KGU^`,??`,?,:I븤E23Ӫ??E23Ӫ?$R.Ͽ?!!y?$#Qz(?Fw>ÿ֤#?GFNߺc? e]Ϩ? طI ?aV#&?aVSR5?둾(+ #&?ꑾ(+ 0p>~h2?l{sl?E.?mf##mg?ĵ9K[de?ĵ9KO{İ?l\de?l`t, T玑?M5ïz?vu?|Q/wr\Λ?\9bǿۿ6?\9bǿ̌?h[Nܿۿ6?h[NܿziK=?"@s8?:K?3'GmgA-_,?n5ȿnj?n5ȿ9YV?ۿnj?ۿuIH6c ?(ԒJe@|Uvg?KW|y8 OKb@џ-Q?1Ĺі.ȼ}ӺB?1Ĺі.>pg?kͷȼ}ӺB?kͷĉQ?/ nx>?^ ;?4Lr?]Izu"J^?]?q?E19d ?q?: f?+E19d ?+wBX? c?iRJ?z ]9ADǜuh&?m ?KL ҿ(p?KL ҿƲ ?TԿ(p?TԿFx?ϡ?ݽ4D?y֤8r7;? O ?_:iӿpZ?_:iӿ:dJ?g$ʿpZ?g$ʿȜ?y+]Nϡ?l?)J3UqbmvC?B Ln?xVQU ҿ~-?xVQU ҿ`k?Coɿ~-?Coɿw8藍d)?2ab?CoXxCq,6?q:M?Ǘ0?/z*g86h?/ZХ?/2R׿uL/2R׿x] S?tL S? 9Rҿe6;??<+6MVB(?T'r?Yп#SYп%m迈&h}B#S过&h}B圩<<տ`9NSŢ?+ =?%$|]q*]?_Vſ*]"Ŀ}*]"Ŀ_>{οQɿ}Qɿ{Fۼh&7?` @?#/7W*x{z?Ev8¿YgP7¿*eqYgP7¿U̿u?ȿ*equ?ȿHU$Ψ ?\˒qknHF?<2?l/s? a{P8=2A?7w{Úkru?T=P?"vH?T=P?~V2:`8!vH?8`8y)u߿?_'0 ){f)A?X.V?OL?X.V?m\sv$n ῃOL?$n vk ܿ^Y]?)Hr;yue)?)AUʿʄam?^iʄam?6U@ܿȿ^iȿU=`Dz4V?9Ygyn= 쇌Q{?c#ƿ:קX?``:קX?$ۿ-jȿ``-jȿ_l_p׊p?J7%twi6Y&Jy?B`?v}hxJS@w裿sQ?q>k{쿃z\gf?>k{;b9+6Qz\gf?+6Qѯÿh=C?eo2/1(Y?aQ|+-?MNj?yY?iBA6?Bu\>?/0,uB?y_`aJNMo?3} MԿğd?3} MԿzWտij+ ?ğd?ij+ ?ATE*?Mg9? Twf0v?3 g a?q. jFO?q. jޔVTfſ&W ?FO?&W ?w`uJt?a^?ui7Wl얏?o#`?Npe?FՇv2Ŀ73aFՇv2Ŀz?1?73a1?ZM3x?_b@j?*ܔ%6Zƒ?%\Pe?*BE?,a˿H^j,a˿+Q"d?!C?H^j!C?qIz??F1x?Rp?4Cz? iKacFVP3? cCb?a#OUIѿ0].?`#OUIѿY?@/,?0].?@/,?Ę(?tR?~:?mD~~̄.{E??0`&?0`G+?fa?&?fa?§ۿK?pok\?I?XGEAn?c?YANdiI?YANd*?%…?iI?%…?XUVP?&"Y?]ǻ?m?OtP[?)6Х?)ky^U?*f?6Х?*f?KlF/?RfsQ<֥?EJ}=$L8?>??`5?+gqo?"Ͻ?-o?(Xmп-o?jmϿdh?(Xmпdh?+ACT?4EL?bw?br[)?y?gB9%?|@? $ɿ|@?ypp+οU ? ^?{u6?V2*?ٹ?n0 ?DZ˩?o)uj]ͣ??ϴ‘?伻譙-?伻5cڳ.?譙-??\Z:{p?O??K?H)?A@?pD٤lOG ?pD٤;Gde$?lOG ?e$? 7?X~I@?.)?#C揎? ?gZ??8߿1*??{qٿN&m?8߿1*?N&m?5V?06t??b|"g?_(?RJl4ֿ:cg١?RUoPf?,3}?!C5hmo?FìnjJ?Fìn#9ѿlA?njJ?lA?nޡٿtj?7|L?$U?U4ê⧉\?lu?Q^?%D[H ?Ͻ?9,?Ͻ?Y ;{K?:,?K?3wv uǿX͆U?+bw?x?؀uYj?_t)u{?#7?_t)u{?Wt׿uc{?#7?uc{?O=?ޠ2׿S?8kr?(f>8?ڭ53f ¿7!? ¿u?QL'-s|7!?'-s|Cjӿ^U?qq{s?(Ӡ?@h5GbjH@tt ?jH@tt譸1#3*c?t ?1#3*c?^;PsԿ]?]<ߴp?Oؠ+鶙&?Ҩ<̗mV;ݢ?PY9%4p0Ŵ㿴9%4pe`r?0*'0Ŵ0*'u]wҿ a?WmIȒF M?B$e,MHχ1 4i·1 u?#]tmXa6?#]tm˿#5 ?vS %k?lR~?YɴRؿU?Rؿ:?쉶U?쉶ws ̿H*8?wjB }@/sKrZq+ Mˠ? g;cn2KnءS@4U :DX4U <6=@%F:DX%F˚AK[??ИWY25ᦿISÀ۶ߘ? \q-ۿ 15rJ?2P\q-ۿ2P^UɿP ? Bvn?]@q?M'6?sP˻׿cK+蜿tP˻׿+?D$ĿcK+蜿D$ĿJ:tnNt ?a;\Ir?X@~;mo?\_Bl? ~տ%w ~տvaǎ?:_۰wÿ%w:_۰wÿe؝0Y)T?rd\]W?xvN@Tt?_eǢ?3穃kNU[ŔøhM?@jfn?Jό?jfn?K̈:8?Jό?:8?Ƈ5k翠׌?0x҃r!?:翍(?ҵ&?(?e<|տTE?ҵ&?TE?\Y8ǿy?G[?yq.1i?\52%?xLUQё2%?arC?3 EyLUQё3 EYrx?p{HO?h(?n_Xſ}zF?duĿ}zF?`C WֿeuĿWֿl褯D??m;}?d%n:?8?>d%n:?&:C?bP' ?wQCZ†0PN32~?8if?9Ͽ}n ?9Ͽ2ҿKoxx?}n ?Koxx?c{DR?𓧣7͡?fSx AE??S?Ń|:d俲c爡?׶?{_翴}5P?|_#ᦸ u}5P? u)aZ?75c?ZpZDb? Msj΂☨?ײ?Nۚeҿ ?NۚeҿcBֿ΀J#꿭 ?΀J#O>/ ?5gآ?&&^ ? Ab$ӣ?4?R]?Rj ϿY ]?Y 9 w?̓?G@@<+iAÇ? ?i/'`i$?i/9ݹwϿS.w'`i$?S.wWzHZ?螌V?s\y?ڒ?H?_\?Khdw?!urP?Q?trP?Z?b ￝Q?b ￙f[3ף?'T?8OQQyz ?m F?'.H-ͻ?F?s|8?)'.H-ͻ?)!2-i?:Bw-?# E(>OH?Dv?+PX% Ag?+PXXGʿ =q7% Ag?!=q7d$Pǿjî?b?Ahn.uT?.1O? 6MP?Yz!? 6MP?W 5ɿmDZYz!?mDZ4_ ڮƿʷ?9%%kǿfr9?ޮ[?O&}?G{GԵ?cEr<߿sݿڻ5?sݿP]g@y].I+ڻ5?x].I+.&7UONW?hܖ;?)L¤,0Z?ҐXLɔ BѿTi?ɔ Bѿw??QHTi?QHҟwqNO?VdЖ?60.Y˲}l?۳bҿ>Jˆ?>Jiѿnˆ?naQߕ%? >ו?Q̅ʕ?VjRп oe_ ѱ? oec_[̎Ϳ꽱cڼ_ ѱ?꽱cڼHA?}\?W8ԭWĿiG(P? sG z@y贿q_0"ȿ6zrĿ ̬?5zrĿ.!YS?Ŵz? ̬?Ŵz?4  !V?eJrR2:8Ԗjà?0TXB7?0TXBA6r}˿~by?7?~by? E`k7eq?Z`%?U-⎿#-S$=B?vQ,j߳?t9@l?wQ,j߳?&ѿډ ?t9@l?ډ ?]k⿒wj?㱂W~?P>X$ʉk?~x?#Ŵ?~x?| Xm"fѿk@?#Ŵ?k@?(RlZ_?\5+?DߤԗJ?FWj?EOS:\E*'?KɆtƾ ?KɆtƾdȎV?ٿ ?V?ٿRU B8y?<*;?!vIR߻u~v$ \ֲy#It?$ \ֲKV߿ m1ֿy#It?m1ֿ;;I 7ϰ?X$$r?0 fqw9ƿgt a?Kʞ϶?gt a?ԯϒOſ4a x?Kʞ϶?3a x?)࿡ީ?n#Zu? 㗂;yyLƿaO[ٺ?HS?aO[ٺ?RRrĿ?=HVJ?HS??=HVJ?3~AqM?C Ŧ@ȗ?)j$? <'Jid Xu?apЍjR?1f3ЍjR?$NEW?foH1f3foH8u8I?b7 ̠!jsW:A|?7濑)EK?g'ɿ)EK?ag95xg'ɿ5x翙djd?w"jt210]k&1#o?GyK_~rc?}$i&i?7j6~$i&i?nӿUs67j6Us6iח@?u5?nќ?ᩨ⩓s"X?R?BdmҿR?S^]ֿ߿Bdmҿ߿- pw_?'K?aup?~ߜ?hQ7?9N?T7ÿ9N??u8ʿT7ÿu8ʿa vVe?iu?q`-Р?).U33?He?Ȋ2~ɿHe?DG3?8пȊ2~ɿ8пQrz`?mAL?FDk?]d*â?ݿ7?ԔA??!޷Y۔}W ?tl?Qeſsl?,4)[?J濭QeſJ7f@f04M?h£V1?\k)?3/59p?z@:Cfrz@w^Θ,@?j:Cfr?j eS??E.T4?#ȉ?m{!гo. @h@5SAh@<Չ!@:|3 $5SA:|3 $@y-`m?:?P'R?gB?QmB @NB@@D =OB@@o@D =hTnƗͧ?S*e/? -}?El߮GgA?{ ?pw?CmzIFȿm@p&(@ n.1p&(@0@ANG# n.1ANG#;9_Ֆ˭? 83?.؊?ٽW Ͽ#/@x(A)@rzQ>x(A)@>51@%Q"rzQ>%Q"{F ?DG*?״!?6ZԿJwГ@%@!Q%@*/m.@!Q;|HE5?dz,I(?h6?Ԛ~!տtx@vŎ?%@ zvŎ?%@5U8,@%"m^ z%"m^X3lZ٧?FǭϿj?>, ?`y? iۿҸC?+ ?:+!@ E+U E+$uB3@}&@ @U}&@ @~)#l U:M?Mf)DqG$?XN?pnD1"@_1,ѹ >_1,הyJ4@dM{@ѹ >bM{@EwZ&-?D2srdB?:BW?z F@ )&mDq )&F`%0@%@mDq %@ʼR.,?<)ʡ?*)h?"L@j&bѪ%r j& %H!.@F;n@bѪ%r F;n@+$2?IЋ9W&qYul?楶d?kسQu#R?0 է?~w3Y,Q^uhY,M @Np0R?Q^uhMp0R?(ǡvۿPS?AU*rq7?Ŕ.Q?zi9? 4  T @mD? 4mD?~mZB6m?@&LJfͿ4r?wӴ?Er @1zY2 #H1z@()W@X2 #H()W@|1M?벛2ο8*^?ns?f @,Plǡ~w,P&S%#@}@lǡ~w}@vY"Iy/?=_@)jRފNU.?Q.?#ĈO4l+A?44?*[y?;ڡ)쿸*[y?H\+ѿ!?;ڡ)!?hƥDZ?Nm?ES}NԀv??bkq?z3)c޿akq?R?Ya^?z3)c޿Ya^?rn?eməס?zUl6@ʂ?xz$?Ԫ4?hz$=s¿hz`}7?E?$=s¿E?Q?y>!?:#),7,Ւ?:+*?rȇ?P cgȿP=?ˋi? cgȿˋi?Fa?VK?bN*ο/̞fK˿vB7?0̞fK˿YI\K9vB7?\K9^տFa g?Kj?:f!?F ˿æ˿Z?æ˿lbE ,Z? ,>d ׿i?3?;s¿ w*xl6T??9뽶oמ0nsf??b>qk忼?af)~k2ߗ4ÿkr@w?~kr@w?j+?k]?~j]?n$˿:Z?cy?:OH5$s;],?k?,?WKI?^l ܤ?k?_l ܤ?Hj̿Ν?vOpj ?ϴtcſEU@?e.2 G[?k뺿xa[K'; tV[K'֍m?"?< tV"?R g?iާ?o7 ь?mVսZZCֿMTxܿLQ *k?>)u?Txܿ>)u?Q%?ZT8?1d? l? g*UP1?d?6%=ZIֿd?zr? *e%п6%=ZIֿ!*e%пtľο3?faD ?)0zKV?Z@s8"%Q?(?Ȅwڿ(?\q?uӿȄwڿuӿ߉ ?̿+8?#?QOC??/ؑH?\G?r?ﲧ?r?:qF?,?ﲧ?,?0?7?lJ|?f&?!a=l"?Z9,A@L~UQ?vZ?L~UQ?P?桔ſvZ?桔ſ`\N?/~eܥ?3ꅌ?Ǚ?8OﰲI=XZ@:ՍK @gTd9ՍK @Ȝ( @G gTdG .[׿`xZ?ؐ5?m?v'-pQ_@8@FO;8@%=08 @LR#SFO;LR#SUJ]ֿ :ܹ?Hhn?f$c?ZxڿOCi3? R$?hNue? K޿9>u-@:1@woM+:1@:I/ 1@\++.woM+[++.&5.?.U?^'?A,?;P࿈q#<-@ mj%1@(. Е) mj%1@}@`0@9Zw-(. Е):Zw-ފ^zaѿoC?0?e5?X5 ⿉c$@a1)@*$# a1)@:yN&@p18$+$# p18$q][F'?"#轧?/?xDno}ȉ#@$p(@B>J$p(@2&@M뵎#B>JM뵎#A93uwg3?lQ@Z @/:WI4 Ҝ SN?Y/gL@pXz @yk43MkD4@ʆ|@@̯xCɆ|@@<Y8@.BKͯxC.BKX1#ݕ-?f@ @kl2Z {0@89F)!@pAV^&0?Ծ#pAV^&0|,e1;$?E0? /@B =@m]}%@~ #m]}%@^o !@p ^0 #p ^0z [7 2?fn @{IQ @Fs8? OrKk @O44 @_3@4#@-JA4#@~^8@B=I@-JAB=I@,:E9Ypq)?I. @V 8@v@E/@"t9?:"t9!fU5@7 ED@?:7 ED@໒Zo9r?T$,G ?U0@\r?z@b¼O&13!b¼O&@4!@NoT!/@03!NoT!/@ʪ>g5UN?©;.,@? i2@QU7%!> n QU7%0I^!@T -@"> n T -@x,Q˴5^H=? 7ڿc]??cSt?=(nN?4 v/"*?U2'A2@ XOt4L)XOt4c9r3@U֐.@L)U֐.@UA~yC;e?wuz*?Ew}?C0@3 Q'3nH3@$es-@ Q'#es-@@s ܓX?Hwi%W?(ic?Y˪?Af)?ծ֡?ۛN?*.P?o*V?<&[_2<&[쿄";ǿ/!}?_2/!}? ޣ??XfX?.ZZH># ?N9̤V?a?m6?&E= 1ٿm6?&EȎ3?t ?< 1ٿt ?8Js0?:vZz?* dٔ?@?z?,zD%޿,I?_ ?zD%޿_ ?|#p? bS?}-fd?媓z]ݥ6_R?)B?= U1؃m㸘Gz]L۰?-GJO?z]L۰?ʸm4E ѿ\~&?.GJO?\~&?UZ?C?/wj?ߟ@&~XU\7lra_2?ra_鉋п6T2?6T@%7?54)$?즋mpe!~L:iJ=`VR.X-ֿmh@uQ.X-ֿ})?N:6mh@uN:60sJ? .t /?|tLIPpQQ X'g:H!X/$Cؿ)/$Cؿ-CL?(Ƨ)(ƧjG ?Შh!? k?H%&?&b@I?3]G|?GCt"?DQ+?n$?^XHf?n$?x/S#Ŀg:]XHf?g:=䫺,࿳f ?;68胿($[ގ}%%?L?2bD?ص'?2bD? ieص'?ec\;bڿ1Z~?O:_=?0af?[B@?@m {q?@m '/ ?lP̿{q?lP̿9һQ?[:Z=}zXa?$Az ? tد G? tد4lL3?Ȃ˿ G?Ȃ˿R:n-Je?f|ĵ?O #+ڰ9?rQq?Ͼߑ?-yrD}6|?Osپ?Zʞ`?1sxYʞ`?1?ejڿ2sxejڿdLOF"?D(q?^u,j,OA?Е?\z‹?_v߿\z‹?ƢȂ?aѿ_v߿aѿ͍QԿL\9?n*壄?br(L5-ZHZXr?F|4?'NF|4?JȎ?_3'N_3*L?}"?ߨʂ!ߒ? @d-oc~Mz̹?}f C?}߿)?Q})?s}?;MZF?R};MZF?* ?߱H?%;+D)?nh&kpyИ!/:7쿓?"FGSο?A"??"FGSο?PX?^q3?Srbb?̢HDŽ@˙cNԭ:ӿ\ϫCD?!J7?]ϫCD?gla}?lqL@(?!J7?lqL@(?R ^p?m?r晅?V2i|I:]?aԿ (?q? (?K?uƉ?q?uƉ?-LS?Zg?1Bw?X*M? &?kf?iNl#?̾οL{?{p?L{?R? E?{p? E?;?) !N?yLZh? -?DFdJk?.k+?:ŕ?-k+?#,%? ?m?:ŕ? ?m?Kt3? X?Vԙf@Фr?HaNy PŃ?@Z+???Z+?vxਿ-2p??-2p?M?`?v@_gʠkq?F>Y>$?Sgn??{170?Sgn?>͸"y/??{170?͸"y/?'Q(?|Ț? :a]?Õ?鷿nEJ?;ߠ?H໖?yܚؼT?u?jt̯!t?jt+K4i?Yᔿ˯!Yᔿw@ry/io?:;p?nK t?c„*.?36l⿟f濟36l&ep?r"džMfr"džM4;F?c ?V ?Ve&j[?ߚd|o?0r,?l1z!O1l1z W? s#4c? O1 s#4c?O1?J΋)?)?u~?,7Sh?]{?BY D%BY,/cSo? 2? D% 2?j?Ҥ+;?X5շ?C4](+X޶|?f)?u(?_D1M Gu?UwHu?V˾俭[)tUw迬[)twW̿]|V??I ?1?HʨYc9?굈ܿc9?AM"ڿr H굈ܿr HIJa;?B E?/aJș`)=t^V?スo쿭ijEܿスo"%?;J?ijEܿ;J?؆v[?F-t??mv9.ߤ?>([-VCݜ(}?k!qkµ-k!qkGG?E̋_?µ-E̋_?;WM?هp ?JP'2 пZŷg!@Y  Y I|@i(,@ i(,@o_?@bb?D\Ŀ2;_Г=h?2RI?t{é 'wM?; ު?wKU?C3vKU? 8O?:8T˻?C3:8T˻?7X?1@?ܸ b14Pÿ1`DcwϿFCIl[ܿ-aOB>ÿ-aOB瘹`?8!(?nFt)wx?*Es]?)eǺ? ?? ?l? d)? d)?31*?Q뢱ד?=኿15~]C=?W~N-?}Cc?W~N-?)s'?h9gӽ}Cc?h9gӽ' ~Z?6ؠ?8(;چ|?1)pе?{ڒyZ?F*9U(V  ? [8v@? [Ъ?tE޿8v@?tE޿%}?SF(=?uhbta%?6u..;?IE&ٿjݎ?IE&ٿ$ ??ڜ̿jݎ?>ڜ̿Rv?†I?5d}Vy?~?d4'jOY}z?$ɿW`T?$ɿ "? tz{?W`T? tz{?ҏg%?f[g?iw?!?HRlѣ?Jתhxƿu+j?JתhxƿxϹ?D^~t+j?D^~,6?`qf?DZ?ZA‹?- Q~?*5?w I^o?cTz?'4?pe3geٿu=޿geٿ(K'V| ,?M tL "?4(9 U?(T!v࿕(TܞT-?mn/W!v࿭mn/Wu)?6z?vK+1ZMZoF`%⿂%`%-I:A@9?N,係%N,FP?A_ IϿA_"A;ʺ*׿@9O?eraj,m?.?ts?mW _.Zh̽:.L?Zh俷*ˋ?uLLɭ ̽:.L?uLLɭ ڋP? R.p? 9h?C.d!m?+%tv/ͿoO .؝?oO<Է7?+l#|ʥ? .؝?+l#|ʥ?vwG?bX|v? ]=??};h?$;3!V̪п:Z]{q?;Zj>?R,W? ]{q?R,W?Z)?R-Vc?loq?;oߠ?C%?\@f?Ie>@f?N]?Vj!?M?a?Ѿ oW 9t _hܘ) _hV^Pv%8!b?ܘ)u%8!b?'&iҢ?JmR?Q]Pㇿ/hk`?<3C¿{d_B2fِÿ{d_B?ȿ܉?2fِÿ܉?'f6?Ѣ?§?#`vE?S3=({H" 3ſ({H"*e߼? 3ſ)e߼?--?ؠp*6?r&즿W&\aB,Ϣ?(3a0xryTC'?6@῱)ۄ?6@+h-P48ʚ)ۄ?48ʚ?j:Ƣ?J/u7^B.Rj?7aZ?d83?#6?c83?1dDԀ-#6?Ԁ-=1?x=[ ? hC`?&!w]@4nn*6?WEQCĿ% ÿVEQCĿ_4?iVR?% ÿiVR?]"` ?Sy ?Zxv?} 7y.5 ?MХKKѿN,|ĿMХKKѿ#䮚?Z8?N,|ĿZ8?owՕ?,F?Π_煿23?8B5&g?0P|NRrsHZQRu$T25I?ᘹ15I?K?rmSr?ᘹrmSr?p?7XY?EpCl!ㄿj@YxH?!gMt>% ?"<ꙣ׿>% ?5•yf¯?#<ꙣ׿yf¯?]+o?4/~w>k~xZD_Fu?r?vL~TϿr?WeƊdhY=vL~TϿY=&Vc}?jslǿkK?-&?jK?r ѿUꀿ-&?Uꀿҁ?!Bjj? ح?ODɳO?I?IQ?6?ML5S?.@?3%1r]?N, ?Fu9?N, ?!jlؿN,fʿFu9?N,fʿ禗ᅤW/V?=Jǁ?"p1?M?A?F@?OsTX?F@? ¿td㴿OsTX?td㴿4֋7>u߂?t~tMK,?pMZ?B+Pk?G+G?G+)T<ƿ+{z?G?+{z?o1@ɗ[?|qnsEs?Yռ۹V??`?RLW㯤'Y,sQ0eĿ&Y,!W)9?վS? Aސ?DT#?$z,V{ǿ28ĿV{ǿ#&!;}޿28Ŀ;}޿[gErhyP?u ?ʩ0 n?+Uh?p? _':P _]dwS|?':PS|?"ڼ[Qd?xDc ?Yhbm?OU?|sf?PR୿ ZLPR୿l6dii? ZLii?lzJxsu?uԁmҜK?aڣ?Ny0syAR?%Z?) ?2Vd?2Vd?A r Ipn?Hpn?ڌ҇j#В?:48zݞ?OTu$?Z|No?X!}Oo?jA')GC?X!}()GC?Zg/ٿK 4?/ٿ2\"C?K 4?D?BaJ?YZ2f0?+I\7ۢd ƿ?d ƿ-B^1ҿ(Z6;?)Z6;(`ڿ)QE?V. "8?mB&? 8s_XWeƿN?XWeƿĚr2ӿ2fN?2fQڿ#l<?!jiO?>ZJ?mh(s1?&#+}Ж࿈g?B).5?g?: 3&ֶ/ 1DC).5?/ 1Dn*\$@Ƣ?;%)Lt?O{w4Q ؿҵ?3)?ҵ?5̘ѿ#.'ɿ3)?$.'ɿ I m4?u-lq?w9rR4G,?g5G,?g\c$$ʿ4Vo5?Am/r%ɽ((?a0fqBR@d\"{?@񿿾S㠐Cuzq?j?=XT|?[(>XT|?˞??_int?[(_int? ?KK-4!߿4OE>?CAɿ4OE>?A ĿJ4ȫCAɿJ4ȫbK?pg)?)w?4h?;yP3j逢?ʇq`6 Ym?)VA䠩?\["ѿv?^[?v?!y?i׹Һ?^[?i׹Һ?#VoԜ?*ٮ?OG󔿽V@?WMٟ?g ԿȤ7?7s»Ȥ7?~Ō?Ǯƿ8s»ǮƿnV}?yf0ߢ?jLKLt x?YvlJ?'J1:VRp;ke_ÿRp[6?^16;ke_ÿ]16>M?'s?Tt?09u?oM?!ߋ:t3JSO9¿s3JSI?hǸO9¿hǸd_?{1?m/| ٪? ſ,Cy?4jb,?Y<^Ѷ_kuq ?V}uq ]1ڿ)t??V})t?W;?v?Оa Ni-}R?VoFR 6:nEXm6:n㿬#;'X[?EXm:'X[?щp?(oM?7j1_5Yf%m5m/?JԖc?пKԖc_dpq(?8x]iw??п8x]iw??2@{?;?{v `[fMzf wm{!V7R?H6꒾3ϿG6~G?GI&l?꒾3ϿGI&l?-l?kBǢ??n?=Hv1γSǿqQ /?hW?&Z?2{ԿLĿnjMLĿ7ܿT8P?njMT8P?X+Dο %R?Jۥ)0?f꫿l–гkT)1`'?SEE˿[NI?a"?Z`BF?}4?!N\?lߵ? /RN?GBff߿[GBff߿vdD?9먈^?[9먈^?-Zy￸8WǢ?Q&xg &g?UN=?yUe?Cп+пCпA,3?'Ϲt_?+п'Ϲt_?bϥ~:80?S4^w?yÉsh?am? :<+[lc ? :<} \i #L?+[lc ?j #L?ɏɀοaĊ\?pTjYv?-Ow[2?1Iث?5yC(?5w5:,V0?yC(?:,V0?jbQѿlDK?$ R2!@d?KH?pUF=Rm{GM"h޿y+UտM"h޿Ǭ^T?y+Uտ^T?af-횪ze?gZ?;l}F?5-ܿʼn3տc3W\ǿʼn3տt(jzb?c3W\ǿ zb?9U<翀I* Ţ?G!z?H]΍o7?@%䢿V?OPV?O{ۮշD?PշD?Կv+~â?!ӵhz?t/)7nLћ?K0>*0S%<*0Sr_ V&?$< V&?a þտoVn?_;-"r:M06r\qM? /Ԇ"& ⳿1kAr5U(?r5|,?P"ӿU(?P"ӿKIpſ|>?|Yƈpcst+wo>пRAr25:?RAr20dm?]*?5:?]*?6dֿG͢?=(| a?9xaaxpCxe ǝK?Bxe ǝ<ɗ0ֿ?K??b:)zӿBŢ?0O 4{]e?a啿Aydѡl#?ydѡ[UyӿQ݋?l#?Q݋?03wԿ!᪢?R`P?dsm%JЃpΑWUۢ?B?Be+z/Br$+/0?Dyƿ??Dyƿj?????~+I36)֢?0"g8sk&n 텿tÓKg(Ұ?%uvU ?%uvUjbw|< ?|g7o?+{?IS(?+{?܎%ĿXIS(?XEÿr?q\KĿxA?fu&?Dkbq?'ζ]U0fg= ~#VquĿ#~J?^/VquĿ^/3VL!e"GLQ ?~:٧pŒW{,y)s ^s/Ϳ-R?C-R?q,?yv^k{￶Cyv^k{￑"گ?B$o?~Lފb&H?v uCMH?{ ?Ɩw?{ ?+lÉJ?5пƖw?5пAd`?ޫ"l?qL bf$?ys쁿ۃ|?nA?{?mA?=O\*~?@ο{?@οP?jmG?!?Т+b?.s?%IۛfQcݠ?a?#\3Eț?--Fe.?|M?ARD?|M?ˉ2YT?CwdARD?Cwd鿢?~f?@^Y?&8Kp?N/[?&Mxl?(;{?唥BG?(;{?ܬ8?gF⿤唥BG?gFN/L?/ 8^?_7ob?W1C#yqPյ!?]=ik?Ե!? 7m̫?=dҿ^=ik?=dҿի?,Z]?co]}꫘?0:xR;Fy?y:?;Fy?A>&?h/ѿy:?h/ѿGܗ I?Լ?#buۣhro(xOyĬ?섹䍡?4o?K;e?o{?GMb@k{HkE~lk{HKe?Eܒ?kE~lEܒ?f!?1>͡?BU~?x'Y?x3? ?>t!`>d̫?ϭ|?t!`濽ϭ|?15g?μ%r?3 |?uD=i+?:sR+ݢ%.M? EWZ9{ÿ EWqI ?|԰Z9{ÿ|԰#i!>?lq?ݖGI?م'?*T9?Y~ÿY۽?ɋ,~ÿɋ,6?L ?:?A e{s@mף?覢 ?rCedGZx쫻?76g/쫻?:AIsn?Ɠ&n%?86g/Ɠ&n%?kNO?be?'Ț|aU4լ۳ĿoHs4οլ۳Ŀ b?b?oHs4οb?2YYi?/D`?MB_XM"ӄ޿S9 <ȿ*S9 <ȿeeih8?F/DƏ?*F/DƏ?rqރ%xW?2\aG]?$ՈPCZldtPƿ;_¿dtPƿfX3??;_¿?9\ cV/]s?p!Ѧ?@ߋ[ L?!xd?w֊?Av'ǿBV W?Jz=>'ǿ"jE?BV W?jE?R%?)IsNUUdV&v#ڿ/)1y9?.)o뿋3su1y9?3suZ7|?"xXzPiLN9WΖ??aLI??grӵ˿Ğzt"aLI?Ğzt"@Gy#m:0{?$/yCn?p Қ?;&"2}^T;$?@B?/Խ2?%T,ОۿQc@/5ܿQc@.ڄ|`N?/5ܿ|`N?%_BבͿ}57?W-?^?C$X?YLK~׿`?C>/Eѿ`?>B){?C>/EѿB){?|8:QMʢ?0tR?}jWmXy?sHRKl̎㫿FB?7QHGB?Ԃѿ2N+R?7QH2N+R?Xw?o;zˢ?chه?l5{{?ÐJ-6?l+r?짿l+r?Pѿad짿ad-e?eᶤF?0zl!?$ʱ?.s hPX?P5v$?b׿qǪ^Ъ?qǪ, J>5?^Ъ?=5?~t?:&^?Z*&f?LNi_1KؿjUJjzZg?jUJ9iSq{@ࢾ?jzZg?{@ࢾ?j#m?*w? nR&H3??Q o2724ƿ}F?ycq?~F?oԿtyBycq?tyBCQ|%?F?' 2\?ooŎ? cLg;HƿC1vT?V3ڸ?C1vT?7 IvԿsv]ҧU3ڸ?sv]ҧV5?m?g "mv/}?SnDƯ?b@?&G?Doޚ? :\?i?Rf ?PDsRf ?R;8PDs;8ݰU??t?hˮo?DW ?Ff? -@b?r?iۇ}r?A#eۿQרֿhۇ}PרֿPa?ȷ?y[?^x5-Б? Bvku?hly2t?hPIily2t?. 8 u iPI8 u ?w$9 ?:> ?prX%?r+szܽ ?Iײ?ނעIײ?Wǀ赿2"Uނע2"USr%J?O? ,?X?{KhSK"n?#q ?~W?ݛ)ЩO7g$ ʿ{{Z3ٿ$ ʿQ_p㿅Z=?{{Z3ٿZ=? rS5?Oe"G\? O?|w7t?^>O??]ѿf_I8?JmLl|?T8DĹgx–~t?ggD?7>?w–~t?7>?x;gпs8?1vOgeb<ݴ|?"BH[s:qS PjR򿀣fKhPjR^ g=fO<ѿfKhῙO<ѿ5"sO?0)¥봇809SYy>Ͷ?*tڿ}Oܿ+tڿJ"Wj~OܿjUѼKϿ+*l?#j *Uoː]'WtLdAW߬?GWHWY{2ſbG?bG?h2Ҿ?ג*l?w&3gA\x?Asư.?,?8aH-|x^,?8aH)L%!#ƿ2X?-|x^2X?7,ۇ?SĠ?,?}y0U%[r\?hQXѿHJD0?!UA&ӿ?[r\?!UA&ӿ$5?˖?V}W5o͘ݒQF n+?Ng?zSѵNg?w6kT?{SѵkT?W#7ܤ?)K?]SHkiMvgv?}*?ġX}*?~QrxB?ġXrxB?NyI ?J#xM?[ ƺGMۥuP)?vBѢ?L#y nɤ@EaJw^P?y Jw^P?WBir9y ir9L~K?j}QiL P3-:O,l?,l?(+)Tg^o˿Tg^o˿+}%̖_,<?)Ljq?GK8ʵnf(#ϰ?qq4F?r 9'?qq4F?jÿFq 9'?Fr4xsݎ ?)(li?H+}h)7$!{?i ~?9?i ~?sQ 'ſ5J9?5JoIOٸ|bm?K &?>l?xIA1'z7?0ZJ?hrT? ;\D>rY%?([ H"?kg@'[ H"?;&Yjfkg@jfpHF\?0q8?v{?;,8?~A??e&j?e n&?e&j?bD=~ڿ{kd n&?{k'%?)J?Lz=}?AոPS?顔M?$替sZ(=?B6t?sZ(=?}:ёRB6t?R#RRE?4?+7H?",{L~?ji ޽?/? ޽?iRm\yw/?yw [1X?Ϻ౿$FCVfDA?`WQ? \vV3iؠ?;Sce?׹v?!o \̿Wm Yz!o \̿t,nxs?7:O*?Wm Yz6:O*? ȩ.:91Xn?Ξ\nG~`?a(mH?=(8п?+`~ᅱ?S?)+?+`~)+?AD3Gμlćl??MKG(?:bԁ?n{?L¿ҏ.??aJ颲?ҏ.??k`gz?7kaJ颲?7kjkĿ4l?HC.v@dW?b?sBĿgQt?(~Lu';?gQt?uX;?B(~Lu';?BtYèƿl  ?>+?2ȌͿlq?Z3SY?r}q?FBh1jfݮLA,?%PnLA,?i4?Al'?%PnAl'?ٿھ ?l/Iw4fku?z*:igMF?\&~?,蜎\&~?CnJK?] ?,蜎] ?y ݿGgR?)a"qdk~?,|hm?Ir?_w۷?TIǿ_w۷?]؏@?P]]TIǿP]]6q׍ʿS?%}*m췇&s?g؍?Vt[?Vٹ?|h$>ȿVٹ?rQ?e̠}h$>ȿe̠[hKI^Ͽzul?۲kF~tD?82z%G6!1Ct;Xpu(^#O¿%Of?^#O¿j?mڿ%Of?mڿMW3cu:?򰂟[9Z]ġJhB c흶+$X?c흶}]??hdjJտ+$X?hdjJտH7 w?B/qFi.ÔʡR2d.??}X?L-l?}X?`.?ՙc̷L-l?ՙc̷h)U ??1q/ <Ӵ?Y\)?"ZR?Y\)?)Ӹ?6"ZR?6mӅ? }[?; 6Yr?gyO{$?>{$?d1f9?m?t'8ƒ5B?!p7/ =+;1ZW?QQ?W?afH3I@3w?QQ?3w?/ ??2 ?O6T opmsP|?6VPԆ?P|?{D?AB柍?6VPԆ?AB柍?sW?טr#"?vhuS?ca5teȎK~LYͿLh?ܡWʊLh?id̿0Wr?ܡWʊ0Wr?ȍi?,08?t!O X3rZw&Dp{οhl5?:E-hl5?^x'ǿ򼾝9?:E-򼾝9?>-?Jep k?'R4v`?H |Q7 ,?\3(S?$MS|Hxd@C;?`@b\N?C;?˩?,`@b\N?,鿡E(3@D)wZ?9a_?~Dr糼rѵw}AO?5*̲f?_{?6*̲f?D ߿_{? ߿_@&Izd?zAD?[AvaW}?|*?M1 a?{t}Փ?}ѻ?{t}Փ?+ɿtإ}ѻ?uإA.w-V?!U4N?fBy?!.-耿ـղ)?E?kh ?E?KwſR`'@kh ?R`'@RDƛp?%6o?I绿?M?aVjr‹!X?8?)TɦʾK38/tRz\?K38/tUhIy%?Rz\?Iy%?oKbݏ?8w1ddk$y?Ve u?BUq.{'2c?c?#%{c?6Y1@j~ͣ?#%{j~ͣ?C?tV\Ӡ?o@D wG sZb,?:*?CB? 2ZrCB?Ht?h? 2Zrh?> -c?v1?te䓿"z旿Z}+l^`?]<7G?e\<7G?N[q?`Sл?e`Sл?.i?׉?bF y@i)=8D?Y*~?f֨Y*~?)  ?a[?f֨a[?B"?](?Xpi Gag[Y?'{%VWP?3lӝTR Lhuֿfmb@ LhuֿGqڿB}%ȿfmb@B}%ȿYYr*6?܇*Mv (aCvI YÿIş^ Yÿ> ?R;̿Hş^R;̿YQ!iܿ~)?4!ȷ;_?J>ׅlI?rT?eﹿqT?vl?U3?eﹿU3?#!7?4Űն0?Z_؁P?0 O xچzD?ELna?X3S1FLna?X?eOFw?X3S1eOFw?p.Cڎ?jJh8?]WVP?0麮?\{g˛.m͸tcr?^-~h]Dпp]h]Dп!o%?rf?p] rf?XJL?_?TxI? 똿Bjrǝ:v2Wr?֘>?ǝ:v2W֘>?.p?#?|*'?ixV;mp͐It#K+? ?a6? ?9M]R?C;a6?C;K0Abg̿_it?EU}=?m[Z fcd? xd?'Q%c? xd?X4<}q?Psಿ'Q%c?Psಿu͵ǿP%W?5AG?.H4,p?=?]&D0u?k.u9G??7?W @7?r)`?1[?W @1[?Ɗ ?BU,Z?t&֯±?b%_ЂŠx'b匿߬D?@?B=?@?~Z>?ݚFuB=?ޚFu DU??mIGJ?5_y?lDRÂDWgrnLp?6B?Lp?~zq2ÿ6B?zq2ÿ_ OԿ?-B?S_?bWOZlVsT? p"3s?T?M͔L/ p"3s?L/!e,ҿBh{?!T(d?7x?+? %d?d?Kɰ٪?a?7%?;07%?V~_PlLj?;0PlLj?C}C%ϰ8x?Y;\?[$Y*ԟ?ZD?9"ѿ??{Zu X? X?XϼHн[̢?wB?Zr?JF|w? Ŀair#?aL?air#?鶿AᾤaL?Aᾤe4ۿK .ɢ?".4-? )ˇ?AǑ?,JQ)ǿPb?CR?Pb?T~)_(p՝DR?_(p՝B޿GQ5?DMKƿ=q&B`??mi4ޭj?tеגDz6 8e( 8}V2;6?e(V2;6?l6=>?]?sC0i?rBKg?WQ싢 ѿ|pf?ĭzQ|pf?ΕxAC?ĭzQ@C?BsΠ?gq?+:P?HO?^O˞j2/?.uK奔j2/?ſKg?.uK奔Jg?+d=8 $a?Y Cs?|b?_Tt?4lԟ߿?|J~Uԟ߿??#¿`pW.)?|J~U`pW.)?iVD`[?0?D3΍M5eԿ?y^ј? FܾusԈc̍LFg??U piԿU pi_a3ʿv'?Կv'?{ފ|э0?#n4?!n;U ٟiC5Y?*qa꺿*1ƿ)qa꺿_7οi=?*1ƿi=?'Cֿ &??ϋG[?hx;pq\,?vdKIG(?vdK|Gx?IG(?x?ߌT?GG?I9? xv:a$ , tl?Qw꺮vB?Qw꺮Bӱ1R~?vB?1R~?Yx?&`'g?Tk k?CovO GCgSD?_C?@}_Ij<7=t?S i_ "w?S0࿲$x/6?i_ "w迲$x/6?E?OCbٮJ?Â?d{RQR:濱唒?O&Ŀ唒?+WҿX;Z?O&ĿX;Z?]!?O?yH7?_?gx+lG\p?ՊJ3Nο:1?tbEk?:1?GZÿ^?tbEk?^?.ֵ?A:?>5w? gCe]:j?§nyпJ De?ר z?J De?c`׼0$|D?ר z?0$|D?M? wn?H8P+|~?^󲷿hn?҇T?MF霿iD_h?$a?_h?\xuG?N?$a?N?s\??u[݈sŒ?*i?RgЛ?MK?R_?V?MK?փ#*؍?(gI?R_?V?(gI?g 5?S2ӡ?]?ta?/?sU?9?Yqf?9?<αp,ɰ?Yqf?p,ɰ?N;v?͡?բ&? 5 ?ksC?QBٲi ??4H`tv?i ??U"jĂ?4H`tv?Ă?wx'?$HR?LkJ?ZIYg{?Y6@%+?Q͈?{A̝R?S ? i/?I%jB?&>FɿB?5yHk@|S俺&>Fɿ|S俬3?Rl?斗U?l25t?N?OT%|տM96O?k\?M96O?dv?ۛڿk\?ۛڿӹ)̃?`~>?o(?& 1E?͂.k?7XgG;̯R?Th?gG;̯R? ?OTh?O5I?Do?8?V\?pܔR?'>{i?]Ɛg{O:?.?g{O:?:.t?pY.?pYme? P?t`cYi?uq&?-?NՕVۿ?!B>??V>"䳿ޖ]?U>"䳿k@ykȿޖ]?ykȿBvI@7ؠ?P m?~V{?-G4?%'Â?%'Âs/?L~c7?M~c7@@y*}9?6MΥaO+?.ytf?خo$ݯVsͿ$ݯVܜt?%K csͿ%K c\/I?VQ5?2|6󃿎?+}3מ?'KgAh#1H”NiBM˿#1H”K ?6emNiBM˿6em9B݉|?BΊO?sУG]?$FZ?N,%$?z11MƸ?6Ml?WсٿVEL-&-UEL@I ?-&-I ?~Ӹ?[O^?fZjR g/?W(c?` `/ҿx< 7ύq^v< _j?Ʒ?8ύq^Ʒ? t?GM?#Ňr#qЙ0M?~q?f?w,:NKؿw,: pE?~ڧ?NKؿ~ڧ?E5/?%?R&p'Ms?tyq?q"? 䔿%D׿ 䔿F2?Ȭ<\?%D׿Ȭ<\?P?pй?RSmd?D *?'z~? ($|? ULQ?#_?dds?nRۄ@R6> ?R6> ?KV?p6ȿ𿜏p6ȿUDP1LϠ?>3P?\֜?엞h ?QѴ1;?Ms?7ˡWMs?6%?N& 7ˡWN& !~ZȿGx?g$f5t?xig'?6wkPpjžݿЈ"ܿpjžݿꖲ ?|nVпЈ"ܿ|nVп@0EnjP?(ȟ?:?{<դ?nY3T>AԿPU>AԿ] >Y?)k οPU)k οi&)ܵȉ{?[,-u?.e@v?:n?#KŴ׿| *kz:| *1o^x?w-.ȿkz:w-.ȿO0GF ?1?1y?& B(v?; l3?~k׿)wd4 G)wA'?r ǿc4 Gr ǿqָW қ? V?S2(?i:I>즿d~X?qҀ?},wY?:ɚrA%@7Ŕ],'7Ŕ]9?מּb?,'࿗מּb?@, @&d?h%(Ɍ^?,d[[@пoAῬп(Pq? +?oA +?we^@ Yǡ?)> ?o?f |?,?j@^?x?jҿx?-}*?aE4}?jҿbE4}?F)?ơ?Nm?YYQ}?5s g)bѰR?Q e?_HydNѿQ e?+?Xc,s?_HydNѿWc,s?3e ?TUM?Bi?"{ALgu$4)Pb??@?g:וS3yߓ1ѿ>4Eߓ1ѿ./Se?>4E./Se?퇢t'@?2̛?'*kT`#t;;ωɿ92@N;ωɿ'"+?92@N"+?5Box@6?sp?7i? f0\gBɿx+FD?`?x+FD?ϧI? | ?`? | ?-Xg"?ȱj ?4\TQn?vΎqr?j0{y[(ҿ4GW?Bj5|?4GW?1?~ z?Bj5|?~ z?:E?&?MӦ%H.}?I h(???%#h}ZE善?t@AAFN?ߦV؃?AAFN?#du{ ҿW^tߦV؃?W^tURd?r!_?Iϡ8j?wg c8ʤ?R(?l /E?R(?6UÆv?~^xcĿk /E?}^xcĿ \4?Q3?: M{}?h?t(Q?*>̢UQo, v?nUQo, v?f)iyi\?C~?nUD~?:|?_`e-?%ÛQ? ^x?v5юuJ&տq S?dk{I*ܿp S?-| r?CƝ?ck{I*ܿBƝ?F?r%?vԬAڡ?hSLg?&{?(t%}?;,-?v6p?8?v6p?X N?*c ?8?*c ?e82q?􎾗?boK{q?R?u??0y?ͯi?a뮖~?j L?a뮖~?ÕW?f?j L?f?>d^?d?,?!Ԩ2l1=?Ss?)5?lK Eȋ?׊y?6f3u?άп6f3u??4⿇άп4⿸w"?t??6;|?$qVgd?|?JX'-?Y}WſJX'-?ɳ:o? տX}Wſ տ7JMg=?(?=-?̙(jW? 1b?JXs?Ƥb$ƿ ɢ'?Ƥb$ƿAN?1ұÿ ɢ'?1ұÿgj(?[ki ,4?03^t?PVP? ɞ_?pp?A`Lÿoh?@`Lÿ0"?Ի1oh?Ի1LDG?|Fփ?@nN?f$k{"N^4˰?YTE;?n J?,S\5ԥ?cuyտaOj;6mbO1i?G2#?j;6mG2#?\cP?RmkC?lT>?{ qi? Eȿ$!qrh#!Ņ?_׽?qrh_׽?JF\i?!բ&?ŝ?(/o~M?hO۰?0Jګ1 ?0JګUY?xʾ1 ?xʾb}yw?F& ?f[%U?x>QE|Ԃ?©N?l wO)MVF?l wO)#ʹ?͒7BůMVF?Β7Bůs0yn?Pq?W㳠aЮ?w Px\v?]`ӓ?j t*WÐ?Ψ?]C2?6O]C2?BuFenU?6OnU?3?hs#Dס? _*[E?ӂ1rrKE؜?Q|?lo][?b\ֿlo][?n`Gn:}3p?c\ֿo:}3p??;?n|a?( kC\6?d GoC#?GI?oC#?*Ϳ篿GI?篿 ?TR?%?L\cy6?oង?$|>??#|>?ο53?53X ?s9Sf?]Y߶?O>Wp`Xm[?=1^?o"Q?ʎZ6^SI?qC??^SI?azN~?qC??zN~?OrKt ?ܹ'#?p r?C?T??WU]O?Sڰ?Kf?tM9Ῡ¿>Sڰ?¿HpokZ?#߿ni1F?;>#߿oرmA?W`08?>4pU)?)ڸ? K?n;*ϨƿkA?m;*Ϩƿ>Z2J8kA?2J8$fۯ?4?b/*YE}z}r?wj?6I(՞;ZVb?4ÿZVb?ha޿ jvɿ4ÿ jvɿ̃au?0+?|nR~4 V?Y?˕up?g6¿up?d΁޿b{ǿg6¿c{ǿDȾck~?ޝH?n>QkTP?x I?Z˳ӿx I?N V~[˳ӿ~Dt;w܀?fZ?/mژ?RV-?vah?gxd*Egx#MW;mvῆxd*E;mvslxsȿuk߬U?HHji*](N?4q?}Ɂ?9JBf?9JCу񾙸Cf?񾙸A[禿8K}?>9G9+q?ޅq??j̺?j̺t'<ܝHS?HSulf}X)?^kٓML9-ڪ?K3M?T5[?>ELΥ?k`(or| 2Կ?r| 2Կ"7 9XPz쿴? 9XPz쿡) V?ik?F5pzk7p?ܮx8}Xп ne?}Xпə]K* ne?]K*促t J7?J{:b?t=qA-7q?A ?9I#ſ~KB'd ?~JY|N|+ǿKB'd ?X|N|+ǿ1] GMa!?̋?Lh?/(p?zak?Sd}*3ƿkͤVa?kͤma!eQ"f%ǿVa?"f%ǿH |kR$?]+\⠱?Ej[Ϧ?QϠ? 9$ja?=gr?=TPP?}'C[Y?@(D~'C[Y?3 ]ͿayZٿA(DayZٿx)?ݮ?,6bPj!#k?oP%?xų{?$LVw"#_6s?x`4?_6s?4ss?P/n?x`4?O/n?ʭN@D1?OM!h?߰|?pѦ6`3?[Ò?4?;LO?H[L9 ?[Ò?I[L9 ?aGi@31?&ƼH?6Ɣ?yJk,uy[4!?.u?4!?^??.u??k;i)?#??"ܓ?w %tXռ+=J?dF?+=J?މݛ?rA?dF?rA?̊@j?NQ?T\2lWd>?(:ܡ3N~?X?vs?zs`ړj!9@p ^??p ^?Ɖ"@b??b?u?2p?OX?~;5?u%<ԅ+]n?`?͔j?`?(.mf?ټIy?͔j?ټIy?:F?fS-? a:ډ?Ԣ?/Fg?_޷?9?~>`?9?A?agn?~>`?agn?fD7 ?aڢ?P?"j?h?-9?+4?"᧟֧?+4?:Tݩ?|&?!᧟֧?}&?w! (?ՠ?^.o]?~ u?Wi?{a1Dв?di?!w_?)>#UPj?" hj?#UPj?wsj?\?" hj?\?.Y ǣ?XQX2?`? Z ?x{ ?MŷRX?{ ?G?r;J?MŷRX?r;J?IU`?IB߾?{?m?X]??\_?]??am5N9)h`?\_?9)h`?QZ'ϿmBZ?F?΃?ϟE?qnQ?@ 窿Dkc?wgQ?Dkc?eTy?d ?vgQ?d ?_eTοaU'o?0bfAx?Fw>ÿVܦ.Tf?G;ZSMN?UGr xD? xDJF?v.?v.~@-=tK[?Z2I? l?{a_& ;ƥxgSh>/?gS!NfK?X-h>/?X-&𿙚꾾?E~7 o?K?t?^͏q??T]ȿe-T?S]ȿE?*Btڿf-T?*Btڿ^IT%]=yVt?Hb3?>3k$?[ BVnX?C:}}ɿu7/?C:}}ɿ-K?dY8}ٿu7/?dY8}ٿtL!:?Q2'@*?4?qن6?V&oM#D??<ޖܠ?wm?.P?<ޖܠ?.PU"п>%G^?Dfp?8V*?ͷTb?=? KM?=?s?+a KM?+a.(wq?pz?tE-?:Ho.pd?pݒѿkv Q?pݒѿ\䵻?nҿkv Q?nҿ7t??L?7A?a׌?yph7}O?Mп$-1?Mп5x%?{\6ѿ$-1?{\6ѿ!t??e?3(\nx>?R?ֳ?#eP^C?bf ?\t] ?ys]B?\t] ?pB?_:&?ys]B?_:&??C$N?_?^?Rt"Lp ?L x?X3?׿1ҏO?X3?׿AA?&v#̿1ҏO?&v#̿2wm>?5 ǡ?s?/XhjIW]?/=֑G?#}ZӿS@1x?#}Zӿs?gEnȿR@1x?gEnȿ}Gʔoơ?A髲?nYi$|7e?MK?oc/lҿ2(C?oc/lҿn?E}"Wdǿ2(C?E}"WdǿTsܐ\!)?',՜?Q}P響Cq,6?_O@?`jx3Ω?gG6X?\8C?Tѩfп)!TѩfпJ5.}忾wxO?)!wxO?*:࿴(-?Z+{?F|lM >O?C@;??ʿ @DN?ʿ5^~Z? @DNZ?.Kݿ(Y?A?hDV5ϊ?\4Ŀjÿ~J0 KsjÿXMiο*,*ȿ~J0 Ks*,*ȿr@9F?a6_P?kC+?SMyg¿QojaΝ?N -;W?<2??X?~_ϑͧv767>?"[m_&nY?0Q ˶?o %0?0Q ˶?Q{b(3o %0?{b(3R㿜-?0<-I\f?An/?s``? ?s``?aQ@ ۿ ?@ ۿ,࿲-3T?< fto٭QV a?P`'ʿ\?t渿\?PR YݿWV(6ȿt渿XV(6ȿLu*NMMN?3ktj""VE4?&zſ(W.? gܣz)W.?$pܿ&χǿ gܣz&χǿ;ynkJk?+RKgJy?؎_?㏰'eRkߠ pW\?Y?zl鿡z‡?zl鿆C`I]^Nz‡?I]^N'ZA?0 pZmZۄ`?ΰ?Q8̰ݿ@Mfz?Q8̰ݿ3'y<<`Yj?Mfz?<<`Yj_]?m?"RdD{XLuIܞi?sҽ?`lZƿsҽ??S98`R$`lZƿ`R$ia?3 ?TmtՋ]!R>3?n[?|E_ƿn[? 1r݉ط¿|E_ƿ ݉ط¿@x?VcVS?p?Ru?iB%E?{0kd}f?ӧX蓿89?54aտs#?54aտD߿+0.?s#?+0.?ܕBeҿ,nɾ;?ߝE!||jL`? -Gf J?DL Џ.?DL #j.пR&VU?я.?R&VU?KTey\? C-WG: ?׃<]?XU?PĿ\=!?PĿOn?Mۦb(?\=!?Mۦb(?)ߣ,? h?8sxc:U?GjK3d? r4_?]_!ʿXp5+]_!ʿ!?E?Xp5+E?ir/?`ap?C?DÇosS`ŋl2[x? zŵ1?3_Ϳ0?3_ͿAm?mn=?0?mn=?e}п=+"?xT&!^.Ae?LJއ?K]?lz.U?J]?l/Fb?"3??z.U?"3??vjҿDI?Z.AX?xnA?!ڷo?*=?%^VKJH?%^VK;ۻ?\b`?JH?\b`?WBns?ÑhHM?HU?i1?GAo?b}΂?wF}U ?wF}V7?Ť?U ?Ť?al*iMpH?)AJ?EJ} f7?sb?e,>T,?x34;?nD_|?T3! Q?D*׿T3! Q?~kz9տ\)D?C*׿\)D?cɱM?#A?[S}? >?0JB?=?[|?-:%?_-ҿ-:%?fϿbI?_-ҿbI?j[H?^C?rYh]?_Þ?\vX}?PE5q?Qp)5+)^AQp)50ȿ؆?+)^A؆?O?G;d~?[T[?^Q("?Df[{?E ?J;^ݗ}⣺hhJ;^ݗyEǿ##?~⣺hh"#?28/?l3UԢ?du?<ǧ-?ٹ?qy?A=*v??*?tTr?^sf^'5"9w?੔>?w? #k?R5\ ?0Δ?WRy?GI ? D?6?Mχ?6?h&"ٿM߹?Nχ?M߹?@H>?3?1Wӕ?=ȄrC?q^ɽ?S)@?#S)6? -?$S)6?g9yڿӼ2⒊? -?Լ2⒊?WG?`'Ƥ?aqM_?v[p?8??ns@(O#?T:Q.?MzF?W?ZG?cbD?ZG?EmVLPG?cbD?mVLPG?*p1Ϋϕs?.Ǯ?V??'?1ʤ~?5."?2ʤ~?΍,2ԗi'C?5."?i'C?{Uֿvѣ?fhn!H?i?R"um?ܗ?3?a7?3?p&q7}㤷?a7?6}㤷? L`ſ7ʣ?Y..?(H?~@|?:M?4q?/6?4q?.0Z]28?/6?1Z]28?ٙ¿z/?jk0nV?ev=?&<٫l?) CDO?/5¼?glp8>sb΋ڿa ?>sb΋ڿNɣ D,@` ? D,@Dk]9L G?:2r/?4;菇?*nJ?XbK[S-qҿVѪ?S-qҿZ#Тh?VѪ?#Тh?s;DzB ?|?`+֔?RT?T\ʿG?B@?G?uƿ*?B@?ƿ*?d )?ܺmU?SD\?{.?q#OBɿ)K ?&uVx?*K ?tp19L?&uVx?19L?#e-* W??ǖ7^P1l߳w? L-yKthd?"[>&3K忕Lf??Lf?Ĕ뜢?Se@?Se@XQq?N؋6?A&?KzaͿq;l?2 +?q;l?%P3ӿx?2 +?x?TYgtK?(|`?MG?` Ss}ReN?j;p|?j;p|ݥ0տW3??W3?;0Hؿ1?%Y?۝X'1D?8ϟs3V̧?ڽ#q{??ڽ#qdԿ@>?{??@>?hۿKI_? ֨?"Y?U4ê#*kx?@v?DW?5F$<2!?;Q? t E? ɦ2BA^%?2BAÿQ ?^%?Q ?R}?7ӿ ?0 ƭh?s:Ƞ?Z $gW᡿vd?R?q럘޿w:?vd?R?w:? {Կ\A?"顿[N?Oؠpf?J|]w?f 9d~]N"2S4߿O"2AgP Nr俷S4߿ Nr俴s޿(b?R"r=?k-tئZC+B8"G__ZC+B=^,~+k?*_п8"G__*_п׳T2M߿[ ?G:dB"p1?*|k[?!zUM GKٿas?N GKٿ$?Qas?Q#ES˿1 ?9aYښ?VUV?Wr׮iޥ ؿvj6#?iޥ ؿT<N?T*+wj6#?T*+ u̿'xT?t׷xx ϽKrZqK3?--O< S/"5տ9)N 西>5տ#?ҢĿ9)N 西ѢĿPQ ޯF(?.s?xq۲@Tt?HR\?xbjˆ"hT?psl蝍/tr%?^,2?0tr%? uPSSxߺ?^,2?Sxߺ?x,俟s]~?P|ܞ&Jӗ$r7?b&?Dd?b&?a~bοQL]?Dd?PL]?؜xMƿk5J?d}4?[߮@f?nkq?e{?‹/@l?e{?H#j'?6/&‹/@l5/&(? Ĵ?fgI?N}Tď_?q?V?VAyV?oM?TPrÿVAyTPrÿݪd?#?E> ?@{1o?~AAn?!Em6?oӢ? J1?X #?pn_X #?5@Ui?pn_Ui? o˜v?`dnء?(GS`?j" y mg?r?J873C?aKJ873C?~G2\Z? <8?#ċ^q9?0C3?*~wtffYoS?J?9m“y*7I?9m“~?+?y*7I?+?/V?LЌ ?@~䈿4h]_G?EW?N_οl}?N_ο~Pv?kq}?m}?kq}?) tO?/(?/ptq?S~>I?jFV?+J }Czcƿ+J `xU@&&Ix?|Czcƿ&&Ix?Y@?B =أ?̊c?kB?L ?QےXɑ?A.?\.nY?|"]翂8mf?D$ȿ8mf?6:̿>f?D$ȿ>f?9-e?4C?xe<| qWDܒ?U ?ݠ%V? W ӿȒO W ӿw^?*31M?ȒO*31M?6Bh#Ϣ?o fO~lQ_Ŗ?ˣKE?@ ?vp '׿>x.$vp '׿c?QJ=?>x.$QJ=?^"i? ?!* H^^ ?#NA?M0٢?}K\ݔP %ii??ؿd[q(鿛ljȌܿc[q(}v-ٿljȌܿٿ:w??LaTqRT+ؓ?x[ELÿgGp˿XRO-ٱ׿gGp˿-ٱ׿_ u?-?NInΡXe{?5?KԾ-ο .<倘?JԾ-ο^ӿ|׏? .<倘?|׏??:ꈥ~?2֣JHܫC,U;ؕ?r?o5Ͽ1x~?o5ϿvjơѿϪ~̆?1x~?Ϫ~̆?S4Q?g#*?(õAE??f?7JO]OK̰߂FÙ?eA? 9=W? 9=~ϗ?Ʋ[L&W?Dz[L& M1?9CcO?^? q$:!u?}o)?-oѿ?-oѿg/п$,?$,꿿FqY>h?5(h͢? ?"~ba-Z`?Q?bw,6UZ#?bw,6ru<ͿVLUZ#?VL` ՚?z.F̢?U2[? ni. ?p?~? XE2؝ւ? XE2؝q/opͿ1sւ?2s:+??嶙y?]*?oi?Kx4U»?#宿ݯÚ?v#OjFsxz?~l՗?jFsxz?y7i:??)~l՗??)T6?H,dٯ?D!2wO*!?VLqb(`?s_@?(`?3†?L+t_@?M+t=yҿu" ?J:>?|ͶOǓR.ep?cX`?J#pg?u.E?J#pg?8PW+ȿ&Nu.E?&N+oy{?Ÿ?#bI'Z# ?:1?Ԍb*Ev?ATl?Ԍb*Ev?($ƿ1?Tl?0[~Hf5?ܽſfr9?j(?Ƭb=z?,aYgJ? !_,ڿ;Ѳٿ+po2 ?;ѲٿZGC4@\.,po2 ?\.+u*I@ k ?/^ O?4qHJ+ޫMl?IaW,LпŸ7?W,Lп[q'p?b1RtŸ7?c1RtGҥ*~ p?)?zڌYvz?I1ҿ֗|?~\ϸbVL?zK&Xڵ?6?zK&Xڵ?j\п}?6?}? d[Hr?eY5?3ޑߤԗ~v?^t`?V$&[h/_c?iOLd!?ibgj HڿOLd!?i Hڿ36]E?D ?gPe^t vqw2vX?}pyaUM>p?}pyaE!ؿer ׿UM>p?er ׿"ʎ:(q$?@їp?IcXf8#ƿǑ?eY?Ǒ?Tۄÿmb3̤?eY?mb3̤?7R$`ƈSM?i}%s?C UwVzhfhJſX~?kdb ɶ?X~?^4Tÿ7:?jdb ɶ?8:?_HFg"⿽Ѓl?vuT=w!qȗ?tq?MJAئv+yKס?zZ翳?Ov5ο? 1d?h&+Ov5οh&+G7OGB?S~ii^Z- ڣ܊?("Dy-%?L0ҿy-%?Ꞝ ?L1jL0ҿL1jS~wPTnԟ?oX .bv&$?~mIzUjNGf>?%+]"$>?E-<˲$+]"$˲9&䊄ؿ`T?^bTz×ܜO?yS[pAVoWC:ӫk+w ??Nߪl+w ?ݯ -׮y?Nߪ -׮y;JٿVU?H˪VYx?}} ᝿.t4?|+N?bE +=Wx0?ޭ?~ ޭ?.̩㷿OM'#~ OM'#P?MH?١k+jDZH?W曓4[jx5G?#S?;[ݿ#S?}Ͽ~eܿ;[ݿeܿb(s\?Nz6䅢? "V?e\W{?|ڍg?'yo#r? ƿ'yo#r?RЃS?1`Iȿ ƿ1`Iȿ18³/J:??z?\1|?Z~JcP^m? <7?U0Y>%˿ <7?h)%˿/5ϿIH]p?aS[j?tR'c?]dd|2cע?rj?2w?^Fư?Y#b?xEȻY#b?w@y?X ]ܿxEȻX ]ܿk 0?Vf?"?m3?s}ܐ1E}@#!@mɀn#!@l6^#@omɀn⿝o`?],iRe˧? [ K?h?tn7ڿD @܆@}_`܆@R!J@gu'}_`gu'L~sCz??;yW?M?G+( @FȺm@z8 EȺm@aw@^z8 ^,e?܏?%?El߮QN?+?4$?uѵsǿW5 @&.*@ <2M_&.*@Gq2@8a" <2M_8a"UkDXw?oj?8t1?~U)ο[Ō @OR x+@/OR x+@6wY3@bڢ!/bڢ!/2۸+?-p^?tK+?!$ҿer,N@ߟ&@B++ ߟ&@=m/@HcB++ Hc@DP??ƕ~ ?;k:ӿQE^@B,%@< B,%@'cp-@,< ,Ogu}|k(? |.ʿΘq?>, ?Y4,?~B9ؿ)%膺? E:?YZV @[X)]!a![X)ёWAo2@CH\!@]!a!CH\!@V:L0Bd]a?N#޿tw?r$"?\!@ qնC*jD+} qնC*ͱ23 3@{?3!@jD+}{?3!@n.϶?{E6GM ~v?ެX*?"%l@&!%`rP&!%3LԳ.@:I@`rP:I@WiEf1?~4uG俒,[??pqK@`H%j `H%D-),@4{ W(@j 4{ W(@舘;d9ʣ?2]Aul? ?ò*#?o"4?9M* r鿅*@uf@ى? r؉?l1.տi -?.}]`ʵ{Z Ղ?, ?>]?&K nAѲ&K 5H @J @$Fpx $Fpx5S@J=P@ J=P@ LQ,H ?#Me&5ԖNU.?b:?=RrLA(tC3*ژ?0Ґ? υ߳?P3 υ߳?nwwIo?P3 o?Ҡ?_p^?`_Q>0?j?V?NV?0ݲ3?E;?NE;?m~Y?.Dʡ? V{㯂6?ʬm?(Jf?qy""#dƿqy3hF:? ?""#dƿ ?nM?=?1}d>?s=g?cN>(?3I\h"'̿3I\hp ?I=;?"'̿I=;??Š.\?]ر{R+mD??|G?LƬ?ȉy@:  p?B~Ѽr(?`,n.)9 C/1?1a?٩œ*?٩œ[s$0?*?r$0?H[^/ڣ?-UeҀjׂdsE?SZe#ƿa; uK?a; 鿧ꘓ迅3VM?uK?3VM?J`xN)?]Tf?c@dv?Oο_il̿C?_il̿IjylɣC?ylɣ{Qij7ӿV\G~?\d?y3ΜjP=?ޤ*ʿ T̿m? T̿{,cvJ%ޢm?wJ%ޢ}Iʟ6Hտtd3$?jC0?c¿ w*xl\}?cVf ?q +]o??+Y?#U=+Y?$qAҿ#U=AҿD(G/308?bZ? ]4o?Ж.)r4?(Rw?(%2ݿ(Rw?v 5mwy?(%2ݿ5mwy?; UiU?ؗ?\H =1lU?x? 4l?:Y 4l?M2ƥ?[ߠ?:Y[ߠ?)(cg?v?4:JE'̯?(E?{#LK?RquK{#LK?l#p#?Az]?RquKAz]?  7'?:`a?Y0Xݹ?? ,E)¿ff?B˧??p9PfN,*ӚvC?5_(?ӚvC?C?SYx?5_(?SYx?8]fsߢ?kM_r?p) &?v5ͧqV&?'ʷ&?a?L}?'ʷL}?w];G='U?$?UcI&`lg@¿_L?>ˡ_L?W8?%??ˡ%?$ѿ2%]?9a?DRm?/nkVfm۽2?T:2? ??T:?.sѿ Q?S1)sj>?ϴtcſf"n?|u)篿 "##?\.nϥz~M+ХzC&b?z_#"l?~M+z_#"l?ӿvhע?1/=b(y?s͠o.>Me׿`M$ӓ$_M忀&?+凵?$ӓ$+凵?1˿}]$/? 0 ?ػ?؀lQYq?U+D ?sۂlbֿV+D ?vGE?HuпsۂlbֿHuпWJqԿx6,?NLJ?HA?z㑿{2?} ?QIݻTڿ} ?V3?Fc~ _ԿQIݻTڿGc~ _Կ$KJsӿɽ'?GT𴢍?W=q~O/ҽ.?Vݼʉ?=ƍ;8aOQU㗎?=QId?AdCY?=QId?0s?7{좻?BdCY?7{좻?QW 5?B"?O$<"?Hc?ܓDSC="$@O1?IP۹?O1?7?KпIP۹?KпzY,?amg?#Hǿ?~%ЯF?B}v!@&D`$i@ن'D`$i@hV @uنuJy\?qz9t?'Gp?9K@ @ Ӄ@vT Ӄ@0 @*vT*l9Ή#I,B?3BN???Zxڿ;3Eh`?ml͙? |&?ݿT5sk0@0 3@2K{c*0 3@<3@Z9..1K{c*Z9..[rL@-?@D?w1#v?:d?ejM+{20@M>p3@jz"S)M>p3@>Fy2@ѕ-kz"S)ѕ-HJ%3߬_?A9Z?k:ǭ?HhG[uٲY%@w8#*@ߗw8#*@[Ӗ'@מڧ<#ߗמڧ<#*ƃ,:Fxm̺??eB?"|In#F*$@HZ')@.SGZ')@HӹC'@K_".SK_"ow6=#?m_@ο @/:WI4 JmTL?UaD4@ @ v7K5@f;쒀@iӨn˫Xp@b2H&@>r*!b2H&@#p[ʸ!@%N/>r*!%N/5xa2O?Ǚm?Ro@J <fK@d%@@f d%@0!@fwX-.@f fwX-.tW5|&5?e[LS @{IQ @Zr1?0`6u @#v`@g#h92@DCw>/BCCw>Y6Sr6@i5J@/Bi5J@#vIHF)Yt?tTJ@mD%@(Ъ.@]+u 8a[^<]+u 8d_3@ZE@`[^<ZE@JD/Ǔ*} ?5 oe%r@ݕպ=?v\1@/GҞ.@S)/GҞ.@/ V:?6:=쿽(?EįԵ}?/o/@Sl2c((Sl2ͩI1@x Pv-@d((x Pv-@zs޸4q?Bq ;qT=4?|@?i{m1$@=cO)S=cO). &@xϼJ$@SwϼJ$@+My0?Fc0↑Ho ? k ?\|#@ h̝( h̝({zY&@~K#@~K#@,(dtl&?N ۑt?"n?vR/?,T?XƯ৿hg??jbG@oW$`noW$UX?cO '?`ncO '?9Zj?^?'L i&θ?Dm?/iV@H&zϮڣH&zT@ա?Юڣա?:?} 3?]{3ǿz?G?@N)MVyW3 @>Mg?)>Mg?m8XylZ?Gԛ5? iF/ɿ¹k?/BO?X22U@$3Lsi@$3xZ! @s.M-?Ksi@s.M-?tvYkci?}J#>+a?I"2yO ~?"?w?$9=?gH?PD>l(f¿hNǯ濉4_hNǯbGҿ37t?4_쿡37t?pk?1@?z?pX1?8h&y;첢? nUX&R/< nUXŻ ſW?&R/<㿶W?#^ ? 9S?)\փI?38y?-q?xΛKٿx񿅧 E?+1"Yi#?ΛKٿ+1"Yi#?gXq?j-?qip5Ma+YR?V?pV?s*fA޿s*f~1?hn>?A޿hn>?AS?4Gs??Gu?00]ݥbd?c\/DJE^ 皿6$oFT?N?FT?RoϿYf?M?Yf?T ?SkTVQ?WofnX?x}.I|[ZcJՉ+S?Ӊ+}J п˨S?˨fEEn?`'?F.;eM<ᎈ8$w o߽fֿ*?fֿ}Z ? V*? V5?`t1?CʮuEtNNKm뤢bqf׿Cbqf׿2Y9ۡ? 3\?H%&??oډ ??3􅻂?qӏW?gyBG?qӏW?LR?UbOgyBG?bOC@[n?~ʉs7}C3ؒ?N>?E:?ۨM?E:?I=_Ã?$PeۨM?$Pe㿁^~,Ͽm?9w_OAKj?nt[, t? .5GT9hg? .5G+i?BϿT9hg?BϿo¸E? R40FTIW6f?vL+ɓ?@N1lzꢤ?@N1l k?οzꢤ?οT$D?@ڹ?3*YՆB+ڰ9?·Q?22?{,y]kfmߨ?%k ?VFL?[WFL?sΖW?Okԇڿ[OkԇڿZ{?p t?攮)k?\l=a!T̛?8Gߒ?Y?shῈY?cz~> ?WӿshWӿ:8?EY7!?GNL#?+Av'Htm]9?j?=#A/aj?7' ?Mrɸ-=#A/aMrɸ-c)n?O%?Ay?HV8R~kp=iq0\i?Z ?yκZ ?橱Z g?IK&myκIK&mb忴?o ]?`fn?O!?-Q?]q5?N|.?,)ޕ?ͼcN:FC1;?[Of1;?M)??\Of濥?~IZ?)?O),?QXKp/R%g㿌RV?!lѿRV?;a?fֿ?!lѿfֿ?=˳o?G^?(g N?c`ㅿrT䁿Б=`ԿFs>+?7x;{?Fs>+?&nP_P?Z\10?7x;{?Z\10?y?p?c"=P?]W|=/P-Cտ5L5?Dq?5L5?Q?؞hֶ?Dq?؞hֶ?r?d?hvÿ{Y;9S?X*M?la\?רp{?> Y2?'5r¿&Դ?{aF0?&Դ?YY1? C@{aF0? C@.=?ЈI?pfĨ8B/?+>g`hc??bD?'Ih;? PY?'Ih;?"Xợ?UA ? PY?UA ?ߜdN?5P?dVixmY^[tk?kd +ȇ?*$o'?Yz?+$o'?V)'r?Yz?&r??GN>\?~]bm0 i?ya03?QS?tQ%?QS?S𕧿dۭU?tQ%?cۭU?AЉ?aB[?}?8Ӗ?鷿LԻ۠?+ZΏ?IX ֖?ְ_aS3O<EpS3OxI?9ӂ<Ep9ӂ Q@4z?SNO?A؀?hӀ?5CCDP߱??[R޿Ǔ[R޿%QP?* lȓ㿋* lT?́Nجݡ? 2[?,~,?1:Ld?5E?k2䩿'k2䩿kw?\G!`T?'[G!`T?X9^B}>E;?c5?X9^c5?W.g?Hn^豍?E蛝?a9e ?(+&JAfܣ?ɩf?V0Lâ?2 |IP&zO?nB&zO?k[迻  nB翻  #6l-;܇nrz?Elj?;Uױekq|wo?~Xj$?pxW1GFmqO9?A;T-7?A;T-ώ??vԿ7??vԿ P?[̇?a} 5)؈cd*?n&!8GC?n&鯟@8q?!8GC?8q? 6?a5^?~x7U?P,οfaY -@<8-sXB<8-tFo@R4`@sXBR4`@?`?̤Qd? ų+?c~пnLP鸿iVL@p[: p[!D&@%7@: %7@"Ã??+.eSC.Ŀul=h?rD?vHjw!6kBO?L ?a?`[鿟a?da?ܙ?`[鿭ܙ?1Tdq?I74?}ķ0?U)JL?$?u/g6?Qv/g6?xp?WӞ(aӿQXӞ(aӿV??p>t?s¿oF6Ϳ+Kwd?)H?x@Hx@&b@tOHtO o.H??4?84ſ5пV%̶?B[E?΁~G@-΁~G@A@ E- E|v?+]?LiѤ`5ta?qRH?ɟL;Z)B%z yڅϣ#/42?D=442?o ?O, ?D=4P, ?#!>?6,Ž?wF2RdІ1q?d*z?KRA_S?z!޿KRA_S?f;n?$cNz!޿#cN促D'*?w'n?*_C8pqiܟK?? ?]<$?g;-]<$?f_`?ϽA˚g;-νA˚Jk,.?y &`?C)_Yo=>? g^+6j?O[x垿<\]?s?ԿVmV,ۿ?ԿH?Ex VmV,ۿEx ,j?8r?oB]ao+y?B^ozϿQ.SÿpzϿC=a޿%ýjQ.Sÿ%ýjnZhj?%B'?a 9m?Hm6e\l?uZn?IX?uZn?w7-?OG[IX?OG[xo=`/?T44?gOGqh|?'\%G7vt?:"?~_*|'?:"? ͸?1Qĺ~_*|'?1Qĺ ?ir2\?43v;p}?1bH?jJK'Vn3,aC?C7s0Ef"?qQ˷SgyrQ˷2g*c{X?WO~SgyWO~k@i?ꍢ?@~eCwﴒձ aP Lu俗P Ls?JX)AuJX)AO?_h,?HC-?zpS wҿ֫#hdUп֫#y|n?H_Jai?hdUпH_Jai?ox#?P@?Hw{?Yez~^m+\οoj;  οpj;T?c?  οc?U̒?c.?~ F??'ugquy@?v^$?2n1Z??֖R?<{ҿi:(ѿ<{ҿ"3?rK i:(ѿrK Kin? ?Ix?B{YV99 ?yѿ{7?o ?ƌ k?T/,SkE,N?L"?-퐭RM-퐭jԹxo- ?RMxo- ?q6ؿĿh?ߐ=zĿVW>?-|??Z.?\aE#?m?N"4Z " Y?5t԰ܿ?4t԰ܿU0Nġ??Nġ?9b"O޿ ?9Edkm\َ]d6šS'48?J /}?Qy ?J /}?s쿧P6GRy ?P6G]Z\Ɉ5?0ªV_g? {*Kv%dinӰ?$AxĿFOĿ$AxĿ+&g?}H?FOĿ}H?^yq?]Y2$?N|)Ry?EX^ԒI8ՇdE?pµ Hѿbplƿpµ Hѿܔ? _0?bplƿ _0?SM0?Ivd}?*,ZV,*?8B p?ۣ, (C֧ZfJ_OpLJIy?I]ͿLJIy?Kr?Ғajy?I]ͿҒajy?DF+?9 eR? NTir&\#?U؁EAbsI? 1.})޿EAbsI? ܰس? 1.})޿?u34?8O0\3?}:9@ۘbLxn?ߞ@xK?{Wm?& ӿ{Wm?ID(x?制T(ҿ& ӿ戶T(ҿ%8S?MN?%G'mrFOˉ?B?J|Ht2?WԿHt2?)?*_."uƿVԿ*_."uƿlZ2?Ц۝?|^֬?a,ZmH0c?~S?VoG~5?"ɹF ?%?"ɹF ?L^ir7J%?r7J;05?0̼"?Q9۴?uZl?R?"?녅9?fCɿ녅9?(",Q{ӿfCɿQ{ӿ)g?!}F?hF/HV?anG`e%Ƒ?6 ?L:'Ϳ6 ?eQ{'xL:'Ϳ—'xȓ?fO?x9t!&:up@`rդ:?}g$?Ҫ<Ϳ}g$?3sqlҪ<Ϳl? u?-@?7z4Rgc?l~x?/?J?4ă?{c.90+iaտ"F:ӿ+iaտhA?Z⳺aܿ!F:ӿ[⳺aܿ"n6⿄ Zt?+̛n!z?EoqEx!NDO8cEx!@֝1?D{NDO8cD{y*(ؿ.h?H+vf*U!?ZAP$瞏D<7˿9蝢?7oT?9蝢?ӿ?&h7oT?>&h̅Ӭ U;?M _ ~?( "pxɿgT:?k?gT:?Te$ѿ( Wri?k?( Wri?#l@8?\=?ɯ,z?I?x3?Вw?0r}?_z}?0r}?{@>?x y_z}?y y0HDƚ?hy&C~?]M]?|w:?EF _ ?EF -7 Pƿ5M?_ ?5M?0@^l]2?VOjtfd}?( C\? `?˧߾3W?˧»,vǣĿOE@?߾3W?OE@?&@M$:*̣?Rw?o(9Nd?'f?Ւ?>?r?E^R=rSn:ͿR=r*mSn:ͿmU|?|Eꛃ ?<<ȋ?.V?B߬տ tdnȿ$T˿ tdnȿzM)rڿ$T˿N)rڿ:ˆ>_ܥ?lQ&?&\Yf?MӃ?7Z?̇A4ẇA %M?4wM?Mr-?%?'mM?f?nZJ?#~0?y,~(C>y,5"<%?fc*J_?${mҜK? v9У?{P?k' ?1`ۭ?ٷ/ JD?Tb JD?QE!;7z?Tb:7z?8Rc?Us?s m?T $(?ݻ_a? ǿ$Yd?V ⵬@$Yd?`gy! ?U ⵬@y! ?Mϔ\V?>Y]^?&Q4S?uI16ct?lnVԑ?Okp֨V1a>? !S?a>?6g?wῈ !S?wFk}n?Ȣ?=ܔ{Ös\?E$`G֦ ڿU\? /֘t?U\?eͿHMT̿!/֘t?HMT̿=29C}7?R].| fܢ?y^uj:ЫQJJ*?QJu}̿lJ*?|̿lI֠̿8?߂>(yQː?us(h9P㷼Ro?㷼m=/*G=ͻRo?G=ͻ\̂K7+ο?)zK,??CKš?(3ιĥ?$u<ŷ?cTAry?>Ӏ3?>Ӏ3le݄?@$?A$wA? |ա?7uX8&?[ú;?4U?XLۿ իy?XLۿuwFȿw իy?w<?;l?uqV䴅?KWo?&g?JadX?Ja~٦U?ͨ4ҿdX?ͨ4ҿGwr?Z8:?OqP\?M??T?Q(!s8Z?Q(!a+dϰ?ZLѿs8Z?ZLѿYo4P?vJ?,e-QʁMen4vY? ?@o n\٢?UnSֿלԿyȚלԿ8\?yȚbJ@z"*G?;΂EPD6?FW >]a?0m,ӿ(VPe9c>Mۜ.Q>?ks?VPe࿁ks?i?6?/χ c?S_T^|?'-f? )]f?sBȿ )]f? jc?s/sBȿr/CNK^?2J;?V^:?.ʛSڿ:?<# ȵ(̿.ʛSڿȵ(̿NXs޿4#?`SH?:eP? )v4ZP޿?p?xt˿ ?p?,Sjvwt˿jvP#hS^B?YHl?4b?_bM߿&VI?-3pɿ&VI?QjÿX뽿-3pɿX뽿6B}?3?CA|Vl};b?;yP&|?eM!?J;q?rVj`ʿ"?ɳ޸?"?\;i?*?ʳ޸?*?,ۈ?{SCݧ?Z.XXyNbS?Ԁ?kIϿ-\?0NB-\?_}v?^[ɿ1NB_[ɿ%f ?9?1?ԣ(?,B? >9?,B?j4ˤף?ܽSP7*4|/Cj?M֙N͟l?'']Cpi+ۆrFt/t ck? @t ck?"@au|? @@au|?yZ'?&pf.H?QƁᒿ#leE?{ =޿@m?AOz?@m?wYH?BOz?I??@GjԖn?T5jdkeuhJ"}&3<? ~L?Xsm4 ?? ű?Wsm4 ?=@'Do,?@ ű?o,?G낃ȿ_oo?3`\j}7TVzI?!ӑ0?s:j¶?q G?s:j¶?Ћ$.VK?r G?VK?̿zx@?LhR.Ua?Z`BF?R}?89cw?ﳚ}?E?&Pn'8߿n^q&Pn'8߿E7? ?n^q ?fxjy!__?"#4q1)]Yg?ͱnk?Ͽ( VӿϿI5>?!GU7?( Vӿ!GU7? |pʷaay?(ms?bi|??@K|W?i^20?i^rǸ ?20? ?"<n˿C|?A+r??z%_fa(?vJ(&?PO1pZЋp?PO1ptݝ>mU?ZЋp?mU?U6п:3Ԁ??SJR2!9Tq?/٤?yZ%m՜ ?򭀿bZ fi ֿhSӿ ֿ>tj=hSӿj=kAZ?#I?k?hjp? ޿n{cUпs`z;Ŀn{cUп)->.ss`z;Ŀ-sثSyhW?1js{?!ߎH?[[|`C  ]?|`C ?(E=i,? ]?i,?0WwNӿe3?Nz?uxdɚwa:Ɲ?99P{x>%Д{x>%rٕʍҳ!k=״?Д!k=״?RpտmΣ?}NIx?pB?}!c?pB?,:r@4Kܢ?⍓ߢG9`?q|+>5;g?aE?5;U ֿZ$qg?aE?[$q")ֿYPԢ?|S~_e?BXtߓ7bϤu"v^?ϤuH"~ӿ>J?"v^?>J?B׿zA=? #gQ?;5_6%JЃp?`?mXb?5՗ ݒ"ǣ`]?|Ow?td?|Ow?*mb?L?td?L?e 濌3?xv`x@ - OSaڏMz?;HX?6/k?;HX?<"Q_{6/k?_{\>濁?4?{ q?i@S?'6RY߽? ?'6RY߽?j~Aʿ)Y^㽿 ?)Y^㽿Dƿܵ|?(G~zﵱ.Byq?J A b?7(Z[ӹ?6?7(Z[ӹ?xPſ !v6? !vXO<ȿSx?z6ÿ*#'u&?l]C ?4oSFQ=}nBk 2v2ܿAĭ? ߿Aĭ?R#ʵ?4[ ߿4[ڧY.?u ](?M{AJsbq?."0Narǿqr;?bԿqr;?R?z#bԿz#0SQ?~,بv?YL 9bGP?gpc&/~X?#ta?1Po$ta?:3_}6п1Po3_}6пoq׶?F^s?a|ib#|?3b.2nϥ?x~?J Zx~?H&x?,οJ Z,ο.)?4r?=Ct?{ )?%Iۛ!? -G`? 1RFA?Ted xuU?X ?ԆO?X ?21v ?WHՆO?WH翃"wQ@*f|j?WAЄ?rӠ?5SL?b,ӌ?V5F?a,ӌ?Yq?V5F?C0?Fc?7ws͂g0<6?@W;mhNA@]ɭ?qkF#?A@]ɭ?78?2ҿqkF#?2ҿ̾o?b?ϾygcmȱE34?ų ߤS۠ U.DRS??.DRS?Wxb? :Mѿ? :Mѿ};?Sԏ?iN#VIZ(xOyĬ?c16u??@?,,X!j?"k<@6VOkG濎6VOPy!?Jz?jGJz?<<Ǯ?_?:cE??YӈC?_J~?yXz :)MzXz 俿Kq? AE?;)M AE?@?lzJr?D?$sz?'0Sh>?h ޾yrYh ޾`]Y?iNDѺyrYiNDѺ&nr?7kr? ?5EpvK?mιTtL?3TV3T/ 0?P mVP mEw"f?a,?r[y?0\nd.s@16?g? ?T&Oᦿ6`Ndӿ(?bcοӿ(?K[N?;6?bcο;6?(#%3?qo%J?7so稿9Ma/NgUUqǿ31տgUUqǿX{?;L6?31տ;L6?c#R?LHf?ZdhF!?/0^nP߿y5lɿy5lɿV ?BĜ?BĜ?nә¿6?C)'>|d?RVn=%i" ǿt h" ǿņi?xꇠ?t yꇠ?B¿]b?? 9w?TM[ L?X[?#v ?g*a 5?i'ytW*/?3i,?W*/? $~@yI8п3i,?yI8п?!ͤ@??H⎊t7gGtFm? Pῄ=Tƿy?=Tƿ忑m轿y?m轿F'?hFb?ZE] P!v})TPt|c.eSQ`x.e>ik"ֿiFS{)TQ`xiFS{)9K?J?m񈿡<wĔe}qȉR~W+&S W+pIֿK<{߇&S K<{߇0? E0S?͐½?m0? =쿎Ly>?s@^ ο? =^ ο?PWѿUAW?$tΈ ."Fiv?o$Bื B|&?c(Ss?~!B?D!ŰXp8?#_;S[ .,9sѿ,мb?ES ?9sѿES ?w4ʎ?h?{Kl)M`?FLU? i? iRfxt?rCeI??sCeI?d5*V?H׋Q?urU+su0;EO幙?p?~n8?p? BR?[`1 ?~n8?[`1 ?%<*O?J [u(Rr:9)??{?_%?{?SMɖ?_<*?_%?`<*?SJ;6U%n?/4J?ؒ?t.?c)?no,lH?_Ra?Tտ:Tp?Tտ,?(aͿ:Tp?(aͿkSBO?/ ١?){?ٰ"r}e2?+QJ?̚oҿ~?̚oҿ ?z?@w~?z?@w*~߰?EBt*??P:DN?ܽ2L`ojb? l??7Ƙ?o@e?7Ƙ?*;*?4 ?o@e?4 ?h`T>?Ҿ6]?*O]cc?]'h?oD@&?B8pM?oD@&?B4x?s Ңy?B8pM?t Ңy?F>q?p/ه ԑ?_c?4P)Ür#Q?Hs?'{ ts?A*t*cXp({ t+cXp7?E X??XwFkZO3Xzoѥ?ؐp0俀Xн]?ħUk迀Xн]?YWW!?4y+}ħUk4y+}N4e?y ?D?JldJ8[Ii>LJ?hQ?5ZAw&Ar' ?ZAwgwP?7&Ar' ?7FBE=ƩdH?dmP-k{} Q馜?ˡcRt>?Rtp8]?٪þt>?٪þt(jhK?;m28Nd˿t;?Xc vx'}(jbO?cIϖ?bIwdn?hZЖ?iZ7p|"?S%tѡ?e;rဧi+pTFe?ۯe*ۿakw1گe*ۿ8{@?jfH3bakw1jfH3bg>? --z/? .rgHт'Ω?.Ȳ?(,c¿(L]ho?ǿ,c¿ǿv!eS̐3?īJe\m-t{?FyR*r?u-w/LQӠ~¿u-w/LOQi?T>ǿQӠ~¿T>ǿh—dH?5lzi?ю?<L;y?AbxCxEzU^ ;?N'a:qmNnWN?rmNA"-I8?nWN?"-I8?29TaH?dɐ;F ʉ2\&䥿ݿҺぱQ?Һs6F鿚K*ぱQ?K*OX*?;|(V돿h=U:!>?)x?|@?)x?0ʿE8N屿|@?E8N屿Dec@I??Ϲ3 |*#>8{p*m ?\3?[g? HG~+s)ݿ\%ߓ@pw7.忎\%ߓ@>t7x/l?pw7.x/l?wxPL7Ŀır|P?EY5?ʯ3 ?T m ʲA׿dYZ?6eٿdYZ?`ZpEi?5eٿpEi?%آ??#JCuy?6w蒿 On mp?0̊ mp?7敵ҿݤ܅0̊ܤ܅e0jw?Nآ?qy?mU{?ᾈ"S.qfީt?.=dӮfީt?ڷ;W+ӿ!| -=dӮ!| J ?Ó/y?xDGkp?$ʱVУ?egHIݽb ?4&mȧ+ucj#)N!?j#)Nj%g H?!?g H?o˼بz?4x? v.1S?1@Ԗ;R{>B@Bv?{>B@VIcQG?Bv?cQG?TQ?1 ? [\q7?]*,ȿ1*&?3V?1*&?Fۮ7տ?;Q@L3V??;Q@L\*?, ?]*ubwO?p3M0^ljt!ȿup?pbض?up?ֆտfQpbض?fQv?+ˡ?j2DBR1KN?SnDƯ?\N"?l> ?ku?ce?lGY?ITQx?X9yITQx?q<pX9yp%c?9Xm?fߑ?_@M?9ea ?'?#?|2-#?RCֿ\"@ڿ|2-\"@ڿx?K?[z?d ?Ǣw uEp8?;;6?޿H?<;6?TRk()R/޿H?()R/W?+ZY?@A%L?Sy?'ӤNv8$^? Ʈ?^Ls Ʈ?XuNL^LsuNL?j<)??>r4Y?{KhSK)Yjy?@ g.?U_Г?Npaˈ˾8Hǿ ׿Hǿ9>8D@&? ׿8D@&?&RN )5?<? EҦ?lwGСB%r_ʿd6r_ʿʚ H>0Ŀ fk?c6 fk?1?4<#K6YD?˹dԍ?P8)^)I%_q?)I ?*E ?%_q?*E ?r.\ҿP-us??_#f?? E$3k0~?34^?q#?k0~?q#?ƩѿHp!?Tº&_{ss?<{xƢ?_ KxnՔOf񿃝byof񿬉5 Uȿbyo忑5 Uȿ̄| #?^}jOea㤿m/g'n ˻?­#ؿ^/`߿í#ؿ(GPq쿓nX_/`߿nX+ҿ"H4p?) KC:c[wwU?LyaդLyخƿU?aդU?2k6e?bo?sX_Yʌ;vPQLQ?'1Ă7ç'1EKǿTf?Â7çSf?G|X?j?Z|to?JSN?0Uuvrh?}㋆?b$|lM9 쮿EZo?*QRޮ?*?wڿRRޮ?wڿc ?2k?rBK@|?cSIZI bZ@E?ҿ0?ҿ >?eѿ0?eѿs ?j͚?TsN Q r-Qx?˦@?Wd`Uΰʦ@?>!.8?Wd`Uΰ8?+p?8?0ķV 8(׉ 1H?鏞`?\ѩ&鏞`?ۛ鵿/?\ѩ&/?\"=t?HPE?➝g*[}?P)? %4Rע?{o)Oj~F=a}(IGf?$"5͛`Ff?6|Tiv$"5͛`|Tiv[w᧦׎?NZL㗿=β=_d.&(AoD?P AoD?vn'|ȿP n'|ȿ2H֨2֞!j?bݍj?C]-y ;A9@Sm  ?X5Ps?n  ?OVLX5Ps?VL&GH?w3*b?Ȥq>o?.lg? >?/lg? u6ÿjz N>?kz N͇BBٰf?'4۽?Րv?xIAɠI|%D??)?w'?1N.flBQ[{?N'j?ƚJ @N'j?cAH66Y2鿽ƚJ @I66Y2鿑Rn?>9?tC?itX?䃢bz'?x%0?j#, ?x%0?% ܿeOݿk#, ?eOݿF?r@C?~$(7u?/ ^?j`4?AwF \@?WP9R?x?8/Lk?2}:tÇ"ܿ"?;X|"?kp?QM~?;X|QM~??f'@w~8?B||p? t&j%?)ZAGԿ‚`'?)ZAGԿRW}}6?f+LtSR 7/7ޏ(쎜6UG?:?-bn ?:?O<λ?LB κ-bn ?LB κUp?s:m ?H'fs@gq> ORv??`Z?? *ҒP?0 `Z?0 buR} D?p iԩ?5V(?,8|?k -pe!tJJP>(:] {&@ NYW0? N%b?KJk?YW0?KJk?VY7?ߘc(V?/!lQ?6Hoe@32?P쯎?P}sE?ݱdO?쯎?ݱdO?Fahc?ʡ?Xe\|Yb?#Pr)?b?#Pr)?ť򜥠?mLb?mLG?j?0K=0? FEò`vz3?QvN_?L>?QvN_?Ks7Ѡ??3cŁL>?@3cŁIҭrM?"? DԸ?|΂?a?CJ?(ƥhI??w*9;?#.?_յބ臓_ ,?WpnE+?W)V ¿\?WpnE\?΂kLo?п?? VXPk+TF(Ŀdi?v]@di?2/ѳV6@?v]@V6@?lj?g}L?VP(8ƒ\!?PC 䭿.!F'1yzrڿ7{s?DS?7{s?p\S@8|Hf?DS?7|Hf?s i? 6?Ndou嗿3`^⿡.YE?A ?.YE?{bξ?_>y?A ?_>y??x.?갓T?i u dBò1ο;!ů?g S;!ů?F̿4Ƨ?g S4Ƨ?o'o??0)?γsX/1_x E̚п '?s(3 '?Dǿjok?s(3jok?{/^?\CQE?DB\y?H |Q76qA?}tO8?`p?fO|`p?t?"(s ַ?fO|!(s ַ?|9?mRx?8Y% Gag[2AD'?ŜqBwm?1,ۄ zG<\rUӿVC<\rUӿ}{YYܿh VCh j)5,hXA??Uh/űޔB{S۵.ܯ Fy&$ZDFy&ƕb?_|#ZD_|>G6`FQ1?ծc?)U0@p8fۅ?6G?_ii6G?B­?SU?_iiSU?u'^|'P 9?Fg,HY?:m醳sv+WY?] h?¾zk^ h?Iw?{5݌?¾zk{5݌?7Kbȡ? ĸWVP?f? b1R6;љsf?$0{/|Vҿ% /|Vҿ5e~l?H3\d?% H3\d?;+?w⟡?T{EmϙUçy5}i2mk\5}i2ʹy?}A?mk\}A?^;a??2F?i}UCӺZ7?AC??AC?]i?* t?* t9/7οzD?R?>f4C$Dqv?tE]?}?tE]?nQ% *?Q~?QFɿP)Pd?¸[k ?.H4,p?szJ?2WМ?1"Ϋ`S?7k?O$?yy @O$?#qǦ,?yɝ?yy @yɝ?]B.j?+1e?GPO?J꒏/q4?~r?26?~r??y26?y[O ?J?)̑?91~3"O38?֧Om>;@ Ǫ?-R?Ȫ?2 %1 -R?%1 F6ٯҿmRb5?&uW=?7x?~mXף?O\?WVã?xUs?{zGz?ݻ/6Ƶ?ۙ~Eܻ/6Ƶ?.6qm2#?ۙ~E6qm2#???#8[?=Ԉ;??Mq?@+]ܣk ?"<#ܿk ?hC-Uc]?"<#ܿUc]?ob"N?zޕq? P?>s%oՂؿ!yorV!yoʓy t!M>?rVt!M>?c3?i82r?8sR?_O r?`@?PF?cĤPF?skƿ8Lr?cĤ8Lr?#87~?IHt?KVq?wX J¤?e[z?QZ?zV?or!?.PgԄ?.Pg?]K?ʹEԄ?̴ECjrSܿUG¤? Ddô?pfg\?zsw?& ?c}jAֿ$@?c}jAֿޭK?H R$@?H Rῴ'?Z0? }?03n6?d?ڦ3[ٿ5?D&4̿?5?-^L bXʿD&4̿? bXʿ"GYBTqYޣ?Xw?fić?G[s?°2 <ֿ~Pa3-y?w1`?~Pa3-y? Гe}Rʿw1`?}Rʿ5V;J0X?0l_1<T?ÿk\_?.JXsy׵?TH?TE2?q?-5zq?U?LS|?-5zLS|?+Z͠&?]S/ p?>6B?2 3?"?M)Ҵ"? ?4=?M)Ҵ4=?X- Jʣ?(H(=zE-?4Z?U)= ʿ?;a@ÿ?vbnٿhA8?oU>ٿ1Z?ϱ7hA8?ϱ7῕X* Νܢ?A16H$?n˶NZCPm(HNp?(Hhs1R?xbۿNp?xbۿrF=u?Z۳0:@4?$vox?48U?fiP?Cpoe?fiP?aznX? O¿Cpoe? O¿@ݹݿЛ Tx?S9pP+M昖?J;x?_nH ?G❳?}TN?G❳?b*d?FPz5}TN?FPz5kڿ_ w?C+Z s?ŵ?C@?έ(}?}wt-?^vHS?hj~?#3?Kv#ߨ:t5@Kv#ߨ@CPn?Ha1:t5@Ha17C@ @ Z?ί0?*Xx_?n؞?bh?y{R??y{wȁ?É R??É <?faء?,ђ ?$jT0Fq?Km?PͿJ}cݴ?PͿ@?U3ƿJ}cݴ?U3ƿtzQg_M#pס?xd?%,Cjs?Ӡ?>>ues5̿&^*?>>ues5̿Nu?d}ƿ'^*?d}ƿ8,{鵿N{Ĥ?imϚ(Mޱtg7f?Sף?6 +P\4ꣿ ^lTDt# ^+[B?*l_+[B?s!F̂*l̂C?m"y?pjɘf{" ߞNEhvm;hG:i?`c";hG:i?j`k5}?|%;^ :u?"q5XT?0?3 ? i3 ?!uZ? AĿ i AĿuJH?6YZ&?ȬbfZԘ+t?û4?hjuP?V`]juP? ӑ?F^-ÿU`]F^-ÿb-]t>?%ƞ?\p?l5?G\q?^-س?w@?Ƨ|?@?wc?aP{?w?aP{(=?P "??w?O "?0qߴ?x~( ?Zl߅??HKĩ?+]'? W'Eп&G? W'Eпp?&:S0&G?&:S0I#€?Aoy?܊_a??q͙??77&͆?wG{?87&͆?#{j?nQ0µɿwG{?mQ0µɿ>RẶ? 1?lX=HYb?͉?d?[r?%:xv? *sc?%:xv?m}k?Sy{}Bɿ *sc?Sy{}BɿfH?gn??0 ?S)K?ȧh'exy?#z޼?WE?~?ee?~?H;[ ?Kee?K⿙L9$:?%hv?`J].di?'nYu?B7?2@Ŀ#?M)2Y?#?>?uM)2Y?um ?0S?>Vy?b͘?h)4?8͟={?2f4:={?3YH?rѿ2f4:rѿ»QsL?@c訢?'԰w?Ux?H3@Fњ?͞;䞿}ZN?I\ }ZN?7?g$ѿI\ g$ѿJ~, dU,?1lDtΥkcɿrNķgwС?;9 ?~r`I, ͖~-.?, ͖i.hO$ u?Ri?eZ_AP$n?xt?3I"a?sVQ?3I"a?>0ؿ+@wsVQ?+@w_Bο|f}?*az??%ݏx[ l?qn?rO@Gvd?qO@Gv ~ڿ%c?%W4$ο$]7?”Bǿy+a|i-OX%?Z>タxnc |/EH7mW?d˩?6mW? 7:޿e˩? 7:޿ ` y8?&խ?-W1߿yt5:?-W1߿-UkUvyt5:?UkUv!1  טD?qU?ЛB?ի9r@?=Fۿnj?=Fۿ jlzLbʗHnj?cʗHJWCɠ#?!? JaA?fWLֿ-;qfx?O5]c?,;qfx?gMbI2çb'ſO5]c?2çb'ſmL>x6@-? @Q?<]%ʉک? Bӿj1 wY?j1 wBv ƿY? ƿE"pu`?\ǥf ÿl.O?9|~x7 Y`fjy?/;?͸(? k޿(?Q: bVg濦 k޿aVgݩ5fG BL?htwթ?{0i+8rU?$ҿ"6z?ټki޿"6z?^uMxyҒ{޿ټki޿Ғ{޿=.v<(?{ЦJzگW]{Tu?)Z!ҿ~.eT?uFV}.eT?߿%kuFV%kL̕%?r h=T[v?K&,.eѿpBr?PϩŵpBr?*Pϩŵ gf?-+ᮿCqW?Ugʞq*Ľ?mn) "q`big(M/-?4aZ?'V]4aZ?bgHs 'V] lN 3P ?ZL}Q)pRr N?v6/?p>տv6/?7'-p>տ,p㿻?iR|?WQId.rd?K?9&OK? }ڿI"9&OI"4 ο͢zR]?LHr}?Uz̦f_DHb?)0?su)0?0ۿոsuո1;C3˿?f눓ỿ:VZYp\BS߰?ZO堿E&*r?S-[g?q|꿅2O:?q|  ߿zt?2O:?{t?!Fp^V?+;IEk|ުv?f;ӏgVw?0(iY_G+?0(iY9 +ӥ?h0¿_G+?h0¿Ϳ?b^2M[?29 w?(\$~t9?6M)/Ͽ_^L6M)/Ͽ($@g?XE6_^LXE6"ۆpO윾?-aS?>M@6x?N te?ϿnMQϿWR?=ޘҸnMQ=ޘҸ|=ib푿BV0?T餀gF+[ ȿ ʡ?z? dҟ밿 H6s\hԿj?vZN޿j?689?|z?vZN޿|z?XLeпU(ݡ?d@s?|!G~SsY[|?puAؿ|?: ?gr|epuAؿgr|e8W?uDѢ+? Bi{? Jʅ)Tbfwf1鲿QٖAHڧ?Qٖ`V&?$u#ÿAHڧ?$u#ÿeˆh]o,?J9Ӆ{?ΠZy.ꇨsD=?D=:L|f?q U-ÿ?q U-ÿ[e3?5BC?ዤ?+h)w]?wƝ?'kFjx?5t?MQ<|?/%8俀2?/%8O?` EP꿀2?a EPXo?V*h?9$x?VvCO?[b0?^]˿? /4?^]˿]9?$@ /4?$X6' &,b?ׁʖ?|Ɨ?`sM?SV翼`sM?^7@}lSV}lu'?Ⱥ;?B  ? y!a{~??7ֿ?)A5T@ [4Y1迒7ֿ [4Y1连?L˦?.5?UpUDUuRp?fhE?gh[P'?EпE?EпJ/?Қ"%ʟ?>Kwٔ? Y*CԌ,?r[&qx%e?r[&qoysk?Ǭbňпx%e?Ǭbňп௖=_?5n|?d?Ҩ(غ@9[@ׂU?rwЫ?4Q_^4E@y)Tӿ:MQP@\Q?:M&^j@>ˀͿQP@\Q?>ˀͿ!n_?U)?2߰4)LJjs?c0ߎƿ賠?c0ߎƿ%?@["c׿賠?Z"c׿C>E`-Ҡ?5?}V/kl!vR ϛ?ZếS+%?Zế!?x{rYοR+%?w{rYοP'?Oנ?|"?=;uPuȵSD?~?&O:Ɵl?~?&OxHl?FB%Ϳ;Ɵl?FB%ͿmN@~? /?G{?!._+Gc쫿Dԡ?D$)X?:(NiGGkyf)?4G?r[J4G?;^X@?Sr[JΉ?SGIO`WbK?w5?.sW߰l!ϵB?.A~AA?v>?0A˿.A~A0A˿g+ 俺Ec?t?EXjfD;?w^)N?ĿHP0z?N?Ŀ|:<Ն?*6^HP0z?*6^1D?ߢ4?Fp8]MD?ttV꿿rҳ_6ԍ?rҳ_6tb?x/ԍ?x/-=OQm!y?u'2г? X%?ݤU׿H d>п aS?R>%? aS?Giu{y5?2_?&#MKD*j?эyп6 P/ Nʎ?6 P/Chrfe,? Nʎ?rfe,?D-ɿ/.i-? ?.": o?±$οkcgsl]?lcgV %LÊ?sl]?$LÊ?#Cow3-ʿH %R?V9z!Oy0ww`?[[=F >4E옿N2 ? X?iZ~k X?uS.?iZ~kT(໿Aq&?}—-8$!z ?"ʇ? r㼳"ʇ?sN]?-bDXr㼳-bDXBgQ]?>n>?ΥICGҥ.=tcq?j&`ӿ 9ˠ?oc? 9ˠ?ʮ%?oc?%?ZQ 毿m6O楣?3لcɗ&p?PU ѿz* U*?pz* U*?B\M*-M(?p-M(?H[V#P΢?5 ,s'N k9LHÎ}?H[4SA묥Ʃ̟?EbAϣ ῞EbAid,k?'ٞr?ϣ Ὼ'ٞr?{2t݊ͮ?pnP)4l Qnα?0>α?G ?HM?/HM?"}G?j(?z&50^ x6?t1;G6i?z" `]\?N l>w5࿼(C?Gyvv?(C?8`k9?Gyvv?O!O F??ȕޭI?Es?1Bѿ\IM?N P_?\IM??վNIʿN P_?վNIʿWr ۶J?^{ozK`?Z0E|kj!?# J)4@G# J18b ?ghm)4@GghmZ)(;(Y?ɛȳ"?Z_N`L$<4?ׇƿ+Ygׇƿ-$?b&E%+Ygb&E%wbBe(Dd? Q"9? ɔI?4jAJ!?yz ?A ڼ?_ Oh✿@^6.X?^3-?˕f?6.X?˕f?sLdܪA?p^?| ݁!?ww۟?xx.]J?㛶bD# 䛶bDΉj?VG?" VG?<?h+3/?3o?,(sV?LTkB?.0Iy-?8/0Iy-?Wg? vgz?8 vgz?1D=[k 4?4>~q\m?dgD?sR !x ?ZǼD}Zh\?Zj?ǼD}Yj?uRD<JK?'9ElYø?os@<,??kQ??v*&ʋ+W-†?&N?W-†?bѐx"jP?&N?#jP? o6rJ?mb?en?K~sj ?VB6?1??VB6?wӨj/8Me1??/8Me G˺nqȢ?Et{&?$Tف?@U_{?^QjN?$n?^QjN?*RУb$n?RУbĽs;oԿdbȢ??q?Zg*6?oT.~?+4zz?oT.~?*>{a+4zz?{a]eӿQT[9Cݥ?[i?QR^^˸?C=]ԁI???dQ!)?1()?䖙i1(a ٜjst?>+pNg?Q/!?f;b?WG!g?濟!g?7|W(r1(r1@gcEa?cHt?K ܈?&jy\{<0?bUݾ?/ YѿFҘӐibUݾGҘӐiBe׿a?t" 1K?rk?ڧ`D)QMi9? Mi9?H^dҿI: I:Ӧ6 bֿN!3?=afÿ"J ]*?f}KiT?}vHkfS?j⶿;D@Rc?쿇/࿦Rc?l*#.x_쿇/.x_bL@TG*S?Qz0!\ª?ܞ*ÊCCj?{d?T 濽{d?:3|9VK"忨T }9VK"忡@9>Ju쿕7?1]?l:?c FIp?`wCaoz8#ӿ`wCaotWHؿ3Bǿz8#ӿ2Bǿ .,׿~Cڣ?:v?3?͹T(7б?09H{˵5ӿ09H{_\ڿy_ :ɿ˵5ӿy_ :ɿPimy$տԅ?zƥ??247a{?sԪ? ғ?:M~6vYtȕ?nHֿsȕ?>p453nHֿ43l/C?h0^DB?ã.?6xX ?/3.S񗲫^#ؿ񗲫^^q*ZJU#ؿJU:V=E?ZVf8p?Ry??epr&nղ4*~ ο5*|'_x.Jhտ~ ο^x.Jhտgrљٿ&Re? y??M)@}Wο}ŭ-1Q-~JֿWοP-~JֿF2ֿ 3?-J?q?bKϿ'&\Կ@`ɿyҿ.y>bKϿyҿtLC?R?ч6a?0X?mv `?*㾿^s߽+㾿N@?cǿ^s߽cǿ=?%N?H ?L`?k^6F28|?MG!PW?MGy%?YIſ!PW?YIſ?=vՠnX?nl}ڎ?41"?示3b%吊?u2'9H s?v2'9A"E)?E 9ÿH s?E 9ÿ,%?zg?"HӠ?i0?R{_ƿO5jݠ?z3l?zr f?$!6Jɿ9h ?$!6Jɿ%{Ծ?@hǦ?ҜƿHLT??m#"v?6"?όL? UؿF]篜? UؿOt? @q;?F]篜?q;?lӳV?#zv?u#QPH=xz?%x;?K2ٿ#3?K2ٿbWs?5N?$3?5N?79/q?T=?B L?oT`LX?"ю?nhivͿ`|!v?nhivͿ4֏`?|?a|!v?|??*FqN?~Ŀ??%"ކ?!*'\kX? "D?xr̿TtK1?xr̿}3E}?vN?TtK1?uN?$,÷?#"4?y?g |?F6ps?Xk̢?.?h%e? ?dUL??pCÿ?D ?H?oCÿH?s"j?ݣ\?`}:?~p?F3?I˿YN`6 ?foXN`6 ?Zjm?& ?fo& ?`yq?0?á9U\?~KF83?YŽl3οVx܀c?9r*`Vx܀c?x '?UB?9r*`UB?0bV:˨?[$?@?@zj̓$(?<{ɿlP-?&lP-?s?fݯ?&fݯ?!B\V?~e:`?iϙ?f鞲Yբ?jf*q ?Epn~9:bGÿzJcbGÿr7XϿ@.zJc@.>;%>?iMw? IlsῬ0/5{PQҿ0/57tE?1u ʿ{PQҿ1u ʿNi ޿\Hb?Fzsp{$}?x̊u-ױ?%C!?[ ٿ%C!?o'?gYۿ[ ٿfYۿ}L?y?6Fzh,Do?b64EhlĹ?~=g?idܿ~=g?^\3Nc?QAidܿQAQbXUl?.1ݧ?VRtyqPQ?64;bIߣ?Tjq@B?1| 8?Ys俹(沛0?YsT7?(沛0?T7?Y%: d?HY02@zD?ʰƪ`C?b)+͟O?b)K$ۿ8P\t+͟O?8P\t>|c>nDt?&\?$[h\&0:?6'ي %W?7'ي #1Wo@FpN޿%W?FpN޿DzHLO?F ШeԷ?Bw-Sfd?+A7uSS?+A7ΌO)a@:T*\uSS?:T*\٤CV3?ҚνZ!ck? Mv?ѥds1?m)!93h#`"Q3SտWzۿ"Q3Sտ8zR5cTYſVzۿ5cTYſl{=1(U?ݾQ3H7I?$s2T035u/ӿ03?0\&%Oȿ6u/ӿ&Oȿ&gvZТ?p:~H}?lY J4?229ֿnZߤc|229ֿ[4?NeoZߤc|Oe!Կ 7?6Eq0%;?gЇK,]?Gbڿ u?GbڿUI8?Y0<ӻ u?Y0<ӻ {Aӿ݉Bt?@Z7"f!2"O? Lr4+y&ok?{w0P]Gign<"rQdeWٿ<"rlU˿ ?QdeWٿ ?/⡱ؿh7ۣ?>Vk/?@/.%Z^Xj2+毿LZ\bٿj2+毿b{˿P!?MZ\bٿP!?:yXֿv:?0h;|F̿<fo]ĿY vhk?hU{?|(-PU2;ѿ?s 𿆡?s ^Kps?vOi?vOi?J3NEt?Sm}?aJJ;Nt Xos'oFWos'保w ? z?nF z?Tw׿'D2?DJIz?Y:ƂyDG򢿞V7r9fzNĿ_'տ9fzNĿw2}ſY?_'տY?m&ڿ $?vy?'Cb]E~7R׻DZ ޲Ŀ:)Կ ޲Ŀ)XWsſIZ?:)ԿIZ?Pp ֿGCc?6?yѐ묿B@I"`J_?>9$jM렿>9$Uÿe[?jM렿e[?" ?KNt?*(?Ңn?"P6ra?7C|c?2PF7C|c?V|oBm2?3PFBm2?`I?Ty[?N]Vp?{? eԫ{ Nfgg?Qi.#ؿfgg?Jy)*e7"?Qi.#ؿe7"?Q&{,9?ճ ?e?д"!q jG^Z{'鿩atH]??긗~пatH]?u1b)X7JT??긗~пJT?4?C?N_RD?Yg?/3JHITl)\7n8R?)\7=ܺj轿fqg??n8R?fqg??!?X?[a\?#:d?U<${BN?{BN. 9$ <j??=j?qؤ?9! ?C _sG"ښ">EA7?Ș`)ΙYmyPޟf]5w} >Y?5w}3RUUgPL?>Y?VgPL?H"\UE?+ ?PJnR g֘?.'?|?O:2E?Rv?O:2E?%ogNع?cIĿQv?bIĿ"?cf?w?Z?Xrip?15E ?\?a3L׿Q>Pث%#٣?A O? {?u#}?l"J8Z>??7% Z>?ſA?WwW?7% WwWHSr3xn?Cn?})eӸ?b&@mgz\g?5B?U +a5B?pj+]?qqU +arquF?/!y?̲ʵ?0;`?bEwE{SBv?o ? `{@,o ? q?D `{@,D!s6Dk?wXw?n *ў?d'՚sn0;Ti;t?&bq?şy( ?t Ž?( ?=H( ?͊>@8t_=H8t_!N^i@{ 9?&|=?h1s?ĥm?뭘@C~뭘@j!dXq @b:pj^C~b:pj^@x"s?`kt?(όI,?rpҿOɚC@VG @WG @E!B3@[S[S# 9*@ G@ >6 G@@iE >6hE}0mG @^|Y?*GfڿWG?$ȿF,?Gn߿*y?CrϿVG@"f*@vSL@"f*t]3@0@ b @vSL@0@ b =y%ş?{_[DK5|1?Egѿ_o@t7+_W+@t7+C=&5@f _W+@f ,#(z> $?w㿉W!?ezqӿi-@OE$@OE$^I5S0@6M,@6M,SV&hD7?rN~C㿣@l?:EvԿj@7$6qS-@7$ Wy܁/@T2Z6qS-@S2Zxf)M zڥ?AH(9ſQl?M,2'?6l{[^q?3z6yjz@"j?jzkRaz?'}0߿@"j?'}0߿]hZkv?mn%WgrH?Q\D?^Cb V?_Cb Sh @$#V?$#쿎,iFĕ鿤5O?2zHaͿmQ?_887M4" @-e$E?,ea')N@dG҈$E?dG҈"=/ ?VUο)^+E?gEpEY1M @Ё0X Is1y?с0X _B@G|:Hs1y?G|:Wco*I5?5 %X84N?oꩢ?1eovK'sHL%\ ?8S.?_w?.?EHWkѱ?pkW{?_w?pkW{?=9u~ӿt?FK0{$T*1?<ǝ?tL?G |?tL?y~jLP8?G |?iLP8?xe+qľ?eMZEՓːې? ߒr4?Mi?s@U濊mb6?t@U^!r&?XH mb6?XH k@*ٿn{?O}A},?qt?#9?n3/ 6~?n3/谺?KO୿ 6~?KO୿0*>Oڿ%׸{?nz?zY? `b)?n?5/!?=?v8k+?ǿ%2>ǿQ"?TCux3P0՗?U`%?(G̿0j=(G̿>%Pr?} 0j=򿿥} “οj3* ?&Zt*??&FZ?@q ?N7o;-TɝӢw?へ/Q5?RR//Q5?0TDӿRR/0TDӿ17KiAu?ˇƁ`CڍZo?3q8ćq^2Sقſ8ćڂſ xV?q?B_YHx?K$ǿ nEL׿ nEف\sSL׿sSX$¿H??B0j?1:v?{malǿOm¿Ǵu׿Om¿+D9nޒȴu׿9nޒ Y¿03&?|Wb$v|J@?18ΎmNF`?;913?QC(g?CQC(g?Ǎr?^̔?C^̔?~? #; ?ԖF[ȟ@BJ^?Æ?jl=j3?jL0?AjW0or?0/>W0or?'iZ?R?0/R?B6e]?29:?lD`?D}f ?G$2 ?cX'ȣ?1?a1?Dɑ+&?7d?a7d?Ȋ>?y9G?V6\?Z?J ??Y_hڿg K>?p9?oY_hڿo]Ӷ?HM4?NP~? ?_q?&ɿ$q?܊W#$q?\ZD?F%?܊W#F%?cg^_?#)4?%JЋ?Pˊ?GnSr?‐ɿ l$l?鑳R l$l?S(?Xe?鑳R We?EI|?`f?= *i?)񉨿xg?&ݽϨv.4:?B}Pd?dV'^ֿ?{a.ÿ?$ 5? \!¿{a.ÿ \!¿&!xK?u2V%O)x?_Cy?\7]'Ŀ65[G'/Ŀ65[GÀ?F% #'/ĿF% #O]Ϭտ{ބopn?cx?@[?1?Qn˅?_ ?jwSſ_ ?JbV?:|VαjwSſ:|VαH`\?̛l҂z?nAo?G,8? suȭ?uc?7Lgwʿuc?y4 ?Q7LgwʿQ>(?*?hf?(?X*ơ?35?+LS+?wV& ?,Jvƞ?2¿jGKlm42¿ש? lOe(?jGKlm4 lOe(?!< 2J#?A:墫?;- W?@*n=?&Ƀ?ȟ?岛#q|?ȟ? u?gw?岛#q|?fw?Ͽl?kht?6?s"?C7i%?Ds³?mŝC?ZP?/Y ZP?.#Ǫ?tf/Y sfRrPW @u)ئ??h?p{?G+Ѫx@?Ri?,wkHRi?EyK ?F &# ,wkHF &# #'@8?;ݒ|?iW?$w:vؿBȆ/ @j$@z1 Vj$@~g@vz1 Vv/Щѻ@O?!<;??{ZؿȈjԍ @%@x]w$@n򀎠@lEnXx]wlEnXSP's@;?h?^?T=xzͿw? Y?q<;j?]͇_3\dIZS@_@k(_@i]@Mj@+k(Mj@+[xGq4@C,? `?o`5?ZO(-Wm~Aì@gҸ@P3ΒN*hҸ@uk@},P3ΒN*},b4 !5@J?eE]? ?al2'8D @xŀ@jQ8 r(xŀ@ j @D!DE-jQ8 r(C!DE-3Cr0@pzh?{?z ?[[OK @_bn6@y e'_bn6@%H5U@폶24,y e'폶24,i-@ |X?`{V 1 ā@.. ܼ~k?MIs@)W= c<@sޫA;갤p @@sޫA;<~B@AE D갤p @@BE D +f5Gq+]?:"}6@J ulz@II3;@II3er()>@@N5,@;@AN5,@;aCB༴?ܯ_](jd @dv2C}^ۿF5'+i@)@F5'-´!@7G>e*,i@)@7G>e*|˷o9$t?x M@6ў޿DԒk#BgŔ)@DԒkS:!@ex*#BgŔ)@ex*Ț7 *?+M9Yg?/r{Y޿F桳?/AS)9P.y?:6=%ؿ^5@2{;R0u$@2{;56@@@I 2$R0u$@I 2$/ۙ% ? X? ρ5Ag?Sfٿ <1@Mܽ}9h#@Lܽ}9eV>@=,a"h#@>,a"i%+ zμ??i!񿌈s[?Eֿ""@<|=`S,T&@<|=`S,54C|.@ʃuIT&@ɃuI~{&}#\:?#[)W@1H?HN#׿4Èl#!@}D0>+|#P@}D0>+{--@\#wH|#P@\#wHD%3Q4?2?t}RB? B?iKa \р?#7&G^?꾐@iB?i/ˈ @iF`B?iF`VY',?oq٢?Y"~prA?_Z?ܝ'l @~E HP;wϴι?~E HP>W_% @OAɞH;wϴι?OAɞHDleB߿M3s?$хͿ*,?2d]@CNySج?CN`5@&'ySج?&'J>QP5:?>Φ!Ͽ ?ftz@rs7O?rshLj@{97O?{9;sW?΋X@ hї!׶?m?ɑBYu`#c?sv]dV?Z\D?]dV?RXnO¿ \"Z\D? \"㿉Pe*渽?F:ڢ?[l ]I=r 6?)jտG/h¿jտdL\?NG/h¿N_O% ߿VB?!7i؋?v|?qX?=Vڶ2#=dk?d^UпVڶ2#c^Uп5]iܿu4!?6Z ݤm?`#-z?ٹyW?tM͚;nV?tMQ)`8?zSп͚;nV?zSпU󜅷߿"ڄM%?`*ᇠNLI?e6?ʶ .8vfǐE5WBXK?l߿%<l߿ #{?({?%<({?Z9vvT9?:@8u:\Гm%?F%ֿF%ֿj?.U ̿.U ̿qfؿ;@l&?dK\ܿhH5͕Sdm?VҿZOſVҿ?Æ:/ZOſÆ:/zkĿڢ4?0I go]]DE +?lnkIտ/#]? ۿ r;L? ۿYT9?9c.ſ r;L?9c.ſB2?@}Yҡ?%Uze[\ۣ\aBR$z:?saa5T 瞿saӁ_|&?Ғt8?a5T 瞿Ғt8?1>qߋա?b{)Wxy}Pw*?iAiAtE2?V?V?1ζY^\?c8?PP ᐥ@u[b.?4Y?o/TFJu>Va@?xV8^?vϧ>P?xV8^?6;?~|]uϧ>P?~|]ῥ*}@6#=?R? ){xPGs?8N+a?o?΢5?A,? ҡp?ыNǿ^G,?14c?^G,?|?ϵr?14c?еr?K?=KX;?-"&?۷4Z?üt?^/^Pοfs>?̭3?fs>??&?̭3?&?o{Jq?gsj?hn~?67]jM?'7kЁ?O!Wj+129?PI5q.~?129?1ӯq?_KkY?PI5q.~?^KkY?2?Mf!?؏?~S?_VE?Kk$28?J؋?28?ޟB??J؋?? "?Jnk?csgo?P܏PP?(DҚ=jvl?DJd?_#[Z?!#0c?HῴڂL?yTGd?ڂL?)!ѿq?yTGd?q?Q: k*$e?Ir?Ws?:? h!kٿNMls? 3Y?NMls?=" ο'|? 3Y?'|?rmV)?V#ͽ?kKx?>ͽ?,I]?0?}?t&4ʌ?\ m?DPjÿjMn?DPjÿcJW ?kx&Ϳ?iMn?kx&Ϳ?AއY; }@?;!`?;Vݸ?+v(c?AAZ?+r?U?\nR#C=? OH?d޿B=?eä?? OH?d޿?2> ?&g??hD~z(9Ja?c ~?pmPp?.\ MտmPp?_@?*:Tx?-\ Mտ*:Tx?Dan?$I)?)%%\w?C>[?pjB?31W?EMԿ?#t{ϿEMԿ28hB?"*]??#t{Ͽ!*]?v?~%,3٢?{l?Taf#* X?A-=҇?\Tg~&ڿ[Sxҿ[Tg~&ڿ7n?Fd"?[SxҿFd"?曥@+?F?Gõ?$j&?T:?T!?."rh?j^y?~C^1?7{X?O>-ϣuȿO>-jpZ,?ϣuȿZ,?NV|>^??ko?R/?ܛ?rRGrfCl=?QG=?'&> ??@k\ \]:?8FrE9忉8i @&e?GrE9&e?~wb?Ys?rkt?mu Ļb̤cݝU?xKQ>h`L%wKQ>ۜ @,,8?h`L%,,8?F|V?xe#?.A1첿߉z?LR3?qv5?3/ʵ7 Éƙ>?O?Vh3@X|?Vh3@/fO2@y 2X|?y 2}F%@vM?z^ S*rз˝޼?9EOí?lmC@[ӣ?mmC@xh@.[ӣ?.QD|?=L?I,/пiݿ-Y% ?"C@]L@V꿎 ]L@5K(@-gdV꿎-gd|h!X㿒Lv? #f6ѿM~߿ up)?wsl@LAV@HLAV@{'@iQHiQKDgp+t^Յ?kÓ8jK??r??WsȜlq?DFm?)69;Kuο9;Kj_ȿ)kĿuο)kĿ(fտ?!)$Nxt+?$a"?tP"ο)e["ٿ)e!CLJ?qcп["ٿqcпôde,׿Ɋ .?QCX)W,? I ?^@㿡^@LES1 @rdqd뿎c۰$Mf?~9?Ⱥקȴ(X?B`?Y)@FjY)@*!J @Fm\OFjGm\O 2@S$?\'LA?A}B&ç`пDO笔]?ç`п ͅy?zW~˿DO笔]?zW~˿bbhSm?ǟÿw3t ?mQL?w3t ?ျD?3%wmQL?3%w^Mf#??R'h?Rc2` $~+n?qK֖ſ,Qt?pZ%?,Qt?8B?tpZ%?t/ ?] ?(OI?.A?##+?,ńP֤?&u_I1#Ok?`}6?И1\?}6?U?p~uИ1\?p~u!H'?,ms)?>WN?"r򚿱}_?n?Ӯ(t??47[d?b i@pؿӮ(t?b i@pؿ?BOd5տ{?r:?'OMm?F$ȓ?xӿEy?r!g?Ey?E=˿AܥJr!g?BܥJ4Ҡɿz^}6?:l9j?~GrԿ4V?4V?k"dž?0r㿣0r㿨Oo&1^?|P7x?u?7jB?;ÿrV?~jjrV?p,ʿdcn?~jjdcn?ԿiA?c0@Hz?Hփ? 5i?z`ÿmI?wzmI?ǿx,9?wzx,9?_-qԿ8!]$?3@?F`W8^vh?\`;^?Na?M{Uu?b@@O濌&CտF࿍&Cտ_|MH#S?FMH#S? H;?Gt]?qB`+s(iWPx?-юLҿuaпJҥf׿uaп2@ܭٻ$ Xm?Iҥf׿$ Xm?..|?STf ?&'~n<\^|?J*%?h5N r_)#-9ǿr_)zSXÿv|Oi6?#-9ǿv|Oi6?o\ÿϻؖ?2ӄ'\lv?y}o?R"j6O0m3qǿO0mS2Z΄_?3qǿ_?%Y!ƿfl^r?E\ \W?%8ʺ?0?ɘ? ho?/l?1h?ޕ?tYo?z}畸r ѿz}畸ڻj@f'?r ѿf'?)[u3/^?%wpayy@ o?k\?,b ?dƵ8$&9[ſeƵ~*(? 1λ?8$&9[ſ 1λ?ȸaJ˿{D?_둿h?g2i*;>˿g2i*Ƭ$$?ȝ?}2`]ko~ ?w!?k@o˿x!?8 ?#3OBÿk@o˿$3OBÿ??u*}H?"Os"۶1y=?TA4?n춁̢?co(HmS?hNXxq}ƆSMu%?1Bӂ?SMu%?C?D* ?1Bӂ?C* ?"Bl?վA? +S kp?LZ~/뿇h?^fEl?h?-NS?^fEl??s?urSM?OWo?A}qҬs?2~ht#äD_?52t?h;?52t?K%ӿen8lh;?en8l g?<.Z S?Te?Pur?z࿁ٳ?M>Ѵ?ἣ:T?M>Ѵ?,xjҿR^|༣:T?R^|%*3?P?JS? l%Ͼ q8!?wAQ?`?ZG=g? ?x?#?P-뿝EϯϿP-A`_X 텆?EϯϿX 텆?Eև@>Jm?'+?*h슬؅?v<?qo3"2?X ݿgש=X ݿ5ҕ[_H?gש=[_H?> ?%?ߢK!Ɔ?Dq|R?ѵ 1k?$S!?(+Jqu?(+\|J˿޴bJqu?޴b6S%?6O&?7$k?C~?<;k?ԃ?osCy&+?osCS˿F@6y&+?F@6 :خ3? c*BSgL|? nw/;d1?(d1?zI?pO(pOf5?z?Q-DR!d9{?C hqckLi,wdȳ?J5Q,wdȳ?lV!xζJ5Q!xζQQ!@p?| ?ŁwW@c r"r5F5??R>iQ?!*GU|D?_ )Nӿ:?_ )NӿXWrZ8џ࿥:?Z8џˮ{KCn?'a?,?$}#(J?~׿:Bp?~׿lu0PHpϿ:Bp?u0PHpϿ ZB?YD3?C q?NB {?,oV+?7oVRʿF?7oVRʿ~K{qݵF?qݵ2 -?sC!D<տ?L?>!D<տZ修[Kƿ?L?[KƿzVt?bE?!M Mp?V\?F\,1vٿ8HٿXO[?8Hٿ}52FſXO[?52FſChO?S8C? A?Xc}?]q9Ծ4vпܔ?4vп2;u^ܔ?2;u^ߺ## ?R7q?A *X?s6~?}rp:,`t'οv> ?t'οaZ{sw> ?{sf?AU?$ڛd?,?r4C?6? 6ӣr˹A4r?7=?~?׿[;bb?~?׿&p?j_Ո޿[;bb?j_Ո޿ * u@2{nI?v#5c?p6|Z' ~i?`j\?+uܿZ3?+uܿyؿoĿZ3?oĿc?`{?æ#J?3{X,hӄ?aWܮ&ǿvQ?Wܮ&ǿ1lE;vQ?;zDs+VU??0?=vI՝D)?G\?C%ƿ~??\?C%ƿb$׊m:JV~??׊m:JV8R%EɈ*?m?*cqǿRh7n5Z?H]?y+EVIP?cjfU-V[@?U-򿧣1G>?V[@?1G>?҃" ?"3:M?hAaS?"y Iۿsa ? Iۿ@gv| ݛMi?sa ? ݛMi??*7^ ?gO?708~AhZ1?m6.rƿM. Iqs½?M. Iu *b)+Tqs½? +T:ֿ!?? |튿pƕ?ykƿm>L||s.-↽?m>L|Vj5}LRѬ{s.-↽?}LRѬ{Կh;p/?4tˠy񺿌R A1? ##S;G+?-?Ήƴ? Ήƴ?ck9tPƾ Pƾ忸LYvTЁ]?=|x+w`pq@׮? 5;b,J?@(h&nȿ}T:?0?}T:?O&h C5קH࿲0?5קHIq?&jբ?sGp{R<燿[?PhgFĿhb*7?`\gb*7?k.̿R`\Rpic+?fgբ?ko!If+a?!ÿXM?8ԷXM?4Ͽ#/nZ8Է#/nZȟ?9ug?y+ԱYTsF)/=? 1ԑKS 6Nr~?'&C?ٿy3?"Vc)?y3? ͑?B}3|"Vc)?B}3|ܪReWΗ?~iv?#W?hBwܒy?fk ?AWϰ(?̷VTy?̷VT_e$'jɿ]&?y?]&??ab.<? 1Gޜ[S?Yv4Y ?g,̿v)5꠿g,̿Ln?זS?v)5꠿זS?}([V7V ?/,+.|Y>Dh?35Dd+rN?_mD'ƿ_{/?x ?nD'ƿx ?7 ? ,"%?IAqu-bADd?eř~a޺?wшſwш.ֹSʺ?Fd+Ͷ?ſFd+Ͷ?0%?$?T?OJWB Y?<^?hᨡ^?3@l]?d}?uV>]?OK-?xY??{;9?Q[ DFY åf?0;Se??P?0;Se?S].ú?Jx-?P?Jx-Q}ϳz2?b}?9h_SlOrpoi&? ?? .? ?7A?e3+l? .?e3+l q3諿%/xkm1?T}X#n'?7*?OU<[??O@D?@;?6x?_{.??H< ??k_=?ļve?H< ?ļve?|пoP$b?Ì5?KT?5ǂv?q+ퟥ¿AU.??BU.?[ y?oZ#??oZ#?;@by-?Fo ?/=z?~qis?\;ο{V?ä#a?{V?6U?P|`gL?ä#a?`gL?.n̿\ZΤ%?e4?丒I|?6Gs?H#Ͽm?x+?m?Δ艿_3?x+?_3?Xb˿V? 6j?WN 7[3?NɅ1?1"?ē/Օɗ㳊c~?5伿W%z?~?ڰ?;1?1͛Q\&?dKrT?dKRC-?i=?rT?i=?8v4d恡?ƖHˆ?' $?m>VࢿH;wp~?zC:XIĿC"Z?zC:XIĿh6 ?{H?C"Z?{H? i?tLѐ{?{ߍ?ڝU?*{ZE?*{ZzBlTȿT8}?E?T8}?ndrO?<?cӮ{?|ys?v\Yk,ȫ?'O3xf?(O3'3q\Vek?xf?Vek?se1":?إOI ?Bz r8uZlp ǡ?AdS~(4.'IUv_8Jrעοv_ t3 S?8Jrעο3 S?wϠ?Ԟá?5ɂ/̀ˍEs`xԏСZ@ݿN٘lUῡgR?N٘lU)jE?JHw?gR?JHw?U B?+ ?c?@huOy?88`09gYP7]pxL?P7]I叞:l?ốTg?oxL?ốTg?)-?}0?Fm ?zgx?D^#(?kNl$jg4+Ξ?jNl$j/# ?ƫ|,?g4+Ξ?ƫ|,?>X?X]֦?ZY+?2zZ;V-?9 F?W|]?F>`2NrVjԒ?`Q?_л?`Q?>?*Ƌ?_л?*Ƌ?z?ܴ?%sx?ހl!˖oN:4l?"[I?b?`t?b?cpv?s ?`t?r ?Voҭ?Y?ʶy?c%pӴx?|v@n~I*)궿Et@i?I*)궿a?YQ~WEt@i?YQ~W5?85Y?;/z?@*?Zonx?Ŷ̎Gyyy\#k9?Gyyy$1?qBޥ\#k9?qBޥ}؏?3Nxm?Jdz7/-&}YJ/W?2v鰡?-d,o܌?]~k$Du? (`?d?Swۿd?־VZ@A{o뿘SwۿA{ozaU>K?WUG/%?=oT\HR?ڊ*zC?RE?O˿RE?. [?;c俩O˿{b|Wɣ?lvM[?\x9?01?vK@#"?ihy?) C|ɹ<;տC|7f?~=5Wɹ<;տ~=5W濰yɜ#:Ȣ?VfV_?T?ur]E ATdCfֿE A\(?`UdCfֿ`MnB Կ?|enS"sN$;t?=Ֆn5H Ŀnş)?TX`ҿ5H ĿTX`ҿ9+@?9ϳDGBpnөt? P[/>?R*Ŀ[/>?34?0)ѿR*Ŀ0)ѿ'g\|xߞ?ۈo?pNiܴAn꡾?I .=?ozޱ?( f/AagN_Y, [?ŸOc?nwl?ŸOc?0k@H@;nwl?H@;d}P@?U?h:@>A?[zhaL?[zhO*@j鿼aL?jkv]?ě%I~^?ᗵ(h藫I"5`f<be2?hFߴ!)˿w}g?!)˿aCN[jw}g?CN[j"?4MA?%?y;h nx?Rҿ21>LBE1?21b ѿ(*>LBE1?(*R{cƸ?後?G&rq1v?|QӿO#0,{?O#bԿR$+r1,{?R$+rSXe ?4K?m?Z'&:n??i8]?%v8"ƪg?q{w?ۅs?G&G5i ؿ(!?&Ῠ(!?Pzi )?c=w7 SJU x?ܮ` ??ϖI=uۿ?6FZߞ?ϖI=uۿZߞ?7q?KX&?{zVŐ{Mz$տQ?=J]?@?ʿ@?r$2ҿx ҁ?ʿx ҁ?yyTZ?WNI*?pJڔY:zw͖qU?t?<0*?v?YIژz.V)?~L\?x(g?aK\z?aK迆X$1Ť3+迀\z?Ť3+WeW?Q?yxkU"ۧ?G Ns-b!?iԙѿG?iԙѿI-`*?5SݿG?6Sݿ Ep?G&?MRX $ 6\?}}?YYɿ?}}?y6&`YYɿ?&`ÄJ ơ?UQ?XV0z?Թn?K(˯?Թn?v@PѴJ(˯?@PѴ >lȋwH"?Ws"(6?Re?*/?GA)-o?ht?pC{?iӿpC{?R/PN]?w#?iӿw#?Oa,U_.? ` n?QK?Z"݂ڊ?c|k?ĥUA?r쓶?ĥUA?eW1~%-?r쓶?~%-?Q}ߦ忧vҐ?T?us-G1F ~`ӊ:ڧz^?e??z^?UnkEe??nkE1c?ҿȁ4?{~`?15Ni&4P Ͼ5;yvˀ?J5׎҂?vˀ? U46)R:JK5׎҂?)R:JR'_zJѿE?:e??Ԏ?NGf?oV?߿d] M/C'?e] M4 ſ ?/C'? ?tٿ3fNj=?U?=.o$ ?:` L20 Bn?M20 :ȿ]}C?Bn?]}C?ZJ qؿ0פ?DWss}x`J{?$Ber{K ƭ?[4l?{SfȲ?{SI%焐?fȲ?焐?}ym?j|=⫿kR?F?ң]Hca0?n)? Јء?u(b|g򗿨)m?+SLѿdg[ҿ!#ˇT?dg[ҿ A'p?"#ˇT?(p?qmȯN@_t١?ջӞ p/(?s$լƪևh?ƪ#hmP5{?ևh?{?]v7?孶c?P mp?_W,C1?ߣ0h?`4 l??t?v[2HĿ*V쿨 2߿>b}ʿig>HĿ>b}ʿȚvĢ?,'/dgs*͕?deA?Irh俇ҿIrh<0ѿcz-ҿҿcz-ҿB{y⿫g෫?[GE? m?iw t[Mog.¿)YCg.¿ȟ4?xՆDƿ)YCyՆDƿ<:?zD? ?|Fzu~bmp?͢l¿75͢l¿6[)3?aR7Ŀ75aR7ĿRO?e>c?,>[?=ÿni?kyCPggg?aL3.ol^?57?^?絤3'%,,鿃57?&%,,3|+r?oiK! ?06`7C8!I?o~O^!I?P՝qAn~O^A [ P?pDDYz-Riv?Gt%:̬P2p?p?C±?b(ѿ𸿆b(ѿދ?ɿV?odW\w?r+A!!6-ి=?ϳ.>?+Ͼl?!Sпϳ.!Sп PȿZ`QL?p~`?F(8?f!4S|F?ix>N?gMt?S+Etٛ?_Sh_<⿅~WQ_Sh_< z,ڿm?~WQm? yb?D2?;c[?qW|?9aqs?FlпG֯Flпy,]?۹:hG֯۹:h~;?2!?#la v )֢?[ ّY# 3¿jcKra喺?jcKrF`?4ŋƿa喺?4ŋƿ+;Ⱦ?՚?1H&6Ut74?Vt쿿Htuu}3?Htuu#L?eIſ}3?eIſ*Nn?&? ?/O%؞Q?ÿ@?W}x3͡?]Cj8d.?1iFr? )?-e+?-e濵rI@P5i?+?P5i?}?uy$?{)}kT0!_؈'2?8=7:?-HRۿʞ;&?-HRۿ˅ ?4u?ʞ;&?4u?_\P?r%w?8`A*X}c% ~?_ثɿ v˱c>? v˱#xH?"N\D?c>?!N\D?菴⳿?MF3v?|*lEяN[1r}?f)~V|ɿOU3??PUEQ?A Y?3??A Y?{?̰V? Oiܢ?xz5C2?{_3:>?RUSnc47Kky?ru)Z۩?¿Z۩?]ybJ쿚O ?¿O ?̦qC;&$k?ĵ$:i硿?`/:?F>@?rֿF>@?˿8mc&?rֿ7mc&?RQhAֿ9{N?uFfǠ'pВ$t@c??Bgܰ?пBgܰ?&nYꤿj磬?пi磬?,?[ e?gAY{m~6܉ f?9"paY??,qϿ?\v6*2C?,qϿ*2C?Գ?Ӂ ?*5uc*ZH%LEM?@p}H3PdYQo"K>Ҵܡ¿l~}G?K>Ҵܡ¿|3鿀! ѿk~}G?! ѿo˿!K?8clc7U`oc :tJnZ3ײڼț*?nZ3ײ>GO>bվ)3Ϳڼț*?bվ)3Ϳ/& ܿz|#?p"?gYEM\@AZL? /?V!_z? /?FNVaп,sZU!_z?,sZ3/~gg?Щp#?wyV~u<% ۦ'?X9ӆf?57{֒?X9ӆf?p9ѿf57{֒?f>^<ếI&ܐ[?L3r?53-?I.PÌ*U?N?O*@t:$5?|7ܿ?῍?vg}|>dT񿽨?dTqS/AwP?I֊Z? G~9G}?2h$Ѡ?e"9ǕYnl2^tz>9ǕY9-ɋz6U?Ĵjeh ?e` L8c?]23?8c?v4ɿt)ǿ]23?t)ǿP>fӿgοP?+ML?U^&猿LG^?>?9Wm?>?Eo>ɿrǿ9Wm?rǿoѿm(9 8أ?6ÿ ,¿L G?H ?[Q(&JE%4ԥ?{JY鿀=?Y鿻!Y)E?!r+"㿀=?!r+"㿀ʢ/l+z7ۣ?܄Dүj㢿_?NO6@K߱ R?@K߱>*ַ?7ф⿉ R?7фOwDG(İ ?9 rZ?FJ~L2t?*-SM? ?M?ױeR?(?eR?#䬊?Uر?ڹ?`{?2oʠ?:?# @wx?;wy?6y;?@By6y;?^?rQܲ?@ByrQܲ?g7@'d?o%?;{!Idr?We?*/$?wtB0ɿ*/$?&O?t{2?wtB0ɿt{2?st# @<DT?";T?NYn)ы%?/T6?y<^W?-3x<^W?YdW" #C?-3 #C?|9? SS?hi?}>pPX١?^@f?'+m2#ݿ1c?mt&?X >mt&?ݮ@?i%{?E./)E ?cCn+q;?y 1?Dn ?lv?d,!r"?awӿ7`9? bڿ7`9?6D>ɿuq ? bڿuq ?|?!l2?{TC.օ?C+kr?ԍav?,fտ#&:=5?dHؿ#&:=5? BSĿp,>?dHؿp,>?:hQP?!l?Jj ? qb-6? ;|@b?Zs?5oC,M/?q[M/?6,Vn\ڿ5@h1j?q[6@h1j?GV@nS!_Ϝ?Ԟɕr* Vx?|h??π-[˿!(?3|dῑ!(?Na>տ`\3?3|d_\3?g$,ݢ?6OD)ʁ'7e?MhI貿[?`3ܿ?Xsӿ\?`3ܿtÿi#??Xsӿi#?ɶvӿP.ߢ?~). r[E`?N&#~vҎt,5ҿtjږZ{?,5ҿږZ{?!ҿ_l= ?6@XWjʿv ޙ?N+_r:.u1gƩI Ց_ѿ:?Hb0AEH׿:?Wt|?a/Hb0AEH׿a/8~Ab`v?/FY`~?̹ {i^F9F?i?>GHۿi?1E?`Wj=GHۿ_WjtQ΢?1Wxq ?i6 U?HC=~@߸aV?&@?5b&@?n^5bn^ }Gؿ֤v<Ӣ?@4?"(&P?Mbm? \aM?W{QR\aM?Ja40 %_MV{QR %_M xN:ؿ5ho?'MTm?0j`z~?ߌi?$UP>?t"$a3#[0{'?=ȯ?[0{'?.45 5 c4?=ȯ?6 c4?j~8=J޿D7>1?`???Սp\2r6?P?\2r6?`yVS&_?P? S&_? ب߿1M:?:2jeL[ߒ?yhuB޿mϱ?}e5S?mϱ?raѿo# %+}e5S?o# %+[6WeƿBhiW7?EwDe=?φvU)3Z޿4;Q?5'z?4;Q?r̿, \5'z?, \. tƿt!ܡ?-+˿$b:?M?Dg޻fg™?>[4ٷ?bޟܿߐ 뿰sM9?ߐ RbMfd;.H?sM9?;.H?C[?-¡?7w"%r?@>Ц? H3$ۿŖDS?3$ۿ?X#?pq^?ŖDS?pq^?4.?6s?831 L?$˺RͿYCVYi2??ZCVYmoCF?\lAG\?j2??\lAG\?<?E?NudX8⃿<A?Dʿ*?ox2]w?*?ox E?7?2]w?7?էL?SK*? I-Ww?r[)?"󴉠e끌vWp*?P ׿q.l῰ ׿/V>c?ДW?q.lДW?5 @?qe? HX޻oc Ph?ށO(ۿJNlԿ^Jwy߿JNlԿKp=??N "1?^Jwy߿N "1?䥾??i7m lwƪ~?e?wv~DC!spԿwv~$?gO?CC!spԿgO?uB?\OZ?O͏ߎ-3c?+?J{\XؠӿI{\X"S?D^)?ؠӿD^)?f?q?9c0aʿy:?HȿwI6?J)| &-픿X{+lse ඪa ο,?ඪa ο/8?熰,?熰g̝ɷР?d'hMU}S%۰橿iZ.=tÿQ#Y`>tÿ@G;?"tQ#Y`"tҫZ]wPϋנ?ۺ?+ǫPF2D?|0DŠSv|0DŠ|=.r?FSvFR,hĘZr!ߠ?B9%J?q[}6:F?ֹ^͑2C!jֹ^͑"?j8m2C!jj8m* ?D??8.4o?ﭙ/8U?H;x?SE}z{@H ?;5V-~@;.;5V-~H?DE~@;.DEX}Zo?W#@D??'VnQ򑨿&6B_z?^w@?Ikʿ]w@?< 7Cſv?/lᚚ?6+-?$zfwܿ%?{K6?%?J:|w?i@ƿ{K6?i@ƿ;>ĿW^&с?j({?ڭ;PX?|RoQ?k?7#+?U{`kN?,9?kN?ܧJq?,9?q? `|@Y?6?v?ct?m)?m_?i?K%??i? <iOxXe|?0;?>xXe|?6]es51;?es5 Uo=KZ?qps?`.U?PKv?ƿ) ?B?( ?jHeZQ ԿB?eZQ ԿOP?A&sf? }J>,ˠ?c ?NdfW띿ڣ/;g&8@Yy:uw*?Y eBM;vMؿy:uw*?vMؿan-!fBPO%? xb]?kR?1cO~?÷y>?1cO~?ۿǴ׿ķy>?Ǵ׿(r2SX񴜦?@ؖ?qtAҊT݆?m#?&dj!?ԜT|ſ&dj!?R /(?b?𥿰ԜT|ſb?[?oa7?RpHȵ ?q2(@8I6Ň? pG?0t^f?(“yĿ0t^f?Uc?@s(“yĿ@sThWz?h\?_2ǿa} F9RAa?wd&%qqb<]?H/??F7{z׿??4 ЂſlΏx}F7{z׿lΏx}rpZ?܏p"KB?:mvp?5Ruw?6[|ҿuw?Xo-?{Ѳο6[|ҿzѲο#_q{pmPm?+<%?!nk4{?fUV W\>a?cj˿\>a?g?oIm?cj˿oIm?ICi?ҋp?ɧP?AZy?Bҧ'D6l=( [?US]3˿[?Y{ä?4}B?US]3˿4}B?Pu?o?'Z6?5=?*ZB&y?G ?nc?eH,.B9?< T[h< x#X?? =tƞ T[h> =tƞXH ﹖74?qqᝣ?֨?{`yY?U:FNU:u>?N%;FN꿩N%;o;n1?"-?91 ?K?zSѳ# @Jpп#85;?A%? @JpпA%?q!??1w(?&a񷘖?؀?1ڔػ[PvD}h7пvD񰿁d?Τ?}h7пΤ?#T?"uw'ڡ?ߟޯ<}iġӞ6?5>) uX6:QEt/kX?HLn?cVǿHLn?yѲ_"_?cVǿ`"_?8YO@,s?sv@gVl/ҿD2ϡBWkD2ϡfQοyĶ?BWkyĶ?v%.?IʳĢ?ү k~?6{q?b]F׾?pٿX; !?ҿX; !?E%I?r+?ҿr+?.Nd?o]Ϳ?ذ~?Ӎ0Mp?,Z)5M}/WؿBN4ա?fҿBN4ա?;+?g@Fw]?fҿg@Fw]?{0~e?lT?ɽɿp;b?#G?A.? BӲ?`Hf 3o @ςd?2ۖ@ςd? @/DOi2ۖ@/DOiR@pc?'IAMˬM?wlC.',?DFi?x|?DFi?q̵ɱ?;Qw|?;Q&9P?'jVK?$\#z]?ͯ}W-2?>?k{(T?>?{AjF? *?k{(T? *?~E9?j2(K?*)b)ꌅ8? ?JXjQ? \|?غ1? \|?Ӂ; ? 8)ѓ?غ1? 8)ѓ?8?vAvcj??ӪOv̧?4`?(j[F?Mէ?yfe?8FE[#?c6gTξF;N橤ٿξF;cd ?ƂT&N橤ٿƂT&⿚u[i?QT?ri{?nqй߰?UGv!:?\1I.J"ngMap.J"ng'=a?gӿNapgӿN?&?q#ܪ6?n?CUftl@ ?#|8 cͿ&n?#|8 cͿb=_0Hn? zvU?&n? zvU?XI?Ri$?2"Zqk[?lwi0d`Y̿zٸa?d`Y̿s ވE?j!tKzٸa?j!tKFq.??׽v?Wy?~l!}¢E;?"?S;?s- M?F=~?*sEE=~?"'4??*sE?ZM1?m@+X?k?|?6^:̼(?tZ/tfd0?g?Z/g?_]?'1|?.v2U?"d-+9~ii?X>譻?~ii?`FmĿп{j?X>譻?п{j?͡~ܿ$M?jk尿 *?53^?q&gN;ҎMw?B?9Pr ~R/B?8Pr|⿇0Կ ~R/B?0ԿH`v?#놿Z33?E M?˹?f(B?˹?{Ļؿ Ͽf(B? Ͽ<+ҿ$.Yp?3D?Ӗl󹌿ACPf?/̯j%?W0(?Z?W0(?*)$ĿXyյZ?Xyյ=vҿ h?ŝ?/a=[g?G"0?㕺!B?0+9?䕺!B?$Yl+70+9?+7jqѿ2Ha?FR?Rl:Cʳ?Yjn?s&O?ϯch2?ԅ(r. *0?Ks*0?Dƨ ȸh1]ڣ?Ksh1]ڣ?1m,?uc"?h=?vuQ$'r6m?0p=\W?z pҧѿ=\W?cۿc|?z pҧѿc|?+?߫qã?a|aDS?F~7?{<ߑǿz|?.N?z|?1_ɿ3;@Y¿.N?3;@Y¿K[ا T?|B/_؀?Q Q|3 ?Ӹ"ɿg|?}i}?g|?NDſ?ſ}i}??ſSlM=*c?c]A?=}?(Nx?e~[ղ)?-۽~?kHt@2 7 ?h3_ ߿2 7 ?PeM@@b6Pj)?g3_ ߿b6Pj)?f%@:(?uըkzp? AN^U=?ƻ"?>6\Y?o9\ڿ>6\Y?ƧO?*(·?o9\ڿ+(·?I1@YLϣ?!%tie?4?0Ŀn5Œ?%J 7n5Œ?=LFͿ1oǿ&J 71oǿhjrV?? y9:Wn?6?ҝv/?1K/?pM9^ȿ(ާt¿1K(ާt¿uA?to"? !R?[K|g z?sT!}]?\?ɄaL_X'^jH@Yns ſ}h&?Yns ſ+=A)?P]?}h&?P]?9l?}?_G=?H/ua=jņ.uQ?ɷbO̹?ɷ*z\9?4&/L[?bO̹?4&/L[?^a?'?BLPr?3 \T)V? α?AU?w1|$ī죿w1|7s?U0, ?$ī죿U0, ?U,?6?mә?p-^pZ? ]w2X??R9?̻ vAD꡿̻ ގd?{(h&J?uAD꡿{(h&J?t%?Ay?P#By?=#zcrg擄_s?er#?L|Xn?t-Fgh?$=[N?r~?=[N?0^9?q%?r~?q%?P5̡?lz"?ut?_@?4?o}m*4?ahT~m*4?wYh+?ahTYh+?N(/?(Ȟ?I?$f?Ki??ۼL鯥?} G`ɿL鯥?fηÿ-8D?~ G`ɿ.8D?(GU&?m5LM?UV?w[|Ih?M@? Y򵹿dswl?0T @ɿdswl?ʖY+ÿyb?0T @ɿzb?e!w?f?4e8?H,е? wӡ4lT?Gtzq?eg?jgyjLr#y:jLr?o?S݃j'?#y:⿄S݃j'?V)?[?c1y?aע?I/T Xm-8ɷ`Aۮm-8ɷ nigҿo?`Aۮ⿒o?kc8?"M^l?5<ޓ?25՞o?`Lp1lu GX3ƿa猿<տa猿Ik2(ȿ /Z#?<տ /Z#?Q?tqii?rqj?jP's?(+FY7uCſ@6տ:Vdȿ{%mV?@6տz%mV??Cu?NCDh[?>EZ?0+?qCS p?q0 tΓl@7'=z!࿎7'=z;(*?J ?!J ?B{@R,W>?Ep.(?Qn?9x?ؠزaT#;῰1!ooHO9?1!oo:/T|Ajm?GO9?|Ajm?GKi0?l^Ə?>}?pn?P۝gbWļdr?f>?dr?|av%wfC?f>?wfC?.ù&?̌sA?Q`xYgSHJ??Y㖗ɿ*%T5?*c4?*%T5?FռBο)<ӻ*?*c4?(<ӻ*?wT,?(:?9khf5o?r{Wp}&jڢǿ6iz`?$?6iz`?yϿT1p?$?T1p?/7u`ɿ?: ?t{ ?Ff?m3I]c ? 8j W8 l?~i ?fwbտkesklȿ$kesklȿ:NĄ?-?$-?)D翴`٢?ӀSxIG?ܙJ?j5ȿ^0ܿyҭƿ^0ܿ+z ?}1/?zҭƿ}1/?B$YH˿਒b͢?_dOLo?VY\?r綻Ϳ%Gdú?綻Ϳ>@>ſ *?%Gdú? *?1?w ͢? ˓?v(v[?'MgW]m+5A<˿5`> ?n+5A<˿DqſKyaj?5`> ?Kyaj? SAY??e ߡ?Y=؏?T6{.T͡?48͡?ah|x?:׃?XP?z朱?H^!R0D|?G^!Ro࿋)?0D|?)?huڿ3pӺ?0.o? ?T?ٷ@ ;?ܲ(hk~ɿܲ(_J̿Զ6?hk~ɿԶ6?2ԠԿ7Fš?!'@o}v1t?'Pi޵[?'B_ޱ??WO P?WOd#?_.? P_.?}/?u"zˡ?*IzVݕ?}?l}?X"?P!m=lO!m= uSVÿv g o?~e(?}zaΒAZydX?ɑ?h!ɑ?a_ U?vhh!vh#U3ÿ^g?#׬ݮN$8Ir?_wsNi?Y֢?liW';WNLпQn-?> ?Qn-? ?B}81J ?fmW?mV?TK#19Y]24ƿ.?-5E?.?KZQ[?L= -5E?L= HY??]#n?mOEG(uxoQw]F0|{?F0|[c?l3bZǿ{?l3bZǿh0p?j7Z?ьgfTvz=;o?{iDI?z=;o?=5?,[Yoƿ{iDI?.[YoƿE w?{)ߧ?@ ;?,Ud?6ã/?"Hm?N|,z>nI?n ?ldֿN?ldֿ#`q? <ֿN? <ֿ#l ?ӭ]?|A?, B?U?ʿ<dp?ʿ<\)?utӿ<dp?utӿۨH?xmeS?X?wq[蓿yļ0Zp?ǫ[b/Uvaok?/Uva!;νxKֿok?xKֿt\T&.P?rJQ?`񦕦?C։Z}Rq? ۺ }bQ? }bgFPkп1ԿQ?ѿ1Կ﮼%kMH7?"ڂ&a}e#y?ȲaG?HԨ?;x?w{9v?H…gKhVW?kҹw׿gKhVW?NqC.ēkҹw׿pC.ēE@ބ7n'? ϸ?8z+eomClAܠ?A,v9ؿ-?"&ٿ.?z@"jtdvtĿ"&ٿ"jtdvtĿDSӣ?PH? Oi g?aC&C6Ze?hY8dZG< JZG/?x?Wk?x?J3?~Ơ? 2d?"Nz? \5?0,|? Zz^Ͽ`$; Zz^Ͽ7(Q?\$?`$; \$?~(?-@Ϡ?ݟ$?B;i7y?+Xm?4 p?`IͿxԝX⦿`IͿuNg#?4:5H?xԝX⦿4:5H?4}>?`vX?rf?!OёNp<?w奡?![m?(L5.ʧ¶?K#ůDUyLi?#ůDUőFE:Py?yLi?:Py?Ay߿[ڂ?%?˔JVG?|)uȿ: :٫: QC࿵eu?9٫eu?>Z ?ͬ椆?/K ?5R%2r{lk??۟)?X.7dtÿZjJCпX.7dtÿ#O<Tg?[jJCпTg?}X?g?l(?1Z~Ң?Q+c?d=8ÿe'^пd=8ÿNܨ藺Q窎?e'^пQ窎?%-?Ry"E?wb? geaD?t)ξ?-9\?Zrp1?c鿤4YT%?떔ȋ4YT%?ar`35?ꖔȋ35?>ar¢?;?kH~R#޴w?LTWXG? #WXG?]u ={z? #={z?t^ToUQԿܼLQ?(scޕ?2{Q?-TJU& ?E_ "ؿU& ?ڼsHb?E_ "ؿsHb? KDe?PԎS?$5>Õ?&fl?s06qa?כ׿6qa?\ RL*t?כ׿RL*t?4;52?0}ϳ?)GˑŴhy4-X?p塿-?^vh(f|J"hUp?b@BY%X?Z;~AY%X?'Gq?Z;~俛Gq?E)ֿ Ng?[?h`?v/°Ʉעm>?;O? ܁ۿ;O?e6{P? ܁ۿ6{P?RTS_Z?ͲF?@\C`Pg+p^m*D~x?Hv?2K b˿~Hv? 7?2K b˿7?5(S p}[? A'˜?Ub.fVw|&UYt?v?d4".ʿv?&P91U?d4".ʿ0U?vXUCxsb^?S͠P:5edGf?‰${ ϸ?}:?J>Y=Ǐ=? yyر?Z.({tg,%?Y.({O.n򿚺?tg,%??\`s;?۪fqΰ>?AuZ?z(E;VFGtmX?GG)ܬu=B?tmX?v=B? vG&íģ? X?WzC=9^?_t'v#6N?ٿ~l7R\ ?}l7R\Ӈ5ۿļ ? ?ļ ?Wiѿ[꣮?xQ?6(I`?fVHr}׿5_ Zb4?5_ Z Ԩ#ܿUZq?b4?UZq?2Qѿq{?0 gZ?+mO?o1.?bw::x?%v9Rns#?5l՞|]? kZr˿fwnп!kZr˿Uq ?"A`"ۿfwnп"A`"ۿ͒t=?-{DR?s/L[„5?y*]i)?j[ hj[M] =w4?6B¿ h6B¿nI@LZ?tv]_Ӊ?*ouNLv?;8?LEA?%a2?ɤGJOGDϿUU@?OGDϿ- 9B׿CvhUU@?Cvh;6/U6?_{ ~cMw?{X?JrV ;Jު3?qV hӿЮ:Jު3?Юxt ?|F i?GbzWw?bAxC͌bAxCcUL/?Q4 󖼿͌Q4 󖼿`渗-xá?cG%!k?9輒wz? 򔴼` 򔴼tp?o<`o<ڐL H'?(}=ʐ?d+SA?Xq?)_%ǡ?*9?s4{ؤ?]؂?nb"?o T?TƭAo T?^?thUٿUƭAthUٿ t?X&v?*hjO?F9-?ѡJPeX?(2Q}\?ZR_⿹(2Q}\??0*ZZR_0*ZֱK?,?#` N?MD?mkwG ?a+?rsO!a+?7I?g*8rsO!g*8򆿶q ?GY`9/?\4XK?fYP?[l]?NN.`?D/NN.`?f[J?4VtD/4Vt$??ʜ-̲?băkӨ?Y"QioW ͢?v?5`$?Tn" +俪mMoĿgx:?mMoĿg JXqo?@mb?hx:?@mb?c.G/ ?W;*Ĕ??/[?6X;(%_+'Ϳ&7L?%_+'Ϳ毥?ΙPr?&7L?ΙPr?,0r˿yIH?Tכς? z A/¥.ʿ-(꟒yڑ-(]s3?皾7j꟒yڑ皾7jt.?Wq?+D? μD׋vFɺk@ʿ, ; rgވ- >1bՄ?n"; rgވn"vB?zH?8*(MOl"4J-龢?c7|<ΙM7ɉ˿] >m4r?] >St?rbm4r?rb}<,(dJI?&^ɓ*G|ԳwRb^Jο:WWr:v7?9WWr*U@?eq9v7?eq'!ݡ+owϼ?>$*NpFDLK /}!?ѿ`!{}!?ѿY P?VhDѿ`!{VhDѿƳ??kWƉS dNꐿA.пQ n6.пӪ?a/5BѿQ n6a/5Bѿ'n?(?ױwƿi̦?xa?~rf%\U㾿n<+.>?!!翫̿!!ls@Kɼ?%ˑ̿$ˑ 6-v?wy?Ɂ2(t赿btEk *L?P y#忘PkԿQ y#7u?== qPkԿ== q_ ĿЊ?) x:%r `G?ǛͿGuțͿܚQU?lbտGulbտ?<ӕ/?skOvp۝ JJ@d?>O̿igt >O̿?GY8Կigt GY8ԿdH}?A ?4r>놷}:]iL?!0?C*?ގLwl ?h>?#ov;uD~pfE)׿;uDLOdMj}pfE)׿Mj.b-5?:ޠ~͜Y]H(?W ˴?ͅ03W# ^+&LҿV# ^dF-ۣ+&Lҿۣ쿰A + ?u{Pcj8~?- B? oQ?C3@ οS͢ÿC3@ οȓOA'CҿS͢ÿ'Cҿc5?yB@?\9dO"?^\?d! ??{ͿH$:Jÿ{Ϳ8/ҿH$:Jÿҿ&X?eFrh@?#΀?{oׄ-=U.;?ط+Z A?QP-?W&N򪰿W&N򪰿צP?r" r" Hzٹ=Aʼn_?qep8tcjȺ$N?+p ۗ?@1ŁͿx6?1ŁͿhb+c?SKjx6SKj"R6?n ??⬐?its&]ݫ?6w?\qϿ\?KBѿqϿKBѿ@r\T?ڒ 7H?j?xzdpB0?\?b}g:.u5IϿb}g:UV?1<0ѿ.u5IϿ1<0ѿ 4)e?F\V?E?1D'0x?<Ŀ0x?YԔ?b:#!ି<Ŀb:#!ି#Z?&b?dMACwNe)?@?x? ?(AwX G?i?gor?ֿϠ?for?ֿ Sc@PM|?Ϡ?PM|?QG???P{?2ޏ%O?z+Tt?_R?}?=?(e= ҿDxa^?(e= ҿs:A?L?Cxa^?M?9[{?#"Җ?|ܚi?`CBi??.dƿ?4@蚿sr0tXu?4@蚿!C?Ls9?sr0tXuLs9?teg?W8N?و/h?cd}܈?zfKÿ24zZ2g*?Z-I?5zZZ-I?{ NQ פ?JV?.xŶ^WQB?hL:Bv?12.F? R@rʿHjR@rʿ[^:𼋏Hj𼋏C5ؿMt?2ri8>PP<~R{?Ί%2PQٿ6h2PQٿs'fO@V俌6hO@V俫 ٿ@SL)p?^-]rПM rϘ\P?ѿ7p¿ɒ87p¿uAx?=ɒ8='[ӿJi?$q~;iP?Ӝ{ѿ,^^' J,^^L?ᆓ& Jᆓn~[9ӿ5F?4/K jDIU?>Xk"y+AcCR]ki;ſ? p??4Կ-vLs࿠ p?-vLsocgV`(#?PTjЃ"[VMP3&eAX?ڒ?dAX?['࿯ڒ?$8ٴEoԢ?`rNLN=JzS>+ſzh+??)?zh+?(4z?@B+Lrο?)?@B+LrοWrƿբ?S G8rbHmJOmYJC:?W$:?:??1 ?Ǭ{<οW$:?Ǭ{<οn'I ƿ_Y]?UCMEl?=?A`&L? Ÿu'^q>?IM1TT?b¿P2pb¿8_ 섎5D?P2p 섎5D?2^K>?7|FǨ0?ˆ;BS?w?5U?݆ ⏭{w?qINg82=?<׃dkY=׃dAh?keWkYjeWPm?:mj?r7_‰@cx?CRʉ/I?a{Ŀ4SE?a{Ŀa7>\b?mv4SE?mvjxGJ?؝I?w? <}?EaBR_Q_?yZ>ά?V$qAn[dPGW0?? J0??9 4k?ۿpֿ Jۿpֿ )0ĿQr>?U.?wƓmo$GtnZA迹 '!n?O- ? '!n?P`_dʿ O- ?__dʿ'E?0)QvJ?H?N(o?!%>5g?Or?Y0zOr?L9JpZǂz?Y0zZǂz???J?oD[F? mh?yɉA+W?˲?gք˲?7"3)ClbgքClb>[M?tݢ?•AuQ֐&$Mh\!4 ?'Pdlo?6aYs\ռziu?nbYҿ--?nbYҿħz?W&\޿--?W&\޿SSͿdJ?3C?|w.~?&j0l Z$w?4=퓸Z$w?d?}@6ӿ4=퓸~@6ӿw#̋ܢ?BL~?rph?E(Ƒ^]Q ֿl;?"l;?-q.p?uŖϪ?"uŖϪ?0dwY]Cע? 菫*}?w/?w+ګdտ;`?;`?1%r? =L? =L?%YFz`?1s>&?=oɨ?\gs3?V'tD?-UCx}Og%?AпOg%?C!AݿwYjs?AпwYjs?א1Z:.?5CK-e??ҬZ Ib?7`b?*(㤞?^`c?7`^`c?a;?=nTᙎT?P}@k~Olkο(%@:*](% "?4?A:*]4?ڸ'u;?UxyW͂?rؗS,Ͽ~KL<!C}KLw'?A:1;Mu@/~w˿0Կ/~w˿K$$?؊=?0Կ؊=?9"6 ^á?IȱG ?r,a?+'6S"Hpſ*'6 ?z?S"Hpſz?O a{?}e7&x?(5wBK#?Ftz۰Fc;0?Weŀ?tz۰Weŀ?jOp?Hg(?x+Wbey? ~圿w)?&4SXFa&4Sm4?c?XFac?qxѿϻ"?G8w,y?@೤)?'a?;}?O? o ?g42 o ?1?g42y^=??Fe+d4?X*?j?a?a?aXk+v@+3,?+3Di?2v<?,?2v<?'? b?8?;?Ww1?ts?-%?"]U?7+?"]U?{=?L hQvt7+?L hQvtCԱ?ظ?^h?Пb?fLIMn?Wɽ_?fv6_?@];?fv6_?jkFo}?˓"e@];?ʓ"eq~u?9|o?Vf*2?W9 P l?; 3WB??x?gW6~q?>?U+ O\tVe!UU?sVe#ZӲܿTr UU?TrntK?f} /?nG_B?Ef?T&34*y?&34|OT|*?DG?*y??DG?'ῒV?(z$?W:$?oLړ?*<|(t0?Ͽ7q>?'t0?Ͽ x[ -٩?8q>? -٩?peÄ?iN-? ŕO?y_? ?k!q{wοz$e:?{wοʋY'Ըg v?z$e:?g v? ? g?5U$rr`;d?B:?E7Ƃ?(PkgJWFwjwE?8?v[#]ÿz*Xo1ڿv[#]ÿRGGt{*Xo1ڿGt5gݵic%?ZЗ@5 >V ۾*{Ŀ ۾=n^~  R?%ÑZ|?^\D??DvyпY Hq~xY H319пڬEQq~xڬEQ񲿡2䤺jxfwL?.?HC?^Nk?MZ5οM.=w/wL.=w SZ;пzs) U/w{s) U&n`._?)p?Sɯ&?훉Ы ?Jpd%?O7%?F6X}?D. ?2C- ?ӃJ{ſ0:Zx2C0:Zx鿥;bgĿS?C.28 64L? \XwX>Gqs?&0s?$r?\#r⿞&0\#r%:BRȿjo?,OmWI?K_Y鋿P$ jO?SZ?ˉ5M4MڿSZ?Q?z(Կˉ5M4Mڿz(Կ7؟2N;wv?\vpfƃO?J*6iEKp}?|?Cڿ|?F)T?DZrpտCڿCZrpտIbZ?^4;d?Svu?Lߏ?ȑ3?7*[:?)㱫<Ov ?, ;_ ?, ;z㿩1b1_ ?1b10%x3v?̶1?S?n]@v ge?@%4}Z7@?@忐B:}$/j&4}Z7@?$/j">-@&U?/t$?r0P?gD]]O ?)?/K{п)?"?1ں-0K{п1ں-v?˪蘡?$LÊ?`h.?Cij ?put?=yԿqut?*?3?H=yԿ3?H?t0?^јs?,f?Iq>k?}i|?vҧh^_?8AuMx>A?8Au]ƹ K??шNx>A??ш?b]?*|2p} L}?=A쪿U9y ?+Ys?,˩3?=saYs?=saZv/?_Ϧ?Ãk&?%:+!˿O?׎›^h臅@׎›"R*4 @4_#^h臅@4_#"}H)P@*<4?|ePwpu~?>b7̿?t˸[nm2# @t˸b5,(\ @<@*[nm2# @<@*g֑E @=o?d pS^?(>ek{\j?ek:-L?">G?Bڿ?MU,AGnzAF?AڿmzAF?Px? aI? ?'}5N?fJIq' 2c?0 ?̴X տ0 ?Z5bT=k?ʹX տ=k?&Ι?>?C;WbF?劸ֶ?}_?9ow]V9?9ow=ܘ?jq]V9?jq+ ˅?Mz?}X FoQd?W Ec*=?vK ?vK>Ĵ?EvsDƹ ?DvsDƹŴV?5Z )0?&/N2uA ST=cMUpg?L>cMzƍ?!7F?p—8J8?}{*uEPiՔ@?XHħ`ٿ2p!W$9ӿЪh?=W$9ӿzX=80*ʿЪh?80*ʿ#?F~Um?dJކ? a e^?~8ԕCތ?oVMXտ?oVMXտ%,Ϩ?E`ο?E`ο3]t,??_!?e{P_0nMy?c0 ?{Yq&¨ң*j8۔`@M?rdܔ`@M?BQZ?rdZ?9o?Xc?9򿜤?C2;)ԢC he=a2!P?a2DL޿=V?!P?>V?W?wL?*iy)~?]Z} tY-04uտpуX¿}a?pуX¿8YտtKý~a?tKýTЩ C:Oޢ?Fo|?⤡35Њ/yHьuW0zտb(c(ÿ5+`Ϟ?c(c(ÿ@MҿňF5+`Ϟ?ňFѰl"O?.c?MK¿sQ+W?/AF?rC殿}_PʍvD?{G8:4?{G8:৒̵? 6J?4? 6J?(?IJ? ?S sx򝿗H髿0Xr?0忶俎Ӏ(?Xr?Ӏ(? * ;?+4OzfJ27hdǿʼ8קjú ?ʼ8קpcLܿ$t,?jú ?$t,?ש!v{:_a&?EV):ӵ>^鋿EȽǿۆ{KXP?%?چ{KXPAٿDH?>%?DH?eMP &È&?ӑGyVWQ߸?9#Q?mz?ŚD;dAI ?h*tW>пDtN?8V?DtN?WFֿ`8V?`>豛ٿ#{j?tࢴ1hmZ~07iTBseQ7H7?1r ?i*)?1r ?m&" IJDi*)? IJD#B޿ M??ŀLk֓|fsbΨ4P?wu?"Y)?wu?rkݿu:6?"Y)?u:6?:7q?B?[\]-,eyq|OfN?=Xr1?r5"?=Xr1?Y ڿS>?r5"?S>?O#H?*?m"y?n}ԡsD+U? }?og-Pv?K\%rv@pdaE? F#?pdaE?⧒(? F#?>RD?}u? N3?;mS?_ʢE`?{c?&J"oR?{c?}bl?)k&J"oR?)k⿔A@^?0O?E*|=?B_p0?pE qu?GXף?&j?sJC ?&j?:T1Uÿ [ſsJC ? [ſȪ=0ÿn#iH?)!M?Tm?خar?.l?;ix?7QG"6??;ix?Lxd¿0sĿ7QG"6??/sĿY!+R?c(F{E?s\RHQ̣?zeh%?Ȭ!Q2 ?!Tds0{D=pb#OgٿD=pbQXwXyrƿ"OgٿXyrƿ~ֽֿs? S?[(?mc2D@f8Z,eYp?/pU}[,eYp?8:Z\ӻ.pU}Z\ӻvˣČ[cʢ?gs+A"?,A9?OM?&'˿`pBeÿkH=8?`pBeÿtj+dϿPbNƿkH=8?PbNƿl-ƿt 8â?~%?ET?+ul?]@̿d/ G\Q/?d/ G\ɚJϿ=[]ſQ/?=[]ſ7~/@%_C?~4屿]vԕVc?@Ҙ w^Z;?NMH(/Ͱ?e{j ?e{Ӣ?eؿj ?eؿ_qADco?#SKxVS?ޝJA?_?~l}.Mbev?~l}. !5Yթ?1;ؿMbev?1;ؿkq+ʿ`;?/?Pned?1ˤob?Nә"XfտF?"XfտWBÿ?ϿF??Ͽ1 )B?kTL<]'d?ga =?mcƢ@~ӿ&Rcg|?@~ӿnΝTR˓wο&Rcg|?˓wο+Phghk?I㼃?TX@?Wy7}5w?UƜ?3Ikǖ?K]ӷ?j+?/@J\/@}?k90ƿJ\k90ƿ 3t CH?dJ~.a? 'WKT?Ģ}p?w?P.w?萄P.w?f?@'OGڿ萄@'OGڿþC<҉㿷B.?s7Ӄ?4A,ʆ ^"qk?-A? C2EzũԿ C254Ǯ_ni |ԿEzũԿ^ni |Կivv1O?USo@?M!Jhu1{4d?ͪp?kW, [rտkW, [yۧ1ZԿrտZԿ Zuq_,4?fX"=5*?.4Wt?47މ?3mѣ:'϶1W+腥з/?,腥 - ꬝Wܿз/?꬝Wܿj^8?BmrU?*=_į ~-rьdO: D?dO? >,ڋ :ӿ: D?,ڋ :ӿXnϐM+=f8-?vV,{>?x9 U?J^ĩX&Ct?{L㿜t?5iΩѿiݿ{Liݿ=.??^[?|: ?ِu?ί1?b}$o濒b}dP$1ֿ$o濢$1ֿ|3AJϠ?Xg`?5]?bUUֱ?\芩?t-w[芩?"XOտ /t-w / !@7GVD?4]Ţ?8*H?@DwI?Z-?.=n!Z-?yӿ Xv.=n! Xv*}l4@PTH?(ʷ҄)&?~~mɿx"pM?hT31NR:i? U߿܅>?850kZ@85Ij @b'0kZ@a'R"g`/@²,ƪ?¿{A?壖?h_:󺿼 Pd4忐!e}@ Pd4s!y2@v'!e}@v'N,< 1@om$?_5D ͿGU?lUUؿ&b2O@Uؿj'@5?%D'%b2O@5?%D'\}m'0@5}n?x,οW4 ?HNHiCgA=Cۿzu@gA=CۿzK@Z~`&zu@Y~`& -@B^h?%YVDد?D0憿/I??fջ?P/8S`@?i ᾆ?i K`?Qg@ݎn ᾆ?ݎn @~1C]?h%{(AGۗ?egſG?Zqj}@Zq& @ Sj}@ S<2=@J\ W#?3Ϳ s&>?;ڿ[OR{?IP%W@I@Ou#@LP%W@L_@V?rAkοY?R& jڿ(R?𳼻S`1 @𳼻S`lwTr{v?ONY$ X ?ǯ3݋4?|K\Q#?(zuk&`R?(zu'bl?¨l&`R?¨0X(?`X&X?~jxB6X?0泿A[?vIO?vI>? O? y?j?|$?[DpiK?lcov\?,d4$p?tF8Th$U̓ S6ѿ#Ao?6ѿAa Q|hqO8e"?q]`ؿP?q]`ؿt,?v &1ȿP?v &1ȿ'?1̘ ,?*/a4ǿpޡ"◿Ju?^~ Dۿ|5z"?^~ Dۿ:"?YWKBͿ|5z"?YWKBͿB,޷?;wvj?/Q㣊EĿuX )0?,D6?~yD2ѽWhE?AC@?@?AC@?Ĕh@%s?@?%s?{5?=?K#v?oi'?..?9\}?%7RP3bX?a3?-пa3?b@T@?-пT@?,V?F[?kpsšO/ha-\WH͸?Rӄd?".Z?Rӄd?g9V}?Ou2c?".Z?Ou2c?a?-_?Q{uhr&)hO8d7F?x ?g3Tݣ?y ?])?r'~8k?g3Tݣ?s'~8k?Xim?÷D!?¹{?0fCe;g?,Q"n@ƿ?asԿ?G᭧ىasԿىǿhuS ?_Pj?6(hi}?dE5Řyȿso>?g_so>?F^a- g_a- >cxsǿaB/?LB)aC?!糿F2?} siD^?]QkhĆ?ڢh.?A޿ڢh.?nP5?L)ۿ@޿L)ۿ}'w2?uZޥdr|]T?Lrџ7? Ӟ~ ?Nyѿ Ӟ~ ?9˝d]?ܻm|ؿNyѿܻm|ؿf)OܿkPL?; hh>#Μ-?yٟ_.{?i2"?Ni2"?NgkOy?(ϔN(ϔ:e?,|LL+?jƁw\w?lbκ?ƜdS lbκ? GjR~?sǜdS s~TO:hdOޡ?d6#Zϩ?˶gߥ?;vJ-\ǡ?Bw#?93DĤ?5#^承v"G?!r^;)Կ!r^ysvw?V5?;)ԿV5?1.쿙W?"XA? cM?'3RߪA WA?hvƺQk2wʿhvƺrh],?/mur?Qk2wʿ/mur?pH{ȿ CfX?pZ6f?&W: ?U($ ā?ØϿwloMĘϿN?X=vloMW=hx㲿FT?&c^?ۛ##?t[׍?tfO?nIο nIο,p?zI[< zI[<޷ GPO?3(p?_ K5?Њ?ۢ?Q%O(Ɍj5{??iM`nzL$ݳA^{?`nzL$ݳ&?OpA^{?OpG@nv^3]̢?6Yc(v $wy>˴ٿ-T qʿN D?.T qʿu\(R?Ν*οN D?͝*οyc+޿WԩN?Ӵ.tٕ.?e7UCD´пyN ?D´пe0Ԉ?o 0I xN ?o 0I I,6ǿNށͶ?'H:ss{w {R5KjuU"ѿqe6?juU"ѿX }8?E^Upe6?E^UPu#>Ŀ]f"ߠ?'۲?ݙ⚿u?jZ?{С?f`Y:15 ;u?Hyr?!:fUԾ?Fv2ܿ!:fUԾ?[&0?K-ؿFv2ܿK-ؿ A@dh^ġ?.cb?`B|3Wri}?B?@N;ѿB?<GI?{!׿@N;ѿ{!׿ ?p7â?9C4֑?w' CxR م?VHeC$⿚дp̿VHeC$:9?K@P3 ?дp̿L@P3 ?eH?1)?߷?zĦ^OE IG?l`cTҿl`cx]Ċ?F?TҿE?DMw-&?!Zr?/žp1HT15: ?f?% ێȿ6[p"Kh π @Oy ?Kh π @mg@p6 ?Oy ?p6 ?OM喙9r?&9w pJgͿarl# ?@Ϭv`?@KiY@5Q?άv`?5Q?GC_ %?п2"޿赠=v5!q@K졥<@wh3?K졥<@ߒ m&@z@xh3?z@9H'N?iA=;ѿew߿˦PO5 s@!a@LE?!a@_Q%@Y @KE?Y @.aa?::ؘ?ӫR?,}?],?X6i?[9II?tܸ?f07㿗l0?f073o8?(l0?( y?/Rš?;˓^?8Tsh)y?4~C?>(#yq>‹ ?>(#yq&<~?R,>‹ ?R,? Ph?kM H?H/ n?KQ?E ? Pގ?E ?A?u:nB? Pގ?u:nB?@vN%u?YP#4e[]OJ&$?`?BxɣP?N; ?CxɣP?>?x&?Ԟ <?N; ?Ԟ <?iJA?,?hODm,??kP?T9?5{w?s\WbĪ2J,?InĪ2J,?*.?׺QV?In׺QV?]?sN?O^ T&?|myV?5T ^z?^z?X92B?}Կ|?|Կ|?2!8y y!t?X+^?A14q?<@?Ǯy緿}?Lܟ)ſ}?`B~D?1Lܟ)ſ1!0 X묢?<z?;tvqh?,!h?#}WŴT #?'˔MÿT #?sl`m?-e`'˔Mÿ-e`}~EKl6bg?Wi-?ۈǸ?*̢?RdG^?Lb?@cIkǿ@ )?!-Q^@ )?=V?!cÅ?!-Q^!cÅ?j^@?R?g?} PGy?ۍ\"N4%^hMI?Mc?nO2@6y!I?h:niQh:nv?iQ?Zf?Im?0UK?KY3@խp?X09?I,uÿU!?s޿I,uÿr#%EQ<ݿaVwjoةĿaVwjY@޲?pةĿ޲?dŚ|?z?RkMB yҿpk&EW ܿpk&EI:1?q캼?W ܿq캼?Lo+£XF?QEБ?rVq>?)GWA?9&4ҿWA?^H:?9&4ҿ:?"bѿoH^B?I8>ؖ?xIesCq&?+oհ?bD2ѿ+oհ?8/3I?bD2ѿ.3I?(TV ѿꅺ?uQ?9"_lUí?B4?;^F`?a~[a 1ȐAl㿏ؿ,q?ؿ]Drr?-q?Err?Z%?U?eߜ?YѾ[t"[S⿟˵?.D?˵?Y}(PBo?.D?PBo?! eۢ?W?Gh?-`jׇWĿyK|#?1ŅkyK|#?8X-b]Կk10?2Ņkl10?hrſ[Ӣ?;\q?-f+Ee?Ya\dcS0~Ŀs/?<s/?-&ѿ~ ?<~ ?b5l=ſ W1 ?7X.vN%3anQ?#LJ?ψJ|T,P/c?1߿?kb?O-鿹kb?%,Uo BL?P-o BL?x]?Y"}S?enԻ axFCA8?f믵&?)`տf믵&?9C9i$?)`տ8i$?0La{?T+"? `i?ߖUE?0(@Aތ̈́ߊĿ[}?@4u`][}?0=G̿'*%#a?@4u`]'*%#a?zPoP?E_i?>W?. C@A&ANE.?«MdANE.? u9ɿu.φ?«Mdu.φ?MŭP+?0pFMp?:-[6? _ޗ?onZ)?Xfe g?cL~Hɿj g ?cL~HɿxDm @r:¿i g ?r:¿g4pC@h?DgW3?=oAef?WD.8¿7 ?WD.8¿яU?tľſ8 ?tľſ[ ?m.Ы?mg~?GrY&fTï' ޗfTï4ρϻ?YPܳ' ޗXPܳFkZ?۵-n?9J^yr׋0p?1cha!Ra!R;cc1?K? l?U5dO?3QMOy\cT?oՠ?C}.疣?, P]?no2_2kSi?no2_2J?ε|ۿkSi?ε|ۿ+g@&s:?:lֆ?H7Z_w"ŚDD?Peʿ+b%V?Peʿۻ?}'ӿ+b%V?}'ӿ)GS?@:)?lSڅ@9B?s?3d?or^?3d?Nʿ2&h?jB.?5 viޥ?ev?2P.5?!n;M?Hgsߙ?+v?J9ɿq9?J9ɿlIcG?S2 ?q9?T2 ?P[??w!1\gN?iY?tK}'k?tK}6A?DJsX?'k?EJsX?WLw?7ny?x铿4, Jzs?-k?i??Vyx?i?e1?.;W??Vyx?/;W?Kp܄w?d1솢?zL2Hӓz2ڥ?L$?9b~?pET}Կ9b~?X.(Y$"dS࿊pET}ԿY$"dSis?^sF?`?K_{%e‿ ?f󉚠? N7?fkҿ N7?<Q!+Nڿfkҿ!+Nڿ1)?`:Ւa?vV٫?.[BN]aA*?"9?Fט?cH3Fט?[#Dhk cH3Dhk BuTI?U d?xoԐ?+nSSDl??Wvt?n 3?iW|nsn 3?&0-,WiW|ns,Wa\Թ?0y3S?)_?Gx.kop7l?l?yu ?dvB_ipdZۿDF ݿL DF ݿV-CݿL -CݿA?o8tu?'Y?kQw? k`%Zq׿Eu̿~YܿEu̿pc:+׿~Yܿ+׿k@ٿ=t?X?%, R'Li?=5;es 7es9ѿTDxx 7TDxx2KeY=?"wc?nOt&ֈFlQ.t:n?b=P ;z\pP ;z\ѿw"apx"aQjco?Qw Xb4;pZߓ?ʆgu%< uB+οһ? uB+ο0oIQ?һ?IQ?B>T? cG8Zv?zY s?Y)ep? s?ep?YW;4&?#LJTa?o8+ *N? ǿ)l?vf?)l?ӿuf?CFb)?f}$b?5-?(d[?baɿ`?A?`?`Z*Կ;_A?;_-mf)?A,i?4(4ģ?w0$띿7N76oUAD ?I;?;p!?2?53?;p!?KIP1vU?2?53?vU?ٽ࿜%?pi)?YCP|zܥ? ],?-W=A?q.V?-W=A?Al'?q.V?l'?ha0r?E){q2b?I{?^5aWӇ?Up>?MW ֻ?Up>?ԿvMMW ֻ?vM3 ?[?0!oM-ec? T~?)}GK?*pU].?d׀?*pU].?b^6ӿ"3"Qjd׀?"3"QjA-? g?.?:2W?'r~?P?ƣq s?lqΞ?]:ӭ?vP6$?>bcؿ^>bcؿЙzE ?@#g?^A#g?iK9Ƞ Aì?B-@? _?nOM]?[ ?OsؿVofOsؿf ~?nlR?VofnlR?vacŸ?t\d?KG(x?r!.?VX7?o>8ſk0TG̿o>8ſj^`?}Q?j0TG̿}Q?*uf$?l0J?f̿l0J?;c?P}\pp?= ̩6 iD?Z6{t&4 yOlWǍ3(՟ݿĻ04(՟ݿ#|)?75k?Ļ075k?+ܩ`*?~$r?%4pKPsw([|п1w̩ſ|B W2w̩ſ #*-?K^p6v? ?7sr4Ͽ ? H/?pƠ?g q(? ?"[Ŀvl?.*? p?p=$D?h`d?eT?g`d?'-tT?>ؿeT?>ؿCJ=:W?$n؛?mE~ݔ?ԜUb[ Y}?k_ ?ds?k_ ?$l?/\ÿds?/\ÿ;̿ C?xG!?Ѡ[_aXھ?OrPϠPG9KPGQg˭9?Ԝjj?9KԜjj?~ ?;_Dz?U0:?`1]P3=r0'H93"䅸>hs=3"䅸&͂?iY?>hs=iY?f?X>I+?p/Bx?"0^?orע?1C룁?6xXap4? };S-ߌ? };SS`a?o'J=-ߌ?o'J= Y/& @!?ZrY?V컲?nš: 1? qBq p? qB$KxL¿hؿq p?hؿp_%?>D2?AVh?\5~An ,ǿw󪿆p,`w󪿫&BeĿLlH ?p,`LlH ?F电?lAW+?LЁ?UQ{b$LpſmEїmPϋƿ=??Eї=??M:F?v;{Y?6ٲT?aGwlJFf &r*?\$W(%gQ d+—@.<2{?9a?.<2{?(=ψϿ9a?=ψϿǻę?@1@ak$sYx+GXX?zjzem?}Uy?zjzem?-y]"'_?~Uy?"'_?V"ڿtE??gdޓr,`򌿝 /?א-ձ /?zп??א-ձ??tT 憎ۏ7?$,MLDYpP4rjB]?ݟzȸ? _Ǯ6ݟzȸ?0ϿuW? _Ǯ6uW?ۮz?I?Y??I03(Т?ձ?yW?)4w10V w4¿Vƿ"ijVƿ3)5;5.ῧ"ij5.ῺRW?G|a?Ev?s\Iu?`Hx@֕?X̞>?rY̞>?pV &~r&~1b?#D$s?K\ʊC#JD`?ȄwI rk6?'u*?0'm?'u*?NB8ǿUQ?0'm?TQ?v)?Yxs?5Hټ}bkh_d?ubA叿l?d`j;?E?c`j;?zQʿHL?E?HL?9*|?+ =?Dݥ?&Q?a0Mܥ'?k(??A?aP᝟t?@??@?uU?XWJJI??XWJJI?y\"BOaG?W38[?ơU?3ك?`XU[-谑ֿ?0NGt??*c@?#̈́?0NGt?#̈́?>~;?b'[?n?S?[B2ֿg?k ?g?ҫ?Q?k ?P?gҫ|bh?N?םY?; g!? ts ?R ? [" :? [i߽i?~n̿" :?~n̿y(i?5.즿; ?5?b)P喀+M ֿ?uyN?喀+M ֿӂ+n?Z@?uyN?Z@RWXۿAwtɢ?EhYJ?\%F?K w..rĿ< &?.rĿz'Ů?< &?z'Ů?4KP?8 Ţ?Zٞ9^M6(ˊ??R6 䴿nF/Ns ÿU?oF/Ns ÿ‘;㿷57K?U?57K?'ڈ(?̤?2+1| v1G6?{*c?r_𕿞 6m|!"? l\`x2kh0c`x2k%gu|kMQ>?h0cjMQ>?cX m?ٽ'Fl?@:+82~?U@]G?XMD濼X')?LD)?19i?v+?pxOPD,_Q6ȏ?,ř?/tE¿V(~Bѿ/tE¿8uN`=?V(~Bѿ=?ظ+?q}e? fvgQ=LiD̐?R?Q )ÿkпQ )ÿ60HӰ?X?kп?X?կq? ͭi?$lmI?k%mirztm?Z# ?tq}K ڸ?ԋ?w|M5ۿԋ?B{;0?(3}?w|M5ۿ)3}?y>,?ء?qV?f%ЛH&/%Q?OB?Y%P^ٿOB?ŮPU?N-ܱ?Y%P^ٿO-ܱ?/H<1?( K?/\G?ӥ1okʙ8?~9TE?U-Ϳ~9TE?a˦a?qU-Ϳqu8?~yq!M?bF``@?^A=4"Dir㤉?P ?!_̿P ?>鱼x?)j4^gǬ!_̿)j4^gǬyS%?)7+? ?p ߝY?՟ǿ[o?Ü?1{xKO!? 9xKO!?gD-ѤC 9ѤCsx'? ?osu?b~͡[^3_Dq?E*>Ӽܿ_Dq? Zg?_D*>Ӽܿ_Kó#8LK Ӣ??G^6\LE̿,F?),F?=N~?(R.{O)(R.{OB;Bnu?ZԢ?:猇?a Z,GͿk ?U ڶUk ?*?7|lU ڶU6|l7|t?"w=?OC5S?Eȿ*O ?.8s֕2So?B;&ڲ.[?r{,ɿC||/?r{,ɿG;|?pBD||/?pB6; rIT?4?|py xwL?2'?ʽG_俎4zc4mB?-޿yc-޿ ["ٿ^Ε?!69]?ȓ?_o̿}r-߿̕شk}r-߿);?dWB޿̕شkdWB޿񵣝ܿ=(:?uAwR3JCd?E?. $i?i]@ƿ@hǿi]@ƿ#ǕY?m4﬿@hǿm4﬿=>]-a?P?;(2? Mg?z°ƿzȿz°ƿI7:?jӿ֫zȿjӿ֫g-aLx{ 3?`h`lĨ?APi· Q%2?F?@~f?.G>~f?E'Nq?%]?.%]?mTK?Hm>cz?/*y?nqaOweY?f+'?o?鿛f+'?Ҍ?V vo?W v~ֿ>?(zqlQ?~Iݒ?_Y 4?V Q?nYNп [u*̿nYNпb^'?vO%O! [u*̿vO%O!*xm4>?$'I4N?!W`?n0ʜh?˪?t~Ͽ{\̿u~Ͽ?J){\̿K)6K"Ï{?h+T^?2?ʷYƿ ?cj?nm?܂3dLFBg" NԌ"QBg" Nb[;({_?ӌ"Q({_?7 ' ˺)?BIk?XP4jAe [,w俋uUfՇv.׿uUfs:XE1?Շv.׿1?bfVt}?Z*%h?zF?ҦP`?Ey9?Fҿ Fҿb\? g  g*l5N ?rɲe?@ D?gmT?o4Ĕډ=I%Uҿ++8=I%Uҿ4G$?G+,+8G+D.vn?kRX넯t0}D6?cƛ2ԲMFmZ}..LǦCkt?W}4 ?̌Ckt?uDZ(5)?W}4 ?(5)?eë̖(X?"e΢Bu|d?ϵ@s?E@?@s?>×؆l\?E@?؆l\?:e5 NƢ?ǖ,-e?;P׎I'~^?WAׇ" o?" p: AU͞?o?U͞?8>?_xh¢?v|_X?p*g׍: KU?rP-7 Q9q+7PƩOj"@gf ?ƩOj"҃c2U?>G\U?@gf ?>G\U?"zvſ]p,?ʵ>7H<81N?Ŗ*q/V?oڜp/V?;;cH&ZF?oڜ&ZF?[b?#?phH p3$%:?.}a^.Cf?l0둿.Cf?8ԓt<?l0둿t<?KX5J?PuZ}? ܬ? 븿^F#=)?3` ?5-A1dR.D?y>?(%ͷ?^YM{a'%ͷ?[4W,?9_ٙ?^YM{a9_ٙ?OCf&?v?˰@RB'Ɋ6XG?1dA#2连1dW.p_?H?A#2H?hauQ?xPWmם|Sfq?SG Ѱ?J1MSG Ѱ?m`?J1M`?XXa<%Q?Ppl}bfr{c?\W?gYM\W?6\}@u^pѤ??#a8?]G<߰ dФ? T{*?SFkZpt+Ǜ?* YVW?e^S?VW?Ϥ޿e^S?޿18hqMǣ?8b#u(XcvJ>ښ%? ZKrȿZ?ZKrȿh#H(Mp2ѿZ?Mp2ѿHѿ3ZO?@s젿#վBlQ?ՂՁ9ѿ_yÿIo$*?_yÿ|ڿ&Ho$*?&] Y?l ,|?xĊ'9d֖?sBοe¿$>?e¿9-w>ݿ>$>?>P'D?A>?zZAt/ʤ/Y?MYߞ?{ִ hGc+?֭?Rzo?"qQzo?l7?P"nL"qP"nL M y @k}H? P|~R ?D?{?xYtP3S(Ͽ>YtPDG@?Hl?&U?:s?}# ?b֫E?L't鄢?QDK=hcvqb=l?6`V?{+_;HQ hys`Dhys`.*俅?D񿄿?$d?>`;?Wό-?~<Ѧ/XO2עZ7̿/lٿ7̿a: lJ?/lٿJ?!?fcǢ?UHՔ?bnb%uNb镎ӿIEµ?#ts?IEµ?)bzԼ# {?#ts?# {?Mq?? a25Ȣ?l?DIrCuѐe94ԿI|?yWI|?N+k¿]l?yW]l?#jBo??Cm9t[Q=tA ?9cת|½qxI(Igc?<( y?= ?<( y?X3/?i7?= ?i7?ˬ?ebd?I7K=9?|t^C<?,t&/?!:?+t&/?mw芕Mg? :?芕Mg?J?\F ?BHϣl?kJcU?TYz(B¿ Uq?d\8? Uq?4%ѷI=?d\8?I=?&?<?)~l?)Caa.?v( xsثײ??ثײ?QESڟ, W??, W?ӮX|?6?FЮkfj?HK/, #?o.['?& myv(I_ 2?X#ݿ_ 2?W %X#ݿ %[+aT?ȇKʰ?`;p?wwO!x{?Q>kܿO!x{?f9PԿg>u翌Q>kܿg>u翄Z4ȣ?I |…n?zyJ? @w-]MZoǿv-]MeKʯK?ZoǿʯK?zл{ٿsZ~£?37$"??/=(ZGDwǿ(Z wsRD߶?GDwǿD߶?dKVؿSCK?lZ?J ?;I12\ A? ?_)?Xr[MOſPͿ\MOſ`!?p?PͿp?E`l]̿ բ?=ƺQ%v}]0٢?bݞ4|JAӳ7BY0@¢B?0R~@) e࿒@¢B?) epE^ͤϢ?!y~x?q?4_uES 0)=҇C?)=amN?a3sֿӇC?a3sֿiݿ㬚????'l^ "G?;*P?" iSϯ׿ Rϯ׿n.h~?DTS? ETS?{ ;:XēmP?tJ璿dH?30?=64߿޳ֿ0Ca߿޳ֿn??f0Ca?f0?Ee]e?a}veS ƿoL u?w?uQ8U]] ?0^@a{fe$·yt?a{fe$K$?6Q}׿͇yt?6Q}׿{@% 3?@xkJrʿG?f'ݒ?Df%bϿ}?Cf%bϿֵ^RȾ?"ƿ}?"ƿ6K?Y~Y?}dPIst?I;r~?[䀣ҿZxq"?[䀣ҿN3?j?Zxq"?j?{1?Ҁ5VZ?R҇S up?G6y*L?,cѿx$?,cѿR4I?% ǐ?x$?% ǐ?_ ?$އ?>󢰿8Gÿ[w?l="?{rL{k`#t'T.+?$Y?ھ9??۾9?XTJ5ҿSsz?dyġ?/>ġ?] R?};?X^??JrG~?`Bf?MH?:n6;?۝{{ ??.ࣵ?XD~@ſڛWD~@ſI׿Οz?ڛΟz?te}#g?D@~?T4z?Ϯ>?cF ??bL?XuϿ?bL?L ?T}XuϿT}xS.ڿ|n ?VQ?{!j:XA?4#[?F\6nS" ǿ4<ԙk?~=?FoTuX5u)יHy?Llsk? ʼnR-.J ʼnR/, @wl-.JxlDE^??F-J@Ѭ Y?2?)$x92x'8($x99,y|?>2x'8>@?Hg0٨:cq?HwǿF7ۃ@F7^Ț۾?pڃ@pe7?O7?SFLeNIp9\{?Ц\¿Hڪ;HڪzrC?0IS;񥿍0ISsX?z|?Po7?e= ?xc[՜濍#J'^Ώ>՜prHοnmN?#J'^ῶnmN?@q@?В? udNF+nﱿglNӿO,:࿕glNӿ幔п}p(?P,:}p(??Zwd?ߞ4@P??wC?w0 Tt HS?/N?oKɿ/N?~Y0ط?oKɿط?q?*1\?m$v?m͔$Q7PrbU?9ݱ?bUZOQɿ8ݱ?RX,W:9&n#1?cUZOQɿ9&n#1? a?0E ?؉o?;ĠwV,?ٜ?7tU!9ɿYc?2y)\Yc? =<)r?h맺?3y)\i맺?r?Z?%Sߗ?@bxଓh1e5poӿS?zm?QGY?R?zm?:]+D?}D?QGY?}D? %?? Qi?/>FՀ\!aMOIhѿVʹ?@?Uʹ?2=74ۧ?cn?@?cn?2? k?l?KЀS)\숿ۉLѿkW왻?ec?kW왻?t?6?ec?6?ţ?,8?gT@?^FC?G-ޢ??JIe?;?|<М?7Ȗ'ݿIM\?!#%?HM\?j}UTh!#%?Thx}?EzD?iEم?l?bc)v?77п.Y?9* (h?.Y?OC!{'v%9* (h?{'v%BS?mx4,?"pV܃?W]QL`!SpI^₩xEƿs?pd?t?dvq)װ?ޢ ?pd?ޢ ? Rﲎ9?oR)q??wv"G6JQeſ%H&ϼ?6>2?%H&ϼ?!K? xVh?6>2? xVh?G7?%LȪ?֦#턢{:=?QA۲{? *}?5ѻW忍<̭?d8|?<̭?!ݿɁ?e8|?Ɂ?{08Qƴ?eMb[:<޳?D}z?ZͿ0s?SϿ0s?ГunRk\6?SϿSk\6?djWP^.Ԥ?[MazuuɎA?D|N 0?}_3?>9п_3?z6(ֿԵ??9пյ?jc"IWǤ?pvXCy"??ys)?ia tbO?bsc"пbO?W]Կb$$?csc"пb$$?A??Lߕ? ??G?(lh?Xb` G?ʙiQ?3)8cʙiQ?vx12`?3)8c22`?rZ#?ӂ7?~n,0|++?h+K|\cԿh췶?;eh췶?ݴI?;eI?OX?G1v?#9BcXi?F`Z`?!vEԿ<.9M2 ׿<.i0"ӿA"?9M2 ׿A"?t 0V6޿ VRj?ӪBzT?Ý?U"?QNӿ@_^TAcֿ@w#vѿi%?_^TAcֿi%?h=ٿ@fy?, )ÿ2#J.?&[.Cp ?>@!W' ?L.쿈 ?u L~tL.쿿~tE7:oc֥ѻaNؿ>:oc֥?ƤKC?һaNؿKC??ɮtS?c!? ~k? t9mĿ+܌οG(|'+܌οFt!M0䆮?G(|'1䆮?5#?Royj}?VBdnPO?a?PȿjͿx&¿jͿʿ?hA?x&¿ ?hA?~N?˿?2ʵqοB7?d?RqfNfӂ? z}@Cl@:@Cl@:q߿u[3俀@u[3+D @9?DM3Qe?tb;)?8T↑I1?8T+8yc?ҒIrؿI1?ӒIrؿsO @8$B*?En{w%cnI jt?cW? ]qԿ&rv? ]qԿi7hɳ?jM^𔿴&rv?jM^X G?sᔅ-? s" A䞿FbۍHm?qKtƓ?E!ԿzY"?E!ԿCwf?(ZzY"?(Zx^P ? 7??KkfC?j-? +{??N/i?ɴ\FF1?za^"?/VQ?.?/VQ?9U?@0?.?@0?b@p?>v??:j6^aQ?Zш?~Vf1?NSMIG~Vf1?ߓX?{q?NSMIG{q?%@U۲?+dj??bk#}?Nl??d?;YĿ?d?½?ޥ槿;YĿޥ槿T&e,?6,? |?D9B~O韢}?/B{Lz?~/F;?WH¿~/F;?ƒ?c`WH¿c`. 3?TR'?quU޿?l?/X*+?o#鎚?>yx!!?.&⿿V"?eV"?{12?#o jԿe#o jԿ,%?࿳]|?H  鿲]|?  [SӿH  鿥 [Sӿo?%?hA?9u? !0¾=W'˿U(}k?qq;T(}k?l%n@ſqq;n@ſAX?[?w?҇HL4Jɀfvȿ0?ݧE0?D@F̢SAxĿݧEAxĿg1O>?b?/jdM`?6S?.5?hߟ?TvgKS?nу{Կ[c?f?[c?O]?ė/f?ė/Fo1?tTȢ?ty_?-c Yz.f#1?ֿ>? ??3Lf?~,}Yп!?~,}Yп 8r?.u?3JG p?I|%v? ⿩YBp?!eŷ?YBp?[os? й!eŷ? йm. '?Sq?Bd{s1?`O?0̺??̭D?ڐ OGb0̺?ِ OGbWEh%͆6?;)4Ŀ;usK²?2sһ??VoŜ,E ?"9)ؿdr>"9)ؿO*9LοdrLο?xe?8mH?Mom0Bp࿮JZx Ͽz пJZx ϿWQҿ,b ӿz п,b ӿ. ?qŲ?,Ol=œ0?h@ȿV"񮿀StW"񮿎r˶Ȏ?MyӺStNyӺ/п?^޲?K8wSA<}A5c?MD@3Ŀ >c3F >c2J@aK?`Y3F`YBX8 ^?-2?yD;Oy;?Mk ?4aT=;_?Q2⿹ ytOo,? ytC ?ߢA?`{[׿3lX?_{[׿]+?bgBd_?3lX?bgBd_?]NƁ?rԶ:?F){?k#1b#'?eD?+u?@Mo?+u?k=.u?9|?@Mo?9|?5 9)?J?w]dx?LUr(˻X2?t?Hq?@X?Hq?[x?S@^??X?S@^?,ﵙ?8B3r?i2?to? !@;?壵ψ?nAg¸?8~8?qP?>WrP?c;fߧ?$i>?>W$i>?'L?st?9j?t5%?%nT[? G_m?iDf G_m?@ylK?H&1?hDfH&1??0%?sU0?*T%HwLt?Q:?=aj?kHAɿ=aj?_8_|?B?jHAɿB?]Ao?y+?9S?ǃ ?p@?B[?.ʿB[?%S?01L?.ʿ01L?mu/F?#<(7?Ř:Xd죿f Z5?[Ruށ?V߮C?3ROMK_D MK_yIi }BֿLf\g?D Lf\g?l?J@? ?f!Fu?S rc-Ǘ5¿Z15¿rr7ڨ?Z17ڨ?n˱Է)? sb?#?Rbq gРUѿ7&Gdj?Aƿ7&Gdj?O0!?$?Aƿ$?>_c?6d_?U?F`b[p%Q)Gѿ*?71ǿ*?uW?d Up ?71ǿd Up ?m\?O?jl{?ܑաz?2 ZŐp.l9ޝ:пŲrfɿn- ?Ųrfɿqp?"\U"n- ?"\U".t#e @q"hUɢ? 7?O{~pGD vϿf,a}?fv2eћ?I9,a}?I9Zc4>T@2(? /?ew?֙wv?7ڝ?),&k;? ?Z[C!Կ ? ??2 W?Z[C!Կ2 W?+0o?Ŕ?FZ-[?Hfy?ahKw?r??BF_*?09ݘԿAF_*?u?(}m?/9ݘԿ(}m??ꅆ楮? }tqITv-?!݁ÿ؍H?yT\RU!ӎ?8D0пir@?UBv?ir@?u?%#?WBv?$#?񽑠?D[?uFr?E(U?42B sؓ~v?43h.L?~v?(&?ydd?53h.L?ydd?%S/??!Yf?WIK?Ak?ļAټ?e+?\(?Ovmp^e+?\(?|t6\?LE?Ovmp^LE?Es$?G6z ?`I߇?&%3Е?zL?[8엿/)hIko׳?Tx&j}M˿M~}M˿Q֯ (ÿM~ (ÿʠ?\w)?y tp=K0?n nįſ5RXٿnįſQuɿRCп6RXٿQCп?cV9?UYt1ylf0Ra{?4lnѿY Y?1׷? _ɿ  _ɿm?2u=?J:y&gkP 9p|?>ʼҿ!(v!(?zǿvzǿ0+!֯? 9Eڝ?op.1?+x ˋ56?R^e?ܿD"?S?k?&?:׿&?$iJ%@p俟:׿q0Aҍj?`QpF?:EO?حl~?t)8[?N=?3v ?X¦4v ?0ہ?V俸X¦V俳?wVܡ?<x?|\jocd??'?NߡA?b'jnm5?T[e?a'jnm5?a?i%ӿS[e?i%ӿl??~n#\y? !?Jzl?1q2?ZLNi?Z`2%?ZLNi?fn^b?RPsҿ[`2%?SPsҿ9"~?Hmp?_;??# td(⌿dR?aCҢ??.+(.B?܌lZeţ6?kZe7rxDţ6?xD@.?)q}?93?n2`wx܈?ONbfˍ ; Nbfqek?ˍ ; |?׎7WJ?1q?TG\? d?A4,Ϳ\4횏ÿ,eq\4횏ÿ'D?&#<Ӂ',eq'#<Ӂ'>Y/?iQ|M?8>l?+ɤ?ۣ!?FYɣҿA)ÿ- A)ÿ?u)}E2, u)}E2nf?Ce?QB}w&ϷA?{a?t,?! #u?yk?d"?QP?s:y?QP?)΢At?dQDs:y?dQDc@6X)@iq85^?Wg[Dq?=*n5?I?k!o?:?k!o?,mx?] +M:?] +MPCYY?ħ4?N 7p?ݡ'?`5ǿ\mԿ`5ǿ r^gq\mԿgqn?? \^>S?,[! L/ҋGq?RF_?QFǜǿEtrӿQFǜǿּ XEtrӿ XUVp???U)1Jy$Y@.* uzt?t9z|~8O^hSEfǿ=?^w=?*vYtZ ~?^wtZ ~?77گk_?%b"C?цk3\2f ?l1?e9Ϳl1?SҾ<9#?e9ͿҾ<9#?T?ޖq?P(mDž?8Dy!i?DdZ.?2$;ɣDdZ.?oU?2$;ɣU? '%ɨ?0_yr?\|?Ө+l&&?A_??RiJ? |fRiJ?bD ǃ? |f ǃ?R?QCٿ)gRӿE֢w>?E֢Ks2ֿտƷ3Ƣw-&?Ʒ3ƢfPݿ7tk?h6,?3!|?t?բ7 ?R/il?#'Eҩ?QbH<0?nH?PbH<0?<$ ?ލnH?ލf*BaW}?2x0dOr?JSя?{q?T?Bm?qmҿ-;uP;?E>;? Ͽ@)3?E(3?j?Y:ؠ?=€?JDW_/?`?#?Zs(?ED@h?sHxڿED@h?ڵ5ډ?mՎj?sHxڿmՎj?h?k]N֡?N0v?1BZ?)ȔrM?EQYs?@JX?Τv$ӿ@JX?'?bl}?Τv$ӿbl}?"l6?slVء?l¬f?=8^ ? ?L{y4?2V?wItӿ2V?ph?~p?wItӿª~p?8 /?pА} ?~/?88?Jx9ߩ?F6câ?Bm?.?EX~?́a3 ,?z?n۴=?z?D?n=9s?n۴=n=9s?c4q*?,$֗?CGR? 3^w?4fJ?#?!|'$?945P)տ |'$?SJh?p$zO?xLP7YW?9j"#fv?uVD)Ǣ?(8 59??E%׿|Cl?|C'%A`l?A`FY1#?R)Q:KCk? Xf,a?ezԿrgV?rgpc=?B$4V?B$4.@ؿ~?7.5 Pj?,6څ2?I?ɿnCͿ,W,)οnCͿWһȃ']Z‘w?,W,)ο']Z‘w? %5?-̿F> %5?@U?0+ Ϳ-̿0+ Ϳ?ܢu-?L?a[?Zk{c]qNբ?0i H?TG?E:TTBO_)ܖ[lD ` [lD࿂TК` 0MR=㷢?]_?ڱQѲ?gkV~bJҿЄtv&Q?ЄtvgNe,տ,3ȅg￝&Q?+3ȅgr_?"c?Gi?51V㗻?{5U ?㗻?XLd׿|5U ?d׿~FD?PQg?v5#ގ?n.g?w0_Ssϙ?ǜ?ϙ?pN寡?E7jf?̊} %XB?{n?" 3%{n?54n[;$/\ؿ" 3%$/\ؿ0e=g?C?s2dy^_Ry?1W|4)-s?VO?Abd@Ӭbn?Abd@Ӭl|%„r ʿbn?r ʿX"?%V?3jiBڑu6? 4zp?qv?oJO"ت1ެ?oJO"تPB|yzɿ1ެ?zɿ>?&??u ~И?f? mRӨ?mjK?5Aj84?rڰ?SE|?G;8U7ү?G;Ὺ;qݿE/08U7ү?E/0r)r?ӆh?ыid/0?mluo?E@^?=Dvۿnwf=DvۿH`ӿ>e)nwf>e)쿊XXv?H̐c?qb.7l,}#3?n)Ũ?W49ͻYbgƿW49ͻM*1A?C l{YbgƿB l{+?v,??K@ ZDI~Ʊ(?|dQ< ?(h@$3vĿ'h@$c_`b?UI2vĿUIJ6}?vwi?e[?X%ǟ?BG*5e6TCk=?ݙ܋?,G%j0fh?0PQ'0P1$Gd?g+>h?R'g+>h?/r:鿀X=g?{?jVN`蛿G?0\Կ:Ԩ?0\ԿW ?7fč?:Ԩ?7fč?!'a\rޡ??Տ\_?㏓mAI? x7O# xJJ??9 '?7O#@9 '? O'?? I?i$ ޫ%ͅ0?'P=i:'P=)v3?Z ;?i:Z ;?\xsMp&*?I?o/XòaH?vTzӢ? 4S?΄ٿ"ƽXl? Z?S Kk?hYH/`?T Kk?I $䛍?iYH/`?$䛍?Wo:=_ט?{0l/?Uml0j>~)F:=g?K[v'ƿLk?K[v'ƿ"_ݿ?Lk??*߿)g?ݸ<?Z>鲼Rk%ٰ?i5̿t ?i5̿zJ:@? ,%l6t ? ,%l6hsV44?)wz?OuȔK q͋)?_߽˿Q!q"V?_߽˿[$e?=W7 Q!q"V?=W7 5 LSM?g'MN?昣oߙp XH1?F?12/PO >ri( >rpT>mu*5csi(5csnY$cO?'F?ɹ'1T4f}o0^8<ؿ8p[0i;ؿizYD}`?|ySx?6$B?=eeb_ҿ`\ϿӢ?`\Ͽ*Wi ο/9_Ӣ?.9_]e`?Bђ?0!kn$iXEҿg]RͿ,1?g]RͿ_ԅοez\,1?ez\WRĿ;B פ?W?3k1En?qſ~"g&FK8J핱?~"g&̻#࿑Z;FK8J핱?Z;k$XL鰿Cr?_-2Df?|?n6{?L?N2?` ?H|s?*_v@JaϿb?JaϿ:m꿯m?b?m?ۭwAҿrIGI?}HE?㮾ڰ?/uZn?oy;?3㩕ڿC$3㩕ڿ(迯U4c?C$U4c??ÿT]@¢?Cl?}d?0xϹ?CUku?b9aͿ`J) cοb9aͿe׿&ƶ?`J) cο&ƶ?->'S?ĶҾ?*iƂ?fnЗ?u}?bz?S+@?1˿#Z1ȍͿS+@?1˿ͪ'ٿ&d?#Z1ȍͿ&d?@3?vr~?Žtg?{rF}c?X? s? ڷ?t?LK3_Ϧ_?{^>*]?Ϧ*]?Zv6S? 货Q?w?dN?"?ZZ4?]pS5!S-5!ev>Ď?R-Ŏ?Y&?gY}Ua?Jf-?-(>Ѡ?ȿB替ȿ/ܿB替ȿVXp޿,7t?/ܿ,7t?񮠩،¿?@xTXc?]%s? -w'?ڏȿsAǿ/ۿsAǿAM B\-,F?/ۿ]-,F?DMſz%?s ^?U? _k?:ث﫿Q,4G?s6^:d2ҿY$$?EDFzwY$$?4Eu[ӒQ?EDFzw\ӒQ?;!Y d?[ù)?a@?t7J̿ 4?x?hc迥 4?xp͝(TU?x?hc(TU? #.ӊо_ۢ?nseBq?d9 ?ǽX3m%B-?G׸ܿ%B-?I{ο6t?H׸ܿ6t?uɓCWſF(ݢ?vQڏ{)Ԝ?* DˮS fl|?* DˮGoRL?'a?S fl|?(a?xm\?חVKՏE:J#?RB}z?A^ ~?Aql ?NB ^ ~?NB xA{>sw|Y?WnH?=Eoa?esV E\?W*?H;M[Vwjǿo~x?6(?o~x???,TɅ㿃6(?,TɅT861?5~!]?R $?6;F;q̧mڸQ?E0U?+5Qu?E0U?U?Y:aڿ+5Qu?:aڿ5Bp/?(u?/fSr@[o(?r]x? ڬ6Xr]x?WL?H7Mt> ڬ6XH7Mt>Ge?a}?yͶ[dD&k>#Cip?(&w?J!y#(&w?m?&ƸJ!y#&Ƹ;˗?wno'?=R2ekWGP?]DʠJg,?Uօ9Hiz؀?Ȥvc_῰C?*.ُ?C?ѡ?f7P?4%HwKſ'i?aK?'i?8&zK?)M?aK?)M?fdcco?g5?3!Yi?nE&>)Դ[?WtpV?8frXtpV?Y&?RȆy?8frRȆy?z}I?c,[?1ۤy?HA]U:k?FE?S?a~S?P ?<7o5?a=7o5?%c?_?8 \? zɞ?cj ?pzA?>F)Iе? A?_U7dC<ȿntb(a^0y̿ntb(1sY?va^0y̿vc!`^9Yn?6W ?Ypq?2@󏕿̺ſ)W4Ϳ)F)?bqBW4ͿbqB}+8qJz?=IPg?/Ez`dF.O2"d|`ҿ.N29W?|`ҿ3P?l}.N29W?l}q_'rq?؍b?xv?@;Kz0:dy羂ҿX}y?y羂ҿ.j?iHƩX}y?iHƩJ??DA]]r Pÿș ]&Ÿ?dǥXt3#|hޞ[,ޞY4?Fê?[,翖Fê?HP071b?OP {`ӵ_iӕO*l&3|CQ^9Tÿ{C93?Q^9TÿW էB?}$ʝ2??4V[d"W)0HQx¿t `ֿ ?u `ֿ5f/?Y~u ?Y~u#}?×8?+GX0Y[BpDS#t*Y¿wkn տcسp?wkn տ #?|8lH+dسp?|8lH+.h>?Ir5?3lEĿ h23? Ԝ ~obˡdnQ9?"9M?ق|dѿ"9M?Py^G?vA@؂|dѿvA@!@ J̢?? tD{E94Ga?|>/J]§?|>/) ?*EF?J]§?*EF?(P?9!MV~ ?aJxI{>@[=Z?9Ku%ƿWN?9Ku%ƿmr{?WWN?W_.7?a?UKh\Ƈw`}VN!U:V?/lſWU9?/lſ/K2p?2VWU9?2VQ/?yEiD?KWu8?\>3<8U1^_d?Nk`?G|&ÿU}?G|&ÿFB?}T0?U}?}T0? Q.u̿嫙Uճ?S;*4¿__^ӿ_>*4¿~2̿2L?xOptVSk#^Q2L_.?3?6v?cҿ6v?Y/>8:rXM ¿cҿrXM ¿Cs<(u˿+o:?#5Gx?Ҝ:e?eЍ?H/{?p{ţ?.?bFz?bFz?mpт/@1H?0H?Wj~Pδ ~?s Y?i:]VGau?ś4f`?79+?Ew Q69+?'?qɠҺ?Dw QqɠҺ?`5!"iʢ?5<}?ӈy":l[[>?ڹ?"%k(?KglJۿ"%k(? ߛJ?Ij?KglJۿJj?珞п; kʢ?"P%}}?Qlk]?ROA?'m?)bڿ'm?1?k&T ׹z?)bڿj&T ׹z? $ѿi?He@X'?v5Vv?7Y?=9 Qt5@?0Ѻћ?Ok::c?E,˂a:c??ު1p:'?F,˂ap:'?\ d?tW\ ?y?LD3?ξD3?FTf?9j2!?Ͼ9j2!?2LNb .&?Ƴ#dy?l*c?< Bަ˸S?7]ԿS?>LJy?JZ?7]ԿJZ?:jؿ5C ?

ٿW -?#Ђ?,|5AÔcky?̪Y#o둱Bƛm?l-yvѶ?=˱7?m?=˱7?(DyFf? %bmM)բPlwm}㓷\P?C?\5Rq/?8!p?P?C?8!p?qcIe@U?@a*7*'s eFp:Da ٿ+]?a ٿ~4R_P?f1?+]?f1?9lZҿ+?貞N $'T 1U`ٿsFN??2U`ٿ&_@F?߅V?sFN??߅V?cł_[ӿbq嵐?C7?d.ePhÿ;{H']?Nw?qezv1ee X ?bU?X ?E7 @Zy\̿aU?Zy\̿lL?yH*?yvN?X 5?2?8~Ɋ?%~ ?HV*BVD?Ǚ+)BVD?蓫 M?Ǚ+翸 M?3w}_CĢ?#]H?ɬ1yf)rP{25<0ո?UQ^ea15<0ո?"# 6}?UQ^ea 6}?k𿺵y+^>? r,YE&k]|?R:2Ґ&O[AԿs>? 7r>?gL?YZSjt? 7YZSjt?c|?y??H֕+VTW?a'%ӿT~ؓ?RbT~ؓ?b?)?ܶ?Rb)?ܶ?ً?))h?%-An4j4 ?^z4?bܯy ”;? *׊WoܿEy?">zEy?BkOU>">zNU>6z?k Ȉ?;I6,?#z8rԿ dR[?p(׿ dR[?XklAn@4ҿp(׿n@4ҿҎbq?aF?d:&e?~84)`3j ?)`3jlz`?gZ݄ ?gZ݄w6r ?T5?WX s?"֐vVz`MO>ǭ0z?MO>ǭ= ?<;?} ?Hۿdp?Hۿz~?A&׿dp?@&׿`Ġ ߧ֡?Qo#G?7|?t.o?\V?V׫s?V׫??K6uֿs?K6uֿUlrtT?%p("?m@{]?"?c?)E0ʿ?f4)E0ʿ? ?&Imпe4&Imп!2tuRqU?-(;?0FD?NMם?Bj?za%G?cAr.za%G?/?oD4@οcAr.nD4@οJ.h5ţ?j?n= ܒ?Vn@?&Iţ?,(6?|479?˕?N3^ӿ՘0M࿄EF ՘0M,a+?t{:?EF t{:?+s͊tNC4U?:Ia?=K*d?*1?(ٿE|ῑJGE|&G ;f?JGQ".[eп{\*?l'? 0<~?p ?^:((/!p6ӓ(/!MF¿7Dxp6ӓ7DxG.gzNѿo?xZ^7?Nb?4q?Z?9=3f}oOFQ2:Zق?Щق?D/E.JЩ.JYѕKn?c.ZZ>-bWWgŖ~7q4տ(?`Z׿(?zEx쿱`Z׿xqJYֿKhR?Oe_R?r,*N?l@u $?~8wIſl@u $?`r⼿G}8wIſGؿШ? f?h׺ H(àR?sZp gz?ȗv?QQhÿȗv?XQwLPQhÿwL?Xk'W9?r?(](! x2ň?Y ᇴ?`4]+s?HC ɲ?쬉55]쬉59#s?>j.5]>j.*Hk?"N"'ѡ?&ft ?4?+2?E4FKY?PԈ%俕%zQԈ%v^?sP?Կ%z违sP?ԿJ4?4?o?Al?V z?Oum}0? 5՞v?Cx 5՞v?#]% 9)Cx 9)Q왕?Ww?%M"n?5҉?ML]7?nz@8?L!(mz@8?'8#1nhÿL!(nhÿ-?Χݢ?ӑ;H@ ],?c?̯rX+/~@d꒟?y%Wժf~NWժ߫l\?roҿf~Nroҿ ֿܩ?|=hbpjOKw?!˽̸s)Kvڿs)B SDۿKvڿ SDۿ˿fӿĘyKܢ?_”YY?owsg̿+uɿՂ!?+uɿ`)?TՂ!?Tm\R?10??뗿o?qDUgϡƿe[eaϿ %S?d[eaϿ\A?M@0 %S?M@0.BF?Jd?I}䦿=鉸?=AEMi?!?>W=U(s`?R??$<O̿{w,&?$<O̿[Og'?ջ{w,&?ջ"h?O?ע?E+N1E?FUݝ?&d1G?] mprd?õ޿iC?>O %iC?޾GnؿJ'?>O %J'?泝?`=?(¿V ?g-'?qˢ?5(Z-?)5Qx??F7DϿ 6>?B <?ߜJ?ok?\"4_ڗ0u:!?Fi߁?UUFi߁?ZDKuS?UUuS?:$?/@?ֿ;KaBri?;K^ᜨ2tqvοbBri?tqvοm{ bd˚+?Tt?]L( H?VԿMؿT3?MؿsʼC Xi࿕T3? Xiޡk`[?} t?/@ڑԹXn]s+WǿΌѿ#;)x?Όѿ@^ҿy|@ӿ";)x?y|@ӿ4>?P笿Qg٠]?;lͿI }v?<%FؿI }v?r?m2~߿<%Fؿ?m2~߿,xK'`?g@L2{޿t2R[4d$rj0d? j0d?4лPH}Ŀ PH}Ŀ{s 6?Bozci]xWh?>ci\P~?e ?]xWh?e ?Cz?C ?ǧ jbZ7z`l5t֙%vR21wA.`t-wA. W?`t-?r$?[¢?M:AObe>xZ֞{N|kC 亿?ƿkC 亿):: X_??ƿX_?Y`?4킡?RoLp`)zUk*ǡ?M"픿( d\hy/?Jr?8M٨\DJr?T W?=ǿ7M٨\D뿙=ǿ6lZ&P?QB?6'{Bm}?4`FkƐOKoS?A]пA]пbB m9鿉 u@rӱ?dmAk?1mǿ+[aֿh3?E ?h3?D?%MDF ?%MDn-#+@\cD?:70 줿-1?K\K<*ʿ!>տ~?s?~?W ?^|9Js?^|9JOv!U@ޢ?̿f&nÕ&W?kv"Rb4~dFnJgno?nJgLZY? ?no? ?EgC?2y`v sZڦ'c_߲WQŢ2?߲SD0?.JH=?WQŢ2?/JH=?/v?<1ˢ?iI]?%CݺIn?s󦜬Կ§j?s󦜬Կ0I&E??§j??b!@}9|?^!q!]|,?b0<b?>zwٿh??zwٿ?v6 h?w6 {o@TZ떠?񌂝(}?SҼ?Mm/3%{?Bhk?8p~x?:ॿ4B+@Po [DW?@Po<.?#? [DW?#? T#?Q˔?u{?~?Wk љڿ@1r?@1⿟e>?5/?r?5/?F6?w C? l?MFv]ya? ,=x?]ya?U?=f"? ,=x?>f"?~i ?~#z?$ %?f3%ǠV?5.俄 ,~?& ? ,~? ?aX vc?& ?aX vc?819(?/ķh?&ux?{7DcA$˥?0ȿmŸL?mŸP?:W}L?:W}ElWl?1?D+?"s?<䅿2C̓?b{d|ʿ,NS!:?-NS8Al?苎cΓ!:?苎cΓk 3D?<֠?e銻T(Eg7?k落?%h@?&>lKpai܇Z?@Ǭݝ?ܵ+?\=1?ݖ?\=1?&{Ѽ? O?ݖ? O?6l?|-Wk>1?ĽؘU3?oYW?@ZU?3ex?r֋?3ex?pڙ?f?r֋?f?}}?jPۡ?6sؑx%<xcI?`հUqvB?32,ߞ?UqvB?w&??32,ߞ??OT?L%ܡ?\Y{xs R?1ĴqV>!?p6ˤ?U>!?s6?$?q6ˤ?$?R?hţ Z?[,d?`?v~?eH([9лDq?'?Tf?{f8⿼ ?{f8⿔x~?$&35? ?$&35?&ŠR?.P̠?F&]DT:y.R?kg3ݱ?+~KL?cF8MOǿyjGC?bF8MOǿ{? ?yjGC? ?(8?u ߄?E 9loG?}:y??m?i? h?Q+8?7x8x? 7x8x?s?Ear?`Z~?,hm|V?9U̎?̡䱹?pum?92Mpum??e2 ?92Me2 ?,?F44ߡ?0CP?;D?`?+?M>ן???ЊQ??4 1? :[z9{xd?^7pD@V̿x@N?;th?x@N? ⧛s8lR̿;th?8lR̿Ӹ5~Jp?=G?%Ns %Z[g?n%lmyyͿok[?:?ok[?O.¡Dm&ʿ:?Dm&ʿhM.br?lu=SHx=K!ʲ?kWٿtOgjWٿ?YpPa?tOgYpPa?|0f?hC?ZpkR?&NLg| rU?OZ#ȿ ӹ?PZ#ȿㄱ?5F %? ӹ?5F %?Via?ԡVA?J?XrUFw0j_?φjȿ9?φjȿGn3??9??~?@?ʺW?jUC?֗-Pv?8,?eUJ?V#R~ ?0ĘjA?ShVۿ0cR㿬< 6B?0cR"/?_\3?< 6B?_\3?1NNFI? % s?jGǁOo6a?5Khi?jS;ZԿWH@ +a?WH@+=?²- HFοӒIG @rՙ3?ӒIG @b#?pcJ@rՙ3?pcJ@674I?c:??- 2| -y˿ ¿Aj/H2?=6_@t?=6_@&g@ @t? @oܢF?b~nc?˂iY|̿Z9CĿϩCI?]y?@k*%{?]y?@z @חg@l*%{?חg@|ܦs?k?3C4r??@x? Sȕjƒ?*obzQ#?QHB_x@ rdJ?? rdJ?=^?*gR8y@?*gR8y@AkX5¿m.t?#[p̔?MZ^)M(@-|>Q?g?-|>Q?]#MH?Ŭ,?g?Ŭ,?Կ1[)j?8\4{0`^v'hV?G'Ɉ?l6ӹv]?G'Ɉ?T'?[ ?l6ӹv]?[ ?@)Ap!B?Ud/ k3]iO&[5H?D`M?“ z?D`M?%`?hW%?“ z?hW%?A~?|] B?.&V?Ցp3H;?e'` "\?e'`G|7?DQ=?w6-퐿_5pYd?j50!?ky?l(LF?jy?è@~?OlRl(LF?OlR%Z? w1?v1`>?#Ok?Wދ?/ /멿:j+?+돑c?:j+?:6?:Jl+돑c?:JlB?i ?WY%5pJӿ|5@Xb_?%>Xb_?MZ?6#? ˀx;&KmqA?O',^>d߿O',b7?/HD?^>d߿/HD?Vx?#3p?@zku?65`?ftJӸpYu^<_?ڢYu^<_?\A?˄u?ڢ̄u?Z ʳ_gZ ?nׂ?b^[?$S@TasSJ?zSJ?`wm&w? Co8ӿu>m&w?I ݜ4[V? Co8ӿ4[V?y-?,`_?}`?-? ]]'j#waδ?dFj q?F3&Q5-]ĊZv rg?j޿3?j޿H5mB@rWXE3?sWXE8!5ib?'Aw?ڧ$Gt`60WNF?g)ֿVf?g)ֿ6x  ?Mz׿Vf?Mz׿/,Pi?:Z3?zRƋΰnF v?MNTrОmz?~נW<;%?~נW<'?|ؽ8S?;%?|ؽ8S?5.:? %6?P ሿrO:Kt?wrz?CL?GCd ??SԿ% IпSԿoeAZL\Ţ?לHp?Ղg?!N?$=7g?pbc4?-uqbc4] ?8Ej??-u8Ej?*Ԑ h?>Mfܒb?G%lVէ?PWG?X?PWG? ?Y??X??ܱ! ѿ_^1?pGg}?᫝ze2݊Q? O?5.*? O?&v,z?).W?4.*?*.W?Rҿ[?x`^?$_ǍW?BXH'?BXju? 2 ޿H'? 2 ޿ѿB5w#|?.. ?p -?0NrtN{M?["mFOt?["c&O({?6rKmFOt?6rKckD3ٿ`mp?q? k?ޕ?;Ng⿱>ޕ?FRrҿ9 *?;Ng⿻9 *?+GvKs?)1?Xol_Q*0`a򢿌I?-{w? I>ȿ-{w?DdRc.B+g? I>ȿ.B+g?dW|?:I?O {?'m9؞N?!kØR?r}^e?!kØR?q8lW ?q}^e?X ?wW?^4B?~{FQx(UZw? Ty!mŮD?]a?Z{T1V?]a?ƮO+*4:?Z{T1V?*4:?\0?DU?d%뎿nB ?v8?@7?j(#[ a迣.V?BBdkS?kn=; J?T? J?3{r? \?rq;z/ˑz?qYv?C.?ZH}ؿ`}qz?ZH}ؿ60濨\`}qz?\M,?Ζ5,?1p+i&u?oMRp? Ah?3=E:"m;?3=E:zDtõ&ό"m;?õ&όpX ? n .?\kp #I1s?zFp?ؒ)p$?fJխpbկ?fJխ5%.yI,pbկ?yI,\f?S}F?1?Y?(Ret#? R?x?aȢ?lI. x?+y?xXT? 5xXT?v^?)ڊr3@ڊr Sޫ?[zɿo?J{?j? +-7}h3Yb+\?fbNڽ?+\?]J?HOg?fbNڽ?HOg?%ϣˌ[@?*4q?0 Wë?)i{kSC.IK? pҦ?C.IK?^]ْ?x"? pҦ?x"?c8⑿ 2?N'w?z?5*YĦѣ?.Q?bcj?3qq޿"Yv\)# \?"Yv\XL?b<翦)# \?b< .,g8?J#ߣ?KD?E ?0טZc?av޿opy)-??opy)R ?  ߿)-??  ߿<f? z?WYF)?QsD?&xҿg/8Wǿ 3:?g/8WǿRI_Jʿ:9ش 3:?:9ش]AҿW&o?˫N[ӹU?ƽ{s?r'Hfe`ѿ96i@ſF&й?96i@ſ9@ȿ%8F&й? %8.=rU-ҿj fqU?zi֋9ſZ?uV?Ru%,?bFԷcy{y%?, fSNޱ?B&B\0ܿgґ 𿗉B\0ܿ Ʋ?;Nd?gґ ;Nd?ָl@?{^e ?e9ӞwX?_kD?o6 ;ӿ_޿;ӿf0 Կ+i ?^޿*i ?{l%Ģ?6?Ϗ>gaKSv?gʩ?Ҟ!UҞ~^п?!U?@Aoef;u¢?+ YU|[d?p"?Qd%8Qd%b Yjο6彺?86彺?Χr$iXަ?mxMe?>>6?l$gУ 3P9?,4ȿ?,4ȿn Gqn?ȠU&?Ah4?,X?@?k?]Ŀ@D?.IH?@D?s?'?.IH?'?cZ?9?歄{? ri30?_Ϸa&?L?Ϸa&?l>u?6R2=L?6R2=iR?|W?zi{?Z1pٷ1]?wﭯqh??qh?^?i?iiͶ?^>?يsII`?w'nSBk-]?!*?t4Jȓu]8] 信;۰?9] ͯ&PL_Ш)?;۰?_Ш)? qOYƣ?umIѓr?CY}m?;Ġdڿ#J !zۿuhp?"J !zۿ8]4H$cjq?~uhp?$cjq?3P|ΪwN?Z7xgd?]; ?7jtrÿK?rÿg֜?o`*L?o`*m%˿YeN?#XW$x$!?)C?_]GJ86hĿE 3?J86hĿ0ٱ[NE 3?[N/zU\ʿoyX?k+cu$k}ѭZ2qv?S?EE 9H?a6Op?:2F?^3m?:2F?Q>3+^3m?>3+1Q x??lQe?욿cp4嬖?$ &?*71-^̉?+7{/j9_[1-^̉?9_[w?G}7 ??gځgۙfƉ?H ?٠/0Î?=$Ϳ/;=$Ϳ?6M/;6Mbߚ?d?IJcTЫ~?~:?j?ER`˿ חހER`˿4e0?J( חހJ(^?>aKǡ?t#Qv岿ph+?VT,Ze?}噢>P߲?h?H %H֝+r?? %?6N?X,?ɓ^~ 5jBsa%??4(px4(p ?Y!xY!A#N?SІ٢?/hd֞I޿]sj?&M*?@c]KʿxAc]Kʿm]AS?+7o*x+7o* F?su<ڢ?킵h3*B%?EK?Ӷe 4N[z3腿i_ֿNcڙ?>$o?Ncڙ?:ԉ0?vC>$o?wCL1p. ۿ;߿?yVL? (f]ZnY˅? ;B?˅?c?uxMֿ ;B?uxMֿϙ׿d_\?T`u?{8w&$ v?UbUI]ٷ?a?k5#a?z߁zBnG?'\57Ws? 净Ϳqw?g4V?SF?{*I7SF?O'?Wh8!ߴ?z*I7Vh8!ߴ?%Ws紿̉v?Bs)EIn?چiq?r'u擱?U|5?6b?xoڿP3yoڿC"N?4"?P34"?U>Q?ey?F/A5?F+?Zo? Ϳq4x޿ Ϳgo?æ4k?r4x޿æ4k?$;?Ң?^HZ臿#`?B&ْUz?/w?1 ~?w?6|X??1 ~???_xKN썿]ZU̢?qѯ&άI5Zd?<1$y?AfU/0+N?(H$9{?+N?F^].VA9?(H$9{?VA9?ٻ"ije?`â?b' B??cN=lR?HL_^qO?8P@?]3?"R?.x"R?]^L#?ƨc?.xƨc?^&?ɟ妡?;gߜ v??HE&?h.~¿{Ŀh.~¿UD~?P!?{ĿP!?w?7sY>J?j]ߕ.USym?E?T=`:v“T? -FPJ|i=`:vOJ|iGqg?F?3A~%2[+zTm?V(?J7 $I79Ӻtp $tpjZ?-2^פ?5Ð?/m?% ?K2G?o?Q?F1ɓ?G^m? S՗ ȿ39?՗ ȿ 'f /՞!2ƿ39?՞!2ƿcη^ӎp?#R ?=`W?+I0?v嗺evz=qȳ?wz.v5YT?=qȳ?5YT?k޿ ?}~N?4Y\|?5i/B}ڿ9Тj l?8ТjU }`տn2tǽ? l?m2tǽ?qA5ƿl =?n%?/=fFz?@~E1xgniڿ<KԀ?<K9q1׿ɦ#?Ԁ?ɦ#? -`ǿ^{Q?eͥO0Gܕ!?^]? !?<~螿;k~?|olT 77D?_vw忿?Ìc*Mc?_vw*Mc?xZ3?Q.ĜjxI 7b?Iu꿷)(5ֿPIu?)(5ֿEH7} ?PIu?} ?Eďy&C?Q6Őflj?SKYؿ/ϙGѿ#e?0ϙGѿl8~ڿ(D'?#e?(D'?Կ l'?zc)7zv\փ?pSCտ[&пo??[&пj:@ۿ$ΰ?o??$ΰ?kԿX 4?{pɿl%ȿ-D?l&.Y(?%~kn=M?䁮?Sn8?hat?U?ʡ5v?\ʡ5v?fxꭿh\hvh?g]?ۭ[t.Rk?(?~? Tύ?j^ʂ?O7S?/{s|BN-?;I?BN-?D!;I?D!yluۿ_E?)?&Z?S?CֱG+#?rW?+#? !γr(rW?γr({7?}D?M{0H?{w?j iyw?qNxe? v{?xe?F員YԦ v{?員YԦqKns?/woi?#? t?#ςy?L歺]? wF?]?nj![ܨ Kڣ wF? Kڣ7x S?SI?JrٷX)!ڳ˄?"*[u7ސ?=juiMEԵ?;A=>;?MEԵ?%!S @\?;?\?A?'գ?96?H~)`8vܿP,ˢB?tW7X?P,ˢB?u'L<@HʿtW7X?HʿE=?*cUF:?C4A䉿mʥ?)Na? OĿЄj;G?Є4R!&?nſj;G?nſdzI̿ʀE1?2A*KM?xd?v_l¿¨_[834?¨_[83jٱe?1eÿ4?1eÿWHʿ8A?(۱?Ȑ-kɯ?9?=4"ҋ?Rų?o ?yr%?E¯FD?zr%?%@)g޿E¯FD?)g޿%0h?H' ?=ᕿ[ci?k x?B, ͵?V&83$av?V&x %?Jڿ83$av?JڿyՔ?$7???vǣ/?ǯpy?#3zMa5¿O?Ma5¿`Ƹ|2?λkO?λk"2~?vh?Z)wU/?, }?^[I5 L*?J5 G?GcXL*?GcXN 7?D߹? m󰿤H=qڣ?屙|?OMtޡ?7\r=x?הm?FC?"W?3>β^?"W?,:L??3>β^??c҇?+04?3ke: &? &?-|M``?xH/uMݙ/?wH/u,?3+?Nݙ/?3+?E^Ѡ?]-&n+?)^:G?ɱ??`^?eH7Ϳ?dH7ͿQWܱ?_̆B??_̆B?A: ?%@?2?HV%ɏ7%?:?Ok?+̿=?+̦̿1J?2?=?2?¸"#?ڞ>?wq?U?Zd??)gH?#$߸?62?ODI?69v{ݿUL?9v{ݿQ,P=FIX%@UL?GIX%@ 'C]?;B?!y?~?7?\؆%ۿD?\؆%ۿuwڿ;wfq?D?;wfq? ]j*8XТ?WqB2?wߞG?oe ?Ai ?e#\ʿz?e#\ʿn6?.$Ծ?z?.$Ծ?oRĿZ]FxҢ? }D?]؇W?1;#?ig?%>ʿS&l4?$>ʿK8=*?:&9,?S&l4?:&9,?nq(ȿ*\\2%?$(T*𺿖܏X" [E?\gJebe5à?O vmp?,/qr?Hz`N,/qr?f`޿>пHz`N>пP϶:^n?GM^o_G&7?d?a$?-*߿a$?{ǓZEt¿-*߿[Et¿J&#?W@AK醿޶kt81?6~3ſ*ŇW<*Ňaܠ̿/B!g?Wt"M?gÿAKp6Ac{οˏzvJp6ʏzv[+Aҿk?3<U? ۓt~ZGX?BPB8w9?6<&׻?+x,b*x@f ,b $կvbqu$8?!e???gtRw ?έ?z?/ڿέ?ֹlz?/ڿ濂wߴ$?qHS )̥!Go N־?-?hdʘ; -?M hq@ƾ7¨hdʘ;ƾ7¨R=m"Y{%?l_]%ҽ-J$?P7?O7?+4h&¿h&¿TAa̝Ѥ?PA;?xp怿j?a!&?գ? ˖WUu?&}̥U߁_?#L!#EJȿV߁_?(F94?#L!#EJȿ5?p 2_NaK?+σZ?Ow "D ē?lzW"4e4?Xln4e4?y$yڿ)D?Xln)D?TܱD'h?>x{?qrǿ6ci?f+&K9'յ?%K9vlɿB]?'յ?B]?3׊NͿ_]?iޓ׋?IaNcm?@*um1y?vm1Т 0ɿkE?y?kE?Ƚo|&<οN0 Ţ?>C"r?pĿz?m+ ?zɯ h,>%(??Qi?d͚#"R2x?d͚#(? @Y?"R2x? @Y?Ό="L?'Y +L˰2t ? y?RJ8}mپ?SJ8㿆+B?ʱ~?~mپ?ʱ~?H'Yq? ?UZ?))ͿGC v3n?GC(n5{d?v3n?d?,)&I(ߢ?4ǀ*rk< C?siCɿ} 4 C]?.yƏ?3 Ȍb? ?5AOR?@ 65AOR?U(?/f?? 6/f?2AjJV ?yfX8q,r$?viH?P?[*[U޿P?2?tXd?[*[U޿tXd?k+ۿlb?೿v`=?l#? "?֬m1 "?%¦?O !?֬m1O !?;n?_,*?Eq uXt?BwB?`AD?o|?FB n|?H?CA?FB CA?Ę?Ą?Mu; M?}]ʰ qPl?ýoAҟ?nW+?`/Ux?uhiz?lWK| ҿkWK񿌄 LG?=r)?| ҿ=r)?JL0?C?D!;|?(KW?~n俠Rܿ]EֿRܿXZ?pƢ ?\EֿpƢ ?Rb佡?;4q?Dt+?6ĥͽ?eyY?/rf/ӿeyY?D`?$vH?/rf/ӿ%vH?S?Zʡ?Ŗ?s݋?;yc?xB?ZIҿyB?01D?US8%?YIҿUS8%?Zk{?<$E?N}ȴۚ?KVT2?!!ܢT?9=Mዿu%π9'!\ @cC?N+pCcC?@&@ ?N+pC ? 7p?D·?41?q5rwdR^:a5?SZ?OC'*?SZ?qm?!%?OC'*?!%?{x? >^?8?붧z*;j lƺD?o g#?49?;(49?8ޠ?E<;(E#?ُ,fBُ,sAA?d?[7?{^P_?s?v(?oz?%?W??"Y5!DԿW?!DԿ!_?N.?tE?5#q?q.ו?_*' b?Rv? b?ha!z+:kRv?z+:kv?x)? U~?5::R\=i?g4ÿ`/ޭ?w?`/ޭ?OALA"5w?@"51w v%?57ST?pDpo?0RCW¿Qqbb?F>?Qqbb?*QlЗ୿F>?З୿!Ƀᔻ '?~F~>={2?_S?+u_⛹?^mk?i6Xҏ>]IW L`/>]IW ʻ5?M`/쿀?IKBGNܺx?d~*7yጻy?L?yBr߿PerލJֿPerލ|\3~L?Jֿ}L?[lz{?k⍿'N?cB erkͿ 2=D`SD&? 2=D`Z~̿4jv?SD&?4jv?_؈yEo?/okZ_&爣?F: JUsWZ˿eV\-Xգ?eVc?%Ϳw@bM?\-Xգ?w@bM?%_!`῞6>A?sȟ8$9{%|,ҥ?^?!?|y?DRv{H[̖?Rv{XӐ uPͿI[̖? uPͿ jk0Ij?ߔN47X?ހ)'OJt?se׿S!8*gFL >?S!8*gF࿳Φ7pJNXǤL >?JNXǤϯ+uʤ?XUe? &7a?"ƿ$XZdݿ?$X2B<Ϳa~Zdݿ?a~*n u¿Ĥ?)כSX3V??!O2n?ruQpJƿ ً||ew½? ًt˿Jeose?||ew½?Jeose?0LÿN1WC?SUb`ug?[-?Xn ᬿDB¤? }Ͷ?gK ?bCؿ\? bCؿ>:{L|G2]?|G2s?\\e?"t?3 P?Q?މW&6?%ۿ>zoG?%ۿdд뿥S`>zoG?S`ڃf?"<ǣ?V9 %c5a?e񼠋? d?^oпSmxD?^oпF.ǿ#5-?TmxD?#5-?v=?|<+ɣ?rǭ0'\@?3C-?gi?Rx+пag?Qx+пEĿT?ag?T?W?Bf JI?-i6? O?D2E?%?A8iбS?¹?1;꾱?1I]5?uO?;꾱?vO?UM؆&k!?,S`V|?6s.ʻ?ghs" ? 9鿋38? 9hh ?q(?38?q(?W|DWޢ?E_Ȓ|T?Aw?sx=h?`Կ#Q撴?`Կ"WW7J?$Q撴?W7J?v2q˿%%u?0u#ǷoR?#+?ʋI7? ԿU{#? Կ?/o æ֥B~?U{#?եB~?v <п>{Σ?@ը?㑷?LED^?ͼ4ȘĶ1qhVP ?`mN(S?LŰ6N(S?ɽ?ZA5?LŰ6ZA5?'ch?bN[-[7s5nr?}ؿ#;x8?c.#;x8?m_?<1D?c.;1D?HFh3?B@ryHؐZ]?ā܄Ȍ/t?R4ӿȌ/t?q@ ~?:>R4ӿ:> [cܿ^|ͩ8?iK +RG /fY/B%?E-ȯz?e-#ҿȯz?>I¶?z?<ܪe-#ҿz?<ܪEKrЧD?xߥt0L7JtK?sy?Ǟ|k~k?D?J;?D?acJ?8ң%J;?8ң%俻߿4-p̡?I Sqt}70KOefŎ=?&< ?S<7&< ?dC䲪?Z{jĿS<7Z{jĿ3C9Ρ?+=4$q]I2$Aj(? "?R "?_ .?S8ÿR S8ÿL6F=Fk?v5?&Cˬ?~nH ߍ?c?1V |YM?zQ,h7\E?_\ɕ7\E?Y U+?9X_\ɕ9XPa'`h?c\??yp A`f?uzCH?MRҿCH? VX?.࿅MRҿ.࿖zۢ?U?M^[<y65?z2⿶-Q7#?-Q{?E-<7#?E-<9ȿ&բ?\_c?JM"kq P? gAb01י&?Ab01יmVf ? 7&? 7F̿x /?E Y n{TO?L3kK!J{yo ?z j0?%@ҿqJވ?%@ҿ9Bx?z pJވ?y r'sӔUơ?ֶ;H鈿n#q??㌽Bm?0ȿ #G?0ȿd$ׁ?, 뿎 #G?, qգrr"?n*3('ەe'Y?b5cG?b??+7?x b?y d<百?i 3%?72@&3_?ӫ#Zy- o?<~?y- o?Z68?}m+<~?}m+NʛH?WA:֠?yGO޳?j?+QT?=[3? 6p,j[7?;c?6 ?hPC!?6 ?]?um?hPC!?um?u,QCP?_e?Vj.h?pht?6s?g5t?6s? >x9?g5t?>x9?T~`¿8p >?s?/Ȩi"v?-fA?)#s7R?f)#s7R?ygƘ ?;X`xg?f;X`xg?w$3Mz?BK?ʙ?Tohj.᣼]?2?'׃C?<zu'׃C?RQ?vՑ}?<zuvՑ}?:.s?.HHͣ?TM?&b?KGf5&?.w2?.Ϧc˦?kC-?G*Z`,mo?? `,mo?`"Msp?? 𿝕p?ywҸI?OEA㑕?c ?6&,?IL6&,? ^='O0p?IL0p?8(| /1ˡ?{2?Fx?pSSӓ?%?=̛L?4hڿ=̛L?[Ѐ?LX?4hڿLX?`iEա?qزБ?q*zow?;]?Qcq-LV2|?:KP[ٿMV2|?=?86?9KP[ٿ86?O3ȿ>2pN?Ku0=?N &,΢?.ر'Y?(Z2S5?Ҧ/`?te׿$.y?te׿GnJK/|?$.y?KK/|? Y?4`f?PJ-K ?8֙?c* V'?|YQ׿ /B?|YQ׿1Sbk[[Y? /B?k[[Y?1'lg}uy[e?ezD?jAoo?{sQQ~Î ?~Pzjƿ~g Ta?:툿Ozjƿ:툿y ɿWTg?ITٓ?E?p?(Nq@'}\?,<|CĿ,<|?ި?sjDقCĿsjDق~ƥYϿ)?H\?ͣ??O E?;ʋ?Ibzs\>(0?s\9Xm鵺j?>(0?m鵺j?! g?⯡?e=?>ߦ?<}H[ݒ^*Fv?G[ݒws҅=#!?^*Fv?=#!?uzm؏?:f^? `e`W^ow?(5Jf9?5\RJ8?#>ZJf9?#>ZA!Ͽ?>?! bG5Ia񃾿7c۷?TG?Z;7c۷?Z;NNϭѿ9?3w ǿ} 沿sx?@Z4%?\ XOx6Ej?8G{?!МԴտ?Zܩe=9^?"МԴտe=9^? !z&?K+tG}A_?X˦?y>"𿠧­s? an׿­s?La?m m? an׿m m?{ɔxzgCf? xB?-yx[G?^?oJ:?Zpt8ƿoJ:?.;_?d?Ypt8ƿd?NwG!Ϳil?.(]j?{U}?f"v?(o͊?)ƿ(o͊?kW?2`?)ƿ3`?b!Cп͟?^R?sɜ?Thg?H =?گ[/W)?#ߝs? @=)J`0ҿ=;y?41㿊)J`0ҿ41tR9i?9<,?.@Q|!?ӛ?My@Ҕw'~HR?Ӕw'.P?\h?ob~~HR?[h?obpf?!V;? ~sr?&b?klY?VJj?ԀseƿVԀseƿA+?А}?VА}?n?>?WFvn?&>参?qX?d1s?gſ cqgſz ?}hc?!cq}hc?F֨?4?"- ?J'O)\ I6?6N+?=//?9s}W?FZC9zN\ʿ9z6 (߿|"ۿN\ʿ|"ۿ ˿) E?Bn?lWA{z\_? *a俣ĤpҁٿĤ濪q 0ȿɦοpҁٿɦο>)" ٿ{y?fH?B=]s?8Ϗ?mUMÿ_!-ֿª{¿_!-ֿ@MŸ?Dh9Qª{¿Dh9QNB_;>?ri!?@Щ(Ar?:O~ގ?fܼǿ  ⩯տ>(Ŀ  ⩯տTc5?TI<>(ĿTI<ɣB?@m0@)?*{^I^?7ٴ)x ?f?WFAL؃w_ٿm?كw_ٿӜ;+%z"ֿm?&z"ֿpS9 $ ɣ?QY栿0A뤿!s?_'M￰XCҿ؀,?XCҿ(kJ<ÿـ,?kJ<ÿ$k)s?qYh끿\"Z.rf,~?|ÿ ~pbס ~+O;Bpbס;BU! fտX=1?d邿!=Krx?tOFUƿ*5rv|`8w[()5rv|`i` oT;ս8w[(T;սO|*6C9׿>c3?p,пX5?6ϤkJ23?_C$zc?K'fӿi=!?vcӿi=!?r,?JZȓ?vcӿJZȓ?b7\пNY?x?yF8ȿg5 g?ƥ+?a,?^𵣱X4=? Xcuf0K?5 r?e0K?Ә˩?5 r?˩?>K?GP? ?y?эֿ#"dÿэֿάݿIZoź""dÿJZoź\hb_)Dq?&~| ?ɒѿ!B%ukƿ!BF% ȿl43"%ukƿl43"#:&~q??MBmwg%y=}?σ6п ŋỿ yFÿ ŋỿpb7ƿH;" yFÿH;"j8ZŽ?f`I?{ ML܉!t]uѿS@M?)3?94ݙ&y)>x @X X?7[Ga?X X?| S~Ϳ6[Ga?S~Ϳ.X??{LР?+r3rI, ؂@oݷ?#2ZVHoݷ?`|邖o,y; ?^V?X ^V?Vǿgpes?X gpes?3?/[ ?腈t ԣ?M~P%ab2 ?'r? 'r?w;Ŀ:p? :p?籮c?A ?tNh?f 'B!GT]?Oť?Q?mƦlӅpMEp @4cڿMƦ @4cڿ6/Kt?MƦ Kt?,VSn$?ٱ?' ?qvO.Ohƿ&8y.Ohƿ7F,DV?&8yDV?n Z'G^$ģ?DArcc?_nZߠPd).?tȿ.?ɺտH;?tȿH;?dZڿIx;ֻ?LOf? 7'}r3ȞbxR:&"a?ʿR:&"a?ydƇPҿ`?ʿ`? |ۿ3Ĺt:?j[b?mÿ0y~?4䥿RO lD?1Uv؄?P%.?jluP%.?XSd?>&>~Au@jlu>&>~Au@e-i 8"R?turn)fYk? ($|J?X'̖y?MfEX'̖y?Zy~ ¿/?MfE翇/?{ {gБ?JhFp'BT?nLҿsTe? =ƿsTe?R-տ0? =ƿ0?8mFM?`7FTsqc?.M*<@,*w|#п**?TQ(fƿ**?н2ѿT I?TQ(fƿT I?_ _iNq2{?pP|y{@?ئ?Q-?( @8?2.,~X=%*?L?yrtҿ,X0C?Tqb+X0C?\3?B_TqbB_j˓?mqRk?7þg*?L]?)9ƂuԿZziU? ZziU?ؖy(?g'j? g'j?r?JtjcΏ?Rr'BC7@bF?||B?.|bܴN/Ak4|?PcO׿N/Ak4|?@&??!?PcO׿?!?SrMb&?nڠM`?ZMK?H35K?eD֪׿5K?B;җ?P ^?eD֪׿P ^?ePш !?X8d賿+A&?U5(x?$?T#A?!2vo+1,ҁ𿪽o+@<=?1,ҁ⿊͐@ 3V Ƣ?@Jb)?QG.?%w?||0xҿ|Ηތ׿$Nu>ҿ oFw $ ?}Ηތ׿Fw $ ?ؽ%?Z 2?C3 6eps?RC_?\{4/eD4ҿP,}ƽֿ4/eD4ҿ['cܳ#?Q,}ƽֿcܳ#?e!?fڡ?5~a Ŀ? Ҟ@7 s&y`T?<؄4?с? *?'`TZ஠?'`TK7?mпZ஠?mпQ'@@~.?_ײ҆?WL;Iǃ1Az3?1Lw:荿pZ?%L{Iտ:荿%L{IտNd?lz̢?G萿n&?}?G־4ܿ>/ك ҿ4ܿND?ub>/ك ҿubn49?_*:ˢ?L#QQVICG?)(m?(G`DƇۿ}J+{ҿDƇۿ}#R? QӾ}J+{ҿQӾU9?JY?Q'8)L4T? L䘇?]prn+ R忯]r)?{̿࿯]r)?ɯ9?Vn.{̿Vn.^+8v,?<{Ԧ ,Ju0KȸݙZٿF`?9>٨̿F`??_,59>٨̿`,5࿘4ԣBS? })rDx)JG ?nf}jĿg^[%ƿnf}jĿ2T{?5ѿh^[%ƿ5ѿh<|luw%?Gv,Z^j'lz)&p{?޶¿Ìſ޶¿IH?KZпÌſKZп{toT4G?$C&?SNMp'*?ґK5.?ܛkVz?tqwK1?Eߡ?d?.bwп|Z?.bwп_ǣ㒑|Z?㒑f0?m8?_d?}P4ˤVIa??w N?aN`?>w N?|#?dl0aN`?dl0(3?PU?`{[Ř?Ijqüygt˙?Fm?Tz?Fm? )>6g ܿTz?g ܿm!%S?,U?W4h? ߕq$ٶxG,2P?vg?BRƢ?vg? H?:vj ۿARƢ?vj ۿSmF'?aVi?h%8X$#nwB!'?j0>j?Ejߍv?ȣ?k;$?WV?-$n9ɿVV?6"2`V.-$n9ɿ#2`V.'RWPݿCje-t?"sW_? p?E|4?դ2 y4?Cl`?2Kc֤2 y2Kc(?Ij˿BhiB?K7hhfޏ7?7Q:;ǎZؿk1}??(K"syk1}??S]ƿufŸ(K"syufŸtleqCGZV?wy j䠜a?*Ԝ&M⸭ٿ7Q?'d~7Q?M#~iſT#o鷿'd~T#o鷿tZ"@?jE#0? ?iSP??f̡?fê?B w4k?4 U?|L?_o ?]NQ?^o ?!p@5/]NQ?5/ ?ޤ?#,s?˦|& ???0⣁,?0⣁Ѫ?Il,-?Il,K͘Q} ?Y?| 5?~F勿Nsr?d9~SNׄ?SIΛ'@)K.ÿNׄ?)K.ÿꅶKs4No ?ٸB<=? ʚ0!r?|,ړ?]&-?]& Ú<٢_ÿ-?<٢_ÿ^ͰK?TC!Y!O AVX&3P?ԗ:8L?ǾO]􇯿ܻX5? 7,xG4< 迆xG5Dgm%迳4< gm%iMG?"?zd7 N.F?a6ɘp= qRݿʉۿp= qRݿ'J@2п0%eʉۿ0%ePT&'??j7W]x9?RG ̿OMHL? ҉OMHL?MRj?ڤA,Ŀ ҉ۤA,Ŀf(˻?&R? F@SOg֊^$#?(IʿQ?}ʻQ?wE@?{M;G¿}ʻzM;G¿Bf^g?'{Zh?*;?@?He??2u?ψ@N@C:¿?N@C:¿3N3{-?rFqo??rFqo?=^vr?A5L/@Ϥ?ȣ=@Ϥ?֛,?wHø?ȣwHø?" ?Ye?b!y?;{UP#n=u?9]읕? 49]읕?!?r|M? 4r|M?zfI!?B 3?+f?@rq < &??Xk1?Xz2FEܵпp0翻p0&?Cz_ʡΏz_ʡῲvۧ? pF?L*?I |gLۓL-ǿYb;ϿXb39I13e_ᵿ;Ͽ13e_ᵿ1gKH?؅z"?Ш?LSl&?M.1}?Dˣ&ۆe7]?gϿ,ژ?gϿВO卿p&,x?,ژ?p&,x?_i] ?.2?ʝ,?ȑƿi ! ?U۬?y"D8p@,:ܿ 3F|8G;? 3F{Ey_e|8G;?eJx? jշ?3#fJ?@ Et8/OF޿_f8\l&?_f8\l῏o"￶5?&?5?A&?`Е*?W|i&N?(MhxӶҿFI$oξ?FI$o5Aп 5?ξ?5?rF]P9E'?c)?D'~힉.nҿ@j}n)Au?Aj}n;uXп-g??(Au?-g??8s,^n-?ĿNj. =?y)K ?"?[yWo?!ح;ֈ?d8?;ֈ?|bT kC@d8? kC@<kEVG’??2qڳ8&?"i5_׿l?*t̿p?>t̿:C1?xk?SA~ˡN}B r? 4S f?Pច.m/Df׿.m/OKſ74Df׿74j,PzZ.%?G-ؠRXi Bs?"ukd?h@둙ǮʹB׿ǮlXȿukʹB׿uk͚%?d΄Uq#&۞JL>++㏋4?,ID c7|a$:œ?(i!o)i"fy/9?5)~󼿀!o4)~zx Q 43Z?_Lh:X]UjW?tˤe+tM?gK?kU0*s0pi'#DdѴjJ*`ҿcѴN=P?jJ*`ҿ=P?*<-?m͹E?)wT?УZXg翳Zpn(?<?Zpn(?)ˎ]c?<?\c?O| tF?9 H??Ou?Ҡ]4"rIrF/^ڿ%g? V?%g?B.ؿx,d?JΠ?;<ՅJ陿pj'?%a?9d?˞׿9d?CUQ?8b?˞׿8b?n>?wj?y<`qz熁17{?4Xo٫{24?ڷ h٫{24??n줘ٷ hn줘g_? \p?%4'q O*NU|?+]Lmxo?uxo?28{?2 $u2 $2"?ʩt?Q1BGt?\C_6?WT`5s?T'#<+g׿G"U=?G"# ?|rB?U=?{rB??:uݠ?vv?Zݔ ?? ڌƄ<ÿֶEI޿#3?ֶEI޿u?6 ?#3?6 ?怾G?"x:f?Q'Hy?)Ý?j/DeG`F?g`%%Gg`О?|Jҡ?%%G|Jҡ?@g?[G9Wl?z?GYD?\?տ@~&?fiFg?]w>D?eiFg?AR? n?%G?fp㐧mt)XC6 [?,,?]McqlԘp?s?*uu?q )X@*uu?bP*˗?88*?q )X@78*?C%>(@ ;j?!pc֦? й*Ȫۉ ?j?pmE"3?pmE"Qئ?B"x?3?B"x?/Ad?K-?6-!?Hj0m5?Yg?OI˿1?OI˿Ϲ?˪:pК?1?˪:pК? w?G/?1?-c!4L%? X%?V8z]ɿQUʃ?V8z]ɿJk: a[?},?PUʃ?},??Z~Ԋ?``sY'?0 J?c̙?\y᣿S{?e~ӆn?*l?n?0/>?Rtz?*l?Rtz?(,z׿:?,=;\?eca@X}G?uzn}d-@4P?\5w?d-@4P?2?xJpA?[5w?xJpA?R Z?x?9R1W?7q>C>rN5?e˚¿`t} ZlZ?`t} w`Ui?FkT?ZlZ?FkT?s?J-9?ZN?A݋a3Kɡ? M͗;X3?Η;D*G?gs=?X3?gs=?Acfc?S[?k6?%\Di?&#?%$(?KS뢿.}?{ȿYe?-mZYe? {#޿* ׯ}ؿ-mZ) ׯ}ؿ"mXn?ǿy/hyvwm??+qhu.;xSQ?܇v93{>+?Lw,3{>+?1 ?(n?Lw,(n?|q? 01]ON`窰JSǔ{?gῺ#9K?`ӿ#9K?"JȿB^?`ӿB^?O3߹M6?3B=pf0ѳ?xJM]3?v9k#ʿ]3?JMɿaUgGv9k#ʿbUgG'̔ۿ1j?lկ9~$z֚Wq?M&8)v6-?=\aɿ8)v6-?to˿N=\aɿN/eۿ>V0ס?'p? λQ3G?hhqxe?29u4&Z0\E9??G?< Eb?7!ܲԿG6!ܲԿ)_ P?M1)ۢ?lV78yKX*o?ț"j?d :eț"j?v>?iD<Ic :e濹iD<IC~ L ?gPuR1j1BpVpw??KsZ׿?_Yɱ?Ѐ@۝KsZ׿Ѐ@۝w+ʿ,I?,JӨZKn(_ ? tM?GЭֿ tM?k$_=?)A49HЭֿ(A49𣿀(˿h-?T"d%6LM`?湜( b?q<ޡΐ4]"?!"屿q:ԱYt?Q^?ԱYt?Qݚ?{%%U;?Q^?{%%U;?}:?͇_f?va-?d өAzBilk4-?榿lk4-?Kw4?,4?榿,4?ʲJ? tw?bns#u e? =w<:?zr?eqD!ҿzr?v?bCbl?eqD!ҿbCbl?BŽ>68z?[E^ 錿m"?*xF/?JJz?vdݿпIJz?-}?/~S?vdݿп/~S?^›C-?\3ip?e_r)p?n)˹$O?>p7?4犞P?CBx!`\&OǿHG>j[X?HG>b?s: j[X?s: o[?f߉R?H{?YK[FdA?Ǜld߷ U?N8?Wz?t:fΘ?N8?t:fΘ?g/?{ ?E7tV?jDi? 񜿟e0?̭T޿($(?̭T޿uc.rw*?J?($(?J?BcV? ?F`[?TWf?HA6-Y?P2M޿\[|?P2M޿)*?pQL?\[|?pQL?JГBH?k1.?مܧҳszɿLQ9y?b_P?@ Hp?BE-? a_?H2H+ҿ a_?;-x@:%`ʳH2H+ҿ:%`ʳ} ?z+}?B ?JdG& q?/h}? v?{.? v?o"?\Rl?{.?\Rl?Ef^?PtS Z?R앁?l^bzJ?]sSc}y?q?Sc}y?,4?jyz(?q?jyz(?"LJ?jMIYW?y,?qMؔ[T?%[q\!:m-?!穈C?!:m-?Qf?\CE?!穈C?\CE?=,?c>?t}?Iq6?4X[t?n^iC[ ?dB6IVb?Q {?]?Q {?6h?C^>迖]?C^>?G=?Kv?{t`:I$ ?U#k? Ax?U#k?DP*i?-Rj޿ Ax?-Rj޿ԄmZ?hRb? l;A?XJʒfz2?Ot;?L?Ot;?Q~M懻?o4%bL?o4%b=X?c?4HF? =o?R{:E?:1?S{:E?6=J?1:1?1ER?aJp?9`;nm?Ptq'Io?zוj{?=zr5#Ħdݿz- }?z!-޻?±L- }?±LyJh@`TƠ?lkK}ɳ?7f?ӒIةӎ Bd蝇?ӎ `?UyT{Bd蝇?UyT{bZ2?dϠr?ceP?`̈́%{?O?K}??] ;3?)?] ;3R`TЍp|?)?Ѝp| ?VȆt?KQ:?{?hH9{?#,?O|XlX5?N|Xl\')?8r摿X5?8r摿P'FQ? ,G?a5BV?nU]3eС?8h|S? +W?G2|p/X.kVЈ?#jVЈ?[?(͝ȿ#(͝ȿ/@_ڡ?$Bc?YQI?~NX2FY""нV?zuo"нV?jژ0^?2[kzuo2[k5?Diס?a|?=q?(pWd?Ԥ:fcdrݷ?K CĿecdrݷ?"e&!^:?K CĿ&!^:?eD?20)ס?l ? v ?9ua?b3ĿP7^?^f̪¿Q7^?-j;?^f̪¿;?Ѝ?W ?\?R9 ?9tȿm?mȢvj# ?>%|H?(t!|? ?ѿ(t!|?D*X} ?ѿX}鿞"J @F3?$7%3L2E?}&MQ&B?1~I?V;c1~I?ptc?,޿U;c,޿?f?jveo  b?9hiФH ,%x9CTa?x90ip)?LJCTa?LJ隃%8?З[P?[cQ&q?3hPm4`_>٤ |J?`_>٤'7?Ae/- |J?Ae/-ZzoG?B?IMY}͕"?M3c[f`5r?(/;(t0Xeѻ?5?p?ٌ˟&N@ٌ˟~!Ȝ?6S㽿&N@6S㽿;4j?u&͠?EydZf?Ņ ? gP~-?]Au?>%ۿ@>%ۿ޸[?i+¿@i+¿C?P:ܡ?f=X?_~?U،?&ld?+ʿ 8?+ʿJ4?r6 8?r69eZW?0,?溬a?#}?_?LҒw?@2o^ɿg?@2o^ɿxMg? g? <?ov0BRQ5W?۴$Bb?lH_3?۴$Bb?iG?x'K'eǿmH_3?x'K'eǿsUޖѿ0ӯF? liU?yk>u0ڎKu`?[5e?i;h?d/U̶*|ԿLs0{YK!MsF?zsg0{YK!zsgbcS?1CY? <"1U?Lz?S)AݠʿC^ޕ?[S߿C^ޕ?` ?ʹ\{[S߿ʹ\{wc$i?7?@ 聒?vb9s4Y.e?Gj-ѷ?/?Ix/??53οIx53οcf ˿`?e}j?4q^!+y7]?I1???5Ԥ?pIj?57 @Ϳ?5Ԥ57 @Ϳ08(˿ ~?&?6:U…^5N6@)sZK?e/r?=3?v!2Aw?RwL?҈K?RwL?ǁ8?cǿ҈K?cǿӇB?( h?$:}?y2˜?A &4I?@ ܿ凼 ?7hR*b凼 ? C4TL?H+kt_jz?11O0V\?[\wg=0m$|$+?'FƦ?e0x l?'FƦ?+蹒^,Z?e0x l?_,Z? mi@hx?j>hj?F?t}:? ! ?t}:?OG&.¨m4? ! ?¨m4?+?dim?ps$Ž? n?H#?֛9p?f^'?֛9p?,d[;ϿsE[?f^'?sE[?b?ډ?y[]??Km?09Bvl?)؉e?*fu?)؉e?,>.Ϳ,1?)fu?,1?on4?c⋴L?Bh9f`֥4<87ʏ?hi?N?Ƣ1h?\+?@.ս h? N?Gy8Eϙ῜Gy86U?+[?Fϙ+[??MC?V ``?h}? @۠?I7?%.|ڏ?)Ϣi-<ӿ)Ϣi-U?"2?;ӿ"2?bA?`*>?屧U?kINΈ?V}?9|[?M%W'ZȣM%W'DR?`2?Zȣ`2?r?156F?m_T?ꦈ?%؈?z59?ߒkb_<\ kb_JݘM?u%S+?<\ u%S+??u~!?vG4ZkNs8I3?M:GAJ(x4Y^ y"I8 ~yArGfMgB]?"I8 ~gB]?X~G޿Y9T?DGÃ2VإpevտR|G`! +OݿR|G`"&z ;_? +Oݿ ;_?3ؿ?~ۜX@WrD;ϕb ǿN0#ÿxUN0#ÿ#eDӽ-FX0xUӽ-FX0tȌ?iI$?iwfJq]m^;ߠl¿SiCſA=TiCſ2G5žQ6EĿA=Q6EĿջFr?[n?t5/R¿˿#zx?-릿'ID>!+vNۿoz]% ﯿ>#?oz]% ﯿ|U?S>#?S+ ,@w?uܘN$ qxo԰QJ_t,? )R (? )N2?ezR (?ez˜?k'?a~4/`]Zsی8M0?Դef|l?ԴlBr4?4Nѿef|l?4Nѿnă ?93?xFUdzz1p@aJ? -Ԫ -=? -Ԫ_/S??4׿ҿ -=?4׿ҿZ?@?}x?:z=9LAPTY{? TǢ? B??\&_ ?3PۿdHj?@ N?dHj? d^r?@ N?^r?\= PV$No?jE? xI ?OnQ> =?)h? =?MX`$c[g?)h?$c[g?c.9+#?'?:UꈿP-bQt?`:tG?{?]b?{?<|ʿ*jcf?]b?)jcf??vAЗ?W?'n?rF`0ۢ?~??\p?T6?⿥n?'4~N?n?6ۿV?'4~N?V?Mjh5XJآ?L7?Dvm?|*$~?aPK㿣N`?RD0KϠ?N`?݋*ۿI??RD0KϠ?G??*3eZj[?]zпzn(ڜ?y)0?0h?P"wh" T)+?U%_)?mAwοm򿪈 tC[U AwοC[U Z%Kgٿњ0?ҰTJJuniGzüؓ?%v?&߄R#ޗXF遗p?"c&~?԰?ͿjB෿ʉϬjB෿ܡ KпFU @?ʉϬFU @?>m\?Ph)J3,p凡?mP^*W/y 702?`?IH~r翶`?86.?5LW(IH~r5LW(/]S%k]? x mrᕿ H:G? ?\ # ?z5?P2=D߿\ #P2=D߿i_skPҡ???".C/2:f0b?Q7d-֧5ĿQ7dz?egQ,֧5ĿegQ:.фX_fVt֡?9a<i XE7Xmٴ+S$wxC?r4,/+gÿq4|As?]7%%,/+gÿ]7%%9a6 J? \(٭<"7y~?{LGY½;B\xLῠXgY࿂꿠XgY-쿂꿌z>u^?zU?fL;.|9KԔ[ O!.)¿O 4%8H8O Z^3c׿4%8H8d׿+̸F?H}k?I!yt@Asg?Q0bͿOfſQ0bͿ"f:,rϗOfſrϗ)cd?qR̺l?2UR{vjlӎv,?qO̿l ĿqO̿% ('¿eS릿l ĿfS릿nx?m? ?$N%9Ͽ ?O_ P[ ~}¿Ź%?!kS6߿r}ɿ"kS6߿sbe{Z￑r}ɿe{ZF_r@O",x&?BLr?<$_Hw' ?Iʮ޿y⶿Iʮ޿d AIxly⶿xlixE@?Fk 4?#Rj?I0Qvu镖?<26/Ͽ;<26/ϿȷPCĿz&Ϳ<{&ͿY+?eE:8?.*@?2?;y,wͿX{)?;y,wͿxɿׄ˿X{)?ׄ˿@4Y?rgZĢ?"cJL?.p]ȿ%r '?5 $?g~W%ﺿo򉐿j}?z?j}?̮j]^Jz?^JANKN?sɱ#?a\p?!"}uQ-⍲-=?Y?-=?#E9R+Y X?R+Y B|)=?VIv!?T\}?g?Nv )e[G'&?G' uѿ\gſ'&?\gſc?^ji?g/m?n' )u" fg;ǻû˹С,?û˹<ᬮ3ԿĿС,?ĿXFs?6*?|.E[?4_ b9 x?j?a^/%ωӰb?nᪿI:T?ᪿ]ޘDI:T?Eq?+r?ԱJ1@B;[Z8l?GH{OT?(0q?OT?DZ\0_ҿ(0q?0_ҿFVxH9?QtLOrms~>*F?[п}D^́?#?}D^́?п4Pk#?4Pk @?[V ?F_Ww67N?8'zA?'ѿZcJ?DL?ZcJ?:Aw}ѿ+53ҕCL?*53ҕ I܁?;E?ࡂI0(?VTbᮠ?NEd?"Å?v2Ja?[HED @J7օ?7T?J7օ?s'N͞?a-@^ѿ7T?a-@^ѿʯMܦ@ ?^'dJހ?+Hf?)?CvO?ܲ᝔?'iK?۲᝔?~=?"i'iK?"iL*8@K͢?E9B]fۢ)&?ꁘ ҹ?5?yM`5L?5?K4]ޠ|3ީ?xM`5L?|3ީ?oK?lc̢?%}lB?~ =g҄?UE{?v7?7cٗ3?u7? }`TX?7cٗ3?TX?^-? |?Nl^%#u4s?4(?ѓ;΍Pe`eܺ#B?$' C_/ ȿ"h?C_/ ȿcf? _ۿ#h? _ۿ;v8A?!Wᖢ?:-k܆F+?lUp?2[TԿӃ?TԿq?zS ƿӃ?zS ƿj??V>Ȼrb?d:qC?i 2Ϳ4}3L}t74}3@KHK*l2\o?L}t72\o?؆r?c3?C?-ƻ┆?{@ ο,7VT%L#,7VT+zM?jt?%L#jt?ۅ?PgFk\;??5nKxs?e!)zD?+ L8?j1eFy?f?R]g?H/T?3ͫ=H/T?.eu"?`j?3ͫ=`j?kJ;ܳ?V`?UN!?=mh}c]x?RFE ?翐liοRFE ?` %?l4{?翐liοl4{?-[c?^ ??+[GRhj?x-󪿞8泣?ȵ?? ??͔Ԩ?ȵ?͔Ԩ?>,%g(th?hVr"@A?/:nڏ<{^M?p?^M?|SIa?rHû?p?rHû?IC?I=E? cZU\Lk?֨(ZSO?r?EnA?r?mw? u૿EnA? u૿=?Ǖ9??ΆD3?}# {I%#%?đJFn?đ1!N/$?[[}JFn?[[}i;?m#؉u?? B?E}?&?ƫ_?#&i:k?©ۣ? 콦˃l(6I?˃l(\%@&W?6I?&W?TU?rӠ?Phg? {7pԎ? xc?t?Z.8s Ij?[.8s"KU?8z`? Ij?8z`? PO3?5Es ?wX1Es ?}8 {?T)6ԿxX1T)6Կ$? ?LQd#?C}䝡u&BB?0ST_0Do ?l=?0Do ?'z ? Yf>?l=? Yf>?BѾlܶ?Ac?K?:_\_;ܿz?#ݿ֩^i߾ޯ?֩^ ?q/1{?i߾ޯ?q/1{? Nqߩ)!܍?Fs?Rdӎфč*?y߿CQk[?CQMD0?E?k[?E?ѹ;?2Yp?8?Wp ?A;?%- z-?B ?>@f?&CT?=б˿&CT?,7~ =б˿7~ =\EoТ?x+s hY1?iڕg?7^EE?7 7%?=[YԿ7 7%?%ƿ{ſ=[YԿ{ſLVe?qQ8Ʉ*ߖh1A?< vƿgy\?F1W:Ŀfy\?":eQV W?F1W:ĿV W?+2TȿXj?`mR`6!1(?`x+5pƿm)?8WF¿m)?HC#Z?jDZ.?8WF¿iDZ.?;2rǿ\Y=>x??R}?Bv ?麮(|?yd[?p`l?0 `(ѿݎ ߿GDȿݎ ߿y5Du9rU?GDȿDu9rU?׽8" ?!|p?au?i=? ɒr?9ѫ5@1&ǿ9ѫ3.,5?5@1&ǿ3.,5?&my)-?}|lnՈ?Dlyc?\8?kh̨1?Ŀlh̨1?b<?Ŀ b<?nu$y׿{i W,?dC倿F4?/i2?{sd?I ?v,ÿI ?!`s*;?v,ÿ*;?N^f׿o´=?4??sYGi '?(at?-?`!GM?/v]ni?/vq@?6ڌ]ni?6ڌE2z@$_?w?s ??,9Qڏ(f?`LrڿDlQ:?`Lrڿ2'ʿ5CDlQ:?5CGT? |p? ?Wm}L%M({MLR+?B.TR+?+mVL?_ѿB.T_ѿhS4t?6qHK?áM*C?ͿJ?L1gM@pxⱾPh)j|pxⱾPdц?qпh)j|qпb5?]џ?_2XL 9t?̠݈O`?Yd.M?/u?ȟ^6k;E0Te$"?0TF|#Կ?LV?e$"??LV?"Rsϑ?PsTb?7qs=?\&5q )f|ÖvR[:eWW?[:e򿹔ҿnWW?njgÿQe?׋޼b;.7wc^|ocKAr9п^q7bE_q7bwC?jjпEijпjے=z? ?mpf?"+F~fon=,пqN&7EqNr2Ո?%%Ͽ&7E%%Ͽ:X y~?4q42Ҫſ@v? d?-3ف8D1PAϓbKD^?v?v5zݿv?q^?Pץ\пw5zݿPץ\п-{{c?Gm);:pEt@S?Xt7Ի?fH0пXt7Ի?l?;,ZпfH0п;,Zп1wtU72? ![񆿯Iwү?!V+?7LTE!V+?w$ʣ?#!ӿ7LTE#!ӿƿ)8?Xܰ@˃y.MŁcɬ?b0Xθ?PFb0Xθ?tl!?"S4`?ѿPF"S4`?ѿ͵&'ȿ$ o?G~?ZOPe"a??h?Tn1;Mu??s/۴??(HEZ?t/۴?Z?h9M.ӡ?!}?>R0WUrI?KUK?l3 ?KUK?L\Rȿ:|Ga'ſk3 ?:|Ga'ſ hF˪v?vh?upl:\IM@ B?0M|Q?O0M|Q?|-?-YbuοO-YbuοNC/@˿ ?t?;uϬl?nwZe7_p;s?_E|/?q]`E|/?jO?ZN̿q]ZN̿R9ο2nw?Z_M鿁"L ?.ſE?8~oj?+3˃h̢)?U&<OZ#*a?U&<O*??|=?Z#*a?{=?S|}oȇ?iT|?0T3k?1sꂿIwr2K?Q[+<?Q.?z?[+<?z?0l@?I?؏|?ŝp{ Q1?u?ǹhAQtxSف?hAQt\~ $? ŋ}?xSف? ŋ}?=T_ut?{?ߒ1j? Ur=3q?v\ڍ/}E<Ŀ>ٍ?/}E<Ŀ)?Jl%C?>ٍ?Jl%C?H{\#?&Fڣ?qo22&Q?QDSw?' j+U3lMٸ9??2Qᅮz?GL.z?{CP%|N?GL.|N?]=<_Yt.?t=&ZDxԾڃ꿾"Y>?'$ 俿"Y>?{\ ܿY?'$ 俌Y?_Z8ֿf舣?H"ڮ}9|:p^?(8O?є0l?(8O?|?B?є0l?B? Uh?s>H2?~5!<%9%D Z?;3?΢g4?;3? a?d+?΢g4?d+?H_?jdϞ? VW?~$=?(z=\T&!/,?Ry T:VA?m>@7x?E}ۿ6x?mUs? z#E}ۿ z#!kS?f\? [jCzW?W_z顿Lla@q61?TϿq61?&O_?sR׿TϿsR׿ =?"ʒ?MHYN j77ԣG<$N(?zx^?VQ)?zx^?Tb?$V7?VQ)?$V7?$i8?k_?B߾? {^u?MţĿ5?_aē?nSa@?0p:i ?^?i ?C%ˁoq,?_?q,?]M5 ̳4?rmb? R |iTȣ?kCÿd\5/?<?d\5/?Fy Bd;/?<?d;/?maEADU?m4ӁV?дJ0L?=? x? Y? x?TϻSs7 D? Y?7 D? ĿN OJ?+'z?f_7JX7?%8#ö?j@,?ь_ ?j@,?n0U(Ԙ?Ќ_ ?T(Ԙ?z>dYĿ#PW??`?Rɴ?$?W=ۙ0!ޒ?pD?lOo?xP2#@a׽E?xP2#@겸!5?a׽E?5?|%??pLNH~?hz y?,y˷? &cϿQ 1eh?QJ?Q 1eh?̏_ʿ?v7D?PJ?@v7D?5οeF?9?Fƫ?1`-f?*ZKRƿ1/[r?/]#?1/[r?t5BЕ?/]#?BЕ?tGgC?KNY\??F7?tzyſO^?LN2?O^?Hqyֳ ?LN2? ?2ޓ}x?6,@fŰ?;V?)zz?NC\?XPH!so+Bx8?w%Td?lʧq?w%Td? ? @=}ȿkʧq?=}ȿ)mb?]@.? lI?RGԷQMr?FB)Ss?FB)SDJ9g?S6@s?S6@5my#ZW?W Rj? l) 2!-Xؑ ?z?-v?z?+$?NC{-v?NC{~E JY?"n?-8f z\Eܬ?I ~ا?Zx?I ~ا?aT?Q诿Yx?Q诿fsnIe?QPx?Mج?hr+t/?r/W8s?<?!/n&h1?өۄx˲ u0H?vƝV? u0H? =?vƝV?I54^G'w?"jz?8:…X(81׿eֲz?L.?eֲz?@^?G>?L.?G>?YEaҬ?ԥ ?MԊA?gq͊- +)$?;UW?+)$??KFGۯ?ᒿ7 5ay h9pt?ayE`a?;C? h9pt?;C?c죘Pÿʠ٠?1R:T`)2?r\?f\覡?P;Xԋ??Yϐ4W2@?t2"pT˚?PI +{߸?THU?u ñTHU??B϶?u ñB϶?|jNƿYA?)/U? ^?'̆e?I1 ?)(~FI1 ?o ?̾?)(~F̾?_ܧ/ſl4?1=H ġ?#=BϭƢ~4?d?@=|'p?ʎQWH4yx׿z:?5yx׿ɣ?l?z:?l??YTf?zbI?d0+ x??B#&*ٿ,{Ϳ5!?,{Ϳ9a?Us?5!?Us?ɼ?m䵢?mGʆ?M ?qitU" t{2Pǿ\C?{2Pǿs-bi?V\?\C?V\?@nVIB?f^?闡??o6'xsI{h#KTſdF K?"KTſY7``?*/?cF K?*/?l}|HLQ?NvE{,?W~$?qΕ ?d6z"?B".@?k![e|?R?THJ?+aWo?THJ? @Mn?+aWo?Mn?yj? ~?~GA?+j^-?{ִHX?x?*H?'?*H?w (?74oR?'?74oR?rѹM?4 ?h ,?qqVb?Yu8?/W?^A Wh?^A Wu?Fz?h?Fz?<`ٛ?xz?T?y^?y!\)^?# v?kUþӭ̆&d?kUþӭ]M?{Jg?̆&d?{Jg?\%wл?'gq? r#?k\R?, ?7h_? &̧?Lܫ0< ?o2?>0< ?Dnd[%??(I'kb6;?8}8%ӿz~?8%ӿ`&޴ҫ?z~?޴ҫ?]Z{տ3:ޓ?F}?lƣAnvT?Ш?Wk̿օvQs))g?օvQ 7kֿeCs))g?dC^oa?oZ탛?{ t?XMѤɿy ?y?y -KԿ#>y?#٢1q4Rۡ?p}X?fW@?ʳ5?ki ?@ғxܢ?Rˣ?AQ<ա?VtJ?YѿUtJ?e?rG?YѿrG?N470?]J?&l\?cƓZ)e?m2B?G)``?m2B?rk?)@A/?H)``?)@A/?|:}ݿzSl?Q?3̼?ќC )l`f^?-[n?-[R7X? ?n? ? rKǿ{: k?B_ ?IF>?ڶ ⴿ?2Ziq/p3"?2Zi{?!N?q/p3"?!N?f]ǿJMͣ?W/*? 1-K%?!菿ɣ?q&5.?mC?q_K-v,JփxQT?փxQI|#ƿmtݿU?mtݿE"a%+?*lT2?5?EYj?UBNX߳DÿMk?Dÿ >DYٿR8صMk?R8ص)j}Y?N[^G?a1:? F;fs?y jڵkS5̿plcW?kS5̿lPɿ?plcW??tʿ: ?4l[?0J?3lbp?Cb㸿g@K cNɿWַB?g@K cNɿOɿEj?WַB?Fj?Og/ڊ̿A?șU?n4֛zh)x? K6}?+\?DiNAw?'Ө⿅,eh]S{ݰ,ݿ,ehݰ,ݿ6bKjǿçndPF?"Q {?fNB? ^?|o?qܿ9Oe9O>L:# e濂:# 4SH⿯؎B@? Ums? Qo?Հ?RD\ȿ%2Ͽ ױf<%2Ͽ4|W+ܿ&G\c? ױf<&G\c?V%3ſIy=?pEr?:wÄ?\ޖCC}?Ccȿŷ~οgUzP[ŷ~οNpYܿs(P?gUzP[s(P?jխǿ$cT j?(aW

    Ra.Us2jB\aI/&qTǿ\aIB3ٿ9OA:?/&qTǿ9OA:?Byÿg?/?Dz{|`h@ ;vDgg763Lvݰſ63Wʬٿ}LF#k?Lvݰſ|LF#k?BM(ſ?jQ?;ι?V.'?4c;Us ?mD2;눿+w1? Г!&?ݚlR @?g ??ѫ$`?K X|?g ?K X|?j[B:QoW?dBb,hph?P琖?Z` ?Ihȹ?NIe?Hhȹ?>@WZN?5ſNIe?5ſʹUtuC鿩Df?Z.~^?"]>2?Nz=?O?9#7?O?Kߡ?^Dգ9#7?]Dգume\%?LڋR}PEc|?KN?[侢?L좥?w9?L좥?9f?Nߏŧw9?NߏŧIgN_&?xdTq֛<-+Lv!??@r?L+ؔm5z?O)I|꺿I|꺿Qa$ٿ$ٿhKa lF?}PУ>ӢrK'J嬛?bpXԿ16*¿bWǿ16*¿ R2F׿Y脭ӿbWǿY脭ӿTrXt?N*?iTX]?OC}?Д?h1_)UrS?h1_F9Cq?WlA)UrS?VlA@f!xÿ)󱆢?W$9:+eVT?`u6ۇ?ǪF?G!J/\ǭ?G!Jw:o?P_/\ǭ?P_ ſtphW? U?K;?s!T2b?787?}X?)?c?c ?K>?c ?. ?$}GK>?$}GLl?rޗ.?>h20c?!?4Ѷmw<}P?G?6p?G?i5$6L?֖)6p?֖)&&㛇?m"?&ׄ?[_?zڬo @i Q?|7% Q?+HZab?%|7%%A7KOޥ?M??iT?,4NLoDz?HB?EX'HB?:%b?yk;BɻEX'yk;BɻC?*`~ ؤ?L = ?SS|P??r΂?2?bGm{D?9O2Կ14a:X?140Ͽ֭a:X?֭YomJ?z9ˤ?Icx{/?01tٿ]0?翗U[Lݿ޿]0?޿LaV/iaȍ?`5/d*ށ?)|?`g곙Ͽ\K}ͯEt?\Kw=aԿ9ct}ͯEt?9ctcT/'ҿed?E%?_?·~PU:WͿt. Dh?t. 4ӿEDh?EgoEҿLFw ?eBeNЁ??.z?CX!굿-XSr?Fp??UU?{v KxgrkP{?')?^I7_n?^I7- ֿݤ'Nm_n?ݤ'Nm {Cڿuo8ڢ?l9ߢ +?Bx?:?TTft?\OE?k?ԑ$]?a;;Ͽ1 ?a;;Ͽ{5?on01 ?on0 YbZ0vb? Z9p?lsgN#q?lC?`ĿA_O_4?`Ŀ*f?3τ/7?uG(?g?Y(E튵?u1T?0HAV-?0HAqfvoͿTYV-?TYCJ[c]4?4Ⱦ[l?{X ϗ?W,?UOnCDQ?Us>JǿSNOnCDQ?SNҩ~$ﵿD&6=? _8?GRFuǝnJ?ke? M?" # ]ŭ?Y% h?06 㿺0Q#(?cHc6 㿴cHcΨ?Q],J? .G?OQz[pwOA?(n?syz S9̿\ֿsyz S9̿UD?|>4\ֿ|>4Cm?Cj{-k?[-jP?;?U$p?BQ:|KBԬٷ͙LBԬ.DcBtl|^ط͙l|^(=Opٮ?jjij?m=~?O Ac?+.y. uhFh+.XUӜy. uhFӜU?!NVy?XɶjN[À?it?|"@?q"am!`c$?Vyٿ]z޿6 ٿ]z޿EKQ.W?f^56 ٿf^5TS_2տl?G.k=?f4ã2ٝ?";¿!kӿn%տ!kӿLEAO4?sxLjn%տsxLjdLiܿg2?#Ov??^At?[C>򥿳2wN9if2wNgM:ſy2rOuo\?NF>ſvo\?O[ |pv?ۜ|H\??Z?Beq2a=ǿ R1>?2a=ǿ1*ɿ)vG? R1>?)vG? }kCC?Gb[?Cݘ?l7Z8EZ?$4$ȿoԽ?4$ȿpM3WѿoPα?oԽ?nPα?uLyϤ?beDݞD\x?Vr=G?\:"miڝ`t@?vt1 dkd⨿t1 l*C3K?ckd⨿C3K?PsJW6?m"փ1 `|?]Hȯ0EЎ?sé?#7)ٿ.:?#7)ٿ6 r?q7?.:?q7?X`??s=j*<_2s?Px?9!Rҿ==?9!Rҿ4 g#>?RO)k?==?RO)k?mP@?8?'9?iNbp7G?%uڑ&j1ĿUwؘ¿j1Ŀq?w3;hUwؘ¿w3;h3cs9?f C)mts?ΏヿR]ꤘĿ_i@JR]ꤘĿ?; _i@J; +)ѽ>?v?\{.#(&O*?CoX?j v觿fo<+4m9?X?282X?Ye|@+:?282+:?g@+?/ʍ? ?MؔzK-GW\_{{~?~ ?{ ~ ?1٥@|F?{ |F?#z'? I?&i*!|zC4]bt(0:?ur?v!`%Ϳur?L[,?mA2t!`%ͿmA2kecÿ6b?ۀiuLpayTڝvCp?c?~Ze̿c?`E`?O9`~Ze̿O9`DiĿv:#?E?1U43?L?P?Lc4 y#?F?ڊqg?y=^ҿ:f?=^ҿ{IxHe+9f?e+e 9(s⧣?+g'|΢?+達?7Wֿ{fgȿV?{fgȿh^󿗉XG ɿV?XG ɿzb F?>_ǿ_mˢ? p\PH8q"?'F/ˢ?wM&rLNX W?=;ˮ?G> ÿuNF> ÿ5˿lyYuNlyYhgFL{A^?!%?ז?g"guEǨ? l=ۨ?(SW?yy(ֿ͙B9ԿX{1uBL?͙B9Կ=u=tY{1uBL?=tܔX|?^m?Ţ?o>㈝?CՓ4jԿEʿkſEʿ{G߿>o#Ŀkſ>o#Ŀ2[9?b:N?86nI ?nYQÁ?(k†Չ-пS !IB(]hR !I-G%[ҽͿ0竿A(]h0竿Ί5ДA7G?G]U^?;?' jㆿ0;пZCY1YCt-Yο9Y19Ong(??zUTzjE.Q'Xqt'?kTWX|yASU?Gœv#̿ $κKſe1F? $κKſ*IޫJ?)ں\e1F?)ں\$B%鿻H ?4"}P8ɥUx˿E)׿`ǒv?E)׿(BX)?aٿ`ǒv?`ٿj)T1h-_b?ωXvKCwD(nTѿ;&ͿŬD?;&Ϳ9#zȿو{?ŬD?و{?*k9ѿCvΕ?$$2ywN^u*X"Ͽf/;˿m?f/;˿L Ddſ_?m?_?SѿSzRt?IX (Ʊǿ$=&?$uiH?bf_D#繿gkuRUv?D"̣ w?/C?ͣ w?W.?K?/C?K?QbpL?VW6j_tB)Я3Xs?"ʿBP?#n/?BP?G-5 ?F4!?#n/?F4!?c#?/q}i6?aӎ QmAmG?lFk?+` ?#?+` ?}L?03@?#?/3@?տ^͍?&ŗ`ʼnϗ񝐿PWj@?ALy?܏j?K0o?܏j??[?K0o?[?WD+eԿ$_D+?1:GN8?pLҪI')?FUܢ?cPC8G7?pk2|D܃}F?9["ȿC%qd?SFbVQQs?C%qd?77E?RFbVQQs?7E?d՞`&ɿf?Ĥnh?*6- >gOĒ?}#?$kq??%kq?kf3B뿁9%/??9%/?g'ߓǰ?qp~`=b?$wU28?m`? r@E? r@Z?~%7E?~%7EpZr4ݘQ;2?!*I5@h?<fېR@Nw͓?lI?9զkX$>?9զh%o?`񘞿kX$>?`񘞿3SO=Pߜ?q} qkffh?->ΎlPH=ق?6*<.?&q?Kn Կ&q?bڿHG$Ln ԿHG$[xkx5@d֢?&M9R?8^ޙbb?Fk?EU?}8ҿEU?ɈP~AϿ}8ҿAϿ0 ?\Sn1?„?hs6 UdF1?a#/h?Ev}?\ ĿEv}?-(J]/|^ο\ Ŀ]/|^ο){ԥ?@?8Q?!K ?c??jϱĿ?GA;ȇKGYhͿjϱĿKGYhͿ4[L?b?oH?ӈ0y;?֩mҢ?<~P?Lax_qY}?V! 5??f?Vbt??f?I =<@Z-ZAWbt?Z-ZA!lܑ?ro ? \-v?[Qs?'_n?%x?ϘaO?%?ИaO?\[c[?dկӿ%?dկӿyȎ? (X?3/ ?Նk ~?E%F>ÿx=I3 ?x=IYX%?QUTֿ3 ?QUTֿMnW,垿)-v?$ 冿D[?Bl?nz?xwJÿ4 _t*{0l߈?5 _t*{eV)?ot0l߈?ot/픺LrG?:;M1fk?\:j?_Q?t(1Ʋ?mi?W*տf1+?c-Sf1+?V?%(c-S%(xjm0UA?=ÚX?Б?ϫ/oIz?PͿz?4b?*>ҿPͿ)>ҿ>&CԿ'>j?jR[i=>?O? к?agGԓнagGP鉖?q߆ﺓԓнq߆ﺓǿBm?ս8"xEy.1? b?w̟? Ƶ>Sn  Ƶ>Hf?uܤSn uܤgT: "Y*?A?Zq;$UG?àSW?D?jDHp?Hpȸ?J L^_? ￁: =@2񿂂:@2  ׿h’$?W/f0?bYKr=?CH;0s?'ֈ]'Wz<ݿֈ]z<ݿ%LT C6J?z^}?Bx?0S˿+KzϿ/S˿aq]?+,KzϿ,ͭoԿKYd?V(^zڀ ~?Î LE|u7?<ͿD4eϿ<Ϳ'$3?/rD4eϿ/r*zķԿ?Yg"!ÿ4o;ʿ{-q* 9?,=a@dQx\ȹ?$1)?$${N?R8+h11)?R8+h1`?f]#?sVTnh??࿰aL?࿐pۿ9|￯aL?9|¬]?\ 4⺢?5L9Nj>TA~F-ƿ81>cĿsAX?81>cĿ.ffտq:EȿsAX?q:EȿB$ƿC9?^우i1 F :3l¿ ]U&Ŀ ? ]U&Ŀ4cSֿt) ʿ ?t) ʿBƿbzТ?+&ioG?JC~?cڐ?21޷j?`Ǒ!٠n@v?dqHϿo@?m5 ?o@? Ps-Ym5 ? Ps-Yilz?Cc>?[(Ne;ؗ} Ve%-_v?ZwPa??wPa?U#?#⿺ lQ?oEP?{&U[%"(O >dAa?+I*?PI۬+I*?Zwbƿ-ǿOI۬.ǿ *?ifJu?۩UK)싿+]ߔol|/Jǿ?L8?L8?tǿ_o~ ǿ_o~ ǿә?e7Is-?FK%r?o_M?-곿TV|i?.i$?hDy|ƒiSmپ8ܿxB^տ8ܿ26oi?xB^տ26oi?}^A)?-Rh?=;O(?˔È Ģ>˿<Ŀ˿ܲ2S?<Ŀ2S?!aɿ (nA?E.?3ǟ2< f@ Ӹۿ-^M%>Xp?Eg?-^M%>Xp?jnӿ N?Eg? N?qwm:?#Wy?SSQ/Һq\5rܿ ֎?Wڀ? ֎?#!Կ[3d?Wڀ?Z3d? UY ?jTv?!F'锟<򆰿ekB(ʣ?ԥD\m#@D: ? !?,? !?/By~`H?,?~`H? -2}?ZN:?qD󥟿Ab*/`?/j?e?/j?-o i{|)B?e?})B? Ͽ I?ƺs8>RbPw?Kiſ-iS[]?-iuw?i#4?S[]?i#4?NvǿB?<?/ޏ?o¿ia̾Ͽo¿j,J?|9ha̾Ͽ|9tD?<+?Vx?]F|$_I?ɽ~?)D?WI|k?T{?C@ H?- ?H?;m?9nd5 - ?8nd5 R\s?h DΡ?!FY1?~^ zĀ?r:,!b?M?!"M??yEZ_忪!"yEZ_BGV?`L'g?aȿ =?xV5jLg=?GnL?-j{Oҿ-jtcý?耰ѿ|Oҿ耰ѿ 9Ce;,Ci?{\ڈ?=#j+@G>_!Ab?b" Vѿb" >3?v`+yѿVѿu`+yѿ.߷{P9O?P̖?k?d㙑?iz?M4^G|F?n 3W?Uݿ3W?f249wO_ؿUݿ9wO_ؿMaTx g< r?-@?f#ԋ&t ͔p!'W_˿Mwѿ>_˿fڿ\#$?_֏?v5|?&L!G3jTLſU1}߾2jTLſy~v̿0@U1}߾0@h_mrǿG#?u4ov ≒?Jet?s2VQ_,ſ>$^,ſ54kǿKK>$KKwQgvɿP%jDw?[_G{S ?-jۣcU]6hI5xr*!?M֫ʵM֫5*1? *\ry?Yw4vc>?=?N?CgmiAys?aprL?;u^V#3?+YHϿ^V#3?,C?0(A?+YHϿ0(A?\LU3щ?U!{ve| r?4"Tꐣ?썤Rb¿u?T+jͿu?{"M?:?S+jͿ:?=Fd>D??~k]RUE@t8o?;Jᵬ񼽧?|P_D޿oy|- oy|[2F>o}--o}-97?;%?%s$aLf}?8:NϖҁOj'['>N rZJ?%'ϖҁOj&'pHUA?YFu-ںq?nO4?tY? m ࿔ ,upݮ?)m )FtGzT?JⱿ;-Jg¶Ͽu>ޠ?LhtF?X%= :zs>@@"D~Rɔm4y?"D~M#S?ǿ(ERɔm4yǿ(E*l@kX?JRf:?CuGlWx尿d! ?ڿ%:Pqߺڿ? U\T$:Pqߺ U\TL:0?z¡?!'v?9_|tʇnΐU ?-¿gԿ-¿Me?ͮAKͿgԿ̮AKͿN"wÿi? >N"wÿ˼?]? i?]?&'B??CYJogP?5{?DZ|?ݘ?\T`ſl4v?Ё?>ݘ?Ё?Kegҿ޻k?L*?\ey?+Qf ?l;?f?+=?ո1{?U@b꿬p?bΧin>@7' ?p?7' ?-%p_?@F?iUԯ?a\~X?M?X*!?X*濆C?+7r?!?+7r?'%忼qт?UzP?F6k?'\?74?'#ڿ}`Cw'#ڿ4?aOl?}`Cw`Ol?>m1 ̿2C?Vc9{V?*:F/6?PQ?,Q?0k5ٿm'vcR?/k5ٿ.P?" ?m'vcR?" ?H;fȿޝZڣ?7OʿvR?x ?śqpѰ8V^?#9?i*]ۿMntZV{]ۿ?h?MntZV{h?"T5࿄ds?^/pKI !3?ѱA ?e@cM786ݿA M786ݿS/?0a-g?@ 0a-g?1:ROϿa?\^.{w?^J~?EVw?hTt';Ͽi ڿ$ͮӿi ڿZ?5n@i?$ͮӿ6n@i?nC 8c?x1%3[A?,7rw?*34ԿaXrؿBڨ ԿaXrؿ ͮ\Q?YslS?Bڨ ԿYslS?^㿿lp ۡ?RNy^/?c%-yݘ9?E^|vxgσ9勱S4ؿD?v?D? j$ؿ/o?v?0o?F=Nb@:)Y?&7@jFE@8.4߫6((*?#߷ Iܵ]?` p߿ܵ]?j}?r<jD?` p߿r<jD?zZ[?5 %h?h;8@?p'&'(RϿC??mHYfae˿C??ih?5Uv?mHYfae˿5Uv?{td?<Vz{?~'s3ޫп1e ?<\Jʿ1e ? g YS?w?<\Jʿw?q-7ҳ4}ޠ?B RN??Lre?GI?g?HQި?HSѸп2?HJ w?2?x3O?? ?HJ w?? ?AWv@KD) ?e-@?3GAQ!?zG0w}r૆99CiPjDta z?CiPK̿*͇?jDta z?*͇?oHF3ſ~2vP?xN?u[N?ӊYc ʻXw>{?Xxנƿ$W?w>{?#W?u\/Yڢ?BS7:?Zaɵ?R2p?;M?/[?F@&jc?֍HsI?֍H i ?d`\g?sI?c`\g?  W?q{¯?t4?;CɌ?vkI:T\ۿTXw뿉,݊]?TXw뿩b[xo`?,݊]?o`?/:Ŀµ\=t?040vòEn:8ny?~q'俠Ŀj#?ĿVѿ ?j#? ?$]dҿϬmDߢ? -v}3t?^34@ƿa7?5@ƿB@Ϳ7˧r?a7?7˧r?.BͿb꯹E?.k?R6X{-He?y¼ك-Mm?5>@}1?S;}1?!yjP(BSֿS;BSֿY\?U0J[IY^𠿃M#p|? Y@^?۳Dg߿^?^BĿ۳Dg߿BĿDB 礿kFQŐ?2xщ|>[QW\ 9?$ؘ?Pz&|ƿ$ؘ?(>4p>?Pz&|ƿp>?[D&K7?Ϡ3?_ۊ N IZ*? ?#%ƿ ?<'lZB٥?#%ƿZB٥?o??)(&? ?8.?t_#ԡ?GpG?Zl0?1R^?V2|?ܘU2|? ?x~?ܘx~?>% ?s*w¡?NVl?5,?^"IfWq?i?_iҿi?^́?M7F?_iҿL7F?Ɲ/?GtO?r ?H5A? U?@c]]-?ȢVhп@c]]-?HR|M[]?ǢVhпM[]?'I1?@ ?0 O?7MQ?|ዥ8ҏt]陾?\< ??*dп]< ?yѓÃ,e ??*dп+e ?ip?9и??fT3? +?'qٖHxڈk?fqH _ Oē?ѭig<_ Oē?!e?oL%*?ҭig?/CD?}ᒤ?9>l"n?ZG+?Ivm@)2S(MC?)2S ΁fQVK(MC?fQVKގݮI{?̾V ?󳱲\KO*?I(v3 ?EH% ?]]K$>ݿM7ǿ$>ݿ0Կ\4_$?M7ǿ\4_$?FX)̿lc0?6א?h-ڙ?F4 ݿ͕Ŀ ݿPGտ#!&?͕Ŀ"!&?c=Kʿ|엲?VUǿ8 ?.&PL~(?,c wX]A;?#FI5X]A;?D: HtS?7Z);qO3r_1WLo?Ԝc?4/ؿ~ S?Ԝc?~ S?c7?gߡ?S;q{kdQ~ښ>?%ѿG¿%ѿE ޤ?VK U?G¿VK U?;jM?WS?`y9-iȓ6-cF~镚ͺ?׶y_yп׶y_yпKqG9?)w#K9?)w#K9?@nE?Ȗlw?w4M5ѩk¿}?Y?< ^dză8@f ?xdgzq4f ?!LuX`xdgzq4_>..p?0z ? ??{RqGcS|?OP?ԻC0<ҿOP?1ׇqC2}ԻC0<ҿqC2}X_Y?|/?d ?c𕿈N9el*4{? #=?肝YML? #=?|Z6\v6#,肝YML?u6#,Gc^?? v/?3?;=R@n4=Ϧ?}6x#?+ d?}6x#?Dr0+ d?s0"BY\ ? l?N 7Irn>[P;b?6򖧿ocqF 0c?g*׮?M?*׮?k9b?M??#kbRO?x_?鴠@vF6OLjw?}?OLjw?2 d ɇ?}? ɇ? )봿AE8?pl]kt՗z?8#f.,ܿP}G?1O}G?=o̿;ͮ?1;ͮ?]\ʄC`+^KJ~p?=ٮZD/?0?i.q@¶0? $D?.?i.q@¶.?r? PFg?IQc?:3kLfu箷#?є_}?^";;pg*Χ?֛K['@Z!쿒ՙ?'@Z!STۿn?ՙ?o?[Մ׿jY?_8?yA|1?az?*Q6^G˵wM?6^G2eVꯁ[ 翡)?nמBl?;>֞Bl?]1z?2=I:?όO;W?x2jD+ș CZKH?$T6} KH? y׼N?$T6} ׼N?f(}? }?Ұ^~?v\k?&}+Vjпtn?xqk`헼tn?47(ϿSS\?yqk`헼SS\?"*:?B^kߢ? Oŗ?P+j?hS}!8Kο1K1Jc?^3d0K1Jc? Ͽ?=?]3d?=? /?v\nJ?^bYtk?st?-?i)WUgqsXօ?Cu>ѿqsXօ?ܢx|"@,B?Cu>ѿ}"@,B?S8?PK?iB?{uU?:vr,+ڿp{΢?ڪJʿp{΢?WĭGCi?ڪJʿHCi?M?\=oma?AOA?dQ|כ?eVN,e"ɿ^fG(?v]fG(?|O:ٿ?|?v?|?hT qu#?/ u9ſPSu4?ߐ`vf2_nZ1y?lz7G]?b?迪G]?^g(a ?b? ?j0&wn&E֧?j< E^eI?z|,?#hῤ|,?xd)r#h)rH g"[/=?mikl 0w!?`UiֿnmF?9W[UĿnmF? a-[Pڿ9W[UĿ\Pڿ~+Py6ڿݥ?Ez֗QkɄ?SKR}տc?M~Ŀc?!)]]?9 DڿL~Ŀ\?9 DڿqښVؿoOm?PMpi?[6,?azi.eI?nC?wu?e!B?K}%J?e!B?`JU;d gK}%J?d g.0 P>ή.f?!&p]3yER|?WA(?2Z?Ft??)k?Ft?? ˑt9W"b꿃)k?t9W"bڿNбȤ?췦 D~owX[?A̿B? /qſB?W8ǼPѿ /qſǼPѿ1 ÿrV?\t0"Ntk#?oɿ5V7Ԃ?y_Ŀ5V7Ԃ?RBi:пy_Ŀi:пK$|VwĿLT?:4檿wj?zY+ޣ?4w)rdه?ܞ4Ϝ?p3-IBo?%8-IBo?`)nE '%8nE '㿳1kAW( ? Sjl^[?j pk`kܓ?;[޿`kܓ?@j,ysMڿ;[޿ysMڿ18>ݿP=.,?VU࿟n3?y?şPK?r?Vv+5?dۅ?Aąa/?eۅ?eɼm?V?Aąa/?V?טG?K?zQ?Ձ笍Fkrp_S ?⌈*֢O?⌈*/^P"?I, C֢O?I, Cnv5,?!9'?ܠx5t?3j5aӄɫGV4`?9??9L???:1] ?.ݣ?XذY‪?6/uӣ?xuKM͛nD?`piw?ry\՛茳ҿ]6>ſ՛茳ҿ]rտI9?]6>ſI9?O̿h\ʬ?cBBr\m{3zA?m }w?xK)쿾빝?gMI빝?5]߿ؿ$?gMIؿ$?h2nB({C?01? vw)y. g?E>P߿$gy?y?$gy?,zпy8g̽?x?x8g̽?1?N?;?1uiGf?ޒٗ8޿-&??-&?Vi0Oѿpֽ??pֽ?ؾ?vvߞ?H?_?&?P5ցj?Q6dB"U 4ΞnN#<@@^s+F?z8??^s+F?˂@[?z8?[?.$)l?Dm?D+?8Nn]0lQN?ōͿr6>?ōͿ7~$?F?r6>?F?*+̎?R{G?͈DGi?>otC4X?M7}?(w-ĿFET?(w-Ŀ.[f?I`HW|?EET?I`HW|?0?+o?bj?w|~\|N?`6(?f1Mÿ\d??f1Mÿ ¼)?Q=5r?[d??Q=5r?rz?Aq?M?6{?бjɡ?t#: ZRy/5D?eD V$?ż?.G֧|Z?C^i>пV? HMA?V??I],2? HMA?I],2?dDKuRס?V\id~|4x?g+ Ϳ18}?K6E?18}?wuQ?fn?K6E?fn?%J' O\?1[b?pl?.^`?Y?[~h?-SPB/?-SPr{^iƣU?B/?ţU?Ǒ8BE?V |_?<?U]r?5rp~ ?mYrha?mYrݜ⿢gB6?ha?gB6?5Ώ[?cl?ʟp?6?Cljj?PMڧ3=inPMڧV? ?3=in ?/W?vk?2|r?d9ݏ?iP ?0?ps?INC YIN?b?C Yb?o;:?0*g?"&U,ޅSU?r?Fn{j"Jȥ?p]3|g?}% kc!62 j?c!BD(Kx; C?52 j?x; C?ْN š P?ttNƇ?2p?Nٿ02,?/'I2lT6 ?2,?T6 ?k*46 x?/֑?nAf@>?'꾵?6?ѿu3?ѿ%1 a?_c?u3?_c?q3!?A?BnYϐ?I S?Wd?j~@?96Jѿ ?96Jѿ|0֣*ru? ?*ru?h ?x?^ټfDf9},? %?1Ҧ?Ĝ?xG -?S?&7;S?i3G>o&7;S?6pY?ʑS?i3G>o俦ʑS?ap;U??:a/SpH?NtG? b6~?o5ѿ b6~?)^&-7?z=?o5ѿy=?tNĜ̢?rŠ?N4BdK{қ Rx?+(4 ?Uh ??=_oP?Uh ? )?>=_oP?)?ݷDu7ˢ?hcP?4*twrY>ĕ?#?ƪ#?YX?Ǫ#?rn@?YX?@?){2, t?d$6O??&w?s??RX?;ZN/_ƿ?Y\??;ZN/_ƿ?]q+?Fsީ?|bE?L'3?pT`KA?dO2WY@`KA?t¼a?O`K?dO2WY@P`K?d|7G ?pI2?Fkfs?D~J\?PCbԿD?p"*?D?$p^Tֿ_,?p"*?_,?hܿKۿ Z\֣?~LЋ?6s?Lx2IO?a0ҿ_I3)?Lv?`I3)?"ӧѿ!?Mv?!?D׿I${?@Fh?WXӿ.ߣ?{%㙿4 j^p/1달ҿ&sl ? s,Ƒ?%sl ?kt'Yڮ]ſ s,Ƒ?ٮ]ſqdg&ݡ?ac̵ WH O? T( =?W [t?ꙹ]G?$Eh?9t͚1$Eh? G~? G.ۿ9t͚1⿽ G.ۿeѵ׿b?T?5vȨH;x-c?M˓*?0F )?1;.ҿ/F )?<\?f ׿1;.ҿf ׿ɘ ?hw?-^St 4:el?lQU¿l?8B]u?kqlQU¿kqcɺ?"m7?Odj<[׏rG<]?mT)D?[nT)D?s' ?TS[TS᧓:?8buס?MRމ?5Օ?,[y?8Y5@E?xiRφdiW i!@I vi!@R?;聈7ɿI v:聈7ɿPN?Ji?(ﴨסGXG.I6?(tz?y]"(񿒻 S_?K, S_?gM(?Ŏ¿K,Ŏ¿]Ifq?vġ?E+ #ї;?Qʓ,m?g:!"?d3f:!"?b?тId3тIG2wK&?7ѡ?,P8wܖ?ЍB,? x4s? ) x4s?WU?Nwᠿ )Nwᠿ??-4۔?=M?&%cD:>r?+ט|?Bv14mn?+K񿁖˟<.?˟[' `<.?O >`迣Gڊ¿Sb? <^F?GZiؒXaJT!?{s8?{s{!Y??,7dտ8??,7dտ p/d?κM ?|9?g=Ď?J>/d?g=Ď?s|udidEȢ?Nw֚?o=G'9?g+Ϳ-*حmK?-*ح[%?v9?mK?u9?M?ߠ?e?n=)N1?pq\ȿsחg|?r2Q%?sחg|?f}8?J5j?r2Q%?J5j?)K2@HDμV?La8?(K?mEO?Abh^E?z?᳚>?@ۓh׿a?ۓh׿ow3i3B"ٿa?i3B"ٿxK.vb?ċ}\?JG?>}\?tK,Cx쿇?>p?m{e%t?/?l򶿱+&`?S?+&`? e KpoɿaTS?aT}8?8? IuH0s?k>ʴ?0cQan?:?Qan?e?:?e?ʼb˥#_?O8?5e )HMhWn?l6r[?wUgH@<?FS*#0x#cӽ/?0x#EkE?`/cӽ/?`/,Vn+}@?6xy?W;R`N?PA S!D!述?!D!f\&U16xۿ?U16xۿs\06LJ"?pۋdQ \~?,?s3D˺QEly潿QEq'NÿR(ly潿S(y? H7M"?E/#*=g&?5y?}D:!1M2#Kȹ1MRAPơ2#KȹPơ^1?ky1/?ҿ${S:ѿ H?wľ='lWzYx?C'iu@4i忂Y<4i2d @.;#Y<.;#"lQ B ?tG.*&JEf?ilW?B/ҥ:PB/ҥA\?q2:Pq2Jwÿ՛?b~kfm?~Bxn ~?&'L ?H?zm{ʸV$?zm{a;Q?EsKɿʸV$?EsKɿrr?'?~k?#u[x?J?`?g P?gIs?6GS&ʿ P?6GS&ʿ{>E= ?t͡?P9?\⺿̇?"B?Y6ds?&VN?,1?̡ ~>?,1?@x.R @/̡ ~>?/쿁Kނկ.?P%ǽ?xUhÞj*é֤?&?B?l±v?B?I"6 ?O9kcl±v?P9kchcd?G98?(Xhbg?.~Zp?-8Ͽ֓ ?-8Ͽ~ʌ3?]YƿՓ ?]Yƿ9Ŀ?q⟢?J@O}'?+Jbnx2ϙ? ~?*Ϳ>d?*Ϳ׽?ƿ>d?ƿ\.`kY2R?s)Ý?CM?{~߰?90 ,mlb?b#s,#@?#s쿖/$7b|׏,#@?6b|׏>RR?sK ˫?i DMθÌ?O筪 {꯬꿨R?{꯬ @H¿R?H¿uYؿ?頰?u+w`sfD?fߓ׿xտi}2?xտp,࿲>ɿi}2?>ɿx ߿gh? T9KS?W=^尿\?`1ղ?~mK]?4|?о~򚅐? S?)̌-I??-I?p!`S߿%??%?=Yi.5Fd0?FL򯲔?&ror?i /B?~뜈gV[?8\j?V[?Կ5@#?8\j?4@#?0 Rտ?,(?7]ikR帿O?~ %W?O?q6񷿥Ѩ_~ %W?Ѩ_󰃭?H$9+?*ϑ?xy9Vcb \༿ب?6@'?ب? ́6@'?́Li<$?•b?pn+??[?3o?iį?I|p?nZ]\?ɭ?qk;쿱ɭ?6S/P?PM?qk;PM?Ŀ"Dj?5V!C?)?֍b&a?hF ?Od?+ Nd?㹿q?+ ࿻q?[uy n?ͬϥa?lK.l?Yѽx?]/ sƼ@3C){=?݁/?uO4C){=?i׿ 5?݁/?uO 5?փv?] -I?f-p?Ś{?'4#q01';ٳ? ٳ?dؗbӿlɂx>? lɂx>?9<%?i2y?z?mr ?suQ?fHT,?_.?#>݄?ݝ~??;'=sũ?RE0?=sũ?WGӿ?RE0??x$%vO?杇q?zsNM?).s?"rApk?|?Apk?#bks Iͣ^s?|?Iͣ^s?x(Tȷš "?"<yS@ ?*?I`ȵ?f?a?f?ONjdӿD9?b?D9?'%Գ~?س3!tq?̦eK?YA!?-@2?34`q?-@2?[uiҿwƭN?34`q?wƭN?ٓXe댧?>`*Sw?A?MU v\:1?`ΞuP?"(?;b+wU]?:bl#?5/ys*wU]?5/ysw(!$A&3-? JRS?L?@G?*Lz?ËM9Ë'\GXӿM9Xӿs"73"?ie<\Xt?-O+:>?%,y.)׿4-y.)׿O;o?h 1 ?4i 1 ?1ÿLw]?qXV?ˏ?߈:fd{`ֿ7r(d{`ֿ,?{?7r({?f,3ޭZ?qR%'K¿+Q?z?2gFEhȅ?R[B?eƗy&eƗUS4?Q@?>4?PV.ѿ@?Q@?@?7p#h@Σ?gynJR~䒶砣?yιC7&dXQ'M8xny oj`b?ny oѱvv?1߿j`b?1߿i#?jw;]?#De'˗\^lMrHsa3; ^?HsatqMӿV[x?ѿ3; ^?V[x?ѿtPw iX?W@!wŕuIs?x7 mW?$H'ٿQ׿\~?Q׿ޗuC;?96\~?96d$&j۽Y@?빗kAXh?!KST?Yڿ)<׿8ҕUyY?)<׿Ci H?r^!8ҕUyY?r^! MO_? CAпfZ}?큽;Us"I[N2KBc&\@1?qC1?@E@:AqC:AxZ@,׎??q^]{u?8#JO?J8#JO?v3 @y"㿅Jy" 9?.)á?.jDBtXmtP?.DYҿ],¿.DYҿ?k?N'ֿ],¿k?N'ֿ }B#[@9š?[>pY Ȗ2qD]l!?D/mϿS9ݹ¿E/mϿR>(X?;*ԿS9ݹ¿;*ԿOrӢS m?؍S n?I4r8Aנ?L[wc}5şv%{nѿʊm $LA(?ˊm Xۡ@d?$LA(?d?zX?Po?CGxvz{fw BX'lxdȿL]?L^A?Vrk?]?Vrk?$.ܧ?q`t?.f*#NLdkM|G 1?'t03ܿ bs&t03ܿXpjA?RX? bsRX?(?!n?VM"Ø?JX?5?k?tZs?[ak.?Վ,?`> O`Kp?qLB?> OqLB?eJA2k?Q9#W?H??. m.?-=?dv忮o7K{dvU}t:-?Kģg?o7K{Kģg?6[7ۿ ߡ?c;=?Mhrj?z~?ˌ?}W/ƿإ0W?}W/ƿ!?W@Yeإ0W?W@Ye7NoVp};Jrڡ?@HWx?h "p?zmމz?H=u?'iVЎĿ?(iVЎĿC7X?J9t?J9tc 4X㹿e)֤?AF?2~?֯ɝ^?SĹc?"?B]?ZT tX?MR?tX?68BP?ԙ?MR?ԙ?y<{~U?bTu?;f%m?f?:_JvU]%?_JvUF,?CQ?]%?CQ?y1J?,0 ?5yJ(ȉ?v+[{v+򩿳haѿY#"*N?[{Y#"*N?4ο$?ZN?t,ʇ?.3vn(9w+xk?vn(eAX˿z5?9w+xk?z5?Ϳɿ,yǣ?w)b㻿Tr?Vp?́ Ѳ2ES?@;H?Ow땿 +v^?4({￑ +v^?5D󿤯8j?4({¦8j?;p տ7cF?Ea$Ŗ{?3PzĀ?̓пjD?%uLjD?n뿮-bsq?$uL濭-bsq?vxҿRD?I1Jz;P?py|v?9Կ_}G?.N *ʿ`}G?bk#οj"?.N *ʿj"? h:ȿ4?LǙbqZ?|u?῏gԿ+y.?+aqɿ,y.?.Ψ̿5?+aqɿ5?^~Srſ};?:~??4]E?uH^׮Y?ouȗ(?5ƿB&ؿ5ƿP"N=ԿB&ؿ=Կe ~?s?X?H$%`?rl$ZZc?/&?t ٿ/&? \G哳bt ٿ瓳b[{Z^?>?-?Hsm?f|$_??AȽ ̿f|$_?=ǰ-??AȽ ̿-?B<^ϯ?;{1)Ӕ?!삿IJ+?]6?~C˿]6?U2א?~C˿2א?[Nrm󶿤Ё-?:&^?U`ÿu«ц?c]?"604?|b?h,e?P4i,e? \8xxr`O4wr`PYq9gnP8?tច?r{75Gť? ?#A 1? Imܿ#A 1?4LGˑ!Imܿˑ^ B4+ >ˡ?h06q?0:GFo}?˷|2hZ?H4 mÿ2hZ?ǂ?U-пH4 mÿU-пR۟?FС?"< p?16՛y#*b?sRU}h?s#0 Ŀ}h?8'{Zпs#0 Ŀ9'{ZпIm?_J?Y 4Ŀu?n=?yﴱW 2?{ړp4Fg5x~HFg5+厭:gP?w~H9gP?+ s?"%)?eQ(a҃v! ycԿ<} ycԿgB ՛ڿbB`eW?<}bB`eW?|jS?}?ق;hApa<пYh\?b}ooȿYh\?TR?Zÿb}ooȿZÿ+.!̣?HPHqš?&[ yꞙo޵cFп.mX?v7ȿ.mX?U$!?LGÿv7ȿLGÿ\3ǟ?j5v?B->xHe؇?DV7@>]]ſDV7#֡7?K)@>]]ſK)Gd5?x?WqtP?1P 3jVr媕7z?8[8ne?5V㐿8[8ne?1Fр+?8Ё0Ŀ5V㐿8Ё0Ŀ+HT ?S?.@X?S3T4Kq6q_!X?,Q:?g^1Ra+Q:?I?T0 jÿg^1RaT0 jÿ]BA?y?C!:L;3?=? [fC$u[cq?ۋpVg-^?1b?_01b?i^ ?vi?_0vi?A+?A\S@?)񖿌eM9?ItO?Hc??Hc?/FH?pCĴ??pCĴ?es5?LQ? -뵓?mA{?45 2H?SPr}?fC?SPr}? ? q8?fC? q8? ?C?&qc?9w*~z?-T\ҥ-?Yd6Y?]CC?Yd6Y?]?45 ?]CC?55 ?(ޑ-?"xZ+?6Cĵ?W?t?_?niA?LV6̖?i, Cݍm؉?DݍrHa? Vve?m؉? Vve?T'բ?ԏ:|?uKʢ?j?ltVf鿏9?]Q h?9?]󿵾rwz?xV9?Q h?xV9?R;r<пL<ʢ?|x?jsnGg*Hj?0\_Ŀ GBs]̿()? GBs]̿nO'կ?H/+?()?H/+?}]%LƢ?֮yY? 9Iph?л2Cſ_W̿?_W̿v=?pk;??pk;?$ֺ]᥿¥x(?ض1j9\֮@\*Iq?HP5;v`:C05E~Z4^ؿļBϿ3^ؿ׿ ?5RżBϿ5R⿉nS?"Aw?G,it,~Fy)V:0@q:ɼ? mkq:ɼ?=,l?ߝ߿ mkߝ߿LϨWq?ݡf?E[;Hd@f%1 $$п)0?Y?)0?딻?tt^ݮ?Y?tt^ݮ?@?_A;y? [VGt=Yr^ѿx>?"t@c?x>?dUa?@{M?"t@c??{M?lS?fcY,?H6q?P?Lfm-}5b˦?x@9呿Izxd?W.?! W.?:!<?lMc! lMcj3_?5Q#]!:?Aqo#1?u.u #?u?1̍̇u?uz?|gſ2̍̇|gſxA?[%?Wj3pIR4k?ds2?UuW?룔%)rUuW?C?z< /꣔%)rz< /F?1Š?K(ݶe?-EBCg1+?JŅ?&/ ZJŅ?8?~w&/ Z~wkC??͒?S E9?L4ѡ?Kw??UǮ?Y匿/5;a ?ӿ9D?;a ?ӿ2G?t6C?9D?t6C?: ?8͒ˡ?O?rbfء?F XƋm(G 4Ik/¿[ܾ?5Ik/¿`׾Oq??[ܾ?? ?I?g? A~?Q3k6gWGχ ?Gχ{iO?Wdx? ?Wdx?1m?%!͡? Q&o?_I?Bbϧp뱡2z۴?bϧp뱡"z8N?b0Zs?2z۴?b0Zs?zDь?[).?nM9຿Olv寿q \?i$[p(K9Ԟ/7.xXb ?vJmuJ0׍?aƿm񿟵aƿb"%?ge?F }ujz _#31?԰v.˱G\԰vB ?-?-˱G\忹-?Fܾ$?8Ǣ?&7?8u-\?\?Տol ܔ-?Տoleÿ?t? ܔ-?t?kBur[c͢?~u6#?Aܕ^?u*'Me#oVJ)?uSεcub?uSεvcq?fy?cub?fy?ԱXx%W?Iǩ?<~t:?'4ᄻ?ƙSŧW.N᰿Kmq@GB?ˬ=?GB?UNiӻl ?ˬ=?ӻl ?А0@bQ֑?S i?C??|,j?AJʽ?|,j?WT*e?AJʽ?T*e?z>j"6?k|?`Ιq?3Zn?͆9a?TE| ?>*?35?>*?h:h?Ư$]g?35?ǯ$]g?Fr˿zPޣ?Tx?6{OBn?SԊ\??*?9M}-_w?QX/*?9M}-_w?|%ut~;?QX/*?vt~;?SRSGǿ=?.x;?ճ ?IN? WW?| A? nQ gdS$"?EHb?gdS$"?^ZW y SN̿EHb?y SN̿d5j։?(T?iT_:?vқ?~v @:?︠? @:?A=¥??¥?>7@ll?7g7Ǧ?:!?+?z2 ٿCR7:??CR7:?h?Kuӿz2??z2?_ )ܿ#f?0z?㒄2|???D!xr׿W:?u ?W:?bGؿ\|oW?u ?\|oW?f*ٿF'͓?/bg2宱@?Cq(?^9Uw?cgl\?Vof`?0翫$aֿ0  @u]?$aֿu]? Go?^얬Ɵ?'Nd?$̈/ԍ\ނ?Ǟٿ -ʿǞٿ H@W@QlM? -ʿW@QlM?xf{Z?*9֠?+](h>?.d}lH>gw"?-w/s~H?#3}'˼~H?J?5u?#3}'˼5u?L?kkn?F̭O?ЃR %5?o%%5?gvE?{߶?p%{߶?aT#U?ֹB?Zu[ZG W?c(V?"-T?e0gn ?8O{T?Qg?.wčQg?)?&?-wč&?Y4@c}.U?g88ǃ?_]?9<\=̪N?~K?ꝉ?~K?=`?s%?ꝉ?s%?Dv>?Ո t?"pZru? gG?DqT!?[ :8tuNFl?Z :8tWpaG?b4mڽ?uNFl?b4mڽ?#>?+dw?&'Vu?J?I hn&_?4es򹸿S⃭{?4es򹸿Bج? ;?S⃭{? ;?nD}Ͽ?Oӿ?C}]?#}G?&^uǗ?p3F??Ϧ{@{-?b ~@2?̣?a ~@2?&rg2@h/P?̣?h/P??ˑK?xM ?K]䀟?RS=wxcog?"%5  ??"%јv?ŽlX?5  ??ÎlX?Oą?sMf&?F?!p_?sq0x?oTitſ2p^?oTitſр}x?2p^?}x?28E$?NMuѳ?:*`?%/s ?-lÿd 2Q'?-lÿc H=}eΰ?e 2Q'?}eΰ? lHNmk?b? iDp?3c?c7?Ȫh܁?Xir?1*1̿ m7?1*1̿//=!F?kV[ m7?kV[m pƿAG+%Z?hA$k??7?Jw?~?dпV?dпq|*?ŕٿV?ŕٿ_Tο9C&1?sv?v䃪?}+g>v䃪?_=$?:!*|X?Mhkӱc1?s.(Tοw ?Aw ?͠#u;?;?A;? SN?K| SJ?"5Cy>&qB?=9M?ZͯU0?צA,N|#8hQdFcĿ|#8h1*6@F3ܽQdFcĿF3ܽ7;? ?;isJ?"V䞿H /i?"0Kt7_߿q}Ŀ"0Kt7_߿%lƖ?cuٿp}ĿcuٿT%y?Ws?eO~z/?h O?}:?+\ލ}:?&u?E&E?+\ލE&E?3?pݕ?|LjNѠ?_8a?֟t5?*M֟t5?&б?U[ҩ?*MU[ҩ?~yUYH?.5ֳg?=I%Gۼ?BAԾ?6[?[ؙK?| 98ۤ?"ߍV?h1>F4 1ÿ?4 1ÿNࠆ@ix??ix?us~bӳ?ܫJ?K^?^7-?'?;- ?ѧH??;- ?س?gȱ?ѧH?gȱ?,Mƿzx΋?V ?RRh?vm;p ~^eOg ?eOH,ϣ??Y?g ??Y?lWTS?f?;'?/Z;?͸8poW:sS?s4v)? 9?S? 9?]{? ;נ?P]D=벿&I?dܢ?,Xּ?|.L?33,z翊}<@Iˉh鿋}<@3?39?Iˉh39?μf:s?*B&\G?1 7?O<=O??a$eܿ=O?I罓?+P ??a$eܿ+P ?yCj?0n?1??fIi"g?xtw?U!?UXZy?j=??!?j=?? KkÿķBk?\?c?:t?թ hEt7tqPϕ?7t$j3?~?qPϕ?~?DbD¨\?*W\G$?/\5?/ Yq?~>&?RC]Z94/@RU,RU*@ש80?,ש80?UX&U?c(?3?9XDxz?3h'\kk)HNƻ?\kkŬ q?2r)HNƻ?2rS?ڿfj?Q_N?eIsC8uޔ?dѿj׿Z[/=Ͽ?h׿c.ѿ[81Z[/=Ͽ?[81c{ɘoۿ?~#?bU#!?˔xпb ׿m=?b ׿a*Ͽ!sUm=?!sU]׿Z 0ܟ? 2-?M߸?p'?/c ?_u?wvߺ G?ʂ?Ni1?ʂ??X>?Oi1?X>?tʖg?aiO? ?@ ?3Gu'?lU?hve|?lU?T9pAW?T>?gve|?T>? /I,?QG ?n}?ЂC?57쇿*5|s?TQq| ?TQqg>W?=a?} ?=a?8[P?o?h67?Kf?c:vĿSt y9qg?St =WpGU݅y9qg?GU݅RD?9>J??ntף?ڿnMo?tף?*Am'|&?ٿnMo?|&?tǿW?dLߺ(?)%y?J9 J]Aܿ릷vOlп7/S즷vOlпfXB?@>Xz%N_<?B?@N_<?oo?(}J?öTRɏGXLdgMN=@&>K?뤯u?} ƿ뤯u?e8?A̍1 9߿} ƿA̍1 9߿xΟ?-ڣX?Bp oe?q-VBBr?.U7%U5M?JDu?n?IDu?Qڹ?Evvn?EvvڽAQKW3y?rxS"ve?Θe?#$@Q~?Rz?.a?tBkp9_?tBk|H]?I+Ggp9_?I+GgcÿK$ ?Qؠ?}!?NC%M?qI ?t%?~cA?卩?+_v?D: =j ?reZcӿXX(X^gcοYǡX(Yǡ۹tZ%ѿdի?:g6U8?QGS| ޹*?^? V &ֿ V?إ:? &ֿإ:?|Qٿc!"?{訿uP(П?9{?{Glր#Hֿ{Glܡ 6? p?ր#Hֿ p?m6C̯ҿS5?? hs>x??5Qz?*(L ϿwE(ƿ+(L ϿE9g&1V¿wE(ƿ&1V¿!g%ȵgпu7?8}jpD_U*X%Q?(ՍVr}п?ſ}пDYzRPBW @ſW ^ Ua˿<^? xg?:]?.!0K"?PK(J?!?f? [?bi~[r6L?bi5::k~[r6L?kǂ0T?80?`^P?6~?WMp ?:wPB?ߚNIKi:wPB?.PG?{!﾿ߚNIKi{!﾿k8/,?"v?2BqX5\mzN2?%nX-V?t=۹@˿%nX-V?׸y?Tmȳt=۹@˿ Tmȳy|??0Sg=sz7J}.?Xl ?}fX˿Xl ?pZߏ?!ՠ}fX˿!ՠq #S[?` h?~Sr?*h_ht72˿OT?|{?OT?ˁdSӿ0n:S)?|{?0n:S)?X3lz7LKz?e8l?i:,zs?)iրrn ſqR)ϭ?ڀ r?qR)ϭ?yA ӿ' ?ڀ r?' ?1Io=LMUO?B3$E5p_Uz-[?9>BVz-[?0-0.ꡢ?|8v SW+ւLOeѿ̚c߾+ւLOeѿrSѿN|?͚c߾N|?m0Evr䞢?lCqӹ‰5@ZtDFYpiW&ۜsթ"ѿ%!y թ"ѿ.L/VӿX$?%!y X$?1к̝?u@?6hbmF΢?`v٘?tf>$z(?8C|ڿt/?/4?3'E?Q.o~&7 ?)9sؿ)9"? aݿsؿ aݿl!?G8Z?n)?Fm&d?Bz{Zȿ K O?'"U K O?&8KO83&"UJO83MWR?'=?-Ҍ?hK0a?'[zOQȿ& b??w9>C& b?/4bxIù>w9>C󿿧xIù 9x?5 3?e/@а+s?i?[7ze+wm.b3ϥ?x?8"NEҋ?`8"NEҋ?m@wh*3?`wh*3?M6?3.c?21%N%o0,ۅ~?`M<~ؿ`M'W?:6?<~ؿ:6?zk4?[,?N2'2!Rgyi>\Y?G̎3a#zªUlT͓ªUSQh? 8|?mT͓ 8|?7~? n$?PywjLuS'_?>3񗍿-"'OG*K'O3?9Y?G*K9Y? ?g@?WOrx:0?Khv?O&p?[? Γގ{^SKk~?;/R?Kk~?~Wgp?U@;/R?U@ 4$?~ҥ?MH=W9x?*k7俓 ?!? ?(?tq?!?sq??r[a1?|n{`?3eѫ ?0dL,ޛbC#֟^?D|y`ǚ؝?D|y`L0?+,"?ǚ؝?+,"?RZaS?yFZ?HM?Ma_x?~+Lx]_)pN?~+Lx]%F?j?^)pN?j?\jAS'?W7A?gs?ӟA)?(aE?J) ӌ?pOo?ݔ)?T&Iz s?TYRQ?/W?&Iz s?/W?M/v:ÿ`:?t)&Pخ?yc[?8J?pjN?1LQr@?2?OY6?LQr@?OY6?;RYп!@?ă/1??F?+^) Jwӿv~? Jwӿ8ޜ۳?_2S?u~?_2S?Wſډ =?rH?/)GGʾ?(6TvBӿAR?vBӿA3E?X?AR?X?ܨ{^ÿ\Ĩ??5ſ΅߼ʤ?K.?w7ŷRί? Xn5:>庿?n5:>庿mW I\X?\X:cػa5zJ?px(%Ѯ)K_?FKãsu;!q<ƿKãsl4z`c}u;!q<ƿ{`c}ׁ~bk?X˺V9T O]?.п [JSp˴kƿ [Jjٻй]eſSp˴kƿ]eſПf¿(Bi?LSfaEom(m?VYFѿ;|w*Fƿ;|Jd*(=;BĿw*Fƿ(=;BĿFEP;?ֲ5VТ?|Xส˴ ⢿ၿ[koH@smz?2ߨsmz?ǿsԺt{Q2ߨtԺt{Q?X>7? Zރ4響]c9vH?i7|?Q?Iҵ;vk?}iq?eʼn(Կ.L猱?ߩX?.L猱?#uee?*ZuߩX?*Zuv!bfg8?Z["ttZeF?OKqq? 6Կlbʴ?'B?lbʴ?i?@gG?'B?@gG?:;Тlӛ?a0߶X'?:K?CBp<&+Vuڦ?O4?]IN]迲 ѿN]N&?*k(Ͽ ѿ+k(Ͽ=BfQe?e^ ?մt.i?iOgsFdƓտ(et̿FdƓտ.(O?*]ſ'et̿*]ſ[3Gr M?1YsTJg1`?rpa? Aw̿6n H,#F1ÿ6n H:??CV?,#F1ÿCV?8pI٦#w!?{B˘gཌྷ?j a?P:qzοU#Ľ/oKU#ĽIV G? g/L/oK g/L;4[4pҐ>e?1ݪ.V?X?=aˡ?1S@?9wS3?E^?~ؿE^?HOTVݗ⿅~ؿVݗ⿚ Ɛ.?c?펲zҋ?KMK7?j|οE(ڿj|ο.OzPP5ۿE(ڿPP5ۿy?.??e[[]?3EŊ^Қe?2Ͽ$#U̿2ϿvNe?<##U̿<7?/ۚ?g{52I򖿦!ܧ`? Sf-d2?' пҽ˿' п^Tq?T? F?(7帿)Vs?I]" ?e5vT~?e50Gq\_?vT~?q\_?߃(}濣 ??0;;GU~?1N/̿UCY鿯DZ\J{?UCYIo VJ?DZ\J{?VJ? Mmh?'Jv?𑻁d$ǁ˯tȿ!?Ŀ?"?ĿuFUݿeJ?eJS}عy? ԬӍ7?c㒿)L7+暿|զl^K[?AS)ᶿ^K[?!EȿfAS)ᶿf@@-{?4㒿ܮ/?!ɠųTU??K7?xvP?}ʟ?K*T>?}ʟ?t-Q?73?\{?˺]?KZ'Ӻ~¿?Ӻ~¿M9=0?ۉς??ۉς?>-lF]B|B?>x6T ?2+,?^ HBƿU Eϵÿ o0Ҿ?U EϵÿYfփ%?TJ? o0Ҿ?TJ?bb"ʿ,=B?~ءw]0?\X?jBȿܱ3J?ܱok?Y˞}?3J?Y˞}? Tɿ}.!U?sp̟܆>?Ȼ4f?_0P"e1K?#dLfտZ~W4Z~|F u=5k?W4<5k?}n)oBygw?.IZܯl΍ D?Y{p뾿nP€33r uܿoP€3򿅋p ^ ׿ɄD?3r uܿɄD? Y!ۢ?^m9(~)i?y#dh^Lܿog ¿h^LܿP$U?@?aw?ng ¿@?aw?C:OGۢ?IZ5w=I᷄omC?aЙZOܿ>(¿ZOܿH3mhЮ?֚_{z?=(¿֚_{z?vIܹl]2?}wp9V2tNWې.?6 I q)f?ZWI?e?䩇ef?䩇V+Ǎ?a/!Ͽea/!ϿG+࿂8ܣ.4?hЗԦEٿD\Q?S%?ʘ?Pʘ?D ?DaƾοPDaƾοOB~Ϳغ[i?!yhvM0sL?&?=5¿׿=5¿]?l,A o?a-t?G1P#?"Nm?-DRve@,X?0S?,X?[q?a10S?a1|~? "?#e#n?7zpA~P@R6?Ӗ9@?>y4?Ӗ9@??bI 0>y4?bI 0q?z)}+?R}Wү?|tl?w3$PRO2:?1W?B0r1?1W?P ڰ?oԿC0r1?pԿGv?,i?($k'?Fx?k2)q?D,c䒿ɺ=?W)7?Qn ?W)7?z\V?{SԿRn ?{SԿop?iH? pzQjU?β??PjU?Tc3`SH?m?L;?c}=?Q8Z&|vV?~ZSN&?vV?I5 3R?ZSN&?3R?||Eɴ?Sqp?[C?;J?=?<=⿐U?G?U? -\gƇ3jG?Ƈ3ji |߷?G@ ?Rcp?CLLY?w'~aEE?C㿘= m?g*;&?= m?}؅H H(h*;&?H H(p.D?2+?ӆ(|-*ޤ?ƍC~?9Ћb?q}x?1gԿaꣿCHaꣿueGz> )?CHGz> )?7 ~GͿvhߺ?PucQ e?(#Z22t?F0ݿ]qƿ碣.^qƿ7g>x?碣.g>x?u=B_5?I.x.T (?W*!ei]߿fl+jwHPɿfl+;%9iǿzYq6kwHPɿzYq64A,`-?4=-?*:X8Ċ?J(޿d¿tǿd¿7E_gÿL݃tǿL݃7σ?Ipɢ?Z׬:xcv?p9z/?LrmzR_E?ztWԿ\Ho?ztWԿJ?5"0E?MZlnCoF١aACڎa?#ǒߌ ,#?#ǒߌtqӦ?;a ,#?;aW?@>H? ,5hM 6'kvf ?x U"^?x U\gk?fƸ"^?fƸC?Z"8Ӈ?O{bǑT? n {H{߉hwj?{dV?Va4V?7V@^? Lں?Ua4 Lں?_?>L ]|"%?Ț_`ؿV?&L {⿆V?%)?d%I?&L {d%I?'h'تܿZ9?ydg[Ϗ85>ab=?%HKƧb=?u??"o?$HKƧ?"o?/5~g?%}?̣J?Q.̣J?P`$k?H̞#?1瀯??k]mn?DA[{wT??j?ֆܿ?j?ś+hi?g?ֆܿ?g?>tK>va$Mâ?[*n?􇛿v,m?hk]R?.R?]R?գ Z? #D?.R? #D? 켄W?y6;?|"5cNzA&W޿ $?.1m3? $?;Rɩ?;Gx?/1m3?;Gx?BA?W?yI?ēXBuG,࿤45!?K6}?45!?T?ZAY,?K6}?ZAY,?_?`7G?=? W !?7?ih?W+N[s=dh?@ /X?WJ0 /X?DGm?)?5WJ0)?5ԋ9e"o6Y?4:V?^LE\??T^??-0׿?ر. ?v{ƽ?-0׿v{ƽ? 5S㿰`?XbF?8he[.?/*zg?h?yg?sh?h?sh?ByÿP]}V?Y.?̳5.fp-dT?J5-w@O/P?GH5?w@O/P? 5M?HH5?M?:B#=¿kR?)JJe*v?X0 V?eםs?H.?/ȟZ?+7Q]uǡR\ ?Q]uǡVXH]пR\ ?XH]пyTtŸu?vP~T0˿ ?U|:Y?mS`vbh֪M Qb?wbh֪Jt࿵8IƿM Qb?8Iƿ-i+S?+D 4 !@?tOSɛ?BYx>EDճx>gJ'ң?EDճ'ң?`ӿZ|;x?k svNU?J|.^?b,V+7Y$rp8Yܨ׀=?$rp׀=?ѯҿfS?LS) Ͽڙo9?Dc!|8?U5?H)&AB GI)&A Z?F3@B GF3@ ˝.gVss+?kZȹ%t}?eo>?j1 ZXi1 Zd=*b?`ei|&X`ei|&P5Lؿ,\?l;Att>-?h媩rs?lyҿB տlyҿ6/?G! YB տG! YLPr?U`c??N$%닆tFց>?SM1+?EjhOҿ;;ԿEjhOҿ?jn/;;Կjn/9zx&r?W"?G4b?%Ȅ@qaq?>Myd}×?r?8گ2?#^8گ2?D?|?#^|? ? *:nz?% ?C?bmۿC?ty?hߣ?bmۿhߣ?O4P?m"$?X!’Khݭ 4i?.Wei<ؿ.W:ʳ_S?ލ ¿ei<ؿލ ¿)*2?=֎ ?WwpV#CR-dw28~?=ib"mm׿>ib2x+?Q*X"mm׿Q*X~k?|KNN"iοT?.GU?ϢgG[V?]G ?.G꿛]G ?+%8 ?[Hv? [s?\<xtϠf?2?N-9B ӿ2?~<A?ATN-9B ӿATZ?6$}?+H]q?5sr<j^?fbtҮ? Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! teem-1.11.0~svn6057/include/0000775000175000017500000000000012203513752015230 5ustar domibeldomibelteem-1.11.0~svn6057/include/teemDio.h0000664000175000017500000000272612165631065017003 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* ** the end result of this is that the source file which includes ** this can be sure that TEEM_DIO is set, and can be sure that ** it is set to either 0 or 1 */ #ifndef TEEM_DIO # error TEEM_DIO not defined #elif TEEM_DIO == 1 # /* okay, its 1 */ #elif TEEM_DIO == 0 # /* okay, its 0 */ #else # error TEEM_DIO not set to 1 or 0 #endif teem-1.11.0~svn6057/include/teemQnanhibit.h0000664000175000017500000000277212165631065020206 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* ** the end result of this is that the source file which includes ** this can be sure that TEEM_QNANHIBIT is set, and can be sure that ** it is set to either 0 or 1 */ #ifndef TEEM_QNANHIBIT # error TEEM_QNANHIBIT not defined #elif TEEM_QNANHIBIT == 1 # /* okay, its 1 */ #elif TEEM_QNANHIBIT == 0 # /* okay, its 0 */ #else # error TEEM_QNANHIBIT not set to 0 or 1 #endif teem-1.11.0~svn6057/include/teemPng.h0000664000175000017500000000276712165631065017021 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* ** the end result of this is that the source file which includes ** this can be sure that TEEM_ZLIB is set, so that the required ** compression library is always built in when PNG support is requested */ #ifdef TEEM_PNG # ifndef TEEM_ZLIB # error TEEM_PNG set, but TEEM_ZLIB not set # endif #endif #if TEEM_PNG && TEEM_VTK_MANGLE #include "vtk_png_mangle.h" #endif teem-1.11.0~svn6057/python/0000775000175000017500000000000012203513752015126 5ustar domibeldomibelteem-1.11.0~svn6057/python/ctypes/0000775000175000017500000000000012203513752016435 5ustar domibeldomibelteem-1.11.0~svn6057/python/ctypes/gen-teem.py0000664000175000017500000003456512165725376020543 0ustar domibeldomibel#! /usr/bin/env python ## ## gen-teem.py: automatically-generated ctypes python wrappers for Teem ## Copyright (C) 2013, 2012, 2011 University of Chicago ## (originally created by Sam Quinan - samquinan@cs.uchicago.edu) ## ## Permission is hereby granted, free of charge, to any person obtaining ## a copy of this software and associated documentation files (the ## "Software"), to deal in the Software without restriction, including ## without limitation the rights to use, copy, modify, merge, publish, ## distribute, sublicense, and/or sell copies of the Software, and to ## permit persons to whom the Software is furnished to do so, subject to ## the following conditions: ## ## The above copyright notice and this permission notice shall be ## included in all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ## LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ## OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ## WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ## ## ## for posterity; this records the contents of the sourceme-ctypeslib.txt ## file that this script automates ... oldtodo=""" echo === These are commands that GLK used to create teem.py, in tcsh echo === Obviously, anyone is welcome to make this into a proper python script echo === or otherwise make it more robust and portable, especially the stuff echo === that removes the install path specifics from teem.py echo === echo === You will need a recent gccxml to make this work. svn co http://svn.python.org/projects/ctypes/branches/ctypeslib-gccxml-0.9 setenv CTYPES `pwd`/ctypeslib-gccxml-0.9 setenv PYTHONPATH ${CTYPES}:`pwd` echo === A patch is still needed, hopefully this can be fixed patch -p0 -i ctypes-codegen.patch echo === TEEM has to be set to wherever CMake put its make install setenv TEEM /Users/gk/teem-install echo === all.h just includes all the other public top-level .h in Teem rm -rf teemincl mkdir teemincl cp -r ${TEEM}/include/teem teemincl cd teemincl ls -1 teem/*.h | awk '{print "#include <"$1">"}' > all.h cd .. echo === teem.xml represents the Teem API python ${CTYPES}/scripts/h2xml.py `pwd`/teemincl/all.h -I teemincl -o teem.xml echo === DYLD_LIBRARY_PATH is obviously Mac specific, please fix setenv DYLD_LIBRARY_PATH ${TEEM}/lib python ${CTYPES}/scripts/xml2py.py teem.xml -llibteem.dylib -o pre-teem.py -m stdio \ -r "(air|hest|biff|nrrd|ell|unrrdu|alan|moss|tijk|gage|dye|bane|limn|echo|hoover|seek|ten|elf|pull|coil|push|mite|meet).*" rm -rf teemincl echo === At this point GLK does emacs stuff to make pre-teem.py into what is echo === the currently svn committed teem.py echo === Obviously, this really needs to be automated... """ # and now it has been automated! import os, sys, shutil, platform, re, string if len(sys.argv) != 3: sys.exit("usage: gen-teem.py ") ## (TEEM_LIB_LIST) libs_list = ["air", "hest", "biff", "nrrd", "ell", "unrrdu", "alan", "moss", "tijk", "gage", "dye", "bane", "limn", "echo", "hoover", "seek", "ten", "elf", "pull", "coil", "push", "mite", "meet"] # # validate os # if os.name != "posix": sys.exit("program only supports posix compilant systems at this time") # # validate gccxml install # tmp = os.path.join(os.getcwd(), "tmp") os.system("gccxml --version &> tmp") file = open(tmp, "r") file.seek(0) first_line = file.readline() file.close() if not first_line.startswith("GCC-XML version"): os.remove(tmp) sys.exit("error: gccxml of version >= 0.9.0 is required for this script") if not ((int(first_line[16]) > 0) or ((int(first_line[18]) == 9) and (int(first_line[20]) >= 0))): os.remove(tmp) sys.exit("error: gccxml of version >= 0.9.0 is required for this script") os.remove(tmp) # # validate ctypeslib-gccxml source dir path # if not os.path.isdir(sys.argv[1]): sys.exit("%s does not point to a directory" % sys.argv[1]) CTYPES = os.path.abspath(sys.argv[1]) # # NOTE, however, that this really assumes a PATCHED ctypeslib-gccxml, # via a procedure like: # svn co http://svn.python.org/projects/ctypes/branches/ctypeslib-gccxml-0.9 # patch -p0 -i ctypes-codegen.patch # # validate teem install dir path # if not os.path.isdir(sys.argv[2]): sys.exit("%s does not point to a directory" % sys.argv[2]) TEEM = os.path.abspath(sys.argv[2]) # # copy files from install dir # TMP_DIR = "gen-teem-tmp" teem_include = os.path.join(TEEM, "include") if not os.path.isdir(teem_include): sys.exit("%s is not the teem install directory; %s is not a valid path" % (teem, teem-install)) if os.path.isdir(TMP_DIR): shutil.rmtree(TMP_DIR) shutil.copytree(teem_include, TMP_DIR) # # generate all.h # all_h = os.path.join(TMP_DIR, "all.h") f_open = open (all_h, "w") defines = [] Files = os.listdir(os.path.join(TMP_DIR, "teem")) for file in Files: if file.endswith(".h"): f_open.write("#include %s" % (file, os.linesep)) # # get #define statements from file # lines = open (os.path.join(TMP_DIR, "teem", file), "r").readlines() expr1 = re.compile("^#define") expr2 = re.compile("\\\\") expr3 = re.compile("HAS_BEEN_INCLUDED") expr4 = re.compile("##") expr5 = re.compile("^#define [^ ]*\(") expr6 = re.compile("NRRD_TYPE_BIGGEST") # this strips out NRRD_LLONG_MAX, NRRD_LLONG_MIN, and NRRD_ULLONG_MAX, # which depend on macros AIR_LLONG and AIR_LLONG. For some reason # this causes a problem now (Thu Jun 14 11:49:14 CDT 2012) even though # they haven't before, though these constants and macros are not new expr7 = re.compile("LLONG") for line in lines: if (expr1.search(line) and not expr2.search(line) and not expr3.search(line) and not expr4.search(line) and not expr5.search(line) and not expr6.search(line) and not expr7.search(line)): firstword, restwords = string.replace(string.replace(line[8:], "/*", "#" ), "*/", "").split(None,1) defines.append("%s = %s" % (firstword, restwords)) f_open.close() # # generate teem.xml # # note: these calls are unix only because windows support for command line # append to python path (where the scopeis only that one call) is too # difficult at this time -- better support may be added to later versions # of python, so that something to watch for / modify in the future pypath_append = "PYTHONPATH=%s:%s" % (CTYPES, os.getcwd()) teem_xml = os.path.join(os.getcwd(), "teem.xml") os.system("%s python %s %s -I %s -o %s" % (pypath_append, os.path.join(CTYPES, "scripts", "h2xml.py"), os.path.abspath(all_h), TMP_DIR, teem_xml)) # # generate pre-teem.py # pre_teem_py = os.path.join(os.getcwd(), "pre-teem.py") teem_libs = '|'.join(libs_list) system_type = platform.system() dll_path = "" ext = "" substr = "" if system_type == "Darwin": dll_path = "DYLD_LIBRARY_PATH=%s" % os.path.join(TEEM, "lib") ext = "dylib" substr = "_libraries['%s']" % os.path.join(TEEM, "lib", "libteem.dylib") else: dll_path = "LD_LIBRARY_PATH=%s" % os.path.join(TEEM, "lib") ext = "so" substr = "_libraries['libteem.so']" os.system("%s %s python %s %s -l libteem.%s -o %s -m stdio -r \"(%s).*\"" % (dll_path, pypath_append, os.path.join(CTYPES, "scripts", "xml2py.py"), teem_xml, ext, pre_teem_py, teem_libs)) # # generate teem.py # libs_destuctable = list(libs_list) contents = open(pre_teem_py, "r").readlines()[8:] mod_contents = [] for line in contents: l = line.replace(substr, "libteem") if not (("__darwin_size_t = c_ulong" in l) or ("size_t = __darwin_size_t" in l)): l2 = l.replace("\'__darwin_size_t\',", "") #designed to remove defs from long list at end l3 = l2.replace("\'size_t\',", "") #designed to remove defs from long list at end l = l3 if not re.compile("_size_t").search(l3): if not re.match("#", l3): # i.e. do not make changes in commented lines l = l3.replace("size_t", "c_size_t") mod_contents.append(l) if "Present" in l: for lib in libs_destuctable: lib_str = lib+"Present" if lib_str in l: libs_destuctable.remove(lib) break # in experimental libs not included, cleanup and fail if libs_destuctable: # empty sequence implicity false shutil.rmtree(TMP_DIR) os.remove(teem_xml) #os.remove(pre_teem_py) sys.exit("ERROR: experimental libs: %s not turned on - please rebuild teem with BUILD_EXPERIMENTAL_LIBS turned on, then re-run gen-teem.py" % ','.join(libs_destuctable)) header = [ "##", "## teem.py: automatically-generated ctypes python wrappers for Teem", "## Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago", "##", "## Permission is hereby granted, free of charge, to any person obtaining", "## a copy of this software and associated documentation files (the", "## \"Software\"), to deal in the Software without restriction, including", "## without limitation the rights to use, copy, modify, merge, publish,", "## distribute, sublicense, and/or sell copies of the Software, and to", "## permit persons to whom the Software is furnished to do so, subject to", "## the following conditions:", "##", "## The above copyright notice and this permission notice shall be", "## included in all copies or substantial portions of the Software.", "##", "## THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", "## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", "## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", "## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE", "## LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", "## OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION", "## WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", "##", "##############################################################", "##############################################################", "#### NOTE: This teem.py file is automatically produced by", "#### teem/python/ctypes/gen-teem.py. Necessary changes to ", "#### teem.py should be made in gen-teem.py, not here.", "##############################################################", "##############################################################", "", "from ctypes import *", "import ctypes.util", "import sys, os", "", "def load_library(libname, loader_path=\"\"):", " ext = os.path.splitext(libname)[1]", " if not ext:", " # Try to load library with platform-specific name", " if sys.platform == 'win32':", " libname_ext = '%s.dll' % libname", " elif sys.platform == 'darwin':", " libname_ext = '%s.dylib' % libname", " elif sys.platform == 'linux2':", " libname_ext = '%s.so' % libname", " else:" " libname_ext = libname", "", " if (loader_path != \"\"):", " loader_path = os.path.abspath(loader_path)", " if not os.path.isdir(loader_path):", " libdir = os.path.dirname(loader_path)", " else:", " libdir = loader_path", " else:", " libdir = loader_path", "", " try:", " libpath = os.path.join(libdir, libname_ext)", " return CDLL(libpath)", " except OSError, e:", " raise e" "", "try:", " libteem = load_library('libteem')", "except OSError:", " print \"**\"", " print \"** teem.py couldn't find and load the \\\"libteem\\\" shared library.\"", " print \"**\"", " print \"** try setting optional loader_path argument in the load_library() call above to '/lib/'\"", " print \"**\"", " raise ImportError", "", "# =============================================================", "# Utility types and classes to help teem.py be platform-independent.", "", "STRING = c_char_p", "", "class FILE(Structure):", " pass", "", "# oddly, size_t is in ctypes, but not ptrdiff_t", "# which is probably a bug", "if sizeof(c_void_p) == 4:", " ptrdiff_t = c_int32", "elif sizeof(c_void_p) == 8:", " ptrdiff_t = c_int64", "", "# =============================================================", "# What follows are all the functions, struct definitions, globals, ", "# enum values, and typedefs in Teem. This is generated by ctypeslib:", "# http://svn.python.org/projects/ctypes/branches/ctypeslib-gccxml-0.9", "# followed by further post-processing and filtering.", "# See end of this file for definitions of stderr, stdin, stdout", ] footer = [ "# =============================================================", "# Make sure this shared library will work on this machine.", "if not nrrdSanity():", " errstr = biffGetDone(NRRD)", " print \"**\"", " print \"** Sorry, there is a problem (described below) with the \"", " print \"** Teem shared library that prevents its use. This will \"", " print \"** have to be fixed by recompiling the Teem library for \"", " print \"** this platform. \"", " print \"**\"", " print \"** %s\" % errstr", " raise ImportError", "", "# =============================================================", "# Its nice to have these FILE*s around for utility use, but they ", "# aren't available in a platform-independent way in ctypes. These ", "# air functions were created for this purpose.", "stderr = airStderr()", "stdout = airStdout()", "stdin = airStdin()", ] teem_py = os.path.join(os.getcwd(), "teem.py") if os.path.exists(teem_py): os.remove(teem_py) out = open(teem_py, "w") for line in header: out.write(line) out.write(os.linesep) out.writelines(mod_contents) out.write(os.linesep) out.write("# =============================================================\n") out.write("# What follows are the all #define's in Teem, excluding macros,\n") out.write("# and #defines that depend on compile-time tests done by the\n") out.write("# C pre-processor.\n") out.write("# This is created by something akin to grep'ing through the\n") out.write("# public header files, with some extra filters.\n") out.write(os.linesep) out.writelines(defines) out.write(os.linesep) for line in footer: out.write(line) out.write(os.linesep) out.close() # # cleanup # shutil.rmtree(TMP_DIR) os.remove(teem_xml) os.remove(pre_teem_py) teem-1.11.0~svn6057/python/ctypes/go-gen-teem.sh0000775000175000017500000000504312165725557021121 0ustar domibeldomibel#!/usr/bin/env bash # # Teem: Tools to process and visualize scientific data and images . # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # set -o nounset # The various directories used for this process # directory used for making source modifications TEEM_SRC=~/teem # clean check-out; no other modifications TEEM_SVN=~/teem-svn # CMake build directory for teem-svn TEEM_SVN_BUILD=~/teem-svn-build # target of "make install" from teem-svn-build TEEM_SVN_INSTALL=~/teem-svn-install if [[ ! (-d "$TEEM_SRC" && -d "$TEEM_SVN" && -d "$TEEM_SVN_BUILD" && -d "$TEEM_SVN_INSTALL") ]]; then echo "Not all of following directories exist:" echo " TEEM_SRC=$TEEM_SRC" echo " TEEM_SVN=$TEEM_SVN" echo " TEEM_SVN_BUILD=$TEEM_SVN_BUILD" echo " TEEM_SVN_INSTALL=$TEEM_SVN_INSTALL" echo "Sorry for the inconvenience. This script is used mainly by GLK" echo "to periodicaly refresh teem.py, especially prior to releases," echo "and it isn't more generally useful (yet)." exit 1 fi function doo { echo "==== $1" eval $1 ret=$? if [ $ret != 0 ]; then echo "==== ERROR (status $ret)" exit $ret fi } doo "cd $TEEM_SVN" doo "svn update" doo "cd $TEEM_SVN_BUILD" doo "make" doo "make install" doo "cd $TEEM_SRC/python/ctypes" doo "python gen-teem.py ctypeslib-gccxml-0.9 $TEEM_SVN_INSTALL" dfile=$(mktemp /tmp/svndiff.XXXXXXXXX) doo "svn diff teem.py | tee $dfile" if [[ -s $dfile ]]; then echo "====" echo "==== NOTE: There were new differences; consider \"svn commit teem.py\"" echo "====" fi rm -f $dfile teem-1.11.0~svn6057/python/ctypes/teem.py0000664000175000017500000153417712165725611017771 0ustar domibeldomibel## ## teem.py: automatically-generated ctypes python wrappers for Teem ## Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago ## ## Permission is hereby granted, free of charge, to any person obtaining ## a copy of this software and associated documentation files (the ## "Software"), to deal in the Software without restriction, including ## without limitation the rights to use, copy, modify, merge, publish, ## distribute, sublicense, and/or sell copies of the Software, and to ## permit persons to whom the Software is furnished to do so, subject to ## the following conditions: ## ## The above copyright notice and this permission notice shall be ## included in all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ## LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ## OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ## WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ## ############################################################## ############################################################## #### NOTE: This teem.py file is automatically produced by #### teem/python/ctypes/gen-teem.py. Necessary changes to #### teem.py should be made in gen-teem.py, not here. ############################################################## ############################################################## from ctypes import * import ctypes.util import sys, os def load_library(libname, loader_path=""): ext = os.path.splitext(libname)[1] if not ext: # Try to load library with platform-specific name if sys.platform == 'win32': libname_ext = '%s.dll' % libname elif sys.platform == 'darwin': libname_ext = '%s.dylib' % libname elif sys.platform == 'linux2': libname_ext = '%s.so' % libname else: libname_ext = libname if (loader_path != ""): loader_path = os.path.abspath(loader_path) if not os.path.isdir(loader_path): libdir = os.path.dirname(loader_path) else: libdir = loader_path else: libdir = loader_path try: libpath = os.path.join(libdir, libname_ext) return CDLL(libpath) except OSError, e: raise e try: libteem = load_library('libteem') except OSError: print "**" print "** teem.py couldn't find and load the \"libteem\" shared library." print "**" print "** try setting optional loader_path argument in the load_library() call above to '/lib/'" print "**" raise ImportError # ============================================================= # Utility types and classes to help teem.py be platform-independent. STRING = c_char_p class FILE(Structure): pass # oddly, size_t is in ctypes, but not ptrdiff_t # which is probably a bug if sizeof(c_void_p) == 4: ptrdiff_t = c_int32 elif sizeof(c_void_p) == 8: ptrdiff_t = c_int64 # ============================================================= # What follows are all the functions, struct definitions, globals, # enum values, and typedefs in Teem. This is generated by ctypeslib: # http://svn.python.org/projects/ctypes/branches/ctypeslib-gccxml-0.9 # followed by further post-processing and filtering. # See end of this file for definitions of stderr, stdin, stdout gageVecCurlGradient = 19 tenEstimate2MethodUnknown = 0 miteValYi = 4 baneMeasrLaplacian = 5 baneMeasrGradMag = 4 gageVecDivGradient = 18 baneMeasrValueZeroCentered = 2 gageSclHessFrob = 10 baneClipAbsolute = 1 baneMeasrTotalCurv = 7 miteRangeKa = 5 nrrdResampleNonExistentLast = 4 tenGlyphTypeLast = 7 limnPolyDataInfoTang = 4 nrrdIoStateBareText = 2 baneClipUnknown = 0 limnPolyDataInfoNorm = 2 nrrdResampleNonExistentUnknown = 0 echoTypeSuperquad = 2 baneMeasr2ndDD = 6 limnPolyDataInfoUnknown = 0 nrrdFFTWPlanRigorExhaustive = 4 limnSpaceDevice = 4 limnSpaceScreen = 3 nrrdKindCovariantVector = 7 baneRangeLast = 5 limnSpaceView = 2 nrrdTernaryOpInOpen = 13 nrrdTernaryOpExists = 12 nrrdBinaryOpLast = 24 pullInfoHeightLaplacian = 9 echoJitterRandom = 3 nrrdTernaryOpClamp = 9 miteValUnknown = 0 nrrdTernaryOpGTSmooth = 8 gageVecLength = 5 nrrdTernaryOpLTSmooth = 7 tenGageModeHessianEvec2 = 132 nrrdTernaryOpMaxSmooth = 6 nrrdBinaryOpEqual = 18 nrrdTernaryOpMin = 3 nrrdBinaryOpGT = 15 baneMeasrValueAnywhere = 3 nrrdTernaryOpUnknown = 0 nrrdBinaryOpMin = 11 gageSclLaplacian = 9 tenGageOmegaHessianEvec = 139 nrrdBinaryOpMod = 8 ell_cubic_root_single = 1 nrrdBinaryOpFlippedSgnPow = 7 miteRangeEmissivity = 4 pullInterTypeUnivariate = 2 nrrdIoStateDetachedHeader = 1 alanTextureTypeLast = 3 echoTypeCylinder = 1 nrrdBinaryOpAdd = 1 tenGageOmega2ndDD = 144 nrrdTernaryOpInClosed = 14 tenFiberParmLast = 5 pullStatusLast = 5 pullStatusEdge = 4 pullStatusNixMe = 3 pullStatusUnknown = 0 limnPolyDataInfoLast = 5 miteRangeBlue = 3 pullSysParmUnknown = 0 gageVecNormalized = 6 nrrdUnaryOpLast = 31 limnPolyDataInfoTex2 = 3 echoJitterGrid = 1 tenGageModeHessianEvec1 = 131 nrrdResampleNonExistentRenormalize = 2 tenGageModeHessianEvec0 = 130 gageVecVector2 = 4 tenFiberParmUseIndexSpace = 2 nrrdFFTWPlanRigorPatient = 3 tenFiberParmStepSize = 1 pullSysParmAlpha = 1 nrrdFFTWPlanRigorLast = 5 tenGageModeHessianEval2 = 128 nrrdTernaryOpLast = 17 pullConstraintFailProjGradZeroB = 2 gageVecVector = 1 nrrdUnaryOpOne = 30 unrrduScaleSubtract = 5 nrrdKindList = 4 gageItemPackPartHessEvec1 = 10 nrrdTernaryOpRician = 16 pullInfoQuality = 23 miteValWdotD = 19 nrrdOriginStatusUnknown = 0 gageItemPackPartHessEvec0 = 9 gageVecVector0 = 2 pullFlagUseBetaForGammaLearn = 3 tenGageModeHessianEval = 125 nrrdUnaryOpRoundDown = 22 gageSclNPerp = 6 gageItemPackPartHessEval1 = 7 nrrdUnaryOpRoundUp = 21 tenFiberTypeEvec0 = 1 nrrdUnaryOpCeil = 19 nrrdUnaryOpNerf = 18 nrrdUnaryOpLog1p = 13 tenInterpTypeUnknown = 0 pullConstraintFailProjGradZeroA = 1 nrrdUnaryOpLog10 = 12 nrrdUnaryOpLog2 = 11 nrrdUnaryOpLog = 10 nrrdKindTime = 3 nrrdUnaryOpAtan = 8 pullEnergyTypeQuarticWell = 10 nrrdTernaryOpLerp = 11 nrrdUnaryOpAcos = 7 nrrdField_space_units = 29 nrrdUnaryOpAsin = 6 pullConstraintFailUnknown = 0 nrrdUnaryOpTan = 5 nrrdUnaryOpCos = 4 gageItemPackPartNormal = 4 nrrdBinaryOpRicianRand = 23 tenGageAniso = 207 miteStageOpUnknown = 0 tenGageCl1Hessian = 178 tenGageCa1HessianEvec = 201 tenGageCa1HessianEval2 = 200 tenGageCa1HessianEval0 = 198 nrrdBinaryOpIf = 21 tenGageCa1HessianEval = 197 nrrdSpace3DLeftHandedTime = 12 tenGageCp1HessianEvec2 = 195 tenGageCp1HessianEvec1 = 194 pullFlagLast = 16 tenGageCa1HessianEvec1 = 203 nrrdBinaryOpExists = 20 tenGageCp1HessianEval2 = 191 pullCondConstraintFail = 5 tenGageCp1HessianEval1 = 190 airFP_POS_NORM = 5 nrrdUnaryOpIf = 28 tenGageCp1Hessian = 187 nrrdBinaryOpNotEqual = 19 tenInterpTypeLast = 12 tenInterpTypeRThetaPhiLinear = 11 tenInterpTypeQuatGeoLoxR = 10 gageVecUnknown = 0 tenInterpTypeLoxR = 8 nrrdTernaryOpMax = 5 tenGageCl1HessianEval = 179 pullFlagNoPopCntlWithZeroAlpha = 2 pullFlagPermuteOnRebin = 1 tenGageTensorGradRotE = 176 baneIncUnknown = 0 nrrdBinaryOpCompare = 17 tenInterpTypeLinear = 1 limnDeviceGL = 2 tenGageCp1GradVec = 170 nrrdBinaryOpGTE = 16 tenGageCl1GradVec = 167 tenGlyphTypeBox = 1 pullInfoIsovalueHessian = 21 pullInfoIsovalueGradient = 20 pullSysParmEnergyDecreasePopCntlMin = 14 tenGageCa1HessianEvec0 = 202 pullCondEnergyTry = 4 limnSplineInfoQuaternion = 6 nrrdUnaryOpNormalRand = 27 tijk_class_esh = 2 tijk_class_tensor = 1 tijk_class_unknown = 0 seekTypeLast = 12 tenGageModeHessianEval0 = 126 seekTypeValleySurfaceT = 11 tenGageFARidgeLineAlignment = 110 nrrdBinaryOpMax = 12 seekTypeValleySurfaceOP = 10 tenGageFAHessianEvalMode = 109 pullInfoTangent1 = 15 limnSplineInfo2Vector = 2 pullInfoLiveThresh2 = 13 pullInfoSeedThresh = 11 gageSclHessianTen = 8 pullCondConstraintSatB = 3 pullInfoHeightHessian = 8 seekTypeRidgeSurfaceOP = 8 pullInfoHeightGradient = 7 nrrdUnaryOpRand = 26 pullInfoHeight = 6 pullInfoInsideGradient = 5 pullInfoInside = 4 nrrdBinaryOpFmod = 9 pullInfoTensorInverse = 2 pullInfoTensor = 1 pullInfoUnknown = 0 seekTypeMinimalSurface = 6 airMopAlways = 3 airMopOnOkay = 2 nrrdBinaryOpSgnPow = 6 seekTypeRidgeLine = 4 pullConstraintFailLast = 5 pullConstraintFailTravel = 4 pullConstraintFailIterMaxed = 3 pullPropLast = 18 nrrdBoundaryWeight = 4 seekTypeValleySurface = 3 nrrdBoundaryBleed = 2 nrrdUnaryOpExists = 25 nrrdBoundaryPad = 1 nrrdBoundaryUnknown = 0 pullInterTypeUnknown = 0 baneIncRangeRatio = 2 nrrdBinaryOpMultiply = 3 airNoDio_fpos = 9 airNoDio_ptr = 8 airNoDio_size = 7 airNoDio_dioinfo = 5 nrrdBinaryOpSubtract = 2 airNoDio_fd = 4 airNoDio_std = 3 seekTypeUnknown = 0 airNoDio_okay = 0 alanTextureTypeTuring = 1 tenGageCa1HessianEval1 = 199 alanTextureTypeUnknown = 0 pullPropStability = 17 nrrdUnaryOpSgn = 24 nrrdIoStateKeepNrrdDataFileOpen = 6 airFP_POS_ZERO = 9 nrrdFFTWPlanRigorUnknown = 0 airFP_POS_DENORM = 7 airFP_NEG_NORM = 6 nrrdIoStateSkipData = 5 airFP_NEG_INF = 4 airFP_POS_INF = 3 airFP_QNAN = 2 airFP_SNAN = 1 airFP_Unknown = 0 dyeSpaceLast = 7 dyeSpaceLUV = 6 nrrdIoStateCharsPerLine = 3 dyeSpaceLAB = 5 dyeSpaceXYZ = 4 dyeSpaceRGB = 3 dyeSpaceHSL = 2 dyeSpaceHSV = 1 pullCondUnknown = 0 dyeSpaceUnknown = 0 alanParmWrapAround = 21 tenGageTensorQuatGeoLoxK = 164 pullFlagUnknown = 0 tenGageTensorLogEuclidean = 163 nrrdZlibStrategyDefault = 1 tenGageCovarianceKGRT = 162 tenGageCovariance = 160 tenGageConfDiffusionFraction = 159 tenGageConfDiffusionAlign = 158 tenGageConfGradVecDotEvec0 = 157 tenGageOmegaDiffusionFraction = 156 tenGageOmegaDiffusionAlign = 155 tenGageOmegaGradVecDotEvec0 = 154 tenGageFADiffusionFraction = 153 tenGageFADiffusionAlign = 152 tenGageFAGradVecDotEvec0 = 151 tenGageTraceDiffusionAlign = 149 tenGageTraceGradVecDotEvec0 = 148 nrrdKindDomain = 1 tenGageOmegaHessianContrTenEvec2 = 147 tenGageOmegaHessianContrTenEvec0 = 145 tenGageOmegaLaplacian = 143 pullCountProbe = 7 tenGageOmegaHessianEvec2 = 142 pullSysParmBackStepScale = 13 tenGageOmegaHessianEvec1 = 141 pullPropNeighCovarTrace = 15 tenGageOmegaHessianEvec0 = 140 tenGageOmegaHessianEval2 = 138 tenGageOmegaHessianEval1 = 137 tenGageOmegaHessianEval0 = 136 tenGageOmegaHessianEval = 135 pullFlagPopCntlEnoughTest = 9 tenGageOmegaHessian = 134 coilMethodTypeModifiedCurvatureRings = 5 tenGageModeHessianFrob = 133 tenFiberParmVerbose = 4 tenFiberParmWPunct = 3 tenGageModeHessianEvec = 129 tenGageModeHessianEval1 = 127 tenGageModeHessian = 124 tenGageRHessian = 123 tenGageFAFlowlineCurv = 122 tenGageFACurvDir2 = 121 tenGageFACurvDir1 = 120 tenGageFAGaussCurv = 119 pullCountEnergyFromImage = 3 tenGageFAMeanCurv = 118 tenGageFAShapeIndex = 117 tenGageFATotalCurv = 116 pushEnergyTypeCotan = 4 tenGageFAKappa2 = 115 tenGageFAKappa1 = 114 tenGageFAGeomTens = 113 tenGageFA2ndDD = 112 tenGageFARidgeSurfaceAlignment = 111 tenGageFALaplacian = 108 tenFiberTypeUnknown = 0 tenGageFAValleySurfaceStrength = 107 tenGageFARidgeSurfaceStrength = 106 pullInterTypeLast = 5 airTypeLongInt = 4 pullStatusNewbie = 2 nrrdFormatTypeVTK = 4 pullStatusStuck = 1 nrrdFormatTypeLast = 7 gagePvlFlagVolume = 1 gagePvlFlagUnknown = 0 nrrdFormatTypeEPS = 6 pushEnergyTypeZero = 5 tenDwiGageLast = 36 tenDwiGage2TensorPeledError = 33 tenDwiGage2TensorQSegAndError = 31 tenDwiGage2TensorQSeg = 29 pullIterParmPopCntlPeriod = 5 tenDwiGageFA = 27 tenDwiGageConfidence = 26 gagePvlFlagLast = 4 tenDwiGageTensorLikelihood = 25 tenGageFAHessianEvec1 = 103 tenDwiGageTensor = 22 pullIterParmConstraintMax = 4 tenDwiGageTensorMLELikelihood = 21 tenDwiGageTensorMLEErrorLog = 20 gagePvlFlagNeedD = 3 tenDwiGageTensorMLEError = 19 pushEnergyTypeSpring = 1 gagePvlFlagQuery = 2 tenDwiGageTensorWLSLikelihood = 13 tenDwiGageTensorWLSErrorLog = 12 tenDwiGageTensorLLSLikelihood = 9 tenDwiGageTensorLLSErrorLog = 8 airEndianLast = 4322 pullTraceStopLength = 4 gageSclHessEval = 11 pullTraceStopConstrFail = 2 pullTraceStopUnknown = 0 gageSclHessian = 7 tenGageCp1HessianEvec0 = 193 pullSourceLast = 3 pullSourceProp = 2 gageSclNormal = 4 pullSourceUnknown = 0 gageSclGradVec = 2 pullEnergyTypeLast = 14 pullEnergyTypeButterworthParabola = 13 pullEnergyTypeZero = 12 pullEnergyTypeHepticWell = 11 pullPropNeighDistMean = 9 pullEnergyTypeBetterCubicWell = 9 pullEnergyTypeCubicWell = 8 gageKernelStack = 7 pullEnergyTypeCubic = 6 pullEnergyTypeCotan = 5 pullEnergyTypeButterworth = 4 pullEnergyTypeBspln = 3 pullEnergyTypeGauss = 2 pullEnergyTypeSpring = 1 gageKernelUnknown = 0 gageCtxFlagLast = 7 airTypeSize_t = 6 gageCtxFlagShape = 6 gageCtxFlagRadius = 5 gageCtxFlagKernel = 4 gageCtxFlagNeedK = 3 tenDwiGageAll = 1 gageCtxFlagK3Pack = 2 gageErrLast = 7 pullSysParmOpporStepScale = 12 gageErrStackUnused = 6 gageErrStackSearch = 5 gageErrStackIntegral = 4 gageErrUnknown = 0 coilMethodTypeUnknown = 0 gageParmLast = 15 gageParmGenerateErrStr = 14 gageParmOrientationFromSpacing = 13 gageParmStackNormalizeRecon = 12 gageParmStackNormalizeDerivBias = 11 gageParmStackNormalizeDeriv = 10 coilMethodTypeLast = 9 coilMethodTypeFinish = 8 tenTripleTypeK = 7 coilMethodTypeSelf = 7 coilMethodTypeCurvatureFlow = 6 coilMethodTypeModifiedCurvature = 4 coilMethodTypePeronaMalik = 3 echoTypeLast = 12 coilMethodTypeHomogeneous = 2 gageKernel22 = 6 coilMethodTypeTesting = 1 tenFiberStopStub = 8 nrrdEncodingTypeLast = 6 nrrdTypeBlock = 11 nrrdMeasureLast = 30 nrrdMeasureHistoSD = 29 nrrdMeasureHistoVariance = 28 nrrdUnaryOpErf = 17 nrrdMeasureHistoSum = 26 nrrdMeasureHistoProduct = 25 baneMeasrFlowlineCurv = 8 nrrdEncodingTypeGzip = 4 tenDwiGage2TensorPeledLevmarInfo = 35 tenTripleTypeJ = 6 tenDwiGage2TensorPeledAndError = 34 tenEstimate2MethodQSegLLS = 1 alanParmLast = 22 miteRangeRed = 1 alanParmAlpha = 18 tenFiberStopFraction = 7 alanParmMaxPixelChange = 17 airTypeLast = 13 tenDwiGage2TensorPeled = 32 alanParmF = 15 airTypeEnum = 11 alanParmReact = 13 alanParmDiffB = 12 airTypeDouble = 8 nrrdUnaryOpCbrt = 16 tenGageTensorGradMag = 31 airTypeFloat = 7 alanParmDeltaT = 9 echoMatterMetalKd = 2 airTypeULongInt = 5 alanParmMaxIteration = 7 alanParmSaveInterval = 6 alanParmHomogAniso = 5 alanParmFrameInterval = 4 tenDwiGage2TensorQSegError = 30 alanParmNumThreads = 3 alanParmTextureType = 2 alanParmVerbose = 1 gageVecHelGradient = 22 alanParmUnknown = 0 nrrdHasNonExistUnknown = 3 nrrdMeasureHistoMode = 24 nrrdMeasureHistoMedian = 23 nrrdMeasureHistoMean = 22 nrrdMeasureHistoMax = 21 baneIncPercentile = 3 nrrdMeasureLineError = 19 tenDwiGageTensorAllDWIError = 28 nrrdMeasureLineIntercept = 18 nrrdMeasureSkew = 16 tenFiberStopBounds = 6 nrrdMeasureVariance = 13 nrrdUnaryOpZero = 29 tenGageCp1HessianEval0 = 189 nrrdMeasureNormalizedL2 = 10 nrrdMeasureL1 = 8 airInsane_nInfExists = 3 nrrdMeasureProduct = 6 nrrdMeasureMode = 5 nrrdMeasureMedian = 4 nrrdMeasureMean = 3 tenGageFAHessianEval1 = 99 nrrdMeasureMax = 2 gageParmKernelIntegralNearZero = 7 nrrdMeasureMin = 1 nrrdField_old_min = 21 nrrdMeasureUnknown = 0 nrrdOriginStatusLast = 5 nrrdOriginStatusOkay = 4 nrrdOriginStatusNoMaxOrSpacing = 3 nrrdOriginStatusNoMin = 2 nrrdOriginStatusDirection = 1 tenDwiGageTensorErrorLog = 24 airTypeBool = 1 nrrdField_data_file = 32 nrrdField_sample_units = 28 nrrdField_keyvalue = 27 nrrdTypeFloat = 9 nrrdField_byte_skip = 26 nrrdField_line_skip = 25 nrrdField_encoding = 24 nrrdField_endian = 23 tenGageQHessian = 95 nrrdFormatTypeText = 5 airInsane_pInfExists = 2 nrrdField_max = 20 nrrdField_min = 19 nrrdField_units = 18 tenGageFAHessianEval = 97 nrrdField_kinds = 16 hooverErrLast = 11 nrrdAxisInfoLabel = 9 pullPropForce = 8 nrrdField_axis_mins = 12 nrrdField_thicknesses = 11 nrrdField_sizes = 9 nrrdField_space = 7 nrrdField_dimension = 6 nrrdField_block_size = 5 tenInterpTypeAffineInvariant = 3 nrrdField_type = 4 pullSysParmEnergyIncreasePermit = 15 nrrdField_number = 3 nrrdField_content = 2 gageKernel10 = 2 nrrdField_comment = 1 hooverErrThreadEnd = 8 nrrdKind3DMaskedMatrix = 31 nrrdKind3DMatrix = 30 nrrdKind3DMaskedSymMatrix = 29 nrrdKind3DSymMatrix = 28 airInsane_endian = 1 nrrdKind2DSymMatrix = 24 nrrdKind3Normal = 21 hooverErrSample = 6 nrrdKind3Gradient = 20 nrrdKind3Vector = 19 tenDwiGageTensorNLSErrorLog = 16 nrrdKindRGBAColor = 18 nrrdKind4Color = 17 nrrdKindXYZColor = 16 nrrdKindHSVColor = 15 nrrdKindRGBColor = 14 nrrdKind3Color = 13 nrrdKind2Vector = 12 nrrdKindComplex = 11 echoJittableLast = 7 nrrdKindScalar = 10 nrrdKindStub = 9 tenGageTraceHessianEvec = 87 echoTypeInstance = 11 tenDwiGageTensorNLS = 14 nrrdKindVector = 6 nrrdKindPoint = 5 pullSysParmBinWidthSpace = 9 nrrdKindSpace = 2 tenDwiFiberTypeLast = 4 nrrdKindUnknown = 0 echoMatterUnknown = 0 tenGageCl1HessianEvec2 = 186 tenGageTraceHessianEval1 = 85 airInsane_not = 0 tenGageTraceHessianEval0 = 84 pullSysParmStepInitial = 6 tenDwiGageTensorWLS = 10 echoTypeCube = 3 pullTraceStopLast = 6 tenGageCl1HessianEvec1 = 185 tenGageClpmin2 = 80 tenDwiGageTensorLLSError = 7 echoTypeSphere = 0 limnSplineInfoLast = 7 gageParmCheckIntegrals = 3 nrrdAxisInfoSpaceDirection = 6 limnSplineInfoNormal = 4 seekTypeRidgeSurfaceT = 9 tenGageCp2 = 78 seekTypeMaximalSurface = 7 mossFlagLast = 2 mossFlagKernel = 1 echoMatterGlassKa = 1 seekTypeRidgeSurface = 2 airEndianUnknown = 0 tenFiberParmUnknown = 0 echoMatterGlassIndex = 0 nrrdIoStateValsPerLine = 4 pullTraceStopSpeeding = 1 tenGageCl1HessianEvec0 = 184 tenGageCa1 = 75 baneMeasrValuePositive = 1 tenGageCl1 = 73 tenDwiGageUnknown = 0 nrrdUnaryOpAbs = 23 nrrdFormatTypePNG = 3 nrrdFormatTypePNM = 2 gageSclNProj = 5 nrrdFormatTypeUnknown = 0 miteShadeMethodPhong = 2 pullSourceGage = 1 tenGageCl1HessianEvec = 183 pullPropIdtag = 1 tenGageEvalHessian = 177 mossFlagImage = 0 gageSclGradMag = 3 baneMeasrUnknown = 0 tenGageInvarRGradMags = 69 gageParmVerbose = 1 tenGageFAHessianFrob = 105 tenGageFAHessianEvec2 = 104 nrrdUnaryOpFloor = 20 tenGageFAHessianEvec0 = 102 tenGageFAHessianEvec = 101 pullPropNeighTanCovar = 13 tenGageFAHessianEval2 = 100 gageSclValue = 1 tenGageFAHessianEval0 = 98 pullSysParmLast = 20 tenGageFAHessian = 96 pullSysParmWall = 18 tenGageInvarKGradMags = 67 tenGageSHessian = 94 pullSysParmEnergyDecreaseMin = 16 gageSclUnknown = 0 tenGageBHessian = 92 tenGageTraceHessianFrob = 91 unrrduScaleExact = 7 echoTypeSplit = 9 echoTypeAABBox = 8 tenGageTraceHessianEval2 = 86 echoTypeTriMesh = 6 tenGageCl1HessianEval2 = 182 echoTypeRectangle = 5 pullPropUnknown = 0 echoTypeTriangle = 4 tenGageDelNormK3 = 24 unrrduScaleAspectRatio = 6 pullSysParmSeparableGammaLearnRescale = 4 pullSysParmGamma = 3 tenGageCa2 = 79 echoTypeUnknown = -1 nrrdIoStateLast = 10 tenGageCl2 = 77 tenGageOmegaGradMag = 64 gageParmUnknown = 0 tenGageCp1 = 74 nrrdUnaryOpSqrt = 15 pullPropNeighCovarDet = 16 tenGageRotTans = 70 pullPropNeighInterNum = 14 pullPropNeighCovar7Ten = 12 nrrdUnaryOpExpm1 = 14 pullPropNeighCovar = 11 pullPropScale = 10 echoTypeList = 10 tenGageOmegaGradVec = 63 gageKernelLast = 8 pullPropPosition = 7 pullPropStuck = 6 tenGageModeNormal = 59 tenGageThetaGradMag = 61 tenGageModeGradMag = 58 pullPropIdCC = 2 pullEnergyTypeQuartic = 7 tenGageRNormal = 56 tenGageRGradMag = 55 tenGageRGradVec = 54 tenGageFANormal = 53 tenGageFAGradMag = 52 nrrdResampleNonExistentWeight = 3 tenGageFAGradVec = 51 tenGageQNormal = 50 tenGageQGradMag = 49 tenGageQGradVec = 48 tenGageSGradMag = 46 gageKernel21 = 5 miteRangeLast = 9 tenGageDetNormal = 44 tenGageDetGradMag = 43 tenGageDetGradVec = 42 pullPropEnergy = 3 tenGageBNormal = 41 gageKernel20 = 4 tenGageBGradVec = 39 nrrdFFTWPlanRigorMeasure = 2 nrrdUnaryOpExp = 9 tenGageNormGradMag = 37 tenGageNormGradVec = 36 tenGageTraceNormal = 35 tenGageTensorGradMagMag = 32 limnPolyDataInfoRGBA = 1 tenGageDelNormK2 = 23 tenGageDelNormPhi3 = 29 tenGageEvec2 = 22 gageKernel00 = 1 tenGageEvec1 = 21 tenGageEvec0 = 20 tenGageEval2 = 18 tenGageEval1 = 17 pullEnergyTypeUnknown = 0 tenGageOmega = 14 tenGageModeWarp = 13 tenGageTheta = 12 tenGageMode = 11 tenGageR = 10 tenGageFA = 9 tenGageQ = 8 tenGageS = 7 tenGageDet = 6 tenGageNorm = 4 tenGageTrace = 3 gageKernel11 = 3 tenGageConfidence = 2 tenGageUnknown = 0 nrrdTypeDefault = 0 nrrdResampleNonExistentNoop = 1 airTypeUnknown = 0 echoMatterLight = 4 nrrdUnaryOpNegative = 1 tenGageTraceHessianEvec0 = 88 echoMatterPhong = 1 tenGageLast = 208 gageCtxFlagNeedD = 1 tenGageFiberDispersion = 206 tenGageSNormal = 47 nrrdTypeUnknown = 0 gageCtxFlagUnknown = 0 tenGageFiberCurving = 205 nrrdBasicInfoLast = 16 nrrdBasicInfoComments = 14 nrrdBasicInfoOldMax = 13 tenGageCa1HessianEvec2 = 204 nrrdBasicInfoMeasurementFrame = 11 nrrdBasicInfoSpaceOrigin = 10 tenGageSGradVec = 45 nrrdBasicInfoSpaceUnits = 9 nrrdBasicInfoSpace = 7 nrrdBasicInfoSampleUnits = 6 alanStopDiverged = 5 nrrdBasicInfoContent = 5 nrrdBasicInfoDimension = 4 nrrdBasicInfoData = 1 nrrdBlind8BitRangeState = 3 nrrdBlind8BitRangeFalse = 2 nrrdBlind8BitRangeTrue = 1 nrrdBlind8BitRangeUnknown = 0 gageVecCurlNorm = 11 gageErrBoundsSpace = 2 tenGageBGradMag = 40 gageErrNone = 1 nrrdBasicInfoUnknown = 0 tenGageNormNormal = 38 tenGageCa1Hessian = 196 nrrdUnaryOpReciprocal = 2 tenGageTraceGradMag = 34 pullInterTypeJustR = 1 tenGageCp1HessianEvec = 192 tenGageTraceGradVec = 33 tenAniso_eval0 = 27 nrrdCenterLast = 3 nrrdCenterCell = 2 pullFlagStartSkipsPoints = 14 nrrdCenterNode = 1 nrrdCenterUnknown = 0 limnSplineTypeLast = 6 gageParmStackUse = 9 limnSplineTypeBC = 5 limnSplineTypeCubicBezier = 4 limnSplineTypeHermite = 3 limnSplineTypeTimeWarp = 2 tenGageTensorGrad = 30 limnSplineTypeLinear = 1 limnSplineTypeUnknown = 0 gageParmDefaultCenter = 8 tenGageCp1HessianEval = 188 tenAniso_Th = 23 tenGageDelNormPhi2 = 28 ell_cubic_root_last = 5 ell_cubic_root_three = 4 ell_cubic_root_unknown = 0 tenGageDelNormPhi1 = 27 limnSpaceWorld = 1 alanParmBeta = 19 tenGageDelNormR2 = 26 nrrdUnaryOpUnknown = 0 pullFlagNoAdd = 8 nrrdBinaryOpLTE = 14 tenGageTraceHessianEval = 83 tenGageDelNormR1 = 25 nrrdSpaceScannerXYZTime = 8 tenGageTensorRThetaPhiLinear = 166 pullFlagEnergyFromStrength = 5 pullFlagRestrictiveAddToBins = 4 tenAniso_Det = 25 nrrdFFTWPlanRigorEstimate = 1 tenGageTraceHessian = 82 pullPropStepConstr = 5 tenInterpTypeGeoLoxR = 6 airNoDio_format = 2 tenInterpTypeGeoLoxK = 5 baneIncAbsolute = 1 tenDwiFiberType2Evec0 = 2 pullSysParmRadiusScale = 8 tenGageCp1Normal = 172 tenGageEval0 = 16 gageItemPackPartHessEval0 = 6 tenDwiFiberType1Evec0 = 1 tenGageHessian = 81 tenGageEval = 15 tenGageCa1GradVec = 173 pullInfoIsovalue = 19 nrrdSpaceLeftPosteriorSuperiorTime = 6 tenAniso_Cl2 = 8 limnSpaceUnknown = 0 tenGageCp1GradMag = 171 tenAniso_Cs1 = 6 gageItemPackPartHessian = 5 pullInitMethodLast = 5 pullInitMethodPointPerVoxel = 3 pullInitMethodHalton = 2 tenDwiGageTensorError = 23 pullInitMethodRandom = 1 pullInitMethodUnknown = 0 miteRangeAlpha = 0 pullInfoNegativeTangent2 = 18 pullIterParmStuckMax = 3 limnEdgeTypeFrontCrease = 4 nrrdBoundaryLast = 6 tenAniso_Mode = 22 tenGageB = 5 miteStageOpLast = 5 nrrdSpaceRightAnteriorSuperiorTime = 4 alanStopLast = 6 nrrdTypeLast = 12 pullInfoNegativeTangent1 = 17 alanStopNonExist = 3 alanStopMaxIteration = 2 pullInfoTangent2 = 16 tenAniso_Skew = 21 tenGageCl1Normal = 169 tenAnisoLast = 30 tenGageTensor = 1 tenAniso_eval2 = 29 tenAniso_eval1 = 28 tenAniso_Tr = 26 nrrdBinaryOpLT = 13 tenAniso_B = 17 tenAniso_FA = 15 limnDeviceUnknown = 0 tijk_class_efs = 3 tenAniso_Ct2 = 13 tenAniso_Cs2 = 12 tenAniso_Clpmin2 = 11 tenDwiFiberTypeUnknown = 0 tenAniso_Ct1 = 7 tenAniso_Clpmin1 = 5 tenAniso_Cp1 = 3 tenAniso_S = 20 tenAniso_Cl1 = 2 tenGageCl1GradMag = 168 tenAniso_Conf = 1 nrrdBinaryOpNormalRandScaleAdd = 22 airNoDio_disable = 12 pullPropStepEnergy = 4 airNoDio_arch = 1 nrrdTernaryOpGaussian = 15 gageErrBoundsStack = 3 tenEstimate1MethodLLS = 1 limnDeviceLast = 3 tenAniso_R = 19 nrrdUnaryOpSin = 3 tenDwiGageTensorMLE = 18 tenGageClpmin1 = 76 nrrdField_unknown = 0 echoMatterMetal = 3 tenAniso_Omega = 24 coilKindType7Tensor = 3 echoMatterGlass = 2 coilKindType3Color = 2 coilKindTypeScalar = 1 coilKindTypeUnknown = 0 airInsane_DLSize = 11 airInsane_dio = 8 airInsane_AIR_NAN = 7 tenAniso_Q = 18 airInsane_QNaNHiBit = 6 airInsane_FltDblFPClass = 5 airTypeOther = 12 tenDwiGageTensorNLSLikelihood = 17 nrrdIoStateUnknown = 0 airTypeString = 10 pullIterParmMax = 2 unrrduScaleAdd = 4 limnQNLast = 17 airTypeChar = 9 limnQN8checker = 15 limnDevicePS = 1 tenFiberStopLast = 11 alanParmDiffA = 11 tenFiberStopMinNumSteps = 10 limnQN12checker = 9 limnQN13octa = 8 limnQN14octa = 7 tenFiberStopRadius = 5 alanParmDeltaX = 10 tenFiberStopConfidence = 4 alanStopNot = 1 tenFiberStopNumSteps = 3 tenFiberStopLength = 2 tenFiberIntgLast = 4 tenGlyphTypeSuperquad = 4 tenFiberIntgRK4 = 3 tenFiberIntgMidpoint = 2 tenFiberIntgEuler = 1 tenFiberIntgUnknown = 0 pullSysParmFracNeighNixedMax = 19 alanParmRandRange = 8 pushEnergyTypeLast = 6 tijk_class_last = 4 limnQN15octa = 5 limnQN16octa = 4 limnQN16checker = 3 limnQN16border1 = 2 limnQN16simple = 1 pullInfoLiveThresh3 = 14 airTypeUInt = 3 tenAniso_VF = 16 tenGageDetHessian = 93 tenDwiGageTensorNLSError = 15 miteValLast = 20 miteValVdefTdotV = 18 miteValVdefT = 17 miteValGTdotV = 16 pullInfoSeedPreThresh = 10 miteValVrefN = 15 airInsane_FISize = 10 miteValView = 11 miteValTi = 10 miteValTw = 9 miteValRi = 8 miteValRw = 7 miteValZi = 6 baneClipPercentile = 3 baneClipPeakRatio = 2 miteValYw = 3 miteValXi = 2 miteValXw = 1 baneRangeAnywhere = 4 baneRangeZeroCentered = 3 alanParmK = 14 baneRangeNegative = 2 baneRangePositive = 1 baneRangeUnknown = 0 tenGageEvalGrads = 72 pullInitMethodGivenPos = 4 gageItemPackPartLast = 12 nrrdTernaryOpIfElse = 10 gageItemPackPartHessEvec2 = 11 tenInterpTypeLogLinear = 2 gageItemPackPartHessEval2 = 8 miteStageOpMultiply = 4 miteStageOpAdd = 3 miteStageOpMax = 2 miteStageOpMin = 1 gageItemPackPartGradMag = 3 gageItemPackPartGradVec = 2 gageItemPackPartScalar = 1 gageItemPackPartUnknown = 0 pullInfoHessian = 3 tenAniso_RA = 14 tenGageCa1Normal = 175 alanParmConstantFilename = 20 tenGageRotTanMags = 71 nrrdMeasureHistoMin = 20 pullCountLast = 15 pullCountIteration = 14 nrrdBasicInfoKeyValuePairs = 15 pullCountCC = 13 pullCountPoints = 12 pullCountPointsStuck = 11 pullCountNixing = 10 pullCountAdding = 9 pullCountConstraintSatisfy = 8 pullCountForceFromPoints = 6 tenDwiFiberType12BlendEvec0 = 3 pullCountEnergyFromPoints = 5 pullCountForceFromImage = 4 pullCountTestStep = 2 pullCountDescent = 1 pullCountUnknown = 0 nrrdMeasureLineSlope = 17 nrrdSpacingStatusLast = 5 nrrdSpacingStatusDirection = 4 nrrdSpacingStatusScalarWithSpace = 3 nrrdSpacingStatusScalarNoSpace = 2 nrrdBasicInfoOldMin = 12 nrrdSpacingStatusNone = 1 nrrdSpacingStatusUnknown = 0 nrrdTypeUInt = 6 airTypeInt = 2 nrrdMeasureCoV = 15 coilKindTypeLast = 4 seekTypeValleyLine = 5 miteShadeMethodLast = 4 miteShadeMethodLitTen = 3 nrrdBasicInfoSpaceDimension = 8 pullInterTypeAdditive = 4 tenDwiGageTensorWLSError = 11 miteShadeMethodNone = 1 nrrdKind2DMaskedMatrix = 27 nrrdMeasureL2 = 9 airMopOnError = 1 airMopNever = 0 nrrdBasicInfoBlocksize = 3 limnSplineInfo4Vector = 5 tenFiberStopAniso = 1 tenGageInvarRGrads = 68 airInsane_UCSize = 9 pullFlagZeroZ = 15 gageSclHessEvec = 15 limnCameraPathTrackAt = 2 nrrdSpaceLast = 13 tenAniso_Ca2 = 10 gageSclHessEval1 = 13 limnCameraPathTrackUnknown = 0 alanParmMinAverageChange = 16 nrrdSpace3DRightHandedTime = 11 nrrdKind2DMaskedSymMatrix = 25 nrrdBoundaryMirror = 5 hooverErrThreadBegin = 4 nrrdFormatTypeNRRD = 1 baneMeasrLast = 9 tenGageTraceDiffusionFraction = 150 airInsane_NaNExists = 4 tenEstimate1MethodLast = 5 mossFlagUnknown = -1 nrrdBoundaryWrap = 3 tenAniso_Cp2 = 9 miteRangeKs = 7 miteRangeKd = 6 tenFiberTypeLast = 7 nrrdSpaceScannerXYZ = 7 limnSplineInfo3Vector = 3 tenGageEvec = 19 tenGageInvarKGrads = 66 miteRangeUnknown = -1 nrrdTernaryOpMinSmooth = 4 nrrdSpaceLeftAnteriorSuperiorTime = 5 nrrdMeasureSD = 14 seekTypeIsocontour = 1 nrrdBlind8BitRangeLast = 4 nrrdAxisInfoThickness = 3 tenGageOmegaNormal = 65 miteRangeSP = 8 nrrdField_last = 33 echoMatterLast = 5 nrrdTypeDouble = 10 nrrdTypeULLong = 8 nrrdTypeLLong = 7 nrrdField_measurement_frame = 31 nrrdTypeInt = 5 airNoDio_test = 11 nrrdTypeUShort = 4 nrrdTypeShort = 3 limnPrimitiveTriangleFan = 4 nrrdTypeUChar = 2 tenFiberTypePureLine = 5 nrrdTypeChar = 1 nrrdField_space_origin = 30 pullFlagScaleIsTau = 13 miteShadeMethodUnknown = 0 tenDwiGageTensorLLS = 6 limnSplineInfoScalar = 1 airNoDio_setfl = 10 tenFiberTypeZhukov = 6 tenGlyphTypeCylinder = 3 tenFiberTypeEvec2 = 3 tenFiberTypeEvec1 = 2 nrrdSpace3DLeftHanded = 10 nrrdSpace3DRightHanded = 9 hooverErrInit = 1 nrrdTernaryOpMultiply = 2 tenEstimate1MethodMLE = 4 nrrdMeasureLinf = 12 tenEstimate1MethodNLS = 3 tenEstimate1MethodUnknown = 0 nrrdSpaceLeftPosteriorSuperior = 3 nrrdSpaceLeftAnteriorSuperior = 2 nrrdSpaceRightAnteriorSuperior = 1 nrrdSpaceUnknown = 0 tenGlyphTypePolarPlot = 6 tenGlyphTypeBetterquad = 5 tenGlyphTypeUnknown = 0 tenGlyphTypeSphere = 2 airNoDio_small = 6 tenFiberTypeTensorLine = 4 pullFlagAllowCodimension3Constraints = 12 tenDwiGageMeanDWIValue = 5 limnSplineInfoUnknown = 0 nrrdBinaryOpAtan2 = 10 limnQN8octa = 16 nrrdHasNonExistOnly = 2 nrrdHasNonExistFalse = 0 nrrdMeasureRootMeanSquare = 11 baneIncStdv = 4 nrrdField_old_max = 22 limnQN9octa = 14 limnQN10octa = 13 pullCondConstraintSatA = 2 pullFlagBinSingle = 11 tenDwiGageADC = 4 limnQN10checker = 12 echoJitterJitter = 2 gageParmCurvNormalSide = 6 tenGageThetaNormal = 62 airEndianBig = 4321 nrrdAxisInfoUnits = 10 nrrdAxisInfoKind = 8 limnQN11octa = 11 echoMatterMetalKa = 1 echoJittableLight = 1 nrrdAxisInfoMin = 4 gageSclGeomTens = 20 nrrdAxisInfoSpacing = 2 limnQN12octa = 10 nrrdAxisInfoSize = 1 nrrdAxisInfoUnknown = 0 gageParmK3Pack = 4 nrrdField_labels = 17 tenFiberStopMinLength = 9 nrrdHasNonExistLast = 4 gageVecMGFrob = 29 tenAniso_Ca1 = 4 gageVecMultiGrad = 28 gageParmRenormalize = 2 gageVecGradient2 = 27 gageVecGradient1 = 26 nrrdField_centers = 15 pullFlagConvergenceIgnoresPopCntl = 10 gageVecGradient0 = 25 tenDwiGageJustDWI = 3 gageVecProjHelGradient = 24 gageVecDirHelDeriv = 23 gageVecNCurlNormGrad = 21 airEndianLittle = 1234 pullTraceStopStub = 5 gageVecImaginaryPart = 16 gageVecLambda2 = 15 gageScl2ndDD = 19 gageVecNormHelicity = 13 gageVecHelicity = 12 gageVecHessian = 17 gageVecCurl = 10 gageVecDivergence = 9 gageVecStrain = 8 gageVecJacobian = 7 tenGageCa1GradMag = 174 gageVecVector1 = 3 pullInfoStrength = 22 nrrdField_spacings = 10 nrrdBinaryOpPow = 5 tenDwiGageB0 = 2 miteRangeGreen = 2 tenGageThetaGradVec = 60 tenTripleTypeRThetaPhi = 5 nrrdField_space_dimension = 8 tenFiberStopUnknown = 0 tenTripleTypeRThetaZ = 4 tenGageCl1HessianEval1 = 181 alanTextureTypeGrayScott = 2 nrrdZlibStrategyLast = 4 nrrdZlibStrategyFiltered = 3 nrrdZlibStrategyHuffman = 2 nrrdBinaryOpDivide = 4 nrrdZlibStrategyUnknown = 0 tenTripleTypeEigenvalue = 1 airFP_NEG_ZERO = 10 pullTraceStopBounds = 3 tenTripleTypeUnknown = 0 ell_cubic_root_single_double = 3 nrrdMeasureSum = 7 pullIterParmLast = 10 pullIterParmEnergyIncreasePermitHalfLife = 9 pullIterParmSnap = 8 pullIterParmCallback = 7 pullCondLast = 8 pullIterParmAddDescent = 6 pushEnergyTypeCoulomb = 3 airFP_NEG_DENORM = 8 pushEnergyTypeGauss = 2 pushEnergyTypeUnknown = 0 pullIterParmMin = 1 pullCondNew = 7 pullIterParmUnknown = 0 tenGageTraceHessianEvec2 = 90 pullSysParmProbeProb = 11 pullFlagConstraintBeforeSeedThresh = 7 nrrdMeasureHistoL2 = 27 pullSysParmConstraintStepMin = 17 tenEstimate2MethodLast = 3 pullCondEnergyBad = 6 nrrdEncodingTypeHex = 3 echoMatterLightUnit = 1 pullInterTypeSeparable = 3 echoMatterLightPower = 0 gageSclHessEvec0 = 16 tenGageOmegaHessianContrTenEvec1 = 146 ell_cubic_root_triple = 2 limnQN14checker = 6 gageVecSOmega = 14 nrrdKindLast = 32 hooverErrRenderEnd = 10 hooverErrThreadJoin = 9 hooverErrRayEnd = 7 hooverErrRayBegin = 5 hooverErrThreadCreate = 3 hooverErrRenderBegin = 2 pullSysParmNeighborTrueProb = 10 pullFlagNixAtVolumeEdgeSpace = 6 hooverErrNone = 0 tenGageModeGradVec = 57 echoMatterGlassFuzzy = 3 pullCondOld = 1 echoMatterGlassKd = 2 limnSpaceLast = 5 echoMatterPhongSp = 3 echoMatterPhongKs = 2 echoMatterPhongKd = 1 echoMatterPhongKa = 0 echoJitterLast = 4 unrrduScaleLast = 8 echoJitterNone = 0 echoJitterUnknown = -1 limnQNUnknown = 0 unrrduScaleDivide = 3 unrrduScaleMultiply = 2 nrrdKind2DMatrix = 26 unrrduScaleNothing = 1 unrrduScaleUnknown = 0 tenAnisoUnknown = 0 nrrdIoStateBzip2BlockSize = 9 nrrdAxisInfoLast = 11 tenGageCl1HessianEval0 = 180 nrrdKindQuaternion = 23 baneIncLast = 5 nrrdEncodingTypeBzip2 = 5 nrrdKind4Vector = 22 nrrdField_space_directions = 14 tenEstimate1MethodWLS = 2 nrrdAxisInfoCenter = 7 nrrdBinaryOpUnknown = 0 nrrdIoStateZlibStrategy = 8 alanStopConverged = 4 nrrdAxisInfoMax = 5 nrrdHasNonExistTrue = 1 limnEdgeTypeLast = 8 limnEdgeTypeLone = 7 limnEdgeTypeBorder = 6 limnEdgeTypeContour = 3 limnEdgeTypeBackCrease = 2 limnEdgeTypeBackFacet = 1 limnEdgeTypeUnknown = 0 pullSysParmRadiusSpace = 7 nrrdIoStateZlibLevel = 7 echoTypeIsosurface = 7 tenInterpTypeQuatGeoLoxK = 9 tenGageTensorQuatGeoLoxR = 165 tenInterpTypeWang = 4 limnEdgeTypeFrontFacet = 5 gageSclLast = 36 gageSclHessMode = 35 gageSclHessRidgeness = 34 pullInfoLast = 24 tenGageTraceHessianEvec1 = 89 gageSclHessValleyness = 33 gageSclMedian = 32 gageSclFlowlineCurv = 31 gageSclCurvDir2 = 30 gageSclCurvDir1 = 29 gageSclGaussCurv = 28 gageSclMeanCurv = 27 gageSclShapeIndex = 26 gageSclShapeTrace = 25 gageSclTotalCurv = 24 gageSclK2 = 23 gageSclK1 = 22 gageSclGeomTensTen = 21 tenGageCovarianceRGRT = 161 gageSclHessEvec2 = 18 nrrdEncodingTypeAscii = 2 gageSclHessEvec1 = 17 limnCameraPathTrackLast = 4 limnCameraPathTrackBoth = 3 gageSclHessEval2 = 14 limnCameraPathTrackFrom = 1 gageSclHessEval0 = 12 pullSysParmTheta = 5 gageVecLast = 32 limnPrimitiveLast = 8 limnPrimitiveLines = 7 limnPrimitiveLineStrip = 6 limnPrimitiveQuads = 5 tenInterpTypeLoxK = 7 limnPrimitiveTriangleStrip = 3 gageVecMGEvec = 31 limnPrimitiveTriangles = 2 limnPrimitiveNoop = 1 limnPrimitiveUnknown = 0 pullInfoLiveThresh = 12 gageVecMGEval = 30 echoMatterMetalFuzzy = 3 nrrdEncodingTypeRaw = 1 gageParmGradMagCurvMin = 5 echoMatterMetalR0 = 0 tenEstimate2MethodPeled = 2 miteValNdotL = 14 tenTripleTypeLast = 10 tenTripleTypeWheelParm = 9 tenTripleTypeR = 8 miteValNdotV = 13 pullSysParmBeta = 2 tenTripleTypeXYZ = 3 tenTripleTypeMoment = 2 miteValNormal = 12 nrrdBasicInfoType = 2 echoJittableMotionB = 6 echoJittableMotionA = 5 nrrdTernaryOpAdd = 1 echoJittableNormalB = 4 echoJittableNormalA = 3 echoJittableLens = 2 alanStopUnknown = 0 echoJittablePixel = 0 echoJittableUnknown = -1 nrrdEncodingTypeUnknown = 0 pullProcessModeLast = 5 pullProcessModeNixing = 4 pullProcessModeAdding = 3 pullProcessModeNeighLearn = 2 pullProcessModeDescent = 1 pullProcessModeUnknown = 0 nrrdField_axis_maxs = 13 baneClipLast = 5 airFP_Last = 11 nrrdKindNormal = 8 baneClipTopN = 4 gageVecCurlNormGrad = 20 miteValZw = 5 airLLong = c_longlong airULLong = c_ulonglong class airPtrPtrUnion(Union): pass airPtrPtrUnion._fields_ = [ ('uc', POINTER(POINTER(c_ubyte))), ('sc', POINTER(POINTER(c_byte))), ('c', POINTER(STRING)), ('cp', POINTER(POINTER(STRING))), ('us', POINTER(POINTER(c_ushort))), ('s', POINTER(POINTER(c_short))), ('ui', POINTER(POINTER(c_uint))), ('i', POINTER(POINTER(c_int))), ('f', POINTER(POINTER(c_float))), ('d', POINTER(POINTER(c_double))), ('v', POINTER(c_void_p)), ] class airEnum(Structure): pass airEnum._fields_ = [ ('name', STRING), ('M', c_uint), ('str', POINTER(STRING)), ('val', POINTER(c_int)), ('desc', POINTER(STRING)), ('strEqv', POINTER(STRING)), ('valEqv', POINTER(c_int)), ('sense', c_int), ] airEnumUnknown = libteem.airEnumUnknown airEnumUnknown.restype = c_int airEnumUnknown.argtypes = [POINTER(airEnum)] airEnumValCheck = libteem.airEnumValCheck airEnumValCheck.restype = c_int airEnumValCheck.argtypes = [POINTER(airEnum), c_int] airEnumStr = libteem.airEnumStr airEnumStr.restype = STRING airEnumStr.argtypes = [POINTER(airEnum), c_int] airEnumDesc = libteem.airEnumDesc airEnumDesc.restype = STRING airEnumDesc.argtypes = [POINTER(airEnum), c_int] airEnumVal = libteem.airEnumVal airEnumVal.restype = c_int airEnumVal.argtypes = [POINTER(airEnum), STRING] airEnumFmtDesc = libteem.airEnumFmtDesc airEnumFmtDesc.restype = STRING airEnumFmtDesc.argtypes = [POINTER(airEnum), c_int, c_int, STRING] airEnumPrint = libteem.airEnumPrint airEnumPrint.restype = None airEnumPrint.argtypes = [POINTER(FILE), POINTER(airEnum)] airEnumCheck = libteem.airEnumCheck airEnumCheck.restype = c_int airEnumCheck.argtypes = [STRING, POINTER(airEnum)] airEndian = (POINTER(airEnum)).in_dll(libteem, 'airEndian') airMyEndian = libteem.airMyEndian airMyEndian.restype = c_int airMyEndian.argtypes = [] class airArray(Structure): pass airArray._fields_ = [ ('data', c_void_p), ('dataP', POINTER(c_void_p)), ('len', c_uint), ('lenP', POINTER(c_uint)), ('incr', c_uint), ('size', c_uint), ('unit', c_size_t), ('noReallocWhenSmaller', c_int), ('allocCB', CFUNCTYPE(c_void_p)), ('freeCB', CFUNCTYPE(c_void_p, c_void_p)), ('initCB', CFUNCTYPE(None, c_void_p)), ('doneCB', CFUNCTYPE(None, c_void_p)), ] airArrayNew = libteem.airArrayNew airArrayNew.restype = POINTER(airArray) airArrayNew.argtypes = [POINTER(c_void_p), POINTER(c_uint), c_size_t, c_uint] airArrayStructCB = libteem.airArrayStructCB airArrayStructCB.restype = None airArrayStructCB.argtypes = [POINTER(airArray), CFUNCTYPE(None, c_void_p), CFUNCTYPE(None, c_void_p)] airArrayPointerCB = libteem.airArrayPointerCB airArrayPointerCB.restype = None airArrayPointerCB.argtypes = [POINTER(airArray), CFUNCTYPE(c_void_p), CFUNCTYPE(c_void_p, c_void_p)] airArrayLenSet = libteem.airArrayLenSet airArrayLenSet.restype = None airArrayLenSet.argtypes = [POINTER(airArray), c_uint] airArrayLenPreSet = libteem.airArrayLenPreSet airArrayLenPreSet.restype = None airArrayLenPreSet.argtypes = [POINTER(airArray), c_uint] airArrayLenIncr = libteem.airArrayLenIncr airArrayLenIncr.restype = c_uint airArrayLenIncr.argtypes = [POINTER(airArray), c_int] airArrayNix = libteem.airArrayNix airArrayNix.restype = POINTER(airArray) airArrayNix.argtypes = [POINTER(airArray)] airArrayNuke = libteem.airArrayNuke airArrayNuke.restype = POINTER(airArray) airArrayNuke.argtypes = [POINTER(airArray)] class airHeap(Structure): pass airHeap._fields_ = [ ('key_a', POINTER(airArray)), ('data_a', POINTER(airArray)), ('idx_a', POINTER(airArray)), ('invidx_a', POINTER(airArray)), ('key', POINTER(c_double)), ('data', c_void_p), ('idx', POINTER(c_uint)), ('invidx', POINTER(c_uint)), ] airHeapNew = libteem.airHeapNew airHeapNew.restype = POINTER(airHeap) airHeapNew.argtypes = [c_size_t, c_uint] airHeapFromArray = libteem.airHeapFromArray airHeapFromArray.restype = POINTER(airHeap) airHeapFromArray.argtypes = [POINTER(airArray), POINTER(airArray)] airHeapNix = libteem.airHeapNix airHeapNix.restype = POINTER(airHeap) airHeapNix.argtypes = [POINTER(airHeap)] airHeapLength = libteem.airHeapLength airHeapLength.restype = c_uint airHeapLength.argtypes = [POINTER(airHeap)] airHeapInsert = libteem.airHeapInsert airHeapInsert.restype = c_uint airHeapInsert.argtypes = [POINTER(airHeap), c_double, c_void_p] airHeapMerge = libteem.airHeapMerge airHeapMerge.restype = c_uint airHeapMerge.argtypes = [POINTER(airHeap), POINTER(airHeap)] airHeapFrontPeek = libteem.airHeapFrontPeek airHeapFrontPeek.restype = c_double airHeapFrontPeek.argtypes = [POINTER(airHeap), c_void_p] airHeapFrontPop = libteem.airHeapFrontPop airHeapFrontPop.restype = c_double airHeapFrontPop.argtypes = [POINTER(airHeap), c_void_p] airHeapFrontUpdate = libteem.airHeapFrontUpdate airHeapFrontUpdate.restype = c_int airHeapFrontUpdate.argtypes = [POINTER(airHeap), c_double, c_void_p] airHeapFind = libteem.airHeapFind airHeapFind.restype = c_int airHeapFind.argtypes = [POINTER(airHeap), POINTER(c_uint), c_void_p] airHeapRemove = libteem.airHeapRemove airHeapRemove.restype = c_int airHeapRemove.argtypes = [POINTER(airHeap), c_uint] airHeapUpdate = libteem.airHeapUpdate airHeapUpdate.restype = c_int airHeapUpdate.argtypes = [POINTER(airHeap), c_uint, c_double, c_void_p] airThreadCapable = (c_int).in_dll(libteem, 'airThreadCapable') airThreadNoopWarning = (c_int).in_dll(libteem, 'airThreadNoopWarning') class _airThread(Structure): pass airThread = _airThread class _airThreadMutex(Structure): pass airThreadMutex = _airThreadMutex class _airThreadCond(Structure): pass airThreadCond = _airThreadCond class airThreadBarrier(Structure): pass airThreadBarrier._fields_ = [ ('numUsers', c_uint), ('numDone', c_uint), ('doneMutex', POINTER(airThreadMutex)), ('doneCond', POINTER(airThreadCond)), ] airThreadNew = libteem.airThreadNew airThreadNew.restype = POINTER(airThread) airThreadNew.argtypes = [] airThreadStart = libteem.airThreadStart airThreadStart.restype = c_int airThreadStart.argtypes = [POINTER(airThread), CFUNCTYPE(c_void_p, c_void_p), c_void_p] airThreadJoin = libteem.airThreadJoin airThreadJoin.restype = c_int airThreadJoin.argtypes = [POINTER(airThread), POINTER(c_void_p)] airThreadNix = libteem.airThreadNix airThreadNix.restype = POINTER(airThread) airThreadNix.argtypes = [POINTER(airThread)] airThreadMutexNew = libteem.airThreadMutexNew airThreadMutexNew.restype = POINTER(airThreadMutex) airThreadMutexNew.argtypes = [] airThreadMutexLock = libteem.airThreadMutexLock airThreadMutexLock.restype = c_int airThreadMutexLock.argtypes = [POINTER(airThreadMutex)] airThreadMutexUnlock = libteem.airThreadMutexUnlock airThreadMutexUnlock.restype = c_int airThreadMutexUnlock.argtypes = [POINTER(airThreadMutex)] airThreadMutexNix = libteem.airThreadMutexNix airThreadMutexNix.restype = POINTER(airThreadMutex) airThreadMutexNix.argtypes = [POINTER(airThreadMutex)] airThreadCondNew = libteem.airThreadCondNew airThreadCondNew.restype = POINTER(airThreadCond) airThreadCondNew.argtypes = [] airThreadCondWait = libteem.airThreadCondWait airThreadCondWait.restype = c_int airThreadCondWait.argtypes = [POINTER(airThreadCond), POINTER(airThreadMutex)] airThreadCondSignal = libteem.airThreadCondSignal airThreadCondSignal.restype = c_int airThreadCondSignal.argtypes = [POINTER(airThreadCond)] airThreadCondBroadcast = libteem.airThreadCondBroadcast airThreadCondBroadcast.restype = c_int airThreadCondBroadcast.argtypes = [POINTER(airThreadCond)] airThreadCondNix = libteem.airThreadCondNix airThreadCondNix.restype = POINTER(airThreadCond) airThreadCondNix.argtypes = [POINTER(airThreadCond)] airThreadBarrierNew = libteem.airThreadBarrierNew airThreadBarrierNew.restype = POINTER(airThreadBarrier) airThreadBarrierNew.argtypes = [c_uint] airThreadBarrierWait = libteem.airThreadBarrierWait airThreadBarrierWait.restype = c_int airThreadBarrierWait.argtypes = [POINTER(airThreadBarrier)] airThreadBarrierNix = libteem.airThreadBarrierNix airThreadBarrierNix.restype = POINTER(airThreadBarrier) airThreadBarrierNix.argtypes = [POINTER(airThreadBarrier)] class airFloat(Union): pass airFloat._fields_ = [ ('i', c_uint), ('f', c_float), ] class airDouble(Union): pass airDouble._pack_ = 4 airDouble._fields_ = [ ('i', airULLong), ('d', c_double), ] airMyQNaNHiBit = (c_int).in_dll(libteem, 'airMyQNaNHiBit') airFPPartsToVal_f = libteem.airFPPartsToVal_f airFPPartsToVal_f.restype = c_float airFPPartsToVal_f.argtypes = [c_uint, c_uint, c_uint] airFPValToParts_f = libteem.airFPValToParts_f airFPValToParts_f.restype = None airFPValToParts_f.argtypes = [POINTER(c_uint), POINTER(c_uint), POINTER(c_uint), c_float] airFPPartsToVal_d = libteem.airFPPartsToVal_d airFPPartsToVal_d.restype = c_double airFPPartsToVal_d.argtypes = [c_uint, c_uint, c_uint, c_uint] airFPValToParts_d = libteem.airFPValToParts_d airFPValToParts_d.restype = None airFPValToParts_d.argtypes = [POINTER(c_uint), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint), c_double] airFPGen_f = libteem.airFPGen_f airFPGen_f.restype = c_float airFPGen_f.argtypes = [c_int] airFPGen_d = libteem.airFPGen_d airFPGen_d.restype = c_double airFPGen_d.argtypes = [c_int] airFPClass_f = libteem.airFPClass_f airFPClass_f.restype = c_int airFPClass_f.argtypes = [c_float] airFPClass_d = libteem.airFPClass_d airFPClass_d.restype = c_int airFPClass_d.argtypes = [c_double] airFPFprintf_f = libteem.airFPFprintf_f airFPFprintf_f.restype = None airFPFprintf_f.argtypes = [POINTER(FILE), c_float] airFPFprintf_d = libteem.airFPFprintf_d airFPFprintf_d.restype = None airFPFprintf_d.argtypes = [POINTER(FILE), c_double] airFloatQNaN = (airFloat).in_dll(libteem, 'airFloatQNaN') airFloatSNaN = (airFloat).in_dll(libteem, 'airFloatSNaN') airFloatPosInf = (airFloat).in_dll(libteem, 'airFloatPosInf') airFloatNegInf = (airFloat).in_dll(libteem, 'airFloatNegInf') airNaN = libteem.airNaN airNaN.restype = c_float airNaN.argtypes = [] airIsNaN = libteem.airIsNaN airIsNaN.restype = c_int airIsNaN.argtypes = [c_double] airIsInf_f = libteem.airIsInf_f airIsInf_f.restype = c_int airIsInf_f.argtypes = [c_float] airIsInf_d = libteem.airIsInf_d airIsInf_d.restype = c_int airIsInf_d.argtypes = [c_double] airExists = libteem.airExists airExists.restype = c_int airExists.argtypes = [c_double] class airRandMTState(Structure): pass airRandMTState._fields_ = [ ('state', c_uint * 624), ('pNext', POINTER(c_uint)), ('left', c_uint), ] airRandMTStateGlobal = (POINTER(airRandMTState)).in_dll(libteem, 'airRandMTStateGlobal') airRandMTStateGlobalInit = libteem.airRandMTStateGlobalInit airRandMTStateGlobalInit.restype = None airRandMTStateGlobalInit.argtypes = [] airRandMTStateNew = libteem.airRandMTStateNew airRandMTStateNew.restype = POINTER(airRandMTState) airRandMTStateNew.argtypes = [c_uint] airRandMTStateNix = libteem.airRandMTStateNix airRandMTStateNix.restype = POINTER(airRandMTState) airRandMTStateNix.argtypes = [POINTER(airRandMTState)] airSrandMT_r = libteem.airSrandMT_r airSrandMT_r.restype = None airSrandMT_r.argtypes = [POINTER(airRandMTState), c_uint] airDrandMT_r = libteem.airDrandMT_r airDrandMT_r.restype = c_double airDrandMT_r.argtypes = [POINTER(airRandMTState)] airUIrandMT_r = libteem.airUIrandMT_r airUIrandMT_r.restype = c_uint airUIrandMT_r.argtypes = [POINTER(airRandMTState)] airDrandMT53_r = libteem.airDrandMT53_r airDrandMT53_r.restype = c_double airDrandMT53_r.argtypes = [POINTER(airRandMTState)] airRandInt = libteem.airRandInt airRandInt.restype = c_uint airRandInt.argtypes = [c_uint] airRandInt_r = libteem.airRandInt_r airRandInt_r.restype = c_uint airRandInt_r.argtypes = [POINTER(airRandMTState), c_uint] airSrandMT = libteem.airSrandMT airSrandMT.restype = None airSrandMT.argtypes = [c_uint] airDrandMT = libteem.airDrandMT airDrandMT.restype = c_double airDrandMT.argtypes = [] airRandMTSanity = libteem.airRandMTSanity airRandMTSanity.restype = c_int airRandMTSanity.argtypes = [] airAtod = libteem.airAtod airAtod.restype = c_double airAtod.argtypes = [STRING] airSingleSscanf = libteem.airSingleSscanf airSingleSscanf.restype = c_int airSingleSscanf.argtypes = [STRING, STRING, c_void_p] airBool = (POINTER(airEnum)).in_dll(libteem, 'airBool') airParseStrB = libteem.airParseStrB airParseStrB.restype = c_uint airParseStrB.argtypes = [POINTER(c_int), STRING, STRING, c_uint] airParseStrI = libteem.airParseStrI airParseStrI.restype = c_uint airParseStrI.argtypes = [POINTER(c_int), STRING, STRING, c_uint] airParseStrUI = libteem.airParseStrUI airParseStrUI.restype = c_uint airParseStrUI.argtypes = [POINTER(c_uint), STRING, STRING, c_uint] airParseStrZ = libteem.airParseStrZ airParseStrZ.restype = c_uint airParseStrZ.argtypes = [POINTER(c_size_t), STRING, STRING, c_uint] airParseStrF = libteem.airParseStrF airParseStrF.restype = c_uint airParseStrF.argtypes = [POINTER(c_float), STRING, STRING, c_uint] airParseStrD = libteem.airParseStrD airParseStrD.restype = c_uint airParseStrD.argtypes = [POINTER(c_double), STRING, STRING, c_uint] airParseStrC = libteem.airParseStrC airParseStrC.restype = c_uint airParseStrC.argtypes = [STRING, STRING, STRING, c_uint] airParseStrS = libteem.airParseStrS airParseStrS.restype = c_uint airParseStrS.argtypes = [POINTER(STRING), STRING, STRING, c_uint] airParseStrE = libteem.airParseStrE airParseStrE.restype = c_uint airParseStrE.argtypes = [POINTER(c_int), STRING, STRING, c_uint] airParseStr = (CFUNCTYPE(c_uint, c_void_p, STRING, STRING, c_uint) * 13).in_dll(libteem, 'airParseStr') airStrdup = libteem.airStrdup airStrdup.restype = STRING airStrdup.argtypes = [STRING] airStrlen = libteem.airStrlen airStrlen.restype = c_size_t airStrlen.argtypes = [STRING] airStrcmp = libteem.airStrcmp airStrcmp.restype = c_int airStrcmp.argtypes = [STRING, STRING] airStrtokQuoting = (c_int).in_dll(libteem, 'airStrtokQuoting') airStrtok = libteem.airStrtok airStrtok.restype = STRING airStrtok.argtypes = [STRING, STRING, POINTER(STRING)] airStrntok = libteem.airStrntok airStrntok.restype = c_uint airStrntok.argtypes = [STRING, STRING] airStrtrans = libteem.airStrtrans airStrtrans.restype = STRING airStrtrans.argtypes = [STRING, c_char, c_char] airStrcpy = libteem.airStrcpy airStrcpy.restype = STRING airStrcpy.argtypes = [STRING, c_size_t, STRING] airEndsWith = libteem.airEndsWith airEndsWith.restype = c_int airEndsWith.argtypes = [STRING, STRING] airUnescape = libteem.airUnescape airUnescape.restype = STRING airUnescape.argtypes = [STRING] airOneLinify = libteem.airOneLinify airOneLinify.restype = STRING airOneLinify.argtypes = [STRING] airToLower = libteem.airToLower airToLower.restype = STRING airToLower.argtypes = [STRING] airToUpper = libteem.airToUpper airToUpper.restype = STRING airToUpper.argtypes = [STRING] airOneLine = libteem.airOneLine airOneLine.restype = c_uint airOneLine.argtypes = [POINTER(FILE), STRING, c_uint] airInsaneErr = libteem.airInsaneErr airInsaneErr.restype = STRING airInsaneErr.argtypes = [c_int] airSanity = libteem.airSanity airSanity.restype = c_int airSanity.argtypes = [] airTeemVersion = (STRING).in_dll(libteem, 'airTeemVersion') airTeemReleaseDate = (STRING).in_dll(libteem, 'airTeemReleaseDate') airNull = libteem.airNull airNull.restype = c_void_p airNull.argtypes = [] airSetNull = libteem.airSetNull airSetNull.restype = c_void_p airSetNull.argtypes = [POINTER(c_void_p)] airFree = libteem.airFree airFree.restype = c_void_p airFree.argtypes = [c_void_p] airFopen = libteem.airFopen airFopen.restype = POINTER(FILE) airFopen.argtypes = [STRING, POINTER(FILE), STRING] airFclose = libteem.airFclose airFclose.restype = POINTER(FILE) airFclose.argtypes = [POINTER(FILE)] airSinglePrintf = libteem.airSinglePrintf airSinglePrintf.restype = c_int airSinglePrintf.argtypes = [POINTER(FILE), STRING, STRING] airSprintSize_t = libteem.airSprintSize_t airSprintSize_t.restype = STRING airSprintSize_t.argtypes = [STRING, c_size_t] airSprintVecSize_t = libteem.airSprintVecSize_t airSprintVecSize_t.restype = STRING airSprintVecSize_t.argtypes = [STRING, POINTER(c_size_t), c_uint] airPrettySprintSize_t = libteem.airPrettySprintSize_t airPrettySprintSize_t.restype = STRING airPrettySprintSize_t.argtypes = [STRING, c_size_t] airSprintPtrdiff_t = libteem.airSprintPtrdiff_t airSprintPtrdiff_t.restype = STRING airSprintPtrdiff_t.argtypes = [STRING, ptrdiff_t] airPresent = (c_int).in_dll(libteem, 'airPresent') airStderr = libteem.airStderr airStderr.restype = POINTER(FILE) airStderr.argtypes = [] airStdout = libteem.airStdout airStdout.restype = POINTER(FILE) airStdout.argtypes = [] airStdin = libteem.airStdin airStdin.restype = POINTER(FILE) airStdin.argtypes = [] airIndex = libteem.airIndex airIndex.restype = c_uint airIndex.argtypes = [c_double, c_double, c_double, c_uint] airIndexClamp = libteem.airIndexClamp airIndexClamp.restype = c_uint airIndexClamp.argtypes = [c_double, c_double, c_double, c_uint] airIndexULL = libteem.airIndexULL airIndexULL.restype = airULLong airIndexULL.argtypes = [c_double, c_double, c_double, airULLong] airIndexClampULL = libteem.airIndexClampULL airIndexClampULL.restype = airULLong airIndexClampULL.argtypes = [c_double, c_double, c_double, airULLong] airDoneStr = libteem.airDoneStr airDoneStr.restype = STRING airDoneStr.argtypes = [c_double, c_double, c_double, STRING] airTime = libteem.airTime airTime.restype = c_double airTime.argtypes = [] airTypeStr = (c_char * 129 * 13).in_dll(libteem, 'airTypeStr') airTypeSize = (c_size_t * 13).in_dll(libteem, 'airTypeSize') airEqvAdd = libteem.airEqvAdd airEqvAdd.restype = None airEqvAdd.argtypes = [POINTER(airArray), c_uint, c_uint] airEqvMap = libteem.airEqvMap airEqvMap.restype = c_uint airEqvMap.argtypes = [POINTER(airArray), POINTER(c_uint), c_uint] airEqvSettle = libteem.airEqvSettle airEqvSettle.restype = c_uint airEqvSettle.argtypes = [POINTER(c_uint), c_uint] airFastExp = libteem.airFastExp airFastExp.restype = c_double airFastExp.argtypes = [c_double] airExp = libteem.airExp airExp.restype = c_double airExp.argtypes = [c_double] airNormalRand = libteem.airNormalRand airNormalRand.restype = None airNormalRand.argtypes = [POINTER(c_double), POINTER(c_double)] airNormalRand_r = libteem.airNormalRand_r airNormalRand_r.restype = None airNormalRand_r.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(airRandMTState)] airShuffle = libteem.airShuffle airShuffle.restype = None airShuffle.argtypes = [POINTER(c_uint), c_uint, c_int] airShuffle_r = libteem.airShuffle_r airShuffle_r.restype = None airShuffle_r.argtypes = [POINTER(airRandMTState), POINTER(c_uint), c_uint, c_int] airCbrt = libteem.airCbrt airCbrt.restype = c_double airCbrt.argtypes = [c_double] airMode3 = libteem.airMode3 airMode3.restype = c_double airMode3.argtypes = [c_double, c_double, c_double] airMode3_d = libteem.airMode3_d airMode3_d.restype = c_double airMode3_d.argtypes = [POINTER(c_double)] airSgnPow = libteem.airSgnPow airSgnPow.restype = c_double airSgnPow.argtypes = [c_double, c_double] airFlippedSgnPow = libteem.airFlippedSgnPow airFlippedSgnPow.restype = c_double airFlippedSgnPow.argtypes = [c_double, c_double] airIntPow = libteem.airIntPow airIntPow.restype = c_double airIntPow.argtypes = [c_double, c_int] airSgn = libteem.airSgn airSgn.restype = c_int airSgn.argtypes = [c_double] airLog2 = libteem.airLog2 airLog2.restype = c_int airLog2.argtypes = [c_size_t] airErfc = libteem.airErfc airErfc.restype = c_double airErfc.argtypes = [c_double] airErf = libteem.airErf airErf.restype = c_double airErf.argtypes = [c_double] airGaussian = libteem.airGaussian airGaussian.restype = c_double airGaussian.argtypes = [c_double, c_double, c_double] airBesselI0 = libteem.airBesselI0 airBesselI0.restype = c_double airBesselI0.argtypes = [c_double] airBesselI1 = libteem.airBesselI1 airBesselI1.restype = c_double airBesselI1.argtypes = [c_double] airBesselI0ExpScaled = libteem.airBesselI0ExpScaled airBesselI0ExpScaled.restype = c_double airBesselI0ExpScaled.argtypes = [c_double] airBesselI1ExpScaled = libteem.airBesselI1ExpScaled airBesselI1ExpScaled.restype = c_double airBesselI1ExpScaled.argtypes = [c_double] airLogBesselI0 = libteem.airLogBesselI0 airLogBesselI0.restype = c_double airLogBesselI0.argtypes = [c_double] airLogRician = libteem.airLogRician airLogRician.restype = c_double airLogRician.argtypes = [c_double, c_double, c_double] airRician = libteem.airRician airRician.restype = c_double airRician.argtypes = [c_double, c_double, c_double] airBesselI1By0 = libteem.airBesselI1By0 airBesselI1By0.restype = c_double airBesselI1By0.argtypes = [c_double] airBesselIn = libteem.airBesselIn airBesselIn.restype = c_double airBesselIn.argtypes = [c_int, c_double] airBesselInExpScaled = libteem.airBesselInExpScaled airBesselInExpScaled.restype = c_double airBesselInExpScaled.argtypes = [c_int, c_double] airVanDerCorput = libteem.airVanDerCorput airVanDerCorput.restype = c_double airVanDerCorput.argtypes = [c_uint, c_uint] airHalton = libteem.airHalton airHalton.restype = None airHalton.argtypes = [POINTER(c_double), c_uint, POINTER(c_uint), c_uint] airPrimeList = (c_uint * 1000).in_dll(libteem, 'airPrimeList') airCRC32 = libteem.airCRC32 airCRC32.restype = c_uint airCRC32.argtypes = [POINTER(c_ubyte), c_size_t, c_size_t, c_int] airNoDioErr = libteem.airNoDioErr airNoDioErr.restype = STRING airNoDioErr.argtypes = [c_int] airMyDio = (c_int).in_dll(libteem, 'airMyDio') airDisableDio = (c_int).in_dll(libteem, 'airDisableDio') airDioInfo = libteem.airDioInfo airDioInfo.restype = None airDioInfo.argtypes = [POINTER(c_int), POINTER(c_int), POINTER(c_int), c_int] airDioTest = libteem.airDioTest airDioTest.restype = c_int airDioTest.argtypes = [c_int, c_void_p, c_size_t] airDioMalloc = libteem.airDioMalloc airDioMalloc.restype = c_void_p airDioMalloc.argtypes = [c_size_t, c_int] airDioRead = libteem.airDioRead airDioRead.restype = c_size_t airDioRead.argtypes = [c_int, c_void_p, c_size_t] airDioWrite = libteem.airDioWrite airDioWrite.restype = c_size_t airDioWrite.argtypes = [c_int, c_void_p, c_size_t] airMopper = CFUNCTYPE(c_void_p, c_void_p) class airMop(Structure): pass airMop._fields_ = [ ('ptr', c_void_p), ('mop', airMopper), ('when', c_int), ] airMopNew = libteem.airMopNew airMopNew.restype = POINTER(airArray) airMopNew.argtypes = [] airMopAdd = libteem.airMopAdd airMopAdd.restype = c_int airMopAdd.argtypes = [POINTER(airArray), c_void_p, airMopper, c_int] airMopSub = libteem.airMopSub airMopSub.restype = None airMopSub.argtypes = [POINTER(airArray), c_void_p, airMopper] airMopMem = libteem.airMopMem airMopMem.restype = None airMopMem.argtypes = [POINTER(airArray), c_void_p, c_int] airMopUnMem = libteem.airMopUnMem airMopUnMem.restype = None airMopUnMem.argtypes = [POINTER(airArray), c_void_p] airMopPrint = libteem.airMopPrint airMopPrint.restype = None airMopPrint.argtypes = [POINTER(airArray), c_void_p, c_int] airMopDone = libteem.airMopDone airMopDone.restype = None airMopDone.argtypes = [POINTER(airArray), c_int] airMopError = libteem.airMopError airMopError.restype = None airMopError.argtypes = [POINTER(airArray)] airMopOkay = libteem.airMopOkay airMopOkay.restype = None airMopOkay.argtypes = [POINTER(airArray)] airMopDebug = libteem.airMopDebug airMopDebug.restype = None airMopDebug.argtypes = [POINTER(airArray)] airMopSingleDone = libteem.airMopSingleDone airMopSingleDone.restype = None airMopSingleDone.argtypes = [POINTER(airArray), c_void_p, c_int] airMopSingleError = libteem.airMopSingleError airMopSingleError.restype = None airMopSingleError.argtypes = [POINTER(airArray), c_void_p] airMopSingleOkay = libteem.airMopSingleOkay airMopSingleOkay.restype = None airMopSingleOkay.argtypes = [POINTER(airArray), c_void_p] alan_t = c_float class alanContext_t(Structure): pass class Nrrd(Structure): pass alanContext_t._fields_ = [ ('dim', c_uint), ('size', c_uint * 3), ('verbose', c_int), ('wrap', c_int), ('textureType', c_int), ('oversample', c_int), ('homogAniso', c_int), ('numThreads', c_int), ('frameInterval', c_int), ('saveInterval', c_int), ('maxIteration', c_int), ('constFilename', c_int), ('K', alan_t), ('F', alan_t), ('deltaX', alan_t), ('minAverageChange', alan_t), ('maxPixelChange', alan_t), ('alpha', alan_t), ('beta', alan_t), ('react', alan_t), ('deltaT', alan_t), ('initA', alan_t), ('initB', alan_t), ('diffA', alan_t), ('diffB', alan_t), ('randRange', alan_t), ('nten', POINTER(Nrrd)), ('perIteration', CFUNCTYPE(c_int, POINTER(alanContext_t), c_int)), ('iter', c_int), ('_nlev', POINTER(Nrrd) * 2), ('nlev', POINTER(Nrrd)), ('nparm', POINTER(Nrrd)), ('averageChange', alan_t), ('changeCount', c_int), ('changeMutex', POINTER(airThreadMutex)), ('iterBarrier', POINTER(airThreadBarrier)), ('stop', c_int), ] alanContext = alanContext_t alanPresent = (c_int).in_dll(libteem, 'alanPresent') alanBiffKey = (STRING).in_dll(libteem, 'alanBiffKey') alanContextNew = libteem.alanContextNew alanContextNew.restype = POINTER(alanContext) alanContextNew.argtypes = [] alanContextNix = libteem.alanContextNix alanContextNix.restype = POINTER(alanContext) alanContextNix.argtypes = [POINTER(alanContext)] alanDimensionSet = libteem.alanDimensionSet alanDimensionSet.restype = c_int alanDimensionSet.argtypes = [POINTER(alanContext), c_int] alan2DSizeSet = libteem.alan2DSizeSet alan2DSizeSet.restype = c_int alan2DSizeSet.argtypes = [POINTER(alanContext), c_int, c_int] alan3DSizeSet = libteem.alan3DSizeSet alan3DSizeSet.restype = c_int alan3DSizeSet.argtypes = [POINTER(alanContext), c_int, c_int, c_int] alanTensorSet = libteem.alanTensorSet alanTensorSet.restype = c_int alanTensorSet.argtypes = [POINTER(alanContext), POINTER(Nrrd), c_int] alanParmSet = libteem.alanParmSet alanParmSet.restype = c_int alanParmSet.argtypes = [POINTER(alanContext), c_int, c_double] alanStop = (POINTER(airEnum)).in_dll(libteem, 'alanStop') alanUpdate = libteem.alanUpdate alanUpdate.restype = c_int alanUpdate.argtypes = [POINTER(alanContext)] class NrrdAxisInfo(Structure): pass NrrdAxisInfo._pack_ = 4 NrrdAxisInfo._fields_ = [ ('size', c_size_t), ('spacing', c_double), ('thickness', c_double), ('min', c_double), ('max', c_double), ('spaceDirection', c_double * 8), ('center', c_int), ('kind', c_int), ('label', STRING), ('units', STRING), ] Nrrd._pack_ = 4 Nrrd._fields_ = [ ('data', c_void_p), ('type', c_int), ('dim', c_uint), ('axis', NrrdAxisInfo * 16), ('content', STRING), ('sampleUnits', STRING), ('space', c_int), ('spaceDim', c_uint), ('spaceUnits', STRING * 8), ('spaceOrigin', c_double * 8), ('measurementFrame', c_double * 8 * 8), ('blockSize', c_size_t), ('oldMin', c_double), ('oldMax', c_double), ('ptr', c_void_p), ('cmt', POINTER(STRING)), ('cmtArr', POINTER(airArray)), ('kvp', POINTER(STRING)), ('kvpArr', POINTER(airArray)), ] alanInit = libteem.alanInit alanInit.restype = c_int alanInit.argtypes = [POINTER(alanContext), POINTER(Nrrd), POINTER(Nrrd)] alanRun = libteem.alanRun alanRun.restype = c_int alanRun.argtypes = [POINTER(alanContext)] class baneRange(Structure): pass baneRange._pack_ = 4 baneRange._fields_ = [ ('name', c_char * 129), ('type', c_int), ('center', c_double), ('answer', CFUNCTYPE(c_int, POINTER(c_double), POINTER(c_double), c_double, c_double)), ] class baneInc_t(Structure): pass baneInc_t._pack_ = 4 baneInc_t._fields_ = [ ('name', c_char * 129), ('type', c_int), ('S', c_double), ('SS', c_double), ('num', c_int), ('nhist', POINTER(Nrrd)), ('range', POINTER(baneRange)), ('parm', c_double * 5), ('process', CFUNCTYPE(None, POINTER(baneInc_t), c_double) * 2), ('answer', CFUNCTYPE(c_int, POINTER(c_double), POINTER(c_double), POINTER(Nrrd), POINTER(c_double), POINTER(baneRange))), ] baneInc = baneInc_t class baneClip(Structure): pass baneClip._pack_ = 4 baneClip._fields_ = [ ('name', c_char * 129), ('type', c_int), ('parm', c_double * 5), ('answer', CFUNCTYPE(c_int, POINTER(c_int), POINTER(Nrrd), POINTER(c_double))), ] class baneMeasr_t(Structure): pass gageQuery = c_ubyte * 32 baneMeasr_t._pack_ = 4 baneMeasr_t._fields_ = [ ('name', c_char * 129), ('type', c_int), ('parm', c_double * 5), ('query', gageQuery), ('range', POINTER(baneRange)), ('offset0', c_int), ('answer', CFUNCTYPE(c_double, POINTER(baneMeasr_t), POINTER(c_double), POINTER(c_double))), ] baneMeasr = baneMeasr_t class baneAxis(Structure): pass baneAxis._fields_ = [ ('res', c_uint), ('measr', POINTER(baneMeasr)), ('inc', POINTER(baneInc)), ] class baneHVolParm(Structure): pass class NrrdKernel(Structure): pass NrrdKernel._fields_ = [ ('name', c_char * 129), ('numParm', c_uint), ('support', CFUNCTYPE(c_double, POINTER(c_double))), ('integral', CFUNCTYPE(c_double, POINTER(c_double))), ('eval1_f', CFUNCTYPE(c_float, c_float, POINTER(c_double))), ('evalN_f', CFUNCTYPE(None, POINTER(c_float), POINTER(c_float), c_size_t, POINTER(c_double))), ('eval1_d', CFUNCTYPE(c_double, c_double, POINTER(c_double))), ('evalN_d', CFUNCTYPE(None, POINTER(c_double), POINTER(c_double), c_size_t, POINTER(c_double))), ] baneHVolParm._pack_ = 4 baneHVolParm._fields_ = [ ('verbose', c_int), ('makeMeasrVol', c_int), ('renormalize', c_int), ('k3pack', c_int), ('k', POINTER(NrrdKernel) * 8), ('kparm', c_double * 8 * 8), ('clip', POINTER(baneClip)), ('incLimit', c_double), ('axis', baneAxis * 3), ('measrVol', POINTER(Nrrd)), ('measrVolDone', c_int), ] baneBiffKey = (STRING).in_dll(libteem, 'baneBiffKey') baneDefVerbose = (c_int).in_dll(libteem, 'baneDefVerbose') baneDefMakeMeasrVol = (c_int).in_dll(libteem, 'baneDefMakeMeasrVol') baneDefIncLimit = (c_double).in_dll(libteem, 'baneDefIncLimit') baneDefRenormalize = (c_int).in_dll(libteem, 'baneDefRenormalize') baneDefPercHistBins = (c_int).in_dll(libteem, 'baneDefPercHistBins') baneStateHistEqBins = (c_int).in_dll(libteem, 'baneStateHistEqBins') baneStateHistEqSmart = (c_int).in_dll(libteem, 'baneStateHistEqSmart') baneHack = (c_int).in_dll(libteem, 'baneHack') baneRangeNew = libteem.baneRangeNew baneRangeNew.restype = POINTER(baneRange) baneRangeNew.argtypes = [c_int] baneRangeCopy = libteem.baneRangeCopy baneRangeCopy.restype = POINTER(baneRange) baneRangeCopy.argtypes = [POINTER(baneRange)] baneRangeAnswer = libteem.baneRangeAnswer baneRangeAnswer.restype = c_int baneRangeAnswer.argtypes = [POINTER(baneRange), POINTER(c_double), POINTER(c_double), c_double, c_double] baneRangeNix = libteem.baneRangeNix baneRangeNix.restype = POINTER(baneRange) baneRangeNix.argtypes = [POINTER(baneRange)] baneIncNew = libteem.baneIncNew baneIncNew.restype = POINTER(baneInc) baneIncNew.argtypes = [c_int, POINTER(baneRange), POINTER(c_double)] baneIncProcess = libteem.baneIncProcess baneIncProcess.restype = None baneIncProcess.argtypes = [POINTER(baneInc), c_int, c_double] baneIncAnswer = libteem.baneIncAnswer baneIncAnswer.restype = c_int baneIncAnswer.argtypes = [POINTER(baneInc), POINTER(c_double), POINTER(c_double)] baneIncCopy = libteem.baneIncCopy baneIncCopy.restype = POINTER(baneInc) baneIncCopy.argtypes = [POINTER(baneInc)] baneIncNix = libteem.baneIncNix baneIncNix.restype = POINTER(baneInc) baneIncNix.argtypes = [POINTER(baneInc)] baneClipNew = libteem.baneClipNew baneClipNew.restype = POINTER(baneClip) baneClipNew.argtypes = [c_int, POINTER(c_double)] baneClipAnswer = libteem.baneClipAnswer baneClipAnswer.restype = c_int baneClipAnswer.argtypes = [POINTER(c_int), POINTER(baneClip), POINTER(Nrrd)] baneClipCopy = libteem.baneClipCopy baneClipCopy.restype = POINTER(baneClip) baneClipCopy.argtypes = [POINTER(baneClip)] baneClipNix = libteem.baneClipNix baneClipNix.restype = POINTER(baneClip) baneClipNix.argtypes = [POINTER(baneClip)] baneMeasrNew = libteem.baneMeasrNew baneMeasrNew.restype = POINTER(baneMeasr) baneMeasrNew.argtypes = [c_int, POINTER(c_double)] class gageContext_t(Structure): pass gageContext = gageContext_t baneMeasrAnswer = libteem.baneMeasrAnswer baneMeasrAnswer.restype = c_double baneMeasrAnswer.argtypes = [POINTER(baneMeasr), POINTER(gageContext)] baneMeasrCopy = libteem.baneMeasrCopy baneMeasrCopy.restype = POINTER(baneMeasr) baneMeasrCopy.argtypes = [POINTER(baneMeasr)] baneMeasrNix = libteem.baneMeasrNix baneMeasrNix.restype = POINTER(baneMeasr) baneMeasrNix.argtypes = [POINTER(baneMeasr)] banePresent = (c_int).in_dll(libteem, 'banePresent') baneHVolParmNew = libteem.baneHVolParmNew baneHVolParmNew.restype = POINTER(baneHVolParm) baneHVolParmNew.argtypes = [] baneHVolParmGKMSInit = libteem.baneHVolParmGKMSInit baneHVolParmGKMSInit.restype = None baneHVolParmGKMSInit.argtypes = [POINTER(baneHVolParm)] baneHVolParmAxisSet = libteem.baneHVolParmAxisSet baneHVolParmAxisSet.restype = None baneHVolParmAxisSet.argtypes = [POINTER(baneHVolParm), c_uint, c_uint, POINTER(baneMeasr), POINTER(baneInc)] baneHVolParmClipSet = libteem.baneHVolParmClipSet baneHVolParmClipSet.restype = None baneHVolParmClipSet.argtypes = [POINTER(baneHVolParm), POINTER(baneClip)] baneHVolParmNix = libteem.baneHVolParmNix baneHVolParmNix.restype = POINTER(baneHVolParm) baneHVolParmNix.argtypes = [POINTER(baneHVolParm)] baneInputCheck = libteem.baneInputCheck baneInputCheck.restype = c_int baneInputCheck.argtypes = [POINTER(Nrrd), POINTER(baneHVolParm)] baneHVolCheck = libteem.baneHVolCheck baneHVolCheck.restype = c_int baneHVolCheck.argtypes = [POINTER(Nrrd)] baneInfoCheck = libteem.baneInfoCheck baneInfoCheck.restype = c_int baneInfoCheck.argtypes = [POINTER(Nrrd), c_int] banePosCheck = libteem.banePosCheck banePosCheck.restype = c_int banePosCheck.argtypes = [POINTER(Nrrd), c_int] baneBcptsCheck = libteem.baneBcptsCheck baneBcptsCheck.restype = c_int baneBcptsCheck.argtypes = [POINTER(Nrrd)] baneProbe = libteem.baneProbe baneProbe.restype = None baneProbe.argtypes = [POINTER(c_double), POINTER(Nrrd), POINTER(baneHVolParm), POINTER(gageContext), c_uint, c_uint, c_uint] baneFindInclusion = libteem.baneFindInclusion baneFindInclusion.restype = c_int baneFindInclusion.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(Nrrd), POINTER(baneHVolParm), POINTER(gageContext)] baneMakeHVol = libteem.baneMakeHVol baneMakeHVol.restype = c_int baneMakeHVol.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(baneHVolParm)] baneGKMSHVol = libteem.baneGKMSHVol baneGKMSHVol.restype = POINTER(Nrrd) baneGKMSHVol.argtypes = [POINTER(Nrrd), c_float, c_float] baneOpacInfo = libteem.baneOpacInfo baneOpacInfo.restype = c_int baneOpacInfo.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int, c_int] bane1DOpacInfoFrom2D = libteem.bane1DOpacInfoFrom2D bane1DOpacInfoFrom2D.restype = c_int bane1DOpacInfoFrom2D.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] baneSigmaCalc = libteem.baneSigmaCalc baneSigmaCalc.restype = c_int baneSigmaCalc.argtypes = [POINTER(c_float), POINTER(Nrrd)] banePosCalc = libteem.banePosCalc banePosCalc.restype = c_int banePosCalc.argtypes = [POINTER(Nrrd), c_float, c_float, POINTER(Nrrd)] baneOpacCalc = libteem.baneOpacCalc baneOpacCalc.restype = c_int baneOpacCalc.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd)] baneRawScatterplots = libteem.baneRawScatterplots baneRawScatterplots.restype = c_int baneRawScatterplots.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), c_int] class unrrduCmd(Structure): pass class hestParm(Structure): pass unrrduCmd._fields_ = [ ('name', STRING), ('info', STRING), ('main', CFUNCTYPE(c_int, c_int, POINTER(STRING), STRING, POINTER(hestParm))), ('hidden', c_int), ] baneGkms_txfCmd = (unrrduCmd).in_dll(libteem, 'baneGkms_txfCmd') baneGkms_hvolCmd = (unrrduCmd).in_dll(libteem, 'baneGkms_hvolCmd') baneGkms_infoCmd = (unrrduCmd).in_dll(libteem, 'baneGkms_infoCmd') baneGkms_miteCmd = (unrrduCmd).in_dll(libteem, 'baneGkms_miteCmd') baneGkms_scatCmd = (unrrduCmd).in_dll(libteem, 'baneGkms_scatCmd') baneGkms_pvgCmd = (unrrduCmd).in_dll(libteem, 'baneGkms_pvgCmd') baneGkms_opacCmd = (unrrduCmd).in_dll(libteem, 'baneGkms_opacCmd') baneGkmsMeasr = (POINTER(airEnum)).in_dll(libteem, 'baneGkmsMeasr') baneGkmsCmdList = (POINTER(unrrduCmd) * 0).in_dll(libteem, 'baneGkmsCmdList') baneGkmsUsage = libteem.baneGkmsUsage baneGkmsUsage.restype = None baneGkmsUsage.argtypes = [STRING, POINTER(hestParm)] class hestCB(Structure): pass baneGkmsHestIncStrategy = (POINTER(hestCB)).in_dll(libteem, 'baneGkmsHestIncStrategy') baneGkmsHestBEF = (POINTER(hestCB)).in_dll(libteem, 'baneGkmsHestBEF') baneGkmsHestGthresh = (POINTER(hestCB)).in_dll(libteem, 'baneGkmsHestGthresh') class biffMsg(Structure): pass biffMsg._fields_ = [ ('key', STRING), ('err', POINTER(STRING)), ('errNum', c_uint), ('errArr', POINTER(airArray)), ] biffPresent = (c_int).in_dll(libteem, 'biffPresent') biffMsgNew = libteem.biffMsgNew biffMsgNew.restype = POINTER(biffMsg) biffMsgNew.argtypes = [STRING] biffMsgNix = libteem.biffMsgNix biffMsgNix.restype = POINTER(biffMsg) biffMsgNix.argtypes = [POINTER(biffMsg)] biffMsgAdd = libteem.biffMsgAdd biffMsgAdd.restype = None biffMsgAdd.argtypes = [POINTER(biffMsg), STRING] biffMsgClear = libteem.biffMsgClear biffMsgClear.restype = None biffMsgClear.argtypes = [POINTER(biffMsg)] biffMsgLineLenMax = libteem.biffMsgLineLenMax biffMsgLineLenMax.restype = c_uint biffMsgLineLenMax.argtypes = [POINTER(biffMsg)] biffMsgMove = libteem.biffMsgMove biffMsgMove.restype = None biffMsgMove.argtypes = [POINTER(biffMsg), POINTER(biffMsg), STRING] biffMsgAddf = libteem.biffMsgAddf biffMsgAddf.restype = None biffMsgAddf.argtypes = [POINTER(biffMsg), STRING] biffMsgMovef = libteem.biffMsgMovef biffMsgMovef.restype = None biffMsgMovef.argtypes = [POINTER(biffMsg), POINTER(biffMsg), STRING] biffMsgErrNum = libteem.biffMsgErrNum biffMsgErrNum.restype = c_uint biffMsgErrNum.argtypes = [POINTER(biffMsg)] biffMsgStrlen = libteem.biffMsgStrlen biffMsgStrlen.restype = c_uint biffMsgStrlen.argtypes = [POINTER(biffMsg)] biffMsgStrSet = libteem.biffMsgStrSet biffMsgStrSet.restype = None biffMsgStrSet.argtypes = [STRING, POINTER(biffMsg)] biffMsgStrAlloc = libteem.biffMsgStrAlloc biffMsgStrAlloc.restype = STRING biffMsgStrAlloc.argtypes = [POINTER(biffMsg)] biffMsgStrGet = libteem.biffMsgStrGet biffMsgStrGet.restype = STRING biffMsgStrGet.argtypes = [POINTER(biffMsg)] biffMsgNoop = (POINTER(biffMsg)).in_dll(libteem, 'biffMsgNoop') biffAdd = libteem.biffAdd biffAdd.restype = None biffAdd.argtypes = [STRING, STRING] biffAddf = libteem.biffAddf biffAddf.restype = None biffAddf.argtypes = [STRING, STRING] biffMaybeAdd = libteem.biffMaybeAdd biffMaybeAdd.restype = None biffMaybeAdd.argtypes = [STRING, STRING, c_int] biffMaybeAddf = libteem.biffMaybeAddf biffMaybeAddf.restype = None biffMaybeAddf.argtypes = [c_int, STRING, STRING] biffGet = libteem.biffGet biffGet.restype = STRING biffGet.argtypes = [STRING] biffGetStrlen = libteem.biffGetStrlen biffGetStrlen.restype = c_uint biffGetStrlen.argtypes = [STRING] biffSetStr = libteem.biffSetStr biffSetStr.restype = None biffSetStr.argtypes = [STRING, STRING] biffCheck = libteem.biffCheck biffCheck.restype = c_uint biffCheck.argtypes = [STRING] biffMove = libteem.biffMove biffMove.restype = None biffMove.argtypes = [STRING, STRING, STRING] biffMovef = libteem.biffMovef biffMovef.restype = None biffMovef.argtypes = [STRING, STRING, STRING] biffSetStrDone = libteem.biffSetStrDone biffSetStrDone.restype = None biffSetStrDone.argtypes = [STRING, STRING] biffDone = libteem.biffDone biffDone.restype = None biffDone.argtypes = [STRING] biffGetDone = libteem.biffGetDone biffGetDone.restype = STRING biffGetDone.argtypes = [STRING] coil_t = c_float class coilMethod(Structure): pass coilMethod._fields_ = [ ('name', c_char * 129), ('type', c_int), ('numParm', c_int), ] class coilKind(Structure): pass coilKind._fields_ = [ ('name', c_char * 129), ('valLen', c_uint), ('filter', CFUNCTYPE(None, POINTER(coil_t), c_int, c_int, c_int, POINTER(POINTER(coil_t)), POINTER(c_double), POINTER(c_double)) * 9), ('update', CFUNCTYPE(None, POINTER(coil_t), POINTER(coil_t))), ] class coilTask(Structure): pass class coilContext_t(Structure): pass coilTask._fields_ = [ ('cctx', POINTER(coilContext_t)), ('thread', POINTER(airThread)), ('threadIdx', c_uint), ('_iv3', POINTER(coil_t)), ('iv3', POINTER(POINTER(coil_t))), ('iv3Fill', CFUNCTYPE(None, POINTER(POINTER(coil_t)), POINTER(coil_t), c_uint, c_int, c_int, c_int, c_int, c_int, c_int, c_int)), ('returnPtr', c_void_p), ] coilContext_t._pack_ = 4 coilContext_t._fields_ = [ ('nin', POINTER(Nrrd)), ('kind', POINTER(coilKind)), ('method', POINTER(coilMethod)), ('radius', c_uint), ('numThreads', c_uint), ('verbose', c_int), ('parm', c_double * 6), ('iter', c_uint), ('size', c_size_t * 3), ('nextSlice', c_size_t), ('spacing', c_double * 3), ('nvol', POINTER(Nrrd)), ('finished', c_int), ('todoFilter', c_int), ('todoUpdate', c_int), ('nextSliceMutex', POINTER(airThreadMutex)), ('task', POINTER(POINTER(coilTask))), ('filterBarrier', POINTER(airThreadBarrier)), ('updateBarrier', POINTER(airThreadBarrier)), ] coilContext = coilContext_t coilPresent = (c_int).in_dll(libteem, 'coilPresent') coilBiffKey = (STRING).in_dll(libteem, 'coilBiffKey') coilDefaultRadius = (c_int).in_dll(libteem, 'coilDefaultRadius') coilVerbose = (c_int).in_dll(libteem, 'coilVerbose') coilMethodType = (POINTER(airEnum)).in_dll(libteem, 'coilMethodType') coilKindType = (POINTER(airEnum)).in_dll(libteem, 'coilKindType') coilKindScalar = (POINTER(coilKind)).in_dll(libteem, 'coilKindScalar') coilKindArray = (POINTER(coilKind) * 4).in_dll(libteem, 'coilKindArray') coilKind7Tensor = (POINTER(coilKind)).in_dll(libteem, 'coilKind7Tensor') coilMethodTesting = (POINTER(coilMethod)).in_dll(libteem, 'coilMethodTesting') coilMethodArray = (POINTER(coilMethod) * 9).in_dll(libteem, 'coilMethodArray') coilContextNew = libteem.coilContextNew coilContextNew.restype = POINTER(coilContext) coilContextNew.argtypes = [] coilVolumeCheck = libteem.coilVolumeCheck coilVolumeCheck.restype = c_int coilVolumeCheck.argtypes = [POINTER(Nrrd), POINTER(coilKind)] coilContextAllSet = libteem.coilContextAllSet coilContextAllSet.restype = c_int coilContextAllSet.argtypes = [POINTER(coilContext), POINTER(Nrrd), POINTER(coilKind), POINTER(coilMethod), c_uint, c_uint, c_int, POINTER(c_double)] coilOutputGet = libteem.coilOutputGet coilOutputGet.restype = c_int coilOutputGet.argtypes = [POINTER(Nrrd), POINTER(coilContext)] coilContextNix = libteem.coilContextNix coilContextNix.restype = POINTER(coilContext) coilContextNix.argtypes = [POINTER(coilContext)] coilStart = libteem.coilStart coilStart.restype = c_int coilStart.argtypes = [POINTER(coilContext)] coilIterate = libteem.coilIterate coilIterate.restype = c_int coilIterate.argtypes = [POINTER(coilContext), c_int] coilFinish = libteem.coilFinish coilFinish.restype = c_int coilFinish.argtypes = [POINTER(coilContext)] class dyeColor(Structure): pass dyeColor._fields_ = [ ('val', c_float * 3 * 2), ('xWhite', c_float), ('yWhite', c_float), ('spc', c_byte * 2), ('ii', c_byte), ] dyePresent = (c_int).in_dll(libteem, 'dyePresent') dyeBiffKey = (STRING).in_dll(libteem, 'dyeBiffKey') dyeSpaceToStr = (c_char * 129 * 0).in_dll(libteem, 'dyeSpaceToStr') dyeStrToSpace = libteem.dyeStrToSpace dyeStrToSpace.restype = c_int dyeStrToSpace.argtypes = [STRING] dyeColorInit = libteem.dyeColorInit dyeColorInit.restype = POINTER(dyeColor) dyeColorInit.argtypes = [POINTER(dyeColor)] dyeColorSet = libteem.dyeColorSet dyeColorSet.restype = POINTER(dyeColor) dyeColorSet.argtypes = [POINTER(dyeColor), c_int, c_float, c_float, c_float] dyeColorGet = libteem.dyeColorGet dyeColorGet.restype = c_int dyeColorGet.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(dyeColor)] dyeColorGetAs = libteem.dyeColorGetAs dyeColorGetAs.restype = c_int dyeColorGetAs.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(dyeColor), c_int] dyeColorNew = libteem.dyeColorNew dyeColorNew.restype = POINTER(dyeColor) dyeColorNew.argtypes = [] dyeColorCopy = libteem.dyeColorCopy dyeColorCopy.restype = POINTER(dyeColor) dyeColorCopy.argtypes = [POINTER(dyeColor), POINTER(dyeColor)] dyeColorNix = libteem.dyeColorNix dyeColorNix.restype = POINTER(dyeColor) dyeColorNix.argtypes = [POINTER(dyeColor)] dyeColorParse = libteem.dyeColorParse dyeColorParse.restype = c_int dyeColorParse.argtypes = [POINTER(dyeColor), STRING] dyeColorSprintf = libteem.dyeColorSprintf dyeColorSprintf.restype = STRING dyeColorSprintf.argtypes = [STRING, POINTER(dyeColor)] dyeConverter = CFUNCTYPE(None, POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, c_float, c_float) dyeRGBtoHSV = libteem.dyeRGBtoHSV dyeRGBtoHSV.restype = None dyeRGBtoHSV.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, c_float, c_float] dyeHSVtoRGB = libteem.dyeHSVtoRGB dyeHSVtoRGB.restype = None dyeHSVtoRGB.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, c_float, c_float] dyeRGBtoHSL = libteem.dyeRGBtoHSL dyeRGBtoHSL.restype = None dyeRGBtoHSL.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, c_float, c_float] dyeHSLtoRGB = libteem.dyeHSLtoRGB dyeHSLtoRGB.restype = None dyeHSLtoRGB.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, c_float, c_float] dyeRGBtoXYZ = libteem.dyeRGBtoXYZ dyeRGBtoXYZ.restype = None dyeRGBtoXYZ.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, c_float, c_float] dyeXYZtoRGB = libteem.dyeXYZtoRGB dyeXYZtoRGB.restype = None dyeXYZtoRGB.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, c_float, c_float] dyeXYZtoLAB = libteem.dyeXYZtoLAB dyeXYZtoLAB.restype = None dyeXYZtoLAB.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, c_float, c_float] dyeXYZtoLUV = libteem.dyeXYZtoLUV dyeXYZtoLUV.restype = None dyeXYZtoLUV.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, c_float, c_float] dyeLABtoXYZ = libteem.dyeLABtoXYZ dyeLABtoXYZ.restype = None dyeLABtoXYZ.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, c_float, c_float] dyeLUVtoXYZ = libteem.dyeLUVtoXYZ dyeLUVtoXYZ.restype = None dyeLUVtoXYZ.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, c_float, c_float] dyeSimpleConvert = (dyeConverter * 7 * 7).in_dll(libteem, 'dyeSimpleConvert') dyeConvert = libteem.dyeConvert dyeConvert.restype = c_int dyeConvert.argtypes = [POINTER(dyeColor), c_int] echoPos_t = c_double echoCol_t = c_float class echoRTParm(Structure): pass echoRTParm._pack_ = 4 echoRTParm._fields_ = [ ('jitterType', c_int), ('reuseJitter', c_int), ('permuteJitter', c_int), ('textureNN', c_int), ('numSamples', c_int), ('imgResU', c_int), ('imgResV', c_int), ('maxRecDepth', c_int), ('renderLights', c_int), ('renderBoxes', c_int), ('seedRand', c_int), ('sqNRI', c_int), ('numThreads', c_int), ('sqTol', echoPos_t), ('shadow', echoCol_t), ('glassC', echoCol_t), ('aperture', c_float), ('timeGamma', c_float), ('boxOpac', c_float), ('maxRecCol', echoCol_t * 3), ] class echoGlobalState(Structure): pass class limnCamera_t(Structure): pass limnCamera = limnCamera_t class echoScene_t(Structure): pass echoGlobalState._pack_ = 4 echoGlobalState._fields_ = [ ('verbose', c_int), ('time', c_double), ('nraw', POINTER(Nrrd)), ('cam', POINTER(limnCamera)), ('scene', POINTER(echoScene_t)), ('parm', POINTER(echoRTParm)), ('workIdx', c_int), ('workMutex', POINTER(airThreadMutex)), ] class echoThreadState(Structure): pass echoThreadState._fields_ = [ ('thread', POINTER(airThread)), ('gstate', POINTER(echoGlobalState)), ('verbose', c_int), ('threadIdx', c_int), ('depth', c_int), ('nperm', POINTER(Nrrd)), ('njitt', POINTER(Nrrd)), ('permBuff', POINTER(c_uint)), ('jitt', POINTER(echoPos_t)), ('chanBuff', POINTER(echoCol_t)), ('rst', POINTER(airRandMTState)), ('returnPtr', c_void_p), ] class echoObject(Structure): pass echoObject._fields_ = [ ('type', c_byte), ('matter', c_ubyte), ('rgba', echoCol_t * 4), ('mat', echoCol_t * 4), ('ntext', POINTER(Nrrd)), ] class echoSphere(Structure): pass echoSphere._pack_ = 4 echoSphere._fields_ = [ ('type', c_byte), ('matter', c_ubyte), ('rgba', echoCol_t * 4), ('mat', echoCol_t * 4), ('ntext', POINTER(Nrrd)), ('pos', echoPos_t * 3), ('rad', echoPos_t), ] class echoCylinder(Structure): pass echoCylinder._fields_ = [ ('type', c_byte), ('matter', c_ubyte), ('rgba', echoCol_t * 4), ('mat', echoCol_t * 4), ('ntext', POINTER(Nrrd)), ('axis', c_int), ] class echoSuperquad(Structure): pass echoSuperquad._pack_ = 4 echoSuperquad._fields_ = [ ('type', c_byte), ('matter', c_ubyte), ('rgba', echoCol_t * 4), ('mat', echoCol_t * 4), ('ntext', POINTER(Nrrd)), ('axis', c_int), ('A', echoPos_t), ('B', echoPos_t), ] class echoCube(Structure): pass echoCube._fields_ = [ ('type', c_byte), ('matter', c_ubyte), ('rgba', echoCol_t * 4), ('mat', echoCol_t * 4), ('ntext', POINTER(Nrrd)), ] class echoTriangle(Structure): pass echoTriangle._pack_ = 4 echoTriangle._fields_ = [ ('type', c_byte), ('matter', c_ubyte), ('rgba', echoCol_t * 4), ('mat', echoCol_t * 4), ('ntext', POINTER(Nrrd)), ('vert', echoPos_t * 3 * 3), ] class echoRectangle(Structure): pass echoRectangle._pack_ = 4 echoRectangle._fields_ = [ ('type', c_byte), ('matter', c_ubyte), ('rgba', echoCol_t * 4), ('mat', echoCol_t * 4), ('ntext', POINTER(Nrrd)), ('origin', echoPos_t * 3), ('edge0', echoPos_t * 3), ('edge1', echoPos_t * 3), ] class echoTriMesh(Structure): pass echoTriMesh._pack_ = 4 echoTriMesh._fields_ = [ ('type', c_byte), ('matter', c_ubyte), ('rgba', echoCol_t * 4), ('mat', echoCol_t * 4), ('ntext', POINTER(Nrrd)), ('meanvert', echoPos_t * 3), ('min', echoPos_t * 3), ('max', echoPos_t * 3), ('numV', c_int), ('numF', c_int), ('pos', POINTER(echoPos_t)), ('vert', POINTER(c_int)), ] class echoIsosurface(Structure): pass echoIsosurface._fields_ = [ ('type', c_byte), ('matter', c_ubyte), ('rgba', echoCol_t * 4), ('mat', echoCol_t * 4), ('ntext', POINTER(Nrrd)), ('volume', POINTER(Nrrd)), ('value', c_float), ] class echoAABBox(Structure): pass echoAABBox._pack_ = 4 echoAABBox._fields_ = [ ('type', c_byte), ('obj', POINTER(echoObject)), ('min', echoPos_t * 3), ('max', echoPos_t * 3), ] class echoSplit(Structure): pass echoSplit._pack_ = 4 echoSplit._fields_ = [ ('type', c_byte), ('axis', c_int), ('min0', echoPos_t * 3), ('max0', echoPos_t * 3), ('min1', echoPos_t * 3), ('max1', echoPos_t * 3), ('obj0', POINTER(echoObject)), ('obj1', POINTER(echoObject)), ] class echoList(Structure): pass echoList._fields_ = [ ('type', c_byte), ('obj', POINTER(POINTER(echoObject))), ('objArr', POINTER(airArray)), ] class echoInstance(Structure): pass echoInstance._pack_ = 4 echoInstance._fields_ = [ ('type', c_byte), ('Mi', echoPos_t * 16), ('M', echoPos_t * 16), ('obj', POINTER(echoObject)), ] echoScene_t._fields_ = [ ('cat', POINTER(POINTER(echoObject))), ('catArr', POINTER(airArray)), ('rend', POINTER(POINTER(echoObject))), ('rendArr', POINTER(airArray)), ('light', POINTER(POINTER(echoObject))), ('lightArr', POINTER(airArray)), ('nrrd', POINTER(POINTER(Nrrd))), ('nrrdArr', POINTER(airArray)), ('envmap', POINTER(Nrrd)), ('ambi', echoCol_t * 3), ('bkgr', echoCol_t * 3), ] echoScene = echoScene_t class echoRay(Structure): pass echoRay._pack_ = 4 echoRay._fields_ = [ ('from', echoPos_t * 3), ('dir', echoPos_t * 3), ('neer', echoPos_t), ('faar', echoPos_t), ('shadow', c_int), ('transp', echoCol_t), ] class echoIntx(Structure): pass echoIntx._pack_ = 4 echoIntx._fields_ = [ ('obj', POINTER(echoObject)), ('t', echoPos_t), ('u', echoPos_t), ('v', echoPos_t), ('norm', echoPos_t * 3), ('view', echoPos_t * 3), ('refl', echoPos_t * 3), ('pos', echoPos_t * 3), ('face', c_int), ('boxhits', c_int), ] class echoPtrPtrUnion(Union): pass echoPtrPtrUnion._fields_ = [ ('obj', POINTER(POINTER(POINTER(echoObject)))), ('nrd', POINTER(POINTER(POINTER(Nrrd)))), ('v', POINTER(c_void_p)), ] echoJitter = (POINTER(airEnum)).in_dll(libteem, 'echoJitter') echoType = (POINTER(airEnum)).in_dll(libteem, 'echoType') echoMatter = (POINTER(airEnum)).in_dll(libteem, 'echoMatter') echoPresent = (c_int).in_dll(libteem, 'echoPresent') echoBiffKey = (STRING).in_dll(libteem, 'echoBiffKey') echoRTParmNew = libteem.echoRTParmNew echoRTParmNew.restype = POINTER(echoRTParm) echoRTParmNew.argtypes = [] echoRTParmNix = libteem.echoRTParmNix echoRTParmNix.restype = POINTER(echoRTParm) echoRTParmNix.argtypes = [POINTER(echoRTParm)] echoGlobalStateNew = libteem.echoGlobalStateNew echoGlobalStateNew.restype = POINTER(echoGlobalState) echoGlobalStateNew.argtypes = [] echoGlobalStateNix = libteem.echoGlobalStateNix echoGlobalStateNix.restype = POINTER(echoGlobalState) echoGlobalStateNix.argtypes = [POINTER(echoGlobalState)] echoThreadStateNew = libteem.echoThreadStateNew echoThreadStateNew.restype = POINTER(echoThreadState) echoThreadStateNew.argtypes = [] echoThreadStateNix = libteem.echoThreadStateNix echoThreadStateNix.restype = POINTER(echoThreadState) echoThreadStateNix.argtypes = [POINTER(echoThreadState)] echoSceneNew = libteem.echoSceneNew echoSceneNew.restype = POINTER(echoScene) echoSceneNew.argtypes = [] echoSceneNix = libteem.echoSceneNix echoSceneNix.restype = POINTER(echoScene) echoSceneNix.argtypes = [POINTER(echoScene)] echoObjectNew = libteem.echoObjectNew echoObjectNew.restype = POINTER(echoObject) echoObjectNew.argtypes = [POINTER(echoScene), c_byte] echoObjectAdd = libteem.echoObjectAdd echoObjectAdd.restype = c_int echoObjectAdd.argtypes = [POINTER(echoScene), POINTER(echoObject)] echoObjectNix = libteem.echoObjectNix echoObjectNix.restype = POINTER(echoObject) echoObjectNix.argtypes = [POINTER(echoObject)] echoRoughSphereNew = libteem.echoRoughSphereNew echoRoughSphereNew.restype = POINTER(echoObject) echoRoughSphereNew.argtypes = [POINTER(echoScene), c_int, c_int, POINTER(echoPos_t)] echoBoundsGet = libteem.echoBoundsGet echoBoundsGet.restype = None echoBoundsGet.argtypes = [POINTER(echoPos_t), POINTER(echoPos_t), POINTER(echoObject)] echoListAdd = libteem.echoListAdd echoListAdd.restype = None echoListAdd.argtypes = [POINTER(echoObject), POINTER(echoObject)] echoListSplit = libteem.echoListSplit echoListSplit.restype = POINTER(echoObject) echoListSplit.argtypes = [POINTER(echoScene), POINTER(echoObject), c_int] echoListSplit3 = libteem.echoListSplit3 echoListSplit3.restype = POINTER(echoObject) echoListSplit3.argtypes = [POINTER(echoScene), POINTER(echoObject), c_int] echoSphereSet = libteem.echoSphereSet echoSphereSet.restype = None echoSphereSet.argtypes = [POINTER(echoObject), echoPos_t, echoPos_t, echoPos_t, echoPos_t] echoCylinderSet = libteem.echoCylinderSet echoCylinderSet.restype = None echoCylinderSet.argtypes = [POINTER(echoObject), c_int] echoSuperquadSet = libteem.echoSuperquadSet echoSuperquadSet.restype = None echoSuperquadSet.argtypes = [POINTER(echoObject), c_int, echoPos_t, echoPos_t] echoRectangleSet = libteem.echoRectangleSet echoRectangleSet.restype = None echoRectangleSet.argtypes = [POINTER(echoObject), echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t] echoTriangleSet = libteem.echoTriangleSet echoTriangleSet.restype = None echoTriangleSet.argtypes = [POINTER(echoObject), echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t] echoTriMeshSet = libteem.echoTriMeshSet echoTriMeshSet.restype = None echoTriMeshSet.argtypes = [POINTER(echoObject), c_int, POINTER(echoPos_t), c_int, POINTER(c_int)] echoInstanceSet = libteem.echoInstanceSet echoInstanceSet.restype = None echoInstanceSet.argtypes = [POINTER(echoObject), POINTER(echoPos_t), POINTER(echoObject)] echoObjectHasMatter = (c_int * 12).in_dll(libteem, 'echoObjectHasMatter') echoColorSet = libteem.echoColorSet echoColorSet.restype = None echoColorSet.argtypes = [POINTER(echoObject), echoCol_t, echoCol_t, echoCol_t, echoCol_t] echoMatterPhongSet = libteem.echoMatterPhongSet echoMatterPhongSet.restype = None echoMatterPhongSet.argtypes = [POINTER(echoScene), POINTER(echoObject), echoCol_t, echoCol_t, echoCol_t, echoCol_t] echoMatterGlassSet = libteem.echoMatterGlassSet echoMatterGlassSet.restype = None echoMatterGlassSet.argtypes = [POINTER(echoScene), POINTER(echoObject), echoCol_t, echoCol_t, echoCol_t, echoCol_t] echoMatterMetalSet = libteem.echoMatterMetalSet echoMatterMetalSet.restype = None echoMatterMetalSet.argtypes = [POINTER(echoScene), POINTER(echoObject), echoCol_t, echoCol_t, echoCol_t, echoCol_t] echoMatterLightSet = libteem.echoMatterLightSet echoMatterLightSet.restype = None echoMatterLightSet.argtypes = [POINTER(echoScene), POINTER(echoObject), echoCol_t, echoCol_t] echoMatterTextureSet = libteem.echoMatterTextureSet echoMatterTextureSet.restype = None echoMatterTextureSet.argtypes = [POINTER(echoScene), POINTER(echoObject), POINTER(Nrrd)] echoLightPosition = libteem.echoLightPosition echoLightPosition.restype = None echoLightPosition.argtypes = [POINTER(echoPos_t), POINTER(echoObject), POINTER(echoThreadState)] echoLightColor = libteem.echoLightColor echoLightColor.restype = None echoLightColor.argtypes = [POINTER(echoCol_t), echoPos_t, POINTER(echoObject), POINTER(echoRTParm), POINTER(echoThreadState)] echoEnvmapLookup = libteem.echoEnvmapLookup echoEnvmapLookup.restype = None echoEnvmapLookup.argtypes = [POINTER(echoCol_t), POINTER(echoPos_t), POINTER(Nrrd)] echoTextureLookup = libteem.echoTextureLookup echoTextureLookup.restype = None echoTextureLookup.argtypes = [POINTER(echoCol_t), POINTER(Nrrd), echoPos_t, echoPos_t, POINTER(echoRTParm)] echoIntxMaterialColor = libteem.echoIntxMaterialColor echoIntxMaterialColor.restype = None echoIntxMaterialColor.argtypes = [POINTER(echoCol_t), POINTER(echoIntx), POINTER(echoRTParm)] echoIntxLightColor = libteem.echoIntxLightColor echoIntxLightColor.restype = None echoIntxLightColor.argtypes = [POINTER(echoCol_t), POINTER(echoCol_t), POINTER(echoCol_t), echoCol_t, POINTER(echoIntx), POINTER(echoScene), POINTER(echoRTParm), POINTER(echoThreadState)] echoIntxFuzzify = libteem.echoIntxFuzzify echoIntxFuzzify.restype = None echoIntxFuzzify.argtypes = [POINTER(echoIntx), echoCol_t, POINTER(echoThreadState)] echoRayIntx = libteem.echoRayIntx echoRayIntx.restype = c_int echoRayIntx.argtypes = [POINTER(echoIntx), POINTER(echoRay), POINTER(echoScene), POINTER(echoRTParm), POINTER(echoThreadState)] echoIntxColor = libteem.echoIntxColor echoIntxColor.restype = None echoIntxColor.argtypes = [POINTER(echoCol_t), POINTER(echoIntx), POINTER(echoScene), POINTER(echoRTParm), POINTER(echoThreadState)] echoThreadStateInit = libteem.echoThreadStateInit echoThreadStateInit.restype = c_int echoThreadStateInit.argtypes = [c_int, POINTER(echoThreadState), POINTER(echoRTParm), POINTER(echoGlobalState)] echoJitterCompute = libteem.echoJitterCompute echoJitterCompute.restype = None echoJitterCompute.argtypes = [POINTER(echoRTParm), POINTER(echoThreadState)] echoRayColor = libteem.echoRayColor echoRayColor.restype = None echoRayColor.argtypes = [POINTER(echoCol_t), POINTER(echoRay), POINTER(echoScene), POINTER(echoRTParm), POINTER(echoThreadState)] echoChannelAverage = libteem.echoChannelAverage echoChannelAverage.restype = None echoChannelAverage.argtypes = [POINTER(echoCol_t), POINTER(echoRTParm), POINTER(echoThreadState)] echoRTRenderCheck = libteem.echoRTRenderCheck echoRTRenderCheck.restype = c_int echoRTRenderCheck.argtypes = [POINTER(Nrrd), POINTER(limnCamera), POINTER(echoScene), POINTER(echoRTParm), POINTER(echoGlobalState)] echoRTRender = libteem.echoRTRender echoRTRender.restype = c_int echoRTRender.argtypes = [POINTER(Nrrd), POINTER(limnCamera), POINTER(echoScene), POINTER(echoRTParm), POINTER(echoGlobalState)] elfPresent = (c_int).in_dll(libteem, 'elfPresent') class limnPolyData(Structure): pass class tijk_type_t(Structure): pass tijk_type = tijk_type_t elfGlyphHOME = libteem.elfGlyphHOME elfGlyphHOME.restype = c_float elfGlyphHOME.argtypes = [POINTER(limnPolyData), c_char, POINTER(c_float), POINTER(tijk_type), STRING, c_char] elfGlyphPolar = libteem.elfGlyphPolar elfGlyphPolar.restype = c_float elfGlyphPolar.argtypes = [POINTER(limnPolyData), c_char, POINTER(c_float), POINTER(tijk_type), STRING, c_char, c_char, POINTER(c_ubyte), POINTER(c_ubyte)] elfGlyphKDE = libteem.elfGlyphKDE elfGlyphKDE.restype = c_float elfGlyphKDE.argtypes = [POINTER(limnPolyData), c_char, POINTER(c_float), c_size_t, c_float, c_char] elfColorGlyphMaxima = libteem.elfColorGlyphMaxima elfColorGlyphMaxima.restype = c_int elfColorGlyphMaxima.argtypes = [POINTER(limnPolyData), c_char, POINTER(c_int), c_uint, POINTER(c_float), POINTER(tijk_type), c_char, c_float] class elfMaximaContext(Structure): pass class tijk_refine_rank1_parm_t(Structure): pass tijk_refine_rank1_parm = tijk_refine_rank1_parm_t elfMaximaContext._fields_ = [ ('num', c_uint), ('type', POINTER(tijk_type)), ('parm', POINTER(tijk_refine_rank1_parm)), ('refine', c_int), ('neighbors', POINTER(c_int)), ('nbstride', c_uint), ('vertices_f', POINTER(c_float)), ('vertices_d', POINTER(c_double)), ] elfMaximaContextNew = libteem.elfMaximaContextNew elfMaximaContextNew.restype = POINTER(elfMaximaContext) elfMaximaContextNew.argtypes = [POINTER(tijk_type), c_uint] elfMaximaContextNix = libteem.elfMaximaContextNix elfMaximaContextNix.restype = POINTER(elfMaximaContext) elfMaximaContextNix.argtypes = [POINTER(elfMaximaContext)] elfMaximaParmSet = libteem.elfMaximaParmSet elfMaximaParmSet.restype = None elfMaximaParmSet.argtypes = [POINTER(elfMaximaContext), POINTER(tijk_refine_rank1_parm)] elfMaximaRefineSet = libteem.elfMaximaRefineSet elfMaximaRefineSet.restype = None elfMaximaRefineSet.argtypes = [POINTER(elfMaximaContext), c_int] elfMaximaFind_d = libteem.elfMaximaFind_d elfMaximaFind_d.restype = c_int elfMaximaFind_d.argtypes = [POINTER(POINTER(c_double)), POINTER(POINTER(c_double)), POINTER(c_double), POINTER(elfMaximaContext)] elfMaximaFind_f = libteem.elfMaximaFind_f elfMaximaFind_f.restype = c_int elfMaximaFind_f.argtypes = [POINTER(POINTER(c_float)), POINTER(POINTER(c_float)), POINTER(c_float), POINTER(elfMaximaContext)] elfCart2Thetaphi_d = libteem.elfCart2Thetaphi_d elfCart2Thetaphi_d.restype = None elfCart2Thetaphi_d.argtypes = [POINTER(c_double), POINTER(c_double), c_uint] elfCart2Thetaphi_f = libteem.elfCart2Thetaphi_f elfCart2Thetaphi_f.restype = None elfCart2Thetaphi_f.argtypes = [POINTER(c_float), POINTER(c_float), c_uint] elfESHEstimMatrix_d = libteem.elfESHEstimMatrix_d elfESHEstimMatrix_d.restype = c_int elfESHEstimMatrix_d.argtypes = [POINTER(c_double), POINTER(c_double), c_uint, POINTER(c_double), c_uint, c_double, POINTER(c_double)] elfESHEstimMatrix_f = libteem.elfESHEstimMatrix_f elfESHEstimMatrix_f.restype = c_int elfESHEstimMatrix_f.argtypes = [POINTER(c_float), POINTER(c_float), c_uint, POINTER(c_float), c_uint, c_float, POINTER(c_float)] elfTenEstimMatrix_d = libteem.elfTenEstimMatrix_d elfTenEstimMatrix_d.restype = c_int elfTenEstimMatrix_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(tijk_type), POINTER(c_double), c_uint, POINTER(c_double)] elfTenEstimMatrix_f = libteem.elfTenEstimMatrix_f elfTenEstimMatrix_f.restype = c_int elfTenEstimMatrix_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(tijk_type), POINTER(c_float), c_uint, POINTER(c_float)] class elfSingleShellDWI(Structure): pass elfSingleShellDWI._fields_ = [ ('b0', c_float), ('b', c_float), ('dwis', POINTER(c_float)), ('grads', POINTER(c_float)), ('dwino', c_uint), ] elfKernelStick_f = libteem.elfKernelStick_f elfKernelStick_f.restype = c_int elfKernelStick_f.argtypes = [POINTER(c_float), c_uint, c_float, c_float, c_int] elfBallStickODF_f = libteem.elfBallStickODF_f elfBallStickODF_f.restype = c_int elfBallStickODF_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(elfSingleShellDWI), POINTER(c_float), c_uint, c_int] class elfBallStickParms(Structure): pass elfBallStickParms._pack_ = 4 elfBallStickParms._fields_ = [ ('d', c_float), ('fiberct', c_uint), ('fs', c_float * 4), ('vs', c_float * 9), ('stopreason', c_int), ('sqrerr', c_double), ('itr', c_double), ] elfBallStickPredict_f = libteem.elfBallStickPredict_f elfBallStickPredict_f.restype = c_int elfBallStickPredict_f.argtypes = [POINTER(elfBallStickParms), POINTER(c_float), POINTER(tijk_type), c_uint, c_float, c_float] elfBallStickOptimize_f = libteem.elfBallStickOptimize_f elfBallStickOptimize_f.restype = c_int elfBallStickOptimize_f.argtypes = [POINTER(elfBallStickParms), POINTER(elfSingleShellDWI)] ellPresent = (c_int).in_dll(libteem, 'ellPresent') ell_biff_key = (STRING).in_dll(libteem, 'ell_biff_key') ell_cubic_root = (POINTER(airEnum)).in_dll(libteem, 'ell_cubic_root') ell_debug = (c_int).in_dll(libteem, 'ell_debug') ell_3m_print_f = libteem.ell_3m_print_f ell_3m_print_f.restype = None ell_3m_print_f.argtypes = [POINTER(FILE), POINTER(c_float)] ell_3v_print_f = libteem.ell_3v_print_f ell_3v_print_f.restype = None ell_3v_print_f.argtypes = [POINTER(FILE), POINTER(c_float)] ell_3m_print_d = libteem.ell_3m_print_d ell_3m_print_d.restype = None ell_3m_print_d.argtypes = [POINTER(FILE), POINTER(c_double)] ell_3v_print_d = libteem.ell_3v_print_d ell_3v_print_d.restype = None ell_3v_print_d.argtypes = [POINTER(FILE), POINTER(c_double)] ell_4m_print_f = libteem.ell_4m_print_f ell_4m_print_f.restype = None ell_4m_print_f.argtypes = [POINTER(FILE), POINTER(c_float)] ell_4v_print_f = libteem.ell_4v_print_f ell_4v_print_f.restype = None ell_4v_print_f.argtypes = [POINTER(FILE), POINTER(c_float)] ell_4m_print_d = libteem.ell_4m_print_d ell_4m_print_d.restype = None ell_4m_print_d.argtypes = [POINTER(FILE), POINTER(c_double)] ell_4v_print_d = libteem.ell_4v_print_d ell_4v_print_d.restype = None ell_4v_print_d.argtypes = [POINTER(FILE), POINTER(c_double)] ell_4v_norm_f = libteem.ell_4v_norm_f ell_4v_norm_f.restype = None ell_4v_norm_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_3v_perp_f = libteem.ell_3v_perp_f ell_3v_perp_f.restype = None ell_3v_perp_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_3v_perp_d = libteem.ell_3v_perp_d ell_3v_perp_d.restype = None ell_3v_perp_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_3mv_mul_f = libteem.ell_3mv_mul_f ell_3mv_mul_f.restype = None ell_3mv_mul_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float)] ell_3mv_mul_d = libteem.ell_3mv_mul_d ell_3mv_mul_d.restype = None ell_3mv_mul_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_4mv_mul_f = libteem.ell_4mv_mul_f ell_4mv_mul_f.restype = None ell_4mv_mul_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float)] ell_4mv_mul_d = libteem.ell_4mv_mul_d ell_4mv_mul_d.restype = None ell_4mv_mul_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_3v_angle_f = libteem.ell_3v_angle_f ell_3v_angle_f.restype = c_float ell_3v_angle_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_3v_angle_d = libteem.ell_3v_angle_d ell_3v_angle_d.restype = c_double ell_3v_angle_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_3v_area_spherical_d = libteem.ell_3v_area_spherical_d ell_3v_area_spherical_d.restype = c_double ell_3v_area_spherical_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_3v_barycentric_spherical_d = libteem.ell_3v_barycentric_spherical_d ell_3v_barycentric_spherical_d.restype = None ell_3v_barycentric_spherical_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_3m_mul_f = libteem.ell_3m_mul_f ell_3m_mul_f.restype = None ell_3m_mul_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float)] ell_3m_mul_d = libteem.ell_3m_mul_d ell_3m_mul_d.restype = None ell_3m_mul_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_3m_pre_mul_f = libteem.ell_3m_pre_mul_f ell_3m_pre_mul_f.restype = None ell_3m_pre_mul_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_3m_pre_mul_d = libteem.ell_3m_pre_mul_d ell_3m_pre_mul_d.restype = None ell_3m_pre_mul_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_3m_post_mul_f = libteem.ell_3m_post_mul_f ell_3m_post_mul_f.restype = None ell_3m_post_mul_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_3m_post_mul_d = libteem.ell_3m_post_mul_d ell_3m_post_mul_d.restype = None ell_3m_post_mul_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_3m_det_f = libteem.ell_3m_det_f ell_3m_det_f.restype = c_float ell_3m_det_f.argtypes = [POINTER(c_float)] ell_3m_det_d = libteem.ell_3m_det_d ell_3m_det_d.restype = c_double ell_3m_det_d.argtypes = [POINTER(c_double)] ell_3m_inv_f = libteem.ell_3m_inv_f ell_3m_inv_f.restype = None ell_3m_inv_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_3m_inv_d = libteem.ell_3m_inv_d ell_3m_inv_d.restype = None ell_3m_inv_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_4m_mul_f = libteem.ell_4m_mul_f ell_4m_mul_f.restype = None ell_4m_mul_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float)] ell_4m_mul_d = libteem.ell_4m_mul_d ell_4m_mul_d.restype = None ell_4m_mul_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_4m_pre_mul_f = libteem.ell_4m_pre_mul_f ell_4m_pre_mul_f.restype = None ell_4m_pre_mul_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_4m_post_mul_f = libteem.ell_4m_post_mul_f ell_4m_post_mul_f.restype = None ell_4m_post_mul_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_4m_post_mul_d = libteem.ell_4m_post_mul_d ell_4m_post_mul_d.restype = None ell_4m_post_mul_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_4m_det_f = libteem.ell_4m_det_f ell_4m_det_f.restype = c_float ell_4m_det_f.argtypes = [POINTER(c_float)] ell_4m_det_d = libteem.ell_4m_det_d ell_4m_det_d.restype = c_double ell_4m_det_d.argtypes = [POINTER(c_double)] ell_4m_inv_f = libteem.ell_4m_inv_f ell_4m_inv_f.restype = None ell_4m_inv_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_4m_inv_d = libteem.ell_4m_inv_d ell_4m_inv_d.restype = None ell_4m_inv_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_6m_mul_d = libteem.ell_6m_mul_d ell_6m_mul_d.restype = None ell_6m_mul_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_3m_rotate_between_d = libteem.ell_3m_rotate_between_d ell_3m_rotate_between_d.restype = None ell_3m_rotate_between_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_3m_to_q_f = libteem.ell_3m_to_q_f ell_3m_to_q_f.restype = None ell_3m_to_q_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_3m_to_q_d = libteem.ell_3m_to_q_d ell_3m_to_q_d.restype = None ell_3m_to_q_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_4m_to_q_f = libteem.ell_4m_to_q_f ell_4m_to_q_f.restype = None ell_4m_to_q_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_4m_to_q_d = libteem.ell_4m_to_q_d ell_4m_to_q_d.restype = None ell_4m_to_q_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_q_to_3m_f = libteem.ell_q_to_3m_f ell_q_to_3m_f.restype = None ell_q_to_3m_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_q_to_3m_d = libteem.ell_q_to_3m_d ell_q_to_3m_d.restype = None ell_q_to_3m_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_q_to_4m_f = libteem.ell_q_to_4m_f ell_q_to_4m_f.restype = None ell_q_to_4m_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_q_to_4m_d = libteem.ell_q_to_4m_d ell_q_to_4m_d.restype = None ell_q_to_4m_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_q_to_aa_f = libteem.ell_q_to_aa_f ell_q_to_aa_f.restype = c_float ell_q_to_aa_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_q_to_aa_d = libteem.ell_q_to_aa_d ell_q_to_aa_d.restype = c_double ell_q_to_aa_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_aa_to_q_f = libteem.ell_aa_to_q_f ell_aa_to_q_f.restype = None ell_aa_to_q_f.argtypes = [POINTER(c_float), c_float, POINTER(c_float)] ell_aa_to_q_d = libteem.ell_aa_to_q_d ell_aa_to_q_d.restype = None ell_aa_to_q_d.argtypes = [POINTER(c_double), c_double, POINTER(c_double)] ell_aa_to_3m_f = libteem.ell_aa_to_3m_f ell_aa_to_3m_f.restype = None ell_aa_to_3m_f.argtypes = [POINTER(c_float), c_float, POINTER(c_float)] ell_aa_to_3m_d = libteem.ell_aa_to_3m_d ell_aa_to_3m_d.restype = None ell_aa_to_3m_d.argtypes = [POINTER(c_double), c_double, POINTER(c_double)] ell_aa_to_4m_f = libteem.ell_aa_to_4m_f ell_aa_to_4m_f.restype = None ell_aa_to_4m_f.argtypes = [POINTER(c_float), c_float, POINTER(c_float)] ell_aa_to_4m_d = libteem.ell_aa_to_4m_d ell_aa_to_4m_d.restype = None ell_aa_to_4m_d.argtypes = [POINTER(c_double), c_double, POINTER(c_double)] ell_3m_to_aa_f = libteem.ell_3m_to_aa_f ell_3m_to_aa_f.restype = c_float ell_3m_to_aa_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_3m_to_aa_d = libteem.ell_3m_to_aa_d ell_3m_to_aa_d.restype = c_double ell_3m_to_aa_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_4m_to_aa_f = libteem.ell_4m_to_aa_f ell_4m_to_aa_f.restype = c_float ell_4m_to_aa_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_4m_to_aa_d = libteem.ell_4m_to_aa_d ell_4m_to_aa_d.restype = c_double ell_4m_to_aa_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_q_mul_f = libteem.ell_q_mul_f ell_q_mul_f.restype = None ell_q_mul_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float)] ell_q_mul_d = libteem.ell_q_mul_d ell_q_mul_d.restype = None ell_q_mul_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_q_inv_f = libteem.ell_q_inv_f ell_q_inv_f.restype = None ell_q_inv_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_q_inv_d = libteem.ell_q_inv_d ell_q_inv_d.restype = None ell_q_inv_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_q_pow_f = libteem.ell_q_pow_f ell_q_pow_f.restype = None ell_q_pow_f.argtypes = [POINTER(c_float), POINTER(c_float), c_float] ell_q_pow_d = libteem.ell_q_pow_d ell_q_pow_d.restype = None ell_q_pow_d.argtypes = [POINTER(c_double), POINTER(c_double), c_double] ell_q_div_f = libteem.ell_q_div_f ell_q_div_f.restype = None ell_q_div_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float)] ell_q_div_d = libteem.ell_q_div_d ell_q_div_d.restype = None ell_q_div_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_q_exp_f = libteem.ell_q_exp_f ell_q_exp_f.restype = None ell_q_exp_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_q_exp_d = libteem.ell_q_exp_d ell_q_exp_d.restype = None ell_q_exp_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_q_log_f = libteem.ell_q_log_f ell_q_log_f.restype = None ell_q_log_f.argtypes = [POINTER(c_float), POINTER(c_float)] ell_q_log_d = libteem.ell_q_log_d ell_q_log_d.restype = None ell_q_log_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_q_3v_rotate_f = libteem.ell_q_3v_rotate_f ell_q_3v_rotate_f.restype = None ell_q_3v_rotate_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float)] ell_q_3v_rotate_d = libteem.ell_q_3v_rotate_d ell_q_3v_rotate_d.restype = None ell_q_3v_rotate_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_q_4v_rotate_f = libteem.ell_q_4v_rotate_f ell_q_4v_rotate_f.restype = None ell_q_4v_rotate_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float)] ell_q_4v_rotate_d = libteem.ell_q_4v_rotate_d ell_q_4v_rotate_d.restype = None ell_q_4v_rotate_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_q_avg4_d = libteem.ell_q_avg4_d ell_q_avg4_d.restype = c_int ell_q_avg4_d.argtypes = [POINTER(c_double), POINTER(c_uint), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_double, c_uint] ell_q_avgN_d = libteem.ell_q_avgN_d ell_q_avgN_d.restype = c_int ell_q_avgN_d.argtypes = [POINTER(c_double), POINTER(c_uint), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_uint, c_double, c_uint] ell_Nm_check = libteem.ell_Nm_check ell_Nm_check.restype = c_int ell_Nm_check.argtypes = [POINTER(Nrrd), c_int] ell_Nm_tran = libteem.ell_Nm_tran ell_Nm_tran.restype = c_int ell_Nm_tran.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] ell_Nm_mul = libteem.ell_Nm_mul ell_Nm_mul.restype = c_int ell_Nm_mul.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd)] ell_Nm_inv = libteem.ell_Nm_inv ell_Nm_inv.restype = c_int ell_Nm_inv.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] ell_Nm_pseudo_inv = libteem.ell_Nm_pseudo_inv ell_Nm_pseudo_inv.restype = c_int ell_Nm_pseudo_inv.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] ell_Nm_wght_pseudo_inv = libteem.ell_Nm_wght_pseudo_inv ell_Nm_wght_pseudo_inv.restype = c_int ell_Nm_wght_pseudo_inv.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd)] ell_cubic = libteem.ell_cubic ell_cubic.restype = c_int ell_cubic.argtypes = [POINTER(c_double), c_double, c_double, c_double, c_int] ell_3m_1d_nullspace_d = libteem.ell_3m_1d_nullspace_d ell_3m_1d_nullspace_d.restype = None ell_3m_1d_nullspace_d.argtypes = [POINTER(c_double), POINTER(c_double)] ell_3m_2d_nullspace_d = libteem.ell_3m_2d_nullspace_d ell_3m_2d_nullspace_d.restype = None ell_3m_2d_nullspace_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] ell_3m_eigenvalues_d = libteem.ell_3m_eigenvalues_d ell_3m_eigenvalues_d.restype = c_int ell_3m_eigenvalues_d.argtypes = [POINTER(c_double), POINTER(c_double), c_int] ell_3m_eigensolve_d = libteem.ell_3m_eigensolve_d ell_3m_eigensolve_d.restype = c_int ell_3m_eigensolve_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), c_int] ell_3m_svd_d = libteem.ell_3m_svd_d ell_3m_svd_d.restype = c_int ell_3m_svd_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_int] ell_6ms_eigensolve_d = libteem.ell_6ms_eigensolve_d ell_6ms_eigensolve_d.restype = c_int ell_6ms_eigensolve_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), c_double] class gageItemEntry(Structure): pass gageItemEntry._fields_ = [ ('enumVal', c_int), ('answerLength', c_uint), ('needDeriv', c_int), ('prereq', c_int * 8), ('parentItem', c_int), ('parentIndex', c_int), ('needData', c_int), ] class gageShape_t(Structure): pass gageShape_t._pack_ = 4 gageShape_t._fields_ = [ ('defaultCenter', c_int), ('orientationFromSpacing', c_int), ('center', c_int), ('fromOrientation', c_int), ('size', c_uint * 3), ('spacing', c_double * 3), ('ItoW', c_double * 16), ('WtoI', c_double * 16), ('ItoWSubInvTransp', c_double * 9), ('ItoWSubInv', c_double * 9), ] gageShape = gageShape_t class gageParm_t(Structure): pass gageParm_t._pack_ = 4 gageParm_t._fields_ = [ ('renormalize', c_int), ('checkIntegrals', c_int), ('k3pack', c_int), ('gradMagCurvMin', c_double), ('kernelIntegralNearZero', c_double), ('stackNormalizeDerivBias', c_double), ('curvNormalSide', c_int), ('defaultCenter', c_int), ('stackUse', c_int), ('stackNormalizeRecon', c_int), ('stackNormalizeDeriv', c_int), ('orientationFromSpacing', c_int), ('generateErrStr', c_int), ] gageParm = gageParm_t class gagePoint_t(Structure): pass gagePoint_t._pack_ = 4 gagePoint_t._fields_ = [ ('frac', c_double * 4), ('idx', c_uint * 4), ('stackFwNonZeroNum', c_uint), ] gagePoint = gagePoint_t class NrrdKernelSpec(Structure): pass class gagePerVolume_t(Structure): pass gageContext_t._pack_ = 4 gageContext_t._fields_ = [ ('verbose', c_int), ('parm', gageParm), ('ksp', POINTER(NrrdKernelSpec) * 8), ('pvl', POINTER(POINTER(gagePerVolume_t))), ('pvlNum', c_uint), ('pvlArr', POINTER(airArray)), ('shape', POINTER(gageShape)), ('stackPos', POINTER(c_double)), ('stackFsl', POINTER(c_double)), ('stackFw', POINTER(c_double)), ('flag', c_int * 7), ('needD', c_int * 3), ('needK', c_int * 8), ('radius', c_uint), ('fsl', POINTER(c_double)), ('fw', POINTER(c_double)), ('off', POINTER(c_uint)), ('point', gagePoint), ('errStr', c_char * 513), ('errNum', c_int), ('edgeFrac', c_double), ] class gageKind_t(Structure): pass gagePerVolume = gagePerVolume_t gageKind_t._fields_ = [ ('dynamicAlloc', c_int), ('name', c_char * 129), ('enm', POINTER(airEnum)), ('baseDim', c_uint), ('valLen', c_uint), ('itemMax', c_int), ('table', POINTER(gageItemEntry)), ('iv3Print', CFUNCTYPE(None, POINTER(FILE), POINTER(gageContext), POINTER(gagePerVolume))), ('filter', CFUNCTYPE(None, POINTER(gageContext), POINTER(gagePerVolume))), ('answer', CFUNCTYPE(None, POINTER(gageContext), POINTER(gagePerVolume))), ('pvlDataNew', CFUNCTYPE(c_void_p, POINTER(gageKind_t))), ('pvlDataCopy', CFUNCTYPE(c_void_p, POINTER(gageKind_t), c_void_p)), ('pvlDataNix', CFUNCTYPE(c_void_p, POINTER(gageKind_t), c_void_p)), ('pvlDataUpdate', CFUNCTYPE(c_int, POINTER(gageKind_t), POINTER(gageContext), POINTER(gagePerVolume), c_void_p)), ('data', c_void_p), ] gagePerVolume_t._fields_ = [ ('verbose', c_int), ('kind', POINTER(gageKind_t)), ('query', gageQuery), ('needD', c_int * 3), ('nin', POINTER(Nrrd)), ('flag', c_int * 4), ('iv3', POINTER(c_double)), ('iv2', POINTER(c_double)), ('iv1', POINTER(c_double)), ('lup', CFUNCTYPE(c_double, c_void_p, c_size_t)), ('answer', POINTER(c_double)), ('directAnswer', POINTER(POINTER(c_double))), ('data', c_void_p), ] gageKind = gageKind_t class gageItemSpec(Structure): pass gageItemSpec._fields_ = [ ('kind', POINTER(gageKind)), ('item', c_int), ] class gageItemPack(Structure): pass gageItemPack._fields_ = [ ('kind', POINTER(gageKind)), ('item', c_int * 12), ] class gageStackBlurParm(Structure): pass gageStackBlurParm._pack_ = 4 gageStackBlurParm._fields_ = [ ('num', c_uint), ('scale', POINTER(c_double)), ('sigmaMax', c_double), ('padValue', c_double), ('kspec', POINTER(NrrdKernelSpec)), ('dataCheck', c_int), ('boundary', c_int), ('renormalize', c_int), ('verbose', c_int), ] class gageOptimSigParm(Structure): pass gageOptimSigParm._pack_ = 4 gageOptimSigParm._fields_ = [ ('dim', c_uint), ('sigmaMax', c_double), ('cutoff', c_double), ('measrSampleNum', c_uint), ('sampleNum', c_uint), ('volMeasr', c_int), ('lineMeasr', c_int), ('plotting', c_int), ('tentRecon', c_int), ('maxIter', c_uint), ('convEps', c_double), ('sx', c_uint), ('sy', c_uint), ('sz', c_uint), ('sigmatru', POINTER(c_double)), ('truth', POINTER(c_double)), ('ntruth', POINTER(Nrrd)), ('nerr', POINTER(Nrrd)), ('ntruline', POINTER(Nrrd)), ('ninterp', POINTER(Nrrd)), ('ndiff', POINTER(Nrrd)), ('sampleNumMax', c_uint), ('scalePos', POINTER(c_double)), ('step', POINTER(c_double)), ('nsampvol', POINTER(POINTER(Nrrd))), ('pvl', POINTER(gagePerVolume)), ('pvlSS', POINTER(POINTER(gagePerVolume))), ('gctx', POINTER(gageContext)), ('finalErr', c_double), ] gageBiffKey = (STRING).in_dll(libteem, 'gageBiffKey') gageDefVerbose = (c_int).in_dll(libteem, 'gageDefVerbose') gageDefGradMagCurvMin = (c_double).in_dll(libteem, 'gageDefGradMagCurvMin') gageDefRenormalize = (c_int).in_dll(libteem, 'gageDefRenormalize') gageDefCheckIntegrals = (c_int).in_dll(libteem, 'gageDefCheckIntegrals') gageDefK3Pack = (c_int).in_dll(libteem, 'gageDefK3Pack') gageDefCurvNormalSide = (c_int).in_dll(libteem, 'gageDefCurvNormalSide') gageDefKernelIntegralNearZero = (c_double).in_dll(libteem, 'gageDefKernelIntegralNearZero') gageDefDefaultCenter = (c_int).in_dll(libteem, 'gageDefDefaultCenter') gageDefStackUse = (c_int).in_dll(libteem, 'gageDefStackUse') gageDefStackNormalizeRecon = (c_int).in_dll(libteem, 'gageDefStackNormalizeRecon') gageDefStackNormalizeDeriv = (c_int).in_dll(libteem, 'gageDefStackNormalizeDeriv') gageDefStackNormalizeDerivBias = (c_double).in_dll(libteem, 'gageDefStackNormalizeDerivBias') gageDefStackBlurSigmaMax = (c_double).in_dll(libteem, 'gageDefStackBlurSigmaMax') gageDefOrientationFromSpacing = (c_int).in_dll(libteem, 'gageDefOrientationFromSpacing') gageDefGenerateErrStr = (c_int).in_dll(libteem, 'gageDefGenerateErrStr') gagePresent = (c_int).in_dll(libteem, 'gagePresent') gageZeroNormal = (c_double * 3).in_dll(libteem, 'gageZeroNormal') gageErr = (POINTER(airEnum)).in_dll(libteem, 'gageErr') gageKernel = (POINTER(airEnum)).in_dll(libteem, 'gageKernel') gageItemPackPart = (POINTER(airEnum)).in_dll(libteem, 'gageItemPackPart') gageParmReset = libteem.gageParmReset gageParmReset.restype = None gageParmReset.argtypes = [POINTER(gageParm)] gagePointReset = libteem.gagePointReset gagePointReset.restype = None gagePointReset.argtypes = [POINTER(gagePoint)] gageItemSpecNew = libteem.gageItemSpecNew gageItemSpecNew.restype = POINTER(gageItemSpec) gageItemSpecNew.argtypes = [] gageItemSpecInit = libteem.gageItemSpecInit gageItemSpecInit.restype = None gageItemSpecInit.argtypes = [POINTER(gageItemSpec)] gageItemSpecNix = libteem.gageItemSpecNix gageItemSpecNix.restype = POINTER(gageItemSpec) gageItemSpecNix.argtypes = [POINTER(gageItemSpec)] gageKindCheck = libteem.gageKindCheck gageKindCheck.restype = c_int gageKindCheck.argtypes = [POINTER(gageKind)] gageKindTotalAnswerLength = libteem.gageKindTotalAnswerLength gageKindTotalAnswerLength.restype = c_uint gageKindTotalAnswerLength.argtypes = [POINTER(gageKind)] gageKindAnswerLength = libteem.gageKindAnswerLength gageKindAnswerLength.restype = c_uint gageKindAnswerLength.argtypes = [POINTER(gageKind), c_int] gageKindAnswerOffset = libteem.gageKindAnswerOffset gageKindAnswerOffset.restype = c_int gageKindAnswerOffset.argtypes = [POINTER(gageKind), c_int] gageKindVolumeCheck = libteem.gageKindVolumeCheck gageKindVolumeCheck.restype = c_int gageKindVolumeCheck.argtypes = [POINTER(gageKind), POINTER(Nrrd)] gageQueryPrint = libteem.gageQueryPrint gageQueryPrint.restype = None gageQueryPrint.argtypes = [POINTER(FILE), POINTER(gageKind), POINTER(c_ubyte)] gageScl3PFilter_t = CFUNCTYPE(None, POINTER(gageShape), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_int)) gageScl3PFilter2 = libteem.gageScl3PFilter2 gageScl3PFilter2.restype = None gageScl3PFilter2.argtypes = [POINTER(gageShape), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_int)] gageScl3PFilter4 = libteem.gageScl3PFilter4 gageScl3PFilter4.restype = None gageScl3PFilter4.argtypes = [POINTER(gageShape), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_int)] gageScl3PFilter6 = libteem.gageScl3PFilter6 gageScl3PFilter6.restype = None gageScl3PFilter6.argtypes = [POINTER(gageShape), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_int)] gageScl3PFilter8 = libteem.gageScl3PFilter8 gageScl3PFilter8.restype = None gageScl3PFilter8.argtypes = [POINTER(gageShape), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_int)] gageScl3PFilterN = libteem.gageScl3PFilterN gageScl3PFilterN.restype = None gageScl3PFilterN.argtypes = [POINTER(gageShape), c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_int)] gageScl = (POINTER(airEnum)).in_dll(libteem, 'gageScl') gageKindScl = (POINTER(gageKind)).in_dll(libteem, 'gageKindScl') gageItemPackSclValue = (POINTER(gageItemPack)).in_dll(libteem, 'gageItemPackSclValue') gageVec = (POINTER(airEnum)).in_dll(libteem, 'gageVec') gageKindVec = (POINTER(gageKind)).in_dll(libteem, 'gageKindVec') gageShapeReset = libteem.gageShapeReset gageShapeReset.restype = None gageShapeReset.argtypes = [POINTER(gageShape)] gageShapeNew = libteem.gageShapeNew gageShapeNew.restype = POINTER(gageShape) gageShapeNew.argtypes = [] gageShapeCopy = libteem.gageShapeCopy gageShapeCopy.restype = POINTER(gageShape) gageShapeCopy.argtypes = [POINTER(gageShape)] gageShapeNix = libteem.gageShapeNix gageShapeNix.restype = POINTER(gageShape) gageShapeNix.argtypes = [POINTER(gageShape)] gageShapeSet = libteem.gageShapeSet gageShapeSet.restype = c_int gageShapeSet.argtypes = [POINTER(gageShape), POINTER(Nrrd), c_int] gageShapeWtoI = libteem.gageShapeWtoI gageShapeWtoI.restype = None gageShapeWtoI.argtypes = [POINTER(gageShape), POINTER(c_double), POINTER(c_double)] gageShapeItoW = libteem.gageShapeItoW gageShapeItoW.restype = None gageShapeItoW.argtypes = [POINTER(gageShape), POINTER(c_double), POINTER(c_double)] gageShapeEqual = libteem.gageShapeEqual gageShapeEqual.restype = c_int gageShapeEqual.argtypes = [POINTER(gageShape), STRING, POINTER(gageShape), STRING] gageShapeBoundingBox = libteem.gageShapeBoundingBox gageShapeBoundingBox.restype = None gageShapeBoundingBox.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(gageShape)] gageVolumeCheck = libteem.gageVolumeCheck gageVolumeCheck.restype = c_int gageVolumeCheck.argtypes = [POINTER(gageContext), POINTER(Nrrd), POINTER(gageKind)] gagePerVolumeNew = libteem.gagePerVolumeNew gagePerVolumeNew.restype = POINTER(gagePerVolume) gagePerVolumeNew.argtypes = [POINTER(gageContext), POINTER(Nrrd), POINTER(gageKind)] gagePerVolumeNix = libteem.gagePerVolumeNix gagePerVolumeNix.restype = POINTER(gagePerVolume) gagePerVolumeNix.argtypes = [POINTER(gagePerVolume)] gageAnswerPointer = libteem.gageAnswerPointer gageAnswerPointer.restype = POINTER(c_double) gageAnswerPointer.argtypes = [POINTER(gageContext), POINTER(gagePerVolume), c_int] gageAnswerLength = libteem.gageAnswerLength gageAnswerLength.restype = c_uint gageAnswerLength.argtypes = [POINTER(gageContext), POINTER(gagePerVolume), c_int] gageQueryReset = libteem.gageQueryReset gageQueryReset.restype = c_int gageQueryReset.argtypes = [POINTER(gageContext), POINTER(gagePerVolume)] gageQuerySet = libteem.gageQuerySet gageQuerySet.restype = c_int gageQuerySet.argtypes = [POINTER(gageContext), POINTER(gagePerVolume), POINTER(c_ubyte)] gageQueryAdd = libteem.gageQueryAdd gageQueryAdd.restype = c_int gageQueryAdd.argtypes = [POINTER(gageContext), POINTER(gagePerVolume), POINTER(c_ubyte)] gageQueryItemOn = libteem.gageQueryItemOn gageQueryItemOn.restype = c_int gageQueryItemOn.argtypes = [POINTER(gageContext), POINTER(gagePerVolume), c_int] gageOptimSigSet = libteem.gageOptimSigSet gageOptimSigSet.restype = c_int gageOptimSigSet.argtypes = [POINTER(c_double), c_uint, c_uint] gageOptimSigParmNew = libteem.gageOptimSigParmNew gageOptimSigParmNew.restype = POINTER(gageOptimSigParm) gageOptimSigParmNew.argtypes = [c_uint] gageOptimSigParmNix = libteem.gageOptimSigParmNix gageOptimSigParmNix.restype = POINTER(gageOptimSigParm) gageOptimSigParmNix.argtypes = [POINTER(gageOptimSigParm)] gageOptimSigTruthSet = libteem.gageOptimSigTruthSet gageOptimSigTruthSet.restype = c_int gageOptimSigTruthSet.argtypes = [POINTER(gageOptimSigParm), c_uint, c_double, c_double, c_uint] gageOptimSigCalculate = libteem.gageOptimSigCalculate gageOptimSigCalculate.restype = c_int gageOptimSigCalculate.argtypes = [POINTER(gageOptimSigParm), POINTER(c_double), c_uint, c_int, c_int, c_double, c_uint] gageOptimSigPlot = libteem.gageOptimSigPlot gageOptimSigPlot.restype = c_int gageOptimSigPlot.argtypes = [POINTER(gageOptimSigParm), POINTER(Nrrd), POINTER(c_double), c_uint, c_int, c_int] gageTauOfTee = libteem.gageTauOfTee gageTauOfTee.restype = c_double gageTauOfTee.argtypes = [c_double] gageTeeOfTau = libteem.gageTeeOfTau gageTeeOfTau.restype = c_double gageTeeOfTau.argtypes = [c_double] gageSigOfTau = libteem.gageSigOfTau gageSigOfTau.restype = c_double gageSigOfTau.argtypes = [c_double] gageTauOfSig = libteem.gageTauOfSig gageTauOfSig.restype = c_double gageTauOfSig.argtypes = [c_double] gageStackWtoI = libteem.gageStackWtoI gageStackWtoI.restype = c_double gageStackWtoI.argtypes = [POINTER(gageContext), c_double, POINTER(c_int)] gageStackItoW = libteem.gageStackItoW gageStackItoW.restype = c_double gageStackItoW.argtypes = [POINTER(gageContext), c_double, POINTER(c_int)] gageStackPerVolumeNew = libteem.gageStackPerVolumeNew gageStackPerVolumeNew.restype = c_int gageStackPerVolumeNew.argtypes = [POINTER(gageContext), POINTER(POINTER(gagePerVolume)), POINTER(POINTER(Nrrd)), c_uint, POINTER(gageKind)] gageStackPerVolumeAttach = libteem.gageStackPerVolumeAttach gageStackPerVolumeAttach.restype = c_int gageStackPerVolumeAttach.argtypes = [POINTER(gageContext), POINTER(gagePerVolume), POINTER(POINTER(gagePerVolume)), POINTER(c_double), c_uint] gageStackProbe = libteem.gageStackProbe gageStackProbe.restype = c_int gageStackProbe.argtypes = [POINTER(gageContext), c_double, c_double, c_double, c_double] gageStackProbeSpace = libteem.gageStackProbeSpace gageStackProbeSpace.restype = c_int gageStackProbeSpace.argtypes = [POINTER(gageContext), c_double, c_double, c_double, c_double, c_int, c_int] gageStackBlurParmNew = libteem.gageStackBlurParmNew gageStackBlurParmNew.restype = POINTER(gageStackBlurParm) gageStackBlurParmNew.argtypes = [] gageStackBlurParmNix = libteem.gageStackBlurParmNix gageStackBlurParmNix.restype = POINTER(gageStackBlurParm) gageStackBlurParmNix.argtypes = [POINTER(gageStackBlurParm)] gageStackBlurParmScaleSet = libteem.gageStackBlurParmScaleSet gageStackBlurParmScaleSet.restype = c_int gageStackBlurParmScaleSet.argtypes = [POINTER(gageStackBlurParm), c_uint, c_double, c_double, c_int, c_int] NrrdKernelSpec._pack_ = 4 NrrdKernelSpec._fields_ = [ ('kernel', POINTER(NrrdKernel)), ('parm', c_double * 8), ] gageStackBlurParmKernelSet = libteem.gageStackBlurParmKernelSet gageStackBlurParmKernelSet.restype = c_int gageStackBlurParmKernelSet.argtypes = [POINTER(gageStackBlurParm), POINTER(NrrdKernelSpec), c_int] gageStackBlurParmBoundarySet = libteem.gageStackBlurParmBoundarySet gageStackBlurParmBoundarySet.restype = c_int gageStackBlurParmBoundarySet.argtypes = [POINTER(gageStackBlurParm), c_int, c_double] gageStackBlurParmVerboseSet = libteem.gageStackBlurParmVerboseSet gageStackBlurParmVerboseSet.restype = c_int gageStackBlurParmVerboseSet.argtypes = [POINTER(gageStackBlurParm), c_int] gageStackBlurParmCheck = libteem.gageStackBlurParmCheck gageStackBlurParmCheck.restype = c_int gageStackBlurParmCheck.argtypes = [POINTER(gageStackBlurParm)] gageStackBlur = libteem.gageStackBlur gageStackBlur.restype = c_int gageStackBlur.argtypes = [POINTER(POINTER(Nrrd)), POINTER(gageStackBlurParm), POINTER(Nrrd), POINTER(gageKind)] gageStackBlurCheck = libteem.gageStackBlurCheck gageStackBlurCheck.restype = c_int gageStackBlurCheck.argtypes = [POINTER(POINTER(Nrrd)), POINTER(gageStackBlurParm), POINTER(Nrrd), POINTER(gageKind)] gageStackBlurGet = libteem.gageStackBlurGet gageStackBlurGet.restype = c_int gageStackBlurGet.argtypes = [POINTER(POINTER(Nrrd)), POINTER(c_int), POINTER(gageStackBlurParm), STRING, POINTER(Nrrd), POINTER(gageKind)] class NrrdEncoding_t(Structure): pass NrrdEncoding = NrrdEncoding_t gageStackBlurManage = libteem.gageStackBlurManage gageStackBlurManage.restype = c_int gageStackBlurManage.argtypes = [POINTER(POINTER(POINTER(Nrrd))), POINTER(c_int), POINTER(gageStackBlurParm), STRING, c_int, POINTER(NrrdEncoding), POINTER(Nrrd), POINTER(gageKind)] gageContextNew = libteem.gageContextNew gageContextNew.restype = POINTER(gageContext) gageContextNew.argtypes = [] gageContextCopy = libteem.gageContextCopy gageContextCopy.restype = POINTER(gageContext) gageContextCopy.argtypes = [POINTER(gageContext)] gageContextNix = libteem.gageContextNix gageContextNix.restype = POINTER(gageContext) gageContextNix.argtypes = [POINTER(gageContext)] gageParmSet = libteem.gageParmSet gageParmSet.restype = None gageParmSet.argtypes = [POINTER(gageContext), c_int, c_double] gagePerVolumeIsAttached = libteem.gagePerVolumeIsAttached gagePerVolumeIsAttached.restype = c_int gagePerVolumeIsAttached.argtypes = [POINTER(gageContext), POINTER(gagePerVolume)] gagePerVolumeAttach = libteem.gagePerVolumeAttach gagePerVolumeAttach.restype = c_int gagePerVolumeAttach.argtypes = [POINTER(gageContext), POINTER(gagePerVolume)] gagePerVolumeDetach = libteem.gagePerVolumeDetach gagePerVolumeDetach.restype = c_int gagePerVolumeDetach.argtypes = [POINTER(gageContext), POINTER(gagePerVolume)] gageKernelSet = libteem.gageKernelSet gageKernelSet.restype = c_int gageKernelSet.argtypes = [POINTER(gageContext), c_int, POINTER(NrrdKernel), POINTER(c_double)] gageKernelReset = libteem.gageKernelReset gageKernelReset.restype = None gageKernelReset.argtypes = [POINTER(gageContext)] gageProbe = libteem.gageProbe gageProbe.restype = c_int gageProbe.argtypes = [POINTER(gageContext), c_double, c_double, c_double] gageProbeSpace = libteem.gageProbeSpace gageProbeSpace.restype = c_int gageProbeSpace.argtypes = [POINTER(gageContext), c_double, c_double, c_double, c_int, c_int] gageUpdate = libteem.gageUpdate gageUpdate.restype = c_int gageUpdate.argtypes = [POINTER(gageContext)] gageStructureTensor = libteem.gageStructureTensor gageStructureTensor.restype = c_int gageStructureTensor.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int, c_int, c_int] gageDeconvolve = libteem.gageDeconvolve gageDeconvolve.restype = c_int gageDeconvolve.argtypes = [POINTER(Nrrd), POINTER(c_double), POINTER(Nrrd), POINTER(gageKind), POINTER(NrrdKernelSpec), c_int, c_uint, c_int, c_double, c_double, c_int] gageDeconvolveSeparableKnown = libteem.gageDeconvolveSeparableKnown gageDeconvolveSeparableKnown.restype = c_int gageDeconvolveSeparableKnown.argtypes = [POINTER(NrrdKernelSpec)] gageDeconvolveSeparable = libteem.gageDeconvolveSeparable gageDeconvolveSeparable.restype = c_int gageDeconvolveSeparable.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(gageKind), POINTER(NrrdKernelSpec), c_int] hestCB._fields_ = [ ('size', c_size_t), ('type', STRING), ('parse', CFUNCTYPE(c_int, c_void_p, STRING, STRING)), ('destroy', CFUNCTYPE(c_void_p, c_void_p)), ] class hestOpt(Structure): pass hestOpt._fields_ = [ ('flag', STRING), ('name', STRING), ('type', c_int), ('min', c_uint), ('max', c_int), ('valueP', c_void_p), ('dflt', STRING), ('info', STRING), ('sawP', POINTER(c_uint)), ('enm', POINTER(airEnum)), ('CB', POINTER(hestCB)), ('kind', c_int), ('alloc', c_int), ] hestParm._fields_ = [ ('verbosity', c_int), ('respFileEnable', c_int), ('elideSingleEnumType', c_int), ('elideSingleOtherType', c_int), ('elideSingleOtherDefault', c_int), ('elideSingleNonExistFloatDefault', c_int), ('elideMultipleNonExistFloatDefault', c_int), ('elideSingleEmptyStringDefault', c_int), ('elideMultipleEmptyStringDefault', c_int), ('noArgsIsNoProblem', c_int), ('greedySingleString', c_int), ('cleverPluralizeOtherY', c_int), ('columns', c_uint), ('respFileFlag', c_char), ('respFileComment', c_char), ('varParamStopFlag', c_char), ('multiFlagSep', c_char), ] hestVerbosity = (c_int).in_dll(libteem, 'hestVerbosity') hestRespFileEnable = (c_int).in_dll(libteem, 'hestRespFileEnable') hestElideSingleEnumType = (c_int).in_dll(libteem, 'hestElideSingleEnumType') hestElideSingleOtherType = (c_int).in_dll(libteem, 'hestElideSingleOtherType') hestElideSingleOtherDefault = (c_int).in_dll(libteem, 'hestElideSingleOtherDefault') hestElideSingleNonExistFloatDefault = (c_int).in_dll(libteem, 'hestElideSingleNonExistFloatDefault') hestElideMultipleNonExistFloatDefault = (c_int).in_dll(libteem, 'hestElideMultipleNonExistFloatDefault') hestElideSingleEmptyStringDefault = (c_int).in_dll(libteem, 'hestElideSingleEmptyStringDefault') hestElideMultipleEmptyStringDefault = (c_int).in_dll(libteem, 'hestElideMultipleEmptyStringDefault') hestNoArgsIsNoProblem = (c_int).in_dll(libteem, 'hestNoArgsIsNoProblem') hestGreedySingleString = (c_int).in_dll(libteem, 'hestGreedySingleString') hestCleverPluralizeOtherY = (c_int).in_dll(libteem, 'hestCleverPluralizeOtherY') hestColumns = (c_uint).in_dll(libteem, 'hestColumns') hestRespFileFlag = (c_char).in_dll(libteem, 'hestRespFileFlag') hestRespFileComment = (c_char).in_dll(libteem, 'hestRespFileComment') hestVarParamStopFlag = (c_char).in_dll(libteem, 'hestVarParamStopFlag') hestMultiFlagSep = (c_char).in_dll(libteem, 'hestMultiFlagSep') hestPresent = (c_int).in_dll(libteem, 'hestPresent') hestParmNew = libteem.hestParmNew hestParmNew.restype = POINTER(hestParm) hestParmNew.argtypes = [] hestParmFree = libteem.hestParmFree hestParmFree.restype = POINTER(hestParm) hestParmFree.argtypes = [POINTER(hestParm)] hestOptAdd = libteem.hestOptAdd hestOptAdd.restype = None hestOptAdd.argtypes = [POINTER(POINTER(hestOpt)), STRING, STRING, c_int, c_int, c_int, c_void_p, STRING, STRING] hestOptFree = libteem.hestOptFree hestOptFree.restype = POINTER(hestOpt) hestOptFree.argtypes = [POINTER(hestOpt)] hestOptCheck = libteem.hestOptCheck hestOptCheck.restype = c_int hestOptCheck.argtypes = [POINTER(hestOpt), POINTER(STRING)] hestParse = libteem.hestParse hestParse.restype = c_int hestParse.argtypes = [POINTER(hestOpt), c_int, POINTER(STRING), POINTER(STRING), POINTER(hestParm)] hestParseFree = libteem.hestParseFree hestParseFree.restype = c_void_p hestParseFree.argtypes = [POINTER(hestOpt)] hestParseOrDie = libteem.hestParseOrDie hestParseOrDie.restype = None hestParseOrDie.argtypes = [POINTER(hestOpt), c_int, POINTER(STRING), POINTER(hestParm), STRING, STRING, c_int, c_int, c_int] hestMinNumArgs = libteem.hestMinNumArgs hestMinNumArgs.restype = c_int hestMinNumArgs.argtypes = [POINTER(hestOpt)] hestUsage = libteem.hestUsage hestUsage.restype = None hestUsage.argtypes = [POINTER(FILE), POINTER(hestOpt), STRING, POINTER(hestParm)] hestGlossary = libteem.hestGlossary hestGlossary.restype = None hestGlossary.argtypes = [POINTER(FILE), POINTER(hestOpt), POINTER(hestParm)] hestInfo = libteem.hestInfo hestInfo.restype = None hestInfo.argtypes = [POINTER(FILE), STRING, STRING, POINTER(hestParm)] hooverRenderBegin_t = CFUNCTYPE(c_int, POINTER(c_void_p), c_void_p) hooverThreadBegin_t = CFUNCTYPE(c_int, POINTER(c_void_p), c_void_p, c_void_p, c_int) hooverRayBegin_t = CFUNCTYPE(c_int, c_void_p, c_void_p, c_void_p, c_int, c_int, c_double, POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double)) hooverSample_t = CFUNCTYPE(c_double, c_void_p, c_void_p, c_void_p, c_int, c_double, c_int, POINTER(c_double), POINTER(c_double)) hooverRayEnd_t = CFUNCTYPE(c_int, c_void_p, c_void_p, c_void_p) hooverThreadEnd_t = CFUNCTYPE(c_int, c_void_p, c_void_p, c_void_p) hooverRenderEnd_t = CFUNCTYPE(c_int, c_void_p, c_void_p) class hooverContext(Structure): pass hooverContext._pack_ = 4 hooverContext._fields_ = [ ('cam', POINTER(limnCamera)), ('volSize', c_int * 3), ('volSpacing', c_double * 3), ('volCentering', c_int), ('shape', POINTER(gageShape)), ('imgSize', c_int * 2), ('imgCentering', c_int), ('user', c_void_p), ('numThreads', c_uint), ('workIdx', c_int), ('workMutex', POINTER(airThreadMutex)), ('renderBegin', POINTER(hooverRenderBegin_t)), ('threadBegin', POINTER(hooverThreadBegin_t)), ('rayBegin', POINTER(hooverRayBegin_t)), ('sample', POINTER(hooverSample_t)), ('rayEnd', POINTER(hooverRayEnd_t)), ('threadEnd', POINTER(hooverThreadEnd_t)), ('renderEnd', POINTER(hooverRenderEnd_t)), ] hooverPresent = (c_int).in_dll(libteem, 'hooverPresent') hooverBiffKey = (STRING).in_dll(libteem, 'hooverBiffKey') hooverDefVolCentering = (c_int).in_dll(libteem, 'hooverDefVolCentering') hooverDefImgCentering = (c_int).in_dll(libteem, 'hooverDefImgCentering') hooverErr = (POINTER(airEnum)).in_dll(libteem, 'hooverErr') hooverContextNew = libteem.hooverContextNew hooverContextNew.restype = POINTER(hooverContext) hooverContextNew.argtypes = [] hooverContextCheck = libteem.hooverContextCheck hooverContextCheck.restype = c_int hooverContextCheck.argtypes = [POINTER(hooverContext)] hooverContextNix = libteem.hooverContextNix hooverContextNix.restype = None hooverContextNix.argtypes = [POINTER(hooverContext)] hooverRender = libteem.hooverRender hooverRender.restype = c_int hooverRender.argtypes = [POINTER(hooverContext), POINTER(c_int), POINTER(c_int)] hooverStubRenderBegin = libteem.hooverStubRenderBegin hooverStubRenderBegin.restype = c_int hooverStubRenderBegin.argtypes = [POINTER(c_void_p), c_void_p] hooverStubThreadBegin = libteem.hooverStubThreadBegin hooverStubThreadBegin.restype = c_int hooverStubThreadBegin.argtypes = [POINTER(c_void_p), c_void_p, c_void_p, c_int] hooverStubRayBegin = libteem.hooverStubRayBegin hooverStubRayBegin.restype = c_int hooverStubRayBegin.argtypes = [c_void_p, c_void_p, c_void_p, c_int, c_int, c_double, POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double)] hooverStubSample = libteem.hooverStubSample hooverStubSample.restype = c_double hooverStubSample.argtypes = [c_void_p, c_void_p, c_void_p, c_int, c_double, c_int, POINTER(c_double), POINTER(c_double)] hooverStubRayEnd = libteem.hooverStubRayEnd hooverStubRayEnd.restype = c_int hooverStubRayEnd.argtypes = [c_void_p, c_void_p, c_void_p] hooverStubThreadEnd = libteem.hooverStubThreadEnd hooverStubThreadEnd.restype = c_int hooverStubThreadEnd.argtypes = [c_void_p, c_void_p, c_void_p] hooverStubRenderEnd = libteem.hooverStubRenderEnd hooverStubRenderEnd.restype = c_int hooverStubRenderEnd.argtypes = [c_void_p, c_void_p] limnCamera_t._pack_ = 4 limnCamera_t._fields_ = [ ('from', c_double * 3), ('at', c_double * 3), ('up', c_double * 3), ('uRange', c_double * 2), ('vRange', c_double * 2), ('fov', c_double), ('aspect', c_double), ('neer', c_double), ('faar', c_double), ('dist', c_double), ('atRelative', c_int), ('orthographic', c_int), ('rightHanded', c_int), ('W2V', c_double * 16), ('V2W', c_double * 16), ('U', c_double * 4), ('V', c_double * 4), ('N', c_double * 4), ('vspNeer', c_double), ('vspFaar', c_double), ('vspDist', c_double), ] class limnLight(Structure): pass limnLight._fields_ = [ ('amb', c_float * 4), ('_dir', c_float * 4 * 8), ('dir', c_float * 4 * 8), ('col', c_float * 4 * 8), ('on', c_int * 8), ('vsp', c_int * 8), ] class limnOptsPS(Structure): pass limnOptsPS._fields_ = [ ('lineWidth', c_float * 8), ('creaseAngle', c_float), ('bg', c_float * 3), ('edgeColor', c_float * 3), ('showpage', c_int), ('wireFrame', c_int), ('noBackground', c_int), ] class limnWindow(Structure): pass limnWindow._fields_ = [ ('ps', limnOptsPS), ('device', c_int), ('scale', c_float), ('bbox', c_float * 4), ('yFlip', c_int), ('file', POINTER(FILE)), ] class limnLook(Structure): pass limnLook._fields_ = [ ('rgba', c_float * 4), ('kads', c_float * 3), ('spow', c_float), ] class limnVertex(Structure): pass limnVertex._fields_ = [ ('world', c_float * 4), ('rgba', c_float * 4), ('coord', c_float * 4), ('worldNormal', c_float * 3), ] class limnEdge_t(Structure): pass limnEdge_t._fields_ = [ ('vertIdx', c_uint * 2), ('lookIdx', c_uint), ('partIdx', c_uint), ('type', c_int), ('faceIdx', c_int * 2), ('once', c_int), ] limnEdge = limnEdge_t class limnFace_t(Structure): pass limnFace_t._fields_ = [ ('worldNormal', c_float * 3), ('screenNormal', c_float * 3), ('vertIdx', POINTER(c_uint)), ('edgeIdx', POINTER(c_uint)), ('sideNum', c_uint), ('lookIdx', c_uint), ('partIdx', c_uint), ('visible', c_int), ('depth', c_float), ] limnFace = limnFace_t class limnPart_t(Structure): pass limnPart_t._fields_ = [ ('vertIdx', POINTER(c_uint)), ('vertIdxNum', c_uint), ('vertIdxArr', POINTER(airArray)), ('edgeIdx', POINTER(c_uint)), ('edgeIdxNum', c_uint), ('edgeIdxArr', POINTER(airArray)), ('faceIdx', POINTER(c_uint)), ('faceIdxNum', c_uint), ('faceIdxArr', POINTER(airArray)), ('lookIdx', c_int), ('depth', c_float), ] limnPart = limnPart_t class limnObject(Structure): pass limnObject._fields_ = [ ('vert', POINTER(limnVertex)), ('vertNum', c_uint), ('vertArr', POINTER(airArray)), ('edge', POINTER(limnEdge)), ('edgeNum', c_uint), ('edgeArr', POINTER(airArray)), ('face', POINTER(limnFace)), ('faceNum', c_uint), ('faceArr', POINTER(airArray)), ('faceSort', POINTER(POINTER(limnFace))), ('part', POINTER(POINTER(limnPart))), ('partNum', c_uint), ('partArr', POINTER(airArray)), ('partPool', POINTER(POINTER(limnPart))), ('partPoolNum', c_uint), ('partPoolArr', POINTER(airArray)), ('look', POINTER(limnLook)), ('lookNum', c_uint), ('lookArr', POINTER(airArray)), ('vertSpace', c_int), ('setVertexRGBAFromLook', c_int), ('doEdges', c_int), ('incr', c_uint), ] limnPolyData._fields_ = [ ('xyzw', POINTER(c_float)), ('xyzwNum', c_uint), ('rgba', POINTER(c_ubyte)), ('rgbaNum', c_uint), ('norm', POINTER(c_float)), ('normNum', c_uint), ('tex2', POINTER(c_float)), ('tex2Num', c_uint), ('tang', POINTER(c_float)), ('tangNum', c_uint), ('indxNum', c_uint), ('indx', POINTER(c_uint)), ('primNum', c_uint), ('type', POINTER(c_ubyte)), ('icnt', POINTER(c_uint)), ] class limnSpline_t(Structure): pass limnSpline_t._pack_ = 4 limnSpline_t._fields_ = [ ('type', c_int), ('info', c_int), ('loop', c_int), ('B', c_double), ('C', c_double), ('ncpt', POINTER(Nrrd)), ('time', POINTER(c_double)), ] limnSpline = limnSpline_t class limnSplineTypeSpec_t(Structure): pass limnSplineTypeSpec_t._pack_ = 4 limnSplineTypeSpec_t._fields_ = [ ('type', c_int), ('B', c_double), ('C', c_double), ] limnSplineTypeSpec = limnSplineTypeSpec_t limnPresent = (c_int).in_dll(libteem, 'limnPresent') limnBiffKey = (STRING).in_dll(libteem, 'limnBiffKey') limnDefCameraAtRelative = (c_int).in_dll(libteem, 'limnDefCameraAtRelative') limnDefCameraOrthographic = (c_int).in_dll(libteem, 'limnDefCameraOrthographic') limnDefCameraRightHanded = (c_int).in_dll(libteem, 'limnDefCameraRightHanded') limnSpace = (POINTER(airEnum)).in_dll(libteem, 'limnSpace') limnPolyDataInfo = (POINTER(airEnum)).in_dll(libteem, 'limnPolyDataInfo') limnCameraPathTrack = (POINTER(airEnum)).in_dll(libteem, 'limnCameraPathTrack') limnPrimitive = (POINTER(airEnum)).in_dll(libteem, 'limnPrimitive') limnQNBins = (c_uint * 17).in_dll(libteem, 'limnQNBins') limnQNtoV_f = (CFUNCTYPE(None, POINTER(c_float), c_uint) * 17).in_dll(libteem, 'limnQNtoV_f') limnQNtoV_d = (CFUNCTYPE(None, POINTER(c_double), c_uint) * 17).in_dll(libteem, 'limnQNtoV_d') limnVtoQN_f = (CFUNCTYPE(c_uint, POINTER(c_float)) * 17).in_dll(libteem, 'limnVtoQN_f') limnVtoQN_d = (CFUNCTYPE(c_uint, POINTER(c_double)) * 17).in_dll(libteem, 'limnVtoQN_d') limnQNDemo = libteem.limnQNDemo limnQNDemo.restype = c_int limnQNDemo.argtypes = [POINTER(Nrrd), c_uint, c_int] limnLightSet = libteem.limnLightSet limnLightSet.restype = None limnLightSet.argtypes = [POINTER(limnLight), c_int, c_int, c_float, c_float, c_float, c_float, c_float, c_float] limnLightAmbientSet = libteem.limnLightAmbientSet limnLightAmbientSet.restype = None limnLightAmbientSet.argtypes = [POINTER(limnLight), c_float, c_float, c_float] limnLightSwitch = libteem.limnLightSwitch limnLightSwitch.restype = None limnLightSwitch.argtypes = [POINTER(limnLight), c_int, c_int] limnLightReset = libteem.limnLightReset limnLightReset.restype = None limnLightReset.argtypes = [POINTER(limnLight)] limnLightUpdate = libteem.limnLightUpdate limnLightUpdate.restype = c_int limnLightUpdate.argtypes = [POINTER(limnLight), POINTER(limnCamera)] limnEnvMapCB = CFUNCTYPE(None, POINTER(c_float), POINTER(c_float), c_void_p) limnEnvMapFill = libteem.limnEnvMapFill limnEnvMapFill.restype = c_int limnEnvMapFill.argtypes = [POINTER(Nrrd), limnEnvMapCB, c_int, c_void_p] limnLightDiffuseCB = libteem.limnLightDiffuseCB limnLightDiffuseCB.restype = None limnLightDiffuseCB.argtypes = [POINTER(c_float), POINTER(c_float), c_void_p] limnEnvMapCheck = libteem.limnEnvMapCheck limnEnvMapCheck.restype = c_int limnEnvMapCheck.argtypes = [POINTER(Nrrd)] limnLightNew = libteem.limnLightNew limnLightNew.restype = POINTER(limnLight) limnLightNew.argtypes = [] limnCameraInit = libteem.limnCameraInit limnCameraInit.restype = None limnCameraInit.argtypes = [POINTER(limnCamera)] limnLightNix = libteem.limnLightNix limnLightNix.restype = POINTER(limnLight) limnLightNix.argtypes = [POINTER(limnLight)] limnCameraNew = libteem.limnCameraNew limnCameraNew.restype = POINTER(limnCamera) limnCameraNew.argtypes = [] limnCameraNix = libteem.limnCameraNix limnCameraNix.restype = POINTER(limnCamera) limnCameraNix.argtypes = [POINTER(limnCamera)] limnWindowNew = libteem.limnWindowNew limnWindowNew.restype = POINTER(limnWindow) limnWindowNew.argtypes = [c_int] limnWindowNix = libteem.limnWindowNix limnWindowNix.restype = POINTER(limnWindow) limnWindowNix.argtypes = [POINTER(limnWindow)] limnHestCameraOptAdd = libteem.limnHestCameraOptAdd limnHestCameraOptAdd.restype = None limnHestCameraOptAdd.argtypes = [POINTER(POINTER(hestOpt)), POINTER(limnCamera), STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING] limnCameraAspectSet = libteem.limnCameraAspectSet limnCameraAspectSet.restype = c_int limnCameraAspectSet.argtypes = [POINTER(limnCamera), c_uint, c_uint, c_int] limnCameraUpdate = libteem.limnCameraUpdate limnCameraUpdate.restype = c_int limnCameraUpdate.argtypes = [POINTER(limnCamera)] limnCameraPathMake = libteem.limnCameraPathMake limnCameraPathMake.restype = c_int limnCameraPathMake.argtypes = [POINTER(limnCamera), c_int, POINTER(limnCamera), POINTER(c_double), c_int, c_int, POINTER(limnSplineTypeSpec), POINTER(limnSplineTypeSpec), POINTER(limnSplineTypeSpec), POINTER(limnSplineTypeSpec)] limnObjectLookAdd = libteem.limnObjectLookAdd limnObjectLookAdd.restype = c_int limnObjectLookAdd.argtypes = [POINTER(limnObject)] limnObjectNew = libteem.limnObjectNew limnObjectNew.restype = POINTER(limnObject) limnObjectNew.argtypes = [c_int, c_int] limnObjectNix = libteem.limnObjectNix limnObjectNix.restype = POINTER(limnObject) limnObjectNix.argtypes = [POINTER(limnObject)] limnObjectEmpty = libteem.limnObjectEmpty limnObjectEmpty.restype = None limnObjectEmpty.argtypes = [POINTER(limnObject)] limnObjectPreSet = libteem.limnObjectPreSet limnObjectPreSet.restype = c_int limnObjectPreSet.argtypes = [POINTER(limnObject), c_uint, c_uint, c_uint, c_uint, c_uint] limnObjectPartAdd = libteem.limnObjectPartAdd limnObjectPartAdd.restype = c_int limnObjectPartAdd.argtypes = [POINTER(limnObject)] limnObjectVertexNumPreSet = libteem.limnObjectVertexNumPreSet limnObjectVertexNumPreSet.restype = c_int limnObjectVertexNumPreSet.argtypes = [POINTER(limnObject), c_uint, c_uint] limnObjectVertexAdd = libteem.limnObjectVertexAdd limnObjectVertexAdd.restype = c_int limnObjectVertexAdd.argtypes = [POINTER(limnObject), c_uint, c_float, c_float, c_float] limnObjectEdgeAdd = libteem.limnObjectEdgeAdd limnObjectEdgeAdd.restype = c_int limnObjectEdgeAdd.argtypes = [POINTER(limnObject), c_uint, c_uint, c_uint, c_uint, c_uint] limnObjectFaceNumPreSet = libteem.limnObjectFaceNumPreSet limnObjectFaceNumPreSet.restype = c_int limnObjectFaceNumPreSet.argtypes = [POINTER(limnObject), c_uint, c_uint] limnObjectFaceAdd = libteem.limnObjectFaceAdd limnObjectFaceAdd.restype = c_int limnObjectFaceAdd.argtypes = [POINTER(limnObject), c_uint, c_uint, c_uint, POINTER(c_uint)] limnPolyDataNew = libteem.limnPolyDataNew limnPolyDataNew.restype = POINTER(limnPolyData) limnPolyDataNew.argtypes = [] limnPolyDataNix = libteem.limnPolyDataNix limnPolyDataNix.restype = POINTER(limnPolyData) limnPolyDataNix.argtypes = [POINTER(limnPolyData)] limnPolyDataInfoBitFlag = libteem.limnPolyDataInfoBitFlag limnPolyDataInfoBitFlag.restype = c_uint limnPolyDataInfoBitFlag.argtypes = [POINTER(limnPolyData)] limnPolyDataAlloc = libteem.limnPolyDataAlloc limnPolyDataAlloc.restype = c_int limnPolyDataAlloc.argtypes = [POINTER(limnPolyData), c_uint, c_uint, c_uint, c_uint] limnPolyDataSize = libteem.limnPolyDataSize limnPolyDataSize.restype = c_size_t limnPolyDataSize.argtypes = [POINTER(limnPolyData)] limnPolyDataCopy = libteem.limnPolyDataCopy limnPolyDataCopy.restype = c_int limnPolyDataCopy.argtypes = [POINTER(limnPolyData), POINTER(limnPolyData)] limnPolyDataCopyN = libteem.limnPolyDataCopyN limnPolyDataCopyN.restype = c_int limnPolyDataCopyN.argtypes = [POINTER(limnPolyData), POINTER(limnPolyData), c_uint] limnPolyDataTransform_f = libteem.limnPolyDataTransform_f limnPolyDataTransform_f.restype = None limnPolyDataTransform_f.argtypes = [POINTER(limnPolyData), POINTER(c_float)] limnPolyDataTransform_d = libteem.limnPolyDataTransform_d limnPolyDataTransform_d.restype = None limnPolyDataTransform_d.argtypes = [POINTER(limnPolyData), POINTER(c_double)] limnPolyDataPolygonNumber = libteem.limnPolyDataPolygonNumber limnPolyDataPolygonNumber.restype = c_uint limnPolyDataPolygonNumber.argtypes = [POINTER(limnPolyData)] limnPolyDataVertexNormals = libteem.limnPolyDataVertexNormals limnPolyDataVertexNormals.restype = c_int limnPolyDataVertexNormals.argtypes = [POINTER(limnPolyData)] limnPolyDataVertexNormalsNO = libteem.limnPolyDataVertexNormalsNO limnPolyDataVertexNormalsNO.restype = c_int limnPolyDataVertexNormalsNO.argtypes = [POINTER(limnPolyData)] limnPolyDataPrimitiveTypes = libteem.limnPolyDataPrimitiveTypes limnPolyDataPrimitiveTypes.restype = c_uint limnPolyDataPrimitiveTypes.argtypes = [POINTER(limnPolyData)] limnPolyDataPrimitiveVertexNumber = libteem.limnPolyDataPrimitiveVertexNumber limnPolyDataPrimitiveVertexNumber.restype = c_int limnPolyDataPrimitiveVertexNumber.argtypes = [POINTER(Nrrd), POINTER(limnPolyData)] limnPolyDataPrimitiveArea = libteem.limnPolyDataPrimitiveArea limnPolyDataPrimitiveArea.restype = c_int limnPolyDataPrimitiveArea.argtypes = [POINTER(Nrrd), POINTER(limnPolyData)] limnPolyDataRasterize = libteem.limnPolyDataRasterize limnPolyDataRasterize.restype = c_int limnPolyDataRasterize.argtypes = [POINTER(Nrrd), POINTER(limnPolyData), POINTER(c_double), POINTER(c_double), POINTER(c_size_t), c_int] limnPolyDataColorSet = libteem.limnPolyDataColorSet limnPolyDataColorSet.restype = None limnPolyDataColorSet.argtypes = [POINTER(limnPolyData), c_ubyte, c_ubyte, c_ubyte, c_ubyte] limnPolyDataCube = libteem.limnPolyDataCube limnPolyDataCube.restype = c_int limnPolyDataCube.argtypes = [POINTER(limnPolyData), c_uint, c_int] limnPolyDataCubeTriangles = libteem.limnPolyDataCubeTriangles limnPolyDataCubeTriangles.restype = c_int limnPolyDataCubeTriangles.argtypes = [POINTER(limnPolyData), c_uint, c_int] limnPolyDataOctahedron = libteem.limnPolyDataOctahedron limnPolyDataOctahedron.restype = c_int limnPolyDataOctahedron.argtypes = [POINTER(limnPolyData), c_uint, c_int] limnPolyDataCone = libteem.limnPolyDataCone limnPolyDataCone.restype = c_int limnPolyDataCone.argtypes = [POINTER(limnPolyData), c_uint, c_uint, c_int] limnPolyDataCylinder = libteem.limnPolyDataCylinder limnPolyDataCylinder.restype = c_int limnPolyDataCylinder.argtypes = [POINTER(limnPolyData), c_uint, c_uint, c_int] limnPolyDataSuperquadric = libteem.limnPolyDataSuperquadric limnPolyDataSuperquadric.restype = c_int limnPolyDataSuperquadric.argtypes = [POINTER(limnPolyData), c_uint, c_float, c_float, c_uint, c_uint] limnPolyDataSpiralBetterquadric = libteem.limnPolyDataSpiralBetterquadric limnPolyDataSpiralBetterquadric.restype = c_int limnPolyDataSpiralBetterquadric.argtypes = [POINTER(limnPolyData), c_uint, c_float, c_float, c_float, c_float, c_uint, c_uint] limnPolyDataSpiralSuperquadric = libteem.limnPolyDataSpiralSuperquadric limnPolyDataSpiralSuperquadric.restype = c_int limnPolyDataSpiralSuperquadric.argtypes = [POINTER(limnPolyData), c_uint, c_float, c_float, c_uint, c_uint] limnPolyDataPolarSphere = libteem.limnPolyDataPolarSphere limnPolyDataPolarSphere.restype = c_int limnPolyDataPolarSphere.argtypes = [POINTER(limnPolyData), c_uint, c_uint, c_uint] limnPolyDataSpiralSphere = libteem.limnPolyDataSpiralSphere limnPolyDataSpiralSphere.restype = c_int limnPolyDataSpiralSphere.argtypes = [POINTER(limnPolyData), c_uint, c_uint, c_uint] limnPolyDataIcoSphere = libteem.limnPolyDataIcoSphere limnPolyDataIcoSphere.restype = c_int limnPolyDataIcoSphere.argtypes = [POINTER(limnPolyData), c_uint, c_uint] limnPolyDataPlane = libteem.limnPolyDataPlane limnPolyDataPlane.restype = c_int limnPolyDataPlane.argtypes = [POINTER(limnPolyData), c_uint, c_uint, c_uint] limnPolyDataSquare = libteem.limnPolyDataSquare limnPolyDataSquare.restype = c_int limnPolyDataSquare.argtypes = [POINTER(limnPolyData), c_uint] limnPolyDataEdgeHalve = libteem.limnPolyDataEdgeHalve limnPolyDataEdgeHalve.restype = c_int limnPolyDataEdgeHalve.argtypes = [POINTER(limnPolyData), POINTER(limnPolyData)] limnPolyDataVertexWindingFix = libteem.limnPolyDataVertexWindingFix limnPolyDataVertexWindingFix.restype = c_int limnPolyDataVertexWindingFix.argtypes = [POINTER(limnPolyData), c_int] limnPolyDataClip = libteem.limnPolyDataClip limnPolyDataClip.restype = c_int limnPolyDataClip.argtypes = [POINTER(limnPolyData), POINTER(Nrrd), c_double] limnPolyDataClipMulti = libteem.limnPolyDataClipMulti limnPolyDataClipMulti.restype = c_int limnPolyDataClipMulti.argtypes = [POINTER(limnPolyData), POINTER(Nrrd), POINTER(c_double)] limnPolyDataCompress = libteem.limnPolyDataCompress limnPolyDataCompress.restype = POINTER(limnPolyData) limnPolyDataCompress.argtypes = [POINTER(limnPolyData)] limnPolyDataJoin = libteem.limnPolyDataJoin limnPolyDataJoin.restype = POINTER(limnPolyData) limnPolyDataJoin.argtypes = [POINTER(POINTER(limnPolyData)), c_uint] limnPolyDataVertexWindingFlip = libteem.limnPolyDataVertexWindingFlip limnPolyDataVertexWindingFlip.restype = c_int limnPolyDataVertexWindingFlip.argtypes = [POINTER(limnPolyData)] limnPolyDataCCFind = libteem.limnPolyDataCCFind limnPolyDataCCFind.restype = c_int limnPolyDataCCFind.argtypes = [POINTER(limnPolyData)] limnPolyDataPrimitiveSort = libteem.limnPolyDataPrimitiveSort limnPolyDataPrimitiveSort.restype = c_int limnPolyDataPrimitiveSort.argtypes = [POINTER(limnPolyData), POINTER(Nrrd)] limnPolyDataPrimitiveSelect = libteem.limnPolyDataPrimitiveSelect limnPolyDataPrimitiveSelect.restype = c_int limnPolyDataPrimitiveSelect.argtypes = [POINTER(limnPolyData), POINTER(limnPolyData), POINTER(Nrrd)] limnPolyDataNeighborList = libteem.limnPolyDataNeighborList limnPolyDataNeighborList.restype = c_int limnPolyDataNeighborList.argtypes = [POINTER(POINTER(c_uint)), POINTER(c_size_t), POINTER(c_uint), POINTER(limnPolyData)] limnPolyDataNeighborArray = libteem.limnPolyDataNeighborArray limnPolyDataNeighborArray.restype = c_int limnPolyDataNeighborArray.argtypes = [POINTER(POINTER(c_int)), POINTER(c_uint), POINTER(limnPolyData)] limnPolyDataNeighborArrayComp = libteem.limnPolyDataNeighborArrayComp limnPolyDataNeighborArrayComp.restype = c_int limnPolyDataNeighborArrayComp.argtypes = [POINTER(POINTER(c_int)), POINTER(POINTER(c_int)), POINTER(limnPolyData)] limnPolyDataSpiralTubeWrap = libteem.limnPolyDataSpiralTubeWrap limnPolyDataSpiralTubeWrap.restype = c_int limnPolyDataSpiralTubeWrap.argtypes = [POINTER(limnPolyData), POINTER(limnPolyData), c_uint, POINTER(Nrrd), c_uint, c_uint, c_double] limnPolyDataSmoothHC = libteem.limnPolyDataSmoothHC limnPolyDataSmoothHC.restype = c_int limnPolyDataSmoothHC.argtypes = [POINTER(limnPolyData), POINTER(c_int), POINTER(c_int), c_double, c_double, c_int] limnObjectDescribe = libteem.limnObjectDescribe limnObjectDescribe.restype = c_int limnObjectDescribe.argtypes = [POINTER(FILE), POINTER(limnObject)] limnObjectReadOFF = libteem.limnObjectReadOFF limnObjectReadOFF.restype = c_int limnObjectReadOFF.argtypes = [POINTER(limnObject), POINTER(FILE)] limnObjectWriteOFF = libteem.limnObjectWriteOFF limnObjectWriteOFF.restype = c_int limnObjectWriteOFF.argtypes = [POINTER(FILE), POINTER(limnObject)] limnPolyDataWriteIV = libteem.limnPolyDataWriteIV limnPolyDataWriteIV.restype = c_int limnPolyDataWriteIV.argtypes = [POINTER(FILE), POINTER(limnPolyData)] limnPolyDataWriteLMPD = libteem.limnPolyDataWriteLMPD limnPolyDataWriteLMPD.restype = c_int limnPolyDataWriteLMPD.argtypes = [POINTER(FILE), POINTER(limnPolyData)] limnPolyDataReadLMPD = libteem.limnPolyDataReadLMPD limnPolyDataReadLMPD.restype = c_int limnPolyDataReadLMPD.argtypes = [POINTER(limnPolyData), POINTER(FILE)] limnPolyDataWriteVTK = libteem.limnPolyDataWriteVTK limnPolyDataWriteVTK.restype = c_int limnPolyDataWriteVTK.argtypes = [POINTER(FILE), POINTER(limnPolyData)] limnPolyDataReadOFF = libteem.limnPolyDataReadOFF limnPolyDataReadOFF.restype = c_int limnPolyDataReadOFF.argtypes = [POINTER(limnPolyData), POINTER(FILE)] limnPolyDataSave = libteem.limnPolyDataSave limnPolyDataSave.restype = c_int limnPolyDataSave.argtypes = [STRING, POINTER(limnPolyData)] limnHestPolyDataLMPD = (POINTER(hestCB)).in_dll(libteem, 'limnHestPolyDataLMPD') limnHestPolyDataOFF = (POINTER(hestCB)).in_dll(libteem, 'limnHestPolyDataOFF') limnObjectCubeAdd = libteem.limnObjectCubeAdd limnObjectCubeAdd.restype = c_int limnObjectCubeAdd.argtypes = [POINTER(limnObject), c_uint] limnObjectSquareAdd = libteem.limnObjectSquareAdd limnObjectSquareAdd.restype = c_int limnObjectSquareAdd.argtypes = [POINTER(limnObject), c_uint] limnObjectCylinderAdd = libteem.limnObjectCylinderAdd limnObjectCylinderAdd.restype = c_int limnObjectCylinderAdd.argtypes = [POINTER(limnObject), c_uint, c_uint, c_uint] limnObjectPolarSphereAdd = libteem.limnObjectPolarSphereAdd limnObjectPolarSphereAdd.restype = c_int limnObjectPolarSphereAdd.argtypes = [POINTER(limnObject), c_uint, c_uint, c_uint, c_uint] limnObjectConeAdd = libteem.limnObjectConeAdd limnObjectConeAdd.restype = c_int limnObjectConeAdd.argtypes = [POINTER(limnObject), c_uint, c_uint, c_uint] limnObjectPolarSuperquadAdd = libteem.limnObjectPolarSuperquadAdd limnObjectPolarSuperquadAdd.restype = c_int limnObjectPolarSuperquadAdd.argtypes = [POINTER(limnObject), c_uint, c_uint, c_float, c_float, c_uint, c_uint] limnObjectPolarSuperquadFancyAdd = libteem.limnObjectPolarSuperquadFancyAdd limnObjectPolarSuperquadFancyAdd.restype = c_int limnObjectPolarSuperquadFancyAdd.argtypes = [POINTER(limnObject), c_uint, c_uint, c_float, c_float, c_float, c_float, c_uint, c_uint] limnObjectWorldHomog = libteem.limnObjectWorldHomog limnObjectWorldHomog.restype = c_int limnObjectWorldHomog.argtypes = [POINTER(limnObject)] limnObjectFaceNormals = libteem.limnObjectFaceNormals limnObjectFaceNormals.restype = c_int limnObjectFaceNormals.argtypes = [POINTER(limnObject), c_int] limnObjectVertexNormals = libteem.limnObjectVertexNormals limnObjectVertexNormals.restype = c_int limnObjectVertexNormals.argtypes = [POINTER(limnObject)] limnObjectSpaceTransform = libteem.limnObjectSpaceTransform limnObjectSpaceTransform.restype = c_int limnObjectSpaceTransform.argtypes = [POINTER(limnObject), POINTER(limnCamera), POINTER(limnWindow), c_int] limnObjectPartTransform = libteem.limnObjectPartTransform limnObjectPartTransform.restype = c_int limnObjectPartTransform.argtypes = [POINTER(limnObject), c_uint, POINTER(c_float)] limnObjectDepthSortParts = libteem.limnObjectDepthSortParts limnObjectDepthSortParts.restype = c_int limnObjectDepthSortParts.argtypes = [POINTER(limnObject)] limnObjectDepthSortFaces = libteem.limnObjectDepthSortFaces limnObjectDepthSortFaces.restype = c_int limnObjectDepthSortFaces.argtypes = [POINTER(limnObject)] limnObjectFaceReverse = libteem.limnObjectFaceReverse limnObjectFaceReverse.restype = c_int limnObjectFaceReverse.argtypes = [POINTER(limnObject)] limnObjectRender = libteem.limnObjectRender limnObjectRender.restype = c_int limnObjectRender.argtypes = [POINTER(limnObject), POINTER(limnCamera), POINTER(limnWindow)] limnObjectPSDraw = libteem.limnObjectPSDraw limnObjectPSDraw.restype = c_int limnObjectPSDraw.argtypes = [POINTER(limnObject), POINTER(limnCamera), POINTER(Nrrd), POINTER(limnWindow)] limnObjectPSDrawConcave = libteem.limnObjectPSDrawConcave limnObjectPSDrawConcave.restype = c_int limnObjectPSDrawConcave.argtypes = [POINTER(limnObject), POINTER(limnCamera), POINTER(Nrrd), POINTER(limnWindow)] limnSplineTypeSpecNew = libteem.limnSplineTypeSpecNew limnSplineTypeSpecNew.restype = POINTER(limnSplineTypeSpec) limnSplineTypeSpecNew.argtypes = [c_int] limnSplineTypeSpecNix = libteem.limnSplineTypeSpecNix limnSplineTypeSpecNix.restype = POINTER(limnSplineTypeSpec) limnSplineTypeSpecNix.argtypes = [POINTER(limnSplineTypeSpec)] limnSplineNew = libteem.limnSplineNew limnSplineNew.restype = POINTER(limnSpline) limnSplineNew.argtypes = [POINTER(Nrrd), c_int, POINTER(limnSplineTypeSpec)] limnSplineNix = libteem.limnSplineNix limnSplineNix.restype = POINTER(limnSpline) limnSplineNix.argtypes = [POINTER(limnSpline)] limnSplineNrrdCleverFix = libteem.limnSplineNrrdCleverFix limnSplineNrrdCleverFix.restype = c_int limnSplineNrrdCleverFix.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int, c_int] limnSplineCleverNew = libteem.limnSplineCleverNew limnSplineCleverNew.restype = POINTER(limnSpline) limnSplineCleverNew.argtypes = [POINTER(Nrrd), c_int, POINTER(limnSplineTypeSpec)] limnSplineUpdate = libteem.limnSplineUpdate limnSplineUpdate.restype = c_int limnSplineUpdate.argtypes = [POINTER(limnSpline), POINTER(Nrrd)] limnSplineType = (POINTER(airEnum)).in_dll(libteem, 'limnSplineType') limnSplineInfo = (POINTER(airEnum)).in_dll(libteem, 'limnSplineInfo') limnSplineParse = libteem.limnSplineParse limnSplineParse.restype = POINTER(limnSpline) limnSplineParse.argtypes = [STRING] limnSplineTypeSpecParse = libteem.limnSplineTypeSpecParse limnSplineTypeSpecParse.restype = POINTER(limnSplineTypeSpec) limnSplineTypeSpecParse.argtypes = [STRING] limnHestSpline = (POINTER(hestCB)).in_dll(libteem, 'limnHestSpline') limnHestSplineTypeSpec = (POINTER(hestCB)).in_dll(libteem, 'limnHestSplineTypeSpec') limnSplineInfoSize = (c_uint * 7).in_dll(libteem, 'limnSplineInfoSize') limnSplineTypeHasImplicitTangents = (c_int * 6).in_dll(libteem, 'limnSplineTypeHasImplicitTangents') limnSplineNumPoints = libteem.limnSplineNumPoints limnSplineNumPoints.restype = c_int limnSplineNumPoints.argtypes = [POINTER(limnSpline)] limnSplineMinT = libteem.limnSplineMinT limnSplineMinT.restype = c_double limnSplineMinT.argtypes = [POINTER(limnSpline)] limnSplineMaxT = libteem.limnSplineMaxT limnSplineMaxT.restype = c_double limnSplineMaxT.argtypes = [POINTER(limnSpline)] limnSplineBCSet = libteem.limnSplineBCSet limnSplineBCSet.restype = None limnSplineBCSet.argtypes = [POINTER(limnSpline), c_double, c_double] limnSplineEvaluate = libteem.limnSplineEvaluate limnSplineEvaluate.restype = None limnSplineEvaluate.argtypes = [POINTER(c_double), POINTER(limnSpline), c_double] limnSplineNrrdEvaluate = libteem.limnSplineNrrdEvaluate limnSplineNrrdEvaluate.restype = c_int limnSplineNrrdEvaluate.argtypes = [POINTER(Nrrd), POINTER(limnSpline), POINTER(Nrrd)] limnSplineSample = libteem.limnSplineSample limnSplineSample.restype = c_int limnSplineSample.argtypes = [POINTER(Nrrd), POINTER(limnSpline), c_double, c_size_t, c_double] meetPresent = (c_int).in_dll(libteem, 'meetPresent') meetBiffKey = (STRING).in_dll(libteem, 'meetBiffKey') meetAirEnumAll = libteem.meetAirEnumAll meetAirEnumAll.restype = POINTER(POINTER(airEnum)) meetAirEnumAll.argtypes = [] meetAirEnumAllPrint = libteem.meetAirEnumAllPrint meetAirEnumAllPrint.restype = None meetAirEnumAllPrint.argtypes = [POINTER(FILE)] meetAirEnumAllCheck = libteem.meetAirEnumAllCheck meetAirEnumAllCheck.restype = c_int meetAirEnumAllCheck.argtypes = [] meetTeemLibs = (STRING * 0).in_dll(libteem, 'meetTeemLibs') meetNrrdKernelAll = libteem.meetNrrdKernelAll meetNrrdKernelAll.restype = POINTER(POINTER(NrrdKernel)) meetNrrdKernelAll.argtypes = [] meetNrrdKernelAllCheck = libteem.meetNrrdKernelAllCheck meetNrrdKernelAllCheck.restype = c_int meetNrrdKernelAllCheck.argtypes = [] meetGageKindParse = libteem.meetGageKindParse meetGageKindParse.restype = POINTER(gageKind) meetGageKindParse.argtypes = [STRING] meetConstGageKindParse = libteem.meetConstGageKindParse meetConstGageKindParse.restype = POINTER(gageKind) meetConstGageKindParse.argtypes = [STRING] meetHestGageKind = (POINTER(hestCB)).in_dll(libteem, 'meetHestGageKind') meetHestConstGageKind = (POINTER(hestCB)).in_dll(libteem, 'meetHestConstGageKind') class meetPullVol(Structure): pass meetPullVol._pack_ = 4 meetPullVol._fields_ = [ ('kind', POINTER(gageKind)), ('fileName', STRING), ('volName', STRING), ('derivNormSS', c_int), ('uniformSS', c_int), ('optimSS', c_int), ('leeching', c_int), ('recomputedSS', c_int), ('numSS', c_uint), ('rangeSS', c_double * 2), ('derivNormBiasSS', c_double), ('posSS', POINTER(c_double)), ('nin', POINTER(Nrrd)), ('ninSS', POINTER(POINTER(Nrrd))), ] class meetPullInfo(Structure): pass meetPullInfo._pack_ = 4 meetPullInfo._fields_ = [ ('info', c_int), ('source', c_int), ('prop', c_int), ('constraint', c_int), ('volName', STRING), ('itemStr', STRING), ('zero', c_double), ('scale', c_double), ] meetPullVolNew = libteem.meetPullVolNew meetPullVolNew.restype = POINTER(meetPullVol) meetPullVolNew.argtypes = [] meetPullVolParse = libteem.meetPullVolParse meetPullVolParse.restype = c_int meetPullVolParse.argtypes = [POINTER(meetPullVol), STRING] meetPullVolLeechable = libteem.meetPullVolLeechable meetPullVolLeechable.restype = c_int meetPullVolLeechable.argtypes = [POINTER(meetPullVol), POINTER(meetPullVol)] meetPullVolNix = libteem.meetPullVolNix meetPullVolNix.restype = POINTER(meetPullVol) meetPullVolNix.argtypes = [POINTER(meetPullVol)] meetHestPullVol = (POINTER(hestCB)).in_dll(libteem, 'meetHestPullVol') meetPullVolLoadMulti = libteem.meetPullVolLoadMulti meetPullVolLoadMulti.restype = c_int meetPullVolLoadMulti.argtypes = [POINTER(POINTER(meetPullVol)), c_uint, STRING, POINTER(NrrdKernelSpec), c_int, c_double, c_int] class pullContext_t(Structure): pass pullContext = pullContext_t meetPullVolAddMulti = libteem.meetPullVolAddMulti meetPullVolAddMulti.restype = c_int meetPullVolAddMulti.argtypes = [POINTER(pullContext), POINTER(POINTER(meetPullVol)), c_uint, POINTER(NrrdKernelSpec), POINTER(NrrdKernelSpec), POINTER(NrrdKernelSpec), POINTER(NrrdKernelSpec)] meetPullInfoNew = libteem.meetPullInfoNew meetPullInfoNew.restype = POINTER(meetPullInfo) meetPullInfoNew.argtypes = [] meetPullInfoNix = libteem.meetPullInfoNix meetPullInfoNix.restype = POINTER(meetPullInfo) meetPullInfoNix.argtypes = [POINTER(meetPullInfo)] meetPullInfoParse = libteem.meetPullInfoParse meetPullInfoParse.restype = c_int meetPullInfoParse.argtypes = [POINTER(meetPullInfo), STRING] meetHestPullInfo = (POINTER(hestCB)).in_dll(libteem, 'meetHestPullInfo') meetPullInfoAddMulti = libteem.meetPullInfoAddMulti meetPullInfoAddMulti.restype = c_int meetPullInfoAddMulti.argtypes = [POINTER(pullContext), POINTER(POINTER(meetPullInfo)), c_uint] mite_t = c_double class miteUser(Structure): pass miteUser._pack_ = 4 miteUser._fields_ = [ ('nsin', POINTER(Nrrd)), ('nvin', POINTER(Nrrd)), ('ntin', POINTER(Nrrd)), ('ntxf', POINTER(POINTER(Nrrd))), ('nout', POINTER(Nrrd)), ('debug', POINTER(c_double)), ('debugArr', POINTER(airArray)), ('ndebug', POINTER(Nrrd)), ('debugIdx', c_int), ('ntxfNum', c_int), ('shadeStr', c_char * 257), ('normalStr', c_char * 257), ('rangeInit', mite_t * 9), ('refStep', c_double), ('rayStep', c_double), ('opacMatters', c_double), ('opacNear1', c_double), ('hctx', POINTER(hooverContext)), ('fakeFrom', c_double * 3), ('vectorD', c_double * 3), ('ksp', POINTER(NrrdKernelSpec) * 8), ('shape', POINTER(gageShape)), ('gctx0', POINTER(gageContext)), ('lit', POINTER(limnLight)), ('normalSide', c_int), ('verbUi', c_int), ('verbVi', c_int), ('umop', POINTER(airArray)), ('rendTime', c_double), ('sampRate', c_double), ] class miteShadeSpec(Structure): pass miteShadeSpec._fields_ = [ ('method', c_int), ('vec0', POINTER(gageItemSpec)), ('vec1', POINTER(gageItemSpec)), ('scl0', POINTER(gageItemSpec)), ('scl1', POINTER(gageItemSpec)), ] class miteRender(Structure): pass class miteThread_t(Structure): pass miteRender._pack_ = 4 miteRender._fields_ = [ ('ntxf', POINTER(POINTER(Nrrd))), ('ntxfNum', c_int), ('sclPvlIdx', c_int), ('vecPvlIdx', c_int), ('tenPvlIdx', c_int), ('shadeSpec', POINTER(miteShadeSpec)), ('normalSpec', POINTER(gageItemSpec)), ('time0', c_double), ('queryMite', gageQuery), ('queryMiteNonzero', c_int), ('tt', POINTER(miteThread_t) * 512), ('rmop', POINTER(airArray)), ] class miteStage(Structure): pass miteStage._pack_ = 4 miteStage._fields_ = [ ('val', POINTER(c_double)), ('size', c_int), ('op', c_int), ('qn', CFUNCTYPE(c_uint, POINTER(c_double))), ('min', c_double), ('max', c_double), ('data', POINTER(mite_t)), ('rangeIdx', c_int * 9), ('rangeNum', c_int), ('label', STRING), ] miteThread_t._pack_ = 4 miteThread_t._fields_ = [ ('gctx', POINTER(gageContext)), ('ansScl', POINTER(c_double)), ('nPerp', POINTER(c_double)), ('geomTens', POINTER(c_double)), ('ansVec', POINTER(c_double)), ('ansTen', POINTER(c_double)), ('ansMiteVal', POINTER(c_double)), ('directAnsMiteVal', POINTER(POINTER(c_double))), ('_normal', POINTER(c_double)), ('shadeVec0', POINTER(c_double)), ('shadeVec1', POINTER(c_double)), ('shadeScl0', POINTER(c_double)), ('shadeScl1', POINTER(c_double)), ('verbose', c_int), ('skip', c_int), ('thrid', c_int), ('ui', c_int), ('vi', c_int), ('raySample', c_int), ('samples', c_int), ('stage', POINTER(miteStage)), ('stageNum', c_int), ('range', mite_t * 9), ('rayStep', mite_t), ('V', mite_t * 3), ('RR', mite_t), ('GG', mite_t), ('BB', mite_t), ('TT', mite_t), ('ZZ', mite_t), ('rmop', POINTER(airArray)), ] miteThread = miteThread_t mitePresent = (c_int).in_dll(libteem, 'mitePresent') miteBiffKey = (STRING).in_dll(libteem, 'miteBiffKey') miteDefRefStep = (c_double).in_dll(libteem, 'miteDefRefStep') miteDefRenorm = (c_int).in_dll(libteem, 'miteDefRenorm') miteDefNormalSide = (c_int).in_dll(libteem, 'miteDefNormalSide') miteDefOpacNear1 = (c_double).in_dll(libteem, 'miteDefOpacNear1') miteDefOpacMatters = (c_double).in_dll(libteem, 'miteDefOpacMatters') miteVal = (POINTER(airEnum)).in_dll(libteem, 'miteVal') miteValGageKind = (POINTER(gageKind)).in_dll(libteem, 'miteValGageKind') miteStageOp = (POINTER(airEnum)).in_dll(libteem, 'miteStageOp') miteRangeChar = (c_char * 10).in_dll(libteem, 'miteRangeChar') miteVariableParse = libteem.miteVariableParse miteVariableParse.restype = c_int miteVariableParse.argtypes = [POINTER(gageItemSpec), STRING] miteVariablePrint = libteem.miteVariablePrint miteVariablePrint.restype = None miteVariablePrint.argtypes = [STRING, POINTER(gageItemSpec)] miteNtxfCheck = libteem.miteNtxfCheck miteNtxfCheck.restype = c_int miteNtxfCheck.argtypes = [POINTER(Nrrd)] miteQueryAdd = libteem.miteQueryAdd miteQueryAdd.restype = None miteQueryAdd.argtypes = [POINTER(c_ubyte), POINTER(c_ubyte), POINTER(c_ubyte), POINTER(c_ubyte), POINTER(gageItemSpec)] miteUserNew = libteem.miteUserNew miteUserNew.restype = POINTER(miteUser) miteUserNew.argtypes = [] miteUserNix = libteem.miteUserNix miteUserNix.restype = POINTER(miteUser) miteUserNix.argtypes = [POINTER(miteUser)] miteShadeSpecNew = libteem.miteShadeSpecNew miteShadeSpecNew.restype = POINTER(miteShadeSpec) miteShadeSpecNew.argtypes = [] miteShadeSpecNix = libteem.miteShadeSpecNix miteShadeSpecNix.restype = POINTER(miteShadeSpec) miteShadeSpecNix.argtypes = [POINTER(miteShadeSpec)] miteShadeSpecParse = libteem.miteShadeSpecParse miteShadeSpecParse.restype = c_int miteShadeSpecParse.argtypes = [POINTER(miteShadeSpec), STRING] miteShadeSpecPrint = libteem.miteShadeSpecPrint miteShadeSpecPrint.restype = None miteShadeSpecPrint.argtypes = [STRING, POINTER(miteShadeSpec)] miteShadeSpecQueryAdd = libteem.miteShadeSpecQueryAdd miteShadeSpecQueryAdd.restype = None miteShadeSpecQueryAdd.argtypes = [POINTER(c_ubyte), POINTER(c_ubyte), POINTER(c_ubyte), POINTER(c_ubyte), POINTER(miteShadeSpec)] miteRenderBegin = libteem.miteRenderBegin miteRenderBegin.restype = c_int miteRenderBegin.argtypes = [POINTER(POINTER(miteRender)), POINTER(miteUser)] miteRenderEnd = libteem.miteRenderEnd miteRenderEnd.restype = c_int miteRenderEnd.argtypes = [POINTER(miteRender), POINTER(miteUser)] miteThreadNew = libteem.miteThreadNew miteThreadNew.restype = POINTER(miteThread) miteThreadNew.argtypes = [] miteThreadNix = libteem.miteThreadNix miteThreadNix.restype = POINTER(miteThread) miteThreadNix.argtypes = [POINTER(miteThread)] miteThreadBegin = libteem.miteThreadBegin miteThreadBegin.restype = c_int miteThreadBegin.argtypes = [POINTER(POINTER(miteThread)), POINTER(miteRender), POINTER(miteUser), c_int] miteThreadEnd = libteem.miteThreadEnd miteThreadEnd.restype = c_int miteThreadEnd.argtypes = [POINTER(miteThread), POINTER(miteRender), POINTER(miteUser)] miteRayBegin = libteem.miteRayBegin miteRayBegin.restype = c_int miteRayBegin.argtypes = [POINTER(miteThread), POINTER(miteRender), POINTER(miteUser), c_int, c_int, c_double, POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double)] miteSample = libteem.miteSample miteSample.restype = c_double miteSample.argtypes = [POINTER(miteThread), POINTER(miteRender), POINTER(miteUser), c_int, c_double, c_int, POINTER(c_double), POINTER(c_double)] miteRayEnd = libteem.miteRayEnd miteRayEnd.restype = c_int miteRayEnd.argtypes = [POINTER(miteThread), POINTER(miteRender), POINTER(miteUser)] class mossSampler(Structure): pass mossSampler._pack_ = 4 mossSampler._fields_ = [ ('image', POINTER(Nrrd)), ('kernel', POINTER(NrrdKernel)), ('kparm', c_double * 8), ('ivc', POINTER(c_float)), ('xFslw', POINTER(c_double)), ('yFslw', POINTER(c_double)), ('fdiam', c_int), ('ncol', c_int), ('xIdx', POINTER(c_int)), ('yIdx', POINTER(c_int)), ('bg', POINTER(c_float)), ('boundary', c_int), ('flag', c_int * 2), ] mossBiffKey = (STRING).in_dll(libteem, 'mossBiffKey') mossDefBoundary = (c_int).in_dll(libteem, 'mossDefBoundary') mossDefCenter = (c_int).in_dll(libteem, 'mossDefCenter') mossVerbose = (c_int).in_dll(libteem, 'mossVerbose') mossPresent = (c_int).in_dll(libteem, 'mossPresent') mossSamplerNew = libteem.mossSamplerNew mossSamplerNew.restype = POINTER(mossSampler) mossSamplerNew.argtypes = [] mossSamplerFill = libteem.mossSamplerFill mossSamplerFill.restype = c_int mossSamplerFill.argtypes = [POINTER(mossSampler), c_int, c_int] mossSamplerEmpty = libteem.mossSamplerEmpty mossSamplerEmpty.restype = None mossSamplerEmpty.argtypes = [POINTER(mossSampler)] mossSamplerNix = libteem.mossSamplerNix mossSamplerNix.restype = POINTER(mossSampler) mossSamplerNix.argtypes = [POINTER(mossSampler)] mossImageCheck = libteem.mossImageCheck mossImageCheck.restype = c_int mossImageCheck.argtypes = [POINTER(Nrrd)] mossImageAlloc = libteem.mossImageAlloc mossImageAlloc.restype = c_int mossImageAlloc.argtypes = [POINTER(Nrrd), c_int, c_int, c_int, c_int] mossSamplerImageSet = libteem.mossSamplerImageSet mossSamplerImageSet.restype = c_int mossSamplerImageSet.argtypes = [POINTER(mossSampler), POINTER(Nrrd), POINTER(c_float)] mossSamplerKernelSet = libteem.mossSamplerKernelSet mossSamplerKernelSet.restype = c_int mossSamplerKernelSet.argtypes = [POINTER(mossSampler), POINTER(NrrdKernel), POINTER(c_double)] mossSamplerUpdate = libteem.mossSamplerUpdate mossSamplerUpdate.restype = c_int mossSamplerUpdate.argtypes = [POINTER(mossSampler)] mossSamplerSample = libteem.mossSamplerSample mossSamplerSample.restype = c_int mossSamplerSample.argtypes = [POINTER(c_float), POINTER(mossSampler), c_double, c_double] mossHestTransform = (POINTER(hestCB)).in_dll(libteem, 'mossHestTransform') mossHestOrigin = (POINTER(hestCB)).in_dll(libteem, 'mossHestOrigin') mossMatPrint = libteem.mossMatPrint mossMatPrint.restype = None mossMatPrint.argtypes = [POINTER(FILE), POINTER(c_double)] mossMatRightMultiply = libteem.mossMatRightMultiply mossMatRightMultiply.restype = POINTER(c_double) mossMatRightMultiply.argtypes = [POINTER(c_double), POINTER(c_double)] mossMatLeftMultiply = libteem.mossMatLeftMultiply mossMatLeftMultiply.restype = POINTER(c_double) mossMatLeftMultiply.argtypes = [POINTER(c_double), POINTER(c_double)] mossMatInvert = libteem.mossMatInvert mossMatInvert.restype = POINTER(c_double) mossMatInvert.argtypes = [POINTER(c_double), POINTER(c_double)] mossMatIdentitySet = libteem.mossMatIdentitySet mossMatIdentitySet.restype = POINTER(c_double) mossMatIdentitySet.argtypes = [POINTER(c_double)] mossMatTranslateSet = libteem.mossMatTranslateSet mossMatTranslateSet.restype = POINTER(c_double) mossMatTranslateSet.argtypes = [POINTER(c_double), c_double, c_double] mossMatRotateSet = libteem.mossMatRotateSet mossMatRotateSet.restype = POINTER(c_double) mossMatRotateSet.argtypes = [POINTER(c_double), c_double] mossMatFlipSet = libteem.mossMatFlipSet mossMatFlipSet.restype = POINTER(c_double) mossMatFlipSet.argtypes = [POINTER(c_double), c_double] mossMatShearSet = libteem.mossMatShearSet mossMatShearSet.restype = POINTER(c_double) mossMatShearSet.argtypes = [POINTER(c_double), c_double, c_double] mossMatScaleSet = libteem.mossMatScaleSet mossMatScaleSet.restype = POINTER(c_double) mossMatScaleSet.argtypes = [POINTER(c_double), c_double, c_double] mossMatApply = libteem.mossMatApply mossMatApply.restype = None mossMatApply.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), c_double, c_double] mossLinearTransform = libteem.mossLinearTransform mossLinearTransform.restype = c_int mossLinearTransform.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(c_float), POINTER(c_double), POINTER(mossSampler), c_double, c_double, c_double, c_double, c_int, c_int] nrrdDefaultWriteEncodingType = (c_int).in_dll(libteem, 'nrrdDefaultWriteEncodingType') nrrdDefaultWriteBareText = (c_int).in_dll(libteem, 'nrrdDefaultWriteBareText') nrrdDefaultWriteCharsPerLine = (c_uint).in_dll(libteem, 'nrrdDefaultWriteCharsPerLine') nrrdDefaultWriteValsPerLine = (c_uint).in_dll(libteem, 'nrrdDefaultWriteValsPerLine') nrrdDefaultResampleBoundary = (c_int).in_dll(libteem, 'nrrdDefaultResampleBoundary') nrrdDefaultResampleType = (c_int).in_dll(libteem, 'nrrdDefaultResampleType') nrrdDefaultResampleRenormalize = (c_int).in_dll(libteem, 'nrrdDefaultResampleRenormalize') nrrdDefaultResampleRound = (c_int).in_dll(libteem, 'nrrdDefaultResampleRound') nrrdDefaultResampleClamp = (c_int).in_dll(libteem, 'nrrdDefaultResampleClamp') nrrdDefaultResampleCheap = (c_int).in_dll(libteem, 'nrrdDefaultResampleCheap') nrrdDefaultResamplePadValue = (c_double).in_dll(libteem, 'nrrdDefaultResamplePadValue') nrrdDefaultResampleNonExistent = (c_int).in_dll(libteem, 'nrrdDefaultResampleNonExistent') nrrdDefaultKernelParm0 = (c_double).in_dll(libteem, 'nrrdDefaultKernelParm0') nrrdDefaultCenter = (c_int).in_dll(libteem, 'nrrdDefaultCenter') nrrdDefaultSpacing = (c_double).in_dll(libteem, 'nrrdDefaultSpacing') nrrdStateVerboseIO = (c_int).in_dll(libteem, 'nrrdStateVerboseIO') nrrdStateKeyValuePairsPropagate = (c_int).in_dll(libteem, 'nrrdStateKeyValuePairsPropagate') nrrdStateBlind8BitRange = (c_int).in_dll(libteem, 'nrrdStateBlind8BitRange') nrrdStateMeasureType = (c_int).in_dll(libteem, 'nrrdStateMeasureType') nrrdStateMeasureModeBins = (c_int).in_dll(libteem, 'nrrdStateMeasureModeBins') nrrdStateMeasureHistoType = (c_int).in_dll(libteem, 'nrrdStateMeasureHistoType') nrrdStateDisallowIntegerNonExist = (c_int).in_dll(libteem, 'nrrdStateDisallowIntegerNonExist') nrrdStateAlwaysSetContent = (c_int).in_dll(libteem, 'nrrdStateAlwaysSetContent') nrrdStateDisableContent = (c_int).in_dll(libteem, 'nrrdStateDisableContent') nrrdStateUnknownContent = (STRING).in_dll(libteem, 'nrrdStateUnknownContent') nrrdStateGrayscaleImage3D = (c_int).in_dll(libteem, 'nrrdStateGrayscaleImage3D') nrrdStateKeyValueReturnInternalPointers = (c_int).in_dll(libteem, 'nrrdStateKeyValueReturnInternalPointers') nrrdStateKindNoop = (c_int).in_dll(libteem, 'nrrdStateKindNoop') nrrdEnvVarDefaultWriteEncodingType = (STRING).in_dll(libteem, 'nrrdEnvVarDefaultWriteEncodingType') nrrdEnvVarDefaultWriteBareText = (STRING).in_dll(libteem, 'nrrdEnvVarDefaultWriteBareText') nrrdEnvVarDefaultWriteBareTextOld = (STRING).in_dll(libteem, 'nrrdEnvVarDefaultWriteBareTextOld') nrrdEnvVarDefaultCenter = (STRING).in_dll(libteem, 'nrrdEnvVarDefaultCenter') nrrdEnvVarDefaultCenterOld = (STRING).in_dll(libteem, 'nrrdEnvVarDefaultCenterOld') nrrdEnvVarDefaultWriteCharsPerLine = (STRING).in_dll(libteem, 'nrrdEnvVarDefaultWriteCharsPerLine') nrrdEnvVarDefaultWriteValsPerLine = (STRING).in_dll(libteem, 'nrrdEnvVarDefaultWriteValsPerLine') nrrdEnvVarDefaultKernelParm0 = (STRING).in_dll(libteem, 'nrrdEnvVarDefaultKernelParm0') nrrdEnvVarDefaultSpacing = (STRING).in_dll(libteem, 'nrrdEnvVarDefaultSpacing') nrrdEnvVarStateKindNoop = (STRING).in_dll(libteem, 'nrrdEnvVarStateKindNoop') nrrdEnvVarStateVerboseIO = (STRING).in_dll(libteem, 'nrrdEnvVarStateVerboseIO') nrrdEnvVarStateKeyValuePairsPropagate = (STRING).in_dll(libteem, 'nrrdEnvVarStateKeyValuePairsPropagate') nrrdEnvVarStateBlind8BitRange = (STRING).in_dll(libteem, 'nrrdEnvVarStateBlind8BitRange') nrrdEnvVarStateAlwaysSetContent = (STRING).in_dll(libteem, 'nrrdEnvVarStateAlwaysSetContent') nrrdEnvVarStateDisableContent = (STRING).in_dll(libteem, 'nrrdEnvVarStateDisableContent') nrrdEnvVarStateMeasureType = (STRING).in_dll(libteem, 'nrrdEnvVarStateMeasureType') nrrdEnvVarStateMeasureModeBins = (STRING).in_dll(libteem, 'nrrdEnvVarStateMeasureModeBins') nrrdEnvVarStateMeasureHistoType = (STRING).in_dll(libteem, 'nrrdEnvVarStateMeasureHistoType') nrrdEnvVarStateGrayscaleImage3D = (STRING).in_dll(libteem, 'nrrdEnvVarStateGrayscaleImage3D') nrrdGetenvBool = libteem.nrrdGetenvBool nrrdGetenvBool.restype = c_int nrrdGetenvBool.argtypes = [POINTER(c_int), POINTER(STRING), STRING] nrrdGetenvEnum = libteem.nrrdGetenvEnum nrrdGetenvEnum.restype = c_int nrrdGetenvEnum.argtypes = [POINTER(c_int), POINTER(STRING), POINTER(airEnum), STRING] nrrdGetenvInt = libteem.nrrdGetenvInt nrrdGetenvInt.restype = c_int nrrdGetenvInt.argtypes = [POINTER(c_int), POINTER(STRING), STRING] nrrdGetenvUInt = libteem.nrrdGetenvUInt nrrdGetenvUInt.restype = c_int nrrdGetenvUInt.argtypes = [POINTER(c_uint), POINTER(STRING), STRING] nrrdGetenvDouble = libteem.nrrdGetenvDouble nrrdGetenvDouble.restype = c_int nrrdGetenvDouble.argtypes = [POINTER(c_double), POINTER(STRING), STRING] nrrdDefaultGetenv = libteem.nrrdDefaultGetenv nrrdDefaultGetenv.restype = None nrrdDefaultGetenv.argtypes = [] nrrdStateGetenv = libteem.nrrdStateGetenv nrrdStateGetenv.restype = None nrrdStateGetenv.argtypes = [] nrrdFormatType = (POINTER(airEnum)).in_dll(libteem, 'nrrdFormatType') nrrdType = (POINTER(airEnum)).in_dll(libteem, 'nrrdType') nrrdEncodingType = (POINTER(airEnum)).in_dll(libteem, 'nrrdEncodingType') nrrdCenter = (POINTER(airEnum)).in_dll(libteem, 'nrrdCenter') nrrdKind = (POINTER(airEnum)).in_dll(libteem, 'nrrdKind') nrrdField = (POINTER(airEnum)).in_dll(libteem, 'nrrdField') nrrdSpace = (POINTER(airEnum)).in_dll(libteem, 'nrrdSpace') nrrdSpacingStatus = (POINTER(airEnum)).in_dll(libteem, 'nrrdSpacingStatus') nrrdBoundary = (POINTER(airEnum)).in_dll(libteem, 'nrrdBoundary') nrrdMeasure = (POINTER(airEnum)).in_dll(libteem, 'nrrdMeasure') nrrdUnaryOp = (POINTER(airEnum)).in_dll(libteem, 'nrrdUnaryOp') nrrdBinaryOp = (POINTER(airEnum)).in_dll(libteem, 'nrrdBinaryOp') nrrdTernaryOp = (POINTER(airEnum)).in_dll(libteem, 'nrrdTernaryOp') nrrdFFTWPlanRigor = (POINTER(airEnum)).in_dll(libteem, 'nrrdFFTWPlanRigor') nrrdResampleNonExistent = (POINTER(airEnum)).in_dll(libteem, 'nrrdResampleNonExistent') nrrdTypePrintfStr = (c_char * 129 * 12).in_dll(libteem, 'nrrdTypePrintfStr') nrrdTypeSize = (c_size_t * 12).in_dll(libteem, 'nrrdTypeSize') nrrdTypeMin = (c_double * 12).in_dll(libteem, 'nrrdTypeMin') nrrdTypeMax = (c_double * 12).in_dll(libteem, 'nrrdTypeMax') nrrdTypeIsIntegral = (c_int * 12).in_dll(libteem, 'nrrdTypeIsIntegral') nrrdTypeIsUnsigned = (c_int * 12).in_dll(libteem, 'nrrdTypeIsUnsigned') nrrdPresent = (c_int).in_dll(libteem, 'nrrdPresent') class NrrdIoState_t(Structure): pass NrrdIoState = NrrdIoState_t nrrdIoStateNew = libteem.nrrdIoStateNew nrrdIoStateNew.restype = POINTER(NrrdIoState) nrrdIoStateNew.argtypes = [] nrrdIoStateInit = libteem.nrrdIoStateInit nrrdIoStateInit.restype = None nrrdIoStateInit.argtypes = [POINTER(NrrdIoState)] nrrdIoStateNix = libteem.nrrdIoStateNix nrrdIoStateNix.restype = POINTER(NrrdIoState) nrrdIoStateNix.argtypes = [POINTER(NrrdIoState)] class NrrdResampleInfo(Structure): pass nrrdResampleInfoNew = libteem.nrrdResampleInfoNew nrrdResampleInfoNew.restype = POINTER(NrrdResampleInfo) nrrdResampleInfoNew.argtypes = [] nrrdResampleInfoNix = libteem.nrrdResampleInfoNix nrrdResampleInfoNix.restype = POINTER(NrrdResampleInfo) nrrdResampleInfoNix.argtypes = [POINTER(NrrdResampleInfo)] nrrdKernelSpecNew = libteem.nrrdKernelSpecNew nrrdKernelSpecNew.restype = POINTER(NrrdKernelSpec) nrrdKernelSpecNew.argtypes = [] nrrdKernelSpecCopy = libteem.nrrdKernelSpecCopy nrrdKernelSpecCopy.restype = POINTER(NrrdKernelSpec) nrrdKernelSpecCopy.argtypes = [POINTER(NrrdKernelSpec)] nrrdKernelSpecSet = libteem.nrrdKernelSpecSet nrrdKernelSpecSet.restype = None nrrdKernelSpecSet.argtypes = [POINTER(NrrdKernelSpec), POINTER(NrrdKernel), POINTER(c_double)] nrrdKernelParmSet = libteem.nrrdKernelParmSet nrrdKernelParmSet.restype = None nrrdKernelParmSet.argtypes = [POINTER(POINTER(NrrdKernel)), POINTER(c_double), POINTER(NrrdKernelSpec)] nrrdKernelSpecNix = libteem.nrrdKernelSpecNix nrrdKernelSpecNix.restype = POINTER(NrrdKernelSpec) nrrdKernelSpecNix.argtypes = [POINTER(NrrdKernelSpec)] nrrdInit = libteem.nrrdInit nrrdInit.restype = None nrrdInit.argtypes = [POINTER(Nrrd)] nrrdNew = libteem.nrrdNew nrrdNew.restype = POINTER(Nrrd) nrrdNew.argtypes = [] nrrdNix = libteem.nrrdNix nrrdNix.restype = POINTER(Nrrd) nrrdNix.argtypes = [POINTER(Nrrd)] nrrdEmpty = libteem.nrrdEmpty nrrdEmpty.restype = POINTER(Nrrd) nrrdEmpty.argtypes = [POINTER(Nrrd)] nrrdNuke = libteem.nrrdNuke nrrdNuke.restype = POINTER(Nrrd) nrrdNuke.argtypes = [POINTER(Nrrd)] nrrdWrap_nva = libteem.nrrdWrap_nva nrrdWrap_nva.restype = c_int nrrdWrap_nva.argtypes = [POINTER(Nrrd), c_void_p, c_int, c_uint, POINTER(c_size_t)] nrrdWrap_va = libteem.nrrdWrap_va nrrdWrap_va.restype = c_int nrrdWrap_va.argtypes = [POINTER(Nrrd), c_void_p, c_int, c_uint] nrrdBasicInfoInit = libteem.nrrdBasicInfoInit nrrdBasicInfoInit.restype = None nrrdBasicInfoInit.argtypes = [POINTER(Nrrd), c_int] nrrdBasicInfoCopy = libteem.nrrdBasicInfoCopy nrrdBasicInfoCopy.restype = c_int nrrdBasicInfoCopy.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int] nrrdCopy = libteem.nrrdCopy nrrdCopy.restype = c_int nrrdCopy.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] nrrdAlloc_nva = libteem.nrrdAlloc_nva nrrdAlloc_nva.restype = c_int nrrdAlloc_nva.argtypes = [POINTER(Nrrd), c_int, c_uint, POINTER(c_size_t)] nrrdAlloc_va = libteem.nrrdAlloc_va nrrdAlloc_va.restype = c_int nrrdAlloc_va.argtypes = [POINTER(Nrrd), c_int, c_uint] nrrdMaybeAlloc_nva = libteem.nrrdMaybeAlloc_nva nrrdMaybeAlloc_nva.restype = c_int nrrdMaybeAlloc_nva.argtypes = [POINTER(Nrrd), c_int, c_uint, POINTER(c_size_t)] nrrdMaybeAlloc_va = libteem.nrrdMaybeAlloc_va nrrdMaybeAlloc_va.restype = c_int nrrdMaybeAlloc_va.argtypes = [POINTER(Nrrd), c_int, c_uint] nrrdCompare = libteem.nrrdCompare nrrdCompare.restype = c_int nrrdCompare.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int, c_double, POINTER(c_int), STRING] nrrdPPM = libteem.nrrdPPM nrrdPPM.restype = c_int nrrdPPM.argtypes = [POINTER(Nrrd), c_size_t, c_size_t] nrrdPGM = libteem.nrrdPGM nrrdPGM.restype = c_int nrrdPGM.argtypes = [POINTER(Nrrd), c_size_t, c_size_t] nrrdKindIsDomain = libteem.nrrdKindIsDomain nrrdKindIsDomain.restype = c_int nrrdKindIsDomain.argtypes = [c_int] nrrdKindSize = libteem.nrrdKindSize nrrdKindSize.restype = c_uint nrrdKindSize.argtypes = [c_int] nrrdAxisInfoCopy = libteem.nrrdAxisInfoCopy nrrdAxisInfoCopy.restype = c_int nrrdAxisInfoCopy.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(c_int), c_int] nrrdAxisInfoSet_nva = libteem.nrrdAxisInfoSet_nva nrrdAxisInfoSet_nva.restype = None nrrdAxisInfoSet_nva.argtypes = [POINTER(Nrrd), c_int, c_void_p] nrrdAxisInfoSet_va = libteem.nrrdAxisInfoSet_va nrrdAxisInfoSet_va.restype = None nrrdAxisInfoSet_va.argtypes = [POINTER(Nrrd), c_int] nrrdAxisInfoGet_nva = libteem.nrrdAxisInfoGet_nva nrrdAxisInfoGet_nva.restype = None nrrdAxisInfoGet_nva.argtypes = [POINTER(Nrrd), c_int, c_void_p] nrrdAxisInfoGet_va = libteem.nrrdAxisInfoGet_va nrrdAxisInfoGet_va.restype = None nrrdAxisInfoGet_va.argtypes = [POINTER(Nrrd), c_int] nrrdAxisInfoPos = libteem.nrrdAxisInfoPos nrrdAxisInfoPos.restype = c_double nrrdAxisInfoPos.argtypes = [POINTER(Nrrd), c_uint, c_double] nrrdAxisInfoIdx = libteem.nrrdAxisInfoIdx nrrdAxisInfoIdx.restype = c_double nrrdAxisInfoIdx.argtypes = [POINTER(Nrrd), c_uint, c_double] nrrdAxisInfoPosRange = libteem.nrrdAxisInfoPosRange nrrdAxisInfoPosRange.restype = None nrrdAxisInfoPosRange.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(Nrrd), c_uint, c_double, c_double] nrrdAxisInfoIdxRange = libteem.nrrdAxisInfoIdxRange nrrdAxisInfoIdxRange.restype = None nrrdAxisInfoIdxRange.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(Nrrd), c_uint, c_double, c_double] nrrdAxisInfoSpacingSet = libteem.nrrdAxisInfoSpacingSet nrrdAxisInfoSpacingSet.restype = None nrrdAxisInfoSpacingSet.argtypes = [POINTER(Nrrd), c_uint] nrrdAxisInfoMinMaxSet = libteem.nrrdAxisInfoMinMaxSet nrrdAxisInfoMinMaxSet.restype = None nrrdAxisInfoMinMaxSet.argtypes = [POINTER(Nrrd), c_uint, c_int] nrrdAxisInfoCompare = libteem.nrrdAxisInfoCompare nrrdAxisInfoCompare.restype = c_int nrrdAxisInfoCompare.argtypes = [POINTER(NrrdAxisInfo), POINTER(NrrdAxisInfo), POINTER(c_int), STRING] nrrdDomainAxesGet = libteem.nrrdDomainAxesGet nrrdDomainAxesGet.restype = c_uint nrrdDomainAxesGet.argtypes = [POINTER(Nrrd), POINTER(c_uint)] nrrdRangeAxesGet = libteem.nrrdRangeAxesGet nrrdRangeAxesGet.restype = c_uint nrrdRangeAxesGet.argtypes = [POINTER(Nrrd), POINTER(c_uint)] nrrdSpatialAxesGet = libteem.nrrdSpatialAxesGet nrrdSpatialAxesGet.restype = c_uint nrrdSpatialAxesGet.argtypes = [POINTER(Nrrd), POINTER(c_uint)] nrrdNonSpatialAxesGet = libteem.nrrdNonSpatialAxesGet nrrdNonSpatialAxesGet.restype = c_uint nrrdNonSpatialAxesGet.argtypes = [POINTER(Nrrd), POINTER(c_uint)] nrrdSpacingCalculate = libteem.nrrdSpacingCalculate nrrdSpacingCalculate.restype = c_int nrrdSpacingCalculate.argtypes = [POINTER(Nrrd), c_uint, POINTER(c_double), POINTER(c_double)] nrrdOrientationReduce = libteem.nrrdOrientationReduce nrrdOrientationReduce.restype = c_int nrrdOrientationReduce.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int] nrrdBiffKey = (STRING).in_dll(libteem, 'nrrdBiffKey') nrrdSpaceDimension = libteem.nrrdSpaceDimension nrrdSpaceDimension.restype = c_uint nrrdSpaceDimension.argtypes = [c_int] nrrdSpaceSet = libteem.nrrdSpaceSet nrrdSpaceSet.restype = c_int nrrdSpaceSet.argtypes = [POINTER(Nrrd), c_int] nrrdSpaceDimensionSet = libteem.nrrdSpaceDimensionSet nrrdSpaceDimensionSet.restype = c_int nrrdSpaceDimensionSet.argtypes = [POINTER(Nrrd), c_uint] nrrdSpaceOriginGet = libteem.nrrdSpaceOriginGet nrrdSpaceOriginGet.restype = c_uint nrrdSpaceOriginGet.argtypes = [POINTER(Nrrd), POINTER(c_double)] nrrdSpaceOriginSet = libteem.nrrdSpaceOriginSet nrrdSpaceOriginSet.restype = c_int nrrdSpaceOriginSet.argtypes = [POINTER(Nrrd), POINTER(c_double)] nrrdOriginCalculate = libteem.nrrdOriginCalculate nrrdOriginCalculate.restype = c_int nrrdOriginCalculate.argtypes = [POINTER(Nrrd), POINTER(c_uint), c_uint, c_int, POINTER(c_double)] nrrdContentSet_va = libteem.nrrdContentSet_va nrrdContentSet_va.restype = c_int nrrdContentSet_va.argtypes = [POINTER(Nrrd), STRING, POINTER(Nrrd), STRING] nrrdDescribe = libteem.nrrdDescribe nrrdDescribe.restype = None nrrdDescribe.argtypes = [POINTER(FILE), POINTER(Nrrd)] nrrdCheck = libteem.nrrdCheck nrrdCheck.restype = c_int nrrdCheck.argtypes = [POINTER(Nrrd)] nrrdElementSize = libteem.nrrdElementSize nrrdElementSize.restype = c_size_t nrrdElementSize.argtypes = [POINTER(Nrrd)] nrrdElementNumber = libteem.nrrdElementNumber nrrdElementNumber.restype = c_size_t nrrdElementNumber.argtypes = [POINTER(Nrrd)] nrrdSanity = libteem.nrrdSanity nrrdSanity.restype = c_int nrrdSanity.argtypes = [] nrrdSameSize = libteem.nrrdSameSize nrrdSameSize.restype = c_int nrrdSameSize.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int] nrrdSpaceVecCopy = libteem.nrrdSpaceVecCopy nrrdSpaceVecCopy.restype = None nrrdSpaceVecCopy.argtypes = [POINTER(c_double), POINTER(c_double)] nrrdSpaceVecScaleAdd2 = libteem.nrrdSpaceVecScaleAdd2 nrrdSpaceVecScaleAdd2.restype = None nrrdSpaceVecScaleAdd2.argtypes = [POINTER(c_double), c_double, POINTER(c_double), c_double, POINTER(c_double)] nrrdSpaceVecScale = libteem.nrrdSpaceVecScale nrrdSpaceVecScale.restype = None nrrdSpaceVecScale.argtypes = [POINTER(c_double), c_double, POINTER(c_double)] nrrdSpaceVecNorm = libteem.nrrdSpaceVecNorm nrrdSpaceVecNorm.restype = c_double nrrdSpaceVecNorm.argtypes = [c_uint, POINTER(c_double)] nrrdSpaceVecExists = libteem.nrrdSpaceVecExists nrrdSpaceVecExists.restype = c_int nrrdSpaceVecExists.argtypes = [c_uint, POINTER(c_double)] nrrdSpaceVecSetNaN = libteem.nrrdSpaceVecSetNaN nrrdSpaceVecSetNaN.restype = None nrrdSpaceVecSetNaN.argtypes = [POINTER(c_double)] nrrdSanityOrDie = libteem.nrrdSanityOrDie nrrdSanityOrDie.restype = None nrrdSanityOrDie.argtypes = [STRING] nrrdSpaceVecSetZero = libteem.nrrdSpaceVecSetZero nrrdSpaceVecSetZero.restype = None nrrdSpaceVecSetZero.argtypes = [POINTER(c_double)] nrrdZeroSet = libteem.nrrdZeroSet nrrdZeroSet.restype = None nrrdZeroSet.argtypes = [POINTER(Nrrd)] nrrdCommentAdd = libteem.nrrdCommentAdd nrrdCommentAdd.restype = c_int nrrdCommentAdd.argtypes = [POINTER(Nrrd), STRING] nrrdCommentClear = libteem.nrrdCommentClear nrrdCommentClear.restype = None nrrdCommentClear.argtypes = [POINTER(Nrrd)] nrrdCommentCopy = libteem.nrrdCommentCopy nrrdCommentCopy.restype = c_int nrrdCommentCopy.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] nrrdKeyValueSize = libteem.nrrdKeyValueSize nrrdKeyValueSize.restype = c_uint nrrdKeyValueSize.argtypes = [POINTER(Nrrd)] nrrdKeyValueAdd = libteem.nrrdKeyValueAdd nrrdKeyValueAdd.restype = c_int nrrdKeyValueAdd.argtypes = [POINTER(Nrrd), STRING, STRING] nrrdKeyValueGet = libteem.nrrdKeyValueGet nrrdKeyValueGet.restype = STRING nrrdKeyValueGet.argtypes = [POINTER(Nrrd), STRING] nrrdKeyValueIndex = libteem.nrrdKeyValueIndex nrrdKeyValueIndex.restype = None nrrdKeyValueIndex.argtypes = [POINTER(Nrrd), POINTER(STRING), POINTER(STRING), c_uint] nrrdKeyValueErase = libteem.nrrdKeyValueErase nrrdKeyValueErase.restype = c_int nrrdKeyValueErase.argtypes = [POINTER(Nrrd), STRING] nrrdKeyValueClear = libteem.nrrdKeyValueClear nrrdKeyValueClear.restype = None nrrdKeyValueClear.argtypes = [POINTER(Nrrd)] nrrdKeyValueCopy = libteem.nrrdKeyValueCopy nrrdKeyValueCopy.restype = c_int nrrdKeyValueCopy.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] nrrdSwapEndian = libteem.nrrdSwapEndian nrrdSwapEndian.restype = None nrrdSwapEndian.argtypes = [POINTER(Nrrd)] class NrrdFormat(Structure): pass NrrdEncoding_t._fields_ = [ ('name', c_char * 129), ('suffix', c_char * 129), ('endianMatters', c_int), ('isCompression', c_int), ('available', CFUNCTYPE(c_int)), ('read', CFUNCTYPE(c_int, POINTER(FILE), c_void_p, c_size_t, POINTER(Nrrd), POINTER(NrrdIoState_t))), ('write', CFUNCTYPE(c_int, POINTER(FILE), c_void_p, c_size_t, POINTER(Nrrd), POINTER(NrrdIoState_t))), ] NrrdFormat._fields_ = [ ('name', c_char * 129), ('isImage', c_int), ('readable', c_int), ('usesDIO', c_int), ('available', CFUNCTYPE(c_int)), ('nameLooksLike', CFUNCTYPE(c_int, STRING)), ('fitsInto', CFUNCTYPE(c_int, POINTER(Nrrd), POINTER(NrrdEncoding_t), c_int)), ('contentStartsLike', CFUNCTYPE(c_int, POINTER(NrrdIoState_t))), ('read', CFUNCTYPE(c_int, POINTER(FILE), POINTER(Nrrd), POINTER(NrrdIoState_t))), ('write', CFUNCTYPE(c_int, POINTER(FILE), POINTER(Nrrd), POINTER(NrrdIoState_t))), ] nrrdFormatNRRD = (POINTER(NrrdFormat)).in_dll(libteem, 'nrrdFormatNRRD') nrrdFormatPNM = (POINTER(NrrdFormat)).in_dll(libteem, 'nrrdFormatPNM') nrrdFormatPNG = (POINTER(NrrdFormat)).in_dll(libteem, 'nrrdFormatPNG') nrrdFormatVTK = (POINTER(NrrdFormat)).in_dll(libteem, 'nrrdFormatVTK') nrrdFormatText = (POINTER(NrrdFormat)).in_dll(libteem, 'nrrdFormatText') nrrdFormatEPS = (POINTER(NrrdFormat)).in_dll(libteem, 'nrrdFormatEPS') nrrdFormatUnknown = (POINTER(NrrdFormat)).in_dll(libteem, 'nrrdFormatUnknown') nrrdFormatArray = (POINTER(NrrdFormat) * 7).in_dll(libteem, 'nrrdFormatArray') nrrdEncodingRaw = (POINTER(NrrdEncoding)).in_dll(libteem, 'nrrdEncodingRaw') nrrdEncodingAscii = (POINTER(NrrdEncoding)).in_dll(libteem, 'nrrdEncodingAscii') nrrdEncodingHex = (POINTER(NrrdEncoding)).in_dll(libteem, 'nrrdEncodingHex') nrrdEncodingGzip = (POINTER(NrrdEncoding)).in_dll(libteem, 'nrrdEncodingGzip') nrrdEncodingBzip2 = (POINTER(NrrdEncoding)).in_dll(libteem, 'nrrdEncodingBzip2') nrrdEncodingUnknown = (POINTER(NrrdEncoding)).in_dll(libteem, 'nrrdEncodingUnknown') nrrdEncodingArray = (POINTER(NrrdEncoding) * 6).in_dll(libteem, 'nrrdEncodingArray') nrrdFieldInfoParse = (CFUNCTYPE(c_int, POINTER(FILE), POINTER(Nrrd), POINTER(NrrdIoState), c_int) * 33).in_dll(libteem, 'nrrdFieldInfoParse') nrrdLineSkip = libteem.nrrdLineSkip nrrdLineSkip.restype = c_int nrrdLineSkip.argtypes = [POINTER(FILE), POINTER(NrrdIoState)] nrrdByteSkip = libteem.nrrdByteSkip nrrdByteSkip.restype = c_int nrrdByteSkip.argtypes = [POINTER(FILE), POINTER(Nrrd), POINTER(NrrdIoState)] nrrdLoad = libteem.nrrdLoad nrrdLoad.restype = c_int nrrdLoad.argtypes = [POINTER(Nrrd), STRING, POINTER(NrrdIoState)] nrrdLoadMulti = libteem.nrrdLoadMulti nrrdLoadMulti.restype = c_int nrrdLoadMulti.argtypes = [POINTER(POINTER(Nrrd)), c_uint, STRING, c_uint, POINTER(NrrdIoState)] nrrdRead = libteem.nrrdRead nrrdRead.restype = c_int nrrdRead.argtypes = [POINTER(Nrrd), POINTER(FILE), POINTER(NrrdIoState)] nrrdStringRead = libteem.nrrdStringRead nrrdStringRead.restype = c_int nrrdStringRead.argtypes = [POINTER(Nrrd), STRING, POINTER(NrrdIoState)] nrrdIoStateSet = libteem.nrrdIoStateSet nrrdIoStateSet.restype = c_int nrrdIoStateSet.argtypes = [POINTER(NrrdIoState), c_int, c_int] nrrdIoStateEncodingSet = libteem.nrrdIoStateEncodingSet nrrdIoStateEncodingSet.restype = c_int nrrdIoStateEncodingSet.argtypes = [POINTER(NrrdIoState), POINTER(NrrdEncoding)] nrrdIoStateFormatSet = libteem.nrrdIoStateFormatSet nrrdIoStateFormatSet.restype = c_int nrrdIoStateFormatSet.argtypes = [POINTER(NrrdIoState), POINTER(NrrdFormat)] nrrdIoStateGet = libteem.nrrdIoStateGet nrrdIoStateGet.restype = c_int nrrdIoStateGet.argtypes = [POINTER(NrrdIoState), c_int] nrrdIoStateEncodingGet = libteem.nrrdIoStateEncodingGet nrrdIoStateEncodingGet.restype = POINTER(NrrdEncoding) nrrdIoStateEncodingGet.argtypes = [POINTER(NrrdIoState)] nrrdIoStateFormatGet = libteem.nrrdIoStateFormatGet nrrdIoStateFormatGet.restype = POINTER(NrrdFormat) nrrdIoStateFormatGet.argtypes = [POINTER(NrrdIoState)] nrrdSave = libteem.nrrdSave nrrdSave.restype = c_int nrrdSave.argtypes = [STRING, POINTER(Nrrd), POINTER(NrrdIoState)] nrrdSaveMulti = libteem.nrrdSaveMulti nrrdSaveMulti.restype = c_int nrrdSaveMulti.argtypes = [STRING, POINTER(POINTER(Nrrd)), c_uint, c_uint, POINTER(NrrdIoState)] nrrdWrite = libteem.nrrdWrite nrrdWrite.restype = c_int nrrdWrite.argtypes = [POINTER(FILE), POINTER(Nrrd), POINTER(NrrdIoState)] nrrdStringWrite = libteem.nrrdStringWrite nrrdStringWrite.restype = c_int nrrdStringWrite.argtypes = [POINTER(STRING), POINTER(Nrrd), POINTER(NrrdIoState)] nrrdDLoad = (CFUNCTYPE(c_double, c_void_p) * 12).in_dll(libteem, 'nrrdDLoad') nrrdFLoad = (CFUNCTYPE(c_float, c_void_p) * 12).in_dll(libteem, 'nrrdFLoad') nrrdILoad = (CFUNCTYPE(c_int, c_void_p) * 12).in_dll(libteem, 'nrrdILoad') nrrdUILoad = (CFUNCTYPE(c_uint, c_void_p) * 12).in_dll(libteem, 'nrrdUILoad') nrrdDStore = (CFUNCTYPE(c_double, c_void_p, c_double) * 12).in_dll(libteem, 'nrrdDStore') nrrdFStore = (CFUNCTYPE(c_float, c_void_p, c_float) * 12).in_dll(libteem, 'nrrdFStore') nrrdIStore = (CFUNCTYPE(c_int, c_void_p, c_int) * 12).in_dll(libteem, 'nrrdIStore') nrrdUIStore = (CFUNCTYPE(c_uint, c_void_p, c_uint) * 12).in_dll(libteem, 'nrrdUIStore') nrrdDLookup = (CFUNCTYPE(c_double, c_void_p, c_size_t) * 12).in_dll(libteem, 'nrrdDLookup') nrrdFLookup = (CFUNCTYPE(c_float, c_void_p, c_size_t) * 12).in_dll(libteem, 'nrrdFLookup') nrrdILookup = (CFUNCTYPE(c_int, c_void_p, c_size_t) * 12).in_dll(libteem, 'nrrdILookup') nrrdUILookup = (CFUNCTYPE(c_uint, c_void_p, c_size_t) * 12).in_dll(libteem, 'nrrdUILookup') nrrdDInsert = (CFUNCTYPE(c_double, c_void_p, c_size_t, c_double) * 12).in_dll(libteem, 'nrrdDInsert') nrrdFInsert = (CFUNCTYPE(c_float, c_void_p, c_size_t, c_float) * 12).in_dll(libteem, 'nrrdFInsert') nrrdIInsert = (CFUNCTYPE(c_int, c_void_p, c_size_t, c_int) * 12).in_dll(libteem, 'nrrdIInsert') nrrdUIInsert = (CFUNCTYPE(c_uint, c_void_p, c_size_t, c_uint) * 12).in_dll(libteem, 'nrrdUIInsert') nrrdSprint = (CFUNCTYPE(c_int, STRING, c_void_p) * 12).in_dll(libteem, 'nrrdSprint') nrrdFprint = (CFUNCTYPE(c_int, POINTER(FILE), c_void_p) * 12).in_dll(libteem, 'nrrdFprint') nrrdMinMaxExactFind = (CFUNCTYPE(None, c_void_p, c_void_p, POINTER(c_int), POINTER(Nrrd)) * 12).in_dll(libteem, 'nrrdMinMaxExactFind') nrrdValCompare = (CFUNCTYPE(c_int, c_void_p, c_void_p) * 12).in_dll(libteem, 'nrrdValCompare') nrrdValCompareInv = (CFUNCTYPE(c_int, c_void_p, c_void_p) * 12).in_dll(libteem, 'nrrdValCompareInv') nrrdArrayCompare = libteem.nrrdArrayCompare nrrdArrayCompare.restype = c_int nrrdArrayCompare.argtypes = [c_int, c_void_p, c_void_p, c_size_t, c_double, POINTER(c_int), STRING] nrrdAxesInsert = libteem.nrrdAxesInsert nrrdAxesInsert.restype = c_int nrrdAxesInsert.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint] nrrdInvertPerm = libteem.nrrdInvertPerm nrrdInvertPerm.restype = c_int nrrdInvertPerm.argtypes = [POINTER(c_uint), POINTER(c_uint), c_uint] nrrdAxesPermute = libteem.nrrdAxesPermute nrrdAxesPermute.restype = c_int nrrdAxesPermute.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(c_uint)] nrrdShuffle = libteem.nrrdShuffle nrrdShuffle.restype = c_int nrrdShuffle.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, POINTER(c_size_t)] nrrdAxesSwap = libteem.nrrdAxesSwap nrrdAxesSwap.restype = c_int nrrdAxesSwap.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, c_uint] nrrdFlip = libteem.nrrdFlip nrrdFlip.restype = c_int nrrdFlip.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint] nrrdJoin = libteem.nrrdJoin nrrdJoin.restype = c_int nrrdJoin.argtypes = [POINTER(Nrrd), POINTER(POINTER(Nrrd)), c_uint, c_uint, c_int] nrrdReshape_va = libteem.nrrdReshape_va nrrdReshape_va.restype = c_int nrrdReshape_va.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint] nrrdReshape_nva = libteem.nrrdReshape_nva nrrdReshape_nva.restype = c_int nrrdReshape_nva.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, POINTER(c_size_t)] nrrdAxesSplit = libteem.nrrdAxesSplit nrrdAxesSplit.restype = c_int nrrdAxesSplit.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, c_size_t, c_size_t] nrrdAxesDelete = libteem.nrrdAxesDelete nrrdAxesDelete.restype = c_int nrrdAxesDelete.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint] nrrdAxesMerge = libteem.nrrdAxesMerge nrrdAxesMerge.restype = c_int nrrdAxesMerge.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint] nrrdBlock = libteem.nrrdBlock nrrdBlock.restype = c_int nrrdBlock.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] nrrdUnblock = libteem.nrrdUnblock nrrdUnblock.restype = c_int nrrdUnblock.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int] nrrdTile2D = libteem.nrrdTile2D nrrdTile2D.restype = c_int nrrdTile2D.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, c_uint, c_uint, c_size_t, c_size_t] nrrdUntile2D = libteem.nrrdUntile2D nrrdUntile2D.restype = c_int nrrdUntile2D.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, c_uint, c_uint, c_size_t, c_size_t] nrrdHestNrrd = (POINTER(hestCB)).in_dll(libteem, 'nrrdHestNrrd') nrrdHestKernelSpec = (POINTER(hestCB)).in_dll(libteem, 'nrrdHestKernelSpec') nrrdHestIter = (POINTER(hestCB)).in_dll(libteem, 'nrrdHestIter') class NrrdIter(Structure): pass nrrdIterNew = libteem.nrrdIterNew nrrdIterNew.restype = POINTER(NrrdIter) nrrdIterNew.argtypes = [] nrrdIterSetValue = libteem.nrrdIterSetValue nrrdIterSetValue.restype = None nrrdIterSetValue.argtypes = [POINTER(NrrdIter), c_double] nrrdIterSetNrrd = libteem.nrrdIterSetNrrd nrrdIterSetNrrd.restype = None nrrdIterSetNrrd.argtypes = [POINTER(NrrdIter), POINTER(Nrrd)] nrrdIterSetOwnNrrd = libteem.nrrdIterSetOwnNrrd nrrdIterSetOwnNrrd.restype = None nrrdIterSetOwnNrrd.argtypes = [POINTER(NrrdIter), POINTER(Nrrd)] nrrdIterValue = libteem.nrrdIterValue nrrdIterValue.restype = c_double nrrdIterValue.argtypes = [POINTER(NrrdIter)] nrrdIterContent = libteem.nrrdIterContent nrrdIterContent.restype = STRING nrrdIterContent.argtypes = [POINTER(NrrdIter)] nrrdIterNix = libteem.nrrdIterNix nrrdIterNix.restype = POINTER(NrrdIter) nrrdIterNix.argtypes = [POINTER(NrrdIter)] class NrrdRange(Structure): pass nrrdRangeNew = libteem.nrrdRangeNew nrrdRangeNew.restype = POINTER(NrrdRange) nrrdRangeNew.argtypes = [c_double, c_double] NrrdRange._pack_ = 4 NrrdRange._fields_ = [ ('min', c_double), ('max', c_double), ('hasNonExist', c_int), ] nrrdRangeCopy = libteem.nrrdRangeCopy nrrdRangeCopy.restype = POINTER(NrrdRange) nrrdRangeCopy.argtypes = [POINTER(NrrdRange)] nrrdRangeNix = libteem.nrrdRangeNix nrrdRangeNix.restype = POINTER(NrrdRange) nrrdRangeNix.argtypes = [POINTER(NrrdRange)] nrrdRangeReset = libteem.nrrdRangeReset nrrdRangeReset.restype = None nrrdRangeReset.argtypes = [POINTER(NrrdRange)] nrrdRangeSet = libteem.nrrdRangeSet nrrdRangeSet.restype = None nrrdRangeSet.argtypes = [POINTER(NrrdRange), POINTER(Nrrd), c_int] nrrdRangePercentileSet = libteem.nrrdRangePercentileSet nrrdRangePercentileSet.restype = c_int nrrdRangePercentileSet.argtypes = [POINTER(NrrdRange), POINTER(Nrrd), c_double, c_double, c_uint, c_int] nrrdRangePercentileFromStringSet = libteem.nrrdRangePercentileFromStringSet nrrdRangePercentileFromStringSet.restype = c_int nrrdRangePercentileFromStringSet.argtypes = [POINTER(NrrdRange), POINTER(Nrrd), STRING, STRING, c_uint, c_int] nrrdRangeSafeSet = libteem.nrrdRangeSafeSet nrrdRangeSafeSet.restype = None nrrdRangeSafeSet.argtypes = [POINTER(NrrdRange), POINTER(Nrrd), c_int] nrrdRangeNewSet = libteem.nrrdRangeNewSet nrrdRangeNewSet.restype = POINTER(NrrdRange) nrrdRangeNewSet.argtypes = [POINTER(Nrrd), c_int] nrrdHasNonExist = libteem.nrrdHasNonExist nrrdHasNonExist.restype = c_int nrrdHasNonExist.argtypes = [POINTER(Nrrd)] nrrdFClamp = (CFUNCTYPE(c_float, c_float) * 12).in_dll(libteem, 'nrrdFClamp') nrrdDClamp = (CFUNCTYPE(c_double, c_double) * 12).in_dll(libteem, 'nrrdDClamp') nrrdConvert = libteem.nrrdConvert nrrdConvert.restype = c_int nrrdConvert.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int] nrrdClampConvert = libteem.nrrdClampConvert nrrdClampConvert.restype = c_int nrrdClampConvert.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int] nrrdQuantize = libteem.nrrdQuantize nrrdQuantize.restype = c_int nrrdQuantize.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(NrrdRange), c_uint] nrrdUnquantize = libteem.nrrdUnquantize nrrdUnquantize.restype = c_int nrrdUnquantize.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int] nrrdHistoEq = libteem.nrrdHistoEq nrrdHistoEq.restype = c_int nrrdHistoEq.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(POINTER(Nrrd)), c_uint, c_uint, c_float] nrrdApply1DLut = libteem.nrrdApply1DLut nrrdApply1DLut.restype = c_int nrrdApply1DLut.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(NrrdRange), POINTER(Nrrd), c_int, c_int] nrrdApplyMulti1DLut = libteem.nrrdApplyMulti1DLut nrrdApplyMulti1DLut.restype = c_int nrrdApplyMulti1DLut.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(NrrdRange), POINTER(Nrrd), c_int, c_int] nrrdApply1DRegMap = libteem.nrrdApply1DRegMap nrrdApply1DRegMap.restype = c_int nrrdApply1DRegMap.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(NrrdRange), POINTER(Nrrd), c_int, c_int] nrrdApplyMulti1DRegMap = libteem.nrrdApplyMulti1DRegMap nrrdApplyMulti1DRegMap.restype = c_int nrrdApplyMulti1DRegMap.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(NrrdRange), POINTER(Nrrd), c_int, c_int] nrrd1DIrregMapCheck = libteem.nrrd1DIrregMapCheck nrrd1DIrregMapCheck.restype = c_int nrrd1DIrregMapCheck.argtypes = [POINTER(Nrrd)] nrrd1DIrregAclGenerate = libteem.nrrd1DIrregAclGenerate nrrd1DIrregAclGenerate.restype = c_int nrrd1DIrregAclGenerate.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_size_t] nrrd1DIrregAclCheck = libteem.nrrd1DIrregAclCheck nrrd1DIrregAclCheck.restype = c_int nrrd1DIrregAclCheck.argtypes = [POINTER(Nrrd)] nrrdApply1DIrregMap = libteem.nrrdApply1DIrregMap nrrdApply1DIrregMap.restype = c_int nrrdApply1DIrregMap.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(NrrdRange), POINTER(Nrrd), POINTER(Nrrd), c_int, c_int] nrrdApply1DSubstitution = libteem.nrrdApply1DSubstitution nrrdApply1DSubstitution.restype = c_int nrrdApply1DSubstitution.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd)] nrrdApply2DLut = libteem.nrrdApply2DLut nrrdApply2DLut.restype = c_int nrrdApply2DLut.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, POINTER(NrrdRange), POINTER(NrrdRange), POINTER(Nrrd), c_int, c_int, c_int] nrrdSlice = libteem.nrrdSlice nrrdSlice.restype = c_int nrrdSlice.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, c_size_t] nrrdCrop = libteem.nrrdCrop nrrdCrop.restype = c_int nrrdCrop.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(c_size_t), POINTER(c_size_t)] nrrdSliceSelect = libteem.nrrdSliceSelect nrrdSliceSelect.restype = c_int nrrdSliceSelect.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), c_uint, POINTER(Nrrd), c_double] nrrdSample_nva = libteem.nrrdSample_nva nrrdSample_nva.restype = c_int nrrdSample_nva.argtypes = [c_void_p, POINTER(Nrrd), POINTER(c_size_t)] nrrdSample_va = libteem.nrrdSample_va nrrdSample_va.restype = c_int nrrdSample_va.argtypes = [c_void_p, POINTER(Nrrd)] nrrdSimpleCrop = libteem.nrrdSimpleCrop nrrdSimpleCrop.restype = c_int nrrdSimpleCrop.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint] nrrdCropAuto = libteem.nrrdCropAuto nrrdCropAuto.restype = c_int nrrdCropAuto.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(c_size_t), POINTER(c_size_t), POINTER(c_uint), c_uint, c_int, c_double, c_int] nrrdSplice = libteem.nrrdSplice nrrdSplice.restype = c_int nrrdSplice.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), c_uint, c_size_t] nrrdPad_nva = libteem.nrrdPad_nva nrrdPad_nva.restype = c_int nrrdPad_nva.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(ptrdiff_t), POINTER(ptrdiff_t), c_int, c_double] nrrdPad_va = libteem.nrrdPad_va nrrdPad_va.restype = c_int nrrdPad_va.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(ptrdiff_t), POINTER(ptrdiff_t), c_int] nrrdSimplePad_nva = libteem.nrrdSimplePad_nva nrrdSimplePad_nva.restype = c_int nrrdSimplePad_nva.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, c_int, c_double] nrrdSimplePad_va = libteem.nrrdSimplePad_va nrrdSimplePad_va.restype = c_int nrrdSimplePad_va.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, c_int] nrrdInset = libteem.nrrdInset nrrdInset.restype = c_int nrrdInset.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), POINTER(c_size_t)] nrrdMeasureLine = (CFUNCTYPE(None, c_void_p, c_int, c_void_p, c_int, c_size_t, c_double, c_double) * 30).in_dll(libteem, 'nrrdMeasureLine') nrrdProject = libteem.nrrdProject nrrdProject.restype = c_int nrrdProject.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, c_int, c_int] nrrdHisto = libteem.nrrdHisto nrrdHisto.restype = c_int nrrdHisto.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(NrrdRange), POINTER(Nrrd), c_size_t, c_int] nrrdHistoCheck = libteem.nrrdHistoCheck nrrdHistoCheck.restype = c_int nrrdHistoCheck.argtypes = [POINTER(Nrrd)] nrrdHistoDraw = libteem.nrrdHistoDraw nrrdHistoDraw.restype = c_int nrrdHistoDraw.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_size_t, c_int, c_double] nrrdHistoAxis = libteem.nrrdHistoAxis nrrdHistoAxis.restype = c_int nrrdHistoAxis.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(NrrdRange), c_uint, c_size_t, c_int] nrrdHistoJoint = libteem.nrrdHistoJoint nrrdHistoJoint.restype = c_int nrrdHistoJoint.argtypes = [POINTER(Nrrd), POINTER(POINTER(Nrrd)), POINTER(POINTER(NrrdRange)), c_uint, POINTER(Nrrd), POINTER(c_size_t), c_int, POINTER(c_int)] nrrdHistoThresholdOtsu = libteem.nrrdHistoThresholdOtsu nrrdHistoThresholdOtsu.restype = c_int nrrdHistoThresholdOtsu.argtypes = [POINTER(c_double), POINTER(Nrrd), c_double] nrrdArithGamma = libteem.nrrdArithGamma nrrdArithGamma.restype = c_int nrrdArithGamma.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(NrrdRange), c_double] nrrdArithUnaryOp = libteem.nrrdArithUnaryOp nrrdArithUnaryOp.restype = c_int nrrdArithUnaryOp.argtypes = [POINTER(Nrrd), c_int, POINTER(Nrrd)] nrrdArithBinaryOp = libteem.nrrdArithBinaryOp nrrdArithBinaryOp.restype = c_int nrrdArithBinaryOp.argtypes = [POINTER(Nrrd), c_int, POINTER(Nrrd), POINTER(Nrrd)] nrrdArithTernaryOp = libteem.nrrdArithTernaryOp nrrdArithTernaryOp.restype = c_int nrrdArithTernaryOp.argtypes = [POINTER(Nrrd), c_int, POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd)] nrrdArithAffine = libteem.nrrdArithAffine nrrdArithAffine.restype = c_int nrrdArithAffine.argtypes = [POINTER(Nrrd), c_double, POINTER(Nrrd), c_double, c_double, c_double, c_int] nrrdArithIterBinaryOp = libteem.nrrdArithIterBinaryOp nrrdArithIterBinaryOp.restype = c_int nrrdArithIterBinaryOp.argtypes = [POINTER(Nrrd), c_int, POINTER(NrrdIter), POINTER(NrrdIter)] nrrdArithIterBinaryOpSelect = libteem.nrrdArithIterBinaryOpSelect nrrdArithIterBinaryOpSelect.restype = c_int nrrdArithIterBinaryOpSelect.argtypes = [POINTER(Nrrd), c_int, POINTER(NrrdIter), POINTER(NrrdIter), c_uint] nrrdArithIterTernaryOp = libteem.nrrdArithIterTernaryOp nrrdArithIterTernaryOp.restype = c_int nrrdArithIterTernaryOp.argtypes = [POINTER(Nrrd), c_int, POINTER(NrrdIter), POINTER(NrrdIter), POINTER(NrrdIter)] nrrdArithIterTernaryOpSelect = libteem.nrrdArithIterTernaryOpSelect nrrdArithIterTernaryOpSelect.restype = c_int nrrdArithIterTernaryOpSelect.argtypes = [POINTER(Nrrd), c_int, POINTER(NrrdIter), POINTER(NrrdIter), POINTER(NrrdIter), c_uint] nrrdArithIterAffine = libteem.nrrdArithIterAffine nrrdArithIterAffine.restype = c_int nrrdArithIterAffine.argtypes = [POINTER(Nrrd), POINTER(NrrdIter), POINTER(NrrdIter), POINTER(NrrdIter), POINTER(NrrdIter), POINTER(NrrdIter), c_int] nrrdCRC32 = libteem.nrrdCRC32 nrrdCRC32.restype = c_uint nrrdCRC32.argtypes = [POINTER(Nrrd), c_int] nrrdCheapMedian = libteem.nrrdCheapMedian nrrdCheapMedian.restype = c_int nrrdCheapMedian.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int, c_int, c_uint, c_float, c_uint] nrrdDistanceL2 = libteem.nrrdDistanceL2 nrrdDistanceL2.restype = c_int nrrdDistanceL2.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int, POINTER(c_int), c_double, c_int] nrrdDistanceL2Biased = libteem.nrrdDistanceL2Biased nrrdDistanceL2Biased.restype = c_int nrrdDistanceL2Biased.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int, POINTER(c_int), c_double, c_double, c_int] nrrdDistanceL2Signed = libteem.nrrdDistanceL2Signed nrrdDistanceL2Signed.restype = c_int nrrdDistanceL2Signed.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int, POINTER(c_int), c_double, c_int] class NrrdDeringContext(Structure): pass nrrdDeringContextNew = libteem.nrrdDeringContextNew nrrdDeringContextNew.restype = POINTER(NrrdDeringContext) nrrdDeringContextNew.argtypes = [] nrrdDeringContextNix = libteem.nrrdDeringContextNix nrrdDeringContextNix.restype = POINTER(NrrdDeringContext) nrrdDeringContextNix.argtypes = [POINTER(NrrdDeringContext)] nrrdDeringVerboseSet = libteem.nrrdDeringVerboseSet nrrdDeringVerboseSet.restype = c_int nrrdDeringVerboseSet.argtypes = [POINTER(NrrdDeringContext), c_int] nrrdDeringLinearInterpSet = libteem.nrrdDeringLinearInterpSet nrrdDeringLinearInterpSet.restype = c_int nrrdDeringLinearInterpSet.argtypes = [POINTER(NrrdDeringContext), c_int] nrrdDeringVerticalSeamSet = libteem.nrrdDeringVerticalSeamSet nrrdDeringVerticalSeamSet.restype = c_int nrrdDeringVerticalSeamSet.argtypes = [POINTER(NrrdDeringContext), c_int] nrrdDeringInputSet = libteem.nrrdDeringInputSet nrrdDeringInputSet.restype = c_int nrrdDeringInputSet.argtypes = [POINTER(NrrdDeringContext), POINTER(Nrrd)] nrrdDeringCenterSet = libteem.nrrdDeringCenterSet nrrdDeringCenterSet.restype = c_int nrrdDeringCenterSet.argtypes = [POINTER(NrrdDeringContext), c_double, c_double] nrrdDeringClampPercSet = libteem.nrrdDeringClampPercSet nrrdDeringClampPercSet.restype = c_int nrrdDeringClampPercSet.argtypes = [POINTER(NrrdDeringContext), c_double, c_double] nrrdDeringClampHistoBinsSet = libteem.nrrdDeringClampHistoBinsSet nrrdDeringClampHistoBinsSet.restype = c_int nrrdDeringClampHistoBinsSet.argtypes = [POINTER(NrrdDeringContext), c_uint] nrrdDeringRadiusScaleSet = libteem.nrrdDeringRadiusScaleSet nrrdDeringRadiusScaleSet.restype = c_int nrrdDeringRadiusScaleSet.argtypes = [POINTER(NrrdDeringContext), c_double] nrrdDeringThetaNumSet = libteem.nrrdDeringThetaNumSet nrrdDeringThetaNumSet.restype = c_int nrrdDeringThetaNumSet.argtypes = [POINTER(NrrdDeringContext), c_uint] nrrdDeringRadialKernelSet = libteem.nrrdDeringRadialKernelSet nrrdDeringRadialKernelSet.restype = c_int nrrdDeringRadialKernelSet.argtypes = [POINTER(NrrdDeringContext), POINTER(NrrdKernel), POINTER(c_double)] nrrdDeringThetaKernelSet = libteem.nrrdDeringThetaKernelSet nrrdDeringThetaKernelSet.restype = c_int nrrdDeringThetaKernelSet.argtypes = [POINTER(NrrdDeringContext), POINTER(NrrdKernel), POINTER(c_double)] nrrdDeringExecute = libteem.nrrdDeringExecute nrrdDeringExecute.restype = c_int nrrdDeringExecute.argtypes = [POINTER(NrrdDeringContext), POINTER(Nrrd)] nrrdResample_t = c_double class NrrdResampleContext(Structure): pass nrrdResampleContextNew = libteem.nrrdResampleContextNew nrrdResampleContextNew.restype = POINTER(NrrdResampleContext) nrrdResampleContextNew.argtypes = [] nrrdResampleContextNix = libteem.nrrdResampleContextNix nrrdResampleContextNix.restype = POINTER(NrrdResampleContext) nrrdResampleContextNix.argtypes = [POINTER(NrrdResampleContext)] nrrdResampleDefaultCenterSet = libteem.nrrdResampleDefaultCenterSet nrrdResampleDefaultCenterSet.restype = c_int nrrdResampleDefaultCenterSet.argtypes = [POINTER(NrrdResampleContext), c_int] nrrdResampleNonExistentSet = libteem.nrrdResampleNonExistentSet nrrdResampleNonExistentSet.restype = c_int nrrdResampleNonExistentSet.argtypes = [POINTER(NrrdResampleContext), c_int] nrrdResampleNrrdSet = libteem.nrrdResampleNrrdSet nrrdResampleNrrdSet.restype = c_int nrrdResampleNrrdSet.argtypes = [POINTER(NrrdResampleContext), POINTER(Nrrd)] nrrdResampleInputSet = libteem.nrrdResampleInputSet nrrdResampleInputSet.restype = c_int nrrdResampleInputSet.argtypes = [POINTER(NrrdResampleContext), POINTER(Nrrd)] nrrdResampleKernelSet = libteem.nrrdResampleKernelSet nrrdResampleKernelSet.restype = c_int nrrdResampleKernelSet.argtypes = [POINTER(NrrdResampleContext), c_uint, POINTER(NrrdKernel), POINTER(c_double)] nrrdResampleSamplesSet = libteem.nrrdResampleSamplesSet nrrdResampleSamplesSet.restype = c_int nrrdResampleSamplesSet.argtypes = [POINTER(NrrdResampleContext), c_uint, c_size_t] nrrdResampleRangeSet = libteem.nrrdResampleRangeSet nrrdResampleRangeSet.restype = c_int nrrdResampleRangeSet.argtypes = [POINTER(NrrdResampleContext), c_uint, c_double, c_double] nrrdResampleOverrideCenterSet = libteem.nrrdResampleOverrideCenterSet nrrdResampleOverrideCenterSet.restype = c_int nrrdResampleOverrideCenterSet.argtypes = [POINTER(NrrdResampleContext), c_uint, c_int] nrrdResampleRangeFullSet = libteem.nrrdResampleRangeFullSet nrrdResampleRangeFullSet.restype = c_int nrrdResampleRangeFullSet.argtypes = [POINTER(NrrdResampleContext), c_uint] nrrdResampleBoundarySet = libteem.nrrdResampleBoundarySet nrrdResampleBoundarySet.restype = c_int nrrdResampleBoundarySet.argtypes = [POINTER(NrrdResampleContext), c_int] nrrdResamplePadValueSet = libteem.nrrdResamplePadValueSet nrrdResamplePadValueSet.restype = c_int nrrdResamplePadValueSet.argtypes = [POINTER(NrrdResampleContext), c_double] nrrdResampleTypeOutSet = libteem.nrrdResampleTypeOutSet nrrdResampleTypeOutSet.restype = c_int nrrdResampleTypeOutSet.argtypes = [POINTER(NrrdResampleContext), c_int] nrrdResampleRenormalizeSet = libteem.nrrdResampleRenormalizeSet nrrdResampleRenormalizeSet.restype = c_int nrrdResampleRenormalizeSet.argtypes = [POINTER(NrrdResampleContext), c_int] nrrdResampleRoundSet = libteem.nrrdResampleRoundSet nrrdResampleRoundSet.restype = c_int nrrdResampleRoundSet.argtypes = [POINTER(NrrdResampleContext), c_int] nrrdResampleClampSet = libteem.nrrdResampleClampSet nrrdResampleClampSet.restype = c_int nrrdResampleClampSet.argtypes = [POINTER(NrrdResampleContext), c_int] nrrdResampleExecute = libteem.nrrdResampleExecute nrrdResampleExecute.restype = c_int nrrdResampleExecute.argtypes = [POINTER(NrrdResampleContext), POINTER(Nrrd)] NrrdResampleInfo._pack_ = 4 NrrdResampleInfo._fields_ = [ ('kernel', POINTER(NrrdKernel) * 16), ('samples', c_size_t * 16), ('parm', c_double * 8 * 16), ('min', c_double * 16), ('max', c_double * 16), ('boundary', c_int), ('type', c_int), ('renormalize', c_int), ('round', c_int), ('clamp', c_int), ('cheap', c_int), ('padValue', c_double), ] nrrdSpatialResample = libteem.nrrdSpatialResample nrrdSpatialResample.restype = c_int nrrdSpatialResample.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(NrrdResampleInfo)] nrrdSimpleResample = libteem.nrrdSimpleResample nrrdSimpleResample.restype = c_int nrrdSimpleResample.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(NrrdKernel), POINTER(c_double), POINTER(c_size_t), POINTER(c_double)] nrrdCCValid = libteem.nrrdCCValid nrrdCCValid.restype = c_int nrrdCCValid.argtypes = [POINTER(Nrrd)] nrrdCCSize = libteem.nrrdCCSize nrrdCCSize.restype = c_uint nrrdCCSize.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] nrrdCCMax = libteem.nrrdCCMax nrrdCCMax.restype = c_uint nrrdCCMax.argtypes = [POINTER(Nrrd)] nrrdCCNum = libteem.nrrdCCNum nrrdCCNum.restype = c_uint nrrdCCNum.argtypes = [POINTER(Nrrd)] nrrdCCFind = libteem.nrrdCCFind nrrdCCFind.restype = c_int nrrdCCFind.argtypes = [POINTER(Nrrd), POINTER(POINTER(Nrrd)), POINTER(Nrrd), c_int, c_uint] nrrdCCAdjacency = libteem.nrrdCCAdjacency nrrdCCAdjacency.restype = c_int nrrdCCAdjacency.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint] nrrdCCMerge = libteem.nrrdCCMerge nrrdCCMerge.restype = c_int nrrdCCMerge.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), c_int, c_uint, c_uint, c_uint] nrrdCCRevalue = libteem.nrrdCCRevalue nrrdCCRevalue.restype = c_int nrrdCCRevalue.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd)] nrrdCCSettle = libteem.nrrdCCSettle nrrdCCSettle.restype = c_int nrrdCCSettle.argtypes = [POINTER(Nrrd), POINTER(POINTER(Nrrd)), POINTER(Nrrd)] nrrdFFTWEnabled = (c_int).in_dll(libteem, 'nrrdFFTWEnabled') nrrdFFTWWisdomRead = libteem.nrrdFFTWWisdomRead nrrdFFTWWisdomRead.restype = c_int nrrdFFTWWisdomRead.argtypes = [POINTER(FILE)] nrrdFFT = libteem.nrrdFFT nrrdFFT.restype = c_int nrrdFFT.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(c_uint), c_uint, c_int, c_int, c_int] nrrdFFTWWisdomWrite = libteem.nrrdFFTWWisdomWrite nrrdFFTWWisdomWrite.restype = c_int nrrdFFTWWisdomWrite.argtypes = [POINTER(FILE)] nrrdKernelTMF = (POINTER(NrrdKernel) * 5 * 5 * 4).in_dll(libteem, 'nrrdKernelTMF') nrrdKernelTMF_maxD = (c_uint).in_dll(libteem, 'nrrdKernelTMF_maxD') nrrdKernelTMF_maxC = (c_uint).in_dll(libteem, 'nrrdKernelTMF_maxC') nrrdKernelTMF_maxA = (c_uint).in_dll(libteem, 'nrrdKernelTMF_maxA') nrrdKernelHann = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelHann') nrrdKernelHannD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelHannD') nrrdKernelHannDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelHannDD') nrrdKernelBlackman = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBlackman') nrrdKernelBlackmanD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBlackmanD') nrrdKernelBlackmanDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBlackmanDD') nrrdKernelBSpline1 = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline1') nrrdKernelBSpline1D = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline1D') nrrdKernelBSpline2 = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline2') nrrdKernelBSpline2D = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline2D') nrrdKernelBSpline2DD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline2DD') nrrdKernelBSpline3 = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline3') nrrdKernelBSpline3D = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline3D') nrrdKernelBSpline3DD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline3DD') nrrdKernelBSpline3DDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline3DDD') nrrdKernelBSpline3ApproxInverse = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline3ApproxInverse') nrrdKernelBSpline4 = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline4') nrrdKernelBSpline4D = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline4D') nrrdKernelBSpline4DD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline4DD') nrrdKernelBSpline4DDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline4DDD') nrrdKernelBSpline5 = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline5') nrrdKernelBSpline5D = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline5D') nrrdKernelBSpline5DD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline5DD') nrrdKernelBSpline5DDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline5DDD') nrrdKernelBSpline5ApproxInverse = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline5ApproxInverse') nrrdKernelBSpline6 = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline6') nrrdKernelBSpline6D = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline6D') nrrdKernelBSpline6DD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline6DD') nrrdKernelBSpline6DDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline6DDD') nrrdKernelBSpline7 = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline7') nrrdKernelBSpline7D = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline7D') nrrdKernelBSpline7DD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline7DD') nrrdKernelBSpline7DDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline7DDD') nrrdKernelBSpline7ApproxInverse = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBSpline7ApproxInverse') nrrdKernelZero = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelZero') nrrdKernelBox = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBox') nrrdKernelBoxSupportDebug = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBoxSupportDebug') nrrdKernelCos4SupportDebug = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCos4SupportDebug') nrrdKernelCos4SupportDebugD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCos4SupportDebugD') nrrdKernelCos4SupportDebugDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCos4SupportDebugDD') nrrdKernelCos4SupportDebugDDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCos4SupportDebugDDD') nrrdKernelCatmullRomSupportDebug = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCatmullRomSupportDebug') nrrdKernelCatmullRomSupportDebugD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCatmullRomSupportDebugD') nrrdKernelCatmullRomSupportDebugDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCatmullRomSupportDebugDD') nrrdKernelCheap = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCheap') nrrdKernelHermiteScaleSpaceFlag = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelHermiteScaleSpaceFlag') nrrdKernelTent = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelTent') nrrdKernelForwDiff = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelForwDiff') nrrdKernelCentDiff = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCentDiff') nrrdKernelBCCubic = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBCCubic') nrrdKernelBCCubicD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBCCubicD') nrrdKernelBCCubicDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelBCCubicDD') nrrdKernelCatmullRom = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCatmullRom') nrrdKernelCatmullRomD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCatmullRomD') nrrdKernelCatmullRomDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelCatmullRomDD') nrrdKernelAQuartic = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelAQuartic') nrrdKernelAQuarticD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelAQuarticD') nrrdKernelAQuarticDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelAQuarticDD') nrrdKernelC3Quintic = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC3Quintic') nrrdKernelC3QuinticD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC3QuinticD') nrrdKernelC3QuinticDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC3QuinticDD') nrrdKernelC4Hexic = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC4Hexic') nrrdKernelC4HexicD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC4HexicD') nrrdKernelC4HexicDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC4HexicDD') nrrdKernelC4HexicDDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC4HexicDDD') nrrdKernelC4HexicApproxInverse = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC4HexicApproxInverse') nrrdKernelC5Septic = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC5Septic') nrrdKernelC5SepticD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC5SepticD') nrrdKernelC5SepticDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC5SepticDD') nrrdKernelC5SepticDDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC5SepticDDD') nrrdKernelC5SepticApproxInverse = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelC5SepticApproxInverse') nrrdKernelGaussian = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelGaussian') nrrdKernelGaussianD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelGaussianD') nrrdKernelGaussianDD = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelGaussianDD') nrrdKernelDiscreteGaussian = (POINTER(NrrdKernel)).in_dll(libteem, 'nrrdKernelDiscreteGaussian') nrrdKernelParse = libteem.nrrdKernelParse nrrdKernelParse.restype = c_int nrrdKernelParse.argtypes = [POINTER(POINTER(NrrdKernel)), POINTER(c_double), STRING] nrrdKernelSpecParse = libteem.nrrdKernelSpecParse nrrdKernelSpecParse.restype = c_int nrrdKernelSpecParse.argtypes = [POINTER(NrrdKernelSpec), STRING] nrrdKernelSpecSprint = libteem.nrrdKernelSpecSprint nrrdKernelSpecSprint.restype = c_int nrrdKernelSpecSprint.argtypes = [STRING, POINTER(NrrdKernelSpec)] nrrdKernelSprint = libteem.nrrdKernelSprint nrrdKernelSprint.restype = c_int nrrdKernelSprint.argtypes = [STRING, POINTER(NrrdKernel), POINTER(c_double)] nrrdKernelCompare = libteem.nrrdKernelCompare nrrdKernelCompare.restype = c_int nrrdKernelCompare.argtypes = [POINTER(NrrdKernel), POINTER(c_double), POINTER(NrrdKernel), POINTER(c_double), POINTER(c_int), STRING] nrrdKernelCheck = libteem.nrrdKernelCheck nrrdKernelCheck.restype = c_int nrrdKernelCheck.argtypes = [POINTER(NrrdKernel), POINTER(c_double), c_size_t, c_double, c_uint, c_uint, POINTER(NrrdKernel), POINTER(c_double)] class pullInfoSpec_t(Structure): pass pullInfoSpec_t._pack_ = 4 pullInfoSpec_t._fields_ = [ ('info', c_int), ('source', c_int), ('volName', STRING), ('item', c_int), ('prop', c_int), ('scale', c_double), ('zero', c_double), ('constraint', c_int), ('volIdx', c_uint), ] pullInfoSpec = pullInfoSpec_t class pullPoint_t(Structure): pass pullPoint_t._pack_ = 4 pullPoint_t._fields_ = [ ('idtag', c_uint), ('idCC', c_uint), ('neighPoint', POINTER(POINTER(pullPoint_t))), ('neighPointNum', c_uint), ('neighPointArr', POINTER(airArray)), ('neighDistMean', c_double), ('neighCovar', c_float * 10), ('neighTanCovar', c_float * 6), ('stability', c_float), ('neighInterNum', c_uint), ('stuckIterNum', c_uint), ('status', c_int), ('pos', c_double * 4), ('energy', c_double), ('force', c_double * 4), ('stepEnergy', c_double), ('stepConstr', c_double), ('info', c_double * 1), ] pullPoint = pullPoint_t class pullBin_t(Structure): pass pullBin_t._fields_ = [ ('point', POINTER(POINTER(pullPoint))), ('pointNum', c_uint), ('pointArr', POINTER(airArray)), ('neighBin', POINTER(POINTER(pullBin_t))), ] pullBin = pullBin_t class pullEnergy(Structure): pass pullEnergy._fields_ = [ ('name', c_char * 129), ('parmNum', c_uint), ('well', CFUNCTYPE(c_double, POINTER(c_double), POINTER(c_double))), ('eval', CFUNCTYPE(c_double, POINTER(c_double), c_double, POINTER(c_double))), ] class pullEnergySpec(Structure): pass pullEnergySpec._pack_ = 4 pullEnergySpec._fields_ = [ ('energy', POINTER(pullEnergy)), ('parm', c_double * 3), ] class pullVolume(Structure): pass pullVolume._pack_ = 4 pullVolume._fields_ = [ ('verbose', c_int), ('name', STRING), ('kind', POINTER(gageKind)), ('ninSingle', POINTER(Nrrd)), ('ninScale', POINTER(POINTER(Nrrd))), ('scaleNum', c_uint), ('scalePos', POINTER(c_double)), ('scaleDerivNorm', c_int), ('scaleDerivNormBias', c_double), ('ksp00', POINTER(NrrdKernelSpec)), ('ksp11', POINTER(NrrdKernelSpec)), ('ksp22', POINTER(NrrdKernelSpec)), ('kspSS', POINTER(NrrdKernelSpec)), ('pullValQuery', gageQuery), ('gctx', POINTER(gageContext)), ('gpvl', POINTER(gagePerVolume)), ('gpvlSS', POINTER(POINTER(gagePerVolume))), ('seedOnly', c_int), ('forSeedPreThresh', c_int), ] class pullTask_t(Structure): pass pullTask_t._fields_ = [ ('pctx', POINTER(pullContext_t)), ('vol', POINTER(pullVolume) * 4), ('ans', POINTER(c_double) * 24), ('processMode', c_int), ('probeSeedPreThreshOnly', c_int), ('thread', POINTER(airThread)), ('threadIdx', c_uint), ('rng', POINTER(airRandMTState)), ('pointBuffer', POINTER(pullPoint)), ('neighPoint', POINTER(POINTER(pullPoint))), ('addPoint', POINTER(POINTER(pullPoint))), ('addPointNum', c_uint), ('addPointArr', POINTER(airArray)), ('nixPoint', POINTER(POINTER(pullPoint))), ('nixPointNum', c_uint), ('nixPointArr', POINTER(airArray)), ('returnPtr', c_void_p), ('stuckNum', c_uint), ] pullTask = pullTask_t class pullInitParm(Structure): pass pullInitParm._pack_ = 4 pullInitParm._fields_ = [ ('method', c_int), ('liveThreshUse', c_int), ('unequalShapesAllow', c_int), ('jitter', c_double), ('numInitial', c_uint), ('haltonStartIndex', c_uint), ('samplesAlongScaleNum', c_uint), ('ppvZRange', c_uint * 2), ('pointPerVoxel', c_int), ('npos', POINTER(Nrrd)), ] class pullIterParm(Structure): pass pullIterParm._fields_ = [ ('min', c_uint), ('max', c_uint), ('popCntlPeriod', c_uint), ('addDescent', c_uint), ('constraintMax', c_uint), ('stuckMax', c_uint), ('callback', c_uint), ('snap', c_uint), ('energyIncreasePermitHalfLife', c_uint), ] class pullSysParm(Structure): pass pullSysParm._pack_ = 4 pullSysParm._fields_ = [ ('alpha', c_double), ('beta', c_double), ('gamma', c_double), ('separableGammaLearnRescale', c_double), ('theta', c_double), ('wall', c_double), ('radiusSpace', c_double), ('radiusScale', c_double), ('binWidthSpace', c_double), ('neighborTrueProb', c_double), ('probeProb', c_double), ('stepInitial', c_double), ('opporStepScale', c_double), ('backStepScale', c_double), ('constraintStepMin', c_double), ('energyDecreaseMin', c_double), ('energyDecreasePopCntlMin', c_double), ('energyIncreasePermit', c_double), ('fracNeighNixedMax', c_double), ] class pullFlag(Structure): pass pullFlag._fields_ = [ ('permuteOnRebin', c_int), ('noPopCntlWithZeroAlpha', c_int), ('useBetaForGammaLearn', c_int), ('restrictiveAddToBins', c_int), ('energyFromStrength', c_int), ('nixAtVolumeEdgeSpace', c_int), ('constraintBeforeSeedThresh', c_int), ('popCntlEnoughTest', c_int), ('convergenceIgnoresPopCntl', c_int), ('noAdd', c_int), ('binSingle', c_int), ('allowCodimension3Constraints', c_int), ('scaleIsTau', c_int), ('startSkipsPoints', c_int), ('zeroZ', c_int), ] pullContext_t._pack_ = 4 pullContext_t._fields_ = [ ('initParm', pullInitParm), ('iterParm', pullIterParm), ('sysParm', pullSysParm), ('flag', pullFlag), ('verbose', c_int), ('threadNum', c_uint), ('rngSeed', c_uint), ('progressBinMod', c_uint), ('iter_cb', CFUNCTYPE(None, c_void_p)), ('data_cb', c_void_p), ('vol', POINTER(pullVolume) * 4), ('volNum', c_uint), ('ispec', POINTER(pullInfoSpec) * 24), ('interType', c_int), ('energySpecR', POINTER(pullEnergySpec)), ('energySpecS', POINTER(pullEnergySpec)), ('energySpecWin', POINTER(pullEnergySpec)), ('haltonOffset', c_uint), ('bboxMin', c_double * 4), ('bboxMax', c_double * 4), ('infoTotalLen', c_uint), ('infoIdx', c_uint * 24), ('idtagNext', c_uint), ('haveScale', c_int), ('constraint', c_int), ('constraintDim', c_int), ('targetDim', c_int), ('finished', c_int), ('maxDistSpace', c_double), ('maxDistScale', c_double), ('voxelSizeSpace', c_double), ('voxelSizeScale', c_double), ('eipScale', c_double), ('bin', POINTER(pullBin)), ('binsEdge', c_uint * 4), ('binNum', c_uint), ('binNextIdx', c_uint), ('tmpPointPerm', POINTER(c_uint)), ('tmpPointPtr', POINTER(POINTER(pullPoint))), ('tmpPointNum', c_uint), ('binMutex', POINTER(airThreadMutex)), ('task', POINTER(POINTER(pullTask))), ('iterBarrierA', POINTER(airThreadBarrier)), ('iterBarrierB', POINTER(airThreadBarrier)), ('logAdd', POINTER(FILE)), ('timeIteration', c_double), ('timeRun', c_double), ('energy', c_double), ('addNum', c_uint), ('nixNum', c_uint), ('stuckNum', c_uint), ('pointNum', c_uint), ('CCNum', c_uint), ('iter', c_uint), ('count', c_uint * 15), ] class pullTrace(Structure): pass pullTrace._pack_ = 4 pullTrace._fields_ = [ ('seedPos', c_double * 4), ('nvert', POINTER(Nrrd)), ('nstrn', POINTER(Nrrd)), ('nvelo', POINTER(Nrrd)), ('seedIdx', c_uint), ('whyStop', c_int * 2), ('whyNowhere', c_int), ] class pullTraceMulti(Structure): pass pullTraceMulti._fields_ = [ ('trace', POINTER(POINTER(pullTrace))), ('traceNum', c_uint), ('traceArr', POINTER(airArray)), ] class pullPtrPtrUnion(Union): pass pullPtrPtrUnion._fields_ = [ ('points', POINTER(POINTER(POINTER(pullPoint)))), ('v', POINTER(c_void_p)), ] pullPresent = (c_int).in_dll(libteem, 'pullPresent') pullPhistEnabled = (c_int).in_dll(libteem, 'pullPhistEnabled') pullBiffKey = (STRING).in_dll(libteem, 'pullBiffKey') pullInitRandomSet = libteem.pullInitRandomSet pullInitRandomSet.restype = c_int pullInitRandomSet.argtypes = [POINTER(pullContext), c_uint] pullInitHaltonSet = libteem.pullInitHaltonSet pullInitHaltonSet.restype = c_int pullInitHaltonSet.argtypes = [POINTER(pullContext), c_uint, c_uint] pullInitPointPerVoxelSet = libteem.pullInitPointPerVoxelSet pullInitPointPerVoxelSet.restype = c_int pullInitPointPerVoxelSet.argtypes = [POINTER(pullContext), c_int, c_uint, c_uint, c_uint, c_double] pullInitGivenPosSet = libteem.pullInitGivenPosSet pullInitGivenPosSet.restype = c_int pullInitGivenPosSet.argtypes = [POINTER(pullContext), POINTER(Nrrd)] pullInitLiveThreshUseSet = libteem.pullInitLiveThreshUseSet pullInitLiveThreshUseSet.restype = c_int pullInitLiveThreshUseSet.argtypes = [POINTER(pullContext), c_int] pullInitUnequalShapesAllowSet = libteem.pullInitUnequalShapesAllowSet pullInitUnequalShapesAllowSet.restype = c_int pullInitUnequalShapesAllowSet.argtypes = [POINTER(pullContext), c_int] pullIterParmSet = libteem.pullIterParmSet pullIterParmSet.restype = c_int pullIterParmSet.argtypes = [POINTER(pullContext), c_int, c_uint] pullSysParmSet = libteem.pullSysParmSet pullSysParmSet.restype = c_int pullSysParmSet.argtypes = [POINTER(pullContext), c_int, c_double] pullFlagSet = libteem.pullFlagSet pullFlagSet.restype = c_int pullFlagSet.argtypes = [POINTER(pullContext), c_int, c_int] pullVerboseSet = libteem.pullVerboseSet pullVerboseSet.restype = c_int pullVerboseSet.argtypes = [POINTER(pullContext), c_int] pullThreadNumSet = libteem.pullThreadNumSet pullThreadNumSet.restype = c_int pullThreadNumSet.argtypes = [POINTER(pullContext), c_uint] pullRngSeedSet = libteem.pullRngSeedSet pullRngSeedSet.restype = c_int pullRngSeedSet.argtypes = [POINTER(pullContext), c_uint] pullProgressBinModSet = libteem.pullProgressBinModSet pullProgressBinModSet.restype = c_int pullProgressBinModSet.argtypes = [POINTER(pullContext), c_uint] pullCallbackSet = libteem.pullCallbackSet pullCallbackSet.restype = c_int pullCallbackSet.argtypes = [POINTER(pullContext), CFUNCTYPE(None, c_void_p), c_void_p] pullInterEnergySet = libteem.pullInterEnergySet pullInterEnergySet.restype = c_int pullInterEnergySet.argtypes = [POINTER(pullContext), c_int, POINTER(pullEnergySpec), POINTER(pullEnergySpec), POINTER(pullEnergySpec)] pullLogAddSet = libteem.pullLogAddSet pullLogAddSet.restype = c_int pullLogAddSet.argtypes = [POINTER(pullContext), POINTER(FILE)] pullInterType = (POINTER(airEnum)).in_dll(libteem, 'pullInterType') pullEnergyType = (POINTER(airEnum)).in_dll(libteem, 'pullEnergyType') pullEnergyUnknown = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyUnknown') pullEnergySpring = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergySpring') pullEnergyGauss = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyGauss') pullEnergyBspln = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyBspln') pullEnergyButterworth = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyButterworth') pullEnergyCotan = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyCotan') pullEnergyCubic = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyCubic') pullEnergyQuartic = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyQuartic') pullEnergyCubicWell = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyCubicWell') pullEnergyBetterCubicWell = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyBetterCubicWell') pullEnergyQuarticWell = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyQuarticWell') pullEnergyHepticWell = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyHepticWell') pullEnergyZero = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyZero') pullEnergyButterworthParabola = (POINTER(pullEnergy)).in_dll(libteem, 'pullEnergyButterworthParabola') pullEnergyAll = (POINTER(pullEnergy) * 14).in_dll(libteem, 'pullEnergyAll') pullEnergySpecNew = libteem.pullEnergySpecNew pullEnergySpecNew.restype = POINTER(pullEnergySpec) pullEnergySpecNew.argtypes = [] pullEnergySpecSet = libteem.pullEnergySpecSet pullEnergySpecSet.restype = None pullEnergySpecSet.argtypes = [POINTER(pullEnergySpec), POINTER(pullEnergy), POINTER(c_double)] pullEnergySpecCopy = libteem.pullEnergySpecCopy pullEnergySpecCopy.restype = None pullEnergySpecCopy.argtypes = [POINTER(pullEnergySpec), POINTER(pullEnergySpec)] pullEnergySpecNix = libteem.pullEnergySpecNix pullEnergySpecNix.restype = POINTER(pullEnergySpec) pullEnergySpecNix.argtypes = [POINTER(pullEnergySpec)] pullEnergySpecParse = libteem.pullEnergySpecParse pullEnergySpecParse.restype = c_int pullEnergySpecParse.argtypes = [POINTER(pullEnergySpec), STRING] pullHestEnergySpec = (POINTER(hestCB)).in_dll(libteem, 'pullHestEnergySpec') pullVolumeNew = libteem.pullVolumeNew pullVolumeNew.restype = POINTER(pullVolume) pullVolumeNew.argtypes = [] pullVolumeNix = libteem.pullVolumeNix pullVolumeNix.restype = POINTER(pullVolume) pullVolumeNix.argtypes = [POINTER(pullVolume)] pullVolumeSingleAdd = libteem.pullVolumeSingleAdd pullVolumeSingleAdd.restype = c_int pullVolumeSingleAdd.argtypes = [POINTER(pullContext), POINTER(gageKind), STRING, POINTER(Nrrd), POINTER(NrrdKernelSpec), POINTER(NrrdKernelSpec), POINTER(NrrdKernelSpec)] pullVolumeStackAdd = libteem.pullVolumeStackAdd pullVolumeStackAdd.restype = c_int pullVolumeStackAdd.argtypes = [POINTER(pullContext), POINTER(gageKind), STRING, POINTER(Nrrd), POINTER(POINTER(Nrrd)), POINTER(c_double), c_uint, c_int, c_double, POINTER(NrrdKernelSpec), POINTER(NrrdKernelSpec), POINTER(NrrdKernelSpec), POINTER(NrrdKernelSpec)] pullVolumeLookup = libteem.pullVolumeLookup pullVolumeLookup.restype = POINTER(pullVolume) pullVolumeLookup.argtypes = [POINTER(pullContext), STRING] pullConstraintScaleRange = libteem.pullConstraintScaleRange pullConstraintScaleRange.restype = c_int pullConstraintScaleRange.argtypes = [POINTER(pullContext), POINTER(c_double)] pullInfo = (POINTER(airEnum)).in_dll(libteem, 'pullInfo') pullSource = (POINTER(airEnum)).in_dll(libteem, 'pullSource') pullProp = (POINTER(airEnum)).in_dll(libteem, 'pullProp') pullProcessMode = (POINTER(airEnum)).in_dll(libteem, 'pullProcessMode') pullTraceStop = (POINTER(airEnum)).in_dll(libteem, 'pullTraceStop') pullCount = (POINTER(airEnum)).in_dll(libteem, 'pullCount') pullConstraintFail = (POINTER(airEnum)).in_dll(libteem, 'pullConstraintFail') pullPropLen = libteem.pullPropLen pullPropLen.restype = c_uint pullPropLen.argtypes = [c_int] pullInfoLen = libteem.pullInfoLen pullInfoLen.restype = c_uint pullInfoLen.argtypes = [c_int] pullInfoSpecNew = libteem.pullInfoSpecNew pullInfoSpecNew.restype = POINTER(pullInfoSpec) pullInfoSpecNew.argtypes = [] pullInfoSpecNix = libteem.pullInfoSpecNix pullInfoSpecNix.restype = POINTER(pullInfoSpec) pullInfoSpecNix.argtypes = [POINTER(pullInfoSpec)] pullInfoSpecAdd = libteem.pullInfoSpecAdd pullInfoSpecAdd.restype = c_int pullInfoSpecAdd.argtypes = [POINTER(pullContext), POINTER(pullInfoSpec)] pullInfoGet = libteem.pullInfoGet pullInfoGet.restype = c_int pullInfoGet.argtypes = [POINTER(Nrrd), c_int, POINTER(pullContext)] pullInfoSpecSprint = libteem.pullInfoSpecSprint pullInfoSpecSprint.restype = c_int pullInfoSpecSprint.argtypes = [STRING, POINTER(pullContext), POINTER(pullInfoSpec)] pullContextNew = libteem.pullContextNew pullContextNew.restype = POINTER(pullContext) pullContextNew.argtypes = [] pullContextNix = libteem.pullContextNix pullContextNix.restype = POINTER(pullContext) pullContextNix.argtypes = [POINTER(pullContext)] pullOutputGet = libteem.pullOutputGet pullOutputGet.restype = c_int pullOutputGet.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), POINTER(c_double), c_double, POINTER(pullContext)] pullOutputGetFilter = libteem.pullOutputGetFilter pullOutputGetFilter.restype = c_int pullOutputGetFilter.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), POINTER(c_double), c_double, POINTER(pullContext), c_uint, c_uint] pullPositionHistoryGet = libteem.pullPositionHistoryGet pullPositionHistoryGet.restype = c_int pullPositionHistoryGet.argtypes = [POINTER(limnPolyData), POINTER(pullContext)] pullPropGet = libteem.pullPropGet pullPropGet.restype = c_int pullPropGet.argtypes = [POINTER(Nrrd), c_int, POINTER(pullContext)] pullPointInitializePerVoxel = libteem.pullPointInitializePerVoxel pullPointInitializePerVoxel.restype = c_int pullPointInitializePerVoxel.argtypes = [POINTER(pullContext), c_uint, POINTER(pullPoint), POINTER(pullVolume), POINTER(c_int)] pullPointInitializeRandomOrHalton = libteem.pullPointInitializeRandomOrHalton pullPointInitializeRandomOrHalton.restype = c_int pullPointInitializeRandomOrHalton.argtypes = [POINTER(pullContext), c_uint, POINTER(pullPoint), POINTER(pullVolume)] pullPointInitializeGivenPos = libteem.pullPointInitializeGivenPos pullPointInitializeGivenPos.restype = c_int pullPointInitializeGivenPos.argtypes = [POINTER(pullContext), POINTER(c_double), c_uint, POINTER(pullPoint), POINTER(c_int)] pullPointScalar = libteem.pullPointScalar pullPointScalar.restype = c_double pullPointScalar.argtypes = [POINTER(pullContext), POINTER(pullPoint), c_int, POINTER(c_double), POINTER(c_double)] pullPointNumber = libteem.pullPointNumber pullPointNumber.restype = c_uint pullPointNumber.argtypes = [POINTER(pullContext)] pullPointNumberFilter = libteem.pullPointNumberFilter pullPointNumberFilter.restype = c_uint pullPointNumberFilter.argtypes = [POINTER(pullContext), c_uint, c_uint] pullPointNew = libteem.pullPointNew pullPointNew.restype = POINTER(pullPoint) pullPointNew.argtypes = [POINTER(pullContext)] pullPointNix = libteem.pullPointNix pullPointNix.restype = POINTER(pullPoint) pullPointNix.argtypes = [POINTER(pullPoint)] pullProbe = libteem.pullProbe pullProbe.restype = c_int pullProbe.argtypes = [POINTER(pullTask), POINTER(pullPoint)] pullBinsPointAdd = libteem.pullBinsPointAdd pullBinsPointAdd.restype = c_int pullBinsPointAdd.argtypes = [POINTER(pullContext), POINTER(pullPoint), POINTER(POINTER(pullBin))] pullBinsPointMaybeAdd = libteem.pullBinsPointMaybeAdd pullBinsPointMaybeAdd.restype = c_int pullBinsPointMaybeAdd.argtypes = [POINTER(pullContext), POINTER(pullPoint), POINTER(POINTER(pullBin)), POINTER(c_int)] pullTraceNew = libteem.pullTraceNew pullTraceNew.restype = POINTER(pullTrace) pullTraceNew.argtypes = [] pullTraceNix = libteem.pullTraceNix pullTraceNix.restype = POINTER(pullTrace) pullTraceNix.argtypes = [POINTER(pullTrace)] pullTraceMultiSizeof = libteem.pullTraceMultiSizeof pullTraceMultiSizeof.restype = c_size_t pullTraceMultiSizeof.argtypes = [POINTER(pullTraceMulti)] pullTraceSet = libteem.pullTraceSet pullTraceSet.restype = c_int pullTraceSet.argtypes = [POINTER(pullContext), POINTER(pullTrace), c_int, c_double, c_double, c_double, c_uint, POINTER(c_double)] pullTraceMultiNew = libteem.pullTraceMultiNew pullTraceMultiNew.restype = POINTER(pullTraceMulti) pullTraceMultiNew.argtypes = [] pullTraceMultiNix = libteem.pullTraceMultiNix pullTraceMultiNix.restype = POINTER(pullTraceMulti) pullTraceMultiNix.argtypes = [POINTER(pullTraceMulti)] pullTraceMultiAdd = libteem.pullTraceMultiAdd pullTraceMultiAdd.restype = c_int pullTraceMultiAdd.argtypes = [POINTER(pullTraceMulti), POINTER(pullTrace), POINTER(c_int)] pullTraceMultiFilterConcaveDown = libteem.pullTraceMultiFilterConcaveDown pullTraceMultiFilterConcaveDown.restype = c_int pullTraceMultiFilterConcaveDown.argtypes = [POINTER(Nrrd), POINTER(pullTraceMulti), c_double] pullTraceMultiPlotAdd = libteem.pullTraceMultiPlotAdd pullTraceMultiPlotAdd.restype = c_int pullTraceMultiPlotAdd.argtypes = [POINTER(Nrrd), POINTER(pullTraceMulti), POINTER(Nrrd), c_uint, c_uint] pullTraceMultiWrite = libteem.pullTraceMultiWrite pullTraceMultiWrite.restype = c_int pullTraceMultiWrite.argtypes = [POINTER(FILE), POINTER(pullTraceMulti)] pullTraceMultiRead = libteem.pullTraceMultiRead pullTraceMultiRead.restype = c_int pullTraceMultiRead.argtypes = [POINTER(pullTraceMulti), POINTER(FILE)] pullEnergyPlot = libteem.pullEnergyPlot pullEnergyPlot.restype = c_int pullEnergyPlot.argtypes = [POINTER(pullContext), POINTER(Nrrd), c_double, c_double, c_double, c_uint] pullBinProcess = libteem.pullBinProcess pullBinProcess.restype = c_int pullBinProcess.argtypes = [POINTER(pullTask), c_uint] pullGammaLearn = libteem.pullGammaLearn pullGammaLearn.restype = c_int pullGammaLearn.argtypes = [POINTER(pullContext)] pullStart = libteem.pullStart pullStart.restype = c_int pullStart.argtypes = [POINTER(pullContext)] pullRun = libteem.pullRun pullRun.restype = c_int pullRun.argtypes = [POINTER(pullContext)] pullFinish = libteem.pullFinish pullFinish.restype = c_int pullFinish.argtypes = [POINTER(pullContext)] pullCCFind = libteem.pullCCFind pullCCFind.restype = c_int pullCCFind.argtypes = [POINTER(pullContext)] pullCCMeasure = libteem.pullCCMeasure pullCCMeasure.restype = c_int pullCCMeasure.argtypes = [POINTER(pullContext), POINTER(Nrrd), c_int, c_double] pullCCSort = libteem.pullCCSort pullCCSort.restype = c_int pullCCSort.argtypes = [POINTER(pullContext), c_int, c_double] class pushPoint_t(Structure): pass pushPoint_t._pack_ = 4 pushPoint_t._fields_ = [ ('ttaagg', c_uint), ('pos', c_double * 3), ('enr', c_double), ('frc', c_double * 3), ('ten', c_double * 7), ('inv', c_double * 7), ('cnt', c_double * 3), ('grav', c_double), ('gravGrad', c_double * 3), ('seedThresh', c_double), ('neigh', POINTER(POINTER(pushPoint_t))), ('neighNum', c_uint), ('neighArr', POINTER(airArray)), ] pushPoint = pushPoint_t class pushBin_t(Structure): pass pushBin_t._fields_ = [ ('pointNum', c_uint), ('point', POINTER(POINTER(pushPoint))), ('pointArr', POINTER(airArray)), ('neighbor', POINTER(POINTER(pushBin_t))), ] pushBin = pushBin_t class pushTask_t(Structure): pass class pushContext_t(Structure): pass pushTask_t._pack_ = 4 pushTask_t._fields_ = [ ('pctx', POINTER(pushContext_t)), ('gctx', POINTER(gageContext)), ('tenAns', POINTER(c_double)), ('invAns', POINTER(c_double)), ('cntAns', POINTER(c_double)), ('gravAns', POINTER(c_double)), ('gravGradAns', POINTER(c_double)), ('seedThreshAns', POINTER(c_double)), ('thread', POINTER(airThread)), ('threadIdx', c_uint), ('pointNum', c_uint), ('energySum', c_double), ('deltaFracSum', c_double), ('rng', POINTER(airRandMTState)), ('returnPtr', c_void_p), ] pushTask = pushTask_t class pushEnergy(Structure): pass pushEnergy._fields_ = [ ('name', c_char * 129), ('parmNum', c_uint), ('eval', CFUNCTYPE(None, POINTER(c_double), POINTER(c_double), c_double, POINTER(c_double))), ('support', CFUNCTYPE(c_double, POINTER(c_double))), ] class pushEnergySpec(Structure): pass pushEnergySpec._pack_ = 4 pushEnergySpec._fields_ = [ ('energy', POINTER(pushEnergy)), ('parm', c_double * 3), ] pushContext_t._pack_ = 4 pushContext_t._fields_ = [ ('pointNum', c_uint), ('nin', POINTER(Nrrd)), ('npos', POINTER(Nrrd)), ('stepInitial', c_double), ('scale', c_double), ('wall', c_double), ('cntScl', c_double), ('deltaLimit', c_double), ('deltaFracMin', c_double), ('energyStepFrac', c_double), ('deltaFracStepFrac', c_double), ('neighborTrueProb', c_double), ('probeProb', c_double), ('energyImprovMin', c_double), ('detReject', c_int), ('midPntSmp', c_int), ('verbose', c_int), ('seedRNG', c_uint), ('threadNum', c_uint), ('maxIter', c_uint), ('snap', c_uint), ('gravItem', c_int), ('gravGradItem', c_int), ('gravScl', c_double), ('gravZero', c_double), ('seedThreshItem', c_int), ('seedThreshSign', c_int), ('seedThresh', c_double), ('ensp', POINTER(pushEnergySpec)), ('binSingle', c_int), ('binIncr', c_uint), ('ksp00', POINTER(NrrdKernelSpec)), ('ksp11', POINTER(NrrdKernelSpec)), ('ksp22', POINTER(NrrdKernelSpec)), ('ttaagg', c_uint), ('nten', POINTER(Nrrd)), ('ninv', POINTER(Nrrd)), ('nmask', POINTER(Nrrd)), ('gctx', POINTER(gageContext)), ('tpvl', POINTER(gagePerVolume)), ('ipvl', POINTER(gagePerVolume)), ('finished', c_int), ('dimIn', c_uint), ('sliceAxis', c_uint), ('bin', POINTER(pushBin)), ('binsEdge', c_uint * 3), ('binNum', c_uint), ('binIdx', c_uint), ('binMutex', POINTER(airThreadMutex)), ('step', c_double), ('maxDist', c_double), ('maxEval', c_double), ('meanEval', c_double), ('maxDet', c_double), ('energySum', c_double), ('task', POINTER(POINTER(pushTask))), ('iterBarrierA', POINTER(airThreadBarrier)), ('iterBarrierB', POINTER(airThreadBarrier)), ('deltaFrac', c_double), ('timeIteration', c_double), ('timeRun', c_double), ('iter', c_uint), ('noutPos', POINTER(Nrrd)), ('noutTen', POINTER(Nrrd)), ] pushContext = pushContext_t class pushPtrPtrUnion(Union): pass pushPtrPtrUnion._fields_ = [ ('point', POINTER(POINTER(POINTER(pushPoint)))), ('v', POINTER(c_void_p)), ] pushPresent = (c_int).in_dll(libteem, 'pushPresent') pushBiffKey = (STRING).in_dll(libteem, 'pushBiffKey') pushPointNew = libteem.pushPointNew pushPointNew.restype = POINTER(pushPoint) pushPointNew.argtypes = [POINTER(pushContext)] pushPointNix = libteem.pushPointNix pushPointNix.restype = POINTER(pushPoint) pushPointNix.argtypes = [POINTER(pushPoint)] pushContextNew = libteem.pushContextNew pushContextNew.restype = POINTER(pushContext) pushContextNew.argtypes = [] pushContextNix = libteem.pushContextNix pushContextNix.restype = POINTER(pushContext) pushContextNix.argtypes = [POINTER(pushContext)] pushEnergyType = (POINTER(airEnum)).in_dll(libteem, 'pushEnergyType') pushEnergyUnknown = (POINTER(pushEnergy)).in_dll(libteem, 'pushEnergyUnknown') pushEnergySpring = (POINTER(pushEnergy)).in_dll(libteem, 'pushEnergySpring') pushEnergyGauss = (POINTER(pushEnergy)).in_dll(libteem, 'pushEnergyGauss') pushEnergyCoulomb = (POINTER(pushEnergy)).in_dll(libteem, 'pushEnergyCoulomb') pushEnergyCotan = (POINTER(pushEnergy)).in_dll(libteem, 'pushEnergyCotan') pushEnergyZero = (POINTER(pushEnergy)).in_dll(libteem, 'pushEnergyZero') pushEnergyAll = (POINTER(pushEnergy) * 6).in_dll(libteem, 'pushEnergyAll') pushEnergySpecNew = libteem.pushEnergySpecNew pushEnergySpecNew.restype = POINTER(pushEnergySpec) pushEnergySpecNew.argtypes = [] pushEnergySpecSet = libteem.pushEnergySpecSet pushEnergySpecSet.restype = None pushEnergySpecSet.argtypes = [POINTER(pushEnergySpec), POINTER(pushEnergy), POINTER(c_double)] pushEnergySpecNix = libteem.pushEnergySpecNix pushEnergySpecNix.restype = POINTER(pushEnergySpec) pushEnergySpecNix.argtypes = [POINTER(pushEnergySpec)] pushEnergySpecParse = libteem.pushEnergySpecParse pushEnergySpecParse.restype = c_int pushEnergySpecParse.argtypes = [POINTER(pushEnergySpec), STRING] pushHestEnergySpec = (POINTER(hestCB)).in_dll(libteem, 'pushHestEnergySpec') pushStart = libteem.pushStart pushStart.restype = c_int pushStart.argtypes = [POINTER(pushContext)] pushIterate = libteem.pushIterate pushIterate.restype = c_int pushIterate.argtypes = [POINTER(pushContext)] pushRun = libteem.pushRun pushRun.restype = c_int pushRun.argtypes = [POINTER(pushContext)] pushFinish = libteem.pushFinish pushFinish.restype = c_int pushFinish.argtypes = [POINTER(pushContext)] pushBinInit = libteem.pushBinInit pushBinInit.restype = None pushBinInit.argtypes = [POINTER(pushBin), c_uint] pushBinDone = libteem.pushBinDone pushBinDone.restype = None pushBinDone.argtypes = [POINTER(pushBin)] pushBinPointAdd = libteem.pushBinPointAdd pushBinPointAdd.restype = c_int pushBinPointAdd.argtypes = [POINTER(pushContext), POINTER(pushPoint)] pushBinAllNeighborSet = libteem.pushBinAllNeighborSet pushBinAllNeighborSet.restype = None pushBinAllNeighborSet.argtypes = [POINTER(pushContext)] pushRebin = libteem.pushRebin pushRebin.restype = c_int pushRebin.argtypes = [POINTER(pushContext)] pushBinProcess = libteem.pushBinProcess pushBinProcess.restype = c_int pushBinProcess.argtypes = [POINTER(pushTask), c_uint] pushOutputGet = libteem.pushOutputGet pushOutputGet.restype = c_int pushOutputGet.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), POINTER(pushContext)] class seekContext(Structure): pass seekContext._pack_ = 4 seekContext._fields_ = [ ('verbose', c_int), ('ninscl', POINTER(Nrrd)), ('gctx', POINTER(gageContext)), ('pvl', POINTER(gagePerVolume)), ('type', c_int), ('sclvItem', c_int), ('gradItem', c_int), ('normItem', c_int), ('evalItem', c_int), ('evecItem', c_int), ('stngItem', c_int), ('hessItem', c_int), ('lowerInside', c_int), ('normalsFind', c_int), ('strengthUse', c_int), ('strengthSign', c_int), ('isovalue', c_double), ('strength', c_double), ('evalDiffThresh', c_double), ('samples', c_size_t * 3), ('facesPerVoxel', c_double), ('vertsPerVoxel', c_double), ('pldArrIncr', c_uint), ('flag', POINTER(c_int)), ('nin', POINTER(Nrrd)), ('baseDim', c_uint), ('_shape', POINTER(gageShape)), ('shape', POINTER(gageShape)), ('nsclDerived', POINTER(Nrrd)), ('sclvAns', POINTER(c_double)), ('gradAns', POINTER(c_double)), ('normAns', POINTER(c_double)), ('evalAns', POINTER(c_double)), ('evecAns', POINTER(c_double)), ('stngAns', POINTER(c_double)), ('hessAns', POINTER(c_double)), ('reverse', c_int), ('txfNormal', c_double * 9), ('spanSize', c_size_t), ('nspanHist', POINTER(Nrrd)), ('range', POINTER(NrrdRange)), ('sx', c_size_t), ('sy', c_size_t), ('sz', c_size_t), ('txfIdx', c_double * 16), ('vidx', POINTER(c_int)), ('facevidx', POINTER(c_int)), ('sclv', POINTER(c_double)), ('grad', POINTER(c_double)), ('eval', POINTER(c_double)), ('evec', POINTER(c_double)), ('hess', POINTER(c_double)), ('t', POINTER(c_double)), ('edgealpha', POINTER(c_double)), ('edgenorm', POINTER(c_double)), ('edgeicoord', POINTER(c_double)), ('facecoord', POINTER(c_double)), ('facenorm', POINTER(c_double)), ('faceicoord', POINTER(c_double)), ('gradcontext', POINTER(c_double)), ('hesscontext', POINTER(c_double)), ('tcontext', POINTER(c_double)), ('stngcontext', POINTER(c_double)), ('flip', POINTER(c_byte)), ('pairs', POINTER(c_byte)), ('treated', POINTER(c_byte)), ('stng', POINTER(c_double)), ('nvidx', POINTER(Nrrd)), ('nsclv', POINTER(Nrrd)), ('ngrad', POINTER(Nrrd)), ('neval', POINTER(Nrrd)), ('nevec', POINTER(Nrrd)), ('nflip', POINTER(Nrrd)), ('nstng', POINTER(Nrrd)), ('nhess', POINTER(Nrrd)), ('nt', POINTER(Nrrd)), ('nfacevidx', POINTER(Nrrd)), ('nedgealpha', POINTER(Nrrd)), ('nedgenorm', POINTER(Nrrd)), ('nfacecoord', POINTER(Nrrd)), ('nfacenorm', POINTER(Nrrd)), ('npairs', POINTER(Nrrd)), ('nedgeicoord', POINTER(Nrrd)), ('nfaceicoord', POINTER(Nrrd)), ('ngradcontext', POINTER(Nrrd)), ('nhesscontext', POINTER(Nrrd)), ('ntcontext', POINTER(Nrrd)), ('nstngcontext', POINTER(Nrrd)), ('ntreated', POINTER(Nrrd)), ('voxNum', c_uint), ('vertNum', c_uint), ('faceNum', c_uint), ('strengthSeenMax', c_double), ('time', c_double), ] seekBiffKey = (STRING).in_dll(libteem, 'seekBiffKey') seekType = (POINTER(airEnum)).in_dll(libteem, 'seekType') seekContour3DTopoHackEdge = (c_int * 256).in_dll(libteem, 'seekContour3DTopoHackEdge') seekContour3DTopoHackTriangle = (c_int * 16 * 256).in_dll(libteem, 'seekContour3DTopoHackTriangle') seekPresent = (c_int).in_dll(libteem, 'seekPresent') seekContextNew = libteem.seekContextNew seekContextNew.restype = POINTER(seekContext) seekContextNew.argtypes = [] seekContextNix = libteem.seekContextNix seekContextNix.restype = POINTER(seekContext) seekContextNix.argtypes = [POINTER(seekContext)] seekVerboseSet = libteem.seekVerboseSet seekVerboseSet.restype = None seekVerboseSet.argtypes = [POINTER(seekContext), c_int] seekDataSet = libteem.seekDataSet seekDataSet.restype = c_int seekDataSet.argtypes = [POINTER(seekContext), POINTER(Nrrd), POINTER(gageContext), c_uint] seekNormalsFindSet = libteem.seekNormalsFindSet seekNormalsFindSet.restype = c_int seekNormalsFindSet.argtypes = [POINTER(seekContext), c_int] seekStrengthUseSet = libteem.seekStrengthUseSet seekStrengthUseSet.restype = c_int seekStrengthUseSet.argtypes = [POINTER(seekContext), c_int] seekStrengthSet = libteem.seekStrengthSet seekStrengthSet.restype = c_int seekStrengthSet.argtypes = [POINTER(seekContext), c_int, c_double] seekSamplesSet = libteem.seekSamplesSet seekSamplesSet.restype = c_int seekSamplesSet.argtypes = [POINTER(seekContext), POINTER(c_size_t)] seekTypeSet = libteem.seekTypeSet seekTypeSet.restype = c_int seekTypeSet.argtypes = [POINTER(seekContext), c_int] seekLowerInsideSet = libteem.seekLowerInsideSet seekLowerInsideSet.restype = c_int seekLowerInsideSet.argtypes = [POINTER(seekContext), c_int] seekItemScalarSet = libteem.seekItemScalarSet seekItemScalarSet.restype = c_int seekItemScalarSet.argtypes = [POINTER(seekContext), c_int] seekItemStrengthSet = libteem.seekItemStrengthSet seekItemStrengthSet.restype = c_int seekItemStrengthSet.argtypes = [POINTER(seekContext), c_int] seekItemNormalSet = libteem.seekItemNormalSet seekItemNormalSet.restype = c_int seekItemNormalSet.argtypes = [POINTER(seekContext), c_int] seekItemGradientSet = libteem.seekItemGradientSet seekItemGradientSet.restype = c_int seekItemGradientSet.argtypes = [POINTER(seekContext), c_int] seekItemEigensystemSet = libteem.seekItemEigensystemSet seekItemEigensystemSet.restype = c_int seekItemEigensystemSet.argtypes = [POINTER(seekContext), c_int, c_int] seekItemHessSet = libteem.seekItemHessSet seekItemHessSet.restype = c_int seekItemHessSet.argtypes = [POINTER(seekContext), c_int] seekIsovalueSet = libteem.seekIsovalueSet seekIsovalueSet.restype = c_int seekIsovalueSet.argtypes = [POINTER(seekContext), c_double] seekEvalDiffThreshSet = libteem.seekEvalDiffThreshSet seekEvalDiffThreshSet.restype = c_int seekEvalDiffThreshSet.argtypes = [POINTER(seekContext), c_double] seekUpdate = libteem.seekUpdate seekUpdate.restype = c_int seekUpdate.argtypes = [POINTER(seekContext)] seekExtract = libteem.seekExtract seekExtract.restype = c_int seekExtract.argtypes = [POINTER(seekContext), POINTER(limnPolyData)] seekVertexStrength = libteem.seekVertexStrength seekVertexStrength.restype = c_int seekVertexStrength.argtypes = [POINTER(Nrrd), POINTER(seekContext), POINTER(limnPolyData)] seekDescendToDeg = libteem.seekDescendToDeg seekDescendToDeg.restype = c_int seekDescendToDeg.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_int, c_double, c_char] seekDescendToDegCell = libteem.seekDescendToDegCell seekDescendToDegCell.restype = c_int seekDescendToDegCell.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_int, c_double, c_char] seekDescendToRidge = libteem.seekDescendToRidge seekDescendToRidge.restype = c_int seekDescendToRidge.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_int, c_double, c_char, c_double] class tenGlyphParm(Structure): pass tenGlyphParm._fields_ = [ ('verbose', c_int), ('nmask', POINTER(Nrrd)), ('anisoType', c_int), ('onlyPositive', c_int), ('confThresh', c_float), ('anisoThresh', c_float), ('maskThresh', c_float), ('glyphType', c_int), ('facetRes', c_int), ('glyphScale', c_float), ('sqdSharp', c_float), ('edgeWidth', c_float * 5), ('colEvec', c_int), ('colAnisoType', c_int), ('colMaxSat', c_float), ('colIsoGray', c_float), ('colGamma', c_float), ('colAnisoModulate', c_float), ('ADSP', c_float * 4), ('sliceAxis', c_uint), ('slicePos', c_size_t), ('doSlice', c_int), ('sliceAnisoType', c_int), ('sliceOffset', c_float), ('sliceBias', c_float), ('sliceGamma', c_float), ] class tenEvecRGBParm(Structure): pass tenEvecRGBParm._pack_ = 4 tenEvecRGBParm._fields_ = [ ('which', c_uint), ('aniso', c_int), ('confThresh', c_double), ('anisoGamma', c_double), ('gamma', c_double), ('bgGray', c_double), ('isoGray', c_double), ('maxSat', c_double), ('typeOut', c_int), ('genAlpha', c_int), ] class tenFiberContext(Structure): pass tenFiberContext._pack_ = 4 tenFiberContext._fields_ = [ ('nin', POINTER(Nrrd)), ('ksp', POINTER(NrrdKernelSpec)), ('useDwi', c_int), ('fiberType', c_int), ('fiberProbeItem', c_int), ('intg', c_int), ('anisoStopType', c_int), ('anisoSpeedType', c_int), ('stop', c_int), ('useIndexSpace', c_int), ('verbose', c_int), ('anisoThresh', c_double), ('anisoSpeedFunc', c_double * 3), ('maxNumSteps', c_uint), ('minNumSteps', c_uint), ('stepSize', c_double), ('maxHalfLen', c_double), ('minWholeLen', c_double), ('confThresh', c_double), ('minRadius', c_double), ('minFraction', c_double), ('wPunct', c_double), ('ten2Which', c_uint), ('query', gageQuery), ('halfIdx', c_int), ('mframeUse', c_int), ('mframe', c_double * 9), ('mframeT', c_double * 9), ('wPos', c_double * 3), ('wDir', c_double * 3), ('lastDir', c_double * 3), ('seedEvec', c_double * 3), ('lastDirSet', c_int), ('lastTenSet', c_int), ('ten2Use', c_uint), ('gtx', POINTER(gageContext)), ('pvl', POINTER(gagePerVolume)), ('gageTen', POINTER(c_double)), ('gageEval', POINTER(c_double)), ('gageEvec', POINTER(c_double)), ('gageAnisoStop', POINTER(c_double)), ('gageAnisoSpeed', POINTER(c_double)), ('gageTen2', POINTER(c_double)), ('ten2AnisoStop', c_double), ('fiberTen', c_double * 7), ('fiberEval', c_double * 3), ('fiberEvec', c_double * 9), ('fiberAnisoStop', c_double), ('fiberAnisoSpeed', c_double), ('radius', c_double), ('halfLen', c_double * 2), ('numSteps', c_uint * 2), ('whyStop', c_int * 2), ('whyNowhere', c_int), ] class tenFiberSingle(Structure): pass tenFiberSingle._pack_ = 4 tenFiberSingle._fields_ = [ ('seedPos', c_double * 3), ('dirIdx', c_uint), ('dirNum', c_uint), ('nvert', POINTER(Nrrd)), ('halfLen', c_double * 2), ('seedIdx', c_uint), ('stepNum', c_uint * 2), ('whyStop', c_int * 2), ('whyNowhere', c_int), ('nval', POINTER(Nrrd)), ('measr', c_double * 30), ] class tenFiberMulti(Structure): pass tenFiberMulti._fields_ = [ ('fiber', POINTER(tenFiberSingle)), ('fiberNum', c_uint), ('fiberArr', POINTER(airArray)), ] class tenEMBimodalParm(Structure): pass tenEMBimodalParm._pack_ = 4 tenEMBimodalParm._fields_ = [ ('minProb', c_double), ('minProb2', c_double), ('minDelta', c_double), ('minFraction', c_double), ('minConfidence', c_double), ('twoStage', c_double), ('verbose', c_double), ('maxIteration', c_uint), ('histo', POINTER(c_double)), ('pp1', POINTER(c_double)), ('pp2', POINTER(c_double)), ('vmin', c_double), ('vmax', c_double), ('delta', c_double), ('N', c_int), ('stage', c_int), ('iteration', c_uint), ('mean1', c_double), ('stdv1', c_double), ('mean2', c_double), ('stdv2', c_double), ('fraction1', c_double), ('confidence', c_double), ('threshold', c_double), ] class tenGradientParm(Structure): pass tenGradientParm._pack_ = 4 tenGradientParm._fields_ = [ ('initStep', c_double), ('jitter', c_double), ('minVelocity', c_double), ('minPotentialChange', c_double), ('minMean', c_double), ('minMeanImprovement', c_double), ('single', c_int), ('insertZeroVec', c_int), ('verbose', c_int), ('snap', c_uint), ('report', c_uint), ('expo', c_uint), ('seed', c_uint), ('maxEdgeShrink', c_uint), ('minIteration', c_uint), ('maxIteration', c_uint), ('expo_d', c_double), ('step', c_double), ('nudge', c_double), ('itersUsed', c_uint), ('potential', c_double), ('potentialNorm', c_double), ('angle', c_double), ('edge', c_double), ] class tenEstimateContext(Structure): pass tenEstimateContext._pack_ = 4 tenEstimateContext._fields_ = [ ('bValue', c_double), ('valueMin', c_double), ('sigma', c_double), ('dwiConfThresh', c_double), ('dwiConfSoft', c_double), ('_ngrad', POINTER(Nrrd)), ('_nbmat', POINTER(Nrrd)), ('skipList', POINTER(c_uint)), ('skipListArr', POINTER(airArray)), ('all_f', POINTER(c_float)), ('all_d', POINTER(c_double)), ('simulate', c_int), ('estimate1Method', c_int), ('estimateB0', c_int), ('recordTime', c_int), ('recordErrorDwi', c_int), ('recordErrorLogDwi', c_int), ('recordLikelihoodDwi', c_int), ('verbose', c_int), ('negEvalShift', c_int), ('progress', c_int), ('WLSIterNum', c_uint), ('flag', c_int * 128), ('allNum', c_uint), ('dwiNum', c_uint), ('nbmat', POINTER(Nrrd)), ('nwght', POINTER(Nrrd)), ('nemat', POINTER(Nrrd)), ('knownB0', c_double), ('all', POINTER(c_double)), ('bnorm', POINTER(c_double)), ('allTmp', POINTER(c_double)), ('dwiTmp', POINTER(c_double)), ('dwi', POINTER(c_double)), ('skipLut', POINTER(c_ubyte)), ('estimatedB0', c_double), ('ten', c_double * 7), ('conf', c_double), ('mdwi', c_double), ('time', c_double), ('errorDwi', c_double), ('errorLogDwi', c_double), ('likelihoodDwi', c_double), ] class tenDwiGageKindData(Structure): pass tenDwiGageKindData._pack_ = 4 tenDwiGageKindData._fields_ = [ ('ngrad', POINTER(Nrrd)), ('nbmat', POINTER(Nrrd)), ('thresh', c_double), ('soft', c_double), ('bval', c_double), ('valueMin', c_double), ('est1Method', c_int), ('est2Method', c_int), ('randSeed', c_uint), ] class tenDwiGagePvlData(Structure): pass tenDwiGagePvlData._pack_ = 4 tenDwiGagePvlData._fields_ = [ ('tec1', POINTER(tenEstimateContext)), ('tec2', POINTER(tenEstimateContext)), ('vbuf', POINTER(c_double)), ('wght', POINTER(c_uint)), ('qvals', POINTER(c_double)), ('qpoints', POINTER(c_double)), ('dists', POINTER(c_double)), ('weights', POINTER(c_double)), ('nten1EigenGrads', POINTER(Nrrd)), ('randState', POINTER(airRandMTState)), ('randSeed', c_uint), ('ten1', c_double * 7), ('ten1Evec', c_double * 9), ('ten1Eval', c_double * 3), ('levmarUseFastExp', c_int), ('levmarMaxIter', c_uint), ('levmarTau', c_double), ('levmarEps1', c_double), ('levmarEps2', c_double), ('levmarEps3', c_double), ('levmarDelta', c_double), ('levmarMinCp', c_double), ('levmarInfo', c_double * 9), ] class tenInterpParm(Structure): pass tenInterpParm._pack_ = 4 tenInterpParm._fields_ = [ ('verbose', c_int), ('convStep', c_double), ('minNorm', c_double), ('convEps', c_double), ('wghtSumEps', c_double), ('enableRecurse', c_int), ('maxIter', c_uint), ('numSteps', c_uint), ('lengthFancy', c_int), ('allocLen', c_uint), ('eval', POINTER(c_double)), ('evec', POINTER(c_double)), ('rtIn', POINTER(c_double)), ('rtLog', POINTER(c_double)), ('qIn', POINTER(c_double)), ('qBuff', POINTER(c_double)), ('qInter', POINTER(c_double)), ('numIter', c_uint), ('convFinal', c_double), ('lengthShape', c_double), ('lengthOrient', c_double), ] class tenExperSpec(Structure): pass tenExperSpec._fields_ = [ ('set', c_int), ('imgNum', c_uint), ('bval', POINTER(c_double)), ('grad', POINTER(c_double)), ] class tenModelParmDesc(Structure): pass tenModelParmDesc._pack_ = 4 tenModelParmDesc._fields_ = [ ('name', c_char * 129), ('min', c_double), ('max', c_double), ('cyclic', c_int), ('vec3', c_int), ('vecIdx', c_uint), ] class tenModel_t(Structure): pass tenModel_t._fields_ = [ ('name', c_char * 129), ('parmNum', c_uint), ('parmDesc', POINTER(tenModelParmDesc)), ('simulate', CFUNCTYPE(None, POINTER(c_double), POINTER(c_double), POINTER(tenExperSpec))), ('sprint', CFUNCTYPE(STRING, STRING, POINTER(c_double))), ('alloc', CFUNCTYPE(POINTER(c_double))), ('rand', CFUNCTYPE(None, POINTER(c_double), POINTER(airRandMTState), c_int)), ('step', CFUNCTYPE(None, POINTER(c_double), c_double, POINTER(c_double), POINTER(c_double))), ('dist', CFUNCTYPE(c_double, POINTER(c_double), POINTER(c_double))), ('copy', CFUNCTYPE(None, POINTER(c_double), POINTER(c_double))), ('convert', CFUNCTYPE(c_int, POINTER(c_double), POINTER(c_double), POINTER(tenModel_t))), ('sqe', CFUNCTYPE(c_double, POINTER(c_double), POINTER(tenExperSpec), POINTER(c_double), POINTER(c_double), c_int)), ('sqeGrad', CFUNCTYPE(None, POINTER(c_double), POINTER(c_double), POINTER(tenExperSpec), POINTER(c_double), POINTER(c_double), c_int)), ('sqeFit', CFUNCTYPE(c_double, POINTER(c_double), POINTER(c_double), POINTER(c_uint), POINTER(tenExperSpec), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_int, c_uint, c_uint, c_double, c_int)), ('nll', CFUNCTYPE(c_double, POINTER(c_double), POINTER(tenExperSpec), POINTER(c_double), POINTER(c_double), c_int, c_double, c_int)), ('nllGrad', CFUNCTYPE(None, POINTER(c_double), POINTER(c_double), POINTER(tenExperSpec), POINTER(c_double), POINTER(c_double), c_int, c_double)), ('nllFit', CFUNCTYPE(c_double, POINTER(c_double), POINTER(tenExperSpec), POINTER(c_double), POINTER(c_double), c_int, c_double, c_int)), ] tenModel = tenModel_t tenPresent = (c_int).in_dll(libteem, 'tenPresent') tenBiffKey = (STRING).in_dll(libteem, 'tenBiffKey') tenDefFiberKernel = (c_char * 0).in_dll(libteem, 'tenDefFiberKernel') tenDefFiberStepSize = (c_double).in_dll(libteem, 'tenDefFiberStepSize') tenDefFiberUseIndexSpace = (c_int).in_dll(libteem, 'tenDefFiberUseIndexSpace') tenDefFiberMaxNumSteps = (c_int).in_dll(libteem, 'tenDefFiberMaxNumSteps') tenDefFiberMaxHalfLen = (c_double).in_dll(libteem, 'tenDefFiberMaxHalfLen') tenDefFiberAnisoStopType = (c_int).in_dll(libteem, 'tenDefFiberAnisoStopType') tenDefFiberAnisoThresh = (c_double).in_dll(libteem, 'tenDefFiberAnisoThresh') tenDefFiberIntg = (c_int).in_dll(libteem, 'tenDefFiberIntg') tenDefFiberWPunct = (c_double).in_dll(libteem, 'tenDefFiberWPunct') tenTripleConvertSingle_d = libteem.tenTripleConvertSingle_d tenTripleConvertSingle_d.restype = None tenTripleConvertSingle_d.argtypes = [POINTER(c_double), c_int, POINTER(c_double), c_int] tenTripleConvertSingle_f = libteem.tenTripleConvertSingle_f tenTripleConvertSingle_f.restype = None tenTripleConvertSingle_f.argtypes = [POINTER(c_float), c_int, POINTER(c_float), c_int] tenTripleCalcSingle_d = libteem.tenTripleCalcSingle_d tenTripleCalcSingle_d.restype = None tenTripleCalcSingle_d.argtypes = [POINTER(c_double), c_int, POINTER(c_double)] tenTripleCalcSingle_f = libteem.tenTripleCalcSingle_f tenTripleCalcSingle_f.restype = None tenTripleCalcSingle_f.argtypes = [POINTER(c_float), c_int, POINTER(c_float)] tenTripleCalc = libteem.tenTripleCalc tenTripleCalc.restype = c_int tenTripleCalc.argtypes = [POINTER(Nrrd), c_int, POINTER(Nrrd)] tenTripleConvert = libteem.tenTripleConvert tenTripleConvert.restype = c_int tenTripleConvert.argtypes = [POINTER(Nrrd), c_int, POINTER(Nrrd), c_int] tenGradientParmNew = libteem.tenGradientParmNew tenGradientParmNew.restype = POINTER(tenGradientParm) tenGradientParmNew.argtypes = [] tenGradientParmNix = libteem.tenGradientParmNix tenGradientParmNix.restype = POINTER(tenGradientParm) tenGradientParmNix.argtypes = [POINTER(tenGradientParm)] tenGradientCheck = libteem.tenGradientCheck tenGradientCheck.restype = c_int tenGradientCheck.argtypes = [POINTER(Nrrd), c_int, c_uint] tenGradientRandom = libteem.tenGradientRandom tenGradientRandom.restype = c_int tenGradientRandom.argtypes = [POINTER(Nrrd), c_uint, c_uint] tenGradientIdealEdge = libteem.tenGradientIdealEdge tenGradientIdealEdge.restype = c_double tenGradientIdealEdge.argtypes = [c_uint, c_int] tenGradientJitter = libteem.tenGradientJitter tenGradientJitter.restype = c_int tenGradientJitter.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_double] tenGradientBalance = libteem.tenGradientBalance tenGradientBalance.restype = c_int tenGradientBalance.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(tenGradientParm)] tenGradientMeasure = libteem.tenGradientMeasure tenGradientMeasure.restype = None tenGradientMeasure.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(Nrrd), POINTER(tenGradientParm), c_int] tenGradientDistribute = libteem.tenGradientDistribute tenGradientDistribute.restype = c_int tenGradientDistribute.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(tenGradientParm)] tenGradientGenerate = libteem.tenGradientGenerate tenGradientGenerate.restype = c_int tenGradientGenerate.argtypes = [POINTER(Nrrd), c_uint, POINTER(tenGradientParm)] tenAniso = (POINTER(airEnum)).in_dll(libteem, 'tenAniso') tenInterpType = (POINTER(airEnum)).in_dll(libteem, 'tenInterpType') tenGage = (POINTER(airEnum)).in_dll(libteem, 'tenGage') tenFiberType = (POINTER(airEnum)).in_dll(libteem, 'tenFiberType') tenDwiFiberType = (POINTER(airEnum)).in_dll(libteem, 'tenDwiFiberType') tenFiberStop = (POINTER(airEnum)).in_dll(libteem, 'tenFiberStop') tenFiberIntg = (POINTER(airEnum)).in_dll(libteem, 'tenFiberIntg') tenGlyphType = (POINTER(airEnum)).in_dll(libteem, 'tenGlyphType') tenEstimate1Method = (POINTER(airEnum)).in_dll(libteem, 'tenEstimate1Method') tenEstimate2Method = (POINTER(airEnum)).in_dll(libteem, 'tenEstimate2Method') tenTripleType = (POINTER(airEnum)).in_dll(libteem, 'tenTripleType') tenInterpParmNew = libteem.tenInterpParmNew tenInterpParmNew.restype = POINTER(tenInterpParm) tenInterpParmNew.argtypes = [] tenInterpParmCopy = libteem.tenInterpParmCopy tenInterpParmCopy.restype = POINTER(tenInterpParm) tenInterpParmCopy.argtypes = [POINTER(tenInterpParm)] tenInterpParmBufferAlloc = libteem.tenInterpParmBufferAlloc tenInterpParmBufferAlloc.restype = c_int tenInterpParmBufferAlloc.argtypes = [POINTER(tenInterpParm), c_uint] tenInterpParmNix = libteem.tenInterpParmNix tenInterpParmNix.restype = POINTER(tenInterpParm) tenInterpParmNix.argtypes = [POINTER(tenInterpParm)] tenInterpTwo_d = libteem.tenInterpTwo_d tenInterpTwo_d.restype = None tenInterpTwo_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), c_int, c_double, POINTER(tenInterpParm)] tenInterpN_d = libteem.tenInterpN_d tenInterpN_d.restype = c_int tenInterpN_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), c_uint, c_int, POINTER(tenInterpParm)] tenInterpPathLength = libteem.tenInterpPathLength tenInterpPathLength.restype = c_double tenInterpPathLength.argtypes = [POINTER(Nrrd), c_int, c_int, c_int] tenInterpTwoDiscrete_d = libteem.tenInterpTwoDiscrete_d tenInterpTwoDiscrete_d.restype = c_int tenInterpTwoDiscrete_d.argtypes = [POINTER(Nrrd), POINTER(c_double), POINTER(c_double), c_int, c_uint, POINTER(tenInterpParm)] tenInterpDistanceTwo_d = libteem.tenInterpDistanceTwo_d tenInterpDistanceTwo_d.restype = c_double tenInterpDistanceTwo_d.argtypes = [POINTER(c_double), POINTER(c_double), c_int, POINTER(tenInterpParm)] tenInterpMulti3D = libteem.tenInterpMulti3D tenInterpMulti3D.restype = c_int tenInterpMulti3D.argtypes = [POINTER(Nrrd), POINTER(POINTER(Nrrd)), POINTER(c_double), c_uint, c_int, POINTER(tenInterpParm)] tenGlyphParmNew = libteem.tenGlyphParmNew tenGlyphParmNew.restype = POINTER(tenGlyphParm) tenGlyphParmNew.argtypes = [] tenGlyphParmNix = libteem.tenGlyphParmNix tenGlyphParmNix.restype = POINTER(tenGlyphParm) tenGlyphParmNix.argtypes = [POINTER(tenGlyphParm)] tenGlyphParmCheck = libteem.tenGlyphParmCheck tenGlyphParmCheck.restype = c_int tenGlyphParmCheck.argtypes = [POINTER(tenGlyphParm), POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd)] tenGlyphGen = libteem.tenGlyphGen tenGlyphGen.restype = c_int tenGlyphGen.argtypes = [POINTER(limnObject), POINTER(echoScene), POINTER(tenGlyphParm), POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd)] tenGlyphBqdZoneEval = libteem.tenGlyphBqdZoneEval tenGlyphBqdZoneEval.restype = c_uint tenGlyphBqdZoneEval.argtypes = [POINTER(c_double)] tenGlyphBqdUvEval = libteem.tenGlyphBqdUvEval tenGlyphBqdUvEval.restype = None tenGlyphBqdUvEval.argtypes = [POINTER(c_double), POINTER(c_double)] tenGlyphBqdEvalUv = libteem.tenGlyphBqdEvalUv tenGlyphBqdEvalUv.restype = None tenGlyphBqdEvalUv.argtypes = [POINTER(c_double), POINTER(c_double)] tenGlyphBqdZoneUv = libteem.tenGlyphBqdZoneUv tenGlyphBqdZoneUv.restype = c_uint tenGlyphBqdZoneUv.argtypes = [POINTER(c_double)] tenGlyphBqdAbcUv = libteem.tenGlyphBqdAbcUv tenGlyphBqdAbcUv.restype = None tenGlyphBqdAbcUv.argtypes = [POINTER(c_double), POINTER(c_double), c_double] tenVerbose = (c_int).in_dll(libteem, 'tenVerbose') tenRotateSingle_f = libteem.tenRotateSingle_f tenRotateSingle_f.restype = None tenRotateSingle_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float)] tenTensorCheck = libteem.tenTensorCheck tenTensorCheck.restype = c_int tenTensorCheck.argtypes = [POINTER(Nrrd), c_int, c_int, c_int] tenMeasurementFrameReduce = libteem.tenMeasurementFrameReduce tenMeasurementFrameReduce.restype = c_int tenMeasurementFrameReduce.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] tenExpand2D = libteem.tenExpand2D tenExpand2D.restype = c_int tenExpand2D.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_double, c_double] tenExpand = libteem.tenExpand tenExpand.restype = c_int tenExpand.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_double, c_double] tenShrink = libteem.tenShrink tenShrink.restype = c_int tenShrink.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd)] tenEigensolve_f = libteem.tenEigensolve_f tenEigensolve_f.restype = c_int tenEigensolve_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float)] tenEigensolve_d = libteem.tenEigensolve_d tenEigensolve_d.restype = c_int tenEigensolve_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] tenMakeSingle_f = libteem.tenMakeSingle_f tenMakeSingle_f.restype = None tenMakeSingle_f.argtypes = [POINTER(c_float), c_float, POINTER(c_float), POINTER(c_float)] tenMakeSingle_d = libteem.tenMakeSingle_d tenMakeSingle_d.restype = None tenMakeSingle_d.argtypes = [POINTER(c_double), c_double, POINTER(c_double), POINTER(c_double)] tenMake = libteem.tenMake tenMake.restype = c_int tenMake.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd)] tenSlice = libteem.tenSlice tenSlice.restype = c_int tenSlice.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_uint, c_size_t, c_uint] tenInvariantGradientsK_d = libteem.tenInvariantGradientsK_d tenInvariantGradientsK_d.restype = None tenInvariantGradientsK_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_double] tenInvariantGradientsR_d = libteem.tenInvariantGradientsR_d tenInvariantGradientsR_d.restype = None tenInvariantGradientsR_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_double] tenRotationTangents_d = libteem.tenRotationTangents_d tenRotationTangents_d.restype = None tenRotationTangents_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double)] tenLogSingle_d = libteem.tenLogSingle_d tenLogSingle_d.restype = None tenLogSingle_d.argtypes = [POINTER(c_double), POINTER(c_double)] tenLogSingle_f = libteem.tenLogSingle_f tenLogSingle_f.restype = None tenLogSingle_f.argtypes = [POINTER(c_float), POINTER(c_float)] tenExpSingle_d = libteem.tenExpSingle_d tenExpSingle_d.restype = None tenExpSingle_d.argtypes = [POINTER(c_double), POINTER(c_double)] tenExpSingle_f = libteem.tenExpSingle_f tenExpSingle_f.restype = None tenExpSingle_f.argtypes = [POINTER(c_float), POINTER(c_float)] tenSqrtSingle_d = libteem.tenSqrtSingle_d tenSqrtSingle_d.restype = None tenSqrtSingle_d.argtypes = [POINTER(c_double), POINTER(c_double)] tenSqrtSingle_f = libteem.tenSqrtSingle_f tenSqrtSingle_f.restype = None tenSqrtSingle_f.argtypes = [POINTER(c_float), POINTER(c_float)] tenPowSingle_d = libteem.tenPowSingle_d tenPowSingle_d.restype = None tenPowSingle_d.argtypes = [POINTER(c_double), POINTER(c_double), c_double] tenPowSingle_f = libteem.tenPowSingle_f tenPowSingle_f.restype = None tenPowSingle_f.argtypes = [POINTER(c_float), POINTER(c_float), c_float] tenInv_f = libteem.tenInv_f tenInv_f.restype = None tenInv_f.argtypes = [POINTER(c_float), POINTER(c_float)] tenInv_d = libteem.tenInv_d tenInv_d.restype = None tenInv_d.argtypes = [POINTER(c_double), POINTER(c_double)] tenDoubleContract_d = libteem.tenDoubleContract_d tenDoubleContract_d.restype = c_double tenDoubleContract_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double)] tenDWMRIModalityKey = (STRING).in_dll(libteem, 'tenDWMRIModalityKey') tenDWMRIModalityVal = (STRING).in_dll(libteem, 'tenDWMRIModalityVal') tenDWMRINAVal = (STRING).in_dll(libteem, 'tenDWMRINAVal') tenDWMRIBValueKey = (STRING).in_dll(libteem, 'tenDWMRIBValueKey') tenDWMRIGradKeyFmt = (STRING).in_dll(libteem, 'tenDWMRIGradKeyFmt') tenDWMRIBmatKeyFmt = (STRING).in_dll(libteem, 'tenDWMRIBmatKeyFmt') tenDWMRINexKeyFmt = (STRING).in_dll(libteem, 'tenDWMRINexKeyFmt') tenDWMRISkipKeyFmt = (STRING).in_dll(libteem, 'tenDWMRISkipKeyFmt') tenDWMRIKeyValueParse = libteem.tenDWMRIKeyValueParse tenDWMRIKeyValueParse.restype = c_int tenDWMRIKeyValueParse.argtypes = [POINTER(POINTER(Nrrd)), POINTER(POINTER(Nrrd)), POINTER(c_double), POINTER(POINTER(c_uint)), POINTER(c_uint), POINTER(Nrrd)] tenBMatrixCalc = libteem.tenBMatrixCalc tenBMatrixCalc.restype = c_int tenBMatrixCalc.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] tenEMatrixCalc = libteem.tenEMatrixCalc tenEMatrixCalc.restype = c_int tenEMatrixCalc.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int] tenEstimateLinearSingle_f = libteem.tenEstimateLinearSingle_f tenEstimateLinearSingle_f.restype = None tenEstimateLinearSingle_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_double), POINTER(c_double), c_uint, c_int, c_float, c_float, c_float] tenEstimateLinearSingle_d = libteem.tenEstimateLinearSingle_d tenEstimateLinearSingle_d.restype = None tenEstimateLinearSingle_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_uint, c_int, c_double, c_double, c_double] tenEstimateLinear3D = libteem.tenEstimateLinear3D tenEstimateLinear3D.restype = c_int tenEstimateLinear3D.argtypes = [POINTER(Nrrd), POINTER(POINTER(Nrrd)), POINTER(POINTER(Nrrd)), POINTER(POINTER(Nrrd)), c_uint, POINTER(Nrrd), c_int, c_double, c_double, c_double] tenEstimateLinear4D = libteem.tenEstimateLinear4D tenEstimateLinear4D.restype = c_int tenEstimateLinear4D.argtypes = [POINTER(Nrrd), POINTER(POINTER(Nrrd)), POINTER(POINTER(Nrrd)), POINTER(Nrrd), POINTER(Nrrd), c_int, c_double, c_double, c_double] tenSimulateSingle_f = libteem.tenSimulateSingle_f tenSimulateSingle_f.restype = None tenSimulateSingle_f.argtypes = [POINTER(c_float), c_float, POINTER(c_float), POINTER(c_double), c_uint, c_float] tenSimulate = libteem.tenSimulate tenSimulate.restype = c_int tenSimulate.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), c_double] tenEstimateContextNew = libteem.tenEstimateContextNew tenEstimateContextNew.restype = POINTER(tenEstimateContext) tenEstimateContextNew.argtypes = [] tenEstimateVerboseSet = libteem.tenEstimateVerboseSet tenEstimateVerboseSet.restype = None tenEstimateVerboseSet.argtypes = [POINTER(tenEstimateContext), c_int] tenEstimateNegEvalShiftSet = libteem.tenEstimateNegEvalShiftSet tenEstimateNegEvalShiftSet.restype = None tenEstimateNegEvalShiftSet.argtypes = [POINTER(tenEstimateContext), c_int] tenEstimateMethodSet = libteem.tenEstimateMethodSet tenEstimateMethodSet.restype = c_int tenEstimateMethodSet.argtypes = [POINTER(tenEstimateContext), c_int] tenEstimateSigmaSet = libteem.tenEstimateSigmaSet tenEstimateSigmaSet.restype = c_int tenEstimateSigmaSet.argtypes = [POINTER(tenEstimateContext), c_double] tenEstimateValueMinSet = libteem.tenEstimateValueMinSet tenEstimateValueMinSet.restype = c_int tenEstimateValueMinSet.argtypes = [POINTER(tenEstimateContext), c_double] tenEstimateGradientsSet = libteem.tenEstimateGradientsSet tenEstimateGradientsSet.restype = c_int tenEstimateGradientsSet.argtypes = [POINTER(tenEstimateContext), POINTER(Nrrd), c_double, c_int] tenEstimateBMatricesSet = libteem.tenEstimateBMatricesSet tenEstimateBMatricesSet.restype = c_int tenEstimateBMatricesSet.argtypes = [POINTER(tenEstimateContext), POINTER(Nrrd), c_double, c_int] tenEstimateSkipSet = libteem.tenEstimateSkipSet tenEstimateSkipSet.restype = c_int tenEstimateSkipSet.argtypes = [POINTER(tenEstimateContext), c_uint, c_int] tenEstimateSkipReset = libteem.tenEstimateSkipReset tenEstimateSkipReset.restype = c_int tenEstimateSkipReset.argtypes = [POINTER(tenEstimateContext)] tenEstimateThresholdSet = libteem.tenEstimateThresholdSet tenEstimateThresholdSet.restype = c_int tenEstimateThresholdSet.argtypes = [POINTER(tenEstimateContext), c_double, c_double] tenEstimateUpdate = libteem.tenEstimateUpdate tenEstimateUpdate.restype = c_int tenEstimateUpdate.argtypes = [POINTER(tenEstimateContext)] tenEstimate1TensorSimulateSingle_f = libteem.tenEstimate1TensorSimulateSingle_f tenEstimate1TensorSimulateSingle_f.restype = c_int tenEstimate1TensorSimulateSingle_f.argtypes = [POINTER(tenEstimateContext), POINTER(c_float), c_float, c_float, c_float, POINTER(c_float)] tenEstimate1TensorSimulateSingle_d = libteem.tenEstimate1TensorSimulateSingle_d tenEstimate1TensorSimulateSingle_d.restype = c_int tenEstimate1TensorSimulateSingle_d.argtypes = [POINTER(tenEstimateContext), POINTER(c_double), c_double, c_double, c_double, POINTER(c_double)] tenEstimate1TensorSimulateVolume = libteem.tenEstimate1TensorSimulateVolume tenEstimate1TensorSimulateVolume.restype = c_int tenEstimate1TensorSimulateVolume.argtypes = [POINTER(tenEstimateContext), POINTER(Nrrd), c_double, c_double, POINTER(Nrrd), POINTER(Nrrd), c_int, c_int] tenEstimate1TensorSingle_f = libteem.tenEstimate1TensorSingle_f tenEstimate1TensorSingle_f.restype = c_int tenEstimate1TensorSingle_f.argtypes = [POINTER(tenEstimateContext), POINTER(c_float), POINTER(c_float)] tenEstimate1TensorSingle_d = libteem.tenEstimate1TensorSingle_d tenEstimate1TensorSingle_d.restype = c_int tenEstimate1TensorSingle_d.argtypes = [POINTER(tenEstimateContext), POINTER(c_double), POINTER(c_double)] tenEstimate1TensorVolume4D = libteem.tenEstimate1TensorVolume4D tenEstimate1TensorVolume4D.restype = c_int tenEstimate1TensorVolume4D.argtypes = [POINTER(tenEstimateContext), POINTER(Nrrd), POINTER(POINTER(Nrrd)), POINTER(POINTER(Nrrd)), POINTER(Nrrd), c_int] tenEstimateContextNix = libteem.tenEstimateContextNix tenEstimateContextNix.restype = POINTER(tenEstimateContext) tenEstimateContextNix.argtypes = [POINTER(tenEstimateContext)] tenAnisoEval_f = libteem.tenAnisoEval_f tenAnisoEval_f.restype = c_float tenAnisoEval_f.argtypes = [POINTER(c_float), c_int] tenAnisoEval_d = libteem.tenAnisoEval_d tenAnisoEval_d.restype = c_double tenAnisoEval_d.argtypes = [POINTER(c_double), c_int] tenAnisoTen_f = libteem.tenAnisoTen_f tenAnisoTen_f.restype = c_float tenAnisoTen_f.argtypes = [POINTER(c_float), c_int] tenAnisoTen_d = libteem.tenAnisoTen_d tenAnisoTen_d.restype = c_double tenAnisoTen_d.argtypes = [POINTER(c_double), c_int] tenAnisoPlot = libteem.tenAnisoPlot tenAnisoPlot.restype = c_int tenAnisoPlot.argtypes = [POINTER(Nrrd), c_int, c_uint, c_int, c_int, c_int] tenAnisoVolume = libteem.tenAnisoVolume tenAnisoVolume.restype = c_int tenAnisoVolume.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int, c_double] tenAnisoHistogram = libteem.tenAnisoHistogram tenAnisoHistogram.restype = c_int tenAnisoHistogram.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), c_int, c_int, c_uint] tenEvecRGBParmNew = libteem.tenEvecRGBParmNew tenEvecRGBParmNew.restype = POINTER(tenEvecRGBParm) tenEvecRGBParmNew.argtypes = [] tenEvecRGBParmNix = libteem.tenEvecRGBParmNix tenEvecRGBParmNix.restype = POINTER(tenEvecRGBParm) tenEvecRGBParmNix.argtypes = [POINTER(tenEvecRGBParm)] tenEvecRGBParmCheck = libteem.tenEvecRGBParmCheck tenEvecRGBParmCheck.restype = c_int tenEvecRGBParmCheck.argtypes = [POINTER(tenEvecRGBParm)] tenEvecRGBSingle_f = libteem.tenEvecRGBSingle_f tenEvecRGBSingle_f.restype = None tenEvecRGBSingle_f.argtypes = [POINTER(c_float), c_float, POINTER(c_float), POINTER(c_float), POINTER(tenEvecRGBParm)] tenEvecRGBSingle_d = libteem.tenEvecRGBSingle_d tenEvecRGBSingle_d.restype = None tenEvecRGBSingle_d.argtypes = [POINTER(c_double), c_double, POINTER(c_double), POINTER(c_double), POINTER(tenEvecRGBParm)] tenEvecRGB = libteem.tenEvecRGB tenEvecRGB.restype = c_int tenEvecRGB.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(tenEvecRGBParm)] tenEvqVolume = libteem.tenEvqVolume tenEvqVolume.restype = c_int tenEvqVolume.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_int, c_int, c_int] tenBMatrixCheck = libteem.tenBMatrixCheck tenBMatrixCheck.restype = c_int tenBMatrixCheck.argtypes = [POINTER(Nrrd), c_int, c_uint] tenFiberSingleInit = libteem.tenFiberSingleInit tenFiberSingleInit.restype = None tenFiberSingleInit.argtypes = [POINTER(tenFiberSingle)] tenFiberSingleDone = libteem.tenFiberSingleDone tenFiberSingleDone.restype = None tenFiberSingleDone.argtypes = [POINTER(tenFiberSingle)] tenFiberSingleNew = libteem.tenFiberSingleNew tenFiberSingleNew.restype = POINTER(tenFiberSingle) tenFiberSingleNew.argtypes = [] tenFiberSingleNix = libteem.tenFiberSingleNix tenFiberSingleNix.restype = POINTER(tenFiberSingle) tenFiberSingleNix.argtypes = [POINTER(tenFiberSingle)] tenFiberContextNew = libteem.tenFiberContextNew tenFiberContextNew.restype = POINTER(tenFiberContext) tenFiberContextNew.argtypes = [POINTER(Nrrd)] tenFiberContextDwiNew = libteem.tenFiberContextDwiNew tenFiberContextDwiNew.restype = POINTER(tenFiberContext) tenFiberContextDwiNew.argtypes = [POINTER(Nrrd), c_double, c_double, c_double, c_int, c_int] tenFiberVerboseSet = libteem.tenFiberVerboseSet tenFiberVerboseSet.restype = None tenFiberVerboseSet.argtypes = [POINTER(tenFiberContext), c_int] tenFiberTypeSet = libteem.tenFiberTypeSet tenFiberTypeSet.restype = c_int tenFiberTypeSet.argtypes = [POINTER(tenFiberContext), c_int] tenFiberKernelSet = libteem.tenFiberKernelSet tenFiberKernelSet.restype = c_int tenFiberKernelSet.argtypes = [POINTER(tenFiberContext), POINTER(NrrdKernel), POINTER(c_double)] tenFiberProbeItemSet = libteem.tenFiberProbeItemSet tenFiberProbeItemSet.restype = c_int tenFiberProbeItemSet.argtypes = [POINTER(tenFiberContext), c_int] tenFiberIntgSet = libteem.tenFiberIntgSet tenFiberIntgSet.restype = c_int tenFiberIntgSet.argtypes = [POINTER(tenFiberContext), c_int] tenFiberStopSet = libteem.tenFiberStopSet tenFiberStopSet.restype = c_int tenFiberStopSet.argtypes = [POINTER(tenFiberContext), c_int] tenFiberStopAnisoSet = libteem.tenFiberStopAnisoSet tenFiberStopAnisoSet.restype = c_int tenFiberStopAnisoSet.argtypes = [POINTER(tenFiberContext), c_int, c_double] tenFiberStopDoubleSet = libteem.tenFiberStopDoubleSet tenFiberStopDoubleSet.restype = c_int tenFiberStopDoubleSet.argtypes = [POINTER(tenFiberContext), c_int, c_double] tenFiberStopUIntSet = libteem.tenFiberStopUIntSet tenFiberStopUIntSet.restype = c_int tenFiberStopUIntSet.argtypes = [POINTER(tenFiberContext), c_int, c_uint] tenFiberStopOn = libteem.tenFiberStopOn tenFiberStopOn.restype = None tenFiberStopOn.argtypes = [POINTER(tenFiberContext), c_int] tenFiberStopOff = libteem.tenFiberStopOff tenFiberStopOff.restype = None tenFiberStopOff.argtypes = [POINTER(tenFiberContext), c_int] tenFiberStopReset = libteem.tenFiberStopReset tenFiberStopReset.restype = None tenFiberStopReset.argtypes = [POINTER(tenFiberContext)] tenFiberAnisoSpeedSet = libteem.tenFiberAnisoSpeedSet tenFiberAnisoSpeedSet.restype = c_int tenFiberAnisoSpeedSet.argtypes = [POINTER(tenFiberContext), c_int, c_double, c_double, c_double] tenFiberAnisoSpeedReset = libteem.tenFiberAnisoSpeedReset tenFiberAnisoSpeedReset.restype = c_int tenFiberAnisoSpeedReset.argtypes = [POINTER(tenFiberContext)] tenFiberParmSet = libteem.tenFiberParmSet tenFiberParmSet.restype = c_int tenFiberParmSet.argtypes = [POINTER(tenFiberContext), c_int, c_double] tenFiberUpdate = libteem.tenFiberUpdate tenFiberUpdate.restype = c_int tenFiberUpdate.argtypes = [POINTER(tenFiberContext)] tenFiberContextCopy = libteem.tenFiberContextCopy tenFiberContextCopy.restype = POINTER(tenFiberContext) tenFiberContextCopy.argtypes = [POINTER(tenFiberContext)] tenFiberContextNix = libteem.tenFiberContextNix tenFiberContextNix.restype = POINTER(tenFiberContext) tenFiberContextNix.argtypes = [POINTER(tenFiberContext)] tenFiberTraceSet = libteem.tenFiberTraceSet tenFiberTraceSet.restype = c_int tenFiberTraceSet.argtypes = [POINTER(tenFiberContext), POINTER(Nrrd), POINTER(c_double), c_uint, POINTER(c_uint), POINTER(c_uint), POINTER(c_double)] tenFiberTrace = libteem.tenFiberTrace tenFiberTrace.restype = c_int tenFiberTrace.argtypes = [POINTER(tenFiberContext), POINTER(Nrrd), POINTER(c_double)] tenFiberDirectionNumber = libteem.tenFiberDirectionNumber tenFiberDirectionNumber.restype = c_uint tenFiberDirectionNumber.argtypes = [POINTER(tenFiberContext), POINTER(c_double)] tenFiberSingleTrace = libteem.tenFiberSingleTrace tenFiberSingleTrace.restype = c_int tenFiberSingleTrace.argtypes = [POINTER(tenFiberContext), POINTER(tenFiberSingle), POINTER(c_double), c_uint] tenFiberMultiNew = libteem.tenFiberMultiNew tenFiberMultiNew.restype = POINTER(tenFiberMulti) tenFiberMultiNew.argtypes = [] tenFiberMultiNix = libteem.tenFiberMultiNix tenFiberMultiNix.restype = POINTER(tenFiberMulti) tenFiberMultiNix.argtypes = [POINTER(tenFiberMulti)] tenFiberMultiTrace = libteem.tenFiberMultiTrace tenFiberMultiTrace.restype = c_int tenFiberMultiTrace.argtypes = [POINTER(tenFiberContext), POINTER(tenFiberMulti), POINTER(Nrrd)] tenFiberMultiPolyData = libteem.tenFiberMultiPolyData tenFiberMultiPolyData.restype = c_int tenFiberMultiPolyData.argtypes = [POINTER(tenFiberContext), POINTER(limnPolyData), POINTER(tenFiberMulti)] tenFiberMultiProbeVals = libteem.tenFiberMultiProbeVals tenFiberMultiProbeVals.restype = c_int tenFiberMultiProbeVals.argtypes = [POINTER(tenFiberContext), POINTER(Nrrd), POINTER(tenFiberMulti)] tenEpiRegister3D = libteem.tenEpiRegister3D tenEpiRegister3D.restype = c_int tenEpiRegister3D.argtypes = [POINTER(POINTER(Nrrd)), POINTER(POINTER(Nrrd)), c_uint, POINTER(Nrrd), c_int, c_double, c_double, c_double, c_double, c_int, POINTER(NrrdKernel), POINTER(c_double), c_int, c_int] tenEpiRegister4D = libteem.tenEpiRegister4D tenEpiRegister4D.restype = c_int tenEpiRegister4D.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(Nrrd), c_int, c_double, c_double, c_double, c_double, c_int, POINTER(NrrdKernel), POINTER(c_double), c_int, c_int] tenExperSpecNew = libteem.tenExperSpecNew tenExperSpecNew.restype = POINTER(tenExperSpec) tenExperSpecNew.argtypes = [] tenExperSpecGradSingleBValSet = libteem.tenExperSpecGradSingleBValSet tenExperSpecGradSingleBValSet.restype = c_int tenExperSpecGradSingleBValSet.argtypes = [POINTER(tenExperSpec), c_int, c_double, POINTER(c_double), c_uint] tenExperSpecGradBValSet = libteem.tenExperSpecGradBValSet tenExperSpecGradBValSet.restype = c_int tenExperSpecGradBValSet.argtypes = [POINTER(tenExperSpec), c_int, POINTER(c_double), POINTER(c_double), c_uint] tenExperSpecFromKeyValueSet = libteem.tenExperSpecFromKeyValueSet tenExperSpecFromKeyValueSet.restype = c_int tenExperSpecFromKeyValueSet.argtypes = [POINTER(tenExperSpec), POINTER(Nrrd)] tenExperSpecNix = libteem.tenExperSpecNix tenExperSpecNix.restype = POINTER(tenExperSpec) tenExperSpecNix.argtypes = [POINTER(tenExperSpec)] tenExperSpecKnownB0Get = libteem.tenExperSpecKnownB0Get tenExperSpecKnownB0Get.restype = c_double tenExperSpecKnownB0Get.argtypes = [POINTER(tenExperSpec), POINTER(c_double)] tenExperSpecMaxBGet = libteem.tenExperSpecMaxBGet tenExperSpecMaxBGet.restype = c_double tenExperSpecMaxBGet.argtypes = [POINTER(tenExperSpec)] tenDWMRIKeyValueFromExperSpecSet = libteem.tenDWMRIKeyValueFromExperSpecSet tenDWMRIKeyValueFromExperSpecSet.restype = c_int tenDWMRIKeyValueFromExperSpecSet.argtypes = [POINTER(Nrrd), POINTER(tenExperSpec)] tenModelPrefixStr = (STRING).in_dll(libteem, 'tenModelPrefixStr') tenModelParse = libteem.tenModelParse tenModelParse.restype = c_int tenModelParse.argtypes = [POINTER(POINTER(tenModel)), POINTER(c_int), c_int, STRING] tenModelFromAxisLearnPossible = libteem.tenModelFromAxisLearnPossible tenModelFromAxisLearnPossible.restype = c_int tenModelFromAxisLearnPossible.argtypes = [POINTER(NrrdAxisInfo)] tenModelFromAxisLearn = libteem.tenModelFromAxisLearn tenModelFromAxisLearn.restype = c_int tenModelFromAxisLearn.argtypes = [POINTER(POINTER(tenModel)), POINTER(c_int), POINTER(NrrdAxisInfo)] tenModelSimulate = libteem.tenModelSimulate tenModelSimulate.restype = c_int tenModelSimulate.argtypes = [POINTER(Nrrd), c_int, POINTER(tenExperSpec), POINTER(tenModel), POINTER(Nrrd), POINTER(Nrrd), c_int] tenModelSqeFit = libteem.tenModelSqeFit tenModelSqeFit.restype = c_int tenModelSqeFit.argtypes = [POINTER(Nrrd), POINTER(POINTER(Nrrd)), POINTER(POINTER(Nrrd)), POINTER(POINTER(Nrrd)), POINTER(tenModel), POINTER(tenExperSpec), POINTER(Nrrd), c_int, c_int, c_int, c_uint, c_uint, c_uint, c_double, POINTER(airRandMTState), c_int] tenModelNllFit = libteem.tenModelNllFit tenModelNllFit.restype = c_int tenModelNllFit.argtypes = [POINTER(Nrrd), POINTER(POINTER(Nrrd)), POINTER(tenModel), POINTER(tenExperSpec), POINTER(Nrrd), c_int, c_double, c_int] tenModelConvert = libteem.tenModelConvert tenModelConvert.restype = c_int tenModelConvert.argtypes = [POINTER(Nrrd), POINTER(c_int), POINTER(tenModel), POINTER(Nrrd), POINTER(tenModel)] tenModelZero = (POINTER(tenModel)).in_dll(libteem, 'tenModelZero') tenModelB0 = (POINTER(tenModel)).in_dll(libteem, 'tenModelB0') tenModelBall = (POINTER(tenModel)).in_dll(libteem, 'tenModelBall') tenModel1Vector2D = (POINTER(tenModel)).in_dll(libteem, 'tenModel1Vector2D') tenModel1Unit2D = (POINTER(tenModel)).in_dll(libteem, 'tenModel1Unit2D') tenModel2Unit2D = (POINTER(tenModel)).in_dll(libteem, 'tenModel2Unit2D') tenModel1Stick = (POINTER(tenModel)).in_dll(libteem, 'tenModel1Stick') tenModelBall1StickEMD = (POINTER(tenModel)).in_dll(libteem, 'tenModelBall1StickEMD') tenModelBall1Stick = (POINTER(tenModel)).in_dll(libteem, 'tenModelBall1Stick') tenModelBall1Cylinder = (POINTER(tenModel)).in_dll(libteem, 'tenModelBall1Cylinder') tenModel1Cylinder = (POINTER(tenModel)).in_dll(libteem, 'tenModel1Cylinder') tenModel1Tensor2 = (POINTER(tenModel)).in_dll(libteem, 'tenModel1Tensor2') tenSizeNormalize = libteem.tenSizeNormalize tenSizeNormalize.restype = c_int tenSizeNormalize.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(c_double), c_double, c_double] tenSizeScale = libteem.tenSizeScale tenSizeScale.restype = c_int tenSizeScale.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_double] tenAnisoScale = libteem.tenAnisoScale tenAnisoScale.restype = c_int tenAnisoScale.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_double, c_int, c_int] tenEigenvaluePower = libteem.tenEigenvaluePower tenEigenvaluePower.restype = c_int tenEigenvaluePower.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_double] tenEigenvalueClamp = libteem.tenEigenvalueClamp tenEigenvalueClamp.restype = c_int tenEigenvalueClamp.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_double, c_double] tenEigenvalueAdd = libteem.tenEigenvalueAdd tenEigenvalueAdd.restype = c_int tenEigenvalueAdd.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_double] tenEigenvalueMultiply = libteem.tenEigenvalueMultiply tenEigenvalueMultiply.restype = c_int tenEigenvalueMultiply.argtypes = [POINTER(Nrrd), POINTER(Nrrd), c_double] tenLog = libteem.tenLog tenLog.restype = c_int tenLog.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] tenExp = libteem.tenExp tenExp.restype = c_int tenExp.argtypes = [POINTER(Nrrd), POINTER(Nrrd)] tenBVecNonLinearFit = libteem.tenBVecNonLinearFit tenBVecNonLinearFit.restype = c_int tenBVecNonLinearFit.argtypes = [POINTER(Nrrd), POINTER(Nrrd), POINTER(c_double), POINTER(c_double), c_int, c_double] tenGageKind = (POINTER(gageKind)).in_dll(libteem, 'tenGageKind') tenDwiGage = (POINTER(airEnum)).in_dll(libteem, 'tenDwiGage') tenDwiGageKindNew = libteem.tenDwiGageKindNew tenDwiGageKindNew.restype = POINTER(gageKind) tenDwiGageKindNew.argtypes = [] tenDwiGageKindNix = libteem.tenDwiGageKindNix tenDwiGageKindNix.restype = POINTER(gageKind) tenDwiGageKindNix.argtypes = [POINTER(gageKind)] tenDwiGageKindSet = libteem.tenDwiGageKindSet tenDwiGageKindSet.restype = c_int tenDwiGageKindSet.argtypes = [POINTER(gageKind), c_double, c_double, c_double, c_double, POINTER(Nrrd), POINTER(Nrrd), c_int, c_int, c_uint] tenDwiGageKindCheck = libteem.tenDwiGageKindCheck tenDwiGageKindCheck.restype = c_int tenDwiGageKindCheck.argtypes = [POINTER(gageKind)] tenEMBimodalParmNew = libteem.tenEMBimodalParmNew tenEMBimodalParmNew.restype = POINTER(tenEMBimodalParm) tenEMBimodalParmNew.argtypes = [] tenEMBimodalParmNix = libteem.tenEMBimodalParmNix tenEMBimodalParmNix.restype = POINTER(tenEMBimodalParm) tenEMBimodalParmNix.argtypes = [POINTER(tenEMBimodalParm)] tenEMBimodal = libteem.tenEMBimodal tenEMBimodal.restype = c_int tenEMBimodal.argtypes = [POINTER(tenEMBimodalParm), POINTER(Nrrd)] tend_ellipseCmd = (unrrduCmd).in_dll(libteem, 'tend_ellipseCmd') tend_tconvCmd = (unrrduCmd).in_dll(libteem, 'tend_tconvCmd') tend_bmatCmd = (unrrduCmd).in_dll(libteem, 'tend_bmatCmd') tend_simCmd = (unrrduCmd).in_dll(libteem, 'tend_simCmd') tend_evaladdCmd = (unrrduCmd).in_dll(libteem, 'tend_evaladdCmd') tend_anscaleCmd = (unrrduCmd).in_dll(libteem, 'tend_anscaleCmd') tend_anvolCmd = (unrrduCmd).in_dll(libteem, 'tend_anvolCmd') tend_bfitCmd = (unrrduCmd).in_dll(libteem, 'tend_bfitCmd') tend_evalCmd = (unrrduCmd).in_dll(libteem, 'tend_evalCmd') tend_evalmultCmd = (unrrduCmd).in_dll(libteem, 'tend_evalmultCmd') tend_evecCmd = (unrrduCmd).in_dll(libteem, 'tend_evecCmd') tend_glyphCmd = (unrrduCmd).in_dll(libteem, 'tend_glyphCmd') tend_shrinkCmd = (unrrduCmd).in_dll(libteem, 'tend_shrinkCmd') tend_epiregCmd = (unrrduCmd).in_dll(libteem, 'tend_epiregCmd') tend_anplotCmd = (unrrduCmd).in_dll(libteem, 'tend_anplotCmd') tend_aboutCmd = (unrrduCmd).in_dll(libteem, 'tend_aboutCmd') tend_gradsCmd = (unrrduCmd).in_dll(libteem, 'tend_gradsCmd') tend_helixCmd = (unrrduCmd).in_dll(libteem, 'tend_helixCmd') tend_fiberCmd = (unrrduCmd).in_dll(libteem, 'tend_fiberCmd') tend_logCmd = (unrrduCmd).in_dll(libteem, 'tend_logCmd') tend_mconvCmd = (unrrduCmd).in_dll(libteem, 'tend_mconvCmd') tend_evalclampCmd = (unrrduCmd).in_dll(libteem, 'tend_evalclampCmd') tend_evqCmd = (unrrduCmd).in_dll(libteem, 'tend_evqCmd') tend_avgCmd = (unrrduCmd).in_dll(libteem, 'tend_avgCmd') tend_unmfCmd = (unrrduCmd).in_dll(libteem, 'tend_unmfCmd') tend_expandCmd = (unrrduCmd).in_dll(libteem, 'tend_expandCmd') tend_makeCmd = (unrrduCmd).in_dll(libteem, 'tend_makeCmd') tend_anhistCmd = (unrrduCmd).in_dll(libteem, 'tend_anhistCmd') tend_normCmd = (unrrduCmd).in_dll(libteem, 'tend_normCmd') tend_evalpowCmd = (unrrduCmd).in_dll(libteem, 'tend_evalpowCmd') tend_tripleCmd = (unrrduCmd).in_dll(libteem, 'tend_tripleCmd') tend_satinCmd = (unrrduCmd).in_dll(libteem, 'tend_satinCmd') tend_estimCmd = (unrrduCmd).in_dll(libteem, 'tend_estimCmd') tend_stenCmd = (unrrduCmd).in_dll(libteem, 'tend_stenCmd') tend_expCmd = (unrrduCmd).in_dll(libteem, 'tend_expCmd') tend_sliceCmd = (unrrduCmd).in_dll(libteem, 'tend_sliceCmd') tend_mfitCmd = (unrrduCmd).in_dll(libteem, 'tend_mfitCmd') tend_pointCmd = (unrrduCmd).in_dll(libteem, 'tend_pointCmd') tend_evecrgbCmd = (unrrduCmd).in_dll(libteem, 'tend_evecrgbCmd') tend_msimCmd = (unrrduCmd).in_dll(libteem, 'tend_msimCmd') tendCmdList = (POINTER(unrrduCmd) * 0).in_dll(libteem, 'tendCmdList') tendFiberStopCB = (POINTER(hestCB)).in_dll(libteem, 'tendFiberStopCB') tendTitle = (STRING).in_dll(libteem, 'tendTitle') class tijk_sym_fun_t(Structure): pass tijk_sym_fun_t._fields_ = [ ('s_form_d', CFUNCTYPE(c_double, POINTER(c_double), POINTER(c_double))), ('s_form_f', CFUNCTYPE(c_float, POINTER(c_float), POINTER(c_float))), ('mean_d', CFUNCTYPE(c_double, POINTER(c_double))), ('mean_f', CFUNCTYPE(c_float, POINTER(c_float))), ('var_d', CFUNCTYPE(c_double, POINTER(c_double))), ('var_f', CFUNCTYPE(c_float, POINTER(c_float))), ('v_form_d', CFUNCTYPE(None, POINTER(c_double), POINTER(c_double), POINTER(c_double))), ('v_form_f', CFUNCTYPE(None, POINTER(c_float), POINTER(c_float), POINTER(c_float))), ('m_form_d', CFUNCTYPE(None, POINTER(c_double), POINTER(c_double), POINTER(c_double))), ('m_form_f', CFUNCTYPE(None, POINTER(c_float), POINTER(c_float), POINTER(c_float))), ('grad_d', CFUNCTYPE(None, POINTER(c_double), POINTER(c_double), POINTER(c_double))), ('grad_f', CFUNCTYPE(None, POINTER(c_float), POINTER(c_float), POINTER(c_float))), ('hess_d', CFUNCTYPE(None, POINTER(c_double), POINTER(c_double), POINTER(c_double))), ('hess_f', CFUNCTYPE(None, POINTER(c_float), POINTER(c_float), POINTER(c_float))), ('make_rank1_d', CFUNCTYPE(None, POINTER(c_double), c_double, POINTER(c_double))), ('make_rank1_f', CFUNCTYPE(None, POINTER(c_float), c_float, POINTER(c_float))), ('make_iso_d', CFUNCTYPE(None, POINTER(c_double), c_double)), ('make_iso_f', CFUNCTYPE(None, POINTER(c_float), c_float)), ] tijk_sym_fun = tijk_sym_fun_t tijk_type_t._fields_ = [ ('name', STRING), ('order', c_uint), ('dim', c_uint), ('num', c_uint), ('mult', POINTER(c_uint)), ('unsym2uniq', POINTER(c_int)), ('uniq2unsym', POINTER(c_int)), ('uniq_idx', POINTER(c_uint)), ('tsp_d', CFUNCTYPE(c_double, POINTER(c_double), POINTER(c_double))), ('tsp_f', CFUNCTYPE(c_float, POINTER(c_float), POINTER(c_float))), ('norm_d', CFUNCTYPE(c_double, POINTER(c_double))), ('norm_f', CFUNCTYPE(c_float, POINTER(c_float))), ('trans_d', CFUNCTYPE(None, POINTER(c_double), POINTER(c_double), POINTER(c_double))), ('trans_f', CFUNCTYPE(None, POINTER(c_float), POINTER(c_float), POINTER(c_float))), ('convert_d', CFUNCTYPE(c_int, POINTER(c_double), POINTER(tijk_type_t), POINTER(c_double))), ('convert_f', CFUNCTYPE(c_int, POINTER(c_float), POINTER(tijk_type_t), POINTER(c_float))), ('approx_d', CFUNCTYPE(c_int, POINTER(c_double), POINTER(tijk_type_t), POINTER(c_double))), ('approx_f', CFUNCTYPE(c_int, POINTER(c_float), POINTER(tijk_type_t), POINTER(c_float))), ('_convert_from_d', CFUNCTYPE(c_int, POINTER(c_double), POINTER(c_double), POINTER(tijk_type_t))), ('_convert_from_f', CFUNCTYPE(c_int, POINTER(c_float), POINTER(c_float), POINTER(tijk_type_t))), ('_approx_from_d', CFUNCTYPE(c_int, POINTER(c_double), POINTER(c_double), POINTER(tijk_type_t))), ('_approx_from_f', CFUNCTYPE(c_int, POINTER(c_float), POINTER(c_float), POINTER(tijk_type_t))), ('sym', POINTER(tijk_sym_fun)), ] tijk_2o2d_unsym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_2o2d_unsym') tijk_2o2d_sym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_2o2d_sym') tijk_2o2d_asym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_2o2d_asym') tijk_3o2d_sym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_3o2d_sym') tijk_4o2d_unsym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_4o2d_unsym') tijk_4o2d_sym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_4o2d_sym') tijk_1o3d = (POINTER(tijk_type)).in_dll(libteem, 'tijk_1o3d') tijk_2o3d_unsym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_2o3d_unsym') tijk_2o3d_sym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_2o3d_sym') tijk_2o3d_asym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_2o3d_asym') tijk_3o3d_unsym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_3o3d_unsym') tijk_3o3d_sym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_3o3d_sym') tijk_4o3d_sym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_4o3d_sym') tijk_6o3d_sym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_6o3d_sym') tijk_8o3d_sym = (POINTER(tijk_type)).in_dll(libteem, 'tijk_8o3d_sym') tijkPresent = (c_int).in_dll(libteem, 'tijkPresent') tijk_add_d = libteem.tijk_add_d tijk_add_d.restype = None tijk_add_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type)] tijk_add_f = libteem.tijk_add_f tijk_add_f.restype = None tijk_add_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type)] tijk_sub_d = libteem.tijk_sub_d tijk_sub_d.restype = None tijk_sub_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type)] tijk_sub_f = libteem.tijk_sub_f tijk_sub_f.restype = None tijk_sub_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type)] tijk_incr_d = libteem.tijk_incr_d tijk_incr_d.restype = None tijk_incr_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(tijk_type)] tijk_incr_f = libteem.tijk_incr_f tijk_incr_f.restype = None tijk_incr_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(tijk_type)] tijk_negate_d = libteem.tijk_negate_d tijk_negate_d.restype = None tijk_negate_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(tijk_type)] tijk_negate_f = libteem.tijk_negate_f tijk_negate_f.restype = None tijk_negate_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(tijk_type)] tijk_scale_d = libteem.tijk_scale_d tijk_scale_d.restype = None tijk_scale_d.argtypes = [POINTER(c_double), c_double, POINTER(c_double), POINTER(tijk_type)] tijk_scale_f = libteem.tijk_scale_f tijk_scale_f.restype = None tijk_scale_f.argtypes = [POINTER(c_float), c_float, POINTER(c_float), POINTER(tijk_type)] tijk_zero_d = libteem.tijk_zero_d tijk_zero_d.restype = None tijk_zero_d.argtypes = [POINTER(c_double), POINTER(tijk_type)] tijk_zero_f = libteem.tijk_zero_f tijk_zero_f.restype = None tijk_zero_f.argtypes = [POINTER(c_float), POINTER(tijk_type)] tijk_copy_d = libteem.tijk_copy_d tijk_copy_d.restype = None tijk_copy_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(tijk_type)] tijk_copy_f = libteem.tijk_copy_f tijk_copy_f.restype = None tijk_copy_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(tijk_type)] tijk_refine_rank1_parm_t._pack_ = 4 tijk_refine_rank1_parm_t._fields_ = [ ('eps_start', c_double), ('eps_impr', c_double), ('beta', c_double), ('gamma', c_double), ('sigma', c_double), ('maxtry', c_uint), ] tijk_refine_rank1_parm_new = libteem.tijk_refine_rank1_parm_new tijk_refine_rank1_parm_new.restype = POINTER(tijk_refine_rank1_parm) tijk_refine_rank1_parm_new.argtypes = [] tijk_refine_rank1_parm_nix = libteem.tijk_refine_rank1_parm_nix tijk_refine_rank1_parm_nix.restype = POINTER(tijk_refine_rank1_parm) tijk_refine_rank1_parm_nix.argtypes = [POINTER(tijk_refine_rank1_parm)] class tijk_refine_rankk_parm_t(Structure): pass tijk_refine_rankk_parm_t._pack_ = 4 tijk_refine_rankk_parm_t._fields_ = [ ('eps_res', c_double), ('eps_impr', c_double), ('pos', c_char), ('rank1_parm', POINTER(tijk_refine_rank1_parm)), ] tijk_refine_rankk_parm = tijk_refine_rankk_parm_t tijk_refine_rankk_parm_new = libteem.tijk_refine_rankk_parm_new tijk_refine_rankk_parm_new.restype = POINTER(tijk_refine_rankk_parm) tijk_refine_rankk_parm_new.argtypes = [] tijk_refine_rankk_parm_nix = libteem.tijk_refine_rankk_parm_nix tijk_refine_rankk_parm_nix.restype = POINTER(tijk_refine_rankk_parm) tijk_refine_rankk_parm_nix.argtypes = [POINTER(tijk_refine_rankk_parm)] class tijk_approx_heur_parm_t(Structure): pass tijk_approx_heur_parm_t._pack_ = 4 tijk_approx_heur_parm_t._fields_ = [ ('eps_res', c_double), ('eps_impr', c_double), ('ratios', POINTER(c_double)), ('refine_parm', POINTER(tijk_refine_rankk_parm)), ] tijk_approx_heur_parm = tijk_approx_heur_parm_t tijk_approx_heur_parm_new = libteem.tijk_approx_heur_parm_new tijk_approx_heur_parm_new.restype = POINTER(tijk_approx_heur_parm) tijk_approx_heur_parm_new.argtypes = [] tijk_approx_heur_parm_nix = libteem.tijk_approx_heur_parm_nix tijk_approx_heur_parm_nix.restype = POINTER(tijk_approx_heur_parm) tijk_approx_heur_parm_nix.argtypes = [POINTER(tijk_approx_heur_parm)] tijk_init_rank1_2d_d = libteem.tijk_init_rank1_2d_d tijk_init_rank1_2d_d.restype = c_int tijk_init_rank1_2d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type)] tijk_init_rank1_2d_f = libteem.tijk_init_rank1_2d_f tijk_init_rank1_2d_f.restype = c_int tijk_init_rank1_2d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type)] tijk_init_rank1_3d_d = libteem.tijk_init_rank1_3d_d tijk_init_rank1_3d_d.restype = c_int tijk_init_rank1_3d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type)] tijk_init_rank1_3d_f = libteem.tijk_init_rank1_3d_f tijk_init_rank1_3d_f.restype = c_int tijk_init_rank1_3d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type)] tijk_init_max_2d_d = libteem.tijk_init_max_2d_d tijk_init_max_2d_d.restype = c_int tijk_init_max_2d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type)] tijk_init_max_2d_f = libteem.tijk_init_max_2d_f tijk_init_max_2d_f.restype = c_int tijk_init_max_2d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type)] tijk_init_max_3d_d = libteem.tijk_init_max_3d_d tijk_init_max_3d_d.restype = c_int tijk_init_max_3d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type)] tijk_init_max_3d_f = libteem.tijk_init_max_3d_f tijk_init_max_3d_f.restype = c_int tijk_init_max_3d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type)] tijk_refine_rank1_2d_d = libteem.tijk_refine_rank1_2d_d tijk_refine_rank1_2d_d.restype = c_int tijk_refine_rank1_2d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type), POINTER(tijk_refine_rank1_parm)] tijk_refine_rank1_2d_f = libteem.tijk_refine_rank1_2d_f tijk_refine_rank1_2d_f.restype = c_int tijk_refine_rank1_2d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type), POINTER(tijk_refine_rank1_parm)] tijk_refine_rank1_3d_d = libteem.tijk_refine_rank1_3d_d tijk_refine_rank1_3d_d.restype = c_int tijk_refine_rank1_3d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type), POINTER(tijk_refine_rank1_parm)] tijk_refine_rank1_3d_f = libteem.tijk_refine_rank1_3d_f tijk_refine_rank1_3d_f.restype = c_int tijk_refine_rank1_3d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type), POINTER(tijk_refine_rank1_parm)] tijk_refine_max_2d_d = libteem.tijk_refine_max_2d_d tijk_refine_max_2d_d.restype = c_int tijk_refine_max_2d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type), POINTER(tijk_refine_rank1_parm)] tijk_refine_max_2d_f = libteem.tijk_refine_max_2d_f tijk_refine_max_2d_f.restype = c_int tijk_refine_max_2d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type), POINTER(tijk_refine_rank1_parm)] tijk_refine_max_3d_d = libteem.tijk_refine_max_3d_d tijk_refine_max_3d_d.restype = c_int tijk_refine_max_3d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type), POINTER(tijk_refine_rank1_parm)] tijk_refine_max_3d_f = libteem.tijk_refine_max_3d_f tijk_refine_max_3d_f.restype = c_int tijk_refine_max_3d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type), POINTER(tijk_refine_rank1_parm)] tijk_refine_rankk_2d_d = libteem.tijk_refine_rankk_2d_d tijk_refine_rankk_2d_d.restype = c_int tijk_refine_rankk_2d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_double, POINTER(tijk_type), c_uint, POINTER(tijk_refine_rankk_parm)] tijk_refine_rankk_2d_f = libteem.tijk_refine_rankk_2d_f tijk_refine_rankk_2d_f.restype = c_int tijk_refine_rankk_2d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, POINTER(tijk_type), c_uint, POINTER(tijk_refine_rankk_parm)] tijk_refine_rankk_3d_d = libteem.tijk_refine_rankk_3d_d tijk_refine_rankk_3d_d.restype = c_int tijk_refine_rankk_3d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_double, POINTER(tijk_type), c_uint, POINTER(tijk_refine_rankk_parm)] tijk_refine_rankk_3d_f = libteem.tijk_refine_rankk_3d_f tijk_refine_rankk_3d_f.restype = c_int tijk_refine_rankk_3d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), c_float, POINTER(tijk_type), c_uint, POINTER(tijk_refine_rankk_parm)] tijk_approx_rankk_2d_d = libteem.tijk_approx_rankk_2d_d tijk_approx_rankk_2d_d.restype = c_int tijk_approx_rankk_2d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type), c_uint, POINTER(tijk_refine_rankk_parm)] tijk_approx_rankk_2d_f = libteem.tijk_approx_rankk_2d_f tijk_approx_rankk_2d_f.restype = c_int tijk_approx_rankk_2d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type), c_uint, POINTER(tijk_refine_rankk_parm)] tijk_approx_rankk_3d_d = libteem.tijk_approx_rankk_3d_d tijk_approx_rankk_3d_d.restype = c_int tijk_approx_rankk_3d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type), c_uint, POINTER(tijk_refine_rankk_parm)] tijk_approx_rankk_3d_f = libteem.tijk_approx_rankk_3d_f tijk_approx_rankk_3d_f.restype = c_int tijk_approx_rankk_3d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type), c_uint, POINTER(tijk_refine_rankk_parm)] tijk_approx_heur_2d_d = libteem.tijk_approx_heur_2d_d tijk_approx_heur_2d_d.restype = c_int tijk_approx_heur_2d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type), c_uint, POINTER(tijk_approx_heur_parm)] tijk_approx_heur_2d_f = libteem.tijk_approx_heur_2d_f tijk_approx_heur_2d_f.restype = c_int tijk_approx_heur_2d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type), c_uint, POINTER(tijk_approx_heur_parm)] tijk_approx_heur_3d_d = libteem.tijk_approx_heur_3d_d tijk_approx_heur_3d_d.restype = c_int tijk_approx_heur_3d_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(tijk_type), c_uint, POINTER(tijk_approx_heur_parm)] tijk_approx_heur_3d_f = libteem.tijk_approx_heur_3d_f tijk_approx_heur_3d_f.restype = c_int tijk_approx_heur_3d_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(tijk_type), c_uint, POINTER(tijk_approx_heur_parm)] tijk_esh_len = (c_uint * 0).in_dll(libteem, 'tijk_esh_len') tijk_max_esh_order = (c_uint).in_dll(libteem, 'tijk_max_esh_order') tijk_eval_esh_basis_d = libteem.tijk_eval_esh_basis_d tijk_eval_esh_basis_d.restype = c_uint tijk_eval_esh_basis_d.argtypes = [POINTER(c_double), c_uint, c_double, c_double] tijk_eval_esh_basis_f = libteem.tijk_eval_esh_basis_f tijk_eval_esh_basis_f.restype = c_uint tijk_eval_esh_basis_f.argtypes = [POINTER(c_float), c_uint, c_float, c_float] tijk_eval_esh_d = libteem.tijk_eval_esh_d tijk_eval_esh_d.restype = c_double tijk_eval_esh_d.argtypes = [POINTER(c_double), c_uint, c_double, c_double] tijk_eval_esh_f = libteem.tijk_eval_esh_f tijk_eval_esh_f.restype = c_float tijk_eval_esh_f.argtypes = [POINTER(c_float), c_uint, c_float, c_float] tijk_esh_sp_d = libteem.tijk_esh_sp_d tijk_esh_sp_d.restype = c_double tijk_esh_sp_d.argtypes = [POINTER(c_double), POINTER(c_double), c_uint] tijk_esh_sp_f = libteem.tijk_esh_sp_f tijk_esh_sp_f.restype = c_float tijk_esh_sp_f.argtypes = [POINTER(c_float), POINTER(c_float), c_uint] tijk_3d_sym_to_esh_d = libteem.tijk_3d_sym_to_esh_d tijk_3d_sym_to_esh_d.restype = c_int tijk_3d_sym_to_esh_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(tijk_type)] tijk_3d_sym_to_esh_f = libteem.tijk_3d_sym_to_esh_f tijk_3d_sym_to_esh_f.restype = c_int tijk_3d_sym_to_esh_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(tijk_type)] tijk_esh_to_3d_sym_d = libteem.tijk_esh_to_3d_sym_d tijk_esh_to_3d_sym_d.restype = POINTER(tijk_type) tijk_esh_to_3d_sym_d.argtypes = [POINTER(c_double), POINTER(c_double), c_uint] tijk_esh_to_3d_sym_f = libteem.tijk_esh_to_3d_sym_f tijk_esh_to_3d_sym_f.restype = POINTER(tijk_type) tijk_esh_to_3d_sym_f.argtypes = [POINTER(c_float), POINTER(c_float), c_uint] tijk_3d_sym_to_esh_matrix_d = libteem.tijk_3d_sym_to_esh_matrix_d tijk_3d_sym_to_esh_matrix_d.restype = POINTER(c_double) tijk_3d_sym_to_esh_matrix_d.argtypes = [POINTER(tijk_type)] tijk_3d_sym_to_esh_matrix_f = libteem.tijk_3d_sym_to_esh_matrix_f tijk_3d_sym_to_esh_matrix_f.restype = POINTER(c_float) tijk_3d_sym_to_esh_matrix_f.argtypes = [POINTER(tijk_type)] tijk_esh_to_3d_sym_matrix_d = libteem.tijk_esh_to_3d_sym_matrix_d tijk_esh_to_3d_sym_matrix_d.restype = POINTER(c_double) tijk_esh_to_3d_sym_matrix_d.argtypes = [c_uint] tijk_esh_to_3d_sym_matrix_f = libteem.tijk_esh_to_3d_sym_matrix_f tijk_esh_to_3d_sym_matrix_f.restype = POINTER(c_float) tijk_esh_to_3d_sym_matrix_f.argtypes = [c_uint] tijk_esh_convolve_d = libteem.tijk_esh_convolve_d tijk_esh_convolve_d.restype = None tijk_esh_convolve_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), c_uint] tijk_esh_convolve_f = libteem.tijk_esh_convolve_f tijk_esh_convolve_f.restype = None tijk_esh_convolve_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_uint] tijk_esh_deconvolve_d = libteem.tijk_esh_deconvolve_d tijk_esh_deconvolve_d.restype = None tijk_esh_deconvolve_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), c_uint] tijk_esh_deconvolve_f = libteem.tijk_esh_deconvolve_f tijk_esh_deconvolve_f.restype = None tijk_esh_deconvolve_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_uint] tijk_esh_make_kernel_rank1_f = libteem.tijk_esh_make_kernel_rank1_f tijk_esh_make_kernel_rank1_f.restype = c_int tijk_esh_make_kernel_rank1_f.argtypes = [POINTER(c_float), POINTER(c_float), c_uint] tijk_esh_make_kernel_rank1_d = libteem.tijk_esh_make_kernel_rank1_d tijk_esh_make_kernel_rank1_d.restype = c_int tijk_esh_make_kernel_rank1_d.argtypes = [POINTER(c_double), POINTER(c_double), c_uint] tijk_esh_make_kernel_delta_f = libteem.tijk_esh_make_kernel_delta_f tijk_esh_make_kernel_delta_f.restype = c_int tijk_esh_make_kernel_delta_f.argtypes = [POINTER(c_float), POINTER(c_float), c_uint] tijk_esh_make_kernel_delta_d = libteem.tijk_esh_make_kernel_delta_d tijk_esh_make_kernel_delta_d.restype = c_int tijk_esh_make_kernel_delta_d.argtypes = [POINTER(c_double), POINTER(c_double), c_uint] tijk_max_efs_order = (c_uint).in_dll(libteem, 'tijk_max_efs_order') tijk_eval_efs_basis_d = libteem.tijk_eval_efs_basis_d tijk_eval_efs_basis_d.restype = c_uint tijk_eval_efs_basis_d.argtypes = [POINTER(c_double), c_uint, c_double] tijk_eval_efs_basis_f = libteem.tijk_eval_efs_basis_f tijk_eval_efs_basis_f.restype = c_uint tijk_eval_efs_basis_f.argtypes = [POINTER(c_float), c_uint, c_float] tijk_eval_efs_d = libteem.tijk_eval_efs_d tijk_eval_efs_d.restype = c_double tijk_eval_efs_d.argtypes = [POINTER(c_double), c_uint, c_double] tijk_eval_efs_f = libteem.tijk_eval_efs_f tijk_eval_efs_f.restype = c_float tijk_eval_efs_f.argtypes = [POINTER(c_float), c_uint, c_float] tijk_2d_sym_to_efs_d = libteem.tijk_2d_sym_to_efs_d tijk_2d_sym_to_efs_d.restype = c_int tijk_2d_sym_to_efs_d.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(tijk_type)] tijk_2d_sym_to_efs_f = libteem.tijk_2d_sym_to_efs_f tijk_2d_sym_to_efs_f.restype = c_int tijk_2d_sym_to_efs_f.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(tijk_type)] tijk_efs_to_2d_sym_d = libteem.tijk_efs_to_2d_sym_d tijk_efs_to_2d_sym_d.restype = POINTER(tijk_type) tijk_efs_to_2d_sym_d.argtypes = [POINTER(c_double), POINTER(c_double), c_uint] tijk_efs_to_2d_sym_f = libteem.tijk_efs_to_2d_sym_f tijk_efs_to_2d_sym_f.restype = POINTER(tijk_type) tijk_efs_to_2d_sym_f.argtypes = [POINTER(c_float), POINTER(c_float), c_uint] tijk_class = (POINTER(airEnum)).in_dll(libteem, 'tijk_class') tijk_set_axis_tensor = libteem.tijk_set_axis_tensor tijk_set_axis_tensor.restype = c_int tijk_set_axis_tensor.argtypes = [POINTER(Nrrd), c_uint, POINTER(tijk_type)] tijk_set_axis_esh = libteem.tijk_set_axis_esh tijk_set_axis_esh.restype = c_int tijk_set_axis_esh.argtypes = [POINTER(Nrrd), c_uint, c_uint] tijk_set_axis_efs = libteem.tijk_set_axis_efs tijk_set_axis_efs.restype = c_int tijk_set_axis_efs.argtypes = [POINTER(Nrrd), c_uint, c_uint] class tijk_axis_info_t(Structure): pass tijk_axis_info_t._fields_ = [ ('tclass', c_int), ('masked', c_uint), ('type', POINTER(tijk_type)), ('order', c_uint), ] tijk_axis_info = tijk_axis_info_t tijk_get_axis_type = libteem.tijk_get_axis_type tijk_get_axis_type.restype = c_int tijk_get_axis_type.argtypes = [POINTER(tijk_axis_info), POINTER(Nrrd), c_uint] unrrdu_distCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_distCmd') unrrdu_spliceCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_spliceCmd') unrrdu_mlutCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_mlutCmd') unrrdu_imapCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_imapCmd') unrrdu_cksumCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_cksumCmd') unrrdu_basinfoCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_basinfoCmd') unrrdu_unquantizeCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_unquantizeCmd') unrrdu_mrmapCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_mrmapCmd') unrrdu_ccfindCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_ccfindCmd') unrrdu_diffCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_diffCmd') unrrdu_resampleCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_resampleCmd') unrrdu_envCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_envCmd') unrrdu_saveCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_saveCmd') unrrdu_quantizeCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_quantizeCmd') unrrdu_shuffleCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_shuffleCmd') unrrdu_axinsertCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_axinsertCmd') unrrdu_cropCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_cropCmd') unrrdu_aboutCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_aboutCmd') unrrdu_w2iCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_w2iCmd') unrrdu_convertCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_convertCmd') unrrdu_gammaCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_gammaCmd') unrrdu_ccadjCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_ccadjCmd') unrrdu_jhistoCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_jhistoCmd') unrrdu_flipCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_flipCmd') unrrdu_swapCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_swapCmd') unrrdu_lutCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_lutCmd') unrrdu_joinCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_joinCmd') unrrdu_undosCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_undosCmd') unrrdu_sselectCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_sselectCmd') unrrdu_permuteCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_permuteCmd') unrrdu_histoCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_histoCmd') unrrdu_minmaxCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_minmaxCmd') unrrdu_axdeleteCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_axdeleteCmd') unrrdu_lut2Cmd = (unrrduCmd).in_dll(libteem, 'unrrdu_lut2Cmd') unrrdu_substCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_substCmd') unrrdu_axinfoCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_axinfoCmd') unrrdu_dataCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_dataCmd') unrrdu_reshapeCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_reshapeCmd') unrrdu_headCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_headCmd') unrrdu_ccmergeCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_ccmergeCmd') unrrdu_cmedianCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_cmedianCmd') unrrdu_vidiconCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_vidiconCmd') unrrdu_acropCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_acropCmd') unrrdu_tileCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_tileCmd') unrrdu_heqCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_heqCmd') unrrdu_rmapCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_rmapCmd') unrrdu_padCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_padCmd') unrrdu_fftCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_fftCmd') unrrdu_histaxCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_histaxCmd') unrrdu_projectCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_projectCmd') unrrdu_sliceCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_sliceCmd') unrrdu_2opCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_2opCmd') unrrdu_deringCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_deringCmd') unrrdu_untileCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_untileCmd') unrrdu_insetCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_insetCmd') unrrdu_3opCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_3opCmd') unrrdu_ccsettleCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_ccsettleCmd') unrrdu_unorientCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_unorientCmd') unrrdu_dhistoCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_dhistoCmd') unrrdu_diceCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_diceCmd') unrrdu_i2wCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_i2wCmd') unrrdu_axmergeCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_axmergeCmd') unrrdu_axsplitCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_axsplitCmd') unrrdu_affineCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_affineCmd') unrrdu_1opCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_1opCmd') unrrdu_makeCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_makeCmd') unrrdu_dnormCmd = (unrrduCmd).in_dll(libteem, 'unrrdu_dnormCmd') unrrduPresent = (c_int).in_dll(libteem, 'unrrduPresent') unrrduBiffKey = (STRING).in_dll(libteem, 'unrrduBiffKey') unrrduDefNumColumns = (c_uint).in_dll(libteem, 'unrrduDefNumColumns') unrrduCmdList = (POINTER(unrrduCmd) * 0).in_dll(libteem, 'unrrduCmdList') unrrduUsageUnu = libteem.unrrduUsageUnu unrrduUsageUnu.restype = None unrrduUsageUnu.argtypes = [STRING, POINTER(hestParm)] unrrduUsage = libteem.unrrduUsage unrrduUsage.restype = c_int unrrduUsage.argtypes = [STRING, POINTER(hestParm), STRING, POINTER(POINTER(unrrduCmd))] unrrduHestPosCB = (hestCB).in_dll(libteem, 'unrrduHestPosCB') unrrduHestMaybeTypeCB = (hestCB).in_dll(libteem, 'unrrduHestMaybeTypeCB') unrrduHestScaleCB = (hestCB).in_dll(libteem, 'unrrduHestScaleCB') unrrduHestBitsCB = (hestCB).in_dll(libteem, 'unrrduHestBitsCB') unrrduHestFileCB = (hestCB).in_dll(libteem, 'unrrduHestFileCB') unrrduHestEncodingCB = (hestCB).in_dll(libteem, 'unrrduHestEncodingCB') _airThread._fields_ = [ ] _airThreadMutex._fields_ = [ ] _airThreadCond._fields_ = [ ] NrrdIoState_t._fields_ = [ ('path', STRING), ('base', STRING), ('line', STRING), ('dataFNFormat', STRING), ('dataFN', POINTER(STRING)), ('headerStringWrite', STRING), ('headerStringRead', STRING), ('dataFNArr', POINTER(airArray)), ('headerFile', POINTER(FILE)), ('dataFile', POINTER(FILE)), ('dataFileDim', c_uint), ('lineLen', c_uint), ('charsPerLine', c_uint), ('valsPerLine', c_uint), ('lineSkip', c_uint), ('headerStrlen', c_uint), ('headerStrpos', c_uint), ('byteSkip', c_long), ('dataFNMin', c_int), ('dataFNMax', c_int), ('dataFNStep', c_int), ('dataFNIndex', c_uint), ('pos', c_int), ('endian', c_int), ('seen', c_int * 33), ('detachedHeader', c_int), ('bareText', c_int), ('skipData', c_int), ('skipFormatURL', c_int), ('keepNrrdDataFileOpen', c_int), ('zlibLevel', c_int), ('zlibStrategy', c_int), ('bzip2BlockSize', c_int), ('learningHeaderStrlen', c_int), ('oldData', c_void_p), ('oldDataSize', c_size_t), ('format', POINTER(NrrdFormat)), ('encoding', POINTER(NrrdEncoding)), ] class NrrdResampleAxis(Structure): pass NrrdResampleAxis._pack_ = 4 NrrdResampleAxis._fields_ = [ ('kernel', POINTER(NrrdKernel)), ('kparm', c_double * 8), ('min', c_double), ('max', c_double), ('samples', c_size_t), ('overrideCenter', c_int), ('center', c_int), ('sizeIn', c_size_t), ('sizePerm', c_size_t * 16), ('axIdx', c_uint), ('passIdx', c_uint), ('axisPerm', c_uint * 16), ('ratio', c_double), ('nrsmp', POINTER(Nrrd)), ('nline', POINTER(Nrrd)), ('nindex', POINTER(Nrrd)), ('nweight', POINTER(Nrrd)), ] NrrdResampleContext._pack_ = 4 NrrdResampleContext._fields_ = [ ('nin', POINTER(Nrrd)), ('verbose', c_int), ('boundary', c_int), ('typeOut', c_int), ('renormalize', c_int), ('roundlast', c_int), ('clamp', c_int), ('defaultCenter', c_int), ('nonExistent', c_int), ('padValue', c_double), ('dim', c_uint), ('passNum', c_uint), ('topRax', c_uint), ('botRax', c_uint), ('permute', c_uint * 17), ('passAxis', c_uint * 16), ('axis', NrrdResampleAxis * 17), ('flag', POINTER(c_int)), ('time', c_double), ] NrrdIter._pack_ = 4 NrrdIter._fields_ = [ ('nrrd', POINTER(Nrrd)), ('ownNrrd', POINTER(Nrrd)), ('val', c_double), ('size', c_size_t), ('data', STRING), ('left', c_size_t), ('load', CFUNCTYPE(c_double, c_void_p)), ] NrrdDeringContext._pack_ = 4 NrrdDeringContext._fields_ = [ ('verbose', c_int), ('linearInterp', c_int), ('verticalSeam', c_int), ('nin', POINTER(Nrrd)), ('center', c_double * 2), ('clampPerc', c_double * 2), ('radiusScale', c_double), ('thetaNum', c_uint), ('clampHistoBins', c_uint), ('rkernel', POINTER(NrrdKernel)), ('rkparm', c_double * 8), ('tkernel', POINTER(NrrdKernel)), ('tkparm', c_double * 8), ('cdataIn', STRING), ('cdataOut', STRING), ('sliceSize', c_size_t), ('clampDo', c_int), ('clamp', c_double * 2), ('ringMagnitude', c_double), ] __all__ = ['tenFiberStopUIntSet', 'biffMsgAddf', 'limnCameraPathTrack', 'gageVecLambda2', 'miteUserNix', 'tenFiberIntgLast', 'tenInterpTypeRThetaPhiLinear', 'ell_4m_post_mul_f', 'nrrdArithGamma', 'ell_Nm_check', 'tijk_refine_max_2d_f', 'pushEnergyCoulomb', 'tijk_class', 'tenEstimate1MethodMLE', 'nrrdHasNonExistOnly', 'nrrdFormatPNG', 'pullInterTypeLast', 'miteBiffKey', 'limnEdgeTypeLast', 'tenGageFAShapeIndex', 'tijk_approx_rankk_3d_f', 'baneMeasrUnknown', 'alanParmF', 'seekTypeMinimalSurface', 'pullIterParmLast', 'airRandMTStateNix', 'nrrdKernelCatmullRomD', 'pullInfoTangent1', 'pullInfoTangent2', 'alanParmK', 'gageContextCopy', 'miteValVdefTdotV', 'nrrdKernelC4Hexic', 'unrrdu_dnormCmd', 'pullEnergyCotan', 'tenEstimateContextNix', 'tenGageFAGeomTens', 'airEndsWith', 'miteUser', 'ell_4m_to_q_d', 'nrrdField_block_size', 'mossSamplerNew', 'gageVecSOmega', 'tenDoubleContract_d', 'pullInfoQuality', 'limnSpaceView', 'limnSplineMinT', 'nrrdResampleNonExistentRenormalize', 'nrrdFieldInfoParse', 'nrrdField_space', 'baneStateHistEqSmart', 'tenGageConfDiffusionAlign', 'gageSclK2', 'hestRespFileComment', 'nrrdApply2DLut', 'nrrdKind3DMaskedMatrix', 'tenGageNormGradMag', 'ell_q_exp_f', 'tijk_refine_max_3d_f', 'bane1DOpacInfoFrom2D', 'tijk_refine_max_3d_d', 'tijk_refine_max_2d_d', 'nrrdBoundaryWrap', 'seekLowerInsideSet', 'limnEdgeTypeBackFacet', 'ell_q_exp_d', 'tenInterpTypeLoxR', 'tijk_3o3d_unsym', 'pushBinPointAdd', 'nrrdFlip', 'airFP_SNAN', 'tenDwiGageTensorMLEError', 'nrrdKernelBlackmanD', 'pullInitMethodLast', 'seekContextNew', 'nrrdField_comment', 'limnSplineInfoSize', 'nrrdSimpleResample', 'tenInterpTypeLoxK', 'nrrdTernaryOpIfElse', 'tenGlyphTypePolarPlot', 'limnPolyDataCylinder', 'tendFiberStopCB', 'tenGageTraceDiffusionAlign', 'unrrdu_flipCmd', 'nrrdKeyValueSize', 'gageErrUnknown', 'limnPolyDataNeighborArray', 'nrrdBinaryOpSgnPow', 'nrrdKeyValueGet', 'nrrdKernelCatmullRomSupportDebugDD', 'limnQN16octa', 'nrrdKernelC3QuinticD', 'airTypeFloat', 'pullFlagBinSingle', 'nrrdField_line_skip', 'tenGlyphTypeBox', 'limnLightReset', 'ellPresent', 'nrrdField_space_units', 'gageSclShapeTrace', 'seekStrengthSet', 'pullCondEnergyTry', 'tenDwiGage2TensorPeled', 'nrrdKernelBSpline4DDD', 'tenGageTraceHessianEvec0', 'tenGageTraceHessianEvec1', 'nrrdMeasureMax', 'miteShadeMethodLast', 'echoMatterLightUnit', 'unrrdu_substCmd', 'limnHestPolyDataLMPD', 'baneGkms_hvolCmd', 'nrrdUnaryOpExists', 'nrrdSpace3DRightHandedTime', 'airArrayNix', 'nrrdArithIterBinaryOp', 'airFPClass_d', 'airNoDio_format', 'gageCtxFlagLast', 'dyeConvert', 'ell_q_3v_rotate_d', 'nrrdFormatPNM', 'gageParmOrientationFromSpacing', 'nrrdBasicInfoMeasurementFrame', 'limnQN8octa', 'limnPolyDataNix', 'tenInterpTypeWang', 'limnPolyDataOctahedron', 'unrrdu_envCmd', 'nrrdSpace3DLeftHanded', 'nrrdUnaryOpCbrt', 'nrrdAxisInfoLast', 'coilMethodTesting', 'tenFiberDirectionNumber', 'tijk_eval_efs_f', 'coilKind7Tensor', 'baneIncAnswer', 'hooverThreadBegin_t', 'airFastExp', 'tenExperSpecNix', 'pullStatusNixMe', 'tenGageQGradVec', 'tenGageOmegaLaplacian', 'pullTraceMultiNix', 'nrrdOriginStatusNoMin', 'nrrdResampleInputSet', 'tenGlyphType', 'pullInitMethodPointPerVoxel', 'tenFiberSingleTrace', 'tenGageOmegaDiffusionFraction', 'tijk_refine_rank1_3d_f', 'gageVecProjHelGradient', 'miteRangeChar', 'nrrdStateMeasureType', 'tijk_class_last', 'pullInfoLast', 'tenTripleConvert', 'hestElideSingleOtherDefault', 'gageSclHessianTen', 'echoTypeRectangle', 'nrrdUntile2D', 'nrrdSpaceLeftAnteriorSuperior', 'nrrdTypeULLong', 'airInsane_UCSize', 'limnSplineTypeSpecParse', 'limnSplineInfo4Vector', 'tenInterpParmBufferAlloc', 'nrrdResampleRangeSet', 'unrrdu_dhistoCmd', 'meetAirEnumAllCheck', 'tenFiberIntgRK4', 'pushEnergySpring', 'tenGageModeWarp', 'nrrdFormatUnknown', 'gageStackBlurParmScaleSet', 'elfMaximaParmSet', 'tenInterpTwoDiscrete_d', 'tenFiberStopMinNumSteps', 'nrrdKernelSprint', 'gageKindVolumeCheck', 'tenGageNormGradVec', 'pullFlagZeroZ', 'tenGageQHessian', 'airHeapNix', 'tenEstimate2MethodUnknown', 'limnPolyDataCone', 'unrrdu_distCmd', 'hestParmNew', 'tenEstimate2Method', 'nrrdUnaryOpAcos', 'tenModel1Stick', 'tendCmdList', 'airMopAlways', 'gageKindAnswerLength', 'nrrdMeasureSD', 'tenBMatrixCalc', 'nrrdLoad', 'miteVariableParse', 'pullCCSort', 'echoThreadStateNew', 'coilKindTypeLast', 'mossMatTranslateSet', 'nrrdBinaryOpGT', 'tenDefFiberMaxHalfLen', 'tenGlyphBqdEvalUv', 'banePosCalc', 'tijk_3o2d_sym', 'airFloatQNaN', 'limnPolyDataIcoSphere', 'tenEigensolve_f', 'pullEnergyTypeZero', 'tenEigensolve_d', 'meetNrrdKernelAllCheck', 'tenGageQGradMag', 'gageDefStackNormalizeDeriv', 'nrrdArithUnaryOp', 'tenGageCp1HessianEvec2', 'tenGageCp1HessianEvec1', 'tenGageCp1HessianEvec0', 'pullTask_t', 'echoColorSet', 'pullConstraintFailTravel', 'nrrdEnvVarStateMeasureModeBins', 'tenFiberMultiProbeVals', 'miteRayBegin', 'alanParmNumThreads', 'pullFlagSet', 'gageParmDefaultCenter', 'unrrdu_ccmergeCmd', 'miteThreadNew', 'miteStage', 'airDrandMT53_r', 'airMopNever', 'tenFiberTypeEvec1', 'tenFiberTypeEvec0', 'tenFiberTypeEvec2', 'gageVecCurlNorm', 'ell_4m_to_q_f', 'ell_3v_print_d', 'nrrdMeasureHistoMedian', 'ell_3v_print_f', 'pullSysParmEnergyDecreasePopCntlMin', 'unrrdu_ccsettleCmd', 'tenGageCa1HessianEvec', 'tenGageFADiffusionFraction', 'gageStackBlurParm', 'limnPolyDataSpiralSuperquadric', 'seekTypeRidgeSurfaceT', 'nrrdBasicInfoSpaceOrigin', 'airUnescape', 'airEnumPrint', 'tenGageCa1HessianEval', 'alanRun', 'pullProcessModeNixing', 'airFPGen_f', 'airFPGen_d', 'airInsane_FltDblFPClass', 'pullInfoSpecNew', 'nrrdBinaryOpIf', 'nrrdAxisInfoSpacing', 'airExists', 'pullInfoLiveThresh', 'gageStackBlurParmNix', 'tenDWMRINexKeyFmt', 'pullInfoInside', 'tijk_approx_heur_3d_f', 'nrrdBinaryOpLTE', 'gageErrStackUnused', 'nrrdZeroSet', 'nrrdSpaceLeftAnteriorSuperiorTime', 'unrrduScaleUnknown', 'nrrdRangePercentileFromStringSet', 'limnPolyDataCopy', 'coilMethodArray', 'gageDeconvolveSeparableKnown', 'coilMethodTypeLast', 'tijk_zero_f', 'nrrdAxisInfoUnknown', 'tijk_zero_d', 'meetHestConstGageKind', 'miteThreadBegin', 'nrrdKernelBSpline2DD', 'nrrdTernaryOpMinSmooth', 'gageShapeItoW', 'miteThreadNix', 'limnPrimitiveLines', 'pullSysParmBeta', 'unrrduCmdList', 'nrrdUnaryOpNerf', 'pushTask', 'gageShapeCopy', 'tenFiberVerboseSet', 'nrrdSanity', 'nrrdCCFind', 'nrrdUnaryOpTan', 'tijk_copy_f', 'tenAniso_RA', 'pullCountLast', 'tenGageRHessian', 'gageKindCheck', 'airNoDio_size', 'nrrdBoundaryWeight', 'pullEnergyZero', 'gagePerVolumeNew', 'alanParmDiffB', 'pullFlagPopCntlEnoughTest', 'alanParmDiffA', 'pushBinDone', 'pullInfoLiveThresh2', 'pullInfoLiveThresh3', 'nrrdAxesSwap', 'gageOptimSigTruthSet', 'gageItemSpec', 'baneMakeHVol', 'gageSclHessRidgeness', 'mossFlagLast', 'nrrdDefaultWriteBareText', 'airShuffle_r', 'limnObjectRender', 'miteValXw', 'nrrdSpacingStatusScalarWithSpace', 'tenDWMRIModalityKey', 'pullFlag', 'gageSclK1', 'miteValXi', 'gageParmStackNormalizeDerivBias', 'alanContext_t', 'nrrdOriginStatusDirection', 'baneMeasrValuePositive', 'baneSigmaCalc', 'nrrdWrite', 'pullInfoIsovalue', 'nrrdAlloc_va', 'gageItemPackPartHessEval1', 'gageItemPackPartHessEval0', 'gageItemPackPartHessEval2', 'seekDescendToRidge', 'tenFiberParmStepSize', 'pullPointInitializeRandomOrHalton', 'limnObjectSpaceTransform', 'nrrdKeyValueErase', 'limnPolyDataPrimitiveArea', 'nrrdResampleTypeOutSet', 'unrrduHestScaleCB', 'nrrdArithBinaryOp', 'meetHestPullVol', 'nrrdZlibStrategyDefault', 'baneClipUnknown', 'pushOutputGet', 'tenEMBimodalParmNix', 'tenAniso_Cp1', 'nrrdIterNix', 'tenAniso_Cp2', 'airMopSingleOkay', 'tijk_refine_rankk_parm_new', 'nrrdUIInsert', 'limnQN15octa', 'tend_shrinkCmd', 'tend_expCmd', 'ell_3m_svd_d', 'limnQN9octa', 'unrrdu_axinsertCmd', 'airBesselIn', 'baneHVolParmNew', 'pullCCMeasure', 'gageDefGenerateErrStr', 'nrrdEnvVarDefaultSpacing', 'nrrdRangePercentileSet', 'tenGradientParmNew', 'tijk_esh_to_3d_sym_f', 'nrrdTypeUChar', 'airNoDio_ptr', 'pullPropStepConstr', 'tijk_esh_to_3d_sym_d', 'airHeapFrontPeek', 'pushBin_t', 'airTypeLast', 'gageVecMGFrob', 'miteStageOpLast', 'tenDwiGageConfidence', 'ell_cubic_root_last', 'biffGet', 'gageProbe', 'nrrdMeasureL2', 'nrrdMeasureL1', 'alanParmConstantFilename', 'miteShadeMethodLitTen', 'nrrdCenterUnknown', 'limnHestCameraOptAdd', 'echoTypeInstance', 'airBesselI1', 'seekSamplesSet', 'nrrdIterValue', 'nrrdKind2DMaskedMatrix', 'pullPhistEnabled', 'airSgnPow', 'nrrdBasicInfoOldMin', 'airMopUnMem', 'gageVecHelicity', 'gageSclHessFrob', 'airThreadNew', 'tenGageOmegaHessianEval2', 'tenGageOmegaHessianEval1', 'tenGageOmegaHessianEval0', 'nrrdAxisInfoLabel', 'nrrdSpacingStatusScalarNoSpace', 'tend_glyphCmd', 'nrrdHistoEq', 'limnSpline_t', 'biffDone', 'tenDWMRIBmatKeyFmt', 'tenGage', 'nrrdBinaryOpExists', 'tenEigenvalueMultiply', 'alanTextureTypeLast', 'tenGageTraceHessianEvec', 'seekDescendToDegCell', 'echoGlobalStateNix', 'limnLook', 'unrrdu_w2iCmd', 'limnLightSet', 'baneGkms_scatCmd', 'pullInfoSpecNix', 'tenModelParmDesc', 'nrrdUnaryOpExp', 'tijk_refine_rank1_parm_new', 'baneRangeNix', 'limnObjectReadOFF', 'pullInfoIsovalueGradient', 'tenGradientParm', 'pullSysParmFracNeighNixedMax', 'hooverContextNew', 'elfGlyphPolar', 'limnPolyDataVertexWindingFlip', 'nrrdBoundaryBleed', 'ell_cubic_root_unknown', 'pullPointNew', 'nrrdArithAffine', 'meetHestPullInfo', 'nrrdKernelCos4SupportDebugDDD', 'elfMaximaContext', 'echoJitterUnknown', 'nrrdResampleRenormalizeSet', 'ell_aa_to_q_f', 'ell_aa_to_q_d', 'baneRawScatterplots', 'nrrdKernelBSpline7D', 'limnSplineNrrdCleverFix', 'pullSysParmEnergyIncreasePermit', 'miteValGageKind', 'nrrdEnvVarDefaultWriteEncodingType', 'pullInfoHeightLaplacian', 'echoSuperquadSet', 'nrrdSpaceOriginSet', 'nrrdResampleInfoNew', 'seekItemHessSet', 'pushEnergySpecNix', 'limnPolyDataSquare', 'dyeLUVtoXYZ', 'tenAnisoUnknown', 'pullFlagNixAtVolumeEdgeSpace', 'gageItemPack', 'nrrdKernelCentDiff', 'miteThread', 'tenGageFAHessianEvec', 'limnSplineInfoLast', 'nrrdMeasureLineSlope', 'pullLogAddSet', 'tenFiberKernelSet', 'nrrdTypeIsUnsigned', 'nrrdHistoCheck', 'gageStackBlurParmCheck', 'pullIterParmSet', 'airNoDio_setfl', 'tend_anhistCmd', 'ell_3m_print_f', 'nrrdKernelBSpline7DDD', 'ell_3m_print_d', 'gageSclGeomTensTen', 'gagePvlFlagNeedD', 'nrrdAxisInfoCompare', 'nrrdBasicInfoComments', 'echoSuperquad', 'nrrdKind3DMaskedSymMatrix', 'limnPolyDataTransform_f', 'tenGlyphTypeCylinder', 'tenEstimate1TensorSimulateSingle_f', 'tenEstimate1TensorSimulateSingle_d', 'nrrdHistoAxis', 'tenDWMRINAVal', 'tenDwiGageTensorWLS', 'tenInterpTypeQuatGeoLoxR', 'limnPrimitiveNoop', 'tenGageTensorGradMag', 'pullIterParmMax', 'tenGageCp1GradMag', 'nrrdSpaceVecExists', 'echoJittableLast', 'nrrdCenterNode', 'nrrdJoin', 'echoCylinder', 'airUIrandMT_r', 'nrrdDStore', 'mossHestOrigin', 'tenGageFAKappa2', 'nrrdUnaryOpLast', 'tenGageFAKappa1', 'unrrdu_imapCmd', 'tijk_max_esh_order', 'tenDwiGageTensorLLSErrorLog', 'limnPrimitive', 'nrrdKind2DSymMatrix', 'tenAniso_Ca2', 'pullFinish', 'tenAniso_Ca1', 'tenGageModeHessian', 'tijk_approx_heur_3d_d', 'nrrdKernelC5SepticApproxInverse', 'mossMatApply', 'tenInvariantGradientsK_d', 'NrrdResampleInfo', 'gageDefDefaultCenter', 'meetTeemLibs', 'airTypeUInt', 'coilKindScalar', 'tenInterpTypeLinear', 'unrrduCmd', 'limnVertex', 'nrrdCheck', 'pullCCFind', 'limnObjectVertexNumPreSet', 'tenTripleCalc', 'gageParmRenormalize', 'coilMethodTypeSelf', 'airBesselI0', 'nrrdKindUnknown', 'ell_cubic_root', 'tenEstimateMethodSet', 'nrrdUnaryOpErf', 'nrrdResampleNonExistentNoop', 'nrrdSliceSelect', 'airNoDio_arch', 'tijk_4o3d_sym', 'tenInterpPathLength', 'echoMatterGlassFuzzy', 'pullTraceStop', 'meetPullVolNew', 'nrrdRangeSafeSet', 'pullCount', 'tenInterpParmNew', 'airCbrt', 'airTypeEnum', 'nrrdApply1DRegMap', 'tend_mconvCmd', 'tenGageCl2', 'tenGageCl1', 'pullFlagScaleIsTau', 'tenFiberStopMinLength', 'nrrdKernelBlackman', 'echoInstance', 'nrrdMeasureHistoMax', 'limnPolyDataPlane', 'limnSplineSample', 'pullPropIdCC', 'nrrdFormatText', 'tend_evalmultCmd', 'nrrdResampleNrrdSet', 'nrrdEnvVarDefaultWriteValsPerLine', 'pullCondConstraintSatB', 'miteStageOpMax', 'pullCondConstraintSatA', 'airArrayNew', 'nrrdKernelHann', 'ell_q_pow_d', 'nrrdBinaryOpGTE', 'ell_q_pow_f', 'pullCountCC', 'tenFiberAnisoSpeedSet', 'pushFinish', 'tenGageFAGradVecDotEvec0', 'tenDwiGageKindNix', 'nrrdKindPoint', 'pullFlagUnknown', 'limnPolyDataPolygonNumber', 'mossMatRightMultiply', 'tenBiffKey', 'pullTraceStopSpeeding', 'nrrdFormatType', 'nrrdDistanceL2Biased', 'nrrdUnaryOpLog', 'limnPolyDataWriteLMPD', 'tenGageTraceHessianEvec2', 'nrrdTernaryOpLerp', 'pullCondNew', 'nrrdFInsert', 'limnCameraPathTrackFrom', 'meetGageKindParse', 'hestRespFileFlag', 'baneDefMakeMeasrVol', 'nrrdMeasureMode', 'nrrdDefaultWriteValsPerLine', 'gageSclLaplacian', 'tenGlyphParm', 'nrrdConvert', 'biffMaybeAddf', 'tenGlyphTypeSuperquad', 'nrrdFormatTypeText', 'miteValYw', 'airDioRead', 'unrrdu_minmaxCmd', 'pullEnergyTypeButterworth', 'hooverContext', 'echoMatterLast', 'airHeapFrontUpdate', 'tenEstimateSkipReset', 'miteValYi', 'tenGageOmegaDiffusionAlign', 'nrrdMeasureHistoSD', 'tenEstimateContextNew', 'tenAnisoPlot', 'nrrdIoStateInit', 'nrrdKernelCatmullRom', 'dyeHSVtoRGB', 'nrrdEnvVarStateMeasureHistoType', 'pullVerboseSet', 'nrrdAxisInfoKind', 'airTime', 'limnFace', 'limnPolyDataInfoNorm', 'echoType', 'tenGageQNormal', 'limnPolyDataInfoTex2', 'nrrdHisto', 'baneHVolParmNix', 'mossMatRotateSet', 'tenEvecRGBParm', 'tijk_refine_rankk_parm_nix', 'tenFiberContextNix', 'nrrdHasNonExistFalse', 'tenDWMRISkipKeyFmt', 'miteVariablePrint', 'tenFiberParmUseIndexSpace', 'nrrdKind3Gradient', 'pushEnergyTypeUnknown', 'gageAnswerPointer', 'coilOutputGet', 'nrrdKeyValueClear', 'pushEnergyZero', 'nrrdCenter', 'baneHVolParmAxisSet', 'airBesselI1ExpScaled', 'nrrdKernelC3Quintic', 'pullProcessModeLast', 'gageShapeNix', 'echoJittableNormalA', 'echoJittableNormalB', 'nrrdResampleDefaultCenterSet', 'pullPropForce', 'tenGageSNormal', 'miteShadeSpecNix', 'nrrdTypeShort', 'mossVerbose', 'baneHVolParmClipSet', 'ell_cubic_root_single', 'gageShapeReset', 'tenGageBGradMag', 'tenGlyphBqdZoneEval', 'limnCameraPathTrackUnknown', 'nrrdDeringClampPercSet', 'ell_3m_1d_nullspace_d', 'nrrdGetenvInt', 'nrrdKindIsDomain', 'ell_3mv_mul_d', 'dyeXYZtoLUV', 'ell_3mv_mul_f', 'nrrdType', 'echoThreadStateInit', 'baneHVolParmGKMSInit', 'airExp', 'NrrdResampleAxis', 'nrrdAxisInfoCenter', 'hestElideSingleOtherType', 'tenEMBimodalParm', 'tenInterpTypeQuatGeoLoxK', 'echoAABBox', 'NrrdIter', 'gageParmVerbose', 'airThreadMutexNew', 'tenGageTensorGrad', 'limnHestSplineTypeSpec', 'pullPropGet', 'limnSplineTypeUnknown', 'nrrdUnaryOpExpm1', 'tend_unmfCmd', 'tenMeasurementFrameReduce', 'nrrdUnaryOpNegative', 'NrrdIoState', 'airErfc', 'tenGlyphBqdZoneUv', 'airNoDio_dioinfo', 'gageItemPackPartScalar', 'pullTraceMultiRead', 'pushBiffKey', 'biffSetStr', 'nrrdBinaryOpDivide', 'nrrdDeringCenterSet', 'nrrdFprint', 'biffMsg', 'elfBallStickODF_f', 'nrrdMeasureVariance', 'nrrdHistoThresholdOtsu', 'airEnumVal', 'tenExperSpecNew', 'nrrdSpaceLeftPosteriorSuperiorTime', 'airHeapFrontPop', 'airTypeStr', 'tenFiberStopOn', 'hooverErrSample', 'gageStackBlurCheck', 'pullInfoNegativeTangent1', 'pullInfoNegativeTangent2', 'nrrdSprint', 'hooverBiffKey', 'nrrdKernelCompare', 'gageOptimSigPlot', 'nrrdTypeMax', 'pullInterTypeUnivariate', 'gageParmLast', 'pullPropUnknown', 'airParseStrF', 'airParseStrD', 'airParseStrE', 'airParseStrB', 'airParseStrC', 'nrrdDistanceL2', 'nrrdEnvVarStateKeyValuePairsPropagate', 'gageSclMeanCurv', 'tend_evalclampCmd', 'tenModel1Cylinder', 'airParseStrI', 'nrrdKeyValueAdd', 'airParseStrS', 'nrrdTernaryOpMin', 'unrrdu_convertCmd', 'limnEdge', 'tenGageEval1', 'tenGageEval0', 'tenAniso_Cs1', 'dyePresent', 'tenGageAniso', 'tenDwiGageTensor', 'echoRTRender', 'gageItemPackPartNormal', 'dyeXYZtoRGB', 'alanStopUnknown', 'gageKindTotalAnswerLength', 'miteStageOpAdd', 'dyeSpaceLUV', 'nrrdKernelGaussianD', 'pullInfoTensor', 'airAtod', 'nrrdField_number', 'tenGageModeGradMag', 'tijk_esh_make_kernel_delta_d', 'tenAniso_Conf', 'airHeapNew', 'tijk_incr_f', 'nrrdDeringInputSet', 'nrrdByteSkip', 'nrrdBasicInfoData', 'baneBcptsCheck', 'gageKernelReset', 'airThreadCapable', 'seekContext', 'nrrdFormatTypeLast', 'dyeColorCopy', 'nrrdEnvVarStateVerboseIO', 'gageDefVerbose', 'tenGageDelNormR1', 'tenGageDelNormR2', 'miteNtxfCheck', 'pullTraceMultiWrite', 'tenAniso_Omega', 'gageStackBlurParmNew', 'pushEnergySpecSet', 'hooverStubThreadBegin', 'unrrdu_1opCmd', 'pullTraceStopUnknown', 'nrrdBinaryOpNormalRandScaleAdd', 'alanParmSaveInterval', 'tenGageFACurvDir1', 'echoMatterMetal', 'hooverErrThreadCreate', 'tenGageCl1HessianEvec2', 'tenGageCl1HessianEvec1', 'tenGageCl1HessianEvec0', 'pullEnergyUnknown', 'nrrdZlibStrategyHuffman', 'nrrdDLookup', 'ell_q_3v_rotate_f', 'nrrdDInsert', 'pullSourceProp', 'tenModelFromAxisLearn', 'tenGageCa1', 'ell_3m_eigenvalues_d', 'hooverDefVolCentering', 'gageVecHessian', 'gageParmStackNormalizeDeriv', 'limnObject', 'pullIterParm', 'tenFiberStopFraction', 'airToLower', 'nrrd1DIrregAclCheck', 'unrrdu_joinCmd', 'elfKernelStick_f', 'miteValZw', 'airThreadCond', 'tendTitle', 'pullSysParmWall', 'tenMake', 'unrrdu_makeCmd', 'miteValZi', 'ell_cubic', 'tenGageClpmin2', 'nrrdResampleNonExistentWeight', 'nrrdUnaryOpCeil', 'tenGageClpmin1', 'limnObjectPreSet', 'gageShape', 'tijk_copy_d', 'tenRotateSingle_f', 'nrrdCCValid', 'mossMatIdentitySet', 'tenDefFiberIntg', 'limnCameraNix', 'pullTraceMulti', 'nrrdKernelBSpline4DD', 'tenDwiFiberType2Evec0', 'pullStatusNewbie', 'tenMakeSingle_f', 'tenGageCa1HessianEval1', 'tenGageCa1HessianEval0', 'nrrdUILookup', 'tenGageCa1HessianEval2', 'tenEstimate1MethodWLS', 'unrrdu_axsplitCmd', 'nrrdAxisInfoPos', 'hooverErrNone', 'tend_fiberCmd', 'ell_Nm_pseudo_inv', 'nrrdIoStateNix', 'echoObjectAdd', 'pullEnergyQuartic', 'gageVecVector0', 'tenBVecNonLinearFit', 'hooverSample_t', 'tenEstimateNegEvalShiftSet', 'nrrdCCAdjacency', 'mossDefBoundary', 'pullTask', 'airEnumDesc', 'tijk_add_f', 'baneIncNix', 'nrrdWrap_va', 'nrrdStateKeyValueReturnInternalPointers', 'unrrduHestBitsCB', 'limnSplineNew', 'miteValGTdotV', 'pushPtrPtrUnion', 'gageItemPackPart', 'nrrdKindTime', 'meetPresent', 'airArrayLenPreSet', 'nrrdMeasure', 'tenModelSqeFit', 'airParseStrZ', 'airMopAdd', 'alanPresent', 'limnObjectEmpty', 'coilMethodType', 'airEqvAdd', 'ell_3m_inv_d', 'tenGageEval2', 'pullProgressBinModSet', 'nrrdKernelCos4SupportDebugD', 'miteValNormal', 'tijk_eval_efs_basis_f', 'tijk_eval_efs_basis_d', 'limnQN12octa', 'tend_evalpowCmd', 'unrrdu_lutCmd', 'tenDwiGageUnknown', 'miteValTw', 'tenFiberIntg', 'miteValTi', 'nrrdFormatTypeEPS', 'tijk_negate_f', 'tend_tripleCmd', 'dyeColorParse', 'airIndexClamp', 'nrrdEncodingTypeLast', 'nrrdTypeDefault', 'baneHVolParm', 'pullBin_t', 'tijk_init_max_3d_f', 'nrrdEncodingTypeAscii', 'coil_t', 'tenAniso_FA', 'tenGageFAHessian', 'gageStackBlurGet', 'limnPolyDataReadOFF', 'gageSclValue', 'airEqvSettle', 'echoIsosurface', 'echoMatterPhongSp', 'nrrdField_max', 'seekTypeRidgeSurfaceOP', 'nrrdFLookup', 'mossPresent', 'ell_q_to_3m_f', 'unrrdu_diceCmd', 'pushBinInit', 'hestNoArgsIsNoProblem', 'limnPolyDataInfoLast', 'airHeapFromArray', 'nrrdResampleSamplesSet', 'gageVecHelGradient', 'miteShadeMethodNone', 'unrrdu_ccfindCmd', 'gagePvlFlagUnknown', 'nrrdIterContent', 'tenEstimateLinear3D', 'gageContextNix', 'nrrdRangeCopy', 'pullPositionHistoryGet', 'alan3DSizeSet', 'tenFiberStopLast', 'tenModelConvert', 'tenModelZero', 'nrrdKernelBSpline5D', 'hooverStubSample', 'biffMsgErrNum', 'gagePerVolumeDetach', 'gageScl', 'nrrdResampleNonExistentUnknown', 'airBool', 'tend_makeCmd', 'nrrdBinaryOpSubtract', 'limnSplineNix', 'tenEvecRGB', 'hestGlossary', 'nrrdUILoad', 'pullHestEnergySpec', 'airNull', 'nrrdField_keyvalue', 'unrrdu_2opCmd', 'gageAnswerLength', 'airTypeOther', 'nrrdKernelBSpline3DD', 'nrrdIoStateFormatSet', 'pullPropIdtag', 'ell_3v_angle_d', 'unrrdu_i2wCmd', 'ell_3v_angle_f', 'airEnumUnknown', 'nrrdFormatVTK', 'echoRayIntx', 'pullPointScalar', 'nrrdField_space_dimension', 'limnObjectPartAdd', 'tenEvecRGBParmCheck', 'limnSplineNrrdEvaluate', 'tijk_incr_d', 'limnPolyData', 'seekBiffKey', 'nrrdDefaultResampleCheap', 'airOneLine', 'nrrdKernelForwDiff', 'tijk_get_axis_type', 'miteShadeSpecNew', 'hestCB', 'tenEstimate2MethodLast', 'tenAniso_eval2', 'tenAniso_eval0', 'tenAniso_eval1', 'baneClipNix', 'pullTraceStopConstrFail', 'ell_4m_det_f', 'ell_4m_det_d', 'nrrdFFTWPlanRigorLast', 'tenEstimate2MethodPeled', 'gageVecNCurlNormGrad', 'nrrdDefaultSpacing', 'unrrdu_unorientCmd', 'hestColumns', 'alanStopConverged', 'tenFiberTraceSet', 'nrrdApplyMulti1DLut', 'nrrdCCMerge', 'nrrdMeasureHistoMode', 'nrrdBinaryOpNotEqual', 'pullVolumeSingleAdd', 'nrrdBinaryOpMultiply', 'gageParmGenerateErrStr', 'pullConstraintFailLast', 'tenInterpTypeUnknown', 'nrrdAxisInfoSpaceDirection', 'biffMaybeAdd', 'tijk_esh_len', 'hooverContextNix', 'nrrdSpatialResample', 'pullPointNix', 'echoSplit', 'dyeColorGetAs', 'tenDefFiberStepSize', 'echoObjectNix', 'pullEnergySpecNew', 'coilContext', 'pushEnergyTypeLast', 'pullSysParmNeighborTrueProb', 'nrrdBiffKey', 'limnPrimitiveQuads', 'limnPolyDataVertexNormals', 'limnLightNix', 'unrrdu_untileCmd', 'gageVecCurl', 'nrrdInset', 'pullInitParm', 'dyeLABtoXYZ', 'nrrdBinaryOpMod', 'pullSysParmTheta', 'limnSplineNumPoints', 'hestOptAdd', 'gageSclNProj', 'nrrdTypeMin', 'airStrdup', 'echoRoughSphereNew', 'airThreadMutexLock', 'nrrdInit', 'gageKernelStack', 'tenGradientIdealEdge', 'nrrdGetenvBool', 'nrrdIoStateEncodingGet', 'alanDimensionSet', 'baneOpacCalc', 'dyeSpaceRGB', 'tijk_refine_rankk_3d_d', 'baneClip', 'tijk_refine_rankk_3d_f', 'tenTripleCalcSingle_f', 'miteThread_t', 'limnBiffKey', 'tijk_2o3d_unsym', 'alanContextNix', 'pullEnergyTypeCubicWell', 'pullEnergyQuarticWell', 'echoJitterJitter', 'baneMeasrLaplacian', 'limnObjectVertexAdd', 'nrrdBinaryOpLT', 'unrrdu_acropCmd', 'coilKindType', 'pullSysParmProbeProb', 'airStrtokQuoting', 'pullInfoHeightGradient', 'pullPropStability', 'coilMethod', 'gageSclMedian', 'miteQueryAdd', 'limnPolyDataClipMulti', 'airTypeSize', 'pullSysParmGamma', 'airStrcmp', 'airStrlen', 'tenGageCp1HessianEval', 'echoMatterUnknown', 'unrrdu_saveCmd', 'gageSclGradVec', 'tenDwiFiberType', 'tijk_init_rank1_2d_d', 'tijk_init_rank1_2d_f', 'tenLog', 'tenGageCp1HessianEval2', 'tenGageCp1HessianEval0', 'tenGageCp1HessianEval1', 'nrrdSpaceDimension', 'miteDefNormalSide', 'nrrdTernaryOpGTSmooth', 'unrrdu_axmergeCmd', 'pullSourceLast', 'tijk_type_t', 'pullInfoSpec', 'nrrdFStore', 'biffAdd', 'tijk_scale_f', 'tenGradientDistribute', 'tijk_scale_d', 'nrrdKind', 'nrrdValCompare', 'gagePvlFlagQuery', 'pullTraceMultiPlotAdd', 'nrrdIterSetValue', 'limnCameraPathMake', 'unrrdu_affineCmd', 'unrrduScaleAdd', 'nrrdHasNonExistUnknown', 'elfBallStickPredict_f', 'miteRenderEnd', 'miteRender', 'nrrdBinaryOpFlippedSgnPow', 'pullPropNeighCovarDet', 'limnObjectWorldHomog', 'tijk_6o3d_sym', 'tenGradientParmNix', 'tenGageEvalHessian', 'nrrdKernelC4HexicApproxInverse', 'nrrdBinaryOpEqual', 'limnQN10checker', 'mitePresent', 'tenFiberStopAnisoSet', 'pullTraceMultiFilterConcaveDown', 'meetHestGageKind', 'tenAnisoTen_d', 'tenPowSingle_d', 'tenAnisoTen_f', 'echoMatterPhongSet', 'tenGageFACurvDir2', 'nrrdTernaryOpMultiply', 'mossImageCheck', 'tenFiberMultiNix', 'nrrdBinaryOpAdd', 'nrrdTernaryOpExists', 'limnObjectCubeAdd', 'nrrdKindVector', 'echoRTParm', 'tijkPresent', 'limnLightAmbientSet', 'gageShapeSet', 'tenDefFiberUseIndexSpace', 'tijk_esh_sp_f', 'nrrdBlind8BitRangeFalse', 'tenGageDetGradVec', 'airFP_NEG_INF', 'hestOptFree', 'tenGageModeHessianEval', 'echoJitterGrid', 'nrrdIoStateBzip2BlockSize', 'limnQN12checker', 'hooverErrRayBegin', 'airFPPartsToVal_f', 'hestVerbosity', 'alanParmMaxIteration', 'nrrdDefaultWriteEncodingType', 'limnLight', 'tenAnisoVolume', 'echoMatterGlassKa', 'airHalton', 'echoMatterGlassKd', 'nrrdAxisInfoCopy', 'NrrdRange', 'airMyDio', 'tenBMatrixCheck', 'limnObjectDescribe', 'nrrdBinaryOpRicianRand', 'pushPoint_t', 'nrrdIoStateSet', 'tenFiberContextCopy', 'tend_helixCmd', 'echoTypeIsosurface', 'tenEstimate1MethodUnknown', 'coilKindTypeScalar', 'echoMatterLightPower', 'nrrdSpaceRightAnteriorSuperior', 'gageVecCurlNormGrad', 'seekEvalDiffThreshSet', 'gageVecNormHelicity', 'pullSysParmBinWidthSpace', 'nrrdContentSet_va', 'pullConstraintFail', 'tenFiberProbeItemSet', 'baneClipTopN', 'nrrdEncodingAscii', 'hooverErrRenderEnd', 'echoRectangleSet', 'ell_4v_norm_f', 'nrrdKernelCos4SupportDebug', 'ell_q_avgN_d', 'airStdout', 'airThreadCondNix', 'baneMeasrValueAnywhere', 'alanParmDeltaT', 'airInsane_not', 'tenExp', 'miteValVdefT', 'tenModel_t', 'alanParmDeltaX', 'tenFiberParmSet', 'pullEnergyBetterCubicWell', 'airTypeChar', 'airRandInt_r', 'nrrdResampleKernelSet', 'tenGageRNormal', 'limnPolyDataReadLMPD', 'tijk_refine_rank1_parm', 'nrrdDefaultWriteCharsPerLine', 'nrrdMaybeAlloc_va', 'nrrdStateDisableContent', 'nrrdKindQuaternion', 'nrrdNonSpatialAxesGet', 'tenExperSpecGradSingleBValSet', 'tenDwiGageTensorErrorLog', 'limnPolyDataInfo', 'airFP_QNAN', 'tijk_axis_info_t', 'nrrdProject', 'baneRangePositive', 'nrrdField_endian', 'nrrdKindCovariantVector', 'baneClipAnswer', 'ell_4v_print_d', 'ell_4v_print_f', 'nrrdEncodingTypeGzip', 'gageParm', 'tenGageOmegaHessian', 'unrrdu_lut2Cmd', 'alanBiffKey', 'limnWindowNix', 'nrrdEnvVarDefaultCenter', 'unrrdu_3opCmd', 'tijk_esh_convolve_f', 'tenEvqVolume', 'nrrdEncodingTypeRaw', 'ell_aa_to_4m_f', 'nrrdKernelBSpline4D', 'nrrdSpaceDimensionSet', 'tijk_type', 'nrrdFormatEPS', 'unrrduScaleLast', 'gageContext', 'gageCtxFlagKernel', 'tend_evaladdCmd', 'limnObjectDepthSortParts', 'tijk_approx_heur_2d_f', 'pullInfoLen', 'nrrdKindSize', 'pullSourceUnknown', 'limnPrimitiveTriangleStrip', 'airThreadMutexNix', 'tenGradientCheck', 'tenSimulateSingle_f', 'tenGageFATotalCurv', 'tijk_4o2d_sym', 'airRandInt', 'pushHestEnergySpec', 'nrrdAlloc_nva', 'miteRayEnd', 'airFloatNegInf', 'nrrdKernelSpecSprint', 'limnVtoQN_f', 'limnVtoQN_d', 'tijk_approx_heur_2d_d', 'miteRangeSP', 'pullStatusLast', 'airThreadCondSignal', 'airEndianBig', 'nrrdUnaryOpReciprocal', 'biffMsgStrSet', 'hestOptCheck', 'hooverErr', 'hooverRayEnd_t', 'echoTriMeshSet', 'nrrdIterSetOwnNrrd', 'limnEnvMapFill', 'unrrdu_aboutCmd', 'tijk_init_max_2d_f', 'dyeSpaceLAB', 'nrrdIoStateNew', 'tenTripleType', 'unrrdu_insetCmd', 'nrrdSample_nva', 'airTypeSize_t', 'tenGradientRandom', 'tenAniso_VF', 'tijk_2o3d_asym', 'seekDataSet', 'unrrdu_histaxCmd', 'tenInterpParm', 'limnPolyDataVertexNormalsNO', 'pullCountConstraintSatisfy', 'tenInterpParmCopy', 'tijk_approx_rankk_2d_f', 'gageBiffKey', 'gageSclCurvDir2', 'tenGageRotTans', 'gageSclCurvDir1', 'nrrdSpaceLeftPosteriorSuperior', 'baneIncLast', 'alanTensorSet', 'nrrdHasNonExistTrue', 'gageProbeSpace', 'baneAxis', 'limnSplineInfo', 'pullEnergyTypeLast', 'nrrdIoStateCharsPerLine', 'NrrdEncoding_t', 'tenGageCa2', 'pullEnergyBspln', 'pullCountForceFromImage', 'ell_4m_pre_mul_f', 'elfMaximaRefineSet', 'tenMakeSingle_d', 'dyeBiffKey', 'miteVal', 'nrrdAxisInfoSpacingSet', 'tenGageDet', 'baneMeasrValueZeroCentered', 'tenAniso_Ct1', 'tenGageModeGradVec', 'tenAniso_Ct2', 'limnEdgeTypeBackCrease', 'limnPolyDataJoin', 'hooverDefImgCentering', 'hooverErrThreadJoin', 'airPrettySprintSize_t', 'airFree', 'tijk_class_tensor', 'unrrdu_jhistoCmd', 'hestRespFileEnable', 'nrrdSpaceSet', 'pullCountProbe', 'limnPolyDataNeighborList', 'limnSplineEvaluate', 'hooverStubRenderBegin', 'tijk_refine_rank1_3d_d', 'biffSetStrDone', 'pullInfoStrength', 'gageKernel10', 'gageKernel11', 'tenFiberTypeTensorLine', 'airFPFprintf_f', 'airFPFprintf_d', 'limnSpaceUnknown', 'tenAniso_Mode', 'gageQueryItemOn', 'nrrdILoad', 'tenGageFANormal', 'pullPropPosition', 'gageVecVector', 'tenExpSingle_d', 'airMopPrint', 'tenExpSingle_f', 'Nrrd', 'tenInterpDistanceTwo_d', 'ell_4m_to_aa_f', 'gageOptimSigParm', 'ell_4m_to_aa_d', 'tenPowSingle_f', 'alanParmBeta', 'airFP_Last', 'unrrdu_basinfoCmd', 'limnPolyDataColorSet', 'tenGageFAFlowlineCurv', 'echoRTRenderCheck', 'nrrdKernelCatmullRomSupportDebug', 'nrrdAxisInfoMin', 'hestParseOrDie', 'echoJittableMotionA', 'pullInitMethodGivenPos', 'echoJittableMotionB', 'echoInstanceSet', 'gageStructureTensor', 'nrrdOrientationReduce', 'tenGageRGradMag', 'alanTextureTypeUnknown', 'tenGageBGradVec', 'tenDwiGage2TensorQSegAndError', 'ell_aa_to_4m_d', 'tenGageB', 'nrrdPad_nva', 'nrrdKeyValueCopy', 'pullStatusStuck', 'baneRangeNew', 'tenGageS', 'tenGageR', 'tenGageQ', 'tenGageTheta', 'miteShadeSpec', 'nrrdSave', 'gageSclGaussCurv', 'tenEMBimodal', 'limnObjectFaceAdd', 'nrrdUnaryOpFloor', 'gageErrLast', 'tenAniso', 'pushTask_t', 'baneClipPeakRatio', 'nrrdBasicInfoUnknown', 'tenEigenvaluePower', 'tenSizeNormalize', 'nrrdKernelBSpline3D', 'baneRangeNegative', 'baneIncStdv', 'tijk_sym_fun', 'gageParmStackNormalizeRecon', 'tenFiberStopDoubleSet', 'gageItemPackPartHessian', 'nrrdKernelBoxSupportDebug', 'nrrdIInsert', 'airInsane_pInfExists', 'baneOpacInfo', 'limnObjectWriteOFF', 'alanParmRandRange', 'tenDwiFiberType1Evec0', 'airLogBesselI0', 'gageDefGradMagCurvMin', 'pushEnergySpecParse', 'nrrdKernelBSpline6D', 'airMop', 'limnPolyDataSuperquadric', 'nrrdTernaryOpUnknown', 'nrrdHistoJoint', 'tend_anvolCmd', 'coilKindType7Tensor', 'nrrdEnvVarStateMeasureType', 'airArrayStructCB', 'tijk_3d_sym_to_esh_matrix_d', 'nrrdField_dimension', 'pullInfoHeight', 'unrrdu_gammaCmd', 'nrrdBoundaryLast', 'miteStageOpMultiply', 'limnPolyDataNeighborArrayComp', 'airVanDerCorput', 'tenGageKind', 'tijk_add_d', 'gageVec', 'nrrdEnvVarDefaultWriteBareTextOld', 'gageStackBlurParmBoundarySet', 'nrrdTypeLast', 'pullTraceStopLength', 'baneHack', 'gageQuerySet', 'tenDwiFiberType12BlendEvec0', 'echoMatterGlassIndex', 'gageStackBlurManage', 'tenSimulate', 'pullPropLast', 'nrrdEnvVarStateDisableContent', 'gageVecDivGradient', 'nrrdKind3DSymMatrix', 'nrrdBasicInfoSpaceDimension', 'tijk_eval_esh_basis_d', 'nrrdAxisInfoIdx', 'airPtrPtrUnion', 'alanInit', 'gageStackBlur', 'nrrdEncodingBzip2', 'pullEnergyTypeButterworthParabola', 'nrrd1DIrregMapCheck', 'echoRTParmNix', 'echoRay', 'elfMaximaContextNew', 'echoMatterGlassSet', 'unrrdu_cropCmd', 'tenGageTensorRThetaPhiLinear', 'meetPullVolAddMulti', 'tenFiberStopNumSteps', 'gageDefOrientationFromSpacing', 'limnCameraAspectSet', 'tenInvariantGradientsR_d', 'meetPullVolLoadMulti', 'nrrdRangeNew', 'baneDefVerbose', 'ell_q_mul_d', 'ell_q_mul_f', 'tenGageTraceHessianEval', 'nrrdEncodingRaw', 'airInsane_NaNExists', 'pullEnergy', 'limnHestPolyDataOFF', 'nrrdQuantize', 'nrrdSpacingStatusDirection', 'tijk_refine_rank1_parm_t', 'tenEstimateSigmaSet', 'tijk_efs_to_2d_sym_f', 'pullBinProcess', 'pullProcessModeAdding', 'tend_simCmd', 'limnEdgeTypeLone', 'unrrdu_heqCmd', 'airStrntok', 'nrrdTernaryOpInOpen', 'nrrdPGM', 'nrrdTernaryOpRician', 'baneFindInclusion', 'gageDefK3Pack', 'echoTypeAABBox', 'nrrdZlibStrategyUnknown', 'tenGageFADiffusionAlign', '_airThread', 'unrrdu_diffCmd', 'airRician', 'airInsane_QNaNHiBit', 'echoMatterLightSet', 'echoPresent', 'limnPrimitiveTriangles', 'nrrdTernaryOpClamp', 'echoObjectHasMatter', 'ell_3m_to_aa_f', 'ell_3m_to_aa_d', 'airIsNaN', 'alanParmTextureType', 'tend_epiregCmd', 'gageVecCurlGradient', 'airMopMem', 'biffMsgStrGet', 'gageDefRenormalize', 'coilKind', 'tenGageCp2', 'pullInfoSpecSprint', 'tenGageCp1', 'limnObjectNew', 'nrrdBasicInfoLast', 'pushPresent', 'tenAniso_Clpmin1', 'tenAniso_Clpmin2', 'echoCube', 'pullTrace', 'echoMatterMetalR0', 'limnEdge_t', 'alanStopLast', 'limnEdgeTypeFrontFacet', 'tenFiberIntgUnknown', 'limnObjectDepthSortFaces', 'tenGageOmegaNormal', 'airStdin', 'gageTeeOfTau', 'tenGageTraceDiffusionFraction', 'airThreadCondBroadcast', 'nrrdBinaryOp', 'tenDwiGage2TensorPeledAndError', 'mossMatShearSet', 'pullInfo', 'nrrdMeasureMin', 'nrrdNuke', 'nrrdTypeIsIntegral', 'airNoDio_disable', 'limnSplineTypeHermite', '_airThreadCond', 'tenEvecRGBSingle_f', 'tenDwiGagePvlData', 'pushContextNix', 'limnSpaceScreen', 'pushRebin', 'airThreadBarrierWait', 'gageItemSpecNew', 'nrrdClampConvert', 'nrrdKernelCos4SupportDebugDD', 'alanStop', 'tenExpand', 'gageItemPackSclValue', 'tijk_refine_rankk_parm', 'ell_3m_mul_f', 'tenTripleTypeWheelParm', 'seekVertexStrength', 'hooverRayBegin_t', 'tenSlice', 'airIndexULL', 'airNormalRand_r', 'baneBiffKey', 'miteValView', 'nrrdReshape_va', 'tijk_sym_fun_t', 'nrrdKindComplex', 'echoChannelAverage', 'limnQN13octa', 'baneMeasrTotalCurv', 'ell_3m_inv_f', 'limnQN14octa', 'tenGageCp1GradVec', 'alanContextNew', 'echoTriangleSet', 'gageKernel00', 'pullCountTestStep', 'nrrdEmpty', 'limnSplineTypeTimeWarp', 'hestMinNumArgs', 'limnObjectVertexNormals', 'tenTripleTypeEigenvalue', 'nrrdKind4Vector', 'airPresent', 'nrrdArithIterAffine', 'tenGageFARidgeSurfaceAlignment', 'nrrdKernelBSpline5ApproxInverse', 'pushPointNix', 'tenAnisoEval_f', 'tijk_2o2d_sym', 'tenAnisoEval_d', 'tenDWMRIBValueKey', 'tenGageCa1HessianEvec0', 'tenGageCa1HessianEvec1', 'tenGageCa1HessianEvec2', 'nrrdFFTWPlanRigorPatient', 'hooverErrInit', 'tend_avgCmd', 'nrrdUnquantize', 'baneMeasrCopy', 'pullEnergyType', 'tenGageOmegaHessianEval', 'nrrdStateUnknownContent', 'baneIncCopy', 'alanParmMinAverageChange', 'echoTypeTriangle', 'airFclose', 'pullPropEnergy', 'tijk_eval_esh_f', 'nrrdEnvVarDefaultWriteBareText', 'limnPolyDataSpiralSphere', 'tenGageModeHessianEvec2', 'tenGageModeHessianEvec1', 'tenGageModeHessianEvec0', 'airFP_POS_INF', 'echoBoundsGet', 'limnObjectPolarSuperquadFancyAdd', 'pullBinsPointAdd', 'baneClipCopy', 'pullSysParmAlpha', 'gageShapeNew', 'pullInterTypeJustR', 'pullIterParmConstraintMax', 'nrrdKind3DMatrix', 'mossSamplerUpdate', 'hestGreedySingleString', 'meetAirEnumAll', 'tenFiberStopAniso', 'gageShapeWtoI', 'pullBinsPointMaybeAdd', 'nrrdHestKernelSpec', 'unrrduScaleNothing', 'nrrdMeasureLinf', 'limnPart', 'tijk_eval_esh_d', 'gagePerVolume', 'tenGageCl1GradVec', 'seekUpdate', 'tenGradientJitter', 'banePresent', 'baneRangeCopy', 'tenFiberStop', 'alanParmAlpha', 'gageParmGradMagCurvMin', 'miteDefOpacNear1', 'nrrdEncodingTypeUnknown', 'tenTripleTypeRThetaZ', 'biffGetDone', 'alanTextureTypeGrayScott', 'ell_debug', 'tenDwiGageTensorNLSLikelihood', 'limnSplineTypeCubicBezier', 'nrrdUnaryOpRand', 'airArrayLenIncr', 'tenEstimate1TensorSingle_d', 'tenEstimate1TensorSingle_f', 'alanStopNonExist', 'pullTraceMultiNew', 'tenDwiGageAll', 'ell_4mv_mul_f', 'ell_4mv_mul_d', 'tenDefFiberAnisoStopType', 'nrrdSlice', 'tenFiberMultiNew', 'nrrdKernelBlackmanDD', 'airNoDio_test', 'nrrdKernelSpecCopy', 'tenGageOmegaGradVecDotEvec0', 'gageStackBlurParmKernelSet', 'limnDefCameraRightHanded', 'nrrdKernelAQuarticD', 'nrrdDefaultResampleNonExistent', 'seekTypeRidgeSurface', 'tenGlyphParmCheck', 'pullCountDescent', 'baneMeasrFlowlineCurv', 'mossLinearTransform', 'ell_4m_inv_d', 'nrrdSpaceVecScaleAdd2', 'nrrdFClamp', 'coilMethodTypeFinish', 'nrrdBlind8BitRangeLast', 'nrrdField_centers', 'pullPropLen', 'airLog2', 'airThreadBarrier', 'ell_q_inv_d', 'pullSysParmUnknown', 'miteRangeGreen', 'airFP_NEG_DENORM', 'nrrdAxisInfoGet_va', 'pullInterTypeUnknown', 'limnEnvMapCB', 'tenGageOmegaGradVec', 'airFP_NEG_NORM', 'airToUpper', 'tenEigenvalueClamp', 'nrrdApply1DIrregMap', 'airFloatSNaN', 'nrrdKernelC3QuinticDD', 'echoTypeTriMesh', 'nrrdStateGrayscaleImage3D', 'hooverRenderEnd_t', 'tenFiberMulti', 'ell_Nm_tran', 'tijk_esh_sp_d', 'nrrdUnaryOpLog10', 'airStrcpy', 'tenFiberMultiPolyData', 'airHeapUpdate', 'miteValNdotL', 'gageParmKernelIntegralNearZero', 'meetPullVolLeechable', 'nrrdFormatTypePNM', 'airErf', 'baneGkms_pvgCmd', 'pullPresent', 'miteValNdotV', 'mossHestTransform', 'ell_3m_det_f', 'ell_3m_det_d', 'ell_q_4v_rotate_d', 'alanParmUnknown', 'gagePerVolumeIsAttached', 'pullEnergySpecParse', 'nrrdField_measurement_frame', 'limnCamera', 'nrrdPPM', 'echoTypeList', 'pullInfoGet', 'nrrdMeasureLast', 'nrrdUnaryOpLog1p', 'airInsane_endian', 'tenEMBimodalParmNew', 'pullInfoInsideGradient', 'pullStatusUnknown', 'tenFiberParmLast', 'nrrdBasicInfoInit', 'nrrdIterSetNrrd', 'pullStart', 'tenModelSimulate', 'tenGageFAHessianEvec2', 'tenGageFAHessianEvec1', 'tenGageFAHessianEvec0', 'ell_4m_post_mul_d', 'echoIntxMaterialColor', 'pullEnergySpring', 'gageSclHessEval', 'airThreadNoopWarning', 'airMopSingleDone', 'limnDevicePS', 'nrrdBasicInfoSpaceUnits', 'nrrdKernelCatmullRomDD', 'tend_mfitCmd', 'elfTenEstimMatrix_f', 'elfTenEstimMatrix_d', 'gageStackProbe', 'nrrdBinaryOpAtan2', 'unrrdu_cmedianCmd', 'tenAniso_Tr', 'biffMove', 'hooverRenderBegin_t', 'nrrdHestNrrd', 'echoSceneNew', 'pullCallbackSet', 'nrrdEnvVarStateAlwaysSetContent', 'tijk_approx_heur_parm_t', 'pushEnergyCotan', 'tijk_eval_efs_d', 'limnWindowNew', 'pullConstraintFailProjGradZeroB', 'pullConstraintFailProjGradZeroA', 'tenAniso_Th', 'ell_3m_rotate_between_d', 'gageTauOfTee', 'nrrdTypeBlock', 'pullEnergyTypeHepticWell', 'pullCountNixing', 'nrrdSpatialAxesGet', 'seekItemGradientSet', 'tenGageTensor', 'pushContext', 'nrrdSpaceScannerXYZ', 'airBesselInExpScaled', 'coilMethodTypeTesting', 'nrrdEncodingGzip', 'airTeemVersion', 'meetPullInfo', 'airHeapMerge', 'airThreadMutex', 'unrrduUsage', 'nrrdDistanceL2Signed', 'tenGageTensorQuatGeoLoxR', 'tenGageInvarKGrads', 'tenSizeScale', 'tenGageTensorQuatGeoLoxK', 'biffAddf', 'gageStackPerVolumeAttach', 'biffGetStrlen', 'gageDefCheckIntegrals', 'coilBiffKey', 'nrrdBinaryOpCompare', 'tend_normCmd', 'airNoDio_okay', 'airTypeUnknown', 'tenAnisoHistogram', 'tenGageFA', 'nrrdSpaceVecNorm', 'alanStopNot', 'tenDwiGageJustDWI', 'tenDwiGageTensorLLSError', 'tenDwiGageTensorAllDWIError', 'tenDwiGage', 'tijk_2d_sym_to_efs_f', 'nrrdBinaryOpFmod', 'nrrdKind3Vector', 'airStrtrans', 'meetPullVolParse', 'nrrdTypeChar', 'nrrdCCRevalue', 'ell_3v_barycentric_spherical_d', 'echoObject', 'nrrdFFTWWisdomWrite', 'pullInitHaltonSet', 'pullIterParmSnap', 'nrrdSpace3DLeftHandedTime', 'ell_biff_key', 'gageSclNormal', 'nrrdDefaultResampleType', 'nrrdDeringContextNew', 'unrrdu_axdeleteCmd', 'nrrdBasicInfoCopy', 'baneInc_t', 'dyeConverter', 'nrrdCrop', 'tenGageFiberDispersion', 'nrrdUnaryOpAbs', 'limnSplineUpdate', 'tenGageCovariance', 'nrrdCompare', 'echoCylinderSet', 'alan2DSizeSet', 'unrrduScaleSubtract', 'hestMultiFlagSep', 'limnObjectPSDraw', 'baneMeasr', 'airInsane_FISize', 'pullTraceStopLast', 'hestParm', 'nrrdKeyValueIndex', 'limnPolyDataCompress', 'seekType', 'nrrdSpacingStatusUnknown', 'limnSplineTypeSpec', 'airEnumStr', 'pushEnergyTypeGauss', 'airDioTest', 'pullEnergyTypeQuarticWell', 'miteValVrefN', 'pushEnergyTypeZero', 'unrrduPresent', 'tenFiberTrace', 'limnEdgeTypeFrontCrease', 'miteSample', 'nrrdField_data_file', 'tenFiberIntgMidpoint', 'meetPullInfoAddMulti', 'limnWindow', 'tend_bfitCmd', 'nrrdField_old_min', 'unrrduScaleExact', 'pullConstraintFailIterMaxed', 'airThreadBarrierNix', 'ell_q_avg4_d', 'coilKindTypeUnknown', 'nrrdTypeFloat', 'airParseStrUI', 'coilContextNew', 'nrrdKernelBSpline1D', 'airEqvMap', 'seekTypeLast', 'gageKind_t', 'gageDefKernelIntegralNearZero', 'nrrdEncodingTypeHex', 'tenGageEvec0', 'tenGageEvec1', 'nrrdIoStateValsPerLine', 'unrrdu_tileCmd', 'pullInterTypeSeparable', 'nrrdKind2DMaskedSymMatrix', 'airMopOnOkay', 'baneDefPercHistBins', 'limnPolyDataPrimitiveSort', 'tenGageTraceHessian', 'nrrdField_space_directions', 'baneRangeAnywhere', 'nrrdHasNonExist', 'tenTripleConvertSingle_f', 'tenGageOmegaGradMag', 'airThreadMutexUnlock', 'tenFiberTypeUnknown', 'nrrdMeasureHistoProduct', 'alanContext', 'tenTripleConvertSingle_d', 'nrrdUnaryOpIf', 'tenGageUnknown', 'tijk_approx_heur_parm_new', 'nrrdBasicInfoBlocksize', 'baneClipNew', 'unrrdu_unquantizeCmd', 'miteValUnknown', 'nrrdFormatNRRD', 'pullEnergyTypeUnknown', 'elfPresent', 'tenGageNormNormal', 'nrrdKindRGBColor', 'gageDefStackNormalizeRecon', 'airSingleSscanf', 'airThreadCondWait', 'pullFlagRestrictiveAddToBins', 'pullProcessModeDescent', 'limnSplineTypeSpecNix', 'gageOptimSigParmNew', 'gageVecMGEval', 'tenFiberIntgSet', 'tenModel1Unit2D', 'pullBin', 'nrrdField_unknown', 'nrrdCCNum', 'pullInitUnequalShapesAllowSet', 'pullPropStepEnergy', 'pullIterParmEnergyIncreasePermitHalfLife', 'tenGageOmega', 'tijk_set_axis_efs', 'pullIterParmAddDescent', 'tijk_set_axis_tensor', 'pullSysParmEnergyDecreaseMin', 'tend_logCmd', 'pullSysParmLast', 'mossSamplerFill', 'alanParmLast', 'limnObjectNix', 'tenGageDetHessian', 'pullCountAdding', 'gageErrNone', 'pullEnergyCubic', 'limnSplineMaxT', 'pullPropNeighCovar7Ten', 'gageErrStackIntegral', 'nrrdRangeNewSet', 'miteShadeSpecQueryAdd', 'nrrdEnvVarStateKindNoop', 'tenGageInvarRGradMags', 'nrrdSpaceVecSetNaN', 'limnCameraInit', 'pullConstraintFailUnknown', 'meetPullInfoParse', 'tend_sliceCmd', 'tenFiberTypePureLine', 'nrrdKindScalar', 'pullPropNeighCovarTrace', 'airTypeInt', 'pushBinAllNeighborSet', 'tenGlyphTypeSphere', 'tijk_refine_rankk_2d_d', 'unrrdu_cksumCmd', 'baneGkmsHestGthresh', 'tijk_8o3d_sym', 'baneClipLast', 'gagePointReset', 'nrrdKernelC5SepticDD', 'hestOpt', 'nrrdMeasureLineError', 'alanParmWrapAround', 'elfBallStickOptimize_f', 'nrrdKernelBCCubicDD', 'limnOptsPS', 'nrrdSpacingStatusLast', 'tenFiberStopBounds', 'airSrandMT_r', 'hooverErrRenderBegin', 'pullProp', 'limnSplineInfo2Vector', 'nrrdKernelBSpline7ApproxInverse', 'pullProcessModeNeighLearn', 'nrrdFFTWWisdomRead', 'nrrdKernelSpecNew', 'baneGkms_txfCmd', 'pullFlagEnergyFromStrength', 'nrrdRangeSet', 'limnEdgeTypeContour', 'ell_4m_inv_f', 'nrrdField_space_origin', 'dyeXYZtoLAB', 'nrrdFormatTypeNRRD', 'echoLightPosition', 'tenAnisoScale', 'echoScene_t', 'nrrdResampleClampSet', 'mossMatPrint', 'gageVecGradient2', 'gageVecGradient0', 'gageVecGradient1', 'limnDeviceGL', 'pullTraceNix', 'tenInterpTypeLast', 'tenGageFAHessianEval2', 'gageItemPackPartGradMag', 'tenGageFAHessianEval0', 'tenGageFAHessianEval1', 'nrrdSpaceLast', 'limnPolyDataEdgeHalve', 'nrrdKernelC5SepticDDD', 'nrrdResampleNonExistentSet', 'pushRun', 'nrrdAxisInfoThickness', 'airEndian', 'nrrdIoStateBareText', 'ell_Nm_inv', 'tijk_init_rank1_3d_f', 'hestElideSingleEnumType', 'tijk_init_rank1_3d_d', 'ell_q_log_d', 'unrrduHestEncodingCB', 'ell_q_log_f', 'airInsaneErr', 'tenGageCovarianceRGRT', 'mossFlagImage', 'pullVolumeNew', 'coilMethodTypeModifiedCurvatureRings', 'meetNrrdKernelAll', 'gageKernel21', 'gageKernel20', 'gageKernel22', 'miteDefRefStep', 'airArrayNuke', 'echoMatterPhong', 'tenGageFARidgeLineAlignment', 'nrrdCropAuto', 'tenGageSGradMag', 'tenGageEvalGrads', 'limnLightNew', 'miteRangeEmissivity', 'pullIterParmUnknown', 'nrrdAxisInfoPosRange', 'gageItemEntry', 'gageSclNPerp', 'pullSource', 'echoJitter', 'seekItemStrengthSet', 'nrrdReshape_nva', 'pullBiffKey', 'tenGlyphTypeUnknown', 'pushEnergyUnknown', 'pushEnergyTypeSpring', 'nrrdBasicInfoSpace', 'tenGageModeHessianEvec', 'nrrdMeasureHistoMin', 'nrrdUnaryOpRoundUp', 'tenFiberTypeLast', 'nrrdILookup', 'gageParmCheckIntegrals', 'tend_evqCmd', 'tenDwiGageTensorNLSError', 'tenDWMRIKeyValueFromExperSpecSet', 'nrrdUnaryOpCos', 'tijk_class_unknown', 'ell_6m_mul_d', 'dyeRGBtoHSL', 'limnPolyDataPrimitiveVertexNumber', 'nrrdField_content', 'nrrdSpaceVecSetZero', 'pullOutputGetFilter', 'gageKernelUnknown', 'nrrdKindSpace', 'nrrdKindRGBAColor', 'gageDefStackUse', 'airHeapInsert', 'pullCountIteration', 'hestUsage', 'nrrdUnaryOpZero', 'dyeRGBtoHSV', 'elfGlyphHOME', 'hooverPresent', 'limnEnvMapCheck', 'nrrdResampleOverrideCenterSet', 'airEnumValCheck', 'pullFlagConstraintBeforeSeedThresh', 'airThread', 'tenGageMode', 'nrrdKernelBSpline6DDD', 'tenGageCp1HessianEvec', 'gageUpdate', 'nrrdSpacingCalculate', 'miteDefRenorm', 'pullEnergyTypeGauss', 'limnObjectPolarSuperquadAdd', 'seekTypeSet', 'airThreadCondNew', 'tijk_esh_to_3d_sym_matrix_f', 'nrrdStateGetenv', 'tenInterpType', 'tenGageCp1Normal', 'airEndianUnknown', 'pullVolume', 'echoIntxColor', 'nrrdMeasureMean', 'miteValWdotD', 'pullInitPointPerVoxelSet', 'tenGlyphGen', 'miteShadeSpecParse', 'airTypeBool', 'unrrdu_deringCmd', 'tenDwiGage2TensorQSeg', 'nrrdStateKeyValuePairsPropagate', 'gageShapeEqual', 'pullFlagLast', 'echoJittableLens', 'tijk_sub_f', 'pullGammaLearn', 'tenInterpParmNix', 'limnObjectPolarSphereAdd', 'tenGageFiberCurving', 'nrrdKernelC5SepticD', 'pullSourceGage', 'nrrdDeringVerticalSeamSet', 'pushPoint', 'nrrdStateMeasureHistoType', 'tenGlyphTypeLast', 'limnSplineInfoQuaternion', 'tenGageTraceGradMag', 'NrrdKernel', 'nrrdField_encoding', 'nrrdKernelGaussian', 'biffMovef', 'echoScene', 'limnSplineTypeBC', 'nrrdIoStateZlibStrategy', 'nrrdField_thicknesses', 'baneMeasrGradMag', 'echoTypeLast', 'gageStackPerVolumeNew', 'nrrdArithIterBinaryOpSelect', 'nrrdEncodingArray', 'gageKernelSet', 'nrrdKindList', 'limnCamera_t', 'echoSphereSet', 'echoGlobalState', 'pullEnergyGauss', 'tenModel', 'baneIncProcess', 'gageItemSpecNix', 'echoTypeSplit', 'nrrdArithIterTernaryOp', 'tenDwiGageFA', 'tenEstimateGradientsSet', 'nrrdHasNonExistLast', 'miteDefOpacMatters', 'nrrdCommentAdd', 'limnSplineType', 'pullIterParmStuckMax', 'nrrdCCSize', 'baneClipAbsolute', 'echoBiffKey', 'pushContextNew', 'tenGageDetGradMag', 'tenDwiGageKindSet', 'tenGageOmegaHessianEvec0', 'tenGageOmegaHessianEvec1', 'nrrdBoundary', 'nrrdAxesPermute', 'tenFiberParmWPunct', 'limnCameraPathTrackBoth', 'echoMatterMetalSet', 'ell_6ms_eigensolve_d', 'limnSplineTypeLinear', 'tijk_esh_deconvolve_f', 'gageParmCurvNormalSide', 'gageSclHessMode', 'tenGageFAGradMag', 'pullPointNumber', 'pullContextNix', 'limnSplineInfoNormal', 'tenGageFAValleySurfaceStrength', 'airFopen', 'airSprintVecSize_t', 'echoEnvmapLookup', 'unrrdu_quantizeCmd', 'tenAniso_Skew', 'nrrdMeasureHistoMean', 'limnQN16border1', 'nrrdCenterLast', 'tijk_refine_rank1_2d_d', 'tijk_refine_rank1_2d_f', 'nrrdAxisInfoUnits', 'miteRangeKa', 'tenFiberSingleDone', 'miteRangeKd', 'nrrdMeasureMedian', 'nrrdMinMaxExactFind', 'pullEnergyTypeCotan', 'ell_q_div_f', 'ell_q_div_d', 'nrrdPad_va', 'nrrdAxisInfoSize', 'miteRangeKs', 'baneMeasr_t', 'pullCondLast', 'nrrdStateAlwaysSetContent', 'tenGageFAGaussCurv', 'NrrdFormat', 'nrrdKernelCheap', 'pullFlagUseBetaForGammaLearn', 'limnSplineTypeLast', 'nrrdArithTernaryOp', 'pushEnergy', 'airInsane_dio', 'baneInc', 'nrrdTernaryOpMax', 'tenGageLast', 'gageScl3PFilter4', 'tenGlyphParmNew', 'baneMeasrAnswer', 'dyeColorNix', 'tenFiberTypeZhukov', 'pullInitGivenPosSet', 'echoListSplit3', 'tenDwiGageKindNew', 'gageQueryPrint', 'tenEstimateVerboseSet', 'airSgn', 'meetAirEnumAllPrint', 'gageCtxFlagUnknown', 'echoJittableUnknown', 'pullPoint', 'pullPropStuck', 'nrrdKernelBCCubicD', 'tenGageCl1HessianEvec', 'nrrdBasicInfoDimension', 'nrrdKernelHannD', 'coilKindArray', 'alanParmHomogAniso', 'limnPolyDataCCFind', 'airTeemReleaseDate', 'limnObjectFaceNormals', 'gageStackBlurParmVerboseSet', 'nrrdKernelTMF_maxC', 'nrrdIoStateDetachedHeader', 'alanStopDiverged', 'tend_expandCmd', 'tenGlyphParmNix', 'tenEstimate2MethodQSegLLS', 'unrrduHestMaybeTypeCB', 'airOneLinify', 'tenEvecRGBSingle_d', 'dyeColorInit', 'gageItemPackPartGradVec', 'pullProcessModeUnknown', 'tenDwiGageKindCheck', 'airMopError', 'coilVolumeCheck', 'echoTriangle', 'limnPolyDataRasterize', 'hestElideMultipleEmptyStringDefault', 'miteValRi', 'echoPos_t', 'hestVarParamStopFlag', 'seekTypeValleyLine', 'pullSysParmStepInitial', 'nrrdUIStore', 'airCRC32', 'tenFiberAnisoSpeedReset', 'coilMethodTypePeronaMalik', 'miteRangeBlue', 'limnPolyDataInfoRGBA', 'miteValRw', 'tenGradientGenerate', 'coilPresent', 'tend_stenCmd', 'tijk_esh_to_3d_sym_matrix_d', 'limnEdgeTypeUnknown', 'airSprintSize_t', 'seekDescendToDeg', 'nrrdBasicInfoOldMax', 'unrrdu_shuffleCmd', 'echoJittableLight', 'tenInterpMulti3D', 'seekIsovalueSet', 'airMopDebug', 'nrrdEncodingType', 'nrrdCRC32', 'baneGkmsCmdList', 'pullCondUnknown', 'limnQN11octa', 'pullEnergyTypeBspln', 'tenDwiGageTensorMLE', 'pullEnergyTypeBetterCubicWell', 'ell_q_to_aa_d', 'gagePoint_t', 'ell_q_to_aa_f', 'unrrdu_padCmd', 'pushEnergyTypeCoulomb', 'airMopOnError', 'nrrdDefaultKernelParm0', 'tend_pointCmd', 'echoTypeUnknown', 'airFPClass_f', 'unrrduScaleAspectRatio', 'tenDwiGageMeanDWIValue', 'tijk_set_axis_esh', 'nrrdGetenvEnum', 'baneRangeLast', 'nrrdUnaryOpSqrt', 'airFP_POS_ZERO', 'nrrdIoStateGet', 'tenModelFromAxisLearnPossible', 'tenGageHessian', 'pullCondEnergyBad', 'nrrdUnaryOpOne', 'gageCtxFlagRadius', 'tenInterpTypeLogLinear', 'ell_3m_mul_d', 'mossFlagUnknown', 'coilContext_t', 'gageKindAnswerOffset', 'nrrdNew', 'nrrdEncodingTypeBzip2', 'airMopSingleError', 'nrrdField_sample_units', 'hooverErrRayEnd', 'tend_anplotCmd', 'pullInfoTensorInverse', 'pullPropNeighTanCovar', 'nrrdAxesSplit', 'nrrdSameSize', 'nrrdTernaryOpMaxSmooth', 'tenEstimateContext', 'unrrdu_swapCmd', 'seekItemEigensystemSet', 'airDioMalloc', 'limnPrimitiveLineStrip', 'airThreadJoin', 'pullEnergyTypeCubic', 'nrrdMeasureSkew', 'tenDefFiberWPunct', 'nrrdTypeUShort', 'hooverContextCheck', 'tenFiberStopSet', 'echoMatterPhongKs', 'limnSpaceDevice', 'nrrdUnaryOpAsin', 'biffMsgAdd', 'tenGageInvarRGrads', 'pullFlagConvergenceIgnoresPopCntl', 'echoMatterPhongKd', 'nrrdUnblock', 'pushEnergySpec', 'echoMatterPhongKa', 'tenFiberStopRadius', 'limnPrimitiveUnknown', 'airSinglePrintf', 'airNormalRand', 'nrrdIterNew', 'pullIterParmPopCntlPeriod', 'airNoDio_std', 'tenModel2Unit2D', 'nrrdFFTWPlanRigorUnknown', 'airFloat', 'tenFiberStopConfidence', 'nrrdKind3Normal', 'tenGageModeHessianEval2', 'tenGageModeHessianEval0', 'tenGageModeHessianEval1', 'airThreadNix', 'gageSclTotalCurv', 'gageCtxFlagK3Pack', 'nrrdMeasureSum', 'airGaussian', 'elfMaximaContextNix', 'nrrdResampleBoundarySet', 'tenEMatrixCalc', 'tenRotationTangents_d', 'nrrdBasicInfoType', 'coilContextNix', 'tenEigenvalueAdd', 'tenModelBall1Stick', 'hooverErrLast', 'nrrdKernelBSpline4', 'nrrdKernelC4HexicDD', 'nrrdTile2D', 'airIndex', 'tenFiberStopStub', 'limnCameraUpdate', 'nrrdKernelBSpline5DDD', 'dyeSpaceLast', 'airDrandMT_r', 'airMode3_d', 'miteRangeAlpha', 'limnPolyDataSave', 'nrrdStateVerboseIO', 'nrrdDefaultResamplePadValue', 'gageOptimSigCalculate', 'nrrd1DIrregAclGenerate', 'baneRange', 'tenGageModeNormal', 'tenTripleCalcSingle_d', 'ell_3v_perp_d', 'ell_3v_perp_f', 'pullIterParmMin', 'nrrdKind2DMatrix', 'airRandMTStateGlobal', 'tijk_sub_d', 'airLLong', 'tenGageConfidence', 'seekItemScalarSet', 'hooverStubThreadEnd', 'nrrdArrayCompare', 'tenGageEvec', 'tenDwiGageTensorLLS', 'limnSplineParse', 'baneClipPercentile', 'tenEpiRegister4D', 'seekItemNormalSet', 'limnSpaceLast', 'miteRenderBegin', 'airMyEndian', 'pullPropNeighCovar', 'miteRangeRed', 'gageVecVector2', 'gageVecVector1', 'nrrdBinaryOpMin', 'tenGageThetaNormal', 'nrrdKernelC4HexicDDD', 'nrrdSimplePad_nva', 'pullEnergySpecNix', 'pullInitRandomSet', 'airPrimeList', 'tijk_negate_d', 'gageSclHessEvec', 'meetBiffKey', 'echoThreadStateNix', 'airThreadStart', 'tenFiberSingle', 'ell_3m_to_q_d', 'ell_3m_to_q_f', 'airFP_POS_DENORM', 'nrrdAxesInsert', 'airThreadBarrierNew', 'gageSclHessian', 'baneIncNew', 'limnSpline', 'meetPullInfoNix', 'limnDeviceLast', 'tenTensorCheck', 'nrrdSample_va', 'limnPolyDataSmoothHC', 'tenGageOmegaHessianContrTenEvec2', 'tenGageOmegaHessianContrTenEvec1', 'tenGageOmegaHessianContrTenEvec0', 'tenAniso_Det', 'tijk_class_esh', 'nrrdResampleNonExistent', 'tenGageFA2ndDD', 'elfSingleShellDWI', 'tenGageCl1GradMag', 'tenFiberContextNew', 'gageParmK3Pack', 'tenGageBNormal', 'tenEpiRegister3D', 'baneGkmsUsage', 'echoSphere', 'nrrdApply1DSubstitution', 'gageOptimSigParmNix', 'airEnumFmtDesc', 'seekPresent', 'tenExperSpecFromKeyValueSet', 'echoJittablePixel', 'nrrdZlibStrategyFiltered', 'ell_3m_eigensolve_d', 'echoIntxLightColor', 'dyeSimpleConvert', 'nrrdSpaceVecCopy', 'seekExtract', 'pullRngSeedSet', 'nrrdField_last', 'nrrdHestIter', 'alanParmVerbose', 'gageVecImaginaryPart', 'nrrdBasicInfoContent', 'tenEstimateValueMinSet', 'tijk_3d_sym_to_esh_f', 'limnPolyDataInfoTang', 'limnCameraPathTrackLast', 'mossImageAlloc', 'echoJitterNone', 'airRandMTStateNew', 'nrrdEnvVarDefaultCenterOld', 'tenExperSpecMaxBGet', 'pullPointNumberFilter', 'tenEstimateUpdate', 'pullInfoIsovalueHessian', 'baneRangeZeroCentered', 'nrrdLineSkip', 'coilStart', 'coilMethodTypeHomogeneous', 'nrrdField_kinds', 'tijk_refine_rankk_parm_t', 'tenExpand2D', 'gageVecJacobian', 'nrrdBlind8BitRangeState', 'tenEstimate1Method', 'tijk_4o2d_unsym', 'pullTraceMultiSizeof', 'tijk_axis_info', 'airFP_NEG_ZERO', 'pushEnergyAll', 'echoMatter', 'pullFlagStartSkipsPoints', 'tenFiberParmUnknown', 'airStderr', 'pushEnergyType', 'unrrdu_histoCmd', 'pullSysParmSeparableGammaLearnRescale', 'unrrduDefNumColumns', 'tenModel1Tensor2', 'airEndianLittle', 'nrrdKind4Color', 'limnSpaceWorld', 'seekTypeUnknown', 'seekContour3DTopoHackTriangle', 'nrrdCommentClear', 'limnPolyDataSpiralTubeWrap', 'tenGageRGradVec', 'nrrdStateBlind8BitRange', 'nrrdField_labels', 'pullInfoSpecAdd', 'tenDwiFiberTypeUnknown', 'tenGageOmegaHessianEvec', 'seekContextNix', 'tijk_efs_to_2d_sym_d', 'nrrdFFTWPlanRigorExhaustive', 'nrrdTernaryOpAdd', 'nrrdKernelHermiteScaleSpaceFlag', 'ell_cubic_root_triple', 'nrrdKernelSpecNix', 'nrrdDefaultResampleBoundary', 'nrrdAxesDelete', 'airFloatPosInf', 'nrrdKernelSpecSet', 'limnPolyDataInfoUnknown', 'tenGageThetaGradVec', 'gageItemSpecInit', 'tend_aboutCmd', 'gageErr', 'gagePoint', 'pullTraceNew', 'tenGageFAHessianFrob', 'alanParmMaxPixelChange', 'seekVerboseSet', 'airMopper', 'nrrdApply1DLut', 'tenGradientMeasure', 'nrrdKindStub', 'nrrdTypeSize', 'tenDwiGageTensorLLSLikelihood', 'nrrdBlock', 'mossFlagKernel', 'airTypeString', 'nrrdKernelC4HexicD', 'miteStageOpMin', 'nrrdFormatTypeUnknown', 'seekTypeMaximalSurface', 'dyeColorSprintf', 'pullEnergySpecCopy', 'airFlippedSgnPow', 'baneMeasr2ndDD', 'pullCountPoints', 'meetConstGageKindParse', 'nrrdSpacingStatusNone', 'gageVecDivergence', 'gageShape_t', 'alanParmSet', 'limnPolyDataCube', 'limnDefCameraAtRelative', 'ell_cubic_root_three', 'biffMsgNix', 'tenGageCa1GradMag', 'limnPolyDataNew', 'nrrdDefaultResampleRound', 'coilMethodTypeModifiedCurvature', 'echoRTParmNew', 'gageSclHessEvec0', 'gageSclHessEvec1', 'gageSclHessEvec2', 'tenGlyphBqdUvEval', 'hestElideSingleNonExistFloatDefault', 'tenFiberStopLength', 'ell_4m_print_f', 'limnSplineInfo3Vector', 'ell_4m_print_d', 'tenDefFiberMaxNumSteps', 'limnLightDiffuseCB', 'nrrdMeasureHistoSum', 'tenGageFARidgeSurfaceStrength', 'nrrdField_axis_mins', 'pullEnergyCubicWell', 'tenFiberUpdate', 'biffMsgStrAlloc', 'tenDwiGageADC', 'pullProbe', 'nrrdOriginCalculate', 'echoJitterLast', 'tenInterpN_d', 'mossMatInvert', 'seekTypeRidgeLine', 'pullCountEnergyFromPoints', 'coilMethodTypeUnknown', 'gageVecLength', 'pullSysParmRadiusSpace', 'pushContext_t', 'limnSpace', 'nrrdIoStateFormatGet', 'gageStackWtoI', 'nrrdKernelBSpline5DD', 'pushPointNew', 'pushEnergySpecNew', 'pullContext_t', 'tenDwiGageKindData', 'nrrdDeringRadiusScaleSet', 'nrrdTypePrintfStr', 'nrrdField_sizes', 'nrrdAxisInfoGet_nva', 'nrrdSimplePad_va', 'tenDwiGageTensorLikelihood', 'tenGageThetaGradMag', 'pullInterEnergySet', 'tijk_2d_sym_to_efs_d', 'nrrdKindLast', 'gageParm_t', 'nrrdIoStateLast', 'tenEvecRGBParmNew', 'limnFace_t', 'banePosCheck', 'nrrdField_axis_maxs', 'tenGageRotTanMags', 'tenGageBHessian', 'airHeapRemove', 'limnObjectPSDrawConcave', 'limnObjectFaceNumPreSet', 'tend_ellipseCmd', 'tenGageCovarianceKGRT', 'biffMsgStrlen', 'NrrdDeringContext', 'mossMatLeftMultiply', 'tenGageOmega2ndDD', 'nrrdDefaultResampleClamp', 'pullEnergyButterworthParabola', 'hestElideSingleEmptyStringDefault', 'tenExperSpec', 'nrrdDeringVerboseSet', 'tenDwiGage2TensorQSegError', 'hestElideMultipleNonExistFloatDefault', 'gageScl2ndDD', 'limnPolyDataTransform_d', 'airTypeDouble', 'pullEnergyHepticWell', 'tijk_esh_make_kernel_rank1_d', 'tijk_esh_make_kernel_rank1_f', 'baneIncUnknown', 'pullInfoUnknown', 'pullEnergyAll', 'unrrdu_permuteCmd', 'limnDeviceUnknown', 'nrrdUnaryOpAtan', 'tenGageDelNormPhi3', 'tenGageDelNormPhi2', 'tenGageDelNormPhi1', 'airRandMTState', 'NrrdKernelSpec', 'nrrdBinaryOpMax', 'tenGageTensorGradRotE', 'nrrdInvertPerm', 'nrrdTernaryOp', 'tenGlyphBqdAbcUv', 'nrrdUnaryOpSgn', 'ell_3v_area_spherical_d', 'pullSysParmBackStepScale', 'dyeColorSet', 'baneIncAbsolute', 'nrrdKernelBSpline5', 'nrrdKernelBSpline6', 'nrrdKernelBSpline7', 'nrrdKernelBSpline1', 'nrrdKernelBSpline2', 'nrrdKernelBSpline3', 'nrrdMeasureHistoL2', 'limnPolyDataAlloc', 'tenGradientBalance', 'tijk_esh_make_kernel_delta_f', 'tenDwiGageTensorWLSErrorLog', 'elfGlyphKDE', 'nrrdHistoDraw', 'nrrdGetenvDouble', 'nrrdLoadMulti', 'echoMatterTextureSet', 'limnQNtoV_f', 'limnQNtoV_d', 'tenGageTraceHessianEval1', 'tenGageTraceHessianEval0', 'echoSceneNix', 'tenGageTraceHessianEval2', 'nrrdTernaryOpLTSmooth', 'elfColorGlyphMaxima', 'nrrdKindHSVColor', 'elfBallStickParms', 'nrrdFormatTypeVTK', 'pullCondOld', 'alanTextureTypeTuring', 'pullCountForceFromPoints', 'tend_evecrgbCmd', 'pushEnergyTypeCotan', 'echoJitterRandom', 'pullPtrPtrUnion', 'tenGageOmegaHessianEvec2', 'echoIntx', 'gageCtxFlagShape', 'airTypeULongInt', 'limnCameraPathTrackAt', 'limnPolyDataCopyN', 'tijk_3d_sym_to_esh_d', 'tenDWMRIModalityVal', 'pullTraceSet', 'nrrdKernelAQuartic', 'nrrdSpace3DRightHanded', 'nrrdUnaryOpSin', 'nrrdFFTWPlanRigorMeasure', 'airHeapFind', 'ell_Nm_mul', 'nrrdKindXYZColor', 'echoTypeCylinder', 'nrrdAxisInfoMinMaxSet', 'gageVolumeCheck', 'gageVecDirHelDeriv', 'tijk_2o2d_unsym', 'nrrdStringWrite', 'tenAniso_B', 'limnLightUpdate', 'nrrdOriginStatusOkay', 'tenAniso_Q', 'tenAniso_R', 'tenAniso_S', 'limnQN16simple', 'tijk_max_efs_order', 'dyeColorGet', 'nrrdMeasureLine', 'tenGageFAMeanCurv', 'ell_4m_mul_f', 'gagePresent', 'ell_4m_mul_d', 'limnPresent', 'tenDwiGageTensorWLSError', 'miteRangeLast', 'tenTripleTypeLast', 'nrrdSwapEndian', 'gageErrBoundsStack', 'tijk_approx_heur_parm', 'tenEstimate1TensorSimulateVolume', 'unrrdu_resampleCmd', 'limnPolyDataInfoBitFlag', 'hooverStubRayBegin', 'nrrdRangeAxesGet', 'tenGageConfDiffusionFraction', 'nrrdKernelBCCubic', 'pullVolumeStackAdd', 'tenGageCa1Normal', 'airDoneStr', 'pullSysParmSet', 'nrrdResamplePadValueSet', 'nrrdDeringContextNix', 'nrrdMeasureNormalizedL2', 'echoMatterMetalFuzzy', 'mossMatScaleSet', 'tenGageCp1Hessian', 'unrrdu_sliceCmd', 'gageKindVec', 'echoMatterMetalKd', 'nrrdIoStateSkipData', 'echoMatterMetalKa', 'unrrduScaleMultiply', 'unrrdu_vidiconCmd', 'gageKindScl', 'hestCleverPluralizeOtherY', 'nrrdCheapMedian', 'nrrdKernelDiscreteGaussian', 'limnSplineInfoUnknown', 'tenEstimateLinearSingle_d', 'tenEstimateLinearSingle_f', 'tenModel1Vector2D', 'nrrdSaveMulti', 'baneDefIncLimit', 'tenLogSingle_d', 'tend_satinCmd', 'tijk_3d_sym_to_esh_matrix_f', 'nrrdIoStateZlibLevel', 'gageQueryAdd', 'gageItemPackPartLast', 'nrrdKernelBSpline3ApproxInverse', 'baneMeasrLast', 'tenFiberSingleNix', 'gageVecLast', 'limnQNUnknown', 'nrrdKernelAQuarticDD', 'airIsInf_f', 'tend_anscaleCmd', 'airIsInf_d', 'tenGageTensorGradMagMag', 'tenDWMRIGradKeyFmt', 'alan_t', 'tenGageCl1Hessian', 'seekTypeIsocontour', 'airFP_Unknown', 'nrrdBasicInfoKeyValuePairs', 'limnQN8checker', 'gageParmUnknown', 'pushBin', 'miteShadeMethodUnknown', 'nrrdBinaryOpPow', 'elfMaximaFind_d', 'biffMsgNoop', 'baneIncPercentile', 'elfMaximaFind_f', 'nrrdTernaryOpLast', 'tenGageTensorLogEuclidean', 'pullVolumeLookup', 'unrrduScaleDivide', 'airParseStr', 'unrrdu_fftCmd', 'pullInitMethodHalton', 'tenInterpTypeGeoLoxR', 'nrrdKernelZero', 'pullRun', 'tenGageFAGradVec', 'tenInterpTypeGeoLoxK', 'nrrdDefaultCenter', 'limnPolyDataWriteVTK', 'ell_3m_post_mul_f', 'ell_3m_post_mul_d', 'limnPart_t', 'baneDefRenormalize', 'tenDWMRIKeyValueParse', 'airIndexClampULL', 'baneIncRangeRatio', 'baneStateHistEqBins', 'nrrdFLoad', 'echoGlobalStateNew', 'tenEstimateThresholdSet', 'pullFlagPermuteOnRebin', 'baneProbe', 'biffMsgLineLenMax', 'nrrdKernelParse', 'tenSqrtSingle_d', 'limnSplineTypeSpecNew', 'gageVecMGEvec', 'echoRectangle', 'unrrdu_ccadjCmd', 'tijk_approx_rankk_3d_d', 'ell_q_to_4m_f', 'nrrdStateDisallowIntegerNonExist', 'ell_q_to_4m_d', 'biffMsgClear', 'ell_3m_2d_nullspace_d', 'nrrdBinaryOpLast', 'tend_evalCmd', 'coilDefaultRadius', 'nrrdRangeNix', 'dyeSpaceUnknown', 'airRandMTStateGlobalInit', 'nrrdDomainAxesGet', 'airHeap', 'pullInterTypeAdditive', 'tenGageDetNormal', 'nrrdDeringClampHistoBinsSet', 'airDrandMT', 'nrrdUnaryOpLog2', 'pullEnergyTypeSpring', 'airMode3', 'tijk_refine_rank1_parm_nix', 'limnObjectConeAdd', 'NrrdAxisInfo', 'tijk_esh_convolve_d', 'airMopDone', 'tenSqrtSingle_f', 'tenGageSHessian', 'baneGkms_infoCmd', 'elfESHEstimMatrix_d', 'baneGkmsMeasr', 'nrrdField_min', 'dyeStrToSpace', 'gageVecStrain', 'nrrdShuffle', 'baneGkms_miteCmd', 'mite_t', 'gageDeconvolveSeparable', 'airSetNull', 'pullThreadNumSet', 'airDioInfo', 'airArray', 'nrrdResampleContextNew', 'limnSplineTypeSpec_t', 'echoListAdd', 'tenDefFiberAnisoThresh', 'pullTraceStopBounds', 'tenGageFAHessianEval', 'nrrdResampleExecute', 'nrrdField_spacings', 'dyeColorNew', 'gageParmStackUse', 'airNoDio_fpos', 'nrrdOriginStatusNoMaxOrSpacing', 'nrrdResampleNonExistentLast', 'nrrdDClamp', 'unrrdu_reshapeCmd', 'pullInfoHessian', 'limnPolyDataPrimitiveTypes', 'gageOptimSigSet', 'gageStackItoW', 'tenTripleTypeMoment', 'nrrdKernelBSpline2D', 'nrrdIStore', 'mossSamplerNix', 'tenModelParse', 'miteRangeUnknown', 'tenGageInvarKGradMags', 'biffPresent', 'nrrdTypeInt', 'seekTypeValleySurfaceT', 'limnPolyDataWriteIV', 'nrrdFormatArray', 'nrrdCCMax', 'airBesselI0ExpScaled', 'tenTripleTypeK', 'tenTripleTypeJ', 'coilTask', 'tenTripleTypeR', 'mossDefCenter', 'tenVerbose', 'nrrdIoStateUnknown', 'pullSysParmRadiusScale', 'nrrdKernelSpecParse', 'gageDefCurvNormalSide', 'tijk_approx_heur_parm_nix', 'tenGageCa1GradVec', 'tijk_approx_rankk_2d_d', 'tenGageDelNormK3', 'tenGageDelNormK2', 'biffMsgMovef', 'gageItemPackPartHessEvec0', 'gageItemPackPartHessEvec1', 'gageItemPackPartHessEvec2', 'limnHestSpline', 'nrrdEnvVarStateBlind8BitRange', 'echoLightColor', 'tenFiberSingleNew', 'dyeRGBtoXYZ', 'tijk_2o2d_asym', 'baneInfoCheck', 'unrrdu_mlutCmd', 'hestParmFree', 'tenFiberIntgEuler', 'baneGkmsHestIncStrategy', 'echoTextureLookup', 'pullOutputGet', 'nrrdGetenvUInt', 'pullVolumeNix', 'nrrdBoundaryMirror', 'nrrdFFTWPlanRigor', 'pullCountPointsStuck', 'pullInfoSpec_t', 'nrrdTernaryOpInClosed', 'tenGageFAHessianEvalMode', 'echoTypeSphere', 'gagePerVolumeAttach', 'pushIterate', 'nrrdField_units', 'unrrduUsageUnu', 'ell_aa_to_3m_f', 'ell_aa_to_3m_d', 'baneMeasrNix', 'pullCondConstraintFail', 'tenGageTraceGradVec', 'tenAniso_Cs2', 'alanStopMaxIteration', 'miteUserNew', 'nrrdSimpleCrop', 'tenEstimate1MethodNLS', 'gageKind', 'nrrdSanityOrDie', 'seekStrengthUseSet', 'pullContextNew', 'tenDwiGageTensorNLSErrorLog', 'echoList', 'airMopNew', 'nrrdOriginStatusLast', 'tijk_3o3d_sym', 'gageQueryReset', 'gageDefStackNormalizeDerivBias', 'pullInfoSeedPreThresh', 'alanUpdate', 'seekNormalsFindSet', 'pullEnergySpec', 'airRandMTSanity', 'nrrdKernelCatmullRomSupportDebugD', 'tenFiberSingleInit', 'tenModelBall1Cylinder', 'unrrdu_projectCmd', 'biffCheck', 'pullInitMethodRandom', 'pullInfoSeedThresh', 'tenDwiGageTensorMLEErrorLog', 'hooverRender', 'biffMsgMove', 'tenDwiGage2TensorPeledError', 'tenEstimate1MethodLLS', 'nrrdEnvVarDefaultWriteCharsPerLine', 'tenInterpTwo_d', 'hestParseFree', 'hooverStubRenderEnd', 'pullPoint_t', 'tenGageTrace', 'mossSamplerKernelSet', 'echoRayColor', 'gageSclGradMag', 'nrrdCopy', 'nrrdUnaryOp', 'nrrdKernelTMF', 'pullEnergyButterworth', 'gagePerVolumeNix', 'tenEstimateSkipSet', 'airInsane_nInfExists', 'tenDwiFiberTypeLast', 'gageScl3PFilter_t', 'echoTriMesh', 'tenDwiGageTensorError', 'coilIterate', 'airTypeLongInt', 'nrrdKernelParmSet', 'pullFlagAllowCodimension3Constraints', 'limnPrimitiveLast', 'gageVecMultiGrad', 'tenGageNorm', 'nrrdFFT', 'pullFlagNoAdd', 'tenDwiGage2TensorPeledLevmarInfo', 'baneMeasrNew', 'airLogRician', 'tenGageTraceGradVecDotEvec0', 'tenDwiGageTensorWLSLikelihood', 'nrrdSplice', 'nrrdKernelGaussianDD', 'gageSigOfTau', 'nrrdFFTWEnabled', 'nrrdRangeReset', 'nrrdKind3Color', 'airSrandMT', 'tenGageModeHessianFrob', 'tenGageConfGradVecDotEvec0', 'echoThreadState', 'tenDwiGageTensorNLS', 'baneInputCheck', 'airNoDio_fd', 'pushEnergyGauss', 'tend_tconvCmd', 'nrrdAxisInfoSet_va', 'gageVecNormalized', 'nrrdValCompareInv', 'nrrdStringRead', 'unrrdu_rmapCmd', 'nrrdDeringLinearInterpSet', 'limnPolyDataPrimitiveSelect', 'pullPointInitializePerVoxel', 'ell_q_inv_f', 'pullPropNeighDistMean', 'nrrdArithIterTernaryOpSelect', 'nrrdSpace', 'pullContext', 'NrrdResampleContext', 'hestParse', 'limnObjectFaceReverse', 'miteShadeSpecPrint', 'NrrdIoState_t', 'nrrdEncodingUnknown', 'airFPValToParts_f', 'airFPValToParts_d', 'dyeColor', 'tenFiberStopReset', 'pullInterType', 'pullEnergySpecSet', 'gageCtxFlagNeedD', 'limnPolyDataClip', 'alanParmFrameInterval', 'nrrdKernelC5Septic', 'gageCtxFlagNeedK', 'mossSamplerEmpty', 'limnQN16checker', 'tenExperSpecGradBValSet', 'gageZeroNormal', 'nrrdFFTWPlanRigorEstimate', 'gageSclHessValleyness', 'nrrdSpacingStatus', 'tenGageSGradVec', 'meetPullVol', 'nrrdMeasureCoV', 'airBesselI1By0', 'nrrdBasicInfoSampleUnits', 'nrrdIoStateEncodingSet', 'nrrdMeasureUnknown', 'ell_3m_pre_mul_d', 'ell_3m_pre_mul_f', 'tijk_esh_deconvolve_d', 'tenPresent', 'unrrduBiffKey', 'coilFinish', 'nrrdResampleRoundSet', 'miteShadeMethodPhong', 'airSprintPtrdiff_t', 'biffMsgNew', 'airEndianLast', 'nrrdSpaceVecScale', 'ell_q_to_3m_d', 'nrrdDLoad', 'hestInfo', 'miteStageOpUnknown', 'nrrdField', 'echoPtrPtrUnion', 'nrrdEncodingHex', 'nrrdStateMeasureModeBins', 'tijk_init_max_3d_d', 'gagePerVolume_t', 'limnPolyDataPolarSphere', 'tend_bmatCmd', 'tenDwiGageB0', 'nrrdApplyMulti1DRegMap', 'limnQNLast', 'baneGKMSHVol', 'echoTypeSuperquad', 'limnPolyDataCubeTriangles', 'nrrdKindNormal', 'miteValLast', 'gageErrStackSearch', 'tenGageTraceNormal', 'nrrdKernelBSpline7DD', 'baneRangeAnswer', 'airFPPartsToVal_d', 'nrrdAxesMerge', 'pullEnergyTypeQuartic', 'airIntPow', 'tenModelBall', 'hooverThreadEnd_t', 'tenTripleTypeRThetaPhi', 'nrrdAxisInfoSet_nva', 'nrrdEnvVarStateGrayscaleImage3D', 'tenEstimateBMatricesSet', 'airHeapLength', 'tenGageEvec2', 'unrrdu_undosCmd', 'gageParmSet', 'nrrdRead', 'pullFlagNoPopCntlWithZeroAlpha', 'nrrdElementNumber', 'tijk_eval_esh_basis_f', 'nrrdDeringExecute', 'tenDefFiberKernel', 'echoJitterCompute', 'tijk_class_efs', 'airInsane_AIR_NAN', 'nrrdBinaryOpUnknown', 'nrrdDefaultResampleRenormalize', 'tenEvecRGBParmNix', 'nrrdUnaryOpNormalRand', 'tend_msimCmd', 'seekTypeValleySurface', 'airShuffle', 'dyeSpaceXYZ', 'tenGageCl1HessianEval', 'mossBiffKey', 'tenAniso_Cl1', 'tenAniso_Cl2', 'nrrdFormatTypePNG', 'airULLong', 'gageKernel', 'gageSclGeomTens', 'nrrdKernelBSpline3DDD', 'ell_cubic_root_single_double', 'airEnum', 'nrrdCenterCell', 'hooverErrThreadBegin', 'nrrdUnaryOpUnknown', 'elfCart2Thetaphi_f', 'elfCart2Thetaphi_d', 'baneGkms_opacCmd', 'airNaN', 'limnPolyDataVertexWindingFix', 'miteStageOp', 'gageDefStackBlurSigmaMax', 'dyeSpaceHSV', 'tenEstimate1MethodLast', 'gageShapeBoundingBox', 'limnObjectPartTransform', 'gageTauOfSig', 'nrrdField_byte_skip', 'pullPropNeighInterNum', 'airDisableDio', 'airArrayLenSet', 'gageQuery', 'pullEnergyPlot', 'gageScl3PFilter8', 'nrrdSpaceRightAnteriorSuperiorTime', 'tenFiberContext', 'gageScl3PFilter2', 'nrrdBlind8BitRangeTrue', 'gageScl3PFilter6', 'dyeSpaceHSL', 'nrrdSpaceScannerXYZTime', 'echoTypeCube', 'nrrdCommentCopy', 'airSanity', 'tenFiberContextDwiNew', 'nrrdKernelTMF_maxA', 'dyeSpaceToStr', 'nrrdKindDomain', 'nrrdKernelTMF_maxD', 'unrrduHestPosCB', 'nrrdKernelTent', 'unrrduHestFileCB', 'gageParmReset', 'airDouble', 'mossSamplerImageSet', 'tenFiberType', 'airInsane_DLSize', 'baneRangeUnknown', 'nrrdElementSize', 'limnCameraNew', 'gageVecUnknown', 'pullSysParmConstraintStepMin', 'tenGlyphTypeBetterquad', 'echoCol_t', 'echoListSplit', 'pullPropScale', 'gageKernelLast', 'airStrtok', 'tenGageTraceHessianFrob', 'seekContour3DTopoHackEdge', 'nrrdBlind8BitRangeUnknown', 'limnQNDemo', 'gageScl3PFilterN', 'unrrdu_spliceCmd', 'nrrdAxisInfoMax', 'tijk_1o3d', 'nrrdTypeLLong', 'nrrdKernelBSpline6DD', 'echoMatterGlass', 'unrrdu_sselectCmd', 'pullIterParmCallback', 'pullStatusEdge', 'nrrdIoStateKeepNrrdDataFileOpen', 'nrrdMaybeAlloc_nva', 'nrrdSpaceUnknown', 'tenModelPrefixStr', 'meetPullVolNix', '_airThreadMutex', 'nrrdWrap_nva', 'nrrdPresent', 'nrrdNix', 'nrrdResampleInfoNix', 'pushBinProcess', 'tenGageFALaplacian', 'nrrdCCSettle', 'gagePvlFlagLast', 'mossSamplerSample', 'ell_q_4v_rotate_f', 'airFP_POS_NORM', 'limnPrimitiveTriangleFan', 'nrrdField_type', 'unrrdu_mrmapCmd', 'miteThreadEnd', 'pullInfoHeightHessian', 'gageSclLast', 'tenInterpTypeAffineInvariant', 'airMopSub', 'dyeHSLtoRGB', 'unrrdu_axinfoCmd', 'tenGageCl1Normal', 'tenGageCa1Hessian', 'limnDefCameraOrthographic', 'airDioWrite', 'nrrdStateKindNoop', 'nrrdTypeDouble', 'tenDwiGageTensorMLELikelihood', 'airMopOkay', 'gagePvlFlagVolume', 'NrrdEncoding', 'tenFiberStopOff', 'nrrdBoundaryPad', 'hestPresent', 'tenEstimate1TensorVolume4D', 'nrrdMeasureHistoVariance', 'tenFiberTypeSet', 'baneGkmsHestBEF', 'limnSplineTypeHasImplicitTangents', 'tenTripleTypeXYZ', 'tenAnisoLast', 'nrrdKernelCheck', 'nrrdDescribe', 'limnObjectEdgeAdd', 'gageStackProbeSpace', 'nrrdDeringRadialKernelSet', 'nrrdKernelBox', 'tenModelBall1StickEMD', 'coilContextAllSet', 'gageContext_t', 'nrrdTypeUInt', 'tenShrink', 'tenLogSingle_f', 'limnObjectCylinderAdd', 'gageErrBoundsSpace', 'gageSclShapeIndex', 'pullSysParmOpporStepScale', 'nrrdKernelHannDD', 'limnQN10octa', 'pullConstraintScaleRange', 'echoIntxFuzzify', 'nrrdTypeUnknown', 'nrrdResampleContextNix', 'hooverStubRayEnd', 'limnSplineBCSet', 'limnPolyDataSpiralBetterquadric', 'pullCountUnknown', 'limnQNBins', 'ell_Nm_wght_pseudo_inv', 'limnObjectSquareAdd', 'limnSplineCleverNew', 'nrrdMeasureRootMeanSquare', 'limnQN14checker', 'tend_estimCmd', 'gageContextNew', 'tend_evecCmd', 'unrrdu_dataCmd', 'tenGageEval', 'coilKindType3Color', 'alanParmReact', 'elfESHEstimMatrix_f', 'pullCountEnergyFromImage', 'tenModelB0', 'mossMatFlipSet', 'nrrdZlibStrategyLast', 'unrrdu_headCmd', 'limnSplineInfoScalar', 'nrrdField_old_max', 'hooverErrThreadEnd', 'seekTypeValleySurfaceOP', 'mossSampler', 'gageSclFlowlineCurv', 'tenExperSpecKnownB0Get', 'nrrdMeasureProduct', 'nrrdTernaryOpGaussian', 'pullPointInitializeGivenPos', 'airNoDio_small', 'gageSclUnknown', 'nrrdResampleRangeFullSet', 'pullSysParm', 'nrrdDeringThetaKernelSet', 'limnPolyDataSize', 'airMyQNaNHiBit', 'coilMethodTypeCurvatureFlow', 'pullInitMethodUnknown', 'pushStart', 'tend_gradsCmd', 'tenFiberParmVerbose', 'limnObjectLookAdd', 'tijk_refine_rankk_2d_f', 'tenModelNllFit', 'tenFiberMultiTrace', 'limnEdgeTypeBorder', 'nrrdSpaceOriginGet', 'nrrdBoundaryUnknown', 'tenInv_f', 'tenInv_d', 'baneHVolCheck', 'pullInitLiveThreshUseSet', 'tenGageCl1HessianEval2', 'tenTripleTypeUnknown', 'tenGageCl1HessianEval0', 'tenGageCl1HessianEval1', 'pullTraceStopStub', 'tenFiberStopUnknown', 'tenDwiGageLast', 'meetPullInfoNew', 'limnLightSwitch', 'echoObjectNew', 'tijk_init_max_2d_d', 'pullTraceMultiAdd', 'nrrdMeasureLineIntercept', 'airNoDioErr', 'nrrdResample_t', 'nrrdUnaryOpRoundDown', 'coilVerbose', 'pullProcessMode', 'airArrayPointerCB', 'airEnumCheck', 'nrrdOriginStatusUnknown', 'nrrdKind2Vector', 'nrrdEnvVarDefaultKernelParm0', 'nrrdAxisInfoIdxRange', 'echoMatterLight', 'gageItemPackPartUnknown', 'tijk_2o3d_sym', 'gageDeconvolve', 'gageSclHessEval1', 'gageSclHessEval0', 'gageSclHessEval2', 'nrrdDeringThetaNumSet', 'tenEstimateLinear4D', 'nrrdDefaultGetenv'] # ============================================================= # What follows are the all #define's in Teem, excluding macros, # and #defines that depend on compile-time tests done by the # C pre-processor. # This is created by something akin to grep'ing through the # public header files, with some extra filters. TEEM_VERSION_MAJOR = 1 # must be 1 digit TEEM_VERSION_MINOR = 11 # 1 or 2 digits TEEM_VERSION_PATCH = 01 # 1 or 2 digits TEEM_VERSION = 11101 # must be 5 digits, to facilitate TEEM_VERSION_STRING = "1.11.1" # cannot be so easily compared AIR_PI = 3.14159265358979323846 AIR_E = 2.71828182845904523536 AIR_STRLEN_SMALL = (128+1) # has to be big enough to hold: AIR_STRLEN_MED = (256+1) AIR_STRLEN_LARGE = (512+1) AIR_STRLEN_HUGE = (1024+1) # has to be big enough to hold AIR_RANDMT_N = 624 AIR_TYPE_MAX = 12 AIR_INSANE_MAX = 11 AIR_PRIME_NUM = 1000 AIR_NODIO_MAX = 12 AIR_TRUE = 1 AIR_FALSE = 0 AIR_ENDIAN = (airMyEndian()) AIR_QNANHIBIT = (airMyQNaNHiBit) AIR_DIO = (airMyDio) AIR_NAN = (airFloatQNaN.f) AIR_QNAN = (airFloatQNaN.f) AIR_SNAN = (airFloatSNaN.f) AIR_POS_INF = (airFloatPosInf.f) AIR_NEG_INF = (airFloatNegInf.f) ALAN = alanBiffKey ALAN_THREAD_MAX = 256 ALAN_STOP_MAX = 5 BANE = baneBiffKey BANE_PARM_NUM = 5 COIL = coilBiffKey COIL_PARMS_NUM = 6 COIL_METHOD_TYPE_MAX = 8 COIL_KIND_TYPE_MAX = 3 DYE = dyeBiffKey DYE_MAX_SPACE = 6 ECHO = echoBiffKey ECHO_LIST_OBJECT_INCR = 32 ECHO_IMG_CHANNELS = 5 ECHO_EPSILON = 0.00005 # used for adjusting ray positions ECHO_NEAR0 = 0.004 # used for comparing transparency to zero ECHO_LEN_SMALL_ENOUGH = 5 # to control splitting for split objects ECHO_THREAD_MAX = 512 # max number of threads ECHO_JITTER_NUM = 4 ECHO_JITTABLE_NUM = 7 ECHO_MATTER_MAX = 4 ECHO_MATTER_PARM_NUM = 4 ECHO_TYPE_NUM = 12 ELL = ell_biff_key ELL_EPS = 1.0e-10 ELL_CUBIC_ROOT_MAX = 4 GAGE = gageBiffKey GAGE_DERIV_MAX = 2 GAGE_ERR_MAX = 6 GAGE_CTX_FLAG_MAX = 6 GAGE_PVL_FLAG_MAX = 3 GAGE_KERNEL_MAX = 7 GAGE_ITEM_PREREQ_MAXNUM = 8 GAGE_SCL_ITEM_MAX = 35 GAGE_VEC_ITEM_MAX = 31 GAGE_ITEM_PACK_PART_MAX = 11 GAGE_QUERY_BYTES_NUM = 32 GAGE_ITEM_MAX = ((8*GAGE_QUERY_BYTES_NUM)-1) GAGE_PERVOLUME_ARR_INCR = 32 GAGE_OPTIMSIG_SIGMA_MAX = 11 GAGE_OPTIMSIG_SAMPLES_MAXNUM = 11 HOOVER = hooverBiffKey HOOVER_THREAD_MAX = 512 HOOVER_ERR_MAX = 10 LIMN = limnBiffKey LIMN_LIGHT_NUM = 8 LIMN_SPLINE_Q_AVG_EPS = 0.00001 LIMN_EDGE_TYPE_MAX = 7 LIMN_SPACE_MAX = 4 LIMN_PRIMITIVE_MAX = 7 LIMN_POLY_DATA_INFO_MAX = 4 LIMN_QN_MAX = 16 LIMN_SPLINE_TYPE_MAX = 5 LIMN_SPLINE_INFO_MAX = 6 LIMN_CAMERA_PATH_TRACK_MAX = 3 MEET = meetBiffKey MITE = miteBiffKey MITE_RANGE_NUM = 9 MITE_STAGE_OP_MAX = 4 MITE_VAL_ITEM_MAX = 19 MOSS = mossBiffKey MOSS_FLAG_NUM = 2 NRRD = nrrdBiffKey NRRD_DIM_MAX = 16 # Max array dimension (nrrd->dim) NRRD_SPACE_DIM_MAX = 8 # Max dimension of "space" around array NRRD_EXT_NRRD = ".nrrd" NRRD_EXT_NHDR = ".nhdr" NRRD_EXT_PGM = ".pgm" NRRD_EXT_PPM = ".ppm" NRRD_EXT_PNG = ".png" NRRD_EXT_VTK = ".vtk" NRRD_EXT_TEXT = ".txt" NRRD_EXT_EPS = ".eps" NRRD_KERNEL_PARMS_NUM = 8 # max # arguments to a kernel- NRRD_MINMAX_PERC_SUFF = "%" NRRD_COMMENT_CHAR = '#' NRRD_FILENAME_INCR = 32 NRRD_COMMENT_INCR = 16 NRRD_KEYVALUE_INCR = 32 NRRD_LIST_FLAG = "LIST" NRRD_PNM_COMMENT = "# NRRD>" # this is designed to be robust against NRRD_PNG_FIELD_KEY = "NRRD" # this is the key used for getting nrrd NRRD_PNG_COMMENT_KEY = "NRRD#" # this is the key used for getting nrrd NRRD_UNKNOWN = "???" # how to represent something unknown in NRRD_NONE = "none" # like NRRD_UNKNOWN, but with an air NRRD_FORMAT_TYPE_MAX = 6 NRRD_BOUNDARY_MAX = 5 NRRD_TYPE_MAX = 11 NRRD_TYPE_SIZE_MAX = 8 # max(sizeof()) over all scalar types NRRD_ENCODING_TYPE_MAX = 5 NRRD_ZLIB_STRATEGY_MAX = 3 NRRD_CENTER_MAX = 2 NRRD_KIND_MAX = 31 NRRD_AXIS_INFO_SIZE_BIT = (1<< 1) NRRD_AXIS_INFO_SPACING_BIT = (1<< 2) NRRD_AXIS_INFO_THICKNESS_BIT = (1<< 3) NRRD_AXIS_INFO_MIN_BIT = (1<< 4) NRRD_AXIS_INFO_MAX_BIT = (1<< 5) NRRD_AXIS_INFO_SPACEDIRECTION_BIT = (1<< 6) NRRD_AXIS_INFO_CENTER_BIT = (1<< 7) NRRD_AXIS_INFO_KIND_BIT = (1<< 8) NRRD_AXIS_INFO_LABEL_BIT = (1<< 9) NRRD_AXIS_INFO_UNITS_BIT = (1<<10) NRRD_AXIS_INFO_MAX = 10 NRRD_AXIS_INFO_NONE = 0 NRRD_BASIC_INFO_DATA_BIT = (1<< 1) NRRD_BASIC_INFO_TYPE_BIT = (1<< 2) NRRD_BASIC_INFO_BLOCKSIZE_BIT = (1<< 3) NRRD_BASIC_INFO_DIMENSION_BIT = (1<< 4) NRRD_BASIC_INFO_CONTENT_BIT = (1<< 5) NRRD_BASIC_INFO_SAMPLEUNITS_BIT = (1<< 6) NRRD_BASIC_INFO_SPACE_BIT = (1<< 7) NRRD_BASIC_INFO_SPACEDIMENSION_BIT = (1<< 8) NRRD_BASIC_INFO_SPACEUNITS_BIT = (1<< 9) NRRD_BASIC_INFO_SPACEORIGIN_BIT = (1<<10) NRRD_BASIC_INFO_MEASUREMENTFRAME_BIT = (1<<11) NRRD_BASIC_INFO_OLDMIN_BIT = (1<<12) NRRD_BASIC_INFO_OLDMAX_BIT = (1<<13) NRRD_BASIC_INFO_COMMENTS_BIT = (1<<14) NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT = (1<<15) NRRD_BASIC_INFO_MAX = 15 NRRD_BASIC_INFO_NONE = 0 NRRD_FIELD_MAX = 32 NRRD_HAS_NON_EXIST_MAX = 3 NRRD_SPACE_MAX = 12 NRRD_SPACING_STATUS_MAX = 4 NRRD_MEASURE_MAX = 29 NRRD_BLIND_8BIT_RANGE_MAX = 3 NRRD_UNARY_OP_MAX = 30 NRRD_BINARY_OP_MAX = 23 NRRD_TERNARY_OP_MAX = 16 NRRD_FFTW_PLAN_RIGOR_MAX = 4 NRRD_RESAMPLE_NON_EXISTENT_MAX = 3 PULL = pullBiffKey PULL_THREAD_MAXNUM = 512 PULL_VOLUME_MAXNUM = 4 PULL_POINT_NEIGH_INCR = 16 PULL_BIN_MAXNUM = 40000000 # sanity check on max number bins PULL_PHIST = 0 PULL_HINTER = 0 PULL_TANCOVAR = 1 PULL_INFO_MAX = 23 PULL_PROP_MAX = 17 PULL_STATUS_STUCK_BIT = (1<< 1) PULL_STATUS_NEWBIE_BIT = (1<< 2) PULL_STATUS_NIXME_BIT = (1<< 3) PULL_STATUS_EDGE_BIT = (1<< 4) PULL_INTER_TYPE_MAX = 4 PULL_ENERGY_TYPE_MAX = 13 PULL_ENERGY_PARM_NUM = 3 PULL_PROCESS_MODE_MAX = 4 PULL_SOURCE_MAX = 2 PULL_COUNT_MAX = 14 PULL_TRACE_STOP_MAX = 5 PULL_CONSTRAINT_FAIL_MAX = 4 PUSH = pushBiffKey PUSH_THREAD_MAXNUM = 512 PUSH_ENERGY_TYPE_MAX = 5 PUSH_ENERGY_PARM_NUM = 3 SEEK = seekBiffKey SEEK_TYPE_MAX = 11 TEN = tenBiffKey TEN_ANISO_MAX = 29 TEN_INTERP_TYPE_MAX = 11 TEN_GLYPH_TYPE_MAX = 6 TEN_GAGE_ITEM_MAX = 207 TEN_DWI_GAGE_ITEM_MAX = 35 TEN_ESTIMATE_1_METHOD_MAX = 4 TEN_ESTIMATE_2_METHOD_MAX = 2 TEN_FIBER_TYPE_MAX = 6 TEN_DWI_FIBER_TYPE_MAX = 3 TEN_FIBER_INTG_MAX = 3 TEN_FIBER_STOP_MAX = 10 TEN_FIBER_NUM_STEPS_MAX = 10240 TEN_FIBER_PARM_MAX = 4 TEN_TRIPLE_TYPE_MAX = 9 TEN_MODEL_B0_MAX = 65500 # HEY: fairly arbitrary, but is set to be TEN_MODEL_DIFF_MAX = 0.006 # in units of mm^2/sec; diffusivity of TEN_MODEL_PARM_GRAD_EPS = 0.000005 # for gradient calculations TEN_MODEL_STR_ZERO = "zero" TEN_MODEL_STR_B0 = "b0" TEN_MODEL_STR_BALL = "ball" TEN_MODEL_STR_1VECTOR2D = "1vector2d" TEN_MODEL_STR_1UNIT2D = "1unit2d" TEN_MODEL_STR_2UNIT2D = "2unit2d" TEN_MODEL_STR_1STICK = "1stick" TEN_MODEL_STR_BALL1STICKEMD = "ball1stickemd" TEN_MODEL_STR_BALL1STICK = "ball1stick" TEN_MODEL_STR_BALL1CYLINDER = "ball1cylinder" TEN_MODEL_STR_1CYLINDER = "1cylinder" TEN_MODEL_STR_1TENSOR2 = "1tensor2" TEN_DWI_GAGE_KIND_NAME = "dwi" TIJK_TYPE_MAX_NUM = 45 TIJK_CLASS_MAX = 3 UNRRDU = unrrduBiffKey UNRRDU_COLUMNS = 78 # how many chars per line do we allow hest # ============================================================= # Make sure this shared library will work on this machine. if not nrrdSanity(): errstr = biffGetDone(NRRD) print "**" print "** Sorry, there is a problem (described below) with the " print "** Teem shared library that prevents its use. This will " print "** have to be fixed by recompiling the Teem library for " print "** this platform. " print "**" print "** %s" % errstr raise ImportError # ============================================================= # Its nice to have these FILE*s around for utility use, but they # aren't available in a platform-independent way in ctypes. These # air functions were created for this purpose. stderr = airStderr() stdout = airStdout() stdin = airStdin() teem-1.11.0~svn6057/python/ctypes/stdio.py0000664000175000017500000000073311272122702020130 0ustar domibeldomibelfrom ctypes import * from ctypes.util import find_library class FILE(Structure): pass # oddly, size_t is in ctypes, but not ptrdiff_t if sizeof(c_void_p) == 4: ptrdiff_t = c_int32 elif sizeof(c_void_p) == 8: ptrdiff_t = c_int64 #libc = CDLL(find_library("c")) # HEY these are probably Mac-specific, please fix #stdin = POINTER(FILE).in_dll(libc, "__stdinp") #stderr = POINTER(FILE).in_dll(libc, "__stderrp") #stdout = POINTER(FILE).in_dll(libc, "__stdoutp") teem-1.11.0~svn6057/python/ctypes/Nrrd.py0000775000175000017500000001665212165630736017742 0ustar domibeldomibel#!/usr/bin/env python ## ## Nrrd.py: bridge between Nrrd and Numpy arrays ## Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago ## created by Sam Quinan - samquinan@cs.uchicago.edu ## ## Permission is hereby granted, free of charge, to any person obtaining ## a copy of this software and associated documentation files (the ## "Software"), to deal in the Software without restriction, including ## without limitation the rights to use, copy, modify, merge, publish, ## distribute, sublicense, and/or sell copies of the Software, and to ## permit persons to whom the Software is furnished to do so, subject to ## the following conditions: ## ## The above copyright notice and this permission notice shall be ## included in all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ## LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ## OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ## WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ## import numpy, teem, ctypes # translates from numpy type to teem type def ndarrayGetTypes( array ): typeTable = { numpy.int8 : (ctypes.c_byte, teem.nrrdTypeChar), numpy.uint8 : (ctypes.c_ubyte, teem.nrrdTypeUChar), numpy.int16 : (ctypes.c_short, teem.nrrdTypeShort), numpy.uint16 : (ctypes.c_ushort, teem.nrrdTypeUShort), numpy.int : (ctypes.c_int, teem.nrrdTypeInt), numpy.uint : (ctypes.c_uint, teem.nrrdTypeUInt), numpy.int32 : (ctypes.c_int32, teem.nrrdTypeInt), numpy.uint32 : (ctypes.c_uint32, teem.nrrdTypeUInt), numpy.int64 : (ctypes.c_int64, teem.nrrdTypeLLong), numpy.uint64 : (ctypes.c_uint64, teem.nrrdTypeULLong), numpy.float : (ctypes.c_float, teem.nrrdTypeFloat), numpy.double : (ctypes.c_double, teem.nrrdTypeDouble) } dt = array.dtype.type if dt not in typeTable: raise Exception("array type %s not supported" % dt) # sanity checking ct, tt = typeTable[dt] x = numpy.dtype(dt).itemsize y = ctypes.sizeof(ct) z = teem.nrrdTypeSize[tt] if not (x == y) or not (x == z) or not (y == z): raise Exception("corresponding numpy, teem, and ctypes types are not of the same size") return typeTable[dt] def teemTypeToNumpyType(teem_type): numpy_endianess_glyph = '<' if teem.airMyEndian() == 1234 else '>' numpy_type_map = { teem.nrrdTypeChar: numpy_endianess_glyph + 'i1', teem.nrrdTypeUChar: numpy_endianess_glyph + 'u1', teem.nrrdTypeShort: numpy_endianess_glyph + 'i2', teem.nrrdTypeUShort: numpy_endianess_glyph + 'u2', teem.nrrdTypeInt: numpy_endianess_glyph + 'i4', teem.nrrdTypeUInt: numpy_endianess_glyph + 'u4', teem.nrrdTypeLLong: numpy_endianess_glyph + 'i8', teem.nrrdTypeULLong: numpy_endianess_glyph + 'u8', teem.nrrdTypeFloat: numpy_endianess_glyph + 'f4', teem.nrrdTypeDouble: numpy_endianess_glyph + 'f8' } return numpy_type_map[teem_type] # design based on a combination of work done by Carlos Scheidegger # [ http://code.google.com/p/python-teem/source/browse/trunk/teem/capi/numpy/__init__.py , # http://code.google.com/p/python-teem/source/browse/trunk/teem/nrrd.py ] # and ideas presented by Travis Oliphant [ http://blog.enthought.com/?p=62 ] class Nrrd: def __init__(self): self._ctypesobj = teem.nrrdNew() # self._init = False self.base_ref = None self.teem = teem # world's ugliest hack -- but it appears to work def __del__(self): if self.base_ref == None: self.teem.nrrdNuke(self._ctypesobj) else: self.teem.nrrdNix(self._ctypesobj) self.base_ref = None self.teem = None def __get_array_interface(self): nrrd = self._ctypesobj.contents s = [] for i in reversed(range(nrrd.dim)): s.append(nrrd.axis[i].size) r = {'shape': tuple(s), 'typestr': teemTypeToNumpyType(nrrd.type), 'data': (nrrd.data, False), 'version': 3} return r __array_interface__ = property(__get_array_interface) def fromNDArray(self, array): if self.base_ref != None: self.base_ref = None teem.nrrdNix(self._ctypesobj) self._ctypesobj = teem.nrrdNew() try: cTy, teemTy = ndarrayGetTypes(array) except Exception as err: print err return c_type_p = ctypes.POINTER( cTy ) if (array.flags.f_contiguous): # memory shared with array array_p = array.ctypes.data_as(c_type_p) dims = list(array.shape) sizes = (ctypes.c_size_t * teem.NRRD_DIM_MAX)(*dims) teem.nrrdWrap_nva(self._ctypesobj, array_p, teemTy, ctypes.c_uint(array.ndim), sizes) self.base_ref = array elif (array.flags.c_contiguous): # memory shared with array array_p = array.ctypes.data_as(c_type_p) dims = list(reversed(array.shape)) sizes = (ctypes.c_size_t * teem.NRRD_DIM_MAX)(*dims) teem.nrrdWrap_nva(self._ctypesobj, array_p, teemTy, ctypes.c_uint(array.ndim), sizes) self.base_ref = array else: # must make c_contiguous -- memory not shared with array, transfer full control to teem arr = numpy.ascontiguousarray(array, dtype=array.dtype.type) array_p = arr.ctypes.data_as(c_type_p) dims = list(reversed(arr.shape)) nrrd_tmp = teem.nrrdNew() sizes = (ctypes.c_size_t * teem.NRRD_DIM_MAX)(*dims) teem.nrrdWrap_nva(nrrd_tmp, array_p, teemTy, ctypes.c_uint(array.ndim), sizes) teem.nrrdCopy(self._ctypesobj, nrrd_tmp) # this call hangs with fmob-ch4.nrrd teem.nrrdNix(nrrd_tmp) self.base_ref = None self._init = True if isinstance(array, ExtendedArray): if not array.base_ref is None: # means there is some Nrrd file sharing it's memory with the array teem.nrrdAxisInfoCopy(self._ctypesobj, array.base_ref._ctypesobj, None, teem.NRRD_AXIS_INFO_SIZE_BIT) def load(self, file, args=None): if self.base_ref != None: self.base_ref = None teem.nrrdNix(self._ctypesobj) self._ctypesobj = teem.nrrdNew() teem.nrrdLoad(self._ctypesobj, file, args) self.base_ref = None self._init = True def save(self, file, args=None): teem.nrrdSave(file, self._ctypesobj, args) class ExtendedArray(numpy.ndarray): def __new__(cls, input_array, base_ref=None): if isinstance(input_array, Nrrd): if input_array.base_ref == None: obj = numpy.asarray(input_array).view(cls) obj.base_ref = input_array else: obj = numpy.asarray(input_array.base_ref).view(cls) obj.base_ref = None else: obj = numpy.asarray(input_array).view(cls) obj.base_ref = base_ref return obj def __array_finalize__(self, obj): if obj is None: return self.base_ref = getattr(obj, 'base_ref', None) teem-1.11.0~svn6057/python/ctypes/mobius.py0000664000175000017500000000150411601232727020306 0ustar domibeldomibel import sys import teem import pullDemo import ctypes npos = teem.nrrdNew() vol = pullDemo.volLoad(['../../data/fmob-c4h.nrrd:scalar:V'], './', 'ds:1,5', 1) infoList = ['h-c:V:val:0:-1', 'hgvec:V:gvec', 'hhess:V:hess', 'tan1:V:hevec2', 'strn:V:heval2:0:-1', 'lthr:V:heval2:-70:-1', 'sthr:V:heval2:-70:-1'] pullDemo.run(npos, init=[teem.pullInitMethodRandom, 300], efs=False, vol=vol, info=infoList, energy={'type':teem.pullInterTypeJustR, 'r':'cwell:0.6,-0.002'}, iterMax=200, radSpace=0.05) pullDemo.volFree(vol) if (teem.nrrdSave("npos.nrrd", npos, None)): estr = teem.biffGetDone('nrrd') print "problem running system:\n%s" % estr sys.exit(1) teem-1.11.0~svn6057/python/ctypes/pullDemo.py0000664000175000017500000003171312007007646020577 0ustar domibeldomibel## ## pullDemo.py: sample wrappers for pull API ## Copyright (C) 2010, 2009, Gordon Kindlmann ## ## Permission is hereby granted, free of charge, to any person obtaining ## a copy of this software and associated documentation files (the ## "Software"), to deal in the Software without restriction, including ## without limitation the rights to use, copy, modify, merge, publish, ## distribute, sublicense, and/or sell copies of the Software, and to ## permit persons to whom the Software is furnished to do so, subject to ## the following conditions: ## ## The above copyright notice and this permission notice shall be ## included in all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ## LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ## OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ## WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ## ''' This is really hastily written, and doesn't conform in the least to good conventions of Python coding. The worst part is using exit() instead of raising exceptions when tings go wrong. The "API" here (this python interface to the pull library) is not well thought-out, and won't be helpful for integrating particles in other applications. Better designed interfaces should come in the future, as more and more of Teem develops idiomatic python interfaces. The code here is mainly to serve as a demo of using the pull library in Teem (whose API should be quite stable), and to simplify documenting using pull to produce typical results. ''' import teem import ctypes import sys ## ## volLoad: loads in volumes ## def volLoad(vlist, cachePath, kssBlurStr, verbose): kSSblur = teem.nrrdKernelSpecNew() if (teem.nrrdKernelSpecParse(kSSblur, kssBlurStr)): estr = teem.biffGetDone('nrrd') print "problem with kernel:\n%s" % estr sys.exit(1) vol = (ctypes.POINTER(teem.meetPullVol) * len(vlist))() E = 0 for i in range(len(vlist)): vol[i] = teem.meetPullVolNew() if (not E): E += teem.meetPullVolParse(vol[i], vlist[i]) if (not E): E += teem.meetPullVolLoadMulti(vol, len(vol), cachePath, kSSblur, teem.nrrdBoundaryWrap, 0.0, verbose) if (E): estr = teem.biffGetDone('meet') print "problem with volumes:\n%s" % estr sys.exit(1) teem.nrrdKernelSpecNix(kSSblur) return vol def volFree(vol): for i in range(len(vol)): vol[i] = teem.meetPullVolNix(vol[i]) ## ## especParse: returns a length-4 list of the arguments to pass ## to pullInterEnergySet. Arguments are: ## type: from pullIterType* enum ## r: string definition for energy function along space (especR) ## s: string definition for energy function along scale (especS) ## w: string definition for windowing energy function (especW) ## def energyParse(**kwargs): type = kwargs['type'] if (teem.pullInterTypeJustR == type or teem.pullInterTypeUnivariate == type): espec = [teem.pullEnergySpecNew(), None, None] elif (teem.pullInterTypeSeparable == type): espec = [teem.pullEnergySpecNew(), teem.pullEnergySpecNew(), None] elif (teem.pullInterTypeAdditive == type): espec = [teem.pullEnergySpecNew(), teem.pullEnergySpecNew(), teem.pullEnergySpecNew()] lett = ['r', 's', 'w'] E = 0 for i in range(3): if (not E and espec[i]): E += teem.pullEnergySpecParse(espec[i], kwargs[lett[i]]) if (E): estr = teem.biffGetDone('pull') print "problem with infos:\n%s" % estr sys.exit(1) ret = [type, espec[0], espec[1], espec[2]] return ret def energyFree(espec): espec[1] = teem.pullEnergySpecNix(espec[1]) espec[2] = teem.pullEnergySpecNix(espec[2]) espec[3] = teem.pullEnergySpecNix(espec[3]) return ## ## initSet: calls (and returns value from) the appropriate ## pullInitMethod* function. Possibilities are: ## [teem.pullInitMethodRandom, num] ## [teem.pullInitMethodPointPerVoxel, ## ppv, zslcmin, zslcmax, alongscalenum, jitter] ## [teem.pullInitMethodGivenPos, npos] ## def initSet(pctx, info): if (teem.pullInitMethodRandom == info[0]): return teem.pullInitRandomSet(pctx, info[1]) elif (teem.pullInitMethodPointPerVoxel == info[0]): return teem.pullInitPointPerVoxelSet(pctx, info[1], info[2], info[3], info[4], info[5]) elif (teem.pullInitMethodGivenPos == info[0]): return teem.pullInitGivenPosSet(pctx, info[1]) ## place for storing pullContext->sysParm.gamma gamma = 0 ## way of getting arguments that allows for checking whether ## there were any bogus options def a(args, a, default=None): if default is None: ret = args.get(a) else: ret = args.get(a, default) if a in args: del args[a] return ret ## ## All the set-up and commands that are required to do a pullContext run ## def run(nposOut, **args): global gamma if (not ('vol' in args and 'info' in args and 'efs' in args)): print "run: didn't get vol, info, and efs args" sys.exit(1) # learn args, with defaults vol = a(args, 'vol') infoStr = a(args, 'info') efs = a(args, 'efs') init = a(args, 'init', [teem.pullInitMethodRandom, 100]) energyDict = a(args, 'energy', {'type':teem.pullInterTypeJustR, 'r':'qwell:0.68'}) verbose = a(args, 'verbose', 1) rngSeed = a(args, 'rngSeed', 42) nobin = a(args, 'nobin', False) noadd = a(args, 'noadd', False) ac3c = a(args, 'ac3c', False) usa = a(args, 'usa', False) nave = a(args, 'nave', True) cbst = a(args, 'cbst', True) ratb = a(args, 'ratb', True) lti = a(args, 'lti', False) npcwza = a(args, 'npcwza', False) ubfgl = a(args, 'ubfgl', False) iterMin = a(args, 'iterMin', 0) iterMax = a(args, 'iterMax', 100) snap = a(args, 'snap', 0) pcp = a(args, 'pcp', 5) iad = a(args, 'iad', 10) radSpace = a(args, 'radSpace', 1) radScale = a(args, 'radScale', 1) alpha = a(args, 'alpha', 0.5) beta = a(args, 'beta', 0.5) gamma = a(args, 'gamma', 1) stepInitial = a(args, 'step', 1) ssBack = a(args, 'ssBack', 0.2) ssOppor = a(args, 'ssOppor', 1.1) pbm = a(args, 'pbm', 50) k00Str = a(args, 'k00', 'c4h') k11Str = a(args, 'k11', 'c4hd') k22Str = a(args, 'k22', 'c4hdd') kSSreconStr = a(args, 'kSSrecon', 'hermite') eip = a(args, 'eip', 0.00005) eiphl = a(args, 'eiphl', 0) fnnm = a(args, 'fnnm', 0.25) edmin = a(args, 'edmin', 0.00001) edpcmin = a(args, 'edpcmin', 0.01) maxci = a(args, 'maxci', 15) if args: print print "sorry, got unrecognized arguments:" print args sys.exit(1) # create pullContext and set up all its state. Its kind of silly that # the pullContext is created anew everytime we want to run it, but thats # because currently the pullContext doesn't have the kind of internal # network of flags (like the gageContext does) that allow the context # to be modifyied and re-used indefinitely. This will be fixed after # the Teem v1.11 release. pctx = teem.pullContextNew() energyList = energyParse(**energyDict) if (teem.pullVerboseSet(pctx, verbose) or teem.pullRngSeedSet(pctx, rngSeed) or teem.pullFlagSet(pctx, teem.pullFlagNixAtVolumeEdgeSpace, nave) or teem.pullFlagSet(pctx, teem.pullFlagConstraintBeforeSeedThresh, cbst) or teem.pullFlagSet(pctx, teem.pullFlagEnergyFromStrength, efs) or teem.pullFlagSet(pctx, teem.pullFlagRestrictiveAddToBins, ratb) or teem.pullFlagSet(pctx, teem.pullFlagNoPopCntlWithZeroAlpha, npcwza) or teem.pullFlagSet(pctx, teem.pullFlagUseBetaForGammaLearn, ubfgl) or teem.pullFlagSet(pctx, teem.pullFlagBinSingle, nobin) or teem.pullFlagSet(pctx, teem.pullFlagNoAdd, noadd) or teem.pullFlagSet(pctx, teem.pullFlagAllowCodimension3Constraints, ac3c) or teem.pullInitUnequalShapesAllowSet(pctx, usa) or teem.pullIterParmSet(pctx, teem.pullIterParmMin, iterMin) or teem.pullIterParmSet(pctx, teem.pullIterParmMax, iterMax) or teem.pullIterParmSet(pctx, teem.pullIterParmSnap, snap) or teem.pullIterParmSet(pctx, teem.pullIterParmConstraintMax, maxci) or teem.pullIterParmSet(pctx, teem.pullIterParmPopCntlPeriod, pcp) or teem.pullIterParmSet(pctx, teem.pullIterParmAddDescent, iad) or teem.pullIterParmSet(pctx, teem.pullIterParmEnergyIncreasePermitHalfLife, eiphl) or teem.pullSysParmSet(pctx, teem.pullSysParmStepInitial, stepInitial) or teem.pullSysParmSet(pctx, teem.pullSysParmRadiusSpace, radSpace) or teem.pullSysParmSet(pctx, teem.pullSysParmRadiusScale, radScale) or teem.pullSysParmSet(pctx, teem.pullSysParmAlpha, alpha) or teem.pullSysParmSet(pctx, teem.pullSysParmBeta, beta) or teem.pullSysParmSet(pctx, teem.pullSysParmGamma, gamma) or teem.pullSysParmSet(pctx, teem.pullSysParmEnergyIncreasePermit, eip) or teem.pullSysParmSet(pctx, teem.pullSysParmFracNeighNixedMax, fnnm) or teem.pullSysParmSet(pctx, teem.pullSysParmEnergyDecreaseMin, edmin) or teem.pullSysParmSet(pctx, teem.pullSysParmEnergyDecreasePopCntlMin, edpcmin) or teem.pullSysParmSet(pctx, teem.pullSysParmBackStepScale, ssBack) or teem.pullSysParmSet(pctx, teem.pullSysParmOpporStepScale, ssOppor) or teem.pullProgressBinModSet(pctx, pbm) or teem.pullInterEnergySet(pctx, energyList[0], energyList[1], energyList[2], energyList[3])): estr = teem.biffGetDone('pull') print "problem with set-up:\n%s" % estr sys.exit(1) # The meet library offers a slightly higher-level abstraction of # the pullInfo; this code parses a bunch of info specifications info = (ctypes.POINTER(teem.meetPullInfo) * len(infoStr))() for i in range(len(infoStr)): info[i] = teem.meetPullInfoNew() if (teem.meetPullInfoParse(info[i], infoStr[i])): estr = teem.biffGetDone('meet') print "problem with infos:\n%s" % estr sys.exit(1) # Create all the kernels needed for reconstruction k00 = teem.nrrdKernelSpecNew() k11 = teem.nrrdKernelSpecNew() k22 = teem.nrrdKernelSpecNew() kSSrecon = teem.nrrdKernelSpecNew() if (teem.nrrdKernelSpecParse(k00, k00Str) or teem.nrrdKernelSpecParse(k11, k11Str) or teem.nrrdKernelSpecParse(k22, k22Str) or teem.nrrdKernelSpecParse(kSSrecon, kSSreconStr)): estr = teem.biffGetDone('nrrd') print "problem with kernels:\n%s" % estr sys.exit(1) # We do assume that the volumes came in loaded (as opposed to reloading # them here for every one); here we add all the volumes and infos into # the pullContext if (teem.meetPullVolAddMulti(pctx, vol, len(vol), k00, k11, k22, kSSrecon) or teem.meetPullInfoAddMulti(pctx, info, len(info))): estr = teem.biffGetDone('meet') print "problem with infos:\n%s" % estr sys.exit(1) # Final setup and the actual "pullRun" call if (initSet(pctx, init) or teem.pullInitLiveThreshUseSet(pctx, lti) or teem.pullStart(pctx) or teem.pullRun(pctx) or teem.pullOutputGet(nposOut, None, None, None, 0, pctx)): estr = teem.biffGetDone('pull') print "problem running system:\n%s" % estr sys.exit(1) # until there's a clean way to re-use a pullContext for different runs, # there's no good way for the user to call pullGammaLearn (because pctx # doesn't survive outside the call to "run". So we call pullGammaLearn # it whenever it makes sense to do so and save the result in the global # "gamma" if (teem.pullInterTypeAdditive == pctx.contents.interType and teem.pullEnergyButterworthParabola.contents.name == pctx.contents.energySpecS.contents.energy.contents.name): if (teem.pullGammaLearn(pctx)): estr = teem.biffGetDone('pull') print "problem learning gamma:\n%s" % estr sys.exit(1) gamma = pctx.contents.sysParm.gamma print "pullGammaLearn returned", gamma teem.nrrdKernelSpecNix(k00) teem.nrrdKernelSpecNix(k11) teem.nrrdKernelSpecNix(k22) teem.nrrdKernelSpecNix(kSSrecon) energyFree(energyList) teem.pullContextNix(pctx) return teem-1.11.0~svn6057/python/ctypes/ctypes-codegen.patch0000664000175000017500000000205411272122702022364 0ustar domibeldomibelIndex: ctypeslib-gccxml-0.9/ctypeslib/codegen/codegenerator.py =================================================================== --- ctypeslib-gccxml-0.9/ctypeslib/codegen/codegenerator.py (revision 75605) +++ ctypeslib-gccxml-0.9/ctypeslib/codegen/codegenerator.py (working copy) @@ -451,6 +451,15 @@ self._variables += 1 if tp.init is None: # wtypes.h contains IID_IProcessInitControl, for example + dllname = self.find_dllname(tp) + if dllname: + libname = self.get_sharedlib(dllname, "cdecl") + self.generate(tp.typ) + print >> self.stream, "%s = (%s).in_dll(%s, %r)" % (tp.name, + self.type_name(tp.typ, True), + libname, + tp.name) + self.names.add(tp.name) return try: value = self.initialize(tp.typ, tp.init) teem-1.11.0~svn6057/python/ctypes/interface_test.py0000775000175000017500000000352111612120722022004 0ustar domibeldomibel#!/usr/bin/env python # test.py: 16-bit grayscale PNG to nrrd file # Sam Quinan import Nrrd as nrd, numpy as np # TESTING: fmob-ch4 crop test print "TEST: fmob-ch4.nrrd" a = nrd.Nrrd() a.load("../../data/fmob-c4h.nrrd") print "nrrd loaded..." b = nrd.ExtendedArray(a) print "wrapped as array..." c = b[5:11,:,:] print "cropped..." #print c.shape d = np.ascontiguousarray(c, dtype=c.dtype.type) print "made contiguous..." #print d.shape e = nrd.Nrrd() e.fromNDArray(d) print "loaded..." e.save("fmob_test_out.nrrd") print "done" # OLD TESTING -- IGNORE #in_image = Image.open("msix.png") #f_data = numpy.asarray(in_image) #f_nrrd = Nrrd() #f_nrrd.fromNDArray(f_data) #del f_data #f_nrrd.save("test.nrrd") #t = f_nrrd #a = numpy.asarray(f_nrrd) #print a #del f_nrrd #a = Nrrd() #a.load("msix.png") # #b = ExtendedArray(a) #print b #print type(b) #print b.flags #print b.data #print b.base_ref #print b.base is None # #print "-------------------------------" # #in_image = Image.open("msix.png") #c = numpy.asarray(in_image) #del in_image # #print c #print type(c) #print c.flags #print c.data # #d = Nrrd() #d.fromNDArray(c) # #e = ExtendedArray(d) #e2 = e[:,[0,2]] # #print e #print type(e) #print e.flags #print e.data #print e.base_ref # # #print "-------------------------------" # #arr = numpy.zeros((4,)) #print arr.base is None #print arr #v1 = arr.view(ExtendedArray) #print v1.base is arr #print v1 #v2 = arr[2:] #print v2.base is arr #print v2 #c_data = numpy.asarray(in_image) #print "C CONTIGUOUS" #print c_data #print c_data.flags #print c_data.strides #f_data = numpy.require(in_image, dtype=None, requirements='F_CONTIGUOUS') #print "F CONTIGUOUS" #print f_data #print f_data.flags #print f_data.strides #c_nrrd = ndarrayToNrrd(c_data) #f_nrrd = ndarrayToNrrd(f_data) #teem.nrrdSave("foo.nrrd", c_nrrd, None) #teem.nrrdSave("bar.nrrd", f_nrrd, None)teem-1.11.0~svn6057/CTestConfig.cmake0000775000175000017500000000320012165631065016762 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # ## This file should be placed in the root directory of your project. ## Then modify the CMakeLists.txt file in the root directory of your ## project to incorporate the testing dashboard. ## # The following are required to uses Dart and the Cdash dashboard ## ENABLE_TESTING() ## INCLUDE(Dart) set(CTEST_PROJECT_NAME "Teem") set(CTEST_NIGHTLY_START_TIME "05:00:00 UTC") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "my.cdash.org") set(CTEST_DROP_LOCATION "/submit.php?project=Teem") set(CTEST_DROP_SITE_CDASH TRUE) teem-1.11.0~svn6057/Examples/0000775000175000017500000000000012203513761015363 5ustar domibeldomibelteem-1.11.0~svn6057/Examples/sanity/0000775000175000017500000000000012203513761016672 5ustar domibeldomibelteem-1.11.0~svn6057/Examples/sanity/sanity.c0000664000175000017500000000363012022525045020344 0ustar domibeldomibel/* ** Copyright (C) 2009, University of Chicago ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** ** Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** ** Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** ** Neither the name of the University of Chicago nor the names of its ** contributors may be used to endorse or promote products derived from ** this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include int main(int argc, char *argv[]) { int ret; char *me, *err; AIR_UNUSED(argc); me = argv[0]; if (!nrrdSanity()) { err = biffGetDone(NRRD); fprintf(stderr, "%s: nrrdSanity failed:\n%s", me, err); free(err); ret = 1; } else { fprintf(stderr, "%s: nrrdSanity passed\n", me); ret = 0; } return ret; } teem-1.11.0~svn6057/Examples/sanity/CMakeLists.txt0000664000175000017500000000042211175676246021447 0ustar domibeldomibelproject (TeemSampleProject) cmake_minimum_required(VERSION 2.4) if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) find_package(Teem REQUIRED) include(${Teem_USE_FILE}) add_executable(sample sanity.c) target_link_libraries(sample teem) teem-1.11.0~svn6057/src/0000775000175000017500000000000012203513761014374 5ustar domibeldomibelteem-1.11.0~svn6057/src/unrrdu/0000775000175000017500000000000012203513753015714 5ustar domibeldomibelteem-1.11.0~svn6057/src/unrrdu/ccadj.c0000664000175000017500000000524512165631065017136 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Form adjecency matrix of connected components" static const char *_unrrdu_ccadjInfoL = (INFO ". This operates on the output of \"ccfind\". Output is unsigned char " "array containing 1 at locations (I,J) and (J,I) if CCs with ids I and J are " "adjacent, according to the chosen style of adjacency.\n " "* Uses nrrdCCAdjacency"); int unrrdu_ccadjMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; airArray *mop; int pret; unsigned int conny; hestOptAdd(&opt, "c,connect", "connectivity", airTypeUInt, 1, 1, &conny, NULL, "what kind of connectivity to use: the number of coordinates " "that vary in order to traverse the neighborhood of a given " "sample. In 2D: \"1\": 4-connected, \"2\": 8-connected"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_ccadjInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCCAdjacency(nout, nin, conny)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error finding adjacencies:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(ccadj, INFO); teem-1.11.0~svn6057/src/unrrdu/dhisto.c0000664000175000017500000000643512165631065017366 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Create image of 1-D value histogram" static const char *_unrrdu_dhistoInfoL = (INFO ". With \"-nolog\", this becomes a quick & dirty way of plotting a function.\n " "* Uses nrrdHistoDraw"); int unrrdu_dhistoMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int pret, nolog, notick; unsigned int size; airArray *mop; double max; hestOptAdd(&opt, "h,height", "height", airTypeUInt, 1, 1, &size, NULL, "height of output image (horizontal size is determined by " "number of bins in input histogram)."); hestOptAdd(&opt, "nolog", NULL, airTypeInt, 0, 0, &nolog, NULL, "do not show the log-scaled histogram with decade tick-marks"); hestOptAdd(&opt, "notick", NULL, airTypeInt, 0, 0, ¬ick, NULL, "do not draw the log decade tick marks"); hestOptAdd(&opt, "max,maximum", "max # hits", airTypeDouble, 1, 1, &max, "nan", "constrain the top of the drawn histogram to be at this " "number of hits. This will either scale the drawn histogram " "downward or clip its top, depending on whether the given max " "is higher or lower than the actual maximum bin count. By " "not using this option (the default), the actual maximum bin " "count is used"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_dhistoInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdHistoDraw(nout, nin, size, nolog ? AIR_FALSE : (notick ? 2 : AIR_TRUE), max)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error drawing histogram nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(dhisto, INFO); teem-1.11.0~svn6057/src/unrrdu/reshape.c0000664000175000017500000000474612165631065017526 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Superficially change dimension and/or axes sizes" static const char *_unrrdu_reshapeInfoL = (INFO ". The underlying linear ordering of the samples is " "unchanged, but the reported dimension or axes sizes " "are changed. Identical in concept to Matlab's " "\"reshape\" command.\n " "* Uses nrrdReshape_nva"); int unrrdu_reshapeMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int pret; size_t *size; unsigned int sizeLen; airArray *mop; hestOptAdd(&opt, "s,size", "sz0 sz1 ", airTypeSize_t, 1, -1, &size, NULL, "new axes sizes", &sizeLen); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_reshapeInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdReshape_nva(nout, nin, sizeLen, size)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error reshaping nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(reshape, INFO); teem-1.11.0~svn6057/src/unrrdu/unblock.c0000664000175000017500000000513212165631065017522 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Expand \"blocks\" into scanlines on axis 0" static const char * _unrrdu_unblockInfoL = (INFO ". Based on the requested output type, the number of " "samples along axis 0 will be determined automatically. " "Axis N information will be bumped up to axis N+1. " "Underlying data is unchanged."); int unrrdu_unblockMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int type, pret; size_t blockSize; airArray *mop; OPT_ADD_TYPE(type, "type to unblock to", NULL); hestOptAdd(&opt, "bs", "blocksize", airTypeSize_t, 1, 1, &blockSize, "0", "Useful only if *output* type is also block: the size of " "blocks in output nrrd"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_unblockInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); nout->blockSize = blockSize; airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdUnblock(nout, nin, type)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error unblocking nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(unblock, INFO); teem-1.11.0~svn6057/src/unrrdu/cksum.c0000664000175000017500000000763412165631065017220 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Compute 32-bit CRC of nrrd data (same as via \"cksum\")" static const char *_unrrdu_cksumInfoL = (INFO ". Unlike other commands, this doesn't produce a nrrd. It only " "prints to standard out the CRC and byte counts for the input nrrd(s), " "seeking to emulate the formatting of cksum output.\n " "* Uses nrrdCRC32"); int unrrdu_cksumDoit(const char *me, char *inS, int endian, int printendian, FILE *fout) { Nrrd *nrrd; airArray *mop; unsigned int crc; char stmp[AIR_STRLEN_SMALL], ends[AIR_STRLEN_SMALL]; size_t nn; mop = airMopNew(); airMopAdd(mop, nrrd=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdLoad(nrrd, inS, NULL)) { biffMovef(me, NRRD, "%s: trouble loading \"%s\"", me, inS); airMopError(mop); return 1; } crc = nrrdCRC32(nrrd, endian); nn = nrrdElementNumber(nrrd)*nrrdElementSize(nrrd); sprintf(ends, "(%s)", airEnumStr(airEndian, endian)); fprintf(fout, "%u%s %s%s%s\n", crc, printendian ? ends : "", airSprintSize_t(stmp, nn), strcmp("-", inS) ? " " : "", strcmp("-", inS) ? inS : ""); airMopOkay(mop); return 0; } int unrrdu_cksumMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *err, **inS; airArray *mop; int pret, endian, printend; unsigned int ni, ninLen; mop = airMopNew(); hestOptAdd(&opt, "en,endian", "end", airTypeEnum, 1, 1, &endian, airEnumStr(airEndian, airMyEndian()), "Endianness in which to compute CRC; \"little\" for Intel and " "friends; \"big\" for everyone else. " "Defaults to endianness of this machine", NULL, airEndian); hestOptAdd(&opt, "pen,printendian", "bool", airTypeBool, 1, 1, &printend, "false", "whether or not to indicate after the CRC value the endianness " "with which the CRC was computed; doing so clarifies " "that the CRC result depends on endianness and may remove " "confusion in comparing results on platforms of different " "endianness"); hestOptAdd(&opt, NULL, "nin1", airTypeString, 1, -1, &inS, NULL, "input nrrd(s)", &ninLen); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_cksumInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); for (ni=0; nioldMin if it exists, otherwise 0.0"); hestOptAdd(&opt, "max,maximum", "value", airTypeDouble, 1, 1, &oldMax, "nan", "Highest value prior to quantization. Defaults to " "nin->oldMax if it exists, otherwise 1.0"); hestOptAdd(&opt, "double", NULL, airTypeBool, 0, 0, &dbl, NULL, "Use double for output type, instead of float"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_unquantizeInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (AIR_EXISTS(oldMin)) nin->oldMin = oldMin; if (AIR_EXISTS(oldMax)) nin->oldMax = oldMax; if (nrrdUnquantize(nout, nin, dbl ? nrrdTypeDouble : nrrdTypeFloat)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error unquantizing nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(unquantize, INFO); teem-1.11.0~svn6057/src/unrrdu/histax.c0000664000175000017500000001060412165631065017365 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Replace each scanline along an axis with its histogram" static const char *_unrrdu_histaxInfoL = (INFO ".\n " "* Uses nrrdHistoAxis"); int unrrdu_histaxMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; char *minStr, *maxStr; int type, pret, blind8BitRange; unsigned int axis, bins; airArray *mop; NrrdRange *range; OPT_ADD_AXIS(axis, "axis to histogram along"); hestOptAdd(&opt, "b,bin", "bins", airTypeUInt, 1, 1, &bins, NULL, "# of bins in histogram"); OPT_ADD_TYPE(type, "output type", "uchar"); /* HEY copy and paste from unrrdu/quantize.c */ hestOptAdd(&opt, "min,minimum", "value", airTypeString, 1, 1, &minStr, "nan", "The value to map to zero, given explicitly as a regular number, " "*or*, if the number is given with a \"" NRRD_MINMAX_PERC_SUFF "\" suffix, this " "minimum is specified in terms of the percentage of samples in " "input that are lower. " "\"0" NRRD_MINMAX_PERC_SUFF "\" means the " "lowest input value is used, " "\"1" NRRD_MINMAX_PERC_SUFF "\" means that the " "1% of the lowest values are all mapped to zero. " "By default (not using this option), the lowest input value is " "used."); hestOptAdd(&opt, "max,maximum", "value", airTypeString, 1, 1, &maxStr, "nan", "The value to map to the highest unsigned integral value, given " "explicitly as a regular number, " "*or*, if the number is given with " "a \"" NRRD_MINMAX_PERC_SUFF "\" suffix, " "this maximum is specified " "in terms of the percentage of samples in input that are higher. " "\"0" NRRD_MINMAX_PERC_SUFF "\" means the highest input value is " "used, which is also the default " "behavior (same as not using this option)."); hestOptAdd(&opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange, nrrdStateBlind8BitRange ? "true" : "false", "Whether to know the range of 8-bit data blindly " "(uchar is always [0,255], signed char is [-128,127])."); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_histaxInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); range = nrrdRangeNew(AIR_NAN, AIR_NAN); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdRangePercentileFromStringSet(range, nin, minStr, maxStr, 10*bins /* HEY magic */, blind8BitRange) || nrrdHistoAxis(nout, nin, range, axis, bins, type)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing axis histogramming:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(histax, INFO); teem-1.11.0~svn6057/src/unrrdu/crop.c0000664000175000017500000001311212165631065017025 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Crop along each axis to make a smaller nrrd" static const char *_unrrdu_cropInfoL = (INFO ".\n " "* Uses nrrdCrop"); int unrrdu_cropMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; unsigned int ai; int minLen, maxLen, pret; long int *minOff, *maxOff; size_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX]; airArray *mop; Nrrd *_nbounds; OPT_ADD_BOUND("min,minimum", 0, minOff, "0", "low corner of bounding box.\n " "\b\bo gives 0-based index\n " "\b\bo M, M+, M- give index relative " "to the last sample on the axis (M == #samples-1).", minLen); OPT_ADD_BOUND("max,maximum", 0, maxOff, "0", "high corner of bounding box. " "Besides the specification styles described above, " "there's also:\n " "\b\bo m+ give index relative to minimum.", maxLen); hestOptAdd(&opt, "b,bounds", "filename", airTypeOther, 1, 1, &_nbounds, "", "a filename given here overrides the -min and -max " "options (they don't need to be used) and provides the " "cropping bounds as a 2-D array; first scanline is for " "-min, second is for -max. Unfortunately the " "\"m\" and \"M\" semantics (above) are currently not " "supported in the bounds file.", NULL, NULL, nrrdHestNrrd); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_cropInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); if (!_nbounds) { if (!( minLen == (int)nin->dim && maxLen == (int)nin->dim )) { fprintf(stderr, "%s: # min coords (%d) or max coords (%d) != nrrd dim (%d)\n", me, minLen, maxLen, nin->dim); airMopError(mop); return 1; } for (ai=0; aidim; ai++) { if (-1 == minOff[0 + 2*ai]) { fprintf(stderr, "%s: can't use m+ specification for axis %d min\n", me, ai); airMopError(mop); return 1; } } for (ai=0; aidim; ai++) { min[ai] = minOff[0 + 2*ai]*(nin->axis[ai].size-1) + minOff[1 + 2*ai]; if (-1 == maxOff[0 + 2*ai]) { max[ai] = min[ai] + maxOff[1 + 2*ai]; } else { max[ai] = maxOff[0 + 2*ai]*(nin->axis[ai].size-1) + maxOff[1 + 2*ai]; } /* fprintf(stderr, "%s: ai %2d: min = %4d, max = %4d\n", me, ai, min[ai], max[ai]); */ } } else { Nrrd *nbounds; airULLong *bounds; unsigned int axi; if (!(2 == _nbounds->dim && nin->dim == AIR_CAST(unsigned int, _nbounds->axis[0].size) && 2 == _nbounds->axis[1].size)) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; if (_nbounds->dim >= 2) { airSprintSize_t(stmp1, _nbounds->axis[1].size); } else { strcpy(stmp1, ""); } fprintf(stderr, "%s: expected 2-D %u-by-2 array of cropping bounds, " "not %u-D %s%s%s%s\n", me, nin->dim, _nbounds->dim, airSprintSize_t(stmp2, _nbounds->axis[0].size), _nbounds->dim >= 2 ? "-by-" : "-long", _nbounds->dim >= 2 ? stmp1 : "", _nbounds->dim > 2 ? "-by-X" : ""); airMopError(mop); return 1; } nbounds = nrrdNew(); airMopAdd(mop, nbounds, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nbounds, _nbounds, nrrdTypeULLong)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error converting bounds array:\n%s", me, err); airMopError(mop); return 1; } bounds = AIR_CAST(airULLong*, nbounds->data); for (axi=0; axidim; axi++) { min[axi] = AIR_CAST(size_t, bounds[axi + 0*(nin->dim)]); max[axi] = AIR_CAST(size_t, bounds[axi + 1*(nin->dim)]); } } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCrop(nout, nin, min, max)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error cropping nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(crop, INFO); teem-1.11.0~svn6057/src/unrrdu/minmax.c0000664000175000017500000000774512165631065017372 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Print out min and max values in one or more nrrds" static const char *_unrrdu_minmaxInfoL = (INFO ". Unlike other commands, this doesn't produce a nrrd. It only " "prints to standard out the min and max values found in the input nrrd(s), " "and it also indicates if there are non-existent values.\n " "* Uses nrrdRangeNewSet"); int unrrdu_minmaxDoit(const char *me, char *inS, int blind8BitRange, FILE *fout) { Nrrd *nrrd; NrrdRange *range; airArray *mop; mop = airMopNew(); airMopAdd(mop, nrrd=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdLoad(nrrd, inS, NULL)) { biffMovef(me, NRRD, "%s: trouble loading \"%s\"", me, inS); airMopError(mop); return 1; } range = nrrdRangeNewSet(nrrd, blind8BitRange); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); airSinglePrintf(fout, NULL, "min: %.17g\n", range->min); airSinglePrintf(fout, NULL, "max: %.17g\n", range->max); if (0 == range->min && 0 == range->max) { fprintf(fout, "# min == max == 0.0 exactly\n"); } if (range->hasNonExist) { fprintf(fout, "# has non-existent values\n"); } airMopOkay(mop); return 0; } int unrrdu_minmaxMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *err, **inS; airArray *mop; int pret, blind8BitRange; unsigned int ni, ninLen; #define B8DEF "false" mop = airMopNew(); hestOptAdd(&opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange, B8DEF, /* NOTE: not using nrrdStateBlind8BitRange here for consistency with previous behavior */ "whether to blindly assert the range of 8-bit data, " "without actually going through the data values, i.e. " "uchar is always [0,255], signed char is [-128,127]. " "Note that even if you do not use this option, the default " "(" B8DEF ") is potentialy over-riding the effect of " "environment variable NRRD_STATE_BLIND_8_BIT_RANGE; " "see \"unu env\""); hestOptAdd(&opt, NULL, "nin1", airTypeString, 1, -1, &inS, NULL, "input nrrd(s)", &ninLen); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_minmaxInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); for (ni=0; ni 1) { fprintf(stdout, "==> %s <==\n", inS[ni]); } if (unrrdu_minmaxDoit(me, inS[ni], blind8BitRange, stdout)) { airMopAdd(mop, err = biffGetDone(me), airFree, airMopAlways); fprintf(stderr, "%s: trouble with \"%s\":\n%s", me, inS[ni], err); /* continue working on the remaining files */ } if (ninLen > 1 && ni < ninLen-1) { fprintf(stdout, "\n"); } } airMopOkay(mop); return 0; } UNRRDU_CMD(minmax, INFO); teem-1.11.0~svn6057/src/unrrdu/tile.c0000664000175000017500000000600512165631065017022 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Tile slices of one axis into two other axes" static const char *_unrrdu_tileInfoL = (INFO ". Tiling an array means splitting one axis into fast and slow parts, " "and then interleaving those parts into other (existing) axes by doing " "two axis merges, which combine an existing axis with part of the split " "axis. This reduces the dimension by one. The three axis arguments all " "identify axes in the input array as is. This provides, for example, " "a simple way of viewing the 128 slices along the slow axis of a 3-D volume " "as a 16x8 tiled array of 2-D slices, as with \"-a 2 0 1 -s 16 8\".\n " "* Uses nrrdTile2D"); int unrrdu_tileMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int pret; size_t size[2]; unsigned int axes[3]; airArray *mop; hestOptAdd(&opt, "a,axis", "axSplit ax0 ax1", airTypeUInt, 3, 3, axes, NULL, "axSplit is divided and merged with ax0 and ax1"); hestOptAdd(&opt, "s,size", "fast slow", airTypeSize_t, 2, 2, size, NULL, "fast and slow axis sizes to produce as result of splitting " "the axSplit axis."); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_tileInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdTile2D(nout, nin, axes[1], axes[2], axes[0], size[0], size[1])) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error tiling nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(tile, INFO); teem-1.11.0~svn6057/src/unrrdu/quantize.c0000664000175000017500000001261312165631065017727 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Quantize values to 8, 16, or 32 bits" static const char *_unrrdu_quantizeInfoL = (INFO ". Input values can be fixed point (e.g. quantizing ushorts down to " "uchars) or floating point. Values are clamped to the min and max before " "they are quantized, so there is no risk of getting 255 where you expect 0 " "(with unsigned char output, for example). The min and max can be specified " "explicitly (as a regular number), or in terms of percentiles (a number " "suffixed with \"" NRRD_MINMAX_PERC_SUFF "\", no space in between). " "This does only linear quantization. " "See also \"unu convert\", \"unu 2op x\", " "and \"unu 3op clamp\".\n " "* Uses nrrdQuantize"); int unrrdu_quantizeMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; char *minStr, *maxStr; int pret, blind8BitRange; unsigned int bits, hbins; NrrdRange *range; airArray *mop; hestOptAdd(&opt, "b,bits", "bits", airTypeOther, 1, 1, &bits, NULL, "Number of bits to quantize down to; determines the type " "of the output nrrd:\n " "\b\bo \"8\": unsigned char\n " "\b\bo \"16\": unsigned short\n " "\b\bo \"32\": unsigned int", NULL, NULL, &unrrduHestBitsCB); hestOptAdd(&opt, "min,minimum", "value", airTypeString, 1, 1, &minStr, "nan", "The value to map to zero, given explicitly as a regular number, " "*or*, if the number is given with a \"" NRRD_MINMAX_PERC_SUFF "\" suffix, this " "minimum is specified in terms of the percentage of samples in " "input that are lower. " "\"0" NRRD_MINMAX_PERC_SUFF "\" means the " "lowest input value is used, " "\"1" NRRD_MINMAX_PERC_SUFF "\" means that the " "1% of the lowest values are all mapped to zero. " "By default (not using this option), the lowest input value is " "used."); hestOptAdd(&opt, "max,maximum", "value", airTypeString, 1, 1, &maxStr, "nan", "The value to map to the highest unsigned integral value, given " "explicitly as a regular number, " "*or*, if the number is given with " "a \"" NRRD_MINMAX_PERC_SUFF "\" suffix, " "this maximum is specified " "in terms of the percentage of samples in input that are higher. " "\"0" NRRD_MINMAX_PERC_SUFF "\" means the highest input value is " "used, which is also the default " "behavior (same as not using this option)."); hestOptAdd(&opt, "hb,bins", "bins", airTypeUInt, 1, 1, &hbins, "5000", "number of bins in histogram of values, for determining min " "or max by percentiles. This has to be large enough so that " "any errant very high or very low values do not compress the " "interesting part of the histogram to an inscrutably small " "number of bins."); hestOptAdd(&opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange, nrrdStateBlind8BitRange ? "true" : "false", "if not using \"-min\" or \"-max\", whether to know " "the range of 8-bit data blindly (uchar is always [0,255], " "signed char is [-128,127])"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_quantizeInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); range = nrrdRangeNew(AIR_NAN, AIR_NAN); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdRangePercentileFromStringSet(range, nin, minStr, maxStr, hbins, blind8BitRange) || nrrdQuantize(nout, nin, range, bits)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error with range or quantizing:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(quantize, INFO); teem-1.11.0~svn6057/src/unrrdu/unorient.c0000664000175000017500000000537712165631065017743 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Make image orientation be axis-aligned" static const char *_unrrdu_unorientInfoL = (INFO ". Does various tricks.\n " "* Uses nrrdOrientationReduce"); int unrrdu_unorientMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int pret; int setMinsFromOrigin; airArray *mop; /* if we gave a default for this, then there it would fine to have no command-line arguments whatsoever, and then the user would not know how to get the basic usage information */ hestOptAdd(&opt, "i,input", "nin", airTypeOther, 1, 1, &nin, NULL, "input nrrd " "(sorry, can't use usual default of \"-\" for stdin " "because of hest quirk)", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "smfo", NULL, airTypeInt, 0, 0, &setMinsFromOrigin, NULL, "set some axis mins based on space origin (hack)"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_unorientInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdOrientationReduce(nout, nin, setMinsFromOrigin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error unorienting nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(unorient, INFO); teem-1.11.0~svn6057/src/unrrdu/lut.c0000664000175000017500000001113112165631065016665 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Map nrrd through one univariate lookup table" static const char *_unrrdu_lutInfoL = (INFO " (itself represented as a nrrd). The lookup table " "can be 1D, in which case the output " "has the same dimension as the input, or 2D, in which case " "the output has one more dimension than the input, and each " "value is mapped to a scanline (along axis 0) from the " "lookup table.\n " "* Uses nrrdApply1DLut"); int unrrdu_lutMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nlut, *nout; airArray *mop; int typeOut, rescale, pret, blind8BitRange; double min, max; NrrdRange *range=NULL; hestOptAdd(&opt, "m,map", "lut", airTypeOther, 1, 1, &nlut, NULL, "lookup table to map input nrrd through", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "r,rescale", NULL, airTypeInt, 0, 0, &rescale, NULL, "rescale the input values from the input range to the " "lut domain. The lut domain is either explicitly " "defined by the axis min,max along axis 0 or 1, or, it " "is implicitly defined as zero to the length of that axis " "minus one."); hestOptAdd(&opt, "min,minimum", "value", airTypeDouble, 1, 1, &min, "nan", "Low end of input range. Defaults to lowest value " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\")"); hestOptAdd(&opt, "max,maximum", "value", airTypeDouble, 1, 1, &max, "nan", "High end of input range. Defaults to highest value " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\")"); hestOptAdd(&opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange, nrrdStateBlind8BitRange ? "true" : "false", "Whether to know the range of 8-bit data blindly " "(uchar is always [0,255], signed char is [-128,127]). " "Explicitly setting this is useful only with rescaling (\"-r\")"); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &typeOut, "default", "specify the type (\"int\", \"float\", etc.) of the " "output nrrd. " "By default (not using this option), the output type " "is the lut's type.", NULL, NULL, &unrrduHestMaybeTypeCB); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_lutInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); /* see comment rmap.c */ if (!( AIR_EXISTS(nlut->axis[nlut->dim - 1].min) && AIR_EXISTS(nlut->axis[nlut->dim - 1].max) )) { rescale = AIR_TRUE; } if (rescale) { range = nrrdRangeNew(min, max); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); nrrdRangeSafeSet(range, nin, blind8BitRange); } if (nrrdTypeDefault == typeOut) { typeOut = nlut->type; } if (nrrdApply1DLut(nout, nin, range, nlut, typeOut, rescale)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble applying LUT:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(lut, INFO); teem-1.11.0~svn6057/src/unrrdu/2op.c0000664000175000017500000001610012165631065016562 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Binary operation on two nrrds, or on a nrrd and a constant" static const char *_unrrdu_2opInfoL = (INFO ". Either the first or second operand can be a float constant, " "but not both. Use \"-\" for an operand to signify " "a nrrd to be read from stdin (a pipe). Note, however, " "that \"-\" can probably only be used once (reliably).\n " "* Uses nrrdArithIterBinaryOp or (with -w) nrrdArithIterBinaryOpSelect"); int unrrdu_2opMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err, *seedS; NrrdIter *in1, *in2; Nrrd *nout, *ntmp=NULL; int op, type, E, pret, which; airArray *mop; unsigned int seed; hestOptAdd(&opt, NULL, "operator", airTypeEnum, 1, 1, &op, NULL, "Binary operator. Possibilities include:\n " "\b\bo \"+\", \"-\", \"x\", \"/\": " "add, subtract, multiply, divide\n " "\b\bo \"^\": exponentiation (pow)\n " "\b\bo \"spow\": signed exponentiation: sgn(x)pow(abs(x),p)\n " "\b\bo \"fpow\": like spow but with curves flipped\n " "\b\bo \"%\": integer modulo\n " "\b\bo \"fmod\": same as fmod() in C\n " "\b\bo \"atan2\": same as atan2() in C\n " "\b\bo \"min\", \"max\": minimum, maximum\n " "\b\bo \"lt\", \"lte\", \"gt\", \"gte\": same as C's <, <=, >, <=\n " "\b\bo \"eq\", \"neq\": same as C's == and !=\n " "\b\bo \"comp\": -1, 0, or 1 if 1st value is less than, " "equal to, or greater than 2nd value\n " "\b\bo \"if\": if 1st value is non-zero, use it, " "else use 2nd value\n " "\b\bo \"exists\": if 1st value exists, use it, " "else use 2nd value\n " "\b\bo \"nrand\": scale unit-stdv Gaussian noise by 2nd value " "and add to first value\n " "\b\bo \"rrand\": sample Rician distribution with 1st value " "for \"true\" mean, and 2nd value for sigma", NULL, nrrdBinaryOp); hestOptAdd(&opt, NULL, "in1", airTypeOther, 1, 1, &in1, NULL, "First input. Can be a single value or a nrrd.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "in2", airTypeOther, 1, 1, &in2, NULL, "Second input. Can be a single value or a nrrd.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, "s,seed", "seed", airTypeString, 1, 1, &seedS, "", "seed value for RNG for nrand, so that you " "can get repeatable results between runs, or, " "by not using this option, the RNG seeding will be " "based on the current time"); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &type, "default", "type to convert all INPUT nrrds to, prior to " "doing operation, useful for doing, for instance, the difference " "between two unsigned char nrrds. This will also determine " "output type. By default (not using this option), the types of " "the input nrrds are left unchanged.", NULL, NULL, &unrrduHestMaybeTypeCB); hestOptAdd(&opt, "w,which", "arg", airTypeInt, 1, 1, &which, "-1", "Which argument (0 or 1) should be used to determine the " "shape of the output nrrd. By default (not using this option), " "the first non-constant argument is used. "); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_2opInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); /* fprintf(stderr, "%s: op = %d\n", me, op); fprintf(stderr, "%s: in1->left = %d, in2->left = %d\n", me, (int)(in1->left), (int)(in2->left)); */ if (nrrdTypeDefault != type) { /* they wanted to convert nrrds to some other type first */ E = 0; if (in1->ownNrrd) { if (!E) E |= nrrdConvert(ntmp=nrrdNew(), in1->ownNrrd, type); if (!E) nrrdIterSetOwnNrrd(in1, ntmp); } if (in2->ownNrrd) { if (!E) E |= nrrdConvert(ntmp=nrrdNew(), in2->ownNrrd, type); if (!E) nrrdIterSetOwnNrrd(in2, ntmp); } if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error converting input nrrd(s):\n%s", me, err); airMopError(mop); return 1; } /* this will still leave a nrrd in the NrrdIter for nrrdIterNix() (called by hestParseFree() called be airMopOkay()) to clear up */ } /* ** Used to only deal with RNG seed for particular op: ** if (nrrdBinaryOpNormalRandScaleAdd == op) { ** but then when nrrdBinaryOpRicianRand was added, then the seed wasn't being ** used ==> BUG. To be more future proof, we try to parse and use seed ** whenever a non-empty string is given, and end up *ALWAYS* calling ** airSrandMT, even for operations that have nothing to do with random ** numbers. Could also have a new array that indicates if an op involves ** the RNG, but this would add rarely-needed complexity */ if (airStrlen(seedS)) { if (1 != sscanf(seedS, "%u", &seed)) { fprintf(stderr, "%s: couldn't parse seed \"%s\" as uint\n", me, seedS); airMopError(mop); return 1; } else { airSrandMT(seed); } } else { /* got no request for specific seed */ airSrandMT(AIR_CAST(unsigned int, airTime())); } if (-1 == which ? nrrdArithIterBinaryOp(nout, op, in1, in2) : nrrdArithIterBinaryOpSelect(nout, op, in1, in2, AIR_CAST(unsigned int, which))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing binary operation:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(2op, INFO); teem-1.11.0~svn6057/src/unrrdu/privateUnrrdu.h0000664000175000017500000001421012165631065020741 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef UNRRDU_PRIVATE_HAS_BEEN_INCLUDED #define UNRRDU_PRIVATE_HAS_BEEN_INCLUDED #ifdef _WIN32 #include #include #endif #ifdef __cplusplus extern "C" { #endif /* handling of "quiet quit", to avoid having a string of piped unu commands generate multiple pages of unwelcome usage info */ /* the environment variable to look for */ #define UNRRDU_QUIET_QUIT_ENV "UNRRDU_QUIET_QUIT" /* the string to search for in the error message that signifies that we should die quietly; this is clearly a hack because it depends on the text of error messages set by a different library. With more work it could be made into less of a hack. At worst the hack's breakage will lead again to the many error messages that inspired the hack in the first place , and will inspire fixing it again */ #define UNRRDU_QUIET_QUIT_STR "[nrrd] _nrrdRead: immediately hit EOF" /* ** OPT_ADD_XXX ** ** These macros are used for setting up command-line options for the various ** unu commands. They define options which are common across many different ** commands, so that the unu interface is as consistent as possible. They ** all assume a hestOpt *opt variable, but they take the option variable ** and option description as arguments. The expected type of the variable ** is given before each macro. */ /* Nrrd *var */ #define OPT_ADD_NIN(var, desc) \ hestOptAdd(&opt, "i,input", "nin", airTypeOther, 1, 1, &(var), "-", desc, \ NULL, NULL, nrrdHestNrrd) /* char *var */ #define OPT_ADD_NOUT(var, desc) \ hestOptAdd(&opt, "o,output", "nout", airTypeString, 1, 1, &(var), "-", desc) /* unsigned int var */ #define OPT_ADD_AXIS(var, desc) \ hestOptAdd(&opt, "a,axis", "axis", airTypeUInt, 1, 1, &(var), NULL, desc) /* int *var; int saw */ #define OPT_ADD_BOUND(name, needmin, var, deflt, desc, saw) \ hestOptAdd(&opt, name, "pos0", airTypeOther, needmin, -1, &(var), \ deflt, desc, &(saw), NULL, &unrrduHestPosCB) /* int var */ #define OPT_ADD_TYPE(var, desc, dflt) \ hestOptAdd(&opt, "t,type", "type", airTypeEnum, 1, 1, &(var), dflt, desc, \ NULL, nrrdType) /* ** USAGE, PARSE, SAVE ** ** These are macros at their worst and most fragile, because of how ** many local variables are assumed. This code is basically the same, ** verbatim, across all the different unrrdu functions, and having ** them as macros just shortens (without necessarily clarifying) their ** code. ** ** The return value for generating usage information was changed from ** 1 to 0 with the thought that merely asking for usage info shouldn't ** be treated as an erroneous invocation; unu about and unu env (which ** don't use this macro) had already been this way. */ #define USAGE(info) \ if (!argc) { \ hestInfo(stdout, me, (info), hparm); \ hestUsage(stdout, opt, me, hparm); \ hestGlossary(stdout, opt, hparm); \ airMopError(mop); \ return 0; \ } /* I nixed this because it meant unu invocations with only a few args (less than hestMinNumArgs()), which were botched because they were missing options, were not being described in the error messages. ** ** NB: below is an unidiomatic use of hestMinNumArgs(), because of ** how unu's main invokes the "main" function of the different ** commands. Normally the comparison is with argc-1, or argc-2 ** the case of cvs/svn-like commands. if ( (hparm->respFileEnable && !argc) || \ (!hparm->respFileEnable && argc < hestMinNumArgs(opt)) ) { \ */ /* ** NOTE: of all places it is inside the PARSE() macro that the ** "quiet-quit" functionality is implemented; this is defensible ** because all unu commands use PARSE */ #define PARSE() \ if ((pret=hestParse(opt, argc, argv, &err, hparm))) { \ if (1 == pret || 2 == pret) { \ if (!(getenv(UNRRDU_QUIET_QUIT_ENV) \ && airEndsWith(err, UNRRDU_QUIET_QUIT_STR "\n"))) { \ fprintf(stderr, "%s: %s\n", me, err); free(err); \ hestUsage(stderr, opt, me, hparm); \ hestGlossary(stderr, opt, hparm); \ } \ airMopError(mop); \ return 1; \ } else { \ /* . . . like tears . . . in rain. Time . . . to die. */ \ exit(1); \ } \ } #define SAVE(outS, nout, io) \ if (nrrdSave((outS), (nout), (io))) { \ airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); \ fprintf(stderr, "%s: error saving nrrd to \"%s\":\n%s\n", me, (outS), err); \ airMopError(mop); \ return 1; \ } #ifdef __cplusplus } #endif #endif /* UNRRDU_PRIVATE_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/unrrdu/head.c0000664000175000017500000000765112165631065016776 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Print header of one or more nrrd files" static const char *_unrrdu_headInfoL = (INFO ". The value of this is simply to print the contents of nrrd " "headers. This avoids the use of \"head -N\", where N has to be " "determined manually, which always risks printing raw binary data " "(following the header) to screen, which tends to clobber terminal " "settings, make pointless beeps, and be annoying.\n " "* Uses _nrrdOneLine"); int unrrdu_headDoit(const char *me, NrrdIoState *nio, char *inS, FILE *fout) { airArray *mop; unsigned int len; FILE *fin; mop = airMopNew(); if (!( fin = airFopen(inS, stdin, "rb") )) { biffAddf(me, "%s: couldn't fopen(\"%s\",\"rb\"): %s\n", me, inS, strerror(errno)); airMopError(mop); return 1; } airMopAdd(mop, fin, (airMopper)airFclose, airMopAlways); if (_nrrdOneLine(&len, nio, fin)) { biffAddf(me, "%s: error getting first line of file \"%s\"", me, inS); airMopError(mop); return 1; } if (!len) { biffAddf(me, "%s: immediately hit EOF\n", me); airMopError(mop); return 1; } if (!( nrrdFormatNRRD->contentStartsLike(nio) )) { biffAddf(me, "%s: first line (\"%s\") isn't a nrrd magic\n", me, nio->line); airMopError(mop); return 1; } while (len > 1) { fprintf(fout, "%s\n", nio->line); _nrrdOneLine(&len, nio, fin); }; /* experience has shown that on at least windows and darwin, the writing process's fwrite() to stdout will fail if we exit without consuming everything from stdin */ if (stdin == fin) { int c = getc(fin); while (EOF != c) { c = getc(fin); } } airMopOkay(mop); return 0; } int unrrdu_headMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *err, **inS; NrrdIoState *nio; airArray *mop; int pret; unsigned int ni, ninLen; mop = airMopNew(); hestOptAdd(&opt, NULL, "nin1", airTypeString, 1, -1, &inS, NULL, "input nrrd(s)", &ninLen); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_headInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); for (ni=0; ni 1) { fprintf(stdout, "==> %s <==\n", inS[ni]); } if (unrrdu_headDoit(me, nio, inS[ni], stdout)) { airMopAdd(mop, err = biffGetDone(me), airFree, airMopAlways); fprintf(stderr, "%s: trouble reading from \"%s\":\n%s", me, inS[ni], err); /* continue working on the remaining files */ } if (ninLen > 1 && ni < ninLen-1) { fprintf(stdout, "\n"); } } airMopOkay(mop); return 0; } UNRRDU_CMD(head, INFO); teem-1.11.0~svn6057/src/unrrdu/pad.c0000664000175000017500000001040512165631065016630 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Pad along each axis to make a bigger nrrd" static const char *_unrrdu_padInfoL = (INFO ".\n " "* Uses nrrdPad_nva"); int unrrdu_padMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; unsigned int ai; int minLen, maxLen, bb, pret; long int *minOff, *maxOff; ptrdiff_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX]; double padVal; airArray *mop; OPT_ADD_BOUND("min,minimum", 1, minOff, NULL, "low corner of bounding box.\n " "\b\bo gives 0-based index\n " "\b\bo M, M+, M- give index relative " "to the last sample on the axis (M == #samples-1).", minLen); OPT_ADD_BOUND("max,maximum", 1, maxOff, NULL, "high corner of bounding box. " "Besides the specification styles described above, " "there's also:\n " "\b\bo m+ give index relative to minimum.", maxLen); hestOptAdd(&opt, "b,boundary", "behavior", airTypeEnum, 1, 1, &bb, "bleed", "How to handle samples beyond the input bounds:\n " "\b\bo \"pad\": use some specified value\n " "\b\bo \"bleed\": extend border values outward\n " "\b\bo \"mirror\": repeated reflections\n " "\b\bo \"wrap\": wrap-around to other side", NULL, nrrdBoundary); hestOptAdd(&opt, "v,value", "val", airTypeDouble, 1, 1, &padVal, "0.0", "for \"pad\" boundary behavior, pad with this value"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_padInfoL); /* hammerhead problems were here */ PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); if (!( minLen == (int)nin->dim && maxLen == (int)nin->dim )) { fprintf(stderr, "%s: # min coords (%d) or max coords (%d) != nrrd dim (%d)\n", me, minLen, maxLen, nin->dim); airMopError(mop); return 1; } for (ai=0; aidim; ai++) { if (-1 == minOff[0 + 2*ai]) { fprintf(stderr, "%s: can't use m+ specification for axis %d min\n", me, ai); airMopError(mop); return 1; } } for (ai=0; aidim; ai++) { min[ai] = minOff[0 + 2*ai]*(nin->axis[ai].size-1) + minOff[1 + 2*ai]; if (-1 == maxOff[0 + 2*ai]) { max[ai] = min[ai] + maxOff[1 + 2*ai]; } else { max[ai] = maxOff[0 + 2*ai]*(nin->axis[ai].size-1) + maxOff[1 + 2*ai]; } /* fprintf(stderr, "%s: ai %2d: min = %4d, max = %4d\n", me, ai, min[ai], mai[ai]); */ } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdPad_nva(nout, nin, min, max, bb, padVal)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error padding nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(pad, INFO); teem-1.11.0~svn6057/src/unrrdu/data.c0000664000175000017500000000721012165631065016775 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Print data segment of a nrrd file" static const char *_unrrdu_dataInfoL = (INFO ". The value of this is to pass the data segment in isolation to a " "stand-alone decoder, in case this Teem build lacks an optional " "data encoding required for a given nrrd file. Caveats: " "Will start copying " "characters from the datafile until EOF is hit, so this won't work " "correctly if the datafile has extraneous content at the end. Will " "skip lines (as per \"line skip:\" header field) if needed, but can only " "skip bytes (as per \"byte skip:\") if the encoding is NOT a compression. " "\n \n " "To make vol.raw contain the uncompressed data from vol.nrrd " "which uses \"gz\" encoding: \"unu data vol.nrrd | gunzip > vol.raw\"\n " "\n " "* Uses nrrdLoad with nio->skipData and nio->keepNrrdDataFileOpen both " "true in the NrrdIoState nio."); int unrrdu_dataMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *err, *inS=NULL; Nrrd *nin; NrrdIoState *nio; airArray *mop; int car, pret; mop = airMopNew(); hestOptAdd(&opt, NULL, "nin", airTypeString, 1, 1, &inS, NULL, "input nrrd"); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_dataInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); nio->skipData = AIR_TRUE; nio->keepNrrdDataFileOpen = AIR_TRUE; nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); if (nrrdLoad(nin, inS, nio)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error reading header:\n%s", me, err); airMopError(mop); return 1; } if (_nrrdDataFNNumber(nio) > 1) { fprintf(stderr, "%s: sorry, currently can't operate with multiple " "detached datafiles\n", me); airMopError(mop); return 1; } if (!( nrrdFormatNRRD == nio->format )) { fprintf(stderr, "%s: can only print data of NRRD format files\n", me); airMopError(mop); return 1; } car = fgetc(nio->dataFile); #ifdef _MSC_VER /* needed because otherwise printing a carraige return will automatically also produce a newline */ _setmode(_fileno(stdout), _O_BINARY); #endif while (EOF != car) { fputc(car, stdout); car = fgetc(nio->dataFile); } airFclose(nio->dataFile); airMopOkay(mop); return 0; } UNRRDU_CMD(data, INFO); teem-1.11.0~svn6057/src/unrrdu/1op.c0000664000175000017500000001245012165631065016565 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Unary operation on a nrrd" static const char *_unrrdu_1opInfoL = (INFO ".\n " "* Uses nrrdArithUnaryOp"); int unrrdu_1opMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err, *seedS; Nrrd *nin, *nout, *ntmp=NULL; int op, pret, type; airArray *mop; unsigned int seed; hestOptAdd(&opt, NULL, "operator", airTypeEnum, 1, 1, &op, NULL, "Unary operator. Possibilities include:\n " "\b\bo \"-\": negative (multiply by -1.0)\n " "\b\bo \"r\": reciprocal (1.0/value)\n " "\b\bo \"sin\", \"cos\", \"tan\", \"asin\", \"acos\", \"atan\": " "same as in C\n " "\b\bo \"exp\", \"log\", \"log10\": same as in C\n " "\b\bo \"log1p\", \"expm1\": accurate log(x+1) and exp(x)-1\n " "\b\bo \"log2\": log base 2\n " "\b\bo \"sqrt\", \"cbrt\", \"ceil\", \"floor\": same as in C\n " "\b\bo \"erf\": error function (integral of Gaussian)\n " "\b\bo \"rup\", \"rdn\": round up or down to integral value\n " "\b\bo \"abs\": absolute value\n " "\b\bo \"sgn\": -1, 0, 1 if value is <0, ==0, or >0\n " "\b\bo \"exists\": 1 iff not NaN or +/-Inf, 0 otherwise\n " "\b\bo \"rand\": random value in [0.0,1.0), " "no relation to input\n " "\b\bo \"nrand\": random sample from normal distribution with " "mean 0.0 and stdv 1.0, no relation to input\n " "\b\bo \"if\": if input is non-zero, 1, else 0\n " "\b\bo \"0\": output always 0\n " "\b\bo \"1\": output always 1", NULL, nrrdUnaryOp); hestOptAdd(&opt, "s,seed", "seed", airTypeString, 1, 1, &seedS, "", "seed value for RNG for rand and nrand, so that you " "can get repeatable results between runs, or, " "by not using this option, the RNG seeding will be " "based on the current time"); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &type, "default", "convert input nrrd to this type prior to " "doing operation. Useful when desired output is float " "(e.g., with log1p), but input is integral. By default " "(not using this option), the types of " "the input nrrds are left unchanged.", NULL, NULL, &unrrduHestMaybeTypeCB); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_1opInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdTypeDefault != type) { /* they requested conversion to another type prior to the 1op */ airMopAdd(mop, ntmp=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(ntmp, nin, type)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error converting input nrrd:\n%s", me, err); airMopError(mop); return 1; } } else { ntmp = nin; } /* see note in 2op.c about the hazards of trying to be clever ** about minimizing the seeding of the RNG ** if (nrrdUnaryOpRand == op ** || nrrdUnaryOpNormalRand == op) { */ if (airStrlen(seedS)) { if (1 != sscanf(seedS, "%u", &seed)) { fprintf(stderr, "%s: couldn't parse seed \"%s\" as uint\n", me, seedS); airMopError(mop); return 1; } else { airSrandMT(seed); } } else { /* got no request for specific seed */ airSrandMT(AIR_CAST(unsigned int, airTime())); } if (nrrdArithUnaryOp(nout, op, ntmp)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing unary operation:\n%s", me, err); airMopError(mop); return 1; } /* if we had to create ntmp with nrrdConvert, it will be mopped, otherwise ntmp is an alias for nin, which will also be mopped */ SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(1op, INFO); teem-1.11.0~svn6057/src/unrrdu/unrrdu.h0000664000175000017500000001637412165631065017423 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef UNRRDU_HAS_BEEN_INCLUDED #define UNRRDU_HAS_BEEN_INCLUDED #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(unrrdu_EXPORTS) || defined(teem_EXPORTS) # define UNRRDU_EXPORT extern __declspec(dllexport) # else # define UNRRDU_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define UNRRDU_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define UNRRDU unrrduBiffKey #define UNRRDU_COLUMNS 78 /* how many chars per line do we allow hest */ /* ******** unrrduCmd ** ** How we associate the one word for the unu command ("name"), ** the one-line info string ("info"), and the single function we ** which implements the command ("main"). */ typedef struct { const char *name, *info; int (*main)(int argc, const char **argv, const char *me, hestParm *hparm); int hidden; } unrrduCmd; /* ** UNRRDU_DECLARE, UNRRDU_LIST, UNRRDU_MAP ** ** Twisted C-preprocessor tricks. The idea is to make it as simple ** as possible to add new commands to unu, so that the new commands ** have to be added to only one thing in this source file, and ** the Makefile. ** ** Associated with each unu command are some pieces of information: ** the single word command (e.g. "slice") that is used by invoke it, ** the short (approx. one-line) description of its function, and the ** "main" function to call with the appropriate argc, argv. It would ** be nice to use a struct to hold this information, and we can: the ** unrrduCmd struct is defined above. It would also be nice to have ** all the command's information be held in one array of unrrduCmds. ** Unfortunately, declaring this is not possible unless all the ** unrrduCmds and their fields are IN THIS FILE, because otherwise ** they're not constant expressions, so they can't initialize an ** aggregate data type. So, we instead make an array of unrrduCmd ** POINTERS, which can be initialized with the addresses of individual ** unrrduCmd structs, declared and defined in the global scope. is ** done in flotsam.c. Each of the source files for the various unu ** commands are responsible for setting the fields (at compile-time) ** of their associated unrrduCmd. ** ** We use three macros to automate this somewhat: ** UNRRDU_DECLARE: declares unrrdu_xxxCmd as an extern unrrduCmd ** (defined in xxx.c), used later in this header file. ** UNRRDU_LIST: the address of unrrdu_xxxCmd, for listing in the array of ** unrrduCmd structs in the (compile-time) definition of ** unrrduCmdList[]. This is used in flotsam.c. ** ** Then, to facilitate running these macros on each of the different ** commands, there is a UNRRDU_MAP macro which is used to essentially map ** the two macros above over the list of unu commands. Functional ** programming meets the C pre-processor. Therefore: *********************************************************** You add commands to unu by: 1) adjusting the definition of UNRRDU_MAP() 2) listing the appropriate object in GNUmakefile and sources.cmake That's it. ********************************************************** */ #define UNRRDU_DECLARE(C) UNRRDU_EXPORT unrrduCmd unrrdu_##C##Cmd; #define UNRRDU_LIST(C) &unrrdu_##C##Cmd, #define UNRRDU_MAP(F) \ F(about) \ F(env) \ F(i2w) \ F(w2i) \ F(make) \ F(head) \ F(data) \ F(convert) \ F(resample) \ F(fft) \ F(cmedian) \ F(dering) \ F(dist) \ F(minmax) \ F(cksum) \ F(diff) \ F(quantize) \ F(unquantize) \ F(project) \ F(slice) \ F(sselect) \ F(dice) \ F(splice) \ F(join) \ F(crop) \ F(acrop) \ F(inset) \ F(pad) \ F(reshape) \ F(permute) \ F(swap) \ F(shuffle) \ F(flip) \ F(unorient) \ F(basinfo) \ F(axinfo) \ F(axinsert) \ F(axsplit) \ F(axdelete) \ F(axmerge) \ F(tile) \ F(untile) \ F(histo) \ F(dhisto) \ F(jhisto) \ F(histax) \ F(heq) \ F(gamma) \ F(1op) \ F(2op) \ F(3op) \ F(affine) \ F(lut) \ F(mlut) \ F(subst) \ F(rmap) \ F(mrmap) \ F(imap) \ F(lut2) \ F(ccfind) \ F(ccadj) \ F(ccmerge) \ F(ccsettle) \ F(dnorm) \ F(vidicon) \ F(undos) \ F(save) /* these two have been removed since no one uses them F(block) \ F(unblock) \ */ /* ******** UNRRDU_CMD ** ** This is used at the very end of the various command sources ** ("xxx.c") to simplify defining a unrrduCmd. "name" should just be ** the command, UNQUOTED, such as flip or slice. */ #define UNRRDU_CMD(name, info) \ unrrduCmd unrrdu_##name##Cmd = { \ #name, info, unrrdu_##name##Main, AIR_FALSE \ } #define UNRRDU_CMD_HIDE(name, info) \ unrrduCmd unrrdu_##name##Cmd = { \ #name, info, unrrdu_##name##Main, AIR_TRUE \ } /* xxx.c */ /* Declare the extern unrrduCmds unrrdu_xxxCmd, for all xxx. These are defined in as many different source files as there are commands. */ UNRRDU_MAP(UNRRDU_DECLARE) /* ** used with unrrduHestScaleCB to identify for "unu resample -s" ** whether to resample along an axis, and if so, how the number of ** samples on an axis should be scaled (though now its more general ** than just scaling) */ enum { unrrduScaleUnknown, unrrduScaleNothing, /* "=" */ unrrduScaleMultiply, /* e.g. "x2" */ unrrduScaleDivide, /* e.g. "/2" */ unrrduScaleAdd, /* e.g. "+2" */ unrrduScaleSubtract, /* e.g. "-2" */ unrrduScaleAspectRatio, /* "a" */ unrrduScaleExact, /* e.g. "128" */ unrrduScaleLast }; /* flotsam.c */ UNRRDU_EXPORT const int unrrduPresent; UNRRDU_EXPORT const char *unrrduBiffKey; UNRRDU_EXPORT unsigned int unrrduDefNumColumns; /* addresses of all unrrdu_xxxCmd */ UNRRDU_EXPORT unrrduCmd *unrrduCmdList[]; UNRRDU_EXPORT void unrrduUsageUnu(const char *me, hestParm *hparm); UNRRDU_EXPORT int unrrduUsage(const char *me, hestParm *hparm, const char *title, unrrduCmd **cmdList); UNRRDU_EXPORT hestCB unrrduHestPosCB; UNRRDU_EXPORT hestCB unrrduHestMaybeTypeCB; UNRRDU_EXPORT hestCB unrrduHestScaleCB; UNRRDU_EXPORT hestCB unrrduHestBitsCB; UNRRDU_EXPORT hestCB unrrduHestFileCB; UNRRDU_EXPORT hestCB unrrduHestEncodingCB; #ifdef __cplusplus } #endif #endif /* UNRRDU_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/unrrdu/sselect.c0000664000175000017500000000700412165631065017527 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Select subset of slices along an axis" static const char *_unrrdu_sselectInfoL = (INFO ". The choice to keep or nix a slice is determined by whether the " "values in a given 1-D line of values is above or below a given " "threshold.\n " "* Uses nrrdSliceSelect"); int unrrdu_sselectMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *err; Nrrd *nin, *noutAbove, *noutBelow, *nline; unsigned int axis; double thresh; int pret; airArray *mop; char *outS[2]; OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_AXIS(axis, "axis to slice along"); hestOptAdd(&opt, "s,selector", "nline", airTypeOther, 1, 1, &nline, NULL, "the 1-D nrrd of values to compare with threshold", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "th", "thresh", airTypeDouble, 1, 1, &thresh, NULL, "threshold on selector line"); hestOptAdd(&opt, "o,output", "above below", airTypeString, 2, 2, outS, "- x", "outputs for slices corresponding to values " "above (first) and below (second) given threshold. " "Use \"x\" to say that no output is desired."); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_sselectInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); if (!strcmp(outS[0], "x") && !strcmp(outS[1], "x")) { fprintf(stderr, "%s: need to save either above or below slices " "(can't use \"x\" for both)\n", me); airMopError(mop); return 1; } if (strcmp(outS[0], "x")) { noutAbove = nrrdNew(); airMopAdd(mop, noutAbove, (airMopper)nrrdNuke, airMopAlways); } else { noutAbove = NULL; } if (strcmp(outS[1], "x")) { noutBelow = nrrdNew(); airMopAdd(mop, noutBelow, (airMopper)nrrdNuke, airMopAlways); } else { noutBelow = NULL; } if (nrrdSliceSelect(noutAbove, noutBelow, nin, axis, nline, thresh)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error selecting slices:\n%s", me, err); airMopError(mop); return 1; } if (noutAbove) { SAVE(outS[0], noutAbove, NULL); } if (noutBelow) { SAVE(outS[1], noutBelow, NULL); } airMopOkay(mop); return 0; } UNRRDU_CMD(sselect, INFO); teem-1.11.0~svn6057/src/unrrdu/w2i.c0000664000175000017500000000601312165631065016565 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "converts from 1-D world to index position" static const char *_unrrdu_w2iInfoL = (INFO ", given the centering of the data (cell vs. node), " "the range of positions, and the number of intervals into " "which position has been quantized. " "This is a demo/utility, which does not actually operate on any nrrds. " "Previously available as the stand-alone pos2idx binary.\n " "* Uses NRRD_IDX macro"); int unrrdu_w2iMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; airArray *mop; int pret; char *err; int center; double minPos, maxPos, pos, indx, size; mop = airMopNew(); hestOptAdd(&opt, NULL, "center", airTypeEnum, 1, 1, ¢er, NULL, "which centering applies to the quantized position.\n " "Possibilities are:\n " "\b\bo \"cell\": for histogram bins, quantized values, and " "pixels-as-squares\n " "\b\bo \"node\": for non-trivially interpolated " "sample points", NULL, nrrdCenter); hestOptAdd(&opt, NULL, "minPos", airTypeDouble, 1, 1, &minPos, NULL, "smallest position associated with index 0"); hestOptAdd(&opt, NULL, "maxPos", airTypeDouble, 1, 1, &maxPos, NULL, "highest position associated with highest index"); hestOptAdd(&opt, NULL, "num", airTypeDouble, 1, 1, &size, NULL, "number of intervals into which position has been quantized"); hestOptAdd(&opt, NULL, "world", airTypeDouble, 1, 1, &pos, NULL, "the input world position, to be converted to index"); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_w2iInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); indx = NRRD_IDX(center, minPos, maxPos, size, pos); printf("%g\n", indx); airMopOkay(mop); return 0; } UNRRDU_CMD(w2i, INFO); teem-1.11.0~svn6057/src/unrrdu/subst.c0000664000175000017500000000530312165631065017225 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Map nrrd through a univariate substitution table" static const char *_unrrdu_substInfoL = (INFO " (itself represented as a nrrd). The substitution table " "is a 2-by-N table: a list of pairs giving the old value (to replace) " "and the the new value (to change to), in that order. A plain text file " "makes this easy. Unlike with \"unu lut\", \"unu rmap\", and \"unu imap\", " "the output type is the same as the input type, rather than the type of " "the lut or map.\n " "* Uses nrrdApply1DSubstitution"); int unrrdu_substMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; int pret; Nrrd *nin, *nsubst, *nout; airArray *mop; hestOptAdd(&opt, "s,subst", "subst", airTypeOther, 1, 1, &nsubst, NULL, "substition table to map input nrrd through", NULL, NULL, nrrdHestNrrd); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_substInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdApply1DSubstitution(nout, nin, nsubst)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble applying SUBST:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(subst, INFO); teem-1.11.0~svn6057/src/unrrdu/axdelete.c0000664000175000017500000000667412165631065017674 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Remove one or more singleton axes from a nrrd" static const char *_unrrdu_axdeleteInfoL = (INFO ". Singleton axes have only a single sample along them. " "The underlying linear ordering of the samples is " "unchanged, and the information about the other axes is " "shifted downwards as needed. As a total hack, if you give " "-1 as the axis, this will do a matlab-style \"squeeze\", in which " "any and all singleton axes are removed.\n " "* Uses nrrdAxesDelete"); int unrrdu_axdeleteMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout, *ntmp; int pret, _axis; unsigned axis; airArray *mop; hestOptAdd(&opt, "a,axis", "axis", airTypeInt, 1, 1, &_axis, NULL, "dimension (axis index) of the axis to remove"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_axdeleteInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (-1 == _axis) { ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error copying axis:\n%s", me, err); airMopError(mop); return 1; } for (axis=0; axisdim && nout->axis[axis].size > 1; axis++) ; while (axisdim) { if (nrrdAxesDelete(ntmp, nout, axis) || nrrdCopy(nout, ntmp)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error deleting axis:\n%s", me, err); airMopError(mop); return 1; } for (axis=0; axisdim && nout->axis[axis].size > 1; axis++) ; } } else { if (nrrdAxesDelete(nout, nin, _axis)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error deleting axis:\n%s", me, err); airMopError(mop); return 1; } } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(axdelete, INFO); teem-1.11.0~svn6057/src/unrrdu/imap.c0000664000175000017500000001302512165631065017013 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Map nrrd through *irregular* univariate map (\"colormap\")" static const char *_unrrdu_imapInfoL = (INFO ". A map is irregular if the control points are not evenly " "spaced along the domain, and hence their position must be " "explicitly represented in the map. As nrrds, these maps " "are necessarily 2D. Along axis 0, the first value is the " "location of the control point, and the remaining values " "give are the range of the map for that control point. " "The output value(s) is the result of linearly " "interpolating between value(s) from the map.\n " "* Uses nrrdApply1DIrregMap"); int unrrdu_imapMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nmap, *nacl, *nout; airArray *mop; NrrdRange *range=NULL; unsigned int aclLen; int typeOut, rescale, pret, blind8BitRange; double min, max; hestOptAdd(&opt, "m,map", "map", airTypeOther, 1, 1, &nmap, NULL, "irregular map to map input nrrd through", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "l,length", "aclLen", airTypeUInt, 1, 1, &aclLen, "0", "length of accelerator array, used to try to speed-up " "task of finding between which pair of control points " "a given value lies. Not terribly useful for small maps " "(about 10 points or less). Use 0 to turn accelorator off. "); hestOptAdd(&opt, "r,rescale", NULL, airTypeInt, 0, 0, &rescale, NULL, "rescale the input values from the input range to the " "map domain"); hestOptAdd(&opt, "min,minimum", "value", airTypeDouble, 1, 1, &min, "nan", "Low end of input range. Defaults to lowest value " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\")"); hestOptAdd(&opt, "max,maximum", "value", airTypeDouble, 1, 1, &max, "nan", "High end of input range. Defaults to highest value " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\")"); hestOptAdd(&opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange, nrrdStateBlind8BitRange ? "true" : "false", "Whether to know the range of 8-bit data blindly " "(uchar is always [0,255], signed char is [-128,127]). " "Explicitly setting this is useful only with rescaling (\"-r\")"); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &typeOut, "default", "specify the type (\"int\", \"float\", etc.) of the " "output nrrd. " "By default (not using this option), the output type " "is the map's type.", NULL, NULL, &unrrduHestMaybeTypeCB); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_imapInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (aclLen) { nacl = nrrdNew(); airMopAdd(mop, nacl, (airMopper)nrrdNuke, airMopAlways); if (nrrd1DIrregAclGenerate(nacl, nmap, aclLen)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble generating accelerator:\n%s", me, err); airMopError(mop); return 1; } } else { nacl = NULL; } if (rescale) { range = nrrdRangeNew(min, max); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); nrrdRangeSafeSet(range, nin, blind8BitRange); } if (nrrdTypeDefault == typeOut) { typeOut = nmap->type; } /* some very non-exhaustive tests seemed to indicate that the accelerator does not in fact reliably speed anything up. This of course depends on the size of the imap (# points), but chances are most imaps will have only a handful of points, in which case the binary search in _nrrd1DIrregFindInterval() will finish quickly ... */ if (nrrdApply1DIrregMap(nout, nin, range, nmap, nacl, typeOut, rescale)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble applying map:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(imap, INFO); teem-1.11.0~svn6057/src/unrrdu/dering.c0000664000175000017500000001510112165631065017332 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Ring removal for CT" static const char *_unrrdu_deringInfoL = (INFO ". Should be considered a work-in-progress. "); int unrrdu_deringMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; airArray *mop; int pret; Nrrd *nmask; NrrdDeringContext *drc; double center[2], radScale, clampPerc[2], backval; int verbose, linterp, vertSeam; unsigned int thetaNum; NrrdKernelSpec *rkspec, *tkspec; /* HEY is this needed? (to display -rk and -tk kernels) */ hparm->elideSingleOtherDefault = AIR_FALSE; hestOptAdd(&opt, "c,center", "x y", airTypeDouble, 2, 2, center, NULL, "center of rings, in index space of fastest two axes"); hestOptAdd(&opt, "v,verbose", "v", airTypeInt, 1, 1, &verbose, "0", "verbosity level"); hestOptAdd(&opt, "li,linterp", "bool", airTypeBool, 1, 1, &linterp, "false", "whether to use linear interpolation during polar transform"); hestOptAdd(&opt, "vs,vertseam", "bool", airTypeBool, 1, 1, &vertSeam, "false", "whether to dering left and right sides separately " "(requires an even value for -tn thetanum)"); hestOptAdd(&opt, "tn,thetanum", "# smpls", airTypeUInt, 1, 1, &thetaNum, "20", "# of theta samples"); hestOptAdd(&opt, "rs,radscale", "scale", airTypeDouble, 1, 1, &radScale, "1.0", "scaling on radius in polar transform"); hestOptAdd(&opt, "rk,radiuskernel", "kern", airTypeOther, 1, 1, &rkspec, "gauss:3,4", "kernel for high-pass filtering along radial direction", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&opt, "tk,thetakernel", "kern", airTypeOther, 1, 1, &tkspec, "box", "kernel for blurring along theta direction.", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&opt, "cp,clampperc", "lo hi", airTypeDouble, 2, 2, clampPerc, "0.0 0.0", "when clamping values as part of ring estimation, the " "clamping range is set to exclude this percent of values " "from the low and high end of the data range"); hestOptAdd(&opt, "m,mask", "mask", airTypeOther, 1, 1, &nmask, "", "optional: after deringing, output undergoes a lerp, " "parameterized by this array, from the background value " "(via \"-b\") where mask=0 to the original deringing " "output where mask=1. This lerp is effectively the same " "as a \"unu 3op lerp\", so this should either be match the " "input in size, or match its slices along the slowest axis.", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "b,back", "val", airTypeDouble, 1, 1, &backval, "0.0", "when using a mask (\"-m\"), the background value to " "lerp with."); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_deringInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nmask) { if (!(2 == nmask->dim && nrrdTypeBlock != nmask->type && nmask->axis[0].size == nin->axis[0].size && nmask->axis[1].size == nin->axis[1].size)) { fprintf(stderr, "%s: given mask not 2-D %u-by-%u array of scalar type", me, AIR_CAST(unsigned int, nin->axis[0].size), AIR_CAST(unsigned int, nin->axis[1].size)); airMopError(mop); return 1; } } drc = nrrdDeringContextNew(); airMopAdd(mop, drc, (airMopper)nrrdDeringContextNix, airMopAlways); if (nrrdDeringVerboseSet(drc, verbose) || nrrdDeringLinearInterpSet(drc, linterp) || nrrdDeringVerticalSeamSet(drc, vertSeam) || nrrdDeringInputSet(drc, nin) || nrrdDeringCenterSet(drc, center[0], center[1]) || nrrdDeringRadiusScaleSet(drc, radScale) || nrrdDeringThetaNumSet(drc, thetaNum) || nrrdDeringRadialKernelSet(drc, rkspec->kernel, rkspec->parm) || nrrdDeringThetaKernelSet(drc, tkspec->kernel, tkspec->parm) || nrrdDeringClampPercSet(drc, clampPerc[0], clampPerc[1]) || nrrdDeringExecute(drc, nout)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error deringing:\n%s", me, err); airMopError(mop); return 1; } if (nmask) { NrrdIter *nitout, *nitmask, *nitback; Nrrd *ntmp; nitout = nrrdIterNew(); airMopAdd(mop, nitout, (airMopper)nrrdIterNix, airMopAlways); nitmask = nrrdIterNew(); airMopAdd(mop, nitmask, (airMopper)nrrdIterNix, airMopAlways); nitback = nrrdIterNew(); airMopAdd(mop, nitback, (airMopper)nrrdIterNix, airMopAlways); nrrdIterSetValue(nitback, backval); ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); nrrdIterSetNrrd(nitout, nout); nrrdIterSetNrrd(nitmask, nmask); if (nrrdArithIterTernaryOpSelect(ntmp, nrrdTernaryOpLerp, nitmask, nitback, nitout, 2) || nrrdCopy(nout, ntmp)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error masking:\n%s", me, err); airMopError(mop); return 1; } } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD_HIDE(dering, INFO); teem-1.11.0~svn6057/src/unrrdu/make.c0000664000175000017500000006614312165631065017013 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" /* learned: some header file must declare private functions ** within extern "C" so that others can link with them */ #define NO_STRING "." #define INFO "Create a nrrd (or nrrd header) from scratch" static const char *_unrrdu_makeInfoL = (INFO ". The data can be in one or more files, or coming from stdin. " "This provides an easy way of specifying the information about some " "data as to wrap it in a NRRD file, either to pass on for further " "unu processing, or to save to disk. Note that with \"-h\", this creates " "a detached nrrd header file, without ever reading or writing data files. " "\n \n " "When using multiple datafiles, the data from each is simply " "concatenated in memory (as opposed to interleaving along a faster axis). " "Keep in mind that all the options below refer to the finished data segment " "resulting from joining all the data pieces together, " "except for \"-ls\", \"-bs\", and \"-e\", which apply (uniformly) to the " "individual data files. Use the \"-fd\" option when the things being joined " "together are not slices of the final result, but slabs or scanlines. " "It may be easier to put multiple filenames in a response file; " "there can be one or more filenames per line of the response file. " "You can also use a sprintf-style format to identify a numbered " "range of files, so for example \"-i I.%03d 1 90 1\" " "refers to I.001, I.002, ... I.090, using the inclusive range from the first " "to the second integer (following the sprintf-style format), in steps of " "the third. Can optionally give a fourth integer to serve same role as " "\"-fd\"." "\n \n " "NOTE: for the \"-l\" (labels), \"-u\" (units), and \"-spu\" (space units) " "options below, you can use a single unquoted period (.) to signify " "an empty string. This creates a convenient way to convey something that " "the shell doesn't make it easy to convey. Shell expansion weirdness " "also requires the use of quotes around the arguments to \"-orig\" (space " "origin), \"-dirs\" (space directions), and \"-mf\" (measurement frame).\n " "\n " "* Uses various components of file and data IO, but currently there is no " "library function that encapsulates the functionality here."); int unrrdu_makeMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *outData, *err, **dataFileNames, **kvp, *content, encInfo[AIR_STRLEN_LARGE]; Nrrd *nrrd; size_t *size, bufLen; int headerOnly, pret, lineSkip, endian, type, encodingType, gotSpacing, gotThickness, space, spaceSet; long int byteSkip; unsigned int ii, kindsLen, thicknessLen, spacingLen, sizeLen, nameLen, centeringsLen, unitsLen, labelLen, kvpLen, spunitsLen, dataFileDim, spaceDim; double *spacing, *thickness; airArray *mop; NrrdIoState *nio; FILE *fileOut; char **label, **units, **spunits, **kinds, **centerings, *parseBuf, *spcStr, *_origStr, *origStr, *_dirStr, *dirStr, *_mframeStr, *mframeStr; const NrrdEncoding *encoding; /* so that long lists of filenames can be read from file */ hparm->respFileEnable = AIR_TRUE; hparm->greedySingleString = AIR_TRUE; mop = airMopNew(); hestOptAdd(&opt, "h", NULL, airTypeBool, 0, 0, &headerOnly, NULL, "Generate header ONLY: don't write out the whole nrrd, " "don't even bother reading the input data, just output the " "detached nrrd header file (usually with a \".nhdr\" " "extension) determined by the options below. The single " "constraint is that detached headers are incompatible with " "using stdin as the data source."); hestOptAdd(&opt, "i,input", "file", airTypeString, 1, -1, &dataFileNames, "-", "Filename(s) of data file(s); use \"-\" for stdin. *OR*, can " "use sprintf-style format for identifying a range of numbered " "files, see above for details.", &nameLen); hestOptAdd(&opt, "t,type", "type", airTypeEnum, 1, 1, &type, NULL, "type of data (e.g. \"uchar\", \"int\", \"float\", " "\"double\", etc.)", NULL, nrrdType); hestOptAdd(&opt, "s,size", "sz0 sz1", airTypeSize_t, 1, -1, &size, NULL, "number of samples along each axis (and implicit indicator " "of dimension of nrrd)", &sizeLen); hestOptAdd(&opt, "fd,filedim", "dim", airTypeUInt, 1, 1, &dataFileDim, "0", "When using *multiple* input data files (to \"-i\"), what is " "the dimension of the array data in each individual file. By " "default (not using this option), this dimension is assumed " "to be one less than the whole data dimension. "); hestOptAdd(&opt, "sp,spacing", "sp0 sp1", airTypeDouble, 1, -1, &spacing, "nan", "spacing between samples on each axis. Use \"nan\" for " "any non-spatial axes (e.g. spacing between red, green, and blue " "along axis 0 of interleaved RGB image data)", &spacingLen); hestOptAdd(&opt, "th,thickness", "th0 th1", airTypeDouble, 1, -1, &thickness, "nan", "thickness of region represented by one sample along each axis. " " As with spacing, use \"nan\" for " "any non-spatial axes.", &thicknessLen); hestOptAdd(&opt, "k,kind", "k0 k1", airTypeString, 1, -1, &kinds, "", "what \"kind\" is each axis, from the nrrdKind airEnum " "(e.g. space, time, 3-vector, 3D-masked-symmetric-matrix, " "or \"none\" to signify no kind)", &kindsLen); hestOptAdd(&opt, "cn,centering", "c0 c1", airTypeString, 1, -1, ¢erings, "", "kind of centering (node or cell) for each axis, or " "\"none\" to signify no centering", ¢eringsLen); hestOptAdd(&opt, "l,label", "lb0 lb1", airTypeString, 1, -1, &label, "", "short string labels for each of the axes", &labelLen); hestOptAdd(&opt, "u,unit", "un0 un1", airTypeString, 1, -1, &units, "", "short strings giving units for each of the axes", &unitsLen); hestOptAdd(&opt, "c,content", "content", airTypeString, 1, 1, &content, "", "Specifies the content string of the nrrd, which is built upon " "by many nrrd function to record a history of operations"); hestOptAdd(&opt, "ls,lineskip", "num", airTypeInt, 1, 1, &lineSkip, "0", "number of ascii lines to skip before reading data"); hestOptAdd(&opt, "bs,byteskip", "num", airTypeLongInt, 1, 1, &byteSkip, "0", "number of bytes to skip (after skipping ascii lines, if any) " "before reading data. Can use \"-bs -1\" to skip a binary " "header of unknown length in raw-encoded data"); strcpy(encInfo, "encoding of input data. Possibilities include:" "\n \b\bo \"raw\": raw encoding" "\n \b\bo \"ascii\": ascii values, one scanline per line of text, " "values within line are delimited by space, tab, or comma" "\n \b\bo \"hex\": two hex digits per byte"); if (nrrdEncodingGzip->available()) { strcat(encInfo, "\n \b\bo \"gzip\", \"gz\": gzip compressed raw data"); } if (nrrdEncodingBzip2->available()) { strcat(encInfo, "\n \b\bo \"bzip2\", \"bz2\": bzip2 compressed raw data"); } hestOptAdd(&opt, "e,encoding", "enc", airTypeEnum, 1, 1, &encodingType, "raw", encInfo, NULL, nrrdEncodingType); hestOptAdd(&opt, "en,endian", "end", airTypeEnum, 1, 1, &endian, airEnumStr(airEndian, airMyEndian()), "Endianness of data; relevent for any data with value " "representation bigger than 8 bits, with a non-ascii encoding: " "\"little\" for Intel and friends " "(least significant byte first, at lower address); " "\"big\" for everyone else (most significant byte first). " "Defaults to endianness of this machine", NULL, airEndian); hestOptAdd(&opt, "kv,keyvalue", "key/val", airTypeString, 1, -1, &kvp, "", "key/value string pairs to be stored in nrrd. Each key/value " "pair must be a single string (put it in \"\"s " "if the key or the value contain spaces). The format of each " "pair is \":=\", with no spaces before or after " "\":=\".", &kvpLen); hestOptAdd(&opt, "spc,space", "space", airTypeString, 1, 1, &spcStr, "", "identify the space (e.g. \"RAS\", \"LPS\") in which the array " "conceptually lives, from the nrrdSpace airEnum, which in turn " "determines the dimension of the space. Or, use an integer>0 to" "give the dimension of a space that nrrdSpace doesn't know about. " "By default (not using this option), the enclosing space is " "set as unknown."); hestOptAdd(&opt, "orig,origin", "origin", airTypeString, 1, 1, &_origStr, "", "(NOTE: must quote vector) the origin in space of the array: " "the location of the center " "of the first sample, of the form \"(x,y,z)\" (or however " "many coefficients are needed for the chosen space). Quoting the " "vector is needed to stop interpretation from the shell"); hestOptAdd(&opt, "dirs,directions", "v0 v1 ...", airTypeString, 1, 1, &_dirStr, "", "(NOTE: must quote whole vector list) The \"space directions\": " "the vectors in space spanned by incrementing (by one) each " "axis index (the column vectors of the index-to-world " "matrix transform), OR, \"none\" for non-spatial axes. Give " "one vector per axis. (Quoting around whole vector list, not " "individually, is needed because of limitations in the parser)"); hestOptAdd(&opt, "mf,measurementframe", "v0 v1 ...", airTypeString, 1, 1, &_mframeStr, "", "(NOTE: must quote whole vector list). Each vector is a *column* " "vector of the matrix which transforms from coordinates in " "measurement frame (in which the coefficients of vectors and " "tensors are given) to coordinates of world space (given with " "\"-spc\"). This is not a per-axis field: the column vectors " "comprise a D-by-D square matrix, where D is the dimension of " "world space."); hestOptAdd(&opt, "spu,spaceunit", "su0 su1", airTypeString, 1, -1, &spunits, "", "short strings giving units with which the coefficients of the " "space origin and direction vectors are measured.", &spunitsLen); hestOptAdd(&opt, "o,output", "nout", airTypeString, 1, 1, &out, "-", "output filename. If \"-h\" has been used, the output file is " "always a detached header. Otherwise, use extension " "\".nrrd\" to signal creation of self-contained nrrd, and " "\".nhdr\" to signal creating of a detached header with " "(single) data file."); hestOptAdd(&opt, "od,outputdata", "name", airTypeString, 1, 1, &outData, "", "when *not* using \"-h\" and saving to a \".nhdr\" file, using " "this option allows you to explicitly name the data file, " "instead of (by default, not using this option) having it be " "the same filename base as the header file."); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_makeInfoL); airStrtokQuoting = AIR_TRUE; PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); encoding = nrrdEncodingArray[encodingType]; /* ---------------- BEGIN ERROR CHECKING --------------- */ if (headerOnly) { for (ii=0; ii 1 || (sizeLen == 1 && AIR_EXISTS(spacing[0]))); if (gotSpacing && spacingLen != sizeLen) { fprintf(stderr, "%s: number of spacings (%d) not same as dimension (%d)\n", me, spacingLen, sizeLen); airMopError(mop); return 1; } gotThickness = (thicknessLen > 1 || (sizeLen == 1 && AIR_EXISTS(thickness[0]))); if (gotThickness && thicknessLen != sizeLen) { fprintf(stderr, "%s: number of thicknesses (%d) not same as dimension (%d)\n", me, thicknessLen, sizeLen); airMopError(mop); return 1; } if (airStrlen(label[0]) && sizeLen != labelLen) { fprintf(stderr, "%s: number of labels (%d) not same as dimension (%d)\n", me, labelLen, sizeLen); airMopError(mop); return 1; } if (airStrlen(units[0]) && sizeLen != unitsLen) { fprintf(stderr, "%s: number of units (%d) not same as dimension (%d)\n", me, unitsLen, sizeLen); airMopError(mop); return 1; } if (airStrlen(kinds[0]) && sizeLen != kindsLen) { fprintf(stderr, "%s: number of kinds (%d) not same as dimension (%d)\n", me, kindsLen, sizeLen); airMopError(mop); return 1; } if (airStrlen(centerings[0]) && sizeLen != centeringsLen) { fprintf(stderr, "%s: number of centerings (%d) not same as dimension (%d)\n", me, centeringsLen, sizeLen); airMopError(mop); return 1; } /* ----------------- END ERROR CHECKING ---------------- */ /* ----------------- BEGIN SETTING INFO ---------------- */ nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); nrrd = nrrdNew(); airMopAdd(mop, nrrd, (airMopper)nrrdNuke, airMopAlways); nrrd->type = type; nrrd->dim = sizeLen; nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoSize, size); /* have to simulate having parsed this line for error checking in _nrrdDataFNCheck() to not cause problems */ nio->seen[nrrdField_sizes] = AIR_TRUE; if (_nrrdContainsPercentThisAndMore(dataFileNames[0], 'd')) { /* trying to do a formatted filename list */ if (nameLen < 4 || nameLen > 5) { fprintf(stderr, "%s: formatted list of filenames needs between " "3 and 4 ints after the format (not %d)\n", me, nameLen-1); airMopError(mop); return 1; } bufLen = 0; for (ii=0; iiline = parseBuf; nio->pos = 0; if (nrrdFieldInfoParse[nrrdField_data_file](NULL, nrrd, nio, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble with formatted filename list \"%s\":\n%s", me, parseBuf, err); nio->line = NULL; airMopError(mop); return 1; } nio->line = NULL; } else { /* single or regular LIST of files */ if (nameLen > 1) { nio->dataFileDim = dataFileDim ? dataFileDim : nrrd->dim-1; } else { nio->dataFileDim = nrrd->dim; } airArrayLenSet(nio->dataFNArr, nameLen); for (ii=0; iidataFN[ii] = airStrdup(dataFileNames[ii]); } } if (_nrrdDataFNCheck(nio, nrrd, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble with input datafiles:\n%s", me, err); airMopError(mop); return 1; } if (gotSpacing) { nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoSpacing, spacing); } if (gotThickness) { nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoThickness, thickness); } if (airStrlen(label[0])) { for (ii=0; iidim; ii++) { if (!strcmp(NO_STRING, label[ii])) { strcpy(label[ii], ""); } } nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoLabel, label); } if (airStrlen(units[0])) { for (ii=0; iidim; ii++) { if (!strcmp(NO_STRING, units[ii])) { strcpy(units[ii], ""); } } nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoUnits, units); } if (airStrlen(content)) { nrrd->content = airStrdup(content); } if (kvpLen) { for (ii=0; iiline as the channel to communicate the key/value pair, since we have to emulate it having been read from a NRRD header. But because nio doesn't own the memory, we must be careful to unset the pointer prior to NrrdIoStateNix being called by the mop. */ nio->line = kvp[ii]; nio->pos = 0; if (nrrdFieldInfoParse[nrrdField_keyvalue](NULL, nrrd, nio, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble with key/value %d \"%s\":\n%s", me, ii, kvp[ii], err); nio->line = NULL; airMopError(mop); return 1; } nio->line = NULL; } } if (airStrlen(kinds[0])) { /* have to allocate line then pass it to parsing */ bufLen = 0; for (ii=0; iiline = parseBuf; nio->pos = 0; if (nrrdFieldInfoParse[nrrdField_kinds](NULL, nrrd, nio, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble with kinds \"%s\":\n%s", me, parseBuf, err); nio->line = NULL; airMopError(mop); return 1; } nio->line = NULL; } if (airStrlen(centerings[0])) { /* have to allocate line then pass it to parsing */ bufLen = 0; for (ii=0; iiline = parseBuf; nio->pos = 0; if (nrrdFieldInfoParse[nrrdField_centers](NULL, nrrd, nio, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble with centerings \"%s\":\n%s", me, parseBuf, err); nio->line = NULL; airMopError(mop); return 1; } nio->line = NULL; } if (airStrlen(spcStr)) { space = airEnumVal(nrrdSpace, spcStr); if (!space) { /* couldn't parse it as space, perhaps its a uint */ if (1 != sscanf(spcStr, "%u", &spaceDim)) { fprintf(stderr, "%s: couldn't parse \"%s\" as a nrrdSpace " "or as a uint", me, spcStr); airMopError(mop); return 1; } /* else we did parse it as a uint */ nrrd->space = nrrdSpaceUnknown; nrrd->spaceDim = spaceDim; } else { /* we did parse a known space */ nrrdSpaceSet(nrrd, space); } spaceSet = AIR_TRUE; } else { /* we got no space information at all */ nrrdSpaceSet(nrrd, nrrdSpaceUnknown); spaceSet = AIR_FALSE; } if (airStrlen(_origStr)) { /* why this is necessary is a bit confusing to me, both the check for enclosing quotes, and the need to use to a separate variable (isn't hest doing memory management of addresses, not variables?) */ if ('\"' == _origStr[0] && '\"' == _origStr[strlen(_origStr)-1]) { _origStr[strlen(_origStr)-1] = 0; origStr = _origStr + 1; } else { origStr = _origStr; } /* same hack about using NrrdIoState->line as basis for parsing */ nio->line = origStr; nio->pos = 0; if (nrrdFieldInfoParse[nrrdField_space_origin](NULL, nrrd, nio, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble with origin \"%s\":\n%s", me, origStr, err); nio->line = NULL; airMopError(mop); return 1; } nio->line = NULL; } if (airStrlen(_dirStr)) { /* same confusion as above */ if ('\"' == _dirStr[0] && '\"' == _dirStr[strlen(_dirStr)-1]) { _dirStr[strlen(_dirStr)-1] = 0; dirStr = _dirStr + 1; } else { dirStr = _dirStr; } /* same hack about using NrrdIoState->line as basis for parsing */ nio->line = dirStr; nio->pos = 0; if (nrrdFieldInfoParse[nrrdField_space_directions](NULL, nrrd, nio, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble with space directions \"%s\":\n%s", me, dirStr, err); nio->line = NULL; airMopError(mop); return 1; } nio->line = NULL; } if (airStrlen(_mframeStr)) { /* same confusion as above */ if ('\"' == _mframeStr[0] && '\"' == _mframeStr[strlen(_mframeStr)-1]) { _mframeStr[strlen(_mframeStr)-1] = 0; mframeStr = _mframeStr + 1; } else { mframeStr = _mframeStr; } /* same hack about using NrrdIoState->line as basis for parsing */ nio->line = mframeStr; nio->pos = 0; if (nrrdFieldInfoParse[nrrdField_measurement_frame](NULL, nrrd, nio, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble with measurement frame \"%s\":\n%s", me, mframeStr, err); nio->line = NULL; airMopError(mop); return 1; } nio->line = NULL; } if (airStrlen(spunits[0])) { if (!spaceSet) { fprintf(stderr, "%s: can't have space units with no space set\n", me); airMopError(mop); return 1; } if (nrrd->spaceDim != spunitsLen) { fprintf(stderr, "%s: number of space units (%d) " "not same as space dimension (%d)\n", me, spunitsLen, nrrd->spaceDim); airMopError(mop); return 1; } for (ii=0; iispaceDim; ii++) { if (!strcmp(NO_STRING, spunits[ii])) { strcpy(spunits[ii], ""); } } /* have to allocate line then pass it to parsing */ bufLen = 0; for (ii=0; iispaceDim; ii++) { bufLen += airStrlen(" ") + airStrlen("\"\"") + airStrlen(spunits[ii]); } parseBuf = AIR_CALLOC(bufLen+1, char); airMopAdd(mop, parseBuf, airFree, airMopAlways); strcpy(parseBuf, ""); for (ii=0; iispaceDim; ii++) { if (ii) { strcat(parseBuf, " "); } strcat(parseBuf, "\""); strcat(parseBuf, spunits[ii]); strcat(parseBuf, "\""); } nio->line = parseBuf; nio->pos = 0; if (nrrdFieldInfoParse[nrrdField_space_units](NULL, nrrd, nio, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble with space units \"%s\":\n%s", me, parseBuf, err); nio->line = NULL; airMopError(mop); return 1; } nio->line = NULL; } if (_nrrdCheck(nrrd, AIR_FALSE, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problems with nrrd as set up:\n%s", me, err); airMopError(mop); return 1; } /* ----------------- END SETTING INFO ---------------- */ /* -------------------- BEGIN I/O -------------------- */ nio->lineSkip = lineSkip; nio->byteSkip = byteSkip; nio->encoding = encoding; nio->endian = endian; /* for the sake of reading in data files, this is as good a guess as any as to what the header-relative path to them is. This assuages concerns that come up even with headerOnly */ nio->path = airStrdup("."); if (headerOnly) { /* we open and hand off the output FILE* to the nrrd writer, which will not write any data, because of nio->skipData = AIR_TRUE */ if (!( fileOut = airFopen(out, stdout, "wb") )) { fprintf(stderr, "%s: couldn't fopen(\"%s\",\"wb\"): %s\n", me, out, strerror(errno)); airMopError(mop); return 1; } airMopAdd(mop, fileOut, (airMopper)airFclose, airMopAlways); /* whatever line and byte skipping is required will be simply recorded in the header, and done by the next reader */ nio->detachedHeader = AIR_TRUE; nio->skipData = AIR_TRUE; if (nrrdFormatNRRD->write(fileOut, nrrd, nio)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing header:\n%s", me, err); airMopError(mop); return 1; } } else { /* all this does is read the data from the files. We up the verbosity because of all places this is probably where we really want it */ nrrdStateVerboseIO++; if (nrrdFormatNRRD->read(NULL, nrrd, nio)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble reading data files:\n%s", me, err); airMopError(mop); return 1; } nrrdStateVerboseIO--; /* then save normally */ nrrdIoStateInit(nio); if (strlen(outData)) { airArrayLenSet(nio->dataFNArr, 1); nio->dataFN[0] = airStrdup(outData); } SAVE(out, nrrd, nio); } airMopOkay(mop); return 0; } UNRRDU_CMD(make, INFO); teem-1.11.0~svn6057/src/unrrdu/mlut.c0000664000175000017500000001350712165631065017053 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Map nrrd through whole nrrd of univariate lookup tables" static const char *_unrrdu_mlutInfoL = (INFO ", with one lookup table per element of input nrrd. The multiple " "tables are stored in a nrrd with a dimension which is either 1 or 2 " "more than the dimension of the input nrrd, resulting in an output " "which has either the same or one more dimension than the input, " "resptectively.\n " "* Uses nrrdApplyMulti1DLut"); int unrrdu_mlutMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, **_nmlut, *nmlut, *nout; airArray *mop; int typeOut, rescale, pret, blind8BitRange; unsigned int _nmlutLen, mapAxis; double min, max; NrrdRange *range=NULL; hestOptAdd(&opt, "m,map", "mlut", airTypeOther, 1, -1, &_nmlut, NULL, "one nrrd of lookup tables to map input nrrd through, or, " "list of nrrds which contain the individual entries of " "the lookup table at each voxel, which will be joined together.", &_nmlutLen, NULL, nrrdHestNrrd); hestOptAdd(&opt, "r,rescale", NULL, airTypeInt, 0, 0, &rescale, NULL, "rescale the input values from the input range to the " "lut domain. The lut domain is either explicitly " "defined by the axis min,max along axis 0 or 1, or, it " "is implicitly defined as zero to the length of that axis " "minus one."); hestOptAdd(&opt, "min,minimum", "value", airTypeDouble, 1, 1, &min, "nan", "Low end of input range. Defaults to lowest value " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\")"); hestOptAdd(&opt, "max,maximum", "value", airTypeDouble, 1, 1, &max, "nan", "High end of input range. Defaults to highest value " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\")"); hestOptAdd(&opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange, nrrdStateBlind8BitRange ? "true" : "false", "Whether to know the range of 8-bit data blindly " "(uchar is always [0,255], signed char is [-128,127]). " "Explicitly setting this is useful only with rescaling (\"-r\")"); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &typeOut, "default", "specify the type (\"int\", \"float\", etc.) of the " "output nrrd. " "By default (not using this option), the output type " "is the lut's type.", NULL, NULL, &unrrduHestMaybeTypeCB); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_mlutInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); /* by the end of this block we need to have nmlut and mapAxis */ if (1 == _nmlutLen) { /* we got the mlut as a single nrrd */ nmlut = _nmlut[0]; mapAxis = nmlut->dim - nin->dim - 1; /* its not our job to do real error checking ... */ mapAxis = AIR_MIN(mapAxis, nmlut->dim - 1); } else { /* we have to join together multiple nrrds to get the mlut */ nmlut = nrrdNew(); airMopAdd(mop, nmlut, (airMopper)nrrdNuke, airMopAlways); /* assume that mlut component nrrds are all compatible sizes, nrrdJoin will fail if they aren't */ mapAxis = _nmlut[0]->dim - nin->dim; if (nrrdJoin(nmlut, (const Nrrd*const*)_nmlut, _nmlutLen, mapAxis, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble joining mlut:\n%s", me, err); airMopError(mop); return 1; } /* set these if they were given, they'll be NaN otherwise */ nmlut->axis[mapAxis].min = min; nmlut->axis[mapAxis].max = max; } if (!( AIR_EXISTS(nmlut->axis[mapAxis].min) && AIR_EXISTS(nmlut->axis[mapAxis].max) )) { rescale = AIR_TRUE; } if (rescale) { range = nrrdRangeNew(min, max); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); nrrdRangeSafeSet(range, nin, blind8BitRange); } if (nrrdTypeDefault == typeOut) { typeOut = nmlut->type; } if (nrrdApplyMulti1DLut(nout, nin, range, nmlut, typeOut, rescale)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble applying multi-LUT:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(mlut, INFO); teem-1.11.0~svn6057/src/unrrdu/heq.c0000664000175000017500000001000212165631065016632 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Perform histogram equalization" static const char *_unrrdu_heqInfoL = (INFO ". If this seems to be doing nothing, try increasing the " "number of histograms bins by an order of magnitude or " "two (or more). Or, use \"unu gamma\" to warp the values " "in the direction you know they need to go. Either of " "these might work because extremely tall and narrow peaks " "in the equalization histogram will produce poor results.\n " "* Uses nrrdHistoEq"); int unrrdu_heqMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err, *mapS; Nrrd *nin, *nout, *nmap; int smart, pret; unsigned int bins; airArray *mop; float amount; /* we want to facilitate saving out the mapping as a text file, but with the domain included */ /* this is commented out with the 8 Aug 2003 advent of nrrdDefGetenv nrrdDefWriteBareTable = AIR_FALSE; */ hestOptAdd(&opt, "b,bin", "bins", airTypeInt, 1, 1, &bins, NULL, "# bins to use in histogram that is created in order to " "calculate the mapping that achieves the equalization."); hestOptAdd(&opt, "s,smart", "bins", airTypeInt, 0, 1, &smart, "0", "# bins in value histogram to ignore in calculating the mapping. " "Bins are ignored when they get more hits than other bins, and " "when the values that fall in them are constant. This is an " "effective way to prevent large regions of background value " "from distorting the equalization mapping."); hestOptAdd(&opt, "a,amount", "amount", airTypeFloat, 1, 1, &amount, "1.0", "extent to which the histogram equalizing mapping should be " "applied; 0.0: no change, 1.0: full equalization"); hestOptAdd(&opt, "m,map", "filename", airTypeString, 1, 1, &mapS, "", "The value mapping used to achieve histogram equalization is " "represented by a univariate regular map. By giving a filename " "here, that map can be saved out and applied to other nrrds " "with \"unu rmap\""); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_heqInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdHistoEq(nout, nin, airStrlen(mapS) ? &nmap : NULL, bins, smart, amount)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble histogram equalizing:\n%s", me, err); airMopError(mop); return 1; } if (airStrlen(mapS)) { SAVE(mapS, nmap, NULL); } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(heq, INFO); teem-1.11.0~svn6057/src/unrrdu/basinfo.c0000664000175000017500000001216412173672127017514 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Modify whole-array attributes (not per-axis)" static const char *_unrrdu_basinfoInfoL = (INFO ", which is called \"basic info\" in Nrrd terminology. " "The only attributes which are set are those for which command-line " "options are given.\n " "* Uses no particular function; just sets fields in NrrdAxisInfo"); int unrrdu_basinfoMain(int argc, const char **argv, const char *me, hestParm *hparm) { /* these are stock for unrrdu */ hestOpt *opt = NULL; airArray *mop; int pret; char *err; /* these are stock for things using the usual -i and -o */ char *out; Nrrd *nin, *nout; /* these are specific to this command */ NrrdIoState *nio; char *spcStr, *_origStr, *origStr; int space; unsigned int spaceDim; hestOptAdd(&opt, "spc,space", "space", airTypeString, 1, 1, &spcStr, "", "identify the space (e.g. \"RAS\", \"LPS\") in which the array " "conceptually lives, from the nrrdSpace airEnum, which in turn " "determines the dimension of the space. Or, use an integer>0 to" "give the dimension of a space that nrrdSpace doesn't know about. " "By default (not using this option), the enclosing space is " "set as unknown."); hestOptAdd(&opt, "orig,origin", "origin", airTypeString, 1, 1, &_origStr, "", "(NOTE: must quote vector) the origin in space of the array: " "the location of the center " "of the first sample, of the form \"(x,y,z)\" (or however " "many coefficients are needed for the chosen space). Quoting the " "vector is needed to stop interpretation from the shell"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); USAGE(_unrrdu_basinfoInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error copying input:\n%s", me, err); airMopError(mop); return 1; } /* HEY: copy and paste from unrrdu/make.c */ if (airStrlen(spcStr)) { space = airEnumVal(nrrdSpace, spcStr); if (!space) { /* couldn't parse it as space, perhaps its a uint */ if (1 != sscanf(spcStr, "%u", &spaceDim)) { fprintf(stderr, "%s: couldn't parse \"%s\" as a nrrdSpace " "or as a uint", me, spcStr); airMopError(mop); return 1; } /* else we did parse it as a uint */ nout->space = nrrdSpaceUnknown; nout->spaceDim = spaceDim; } else { /* we did parse a known space */ nrrdSpaceSet(nout, space); } } /* HEY: copy and paste from unrrdu/make.c */ if (airStrlen(_origStr)) { /* why this is necessary is a bit confusing to me, both the check for enclosing quotes, and the need to use to a separate variable (isn't hest doing memory management of addresses, not variables?) */ if ('\"' == _origStr[0] && '\"' == _origStr[strlen(_origStr)-1]) { fprintf(stderr, "%s: bingo\n", me); _origStr[strlen(_origStr)-1] = 0; origStr = _origStr + 1; } else { origStr = _origStr; } /* same hack about using NrrdIoState->line as basis for parsing */ nio->line = origStr; nio->pos = 0; if (nrrdFieldInfoParse[nrrdField_space_origin](NULL, nout, nio, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble with origin \"%s\":\n%s", me, origStr, err); nio->line = NULL; airMopError(mop); return 1; } nio->line = NULL; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(basinfo, INFO); teem-1.11.0~svn6057/src/unrrdu/diff.c0000664000175000017500000000625412165631065017003 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Sees if two nrrds are different in any way" static const char *_unrrdu_diffInfoL = (INFO ". Looks through all fields to see if two given nrrds contain the " "same information. Or, array meta-data can be excluded, and comparison " "only on the data values is done with the -od flag.\n " "* Uses nrrdCompare"); int unrrdu_diffMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *err; airArray *mop; int pret; Nrrd *ninA, *ninB; int onlyData, differ; double epsilon; char explain[AIR_STRLEN_LARGE]; mop = airMopNew(); hestOptAdd(&opt, NULL, "ninA", airTypeOther, 1, 1, &ninA, NULL, "First input nrrd.", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, NULL, "ninB", airTypeOther, 1, 1, &ninB, NULL, "Second input nrrd.", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "eps,epsilon", "eps", airTypeDouble, 1, 1, &epsilon, "0.0", "threshold for allowable difference in values in " "data values"); hestOptAdd(&opt, "od,onlydata", NULL, airTypeInt, 0, 0, &onlyData, NULL, "Compare data values only, excluding array meta-data"); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_diffInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); if (nrrdCompare(ninA, ninB, onlyData, epsilon, &differ, explain)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing compare:\n%s", me, err); airMopError(mop); return 1; } if (differ) { printf("%s: %s differ: %s\n", me, onlyData ? "data values" : "nrrds", explain); } else { if (0 == epsilon) { printf("%s: %s are the same\n", me, onlyData ? "data values" : "nrrds"); } else { printf("%s: %s are same or within %g of each other\n", me, onlyData ? "data values" : "nrrds", epsilon); } } airMopOkay(mop); return 0; } UNRRDU_CMD(diff, INFO); teem-1.11.0~svn6057/src/unrrdu/histo.c0000664000175000017500000001066512165631065017222 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Create 1-D histogram of values in a nrrd" static const char *_unrrdu_histoInfoL = (INFO ". Can explicitly set bounds of histogram domain or can learn these " "from the data.\n " "* Uses nrrdHisto"); int unrrdu_histoMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout, *nwght; char *minStr, *maxStr; int type, pret, blind8BitRange; unsigned int bins; NrrdRange *range; airArray *mop; hestOptAdd(&opt, "b,bins", "num", airTypeUInt, 1, 1, &bins, NULL, "# of bins in histogram"); hestOptAdd(&opt, "w,weight", "nweight", airTypeOther, 1, 1, &nwght, "", "how to weigh contributions to histogram. By default " "(not using this option), the increment is one bin count per " "sample, but by giving a nrrd, the value in the nrrd at the " "corresponding location will be the bin count increment ", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "min,minimum", "value", airTypeString, 1, 1, &minStr, "nan", "Value at low end of histogram, given explicitly as a " "regular number, " "*or*, if the number is given with a \"" NRRD_MINMAX_PERC_SUFF "\" suffix, this " "minimum is specified in terms of the percentage of samples in " "input that are lower. " "By default (not using this option), the lowest value " "found in input nrrd."); hestOptAdd(&opt, "max,maximum", "value", airTypeString, 1, 1, &maxStr, "nan", "Value at high end of histogram, given " "explicitly as a regular number, " "*or*, if the number is given with " "a \"" NRRD_MINMAX_PERC_SUFF "\" suffix, " "this maximum is specified " "in terms of the percentage of samples in input that are higher. " "Defaults to highest value found in input nrrd."); hestOptAdd(&opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange, nrrdStateBlind8BitRange ? "true" : "false", "Whether to know the range of 8-bit data blindly " "(uchar is always [0,255], signed char is [-128,127])."); OPT_ADD_TYPE(type, "type to use for bins in output histogram", "uint"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_histoInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); range = nrrdRangeNew(AIR_NAN, AIR_NAN); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdRangePercentileFromStringSet(range, nin, minStr, maxStr, 10*bins /* HEY magic */, blind8BitRange) || nrrdHisto(nout, nin, range, nwght, bins, type)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error with range or quantizing:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(histo, INFO); teem-1.11.0~svn6057/src/unrrdu/cmedian.c0000664000175000017500000001506112165631065017467 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Cheap histogram-based median/mode filtering" static const char *_unrrdu_cmedianInfoL = (INFO ". Only works on 1, 2, or 3 dimensions. The window " "over which filtering is done is always square, and " "only a simplistic weighting scheme is available. " "The method is cheap because it does the median " "or mode based on a histogram, which enforces a quantization to the number " "of bins in the histogram, which probably means a loss of precision for " "anything except 8-bit data. Also, integral values can be recovered " "exactly only when the number of bins is exactly min-max+1 (as reported " "by \"unu minmax\").\n " "* Uses nrrdCheapMedian, plus nrrdSlice and nrrdJoin in " "case of \"-c\""); int unrrdu_cmedianMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout, *ntmp, **mnout; int pad, pret, mode, chan, ni, nsize; unsigned int bins, radius; airArray *mop; float wght; hestOptAdd(&opt, "r,radius", "radius", airTypeUInt, 1, 1, &radius, NULL, "how big a window to filter over. \"-r 1\" leads to a " "3x3 window in an image, and a 3x3x3 window in a volume"); hestOptAdd(&opt, "mode", NULL, airTypeInt, 0, 0, &mode, NULL, "By default, median filtering is done. Using this option " "enables mode filtering, in which the most common value is " "used as output"); hestOptAdd(&opt, "b,bins", "num", airTypeUInt, 1, 1, &bins, "256", "# of bins in histogram. It is in your interest to minimize " "this number, since big histograms mean slower execution " "times. 8-bit data needs at most 256 bins."); hestOptAdd(&opt, "w,weight", "weight", airTypeFloat, 1, 1, &wght, "1.0", "How much higher to preferentially weight samples that are " "closer to the center of the window. \"1.0\" weight means that " "all samples are uniformly weighted over the window, which " "facilitates a simple speed-up. "); /* FYI: these are weights which are just high enough to preserve an island of N contiguous high pixels in a row: 1: 7.695 2: 6.160 3: 4.829 (actually only the middle pixel remains */ hestOptAdd(&opt, "p,pad", NULL, airTypeInt, 0, 0, &pad, NULL, "Pad the input (with boundary method \"bleed\"), " "and crop the output, so as to " "overcome our cheapness and correctly " "handle the border. Obviously, this takes more memory."); hestOptAdd(&opt, "c,channel", NULL, airTypeInt, 0, 0, &chan, NULL, "Slice the input along axis 0, run filtering on all slices, " "and join the results back together. This is the way you'd " "want to process color (multi-channel) images or volumes."); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_cmedianInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (chan) { nsize = AIR_UINT(nin->axis[0].size); mnout = AIR_CALLOC(nsize, Nrrd*); airMopAdd(mop, mnout, airFree, airMopAlways); ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); for (ni=0; niavailable()) { strcat(fmtInfo, "\n \b\bo \"png\": PNG image"); } strcat(fmtInfo, "\n \b\bo \"eps\": EPS file"); hestOptAdd(&opt, "f,format", "form", airTypeEnum, 1, 1, &formatType, NULL, fmtInfo, NULL, nrrdFormatType); strcpy(encInfo, "encoding of data in file. Not all encodings are supported in " "a given format. Possibilities include:" "\n \b\bo \"raw\": raw encoding" "\n \b\bo \"ascii\": print data in ascii" "\n \b\bo \"hex\": two hex digits per byte"); if (nrrdEncodingGzip->available()) { strcat(encInfo, "\n \b\bo \"gzip\", \"gz\": gzip compressed raw data"); } if (nrrdEncodingBzip2->available()) { strcat(encInfo, "\n \b\bo \"bzip2\", \"bz2\": bzip2 compressed raw data"); } if (nrrdEncodingGzip->available() || nrrdEncodingBzip2->available()) { strcat(encInfo, "\n The specifiers for compressions may be followed by a colon " "\":\", followed by an optional digit giving compression \"level\" " "(for gzip) or \"block size\" (for bzip2). For gzip, this can be " "followed by an optional character for a compression strategy:\n " "\b\bo \"d\": default, Huffman with string match\n " "\b\bo \"h\": Huffman alone\n " "\b\bo \"f\": specialized for filtered data\n " "For example, \"gz\", \"gz:9\", \"gz:9f\" are all valid"); } hestOptAdd(&opt, "e,encoding", "enc", airTypeOther, 1, 1, enc, "raw", encInfo, NULL, NULL, &unrrduHestEncodingCB); hestOptAdd(&opt, "en,endian", "end", airTypeEnum, 1, 1, &(nio->endian), airEnumStr(airEndian, airMyEndian()), "Endianness to save data out as; \"little\" for Intel and " "friends; \"big\" for everyone else. " "Defaults to endianness of this machine", NULL, airEndian); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); hestOptAdd(&opt, "od,ouputdata", "name", airTypeString, 1, 1, &outData, "", "when saving to a \".nhdr\" file, " "this option allows you to explicitly name the data file, " "instead of (by default, not using this option) having it be " "the same filename base as the header file."); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_saveInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); nrrdCopy(nout, nin); nio->encoding = nrrdEncodingArray[enc[0]]; nio->format = nrrdFormatArray[formatType]; if (nrrdEncodingTypeGzip == enc[0]) { nio->zlibLevel = enc[1]; nio->zlibStrategy = enc[2]; } else if (nrrdEncodingTypeBzip2 == enc[0]) { nio->bzip2BlockSize = enc[1]; } if (airMyEndian() != nio->endian) { nrrdSwapEndian(nout); } if (airEndsWith(out, NRRD_EXT_NHDR)) { if (nio->format != nrrdFormatNRRD) { fprintf(stderr, "%s: WARNING: will use %s format\n", me, nrrdFormatNRRD->name); nio->format = nrrdFormatNRRD; } if (strlen(outData)) { airArrayLenSet(nio->dataFNArr, 1); nio->dataFN[0] = airStrdup(outData); } } SAVE(out, nout, nio); airMopOkay(mop); return 0; } UNRRDU_CMD(save, INFO); teem-1.11.0~svn6057/src/unrrdu/rmap.c0000664000175000017500000001301312165631065017021 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Map nrrd through one *regular* univariate map (\"colormap\")" static const char *_unrrdu_rmapInfoL = (INFO ". A map is regular if the control points are evenly " "spaced along the domain, and hence their position isn't " "explicitly represented in the map; the axis min, axis " "max, and number of points determine their location. " "The map can be a 1D nrrd (for \"grayscale\"), " "in which case the " "output has the same dimension as the input, " "or a 2D nrrd (for \"color\"), in which case " "the output has one more dimension than the input. In " "either case, the output is the result of linearly " "interpolating between map points, either scalar values " "(\"grayscale\"), or scanlines along axis 0 " "(\"color\").\n " "* Uses nrrdApply1DRegMap"); int unrrdu_rmapMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nmap, *nout; airArray *mop; NrrdRange *range=NULL; int typeOut, rescale, pret, blind8BitRange; double min, max; hestOptAdd(&opt, "m,map", "map", airTypeOther, 1, 1, &nmap, NULL, "regular map to map input nrrd through", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "r,rescale", NULL, airTypeInt, 0, 0, &rescale, NULL, "rescale the input values from the input range to the " "map domain. The map domain is either explicitly " "defined by the axis min,max along axis 0 or 1, or, it " "is implicitly defined as zero to the length of " "that axis minus one."); hestOptAdd(&opt, "min,minimum", "value", airTypeDouble, 1, 1, &min, "nan", "Low end of input range. Defaults to lowest value " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\") or if the map domain is only " "implicitly defined"); hestOptAdd(&opt, "max,maximum", "value", airTypeDouble, 1, 1, &max, "nan", "High end of input range. Defaults to highest value " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\") or if the map domain is only " "implicitly defined"); hestOptAdd(&opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange, nrrdStateBlind8BitRange ? "true" : "false", "Whether to know the range of 8-bit data blindly " "(uchar is always [0,255], signed char is [-128,127]). " "Explicitly setting this is useful " "only with rescaling (\"-r\") or if the map domain is only " "implicitly defined"); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &typeOut, "default", "specify the type (\"int\", \"float\", etc.) of the " "output nrrd. " "By default (not using this option), the output type " "is the map's type.", NULL, NULL, &unrrduHestMaybeTypeCB); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_rmapInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); /* here is a big difference between unu and nrrd: we enforce rescaling any time that the map domain is implicit. This is how the pre-1.6 functionality is recreated. Also, whenever there is rescaling we pass a NrrdRange to reflect the (optional) user range specification, instead of letting nrrdApply1DRegMap find the input range itself (by passing a NULL NrrdRange). */ if (!( AIR_EXISTS(nmap->axis[nmap->dim - 1].min) && AIR_EXISTS(nmap->axis[nmap->dim - 1].max) )) { rescale = AIR_TRUE; } if (rescale) { range = nrrdRangeNew(min, max); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); nrrdRangeSafeSet(range, nin, blind8BitRange); } if (nrrdTypeDefault == typeOut) { typeOut = nmap->type; } if (nrrdApply1DRegMap(nout, nin, range, nmap, typeOut, rescale)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble applying map:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(rmap, INFO); teem-1.11.0~svn6057/src/unrrdu/about.c0000664000175000017500000001561412174656123017207 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Information about this program and its use" int unrrdu_aboutMain(int argc, const char **argv, const char *me, hestParm *hparm) { char buff[AIR_STRLEN_MED], fmt[AIR_STRLEN_MED]; char par1[] = "\t\t\t\t" "\"unu\" is a command-line interface to much of the functionality " "in \"nrrd\", a C library for raster data processing. Nrrd is one " "library in the \"Teem\" collection of libraries. More information " "about Teem is at . A checkout of Teem source " "is available via:\n " "svn co http://svn.code.sf.net/p/teem/code/teem/trunk teem\n "; /* "svn co http://teem.svn.sf.net/svnroot/teem/teem/trunk teem\n "; */ char par2[] = "\t\t\t\t" "Long-term maintenance of this software depends on funding, and " "funding depends on being able to document who is using it for what. " "If unu or Nrrd has helped in your research, including for simple one-off " "experiments or mundane data hacking, the developers of Teem would love " "to know. There are multiple ways of communicating this. " "In your publications, consider adding a line such as this " "in the Acknowledgments: " "\"Data processing performed with the unu tool, " "part of the Teem toolkit available at " "http://teem.sf.net\". " "Alternatively, please email glk@uchicago.edu and briefly describe " "how Teem software has helped in your work. " "Please also consider joining the teem-users mailing list: " ". This is " "the primary forum for feedback, questions, and feature requests.\n "; char par3[] = "\t\t\t\t" "A summary list of unu commands is generated by running simply \"unu\". " "Running a unu command without additional arguments " "(e.g. \"unu slice\") generates its description and usage information. " "This includes information (marked with \"* Uses ...\") " "about the underling Nrrd library calls " "(e.g. nrrdSlice()) that implement the basic functionality in " "that unu command.\n "; char par4[] = "\t\t\t\t" "The utility of unu is mainly as a pre-processing tool for getting " "data into a type, encoding, format, or dimensions best suited for some " "visualization or rendering task. Also, slices and projections are " "effective ways to visually inspect the contents of a dataset. " "Especially useful commands include make, resample, crop, slice, " "project, histo, dhisto, quantize, and save. Unu can process " "CT and MRI volume datasets, grayscale and color images, " "time-varying volumes of vector fields (5-D arrays), and more. " "Currently supported formats are plain text files (2-D float arrays), " "NRRD, VTK structured points, and PNG and PNM images. " "\"unu make -bs -1\" can read from most DICOM files. " "\"unu save\" can generate EPS files. " "Supported encodings are raw, ascii, hex, gzip, and bzip2.\n"; char par5[] = "\t\t\t\t" "Much of the functionality of unu derives from chaining multiple " "invocations together with pipes (\"|\"), minimizing the " "need to save out intermediate files. For example, if " "\"data.raw.gz\" is a gzip'ed 256\tx\t256\tx\t80 volume of raw floats " "written from a PC, " "then the following will save to \"zsum.png\" a histogram " "equalized summation projection along the slowest axis:\n"; char par6[] = "\tunu make -i data.raw.gz -t float -s 256 256 80 " "-e gzip -en little \\\n " " | unu project -a 2 -m sum \\\n " " | unu heq -b 2000 -s 1 \\\n " " | unu quantize -b 8 -o zsum.png" "\n"; int enc, form, miss; AIR_UNUSED(argc); AIR_UNUSED(argv); AIR_UNUSED(me); fprintf(stdout, "\n"); sprintf(buff, "--- unu: Utah Nrrd Utilities command-line interface ---"); sprintf(fmt, "%%%ds\n", (int)((hparm->columns-strlen(buff))/2 + strlen(buff) - 1)); fprintf(stdout, fmt, buff); sprintf(buff, "(Teem version %s, %s)", airTeemVersion, airTeemReleaseDate); sprintf(fmt, "%%%ds\n", (int)((hparm->columns-strlen(buff))/2 + strlen(buff) - 1)); fprintf(stdout, fmt, buff); fprintf(stdout, "\n"); _hestPrintStr(stdout, 1, 0, 78, par1, AIR_FALSE); _hestPrintStr(stdout, 1, 0, 78, par2, AIR_FALSE); _hestPrintStr(stdout, 1, 0, 78, par3, AIR_FALSE); _hestPrintStr(stdout, 1, 0, 78, par4, AIR_FALSE); _hestPrintStr(stdout, 1, 0, 78, par5, AIR_FALSE); _hestPrintStr(stdout, 2, 0, 78, par6, AIR_FALSE); printf(" Formats available:"); miss = AIR_FALSE; for (form=nrrdFormatTypeUnknown+1; formavailable()) { printf(" %s", airEnumStr(nrrdFormatType, form)); } else { miss = AIR_TRUE; } } printf("\n"); if (miss) { printf(" (not available:"); for (enc=nrrdFormatTypeUnknown+1; encavailable()) { printf(" %s", airEnumStr(nrrdFormatType, enc)); } } printf(")\n"); } printf(" Nrrd data encodings available:"); miss = AIR_FALSE; for (enc=nrrdEncodingTypeUnknown+1; encavailable()) { printf(" %s", airEnumStr(nrrdEncodingType, enc)); } else { miss = AIR_TRUE; } } printf("\n"); if (miss) { printf(" (not available:"); for (enc=nrrdEncodingTypeUnknown+1; encavailable()) { printf(" %s", airEnumStr(nrrdEncodingType, enc)); } } printf(")\n"); } printf("\n"); /* NOTE: this is an exceptional unu command that doesn't rely on privateUnrrdu.h USAGE() macro; so we determine our own return value */ return 0; } UNRRDU_CMD(about, INFO); teem-1.11.0~svn6057/src/unrrdu/axinfo.c0000664000175000017500000001351612165631065017356 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Modify attributes of one or more axes" static const char *_unrrdu_axinfoInfoL = (INFO ". The only attributes which are set are those for which command-line " "options are given.\n " "* Uses no particular function; just sets fields in NrrdAxisInfo"); int unrrdu_axinfoMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err, *label, *units, *centerStr, *kindStr; Nrrd *nin, *nout; int pret, center, kind; unsigned int *axes, axesLen, axi; double mm[2], spc; airArray *mop; hestOptAdd(&opt, "a,axes", "ax0", airTypeUInt, 1, -1, &axes, NULL, "the one or more axes that should be modified", &axesLen); hestOptAdd(&opt, "l,label", "label", airTypeString, 1, 1, &label, "", "label to associate with axis"); hestOptAdd(&opt, "u,units", "units", airTypeString, 1, 1, &units, "", "units of measurement"); hestOptAdd(&opt, "mm,minmax", "min max", airTypeDouble, 2, 2, mm, "nan nan", "min and max values along axis"); hestOptAdd(&opt, "sp,spacing", "spacing", airTypeDouble, 1, 1, &spc, "nan", "spacing between samples along axis"); /* HEY: this is currently a fundamental (but only rarely annoying) problem in hest. Because there is functionally no difference between whether an option's information comes from the default string or from the command-line, there is no real way to tell hest, "hey, its just fine for this option to not be used, and if its not used, DON'T DO ANYTHING". The games of setting strings to "" and floats/doubles to NaN are ways of compensating for this. However, there is no analogous trick for airEnums. hestOptAdd(&opt, "c,center", "center", airTypeEnum, 1, 1, ¢, "unknown", "centering of axis: \"cell\" or \"node\"", NULL, nrrdCenter); */ /* but this hack will do for now */ hestOptAdd(&opt, "c,center", "center", airTypeString, 1, 1, ¢erStr, "", "axis centering: \"cell\" or \"node\". Not using this option " "leaves the centering as it is on input"); hestOptAdd(&opt, "k,kind", "kind", airTypeString, 1, 1, &kindStr, "", "axis kind. Not using this option " "leaves the kind as it is on input"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_axinfoInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); for (axi=0; axidim )) { fprintf(stderr, "%s: axis %u not in valid range [0,%u]\n", me, axes[axi], nin->dim-1); airMopError(mop); return 1; } } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error copying input:\n%s", me, err); airMopError(mop); return 1; } for (axi=0; axiaxis[axis].label = (char *)airFree(nout->axis[axis].label); nout->axis[axis].label = airStrdup(label); } if (strlen(units)) { nout->axis[axis].units = (char *)airFree(nout->axis[axis].units); nout->axis[axis].units = airStrdup(units); } if (AIR_EXISTS(mm[0])) { nout->axis[axis].min = mm[0]; } if (AIR_EXISTS(mm[1])) { nout->axis[axis].max = mm[1]; } if (AIR_EXISTS(spc)) { nout->axis[axis].spacing = spc; } /* see above if (nrrdCenterUnknown != cent) { nout->axis[axis].center = cent; } */ if (airStrlen(centerStr)) { if (!strcmp("none", centerStr) || !strcmp("???", centerStr)) { center = nrrdCenterUnknown; } else { if (!(center = airEnumVal(nrrdCenter, centerStr))) { fprintf(stderr, "%s: couldn't parse \"%s\" as %s\n", me, centerStr, nrrdCenter->name); airMopError(mop); return 1; } } nout->axis[axis].center = center; } if (airStrlen(kindStr)) { if (!strcmp("none", kindStr) || !strcmp("???", kindStr)) { kind = nrrdKindUnknown; } else { if (!(kind = airEnumVal(nrrdKind, kindStr))) { fprintf(stderr, "%s: couldn't parse \"%s\" as %s\n", me, kindStr, nrrdKind->name); airMopError(mop); return 1; } } nout->axis[axis].kind = kind; } } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(axinfo, INFO); teem-1.11.0~svn6057/src/unrrdu/sources.cmake0000664000175000017500000000162612161670262020410 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(UNRRDU_SOURCES fft.c i2w.c w2i.c 1op.c 2op.c 3op.c affine.c about.c axdelete.c axinfo.c basinfo.c axinsert.c axmerge.c axsplit.c block.c ccadj.c ccfind.c ccmerge.c ccsettle.c cmedian.c convert.c crop.c acrop.c data.c dering.c dhisto.c dice.c dist.c env.c flip.c flotsam.c gamma.c head.c heq.c histax.c histo.c imap.c inset.c jhisto.c join.c lut.c lut2.c make.c minmax.c cksum.c diff.c dnorm.c vidicon.c undos.c mlut.c mrmap.c pad.c permute.c privateUnrrdu.h project.c quantize.c resample.c reshape.c rmap.c save.c shuffle.c slice.c sselect.c splice.c subst.c swap.c tile.c unblock.c unorient.c unquantize.c unrrdu.h untile.c ) ADD_TEEM_LIBRARY(unrrdu ${UNRRDU_SOURCES}) teem-1.11.0~svn6057/src/unrrdu/axmerge.c0000664000175000017500000000620712165631065017521 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Merge two adjacent axes into one" static const char *_unrrdu_axmergeInfoL = (INFO ". A more general version of \"unu axdelete\". " "The underlying linear ordering of the samples is " "unchanged, and the information about the other axes is " "shifted downwards as needed.\n " "* Uses nrrdAxesMerge"); int unrrdu_axmergeMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout[2]; int *axes, pret, ni; unsigned int ii, jj, axesLen; airArray *mop; hestOptAdd(&opt, "a,axis", "ax0", airTypeInt, 1, -1, &axes, NULL, "axis (or axes) to merge. Each axis index identified is the " "lower of the pair of axes that will be merged. Saying \"-a 2\" " "means to merge axis 2 and axis 3 into axis 2. If multiple " "merges are to be done, the indices listed here are for " "the axes prior to any merging.", &axesLen); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_axmergeInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); airMopAdd(mop, nout[0]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nout[1]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (axesLen > 1) { /* sort merge axes into ascending order */ qsort(axes, axesLen, sizeof(*axes), nrrdValCompare[nrrdTypeInt]); } ni = 0; for (ii=0; iielideSingleOtherDefault = AIR_FALSE; hestOptAdd(&opt, "i", "input", airTypeOther, 1, 1, &nin, NULL, "input image. Should be grayscale PNG.", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "rs", "rescale", airTypeDouble, 1, 1, &rescale, "0.75", "how to rescale (downsample) the image prior to processing, " "just to get a better representation of the floating-point " "range of image values (overcoming 8-bit quantization effects)"); hestOptAdd(&opt, "rsk", "kern", airTypeOther, 1, 1, &rescaleKsp, "hann:5", "kernel for rescaling.", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&opt, "rsp", "percentile", airTypeDouble, 1, 1, &rperc, "1.5", "after rescaling, the highest and lowest percentiles are mapped " "to 0.0 and 255.0, just to have a uniform range of intensities " "in subsequent processing. This option determines how big those " "percentiles are."); hestOptAdd(&opt, "vs", "sx sy", airTypeUInt, 2, 2, vsize, "550 525", "the lowest (\"video\") resolution to which the image is " "down-sampled, reflecting the limited resolution of the " "vidicon tubes"); hestOptAdd(&opt, "pad", "padX padY", airTypeUInt, 2, 2, vpadding, "10 10", "at the lowest resolution, there should be this much padding " "by black, to reflect the fact the signal outside the tube " "(e.g. between scanlines is black)"); hestOptAdd(&opt, "vk", "kernX kernY", airTypeOther, 2, 2, vdsmp, "hann:1,4 cubic:0,0.5", "kernels for downsampling to video resolution; the horizontal " "and vertical kernels are different", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&opt, "stp", "prefix", airTypeString, 1, 1, &stpfx, "", "if a string is given here, a series of images are saved, " "representing the various stages of processing"); hestOptAdd(&opt, "o", "output", airTypeString, 1, 1, &out, NULL, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_vidiconInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); b8range = nrrdRangeNew(0.0, 255.0); airMopAdd(mop, b8range, (airMopper)nrrdRangeNix, airMopAlways); if (!( 2 == nin->dim && nrrdTypeBlock != nin->type )) { fprintf(stderr, "%s: need input as 2D grayscale image (not %u-d %s)\n", me, nin->dim, airEnumStr(nrrdType, nin->type)); airMopError(mop); return 1; } nrescale = nrrdNew(); airMopAdd(mop, nrescale, (airMopper)nrrdNuke, airMopAlways); fprintf(stderr, "%s: rescaling by %g ... \n", me, rescale); rsmc = nrrdResampleContextNew(); airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways); if (nrrdResampleDefaultCenterSet(rsmc, nrrdCenterCell) || nrrdResampleInputSet(rsmc, nin) || nrrdResampleKernelSet(rsmc, 0, rescaleKsp->kernel, rescaleKsp->parm) || nrrdResampleKernelSet(rsmc, 1, rescaleKsp->kernel, rescaleKsp->parm) || nrrdResampleSamplesSet(rsmc, 0, AIR_CAST(size_t, rescale*nin->axis[0].size)) || nrrdResampleSamplesSet(rsmc, 1, AIR_CAST(size_t, rescale*nin->axis[1].size)) || nrrdResampleRangeFullSet(rsmc, 0) || nrrdResampleRangeFullSet(rsmc, 1) || nrrdResampleTypeOutSet(rsmc, nrrdTypeFloat) || nrrdResampleRenormalizeSet(rsmc, AIR_TRUE) || nrrdResampleExecute(rsmc, nrescale)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem rescaling:\n%s", me, err); airMopError(mop); return 1; } #define SAVE_TMP(name, nrrd) \ if (airStrlen(stpfx)) { \ sprintf(stname, "%s-" #name ".png", stpfx); \ if (nrrdQuantize(ntmp, nrrd, b8range, 8) \ || nrrdSave(stname, ntmp, 0)) { \ airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); \ fprintf(stderr, "%s: problem saving %s:\n%s", me, stname, err); \ airMopError(mop); return 1; \ } \ } SAVE_TMP(rescale, nrescale); /* rescaling values to 0.0 -- 255.0 based on percentile rperc */ { Nrrd *nhist; double *hist, sum, total, minval, maxval; unsigned int hi, hbins; float *rescaled; size_t ii, nn; submop = airMopNew(); nhist = nrrdNew(); hbins = 3000; airMopAdd(submop, nhist, (airMopper)nrrdNuke, airMopAlways); if (nrrdHisto(nhist, nrescale, NULL, NULL, hbins, nrrdTypeDouble)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble making histogram:\n%s", me, err); airMopError(submop); airMopError(mop); return 1; } hist = AIR_CAST(double *, nhist->data); total = AIR_CAST(double, nrrdElementNumber(nrescale)); minval = AIR_NAN; sum = 0; for (hi=0; hi= rperc*total/100.0) { minval = AIR_AFFINE(0, hi, hbins-1, nhist->axis[0].min, nhist->axis[0].max); break; } } if (hi == hbins || !AIR_EXISTS(minval)) { fprintf(stderr, "%s: failed to find lower %g-percentile value", me, rperc); airMopError(submop); airMopError(mop); return 1; } maxval = AIR_NAN; sum = 0; for (hi=hbins; hi; hi--) { sum += hist[hi-1]; if (sum >= rperc*total/100.0) { maxval = AIR_AFFINE(0, hi-1, hbins-1, nhist->axis[0].min, nhist->axis[0].max); break; } } if (!hi || !AIR_EXISTS(maxval)) { fprintf(stderr, "%s: failed to find upper %g-percentile value", me, rperc); airMopError(submop); airMopError(mop); return 1; } fprintf(stderr, "%s: min %g --> 0, max %g --> 255\n", me, minval, maxval); nn = nrrdElementNumber(nrescale); rescaled = AIR_CAST(float *, nrescale->data); for (ii=0; iiaxis[0].size / vsize[0]); rpadding[1] = AIR_ROUNDUP(AIR_CAST(double, vpadding[1]) * nrescale->axis[1].size / vsize[1]); fprintf(stderr, "%s: padding in rescaled image: %u x %u\n", me, rpadding[0], rpadding[1]); npad = nrrdNew(); airMopAdd(mop, npad, (airMopper)nrrdNuke, airMopAlways); { ptrdiff_t pmin[2], pmax[2]; pmin[0] = -AIR_CAST(ptrdiff_t, rpadding[0]); pmin[1] = -AIR_CAST(ptrdiff_t, rpadding[1]); pmax[0] = nrescale->axis[0].size + rpadding[0]; pmax[1] = nrescale->axis[1].size + rpadding[1]; if (nrrdPad_nva(npad, nrescale, pmin, pmax, nrrdBoundaryPad, 0.0)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem padding:\n%s", me, err); airMopError(mop); return 1; } } /* rescaling down to "video" resolution */ fprintf(stderr, "%s: downsampling to %u x %u\n", me, AIR_CAST(unsigned int, vsize[0] + 2*vpadding[0]), AIR_CAST(unsigned int, vsize[1] + 2*vpadding[1])); nvbase = nrrdNew(); airMopAdd(mop, nvbase, (airMopper)nrrdNuke, airMopAlways); if (nrrdResampleDefaultCenterSet(rsmc, nrrdCenterCell) || nrrdResampleInputSet(rsmc, npad) || nrrdResampleKernelSet(rsmc, 0, vdsmp[0]->kernel, vdsmp[0]->parm) || nrrdResampleKernelSet(rsmc, 1, vdsmp[1]->kernel, vdsmp[1]->parm) || nrrdResampleSamplesSet(rsmc, 0, vsize[0] + 2*vpadding[0]) || nrrdResampleSamplesSet(rsmc, 1, vsize[1] + 2*vpadding[1]) || nrrdResampleRangeFullSet(rsmc, 0) || nrrdResampleRangeFullSet(rsmc, 1) || nrrdResampleTypeOutSet(rsmc, nrrdTypeFloat) || nrrdResampleRenormalizeSet(rsmc, AIR_TRUE) || nrrdResampleExecute(rsmc, nvbase)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem downsampling to video resolution:\n%s", me, err); airMopError(mop); return 1; } /* halo, lowfilt, windowing, noise, filt, interlace, noise, fuzz, upsample */ nrrdCopy(nout, nvbase); SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD_HIDE(vidicon, INFO); teem-1.11.0~svn6057/src/unrrdu/ccmerge.c0000664000175000017500000001143012165631065017470 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Merge CCs with their neighbors, under various constraints" static const char *_unrrdu_ccmergeInfoL = (INFO ". This operates on the output of \"ccfind\". " "Merging of a CC is always done into its largest neighbor. " "Whether or not to merge can be constrained by one or more of: " "CC size (\"-s\"), original CC value being brighter or darker (\"-d\"), " "and number of neighbors (\"-n\").\n " "* Uses nrrdCCMerge"); int unrrdu_ccmergeMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout, *nout2, *nval; airArray *mop; int pret, maxSize, dir, maxNeigh, revalue; unsigned int conny; hestOptAdd(&opt, "d,directed", "dir", airTypeInt, 1, 1, &dir, "0", "do value-driven merging. Using (positive) \"1\" says that " "dark islands get merged with bright surrounds, while \"-1\" " "says the opposite. By default, merging can go either way. "); hestOptAdd(&opt, "s,size", "max size", airTypeInt, 1, 1, &maxSize, "0", "a cap on the CC size that will be absorbed into its " "surround. CCs larger than this are deemed too significant " "to mess with. Or, use \"0\" to remove any such restriction " "on merging."); hestOptAdd(&opt, "n,neighbor", "max # neigh", airTypeInt, 1, 1, &maxNeigh, "1", "a cap on the number of neighbors that a CC may have if it is " "to be be merged. \"1\" allows only islands to be merged, " "\"2\" does merging with bigger of two neighbors, etc, while " "\"0\" says that number of neighbors is no constraint"); hestOptAdd(&opt, "c,connect", "connectivity", airTypeUInt, 1, 1, &conny, NULL, "what kind of connectivity to use: the number of coordinates " "that vary in order to traverse the neighborhood of a given " "sample. In 2D: \"1\": 4-connected, \"2\": 8-connected"); hestOptAdd(&opt, "revalue", NULL, airTypeInt, 0, 0, &revalue, NULL, "If this option is given, then after the merging, the CCs " "are re-assigned their original datavalues, as given by " "the \"-v\" option"); OPT_ADD_NIN(nin, "input nrrd"); hestOptAdd(&opt, "v,values", "values", airTypeOther, 1, 1, &nval, "", "result of using \"ccfind -v\", the record of which values " "were originally associated with each CC. This is required " "for value-directed merging (with non-zero \"-d\" option), " "or if the \"-revalue\" option is given, " "but is not needed otherwise", NULL, NULL, nrrdHestNrrd); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_ccmergeInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); airMopAdd(mop, nout=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nout2=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdCCMerge(nout, nin, nval, dir, maxSize, maxNeigh, conny)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing merging:\n%s", me, err); airMopError(mop); return 1; } if (revalue && nrrdCCRevalue(nout2, nout, nval)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing CC revalue:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, revalue ? nout2 : nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(ccmerge, INFO); teem-1.11.0~svn6057/src/unrrdu/env.c0000664000175000017500000003205312165631065016657 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "List relevant environment variables and their values" static const char *_unrrdu_envInfoL = (INFO ". These environment variables provide a way of " "setting global variables that can affect" " the way Nrrd (and unu) operates.\n " "* Uses nrrdGetenvBool, nrrdGetenvEnum, " "nrrdGetenvInt, and nrrdGetenvUInt"); void _unrrdu_envBool(FILE *file, const char *envKey, int currVal, const char *varName, const char *desc, int columns) { int val, ret; char *envVal; fprintf(file, "%s (bool): ", envKey); ret = nrrdGetenvBool(&val, &envVal, envKey); switch(ret) { case -1: fprintf(file, "not set\n"); break; case AIR_TRUE: fprintf(file, "set to \"%s\"\n", envVal); break; case AIR_FALSE: fprintf(file, "set to \"%s\"? (invalid) \n", envVal); break; } switch(ret) { case -1: case AIR_FALSE: fprintf(file, " (%s == %s; unchanged)\n", varName, airEnumStr(airBool, currVal)); break; case AIR_TRUE: fprintf(file, " ==> %s = %s **********************\n", varName, airEnumStr(airBool, currVal)); break; } _hestPrintStr(file, 0, 0, columns, desc, AIR_FALSE); fprintf(file, "\n"); } void _unrrdu_envEnum(FILE *file, const airEnum *enm, const char *envKey, int currVal, const char *varName, const char *desc, int columns) { int val, ret; char *envVal; /* !!! HEY: CUT + PASTE !!! */ fprintf(file, "%s (%s enum): ", envKey, enm->name); ret = nrrdGetenvEnum(&val, &envVal, enm, envKey); switch(ret) { case -1: fprintf(file, "not set\n"); break; case AIR_TRUE: fprintf(file, "set to \"%s\"\n", envVal); break; case AIR_FALSE: fprintf(file, "set to \"%s\"? (invalid) \n", envVal); break; } switch(ret) { case -1: case AIR_FALSE: fprintf(file, " (%s == %s; unchanged)\n", varName, airEnumStr(enm, currVal)); break; case AIR_TRUE: fprintf(file, " ==> %s = %s **********************\n", varName, airEnumStr(enm, currVal)); break; } _hestPrintStr(file, 0, 0, columns, desc, AIR_FALSE); fprintf(file, "\n"); /* !!! HEY: CUT + PASTE !!! */ } void _unrrdu_envInt(FILE *file, const char *envKey, int currVal, const char *varName, const char *desc, int columns) { int val, ret; char *envVal; /* !!! HEY: CUT + PASTE !!! */ fprintf(file, "%s (int): ", envKey); ret = nrrdGetenvInt(&val, &envVal, envKey); switch(ret) { case -1: fprintf(file, "not set\n"); break; case AIR_TRUE: fprintf(file, "set to \"%s\"\n", envVal); break; case AIR_FALSE: fprintf(file, "set to \"%s\"? (invalid) \n", envVal); break; } switch(ret) { case -1: case AIR_FALSE: fprintf(file, " (%s == %d; unchanged)\n", varName, currVal); break; case AIR_TRUE: fprintf(file, " ==> %s = %d **********************\n", varName, currVal); break; } _hestPrintStr(file, 0, 0, columns, desc, AIR_FALSE); fprintf(file, "\n"); /* !!! HEY: CUT + PASTE !!! */ } void _unrrdu_envUInt(FILE *file, const char *envKey, unsigned int currVal, const char *varName, const char *desc, int columns) { int ret; unsigned int val; char *envVal; /* !!! HEY: CUT + PASTE !!! */ fprintf(file, "%s (unsigned int): ", envKey); ret = nrrdGetenvUInt(&val, &envVal, envKey); switch(ret) { case -1: fprintf(file, "not set\n"); break; case AIR_TRUE: fprintf(file, "set to \"%s\"\n", envVal); break; case AIR_FALSE: fprintf(file, "set to \"%s\"? (invalid) \n", envVal); break; } switch(ret) { case -1: case AIR_FALSE: fprintf(file, " (%s == %d; unchanged)\n", varName, currVal); break; case AIR_TRUE: fprintf(file, " ==> %s = %u **********************\n", varName, currVal); break; } _hestPrintStr(file, 0, 0, columns, desc, AIR_FALSE); fprintf(file, "\n"); /* !!! HEY: CUT + PASTE !!! */ } int unrrdu_envMain(int argc, const char **argv, const char *me, hestParm *hparm) { FILE *out; AIR_UNUSED(argc); AIR_UNUSED(argv); AIR_UNUSED(me); out = stdout; hestInfo(out, me, _unrrdu_envInfoL, hparm); fprintf(out, "\n"); _hestPrintStr(out, 0, 0, hparm->columns, ("Each variable in the listing below starts with the name of " "the environment variable (\"NRRD_...\"), what type of value " "it represents (e.g. \"int\", \"bool\"), what the " "environment variable is currently set to, what the " "corresponding Nrrd global variable is set to, and a " "description of the variable."), AIR_FALSE); fprintf(out, "\n"); _hestPrintStr(out, 0, 0, hparm->columns, ("Bool variables may be set to true simply by setting the " "environment variable; setting the value to \"true\" or " "\"false\" sets the bool accordingly. Enum variables may " "be set by setting the environment variable to any string " "that parses as one of the enum values. Int and unsigned " "int variables are set via a string parse-able as a numeric " "value."), AIR_FALSE); fprintf(out, "\n"); /* UNRRDU_QUIET_QUIT functionality implemented in privateUnrrdu.h */ _hestPrintStr(out, 0, 0, hparm->columns, ("In addition to the the \"NRRD_\" environment variables, " "there is this one, " UNRRDU_QUIET_QUIT_ENV ", which " "determines whether unu exits " "quietly (without error and usage info) when it fails " "because an input nrrd read immediately hit EOF (as " "happens when many unu invocations are piped together). " "This is currently detected by seeing if the error message " "ends with \n \"" UNRRDU_QUIET_QUIT_STR "\"."), AIR_FALSE); fprintf(out, "\n"); fprintf(out, "%s: ", UNRRDU_QUIET_QUIT_ENV); if (getenv(UNRRDU_QUIET_QUIT_ENV)) { fprintf(out, "is set (to what doesn't matter); quiet-quit enabled\n"); } else { fprintf(out, "is NOT set; quiet-quit NOT enabled\n"); } fprintf(out, "\n"); _unrrdu_envBool(out, nrrdEnvVarStateKeyValuePairsPropagate, nrrdStateKeyValuePairsPropagate, "nrrdStateKeyValuePairsPropagate", "When true, key/value pairs are copied from input " "nrrd to output nrrd just like other basic info that hasn't " "just been modified (e.g. type, dimension, block size).", hparm->columns); _unrrdu_envEnum(out, nrrdCenter, nrrdEnvVarDefaultCenter, nrrdDefaultCenter, "nrrdDefaultCenter", "The type of sample centering to use when none has been " "set but one has to be chosen for some operation " "(e.g. resampling).", hparm->columns); _unrrdu_envEnum(out, nrrdEncodingType, nrrdEnvVarDefaultWriteEncodingType, nrrdDefaultWriteEncodingType, "nrrdDefaultWriteEncodingType", "When writing nrrds, what encoding to use. Only " "\"unu save\" affords explicit control of output encoding.", hparm->columns); _unrrdu_envBool(out, nrrdEnvVarStateKindNoop, nrrdStateKindNoop, "nrrdStateKindNoop", "When true, Nrrd makes not even the slightest effort to be " "smart about setting the \"kind\" field of an axis after " "some operation that modified its samples.", hparm->columns); _unrrdu_envInt(out, nrrdEnvVarStateVerboseIO, nrrdStateVerboseIO, "nrrdStateVerboseIO", "The verbosity level of Nrrd input/output operations.", hparm->columns); _unrrdu_envBool(out, nrrdEnvVarStateBlind8BitRange, nrrdStateBlind8BitRange, "nrrdStateBlind8BitRange", "When true, the determined range of 8-bit data will always " "be [0,255] (for uchar) or [-128,127] (for signed char), " "instead of actually looking into the data to find its " "range.", hparm->columns); _unrrdu_envBool(out, nrrdEnvVarDefaultWriteBareText, nrrdDefaultWriteBareText, "nrrdDefaultWriteBareText", "When false, text files used for saving nrrds start with " "comment (\"# ...\") lines containing nrrd fields.", hparm->columns); _unrrdu_envEnum(out, nrrdType, nrrdEnvVarStateMeasureType, nrrdStateMeasureType, "nrrdStateMeasureType", "For measurements (\"unu project\") like sum and product, " "the type of the output result, when one hasn't been " "explicitly requested.", hparm->columns); _unrrdu_envInt(out, nrrdEnvVarStateMeasureModeBins, nrrdStateMeasureModeBins, "nrrdStateMeasureModeBins", "When measuring mode but without a given histogram, how many " "bins to use in the temporary internal histogram.", hparm->columns); _unrrdu_envEnum(out, nrrdType, nrrdEnvVarStateMeasureHistoType, nrrdStateMeasureHistoType, "nrrdStateMeasureHistoType", "Output type for most measurements of histograms, when one " "hasn't been explicitly requested", hparm->columns); _unrrdu_envBool(out, nrrdEnvVarStateAlwaysSetContent, nrrdStateAlwaysSetContent, "nrrdStateAlwaysSetContent", "If true, the output content string is set even when the " "input content string is not set.", hparm->columns); _unrrdu_envBool(out, nrrdEnvVarStateDisableContent, nrrdStateDisableContent, "nrrdStateDisableContent", "If true, output content is never set.", hparm->columns); _unrrdu_envUInt(out, nrrdEnvVarDefaultWriteCharsPerLine, nrrdDefaultWriteCharsPerLine, "nrrdDefaultWriteCharsPerLine", "When using text encoding, maximum # characters allowed " "per line.", hparm->columns); _unrrdu_envUInt(out, nrrdEnvVarDefaultWriteValsPerLine, nrrdDefaultWriteValsPerLine, "nrrdDefaultWriteValsPerLine", "When using text encoding, maximum # values allowed " "per line", hparm->columns); _unrrdu_envBool(out, nrrdEnvVarStateGrayscaleImage3D, nrrdStateGrayscaleImage3D, "nrrdStateGrayscaleImage3D", "If true, reading a 2-D grayscale image results in a " "3-D image with a single sample (size=1) on the first " "(fastest) axis.", hparm->columns); #if 0 /* GLK is ambivalent about the continued existence of these ... */ nrrdGetenvDouble(/**/ &nrrdDefaultKernelParm0, nrrdEnvVarDefaultKernelParm0); nrrdGetenvDouble(/**/ &nrrdDefaultSpacing, nrrdEnvVarDefaultSpacing); #endif /* NOTE: this is an exceptional unu command that doesn't rely on privateUnrrdu.h USAGE() macro; so we determine our own return value */ return 0; } UNRRDU_CMD(env, INFO); teem-1.11.0~svn6057/src/unrrdu/jhisto.c0000664000175000017500000001651012165631065017367 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Create joint histogram of two or more nrrds" static const char *_unrrdu_jhistoInfoL = (INFO ". Each axis of the output corresponds to one of the " "input nrrds, and each bin in the output records the " "number of corresponding positions in the inputs with " "a combination of values represented by the coordinates " "of the bin.\n " "* Uses nrrdHistoJoint"); int unrrdu_jhistoMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd **nin, **npass; Nrrd *nout, *nwght; size_t *bin; int type, clamp[NRRD_DIM_MAX], pret; unsigned int minLen, maxLen, ninLen, binLen, ai, diceax; airArray *mop; double *min, *max; NrrdRange **range; hestOptAdd(&opt, "b,bin", "bins0 bins1", airTypeSize_t, 2, -1, &bin, NULL, "bins is the number of bins to use along axis i (of joint " "histogram), which represents the values of nin ", &binLen); hestOptAdd(&opt, "w,weight", "nweight", airTypeOther, 1, 1, &nwght, "", "how to weigh contributions to joint histogram. By default " "(not using this option), the increment is one bin count per " "sample, but by giving a nrrd, the value in the nrrd at the " "corresponding location will be the bin count increment ", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "min,minimum", "min0 min1", airTypeDouble, 2, -1, &min, "nan nan", "min is the low range of values to be quantized along " "axis i; use \"nan\" to represent lowest value present ", &minLen); hestOptAdd(&opt, "max,maximum", "max0 max1", airTypeDouble, 2, -1, &max, "nan nan", "max is the high range of values to be quantized along " "axis i; use \"nan\" to represent highest value present ", &maxLen); OPT_ADD_TYPE(type, "type to use for output (the type used to store hit " "counts in the joint histogram). Clamping is done on hit " "counts so that they never overflow a fixed-point type", "uint"); hestOptAdd(&opt, "i,input", "nin0 [nin1]", airTypeOther, 1, -1, &nin, "-", "list of nrrds (one for each axis of joint histogram), " "or, single nrrd that will be sliced along specified axis.", &ninLen, NULL, nrrdHestNrrd); hestOptAdd(&opt, "a,axis", "axis", airTypeUInt, 1, 1, &diceax, "0", "axis to slice along when working with single nrrd. "); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_jhistoInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); if (ninLen == 1) { /* Slice a nrrd on the fly */ size_t asize; if (!( diceax <= nin[0]->dim-1 )) { fprintf(stderr, "%s: slice axis %u not valid for single %u-D nrrd", me, diceax, nin[0]->dim); airMopError(mop); return 1; } asize = nin[0]->axis[diceax].size; if (asize != binLen) { fprintf(stderr, "%s: size (%u) of slice axis %u != # bins given (%u)\n", me, AIR_CAST(unsigned int, asize), diceax, AIR_CAST(unsigned int, binLen)); airMopError(mop); return 1; } /* create buffer for slices */ if (!( npass = AIR_CALLOC(binLen, Nrrd*) )) { fprintf(stderr, "%s: couldn't allocate nrrd array (size %u)\n", me, binLen); airMopError(mop); return 1; } airMopMem(mop, &npass, airMopAlways); /* slice this nrrd, allocate new nrrds, and store the slices in nin */ for (ai=0; aimin = min[ai]; } } if (2 != maxLen || (AIR_EXISTS(max[0]) || AIR_EXISTS(max[1]))) { if (maxLen != binLen) { fprintf(stderr, "%s: # maxs (%d) != # input nrrds (%d)\n", me, maxLen, binLen); airMopError(mop); return 1; } for (ai=0; aimax = max[ai]; } } for (ai=0; aitype) { fprintf(stderr, "%s: can only have scalar kinds (not %s)\n", me, airEnumStr(nrrdType, nrrdTypeBlock)); airMopError(mop); exit(1); } /* make sure all kinds are set to something */ /* see if there's a range kind, verify that there's only one */ /* set haveMM */ haveMM = AIR_TRUE; kindIn = nrrdKindUnknown; kindAxis = 0; for (axi=0; axidim; axi++) { if (nrrdKindUnknown == nin->axis[axi].kind || nrrdKindIsDomain(nin->axis[axi].kind)) { haveMM &= AIR_EXISTS(nin->axis[axi].min); haveMM &= AIR_EXISTS(nin->axis[axi].max); } else { if (nrrdKindUnknown != kindIn) { fprintf(stderr, "%s: got non-domain kind %s on axis %u, but already " "have %s from axis %u\n", me, airEnumStr(nrrdKind, nin->axis[axi].kind), axi, airEnumStr(nrrdKind, kindIn), kindAxis); airMopError(mop); exit(1); } kindIn = nin->axis[axi].kind; kindAxis = axi; } } /* see if the non-domain kind is something we can interpret as a tensor */ if (nrrdKindUnknown != kindIn) { switch (kindIn) { /* ======= THESE are the kinds that we can possibly output ======= */ case nrrdKind2Vector: case nrrdKind3Vector: case nrrdKind4Vector: case nrrdKind2DSymMatrix: case nrrdKind2DMatrix: case nrrdKind3DSymMatrix: case nrrdKind3DMatrix: /* =============================================================== */ kindOut = kindIn; break; /* Some other kinds are mapped to those above */ case nrrdKind3Color: case nrrdKindRGBColor: kindOut = nrrdKind3Vector; break; case nrrdKind4Color: case nrrdKindRGBAColor: kindOut = nrrdKind4Vector; break; default: fprintf(stderr, "%s: got non-conforming kind %s on axis %u\n", me, airEnumStr(nrrdKind, kindIn), kindAxis); airMopError(mop); exit(1); break; } } else { kindOut = nrrdKindUnknown; } /* initialize output by copying */ nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, nin)) { airMopAdd(mop, err = biffGet(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble copying:\n%s", me, err); airMopError(mop); exit(1); } /* no comments, either advertising the format URL or anything else */ nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); nio->skipFormatURL = AIR_TRUE; if (headerOnly) { nio->skipData = AIR_TRUE; } nrrdCommentClear(nout); /* no measurement frame */ gotmf = AIR_FALSE; for (si=0; simeasurementFrame[si][sj]); } } if (gotmf) { fprintf(stderr, "%s: WARNING: incoming array measurement frame; " "it will be erased on output.\n", me); airMopError(mop); exit(1); } for (si=0; simeasurementFrame[si][sj] = AIR_NAN; } } /* no key/value pairs */ nrrdKeyValueClear(nout); /* no content field */ nout->content = airFree(nout->content); /* normalize domain kinds to "space" */ /* turn off centers (perhaps Diderot should assume cell-centered) */ /* turn off thickness */ /* turn off labels and units */ for (axi=0; axidim; axi++) { if (nrrdKindUnknown == kindOut) { nout->axis[axi].kind = nrrdKindSpace; } else { nout->axis[axi].kind = (kindAxis == axi ? kindOut : nrrdKindSpace); } nout->axis[axi].center = nrrdCenterUnknown; nout->axis[axi].thickness = AIR_NAN; nout->axis[axi].label = airFree(nout->axis[axi].label); nout->axis[axi].units = airFree(nout->axis[axi].units); nout->axis[axi].min = AIR_NAN; nout->axis[axi].max = AIR_NAN; nout->axis[axi].spacing = AIR_NAN; } /* logic of orientation definition: if space dimension is known: set origin to zero if not already set set space direction to unit vector if not already set else: set origin to zero and all space directions to units might be nice to use gage's logic for mapping from world to index, but we have to accept a greater variety of kinds and dimensions than gage ever has to process. */ if (nout->spaceDim && !trivialOrient) { int saxi = 0; /* we use only the space dimension, not any named space */ nout->space = nrrdSpaceUnknown; if (!nrrdSpaceVecExists(nout->spaceDim, nout->spaceOrigin)) { nrrdSpaceVecSetZero(nout->spaceOrigin); } for (axi=0; axidim; axi++) { if (nrrdKindUnknown == kindOut || kindAxis != axi) { if (!nrrdSpaceVecExists(nout->spaceDim, nout->axis[axi].spaceDirection)) { nrrdSpaceVecSetZero(nout->axis[axi].spaceDirection); nout->axis[axi].spaceDirection[saxi] = sscl; } saxi++; } else { nrrdSpaceVecSetNaN(nout->axis[axi].spaceDirection); } } } else if (haveMM && !trivialOrient) { int saxi = 0; for (axi=0; axidim; axi++) { if (nrrdKindUnknown == kindOut || kindAxis != axi) { nrrdSpaceVecSetZero(nout->axis[axi].spaceDirection); nout->axis[axi].spaceDirection[saxi] = (nin->axis[axi].max - nin->axis[axi].min)/(nin->axis[axi].size-1); nout->spaceOrigin[saxi] = nin->axis[axi].min; saxi++; } else { nrrdSpaceVecSetNaN(nout->axis[axi].spaceDirection); } } nout->spaceDim = saxi; } else { /* either trivialOrient, or not spaceDim, or not not haveMM */ int saxi = 0; nout->space = nrrdSpaceUnknown; nrrdSpaceVecSetZero(nout->spaceOrigin); for (axi=0; axidim; axi++) { if (nrrdKindUnknown == kindOut || kindAxis != axi) { nrrdSpaceVecSetZero(nout->axis[axi].spaceDirection); nout->axis[axi].spaceDirection[saxi] = (AIR_EXISTS(nin->axis[axi].spacing) ? nin->axis[axi].spacing : sscl); saxi++; } else { nrrdSpaceVecSetNaN(nout->axis[axi].spaceDirection); } } nout->spaceDim = saxi; } /* space dimension has to match the number of domain axes */ if (nout->dim != nout->spaceDim + !!kindOut) { fprintf(stderr, "%s: output dim %d != spaceDim %d + %d %s%s%s\n", me, nout->dim, nout->spaceDim, !!kindOut, kindOut ? "for non-scalar (" : "(scalar data)", kindOut ? airEnumStr(nrrdKind, kindOut) : "", kindOut ? ") data" : ""); airMopError(mop); exit(1); } if (recenter) { /* sets field's origin so field is centered on the origin. capiche? */ /* this code was tacked on later than the stuff above, so its logic could probably be moved up there, but it seems cleaner to have it as a separate post-process */ double mean[NRRD_SPACE_DIM_MAX]; nrrdSpaceVecSetZero(mean); for (axi=0; axidim; axi++) { if (nrrdKindUnknown == kindOut || kindAxis != axi) { nrrdSpaceVecScaleAdd2(mean, 1.0, mean, 0.5*(nout->axis[axi].size - 1), nout->axis[axi].spaceDirection); } } nrrdSpaceVecScaleAdd2(mean, 1.0, mean, 1.0, nout->spaceOrigin); /* now mean is the center of the field */ nrrdSpaceVecScaleAdd2(nout->spaceOrigin, 1.0, nout->spaceOrigin, -1.0, mean); } if (nrrdSave(outS, nout, nio)) { airMopAdd(mop, err = biffGet(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving \"%s\":\n%s", me, outS, err); airMopError(mop); exit(1); } airMopOkay(mop); return 0; } UNRRDU_CMD_HIDE(dnorm, INFO); teem-1.11.0~svn6057/src/unrrdu/fft.c0000664000175000017500000001731612165631065016653 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Fast Fourier Transform of selected axes" static const char *_unrrdu_fftInfoL_yes = (INFO ". Initial attempt at wrapping the FFTW3 library; options are " "likely to change in Teem 2.0.\n " "* Uses nrrdFFT"); static const char *_unrrdu_fftInfoL_no = (INFO ". This Teem has NOT been compiled with FFTW3 . " "If it had been, " "this would be a command-line interface to that functionality. " "There is currently no non-FFTW implementation of the FFT available.\n " "* Uses nrrdFFT"); /* We create an airEnum to parse the "forward" and "backwards" values needed to specify which kind of transform to run */ static const char * _directionStr[] = { "(unknown direction)", "forward", "backward" }; static const char * _directionDesc[] = { "unknown direction", "forward transform", "backward (inverse) transform" }; /* from fftw3.h #define FFTW_FORWARD (-1) #define FFTW_BACKWARD (+1) */ #define FORW (-1) #define BACK (+1) static const int _directionVal[] = { 0, FORW, BACK }; static const char * _directionStrEqv[] = { "f", "forw", "forward", "b", "back", "backward", "i", "inv", "inverse", "" }; static const int _directionValEqv[] = { FORW, FORW, FORW, BACK, BACK, BACK, BACK, BACK, BACK }; static const airEnum _direction_enm = { "direction", 2, _directionStr, _directionVal, _directionDesc, _directionStrEqv, _directionValEqv, AIR_FALSE }; static const airEnum *const direction_enm = &_direction_enm; int unrrdu_fftMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *_nin, *nout; int pret; airArray *mop; int sign, rigor, rescale, realInput; char *wispath; FILE *fwise; unsigned int *axes, axesLen; hestOptAdd(&opt, NULL, "dir", airTypeEnum, 1, 1, &sign, NULL, "forward (\"forw\", \"f\") or backward/inverse " "(\"back\", \"b\") transform ", NULL, direction_enm); hestOptAdd(&opt, "a,axes", "ax0", airTypeUInt, 1, -1, &axes, NULL, "the one or more axes that should be transformed", &axesLen); hestOptAdd(&opt, "pr,planrigor", "pr", airTypeEnum, 1, 1, &rigor, "est", "rigor with which fftw plan is constructed. Options include:\n " "\b\bo \"e\", \"est\", \"estimate\": only an estimate\n " "\b\bo \"m\", \"meas\", \"measure\": standard amount of " "measurements of system properties\n " "\b\bo \"p\", \"pat\", \"patient\": slower, more measurements\n " "\b\bo \"x\", \"ex\", \"exhaustive\": slowest, most measurements", NULL, nrrdFFTWPlanRigor); hestOptAdd(&opt, "r,rescale", "bool", airTypeBool, 1, 1, &rescale, "true", "scale fftw output (by sqrt(1/N)) so that forward and backward " "transforms will get back to original values"); hestOptAdd(&opt, "w,wisdom", "filename", airTypeString, 1, 1, &wispath, "", "A filename here is used to read in fftw wisdom (if the file " "exists already), and is used to save out updated wisdom " "after the transform. By default (not using this option), " "no wisdom is read or saved. Note: no wisdom is gained " "(that is, learned by FFTW) with planning rigor \"estimate\"."); OPT_ADD_NIN(_nin, "input nrrd"); hestOptAdd(&opt, "ri,realinput", NULL, airTypeInt, 0, 0, &realInput, NULL, "input is real-valued, so insert new length-2 axis 0 " "and set complex component to 0.0. Axes to transform " "(indicated by \"-a\") will be incremented accordingly."); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); if (nrrdFFTWEnabled) { USAGE(_unrrdu_fftInfoL_yes); } else { USAGE(_unrrdu_fftInfoL_no); } PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (realInput) { ptrdiff_t minPad[NRRD_DIM_MAX], maxPad[NRRD_DIM_MAX]; unsigned int axi; Nrrd *ntmp; ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); if (nrrdAxesInsert(ntmp, _nin, 0)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error creating complex axis:\n%s", me, err); airMopError(mop); return 1; } nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); minPad[0] = 0; maxPad[0] = 1; for (axi=1; axidim; axi++) { minPad[axi] = 0; maxPad[axi] = AIR_CAST(ptrdiff_t, ntmp->axis[axi].size-1); } if (nrrdPad_nva(nin, ntmp, minPad, maxPad, nrrdBoundaryPad, 0.0)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error padding out complex axis:\n%s", me, err); airMopError(mop); return 1; } /* increment specified axes to transform */ for (axi=0; axi M-w (with w > 0) " "there is a smooth transition from x to asymptotic to M\n " "\b\bo \"max_sm\": smoothed maximum function; " "max_sm(M, w, x) is like max(M,x) but for x < m+w (with w > m) " "there is a smooth transition from x to asymptotic to m\n " "\b\bo \"lt_sm\": 1st less than 3rd, smoothed by 2nd\n " "\b\bo \"gt_sm\": 1st greater than 3rd, smoothed by 2nd\n " "\b\bo \"clamp\": 2nd value is clamped to range between " "the 1st and the 3rd\n " "\b\bo \"ifelse\": if 1st value non-zero, then 2nd value, else " "3rd value\n " "\b\bo \"lerp\": linear interpolation between the 2nd and " "3rd values, as the 1st value varies between 0.0 and 1.0, " "respectively\n " "\b\bo \"exists\": if the 1st value exists, use the 2nd " "value, otherwise use the 3rd\n " "\b\bo \"in_op\": 1 iff 2nd value is > 1st and < 3rd, " "0 otherwise\n " "\b\bo \"in_cl\": 1 iff 2nd value is >= 1st and <= 3rd, " "0 otherwise\n " "\b\bo \"gauss\": evaluate (at 1st value) Gaussian with mean=2nd " "and stdv=3rd value\n " "\b\bo \"rician\": evaluate (at 1st value) Rician with mean=2nd " "and stdv=3rd value", NULL, nrrdTernaryOp); hestOptAdd(&opt, NULL, "in1", airTypeOther, 1, 1, &in1, NULL, "First input. Can be a single value or a nrrd.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "in2", airTypeOther, 1, 1, &in2, NULL, "Second input. Can be a single value or a nrrd.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "in3", airTypeOther, 1, 1, &in3, NULL, "Third input. Can be a single value or a nrrd.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &type, "default", "type to convert all nrrd inputs to, prior to " "doing operation. This also determines output type. " "By default (not using this option), the types of the input " "nrrds are left unchanged.", NULL, NULL, &unrrduHestMaybeTypeCB); hestOptAdd(&opt, "w,which", "arg", airTypeInt, 1, 1, &which, "-1", "Which argument (0, 1, or 2) should be used to determine the " "shape of the output nrrd. By default (not using this option), " "the first non-constant argument is used. "); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_3opInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); /* fprintf(stderr, "%s: op = %d\n", me, op); fprintf(stderr, "%s: in1->left = %d, in2->left = %d\n", me, (int)(in1->left), (int)(in2->left)); */ if (nrrdTypeDefault != type) { /* they wanted to convert nrrds to some other type first */ E = 0; if (in1->ownNrrd) { if (!E) E |= nrrdConvert(ntmp=nrrdNew(), in1->ownNrrd, type); if (!E) nrrdIterSetOwnNrrd(in1, ntmp); } if (in2->ownNrrd) { if (!E) E |= nrrdConvert(ntmp=nrrdNew(), in2->ownNrrd, type); if (!E) nrrdIterSetOwnNrrd(in2, ntmp); } if (in3->ownNrrd) { if (!E) E |= nrrdConvert(ntmp=nrrdNew(), in3->ownNrrd, type); if (!E) nrrdIterSetOwnNrrd(in3, ntmp); } if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error converting input nrrd(s):\n%s", me, err); airMopError(mop); return 1; } /* this will still leave a nrrd in the NrrdIter for nrrdIterNix() (called by hestParseFree() called be airMopOkay()) to clear up */ } /* HEY: will need to add handling of RNG seed (as in 1op and 2op) if there are any 3ops involving random numbers */ if (-1 == which ? nrrdArithIterTernaryOp(nout, op, in1, in2, in3) : nrrdArithIterTernaryOpSelect(nout, op, in1, in2, in3, AIR_CAST(unsigned int, which))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing ternary operation:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(3op, INFO); teem-1.11.0~svn6057/src/unrrdu/block.c0000664000175000017500000000543212165631065017162 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Condense axis-0 scanlines into \"blocks\"" static const char *_unrrdu_blockInfoL = (INFO ". Output nrrd will be of type \"block\": the type " "for an opaque chunk of " "memory. Block samples can be sliced, cropped, shuffled, " "permuted, etc., but there is no scalar value associated " "with them, so they can not be histogrammed, quantized, " "resampled, converted, etc. The output nrrd will have " "one less dimension than input; axis N information will " "be shifted down to axis N-1. Underlying data " "is unchanged."); int unrrdu_blockMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; airArray *mop; int pret; /* if we gave a default for this, then there it would fine to have no command-line arguments whatsoever, and then the user would not know how to get the basic usage information */ hestOptAdd(&opt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input nrrd", NULL, NULL, nrrdHestNrrd); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_blockInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdBlock(nout, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error blocking nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(block, INFO); teem-1.11.0~svn6057/src/unrrdu/axsplit.c0000664000175000017500000000517712165631065017562 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Split one axis into two axes" static const char *_unrrdu_axsplitInfoL = (INFO ". More general version of \"unu axinsert\", since a given axis can " "be split into fast and slow axes of arbitrary size, as long as the " "product of the fast and slow sizes is the same as the original size.\n " "* Uses nrrdAxesSplit"); int unrrdu_axsplitMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int pret; size_t size[2]; unsigned int axis; airArray *mop; OPT_ADD_AXIS(axis, "dimension (axis index) to split at"); hestOptAdd(&opt, "s,size", "fast, slow sizes", airTypeSize_t, 2, 2, size, NULL, "fast and slow axis sizes to produce as result of splitting " "given axis."); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_axsplitInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdAxesSplit(nout, nin, axis, size[0], size[1])) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error splitting axis:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(axsplit, INFO); teem-1.11.0~svn6057/src/unrrdu/resample.c0000664000175000017500000004743512174652360017712 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Filtering and {up,down}sampling with a separable kernel" static const char *_unrrdu_resampleInfoL = (INFO ". Simplifies access to the NrrdResampleContext functions " "by assuming (among other things) that the same kernel " "is used for resampling " "every axis that is being resampled. Only required option is " "\"-s\" to specify which axes to resample and how many " "output samples to generate. Resampling kernel \"-k\" defaults " "to an interpolating cubic, but many other choices are available. " "By default, resampling an axis resamples the full extent of its " "samples, but it is possible to offset this range via \"-off\", " "or to crop and/or pad via \"-min\" and \"-max\". " "The resampling respects the difference between cell- and " "node-centered data, but you can over-ride known centering " "with \"-co\".\n " "* Uses the many nrrdResample* functions operating on a nrrdResampleContext"); int unrrdu_resampleMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int type, bb, pret, norenorm, neb, older, E, defaultCenter, verbose, overrideCenter, minSet=AIR_FALSE, maxSet=AIR_FALSE, offSet=AIR_FALSE; unsigned int scaleLen, ai, samplesOut, minLen, maxLen, offLen, aspRatNum, nonAspRatNum; airArray *mop; double *scale; double padVal, *min, *max, *off, aspRatScl=AIR_NAN; NrrdResampleInfo *info; NrrdResampleContext *rsmc; NrrdKernelSpec *unuk; mop = airMopNew(); info = nrrdResampleInfoNew(); airMopAdd(mop, info, (airMopper)nrrdResampleInfoNix, airMopAlways); hparm->elideSingleOtherDefault = AIR_FALSE; hestOptAdd(&opt, "old", NULL, airTypeInt, 0, 0, &older, NULL, "instead of using the new nrrdResampleContext implementation, " "use the old nrrdSpatialResample implementation"); hestOptAdd(&opt, "s,size", "sz0", airTypeOther, 1, -1, &scale, NULL, "For each axis, information about how many samples in output:\n " "\b\bo \"=\": leave this axis completely untouched: no " "resampling whatsoever\n " "\b\bo \"x\": multiply the number of input samples by " ", and round to the nearest integer, to get the number " "of output samples. Use \"x1\" to resample the axis but leave " "the number of samples unchanged\n " "\b\bo \"/\": divide number of samples by \n " "\b\bo \"+=\", \"-=\": add to or subtract " " from number input samples to get number output samples\n " "\b\bo \"\": exact number of output samples\n " "\b\bo \"a\": resample this axis to whatever number of samples " "preserves the aspect ratio of other resampled axes. Currently " "needs to be used on all but one of the resampled axes, " "if at all. ", &scaleLen, NULL, &unrrduHestScaleCB); hestOptAdd(&opt, "off,offset", "off0", airTypeDouble, 0, -1, &off, "", "For each axis, an offset or shift to the position (in index " "space) of the lower end of the sampling domain. " "Either -off can be used, or -min and -max " "together, or none of these (so that, by default, the full " "domain of the axis is resampled).", &offLen); hestOptAdd(&opt, "min,minimum", "min0", airTypeDouble, 0, -1, &min, "", "For each axis, the lower end (in index space) of the domain " "of the resampling. Either -off can be used, or -min and -max " "together, or none of these (so that, by default, the full " "domain of the axis is resampled).", &minLen); hestOptAdd(&opt, "max,maximum", "max0", airTypeDouble, 0, -1, &max, "", "For each axis, the upper end (in index space) of the domain " "of the resampling. Either -off can be used, or -min and -max " "together, or none of these, so that (by default), the full " "domain of the axis is resampled.", &maxLen); hestOptAdd(&opt, "k,kernel", "kern", airTypeOther, 1, 1, &unuk, "cubic:0,0.5", "The kernel to use for resampling. " "Kernels logically live in the input index space for upsampling, " "and in the output index space for downsampling. " "Possibilities include:\n " "\b\bo \"box\": nearest neighbor interpolation on upsampling, " "and uniform averaging on downsampling\n " "\b\bo \"cheap\": nearest neighbor interpolation for upsampling, " "and non-blurring sub-sampling (pick subset of input samples) " "on downsampling\n " "\b\bo \"tent\": linear interpolation\n " "\b\bo \"cubic:B,C\": Mitchell/Netravali BC-family of " "cubics:\n " "\t\t\"cubic:1,0\": B-spline; maximal blurring\n " "\t\t\"cubic:0,0.5\": Catmull-Rom; good interpolating kernel\n " "\b\bo \"c4h\": 6-sample-support, C^4 continuous, accurate\n " "\b\bo \"c4hai\": discrete pre-filter to make c4h interpolate\n " "\b\bo \"bspl3\", \"bspl5\", \"bspl7\": cubic (same as cubic:1,0), " "quintic, and 7th order B-spline\n " "\b\bo \"bspl3ai\", \"bspl5ai\", \"bspl7ai\": discrete pre-filters to make " "bspl3, bspl5, bspl7 interpolate\n " "\b\bo \"hann:R\": Hann (cosine bell) windowed sinc, radius R\n " "\b\bo \"black:R\": Blackman windowed sinc, radius R\n " "\b\bo \"gauss:S,C\": Gaussian blurring, with standard deviation " "S and cut-off at C standard deviations\n " "\b\bo \"dgauss:S,C\": Lindeberg's discrete Gaussian.", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&opt, "nrn", NULL, airTypeInt, 0, 0, &norenorm, NULL, "do NOT do per-pass kernel weight renormalization. " "Doing the renormalization is not a performance hit (hence is " "enabled by default), and the renormalization is sometimes " "needed to avoid \"grating\" on non-integral " "down-sampling. Disabling the renormalization is needed for " "correct results with artificially narrow kernels. "); hestOptAdd(&opt, "ne,nonexistent", "behavior", airTypeEnum, 1, 1, &neb, "noop", "When resampling floating-point values, how to handle " "non-existent values within kernel support:\n " "\b\bo \"noop\": do nothing; let them pollute result\n " "\b\bo \"renorm\": ignore them and renormalize weights of " "existent values\n " "\b\bo \"wght\": ignore them and simply use weights of " "existent values", NULL, nrrdResampleNonExistent); hestOptAdd(&opt, "b,boundary", "behavior", airTypeEnum, 1, 1, &bb, "bleed", "How to handle samples beyond the input bounds:\n " "\b\bo \"pad\": use some specified value\n " "\b\bo \"bleed\": extend border values outward\n " "\b\bo \"mirror\": repeated reflections\n " "\b\bo \"wrap\": wrap-around to other side", NULL, nrrdBoundary); hestOptAdd(&opt, "v,value", "value", airTypeDouble, 1, 1, &padVal, "0.0", "for \"pad\" boundary behavior, pad with this value"); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &type, "default", "type to save OUTPUT as. By default (not using this option), " "the output type is the same as the input type", NULL, NULL, &unrrduHestMaybeTypeCB); hestOptAdd(&opt, "cheap", NULL, airTypeInt, 0, 0, &(info->cheap), NULL, "[DEPRECATED: the \"-k cheap\" option is the new (and more " "reliable) way to access this functionality. \"-cheap\" is " "only here for legacy use in combination with \"-old\".]\n " "When downsampling (reducing number of samples), don't " "try to do correct filtering by scaling kernel to match " "new (stretched) index space; keep it in old index space. " "When used in conjunction with \"-k box\", this can implement " "subsampling which chooses every Nth value. "); hestOptAdd(&opt, "c,center", "center", airTypeEnum, 1, 1, &defaultCenter, (nrrdCenterCell == nrrdDefaultCenter ? "cell" : "node"), "(not available with \"-old\") " "default centering of axes when input nrrd " "axes don't have a known centering: \"cell\" or \"node\" ", NULL, nrrdCenter); hestOptAdd(&opt, "co,center-override", NULL, airTypeInt, 0, 0, &overrideCenter, NULL, "(not available with \"-old\") " "centering info specified via \"-c\" should *over-ride* " "known centering, rather than simply be used when centering " "is unknown."); hestOptAdd(&opt, "verbose", "v", airTypeInt, 1, 1, &verbose, "0", "(not available with \"-old\") verbosity level"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_resampleInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (scaleLen != nin->dim) { fprintf(stderr, "%s: # sampling sizes %d != input nrrd dimension %d\n", me, scaleLen, nin->dim); airMopError(mop); return 1; } if (!older) { if (offLen >= 1) { /* seems to want to set off[] */ if (offLen != scaleLen) { fprintf(stderr, "%s: offLen %u != scaleLen %u\n", me, offLen, scaleLen); airMopError(mop); return 1; } for (ai=0; ai= 1 && AIR_EXISTS(min[0])) { /* HEY copy and paste */ /* seems to want to set min[] */ if (minLen != scaleLen) { fprintf(stderr, "%s: minLen %u != scaleLen %u\n", me, minLen, scaleLen); airMopError(mop); return 1; } for (ai=0; ai= 1 && AIR_EXISTS(max[0])) { /* HEY copy and paste */ /* seems to want to set max[] */ if (maxLen != scaleLen) { fprintf(stderr, "%s: maxLen %u != scaleLen %u\n", me, maxLen, scaleLen); airMopError(mop); return 1; } for (ai=0; aidim; ai++) { int dowhat = AIR_CAST(int, scale[0 + 2*ai]); if (!(unrrduScaleNothing == dowhat)) { if (unrrduScaleAspectRatio == dowhat) { aspRatNum++; } else { nonAspRatNum++; } } } if (aspRatNum) { if (1 != nonAspRatNum) { fprintf(stderr, "%s: sorry, aspect-ratio-preserving " "resampling must currently be used on all but one " "(not %u) resampled axis, if any\n", me, nonAspRatNum); airMopError(mop); return 1; } } rsmc = nrrdResampleContextNew(); rsmc->verbose = verbose; airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways); E = AIR_FALSE; if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, defaultCenter); if (!E) E |= nrrdResampleInputSet(rsmc, nin); for (ai=0; aidim; ai++) { int dowhat = AIR_CAST(int, scale[0 + 2*ai]); switch(dowhat) { case unrrduScaleNothing: /* no resampling */ if (!E) E |= nrrdResampleKernelSet(rsmc, ai, NULL, NULL); break; case unrrduScaleMultiply: case unrrduScaleDivide: case unrrduScaleAdd: case unrrduScaleSubtract: /* scaling of input # samples */ if (defaultCenter && overrideCenter) { if (!E) E |= nrrdResampleOverrideCenterSet(rsmc, ai, defaultCenter); } if (!E) E |= nrrdResampleKernelSet(rsmc, ai, unuk->kernel, unuk->parm); switch(dowhat) { unsigned int incr; char stmp[AIR_STRLEN_SMALL]; case unrrduScaleMultiply: samplesOut = AIR_ROUNDUP(nin->axis[ai].size*scale[1 + 2*ai]); break; case unrrduScaleDivide: samplesOut = AIR_ROUNDUP(nin->axis[ai].size/scale[1 + 2*ai]); break; case unrrduScaleAdd: samplesOut = nin->axis[ai].size + AIR_CAST(unsigned int, scale[1 + 2*ai]); break; case unrrduScaleSubtract: incr = AIR_CAST(unsigned int, scale[1 + 2*ai]); if (nin->axis[ai].size - 1 < incr) { fprintf(stderr, "%s: can't subtract %u from axis size %s\n", me, incr, airSprintSize_t(stmp, nin->axis[ai].size)); airMopError(mop); return 1; } samplesOut = nin->axis[ai].size - incr; break; } aspRatScl = AIR_CAST(double, samplesOut)/nin->axis[ai].size; if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, samplesOut); break; case unrrduScaleExact: /* explicit # of samples */ if (defaultCenter && overrideCenter) { if (!E) E |= nrrdResampleOverrideCenterSet(rsmc, ai, defaultCenter); } if (!E) E |= nrrdResampleKernelSet(rsmc, ai, unuk->kernel, unuk->parm); samplesOut = (size_t)scale[1 + 2*ai]; aspRatScl = AIR_CAST(double, samplesOut)/nin->axis[ai].size; if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, samplesOut); break; case unrrduScaleAspectRatio: /* wants aspect-ratio preserving, but may not know # samples yet */ if (defaultCenter && overrideCenter) { if (!E) E |= nrrdResampleOverrideCenterSet(rsmc, ai, defaultCenter); } if (!E) E |= nrrdResampleKernelSet(rsmc, ai, unuk->kernel, unuk->parm); /* will set samples later, after aspRatScl has been set */ break; default: fprintf(stderr, "%s: sorry, unrecognized unrrduScale value %d\n", me, dowhat); airMopError(mop); return 1; } if (minSet && maxSet) { if (!E) E |= nrrdResampleRangeSet(rsmc, ai, min[ai], max[ai]); } else { if (!E) E |= nrrdResampleRangeFullSet(rsmc, ai); if (offSet) { /* HEY: this is a hack; We're reading out the information from determined by nrrdResampleRangeFullSet, and benefitting from the fact that it set one of the flags that are processed by nrrdResampleExecute() */ rsmc->axis[ai].min += off[ai]; rsmc->axis[ai].max += off[ai]; } } } if (!E && aspRatNum) { if (!AIR_EXISTS(aspRatScl)) { fprintf(stderr, "%s: confusion, should have learned scaling " "of aspect-ratio-preserving resampling by now", me); airMopError(mop); return 1; } for (ai=0; aidim; ai++) { int dowhat = AIR_CAST(int, scale[0 + 2*ai]); if (unrrduScaleAspectRatio == dowhat) { samplesOut = AIR_ROUNDUP(nin->axis[ai].size*aspRatScl); if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, samplesOut); } } } if (!E) E |= nrrdResampleBoundarySet(rsmc, bb); if (!E) E |= nrrdResampleTypeOutSet(rsmc, type); if (!E) E |= nrrdResamplePadValueSet(rsmc, padVal); if (!E) E |= nrrdResampleRenormalizeSet(rsmc, !norenorm); if (!E) E |= nrrdResampleNonExistentSet(rsmc, neb); if (!E) E |= nrrdResampleExecute(rsmc, nout); if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err); airMopError(mop); return 1; } } else { for (ai=0; aidim; ai++) { int dowhat = AIR_CAST(int, scale[0 + 2*ai]); /* this may be over-written below */ info->kernel[ai] = unuk->kernel; switch(dowhat) { case unrrduScaleNothing: /* no resampling */ info->kernel[ai] = NULL; break; case unrrduScaleMultiply: /* scaling of input # samples */ info->samples[ai] = AIR_ROUNDUP(scale[1 + 2*ai]*nin->axis[ai].size); break; case unrrduScaleExact: /* explicit # of samples */ info->samples[ai] = (size_t)scale[1 + 2*ai]; break; default: fprintf(stderr, "%s: sorry, unrecognized unrrduScale value %d\n", me, dowhat); airMopError(mop); return 1; } memcpy(info->parm[ai], unuk->parm, NRRD_KERNEL_PARMS_NUM*sizeof(*unuk->parm)); if (info->kernel[ai] && (!( AIR_EXISTS(nin->axis[ai].min) && AIR_EXISTS(nin->axis[ai].max))) ) { nrrdAxisInfoMinMaxSet(nin, ai, (nin->axis[ai].center ? nin->axis[ai].center : nrrdDefaultCenter)); } info->min[ai] = nin->axis[ai].min; info->max[ai] = nin->axis[ai].max; } info->boundary = bb; info->type = type; info->padValue = padVal; info->renormalize = !norenorm; if (nrrdSpatialResample(nout, nin, info)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err); airMopError(mop); return 1; } } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(resample, INFO); teem-1.11.0~svn6057/src/unrrdu/undos.c0000664000175000017500000002001312165631065017210 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Converts DOS text files to normal, and more" static const char *_unrrdu_undosInfoL = (INFO ". Normally, it converts LF-CR pairs to just CR, or, with the \"-r\" " "option, convert back to DOS, for whatever sick and " "twisted reason you'd have to do that. Can also handle legacy MAC " "text files (only LF). Unlike the simple sed or perl scripts for " "this purpose, this program is careful to be idempotent. Also, this " "makes an effort to not meddle with binary files (on which this may be " "mistakenly invoked). A message is printed to stderr for all the files " "actually modified. Does various things, some more justified than " "others.\n " "* (not actually based on Nrrd)"); #define CR 10 #define LF 13 #define BAD_PERC 5.0 void undosConvert(const char *me, char *name, int reverse, int mac, int quiet, int noAction) { airArray *mop; FILE *fin, *fout; char *data=NULL; airArray *dataArr; unsigned int ci; int car, numBad, willConvert; airPtrPtrUnion appu; mop = airMopNew(); if (!airStrlen(name)) { fprintf(stderr, "%s: empty filename\n", me); airMopError(mop); return; } /* -------------------------------------------------------- */ /* open input file */ fin = airFopen(name, stdin, "rb"); if (!fin) { if (!quiet) { fprintf(stderr, "%s: couldn't open \"%s\" for reading: \"%s\"\n", me, name, strerror(errno)); } airMopError(mop); return; } airMopAdd(mop, fin, (airMopper)airFclose, airMopOnError); /* -------------------------------------------------------- */ /* create buffer */ appu.c = &data; dataArr = airArrayNew(appu.v, NULL, sizeof(char), AIR_STRLEN_HUGE); if (!dataArr) { if (!quiet) { fprintf(stderr, "%s: internal allocation error #1\n", me); } airMopError(mop); return; } airMopAdd(mop, dataArr, (airMopper)airArrayNuke, airMopAlways); /* -------------------------------------------------------- */ /* read input file, testing for binary-ness along the way */ numBad = 0; car = getc(fin); if (EOF == car) { if (!quiet) { fprintf(stderr, "%s: \"%s\" is empty, skipping ...\n", me, name); } airMopError(mop); return; } do { ci = airArrayLenIncr(dataArr, 1); if (!dataArr->data) { if (!quiet) { fprintf(stderr, "%s: internal allocation error #2\n", me); } airMopError(mop); return; } data[ci] = car; numBad += !(isprint(car) || isspace(car)); car = getc(fin); } while (EOF != car && BAD_PERC > 100.0*numBad/dataArr->len); if (EOF != car) { if (!quiet) { fprintf(stderr, "%s: more than %g%% of \"%s\" is non-printing, " "skipping ...\n", me, BAD_PERC, name); } airMopError(mop); return; } fin = airFclose(fin); /* -------------------------------------------------------- */ /* see if we really need to do anything */ willConvert = AIR_FALSE; if (!strcmp("-", name)) { willConvert = AIR_TRUE; } else if (reverse) { for (ci=0; cilen; ci++) { if (mac) { if (CR == data[ci]) { willConvert = AIR_TRUE; break; } } else { if (CR == data[ci] && (ci && LF != data[ci-1])) { willConvert = AIR_TRUE; break; } } } } else { for (ci=0; cilen; ci++) { if (mac) { if (LF == data[ci]) { willConvert = AIR_TRUE; break; } } else { if (LF == data[ci] && (ci+1len && CR == data[ci+1])) { willConvert = AIR_TRUE; break; } } } } if (!willConvert) { /* no, we don't need to do anything; quietly quit */ airMopOkay(mop); return; } else { if (!quiet) { fprintf(stderr, "%s: %s \"%s\" %s %s ... \n", me, noAction ? "would convert" : "converting", name, reverse ? "to" : "from", mac ? "MAC" : "DOS"); } } if (noAction) { /* just joking, we won't actually write anything. (yes, even if input was stdin) */ airMopOkay(mop); return; } /* -------------------------------------------------------- */ /* open output file */ fout = airFopen(name, stdout, "wb"); if (!fout) { if (!quiet) { fprintf(stderr, "%s: couldn't open \"%s\" for writing: \"%s\"\n", me, name, strerror(errno)); } airMopError(mop); return; } airMopAdd(mop, fout, (airMopper)airFclose, airMopOnError); /* -------------------------------------------------------- */ /* write output file */ car = 'a'; if (reverse) { for (ci=0; cilen; ci++) { if ((mac && CR == data[ci]) || (CR == data[ci] && (ci && LF != data[ci-1]))) { car = putc(LF, fout); if (!mac && EOF != car) { car = putc(CR, fout); } } else { car = putc(data[ci], fout); } } } else { for (ci=0; EOF != car && cilen; ci++) { if ((mac && LF == data[ci]) || (LF == data[ci] && (ci+1len && CR == data[ci+1]))) { car = putc(CR, fout); ci += !mac; } else { car = putc(data[ci], fout); } } } if (EOF == car) { if (!quiet) { fprintf(stderr, "%s: ERROR writing \"%s\" possible data loss !!! " "(sorry)\n", me, name); } } fout = airFclose(fout); airMopOkay(mop); return; } int unrrdu_undosMain(int argc, const char **argv, const char *me, hestParm *hparm) { /* these are stock for unrrdu */ hestOpt *opt = NULL; airArray *mop; int pret; char *err; /* these are specific to this command */ char **name; int lenName, ni, reverse, quiet, noAction, mac; hestOptAdd(&opt, "r", NULL, airTypeInt, 0, 0, &reverse, NULL, "convert back to DOS, instead of converting from DOS to normal"); hestOptAdd(&opt, "q", NULL, airTypeInt, 0, 0, &quiet, NULL, "never print anything to stderr, even for errors."); hestOptAdd(&opt, "m", NULL, airTypeInt, 0, 0, &mac, NULL, "deal with legacy MAC text files."); hestOptAdd(&opt, "n", NULL, airTypeInt, 0, 0, &noAction, NULL, "don't actually write converted files, just pretend to. " "This is useful to see which files WOULD be converted. "); hestOptAdd(&opt, NULL, "file", airTypeString, 1, -1, &name, NULL, "all the files to convert. Each file will be over-written " "with its converted contents. Use \"-\" to read from stdin " "and write to stdout", &lenName); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_undosInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); for (ni=0; nidim - nin->dim - 1; /* its not our job to do real error checking ... */ mapAxis = AIR_MIN(mapAxis, nmmap->dim - 1); } else { /* we have to join together multiple nrrds to get the mmap */ nmmap = nrrdNew(); airMopAdd(mop, nmmap, (airMopper)nrrdNuke, airMopAlways); /* assume that mmap component nrrds are all compatible sizes, nrrdJoin will fail if they aren't */ mapAxis = _nmmap[0]->dim - nin->dim; if (nrrdJoin(nmmap, (const Nrrd*const*)_nmmap, _nmmapLen, mapAxis, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble joining mmap:\n%s", me, err); airMopError(mop); return 1; } /* set these if they were given, they'll be NaN otherwise */ nmmap->axis[mapAxis].min = min; nmmap->axis[mapAxis].max = max; } if (!( AIR_EXISTS(nmmap->axis[mapAxis].min) && AIR_EXISTS(nmmap->axis[mapAxis].max) )) { rescale = AIR_TRUE; } if (rescale) { range = nrrdRangeNew(min, max); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); nrrdRangeSafeSet(range, nin, blind8BitRange); } if (nrrdTypeDefault == typeOut) { typeOut = nmmap->type; } if (nrrdApplyMulti1DRegMap(nout, nin, range, nmmap, typeOut, rescale)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble applying map:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(mrmap, INFO); teem-1.11.0~svn6057/src/unrrdu/convert.c0000664000175000017500000000571012165631065017547 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Convert to another type (as if by cast, w/ optional clamp)" static const char *_unrrdu_convertInfoL = (INFO ". By default this does not transform, scale, or intelligently " "quantize values; it just copies them from one type to another, which " "replicates exactly what you'd get in C when you assign from a variable " "of one type to another, or when you cast to a different type. However, " "clamping values to the representable range of the output type is possible. " "with \"-clamp\". " "See also \"unu quantize\"," "\"unu 2op x\", and \"unu 3op clamp\".\n " "* Uses nrrdConvert or nrrdClampConvert"); int unrrdu_convertMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int type, pret, E, doClamp; airArray *mop; OPT_ADD_TYPE(type, "type to convert to", NULL); OPT_ADD_NIN(nin, "input nrrd"); hestOptAdd(&opt, "clamp", NULL, airTypeInt, 0, 0, &doClamp, NULL, "clamp input values to representable range of values of " "output type, to avoid wrap-around problems"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_convertInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (doClamp) { E = nrrdClampConvert(nout, nin, type); } else { E = nrrdConvert(nout, nin, type); } if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error converting nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(convert, INFO); teem-1.11.0~svn6057/src/unrrdu/join.c0000664000175000017500000001026112165631065017023 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Connect slices and/or slabs into a bigger nrrd" static const char *_unrrdu_joinInfoL = (INFO ". Can stich images into volumes, or tile images side " "by side, or attach images onto volumes. If there are many many " "files to name in the \"-i\" option, and using wildcards won't work, " "consider putting the list of " "filenames into a separate text file (e.g. \"slices.txt\"), and then " "name this file as a response file (e.g. \"-i @slices.txt\"). " "This command now allows you to set the same pieces of information that " "previously had to be set with \"unu axinfo\": label, spacing, and min/max. " "These can be use whether the join axis is new (because of \"-incr\") or " "not.\n " "* Uses nrrdJoin"); int unrrdu_joinMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err, *label; Nrrd **nin; Nrrd *nout; int incrDim, pret; unsigned int ninLen, axis; double mm[2], spc; airArray *mop; hparm->respFileEnable = AIR_TRUE; hestOptAdd(&opt, "i,input", "nin0", airTypeOther, 1, -1, &nin, NULL, "everything to be joined together", &ninLen, NULL, nrrdHestNrrd); OPT_ADD_AXIS(axis, "axis to join along"); hestOptAdd(&opt, "incr", NULL, airTypeInt, 0, 0, &incrDim, NULL, "in situations where the join axis is *not* among the existing " "axes of the input nrrds, then this flag signifies that the join " "axis should be *inserted*, and the output dimension should " "be one greater than input dimension. Without this flag, the " "nrrds are joined side-by-side, along an existing axis."); hestOptAdd(&opt, "l,label", "label", airTypeString, 1, 1, &label, "", "label to associate with join axis"); hestOptAdd(&opt, "mm,minmax", "min max", airTypeDouble, 2, 2, mm, "nan nan", "min and max values along join axis"); hestOptAdd(&opt, "sp,spacing", "spc", airTypeDouble, 1, 1, &spc, "nan", "spacing between samples along join axis"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_joinInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdJoin(nout, AIR_CAST(const Nrrd*const*, nin), ninLen, axis, incrDim)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error joining nrrds:\n%s", me, err); airMopError(mop); return 1; } if (strlen(label)) { nout->axis[axis].label = (char *)airFree(nout->axis[axis].label); nout->axis[axis].label = airStrdup(label); } if (AIR_EXISTS(mm[0])) { nout->axis[axis].min = mm[0]; } if (AIR_EXISTS(mm[1])) { nout->axis[axis].max = mm[1]; } if (AIR_EXISTS(spc)) { nout->axis[axis].spacing = spc; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(join, INFO); teem-1.11.0~svn6057/src/unrrdu/flotsam.c0000664000175000017500000003732512165631065017543 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #include const int unrrduPresent = 42; const char * unrrduBiffKey = "unrrdu"; /* number of columns that hest will used */ unsigned int unrrduDefNumColumns = 78; /* ******** unrrduCmdList[] ** ** NULL-terminated array of unrrduCmd pointers, as ordered by UNRRDU_MAP macro */ unrrduCmd * unrrduCmdList[] = { UNRRDU_MAP(UNRRDU_LIST) NULL }; /* ******** unrrduUsageSpecial ** ** prints out a little banner, and a listing of all available commands ** with their one-line descriptions */ void unrrduUsageUnu(const char *me, hestParm *hparm) { char buff[AIR_STRLEN_LARGE], fmt[AIR_STRLEN_LARGE]; unsigned int cmdi, chi, len, maxlen; maxlen = 0; for (cmdi=0; unrrduCmdList[cmdi]; cmdi++) { maxlen = AIR_MAX(maxlen, AIR_UINT(strlen(unrrduCmdList[cmdi]->name))); } sprintf(buff, "--- unu: Utah Nrrd Utilities command-line interface ---"); len = AIR_UINT(strlen(buff)); sprintf(fmt, "%%%us\n", (hparm->columns > len ? hparm->columns-len : 0)/2 + len - 1); fprintf(stdout, fmt, buff); for (cmdi=0; unrrduCmdList[cmdi]; cmdi++) { int nofft; if (unrrduCmdList[cmdi]->hidden) { /* nothing to see here! */ continue; } nofft = !strcmp(unrrduCmdList[cmdi]->name, "fft") && !nrrdFFTWEnabled; len = AIR_UINT(strlen(unrrduCmdList[cmdi]->name)); len += !!nofft; strcpy(buff, ""); for (chi=len; chiname); strcat(buff, " ... "); len = AIR_UINT(strlen(buff)); fprintf(stdout, "%s", buff); if (nofft) { char *infop; /* luckily, still fits within 80 columns */ fprintf(stdout, "Not Enabled: "); infop = AIR_CALLOC(strlen(unrrduCmdList[cmdi]->info) + 2, char); sprintf(infop, "%s)", unrrduCmdList[cmdi]->info); _hestPrintStr(stdout, len, len, hparm->columns, infop, AIR_FALSE); free(infop); } else { _hestPrintStr(stdout, len, len, hparm->columns, unrrduCmdList[cmdi]->info, AIR_FALSE); } } return; } /* ******** unrrduUsage ** ** A generic version of the usage command, which can be used by other ** programs that are leveraging the unrrduCmd infrastructure. ** ** does not use biff */ int unrrduUsage(const char *me, hestParm *hparm, const char *title, unrrduCmd **cmdList) { char buff[AIR_STRLEN_LARGE], fmt[AIR_STRLEN_LARGE]; unsigned int cmdi, chi, len, maxlen; if (!(title && cmdList)) { /* got NULL pointer */ return 1; } maxlen = 0; for (cmdi=0; cmdList[cmdi]; cmdi++) { maxlen = AIR_MAX(maxlen, AIR_UINT(strlen(cmdList[cmdi]->name))); } sprintf(buff, "--- %s ---", title); len = AIR_UINT(strlen(buff)); sprintf(fmt, "%%%us\n", (hparm->columns > len ? hparm->columns-len : 0)/2 + len - 1); fprintf(stdout, fmt, buff); for (cmdi=0; cmdList[cmdi]; cmdi++) { len = AIR_UINT(strlen(cmdList[cmdi]->name)); strcpy(buff, ""); for (chi=len; chiname); strcat(buff, " ... "); len = AIR_UINT(strlen(buff)); fprintf(stdout, "%s", buff); _hestPrintStr(stdout, len, len, hparm->columns, cmdList[cmdi]->info, AIR_FALSE); } return 0; } /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* ******** unrrduHestPosCB ** ** For parsing position along an axis. Can be a simple (long) integer, ** or M to signify last position along axis (#samples-1), or ** M+ or M- to signify some position relative to the end. ** ** It can also be m+ to signify some position relative to some ** "minimum", assuming that a minimum position is being specified ** at the same time as this one. Obviously, there has to be some ** error handling to make sure that no one is trying to define a ** minimum position with respect to itself. And, the ability to ** specify a position as "m+" shouldn't be advertised in situations ** (unu slice) where you only have one position, rather than an interval ** between two positions (unu crop and unu pad). ** ** This information is represented with two integers, pos[0] and pos[1]: ** pos[0] == 0: pos[1] gives the absolute position ** pos[0] == 1: pos[1] gives the position relative to the last index ** pos[0] == -1: pos[1] gives the position relative to a "minimum" position */ int unrrduParsePos(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[]="unrrduParsePos"; long int *pos; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } pos = (long int*)ptr; if (!strcmp("M", str)) { pos[0] = 1; pos[1] = 0; return 0; } if ('M' == str[0]) { if (!( '-' == str[1] || '+' == str[1] )) { sprintf(err, "%s: \'M\' can be followed only by \'+\' or \'-\'", me); return 1; } pos[0] = 1; if (1 != sscanf(str+1, "%ld", &(pos[1]))) { sprintf(err, "%s: can't parse \"%s\" as M+ or M-", me, str); return 1; } return 0; } if ('m' == str[0]) { if ('+' != str[1]) { sprintf(err, "%s: \'m\' can only be followed by \'+\'", me); return 1; } pos[0] = -1; if (1 != sscanf(str+1, "%ld", &(pos[1]))) { sprintf(err, "%s: can't parse \"%s\" as m+", me, str); return 1; } if (pos[1] < 0 ) { sprintf(err, "%s: int in m+ must be non-negative (not %ld)", me, pos[1]); return 1; } return 0; } /* else its just a plain unadorned integer */ pos[0] = 0; if (1 != sscanf(str, "%ld", &(pos[1]))) { sprintf(err, "%s: can't parse \"%s\" as int", me, str); return 1; } return 0; } hestCB unrrduHestPosCB = { 2*sizeof(long int), "position", unrrduParsePos, NULL }; /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* ******** unrrduHestMaybeTypeCB ** ** although nrrdType is an airEnum that hest already knows how ** to parse, we want the ability to have "unknown" be a valid ** parsable value, contrary to how airEnums usually work with hest. ** For instance, we might want to use "unknown" to represent ** "same type as the input, whatever that is". ** ** 18 July 03: with new nrrdTypeDefault, this function becomes ** less of a hack, and more necessary, because the notion of an ** unknown but valid type (as a default type is) falls squarely ** outside the nrrdType airEnum framework. Added a separate test ** for "default", even though currently nrrdTypeUnknown is the same ** value as nrrdTypeDefault. */ int unrrduParseMaybeType(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[]="unrrduParseMaybeType"; int *typeP; /* fprintf(stderr, "!%s: str = \"%s\"\n", me, str); */ if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } typeP = (int*)ptr; if (!strcmp("unknown", str)) { *typeP = nrrdTypeUnknown; } else if (!strcmp("default", str)) { *typeP = nrrdTypeDefault; } else { *typeP = airEnumVal(nrrdType, str); if (nrrdTypeUnknown == *typeP) { sprintf(err, "%s: can't parse \"%s\" as type", me, str); return 1; } } /* fprintf(stderr, "!%s: *typeP = %d\n", me, *typeP); */ return 0; } hestCB unrrduHestMaybeTypeCB = { sizeof(int), "type", unrrduParseMaybeType, NULL }; /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* ******** unrrduHestBitsCB ** ** for parsing an int that can be 8, 16, or 32 */ int unrrduParseBits(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[]="unrrduParseBits"; unsigned int *bitsP; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } bitsP = (unsigned int*)ptr; if (1 != sscanf(str, "%u", bitsP)) { sprintf(err, "%s: can't parse \"%s\" as int", me, str); return 1; } if (!( 8 == *bitsP || 16 == *bitsP || 32 == *bitsP )) { sprintf(err, "%s: bits (%d) not 8, 16, or 32", me, *bitsP); return 1; } return 0; } hestCB unrrduHestBitsCB = { sizeof(int), "quantization bits", unrrduParseBits, NULL }; /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* ******** unrrduParseScale ** ** parse the strings used with "unu resample -s" to indicate ** the new number of samples. ** = : unrrduScaleNothing ** a : unrrduScaleAspectRatio ** x : unrrduScaleMultiply ** x= : unrrduScaleMultiply ** / : unrrduScaleDivide ** /= : unrrduScaleDivide ** += : unrrduScaleAdd ** -= : unrrduScaleSubstract ** : unrrduScaleExact */ int unrrduParseScale(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[]="unrrduParseScale"; double *scale; unsigned int num; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } scale = AIR_CAST(double *, ptr); if (!strcmp("=", str)) { scale[0] = AIR_CAST(double, unrrduScaleNothing); scale[1] = 0.0; } else if (!strcmp("a", str)) { scale[0] = AIR_CAST(double, unrrduScaleAspectRatio); scale[1] = 0.0; } else if (strlen(str) > 2 && ('x' == str[0] || '/' == str[0]) && '=' == str[1]) { if (1 != sscanf(str+2, "%lf", scale+1)) { sprintf(err, "%s: can't parse \"%s\" as x= or /=", me, str); return 1; } scale[0] = AIR_CAST(double, ('x' == str[0] ? unrrduScaleMultiply : unrrduScaleDivide)); } else if (strlen(str) > 1 && ('x' == str[0] || '/' == str[0])) { if (1 != sscanf(str+1, "%lf", scale+1)) { sprintf(err, "%s: can't parse \"%s\" as x or /", me, str); return 1; } scale[0] = AIR_CAST(double, ('x' == str[0] ? unrrduScaleMultiply : unrrduScaleDivide)); } else if (strlen(str) > 2 && ('+' == str[0] || '-' == str[0]) && '=' == str[1]) { if (1 != sscanf(str+2, "%u", &num)) { sprintf(err, "%s: can't parse \"%s\" as += or -=", me, str); return 1; } scale[0] = AIR_CAST(double, ('+' == str[0] ? unrrduScaleAdd : unrrduScaleSubtract)); scale[1] = AIR_CAST(double, num); } else { if (1 != sscanf(str, "%u", &num)) { sprintf(err, "%s: can't parse \"%s\" as uint", me, str); return 1; } scale[0] = AIR_CAST(double, unrrduScaleExact); scale[1] = AIR_CAST(double, num); } return 0; } hestCB unrrduHestScaleCB = { 2*sizeof(double), "sampling specification", unrrduParseScale, NULL }; /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* ******** unrrduHestFileCB ** ** for parsing a filename, which means opening it in "rb" mode and ** getting a FILE *. "-" is interpreted as stdin, which is not ** fclose()ed at the end, unlike all other files. */ void * unrrduMaybeFclose(void *_file) { FILE *file; file = (FILE *)_file; if (stdin != file) { file = airFclose(file); } return NULL; } int unrrduParseFile(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[]="unrrduParseFile"; FILE **fileP; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } fileP = (FILE **)ptr; if (!( *fileP = airFopen(str, stdin, "rb") )) { sprintf(err, "%s: fopen(\"%s\",\"rb\") failed: %s", me, str, strerror(errno)); return 1; } return 0; } hestCB unrrduHestFileCB = { sizeof(FILE *), "filename", unrrduParseFile, unrrduMaybeFclose, }; /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* --------------------------------------------------------- */ /* ******** unrrduHestEncodingCB ** ** for parsing output encoding, including compression flags ** enc[0]: which encoding, from nrrdEncodingType* enum ** enc[1]: for compressions: zlib "level" and bzip2 "blocksize" ** enc[2]: for zlib: strategy, from nrrdZlibStrategy* enum */ int unrrduParseEncoding(void *ptr, char *_str, char err[AIR_STRLEN_HUGE]) { char me[]="unrrduParseEncoding", *str, *opt; int *enc; airArray *mop; if (!(ptr && _str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } enc = (int *)ptr; /* these are the defaults, they may not get over-written */ enc[1] = -1; enc[2] = nrrdZlibStrategyDefault; enc[0] = airEnumVal(nrrdEncodingType, _str); if (nrrdEncodingTypeUnknown != enc[0]) { /* we're done; encoding was simple: "raw" or "gz" */ return 0; } mop = airMopNew(); str = airStrdup(_str); airMopMem(mop, &str, airMopAlways); opt = strchr(str, ':'); if (!opt) { /* couldn't parse string as nrrdEncodingType, but there wasn't a colon */ sprintf(err, "%s: didn't recognize \"%s\" as an encoding", me, str); airMopError(mop); return 1; } else { *opt = '\0'; opt++; enc[0] = airEnumVal(nrrdEncodingType, str); if (nrrdEncodingTypeUnknown == enc[0]) { sprintf(err, "%s: didn't recognize \"%s\" as an encoding", me, str); airMopError(mop); return 1; } if (!nrrdEncodingArray[enc[0]]->isCompression) { sprintf(err, "%s: only compression encodings have parameters", me); airMopError(mop); return 1; } while (*opt) { int opti = AIR_INT(*opt); if (isdigit(opti)) { enc[1] = *opt - '0'; } else if ('d' == tolower(opti)) { enc[2] = nrrdZlibStrategyDefault; } else if ('h' == tolower(opti)) { enc[2] = nrrdZlibStrategyHuffman; } else if ('f' == tolower(opti)) { enc[2] = nrrdZlibStrategyFiltered; } else { sprintf(err, "%s: parameter char \"%c\" not a digit or 'd','h','f'", me, *opt); airMopError(mop); return 1; } opt++; } } airMopOkay(mop); return 0; } hestCB unrrduHestEncodingCB = { 3*sizeof(int), "encoding", unrrduParseEncoding, NULL }; teem-1.11.0~svn6057/src/unrrdu/splice.c0000664000175000017500000000621512165631065017347 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Replace a slice with a different nrrd" static const char *_unrrdu_spliceInfoL = (INFO ". This is functionally the opposite of \"slice\".\n " "* Uses nrrdSplice"); int unrrdu_spliceMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout, *nslice; unsigned int axis; int pret; long int _pos[2]; size_t pos; airArray *mop; OPT_ADD_AXIS(axis, "axis to splice along"); hestOptAdd(&opt, "p,position", "pos", airTypeOther, 1, 1, _pos, NULL, "position to splice at:\n " "\b\bo gives 0-based index\n " "\b\bo M- give index relative " "to the last sample on the axis (M == #samples-1).", NULL, NULL, &unrrduHestPosCB); hestOptAdd(&opt, "s,slice", "nslice", airTypeOther, 1, 1, &(nslice), NULL, "slice nrrd. This is the slice to insert into \"nin\"", NULL, NULL, nrrdHestNrrd); OPT_ADD_NIN(nin, "input nrrd. This is the nrrd into which the slice is " "inserted"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_spliceInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); if (!( axis < nin->dim )) { fprintf(stderr, "%s: axis %u not in range [0,%u]\n", me, axis, nin->dim-1); return 1; } if (_pos[0] == -1) { fprintf(stderr, "%s: m+ specification format meaningless here\n", me); return 1; } pos = _pos[0]*(nin->axis[axis].size-1) + _pos[1]; nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdSplice(nout, nin, nslice, axis, pos)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error splicing nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(splice, INFO); teem-1.11.0~svn6057/src/unrrdu/slice.c0000664000175000017500000001416112165631065017166 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Slice along one or more axes at given positions" static const char *_unrrdu_sliceInfoL = (INFO ". Output nrrd dimension is less than input nrrd " "dimension by the number of slice axes (except when the " "input is or gets down to 1-D). Can slice on all axes " "in order to sample a single value from the array. " "Per-axis information is preserved.\n " "* Uses nrrdSlice (possibly called multiple times)"); int unrrdu_sliceMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; unsigned int *axis; int pret, axi, axisNum, posNum; size_t pos[NRRD_DIM_MAX]; long int *_pos; airArray *mop; hestOptAdd(&opt, "a,axis", "axis", airTypeUInt, 1, -1, &axis, NULL, "single axis or multiple axes to slice along. " "Giving multiple axes here leads to doing multiple slices " "(at the corresponding positions " "given with \"-p\"). Multiple axes should be identified " "in terms of the axis numbering of the original nrrd; as " "the slices are done (in the given ordering) the actual " "slice axis will be different if previous slices were on " "lower-numbered (faster) axes.", &axisNum); hestOptAdd(&opt, "p,position", "pos", airTypeOther, 1, -1, &_pos, NULL, "position(s) to slice at:\n " "\b\bo gives 0-based index\n " "\b\bo M- give index relative " "to the last sample on the axis (M == #samples-1).", &posNum, NULL, &unrrduHestPosCB); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_sliceInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); if (axisNum != posNum) { fprintf(stderr, "%s: # axes %u != # positions %u\n", me, axisNum, posNum); airMopError(mop); return 1; } if (axisNum > NRRD_DIM_MAX) { fprintf(stderr, "%s: got more slice axes %u than max nrrd dimension %u\n", me, axisNum, NRRD_DIM_MAX); airMopError(mop); return 1; } for (axi=0; axi 1) { sprintf(stmp, "[%d]", axi); } else { strcpy(stmp, ""); } if (!( axis[axi] < nin->dim )) { fprintf(stderr, "%s: axis%s %d not in range [0,%d]\n", me, stmp, axis[axi], nin->dim-1); airMopError(mop); return 1; } if (_pos[0 + 2*axi] == -1) { fprintf(stderr, "%s: pos%s m+ spec not meaningful here\n", me, stmp); airMopError(mop); return 1; } pos[axi] = (_pos[0 + 2*axi]*(nin->axis[axis[axi]].size-1) + _pos[1 + 2*axi]); /* printf("%s: [%d] axis = %u, pos = %u\n", me, axi, axis[axi], AIR_CAST(unsigned int, pos[axi])); */ } /* check on possibly adjust slice axes downward */ if (axisNum > 1) { int axj; for (axi=0; axi axis[axi]); } } /* for (axi=0; axi gives 0-based index\n " "\b\bo M, M+, M- give index relative " "to the last sample on the axis (M == #samples-1).", minLen); hestOptAdd(&opt, "s,subset", "nsub", airTypeOther, 1, 1, &(nsub), NULL, "sub-region nrrd. This the data to be inset in \"nin\"", NULL, NULL, nrrdHestNrrd); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_insetInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); if (!( minLen == nin->dim )) { fprintf(stderr, "%s: # min coords (%d) != nrrd dim (%d)\n", me, minLen, nin->dim); airMopError(mop); return 1; } for (ai=0; aidim; ai++) { if (-1 == minOff[0 + 2*ai]) { fprintf(stderr, "%s: can't use m+ specification for axis %u min\n", me, ai); airMopError(mop); return 1; } } for (ai=0; ai<=nin->dim-1; ai++) { min[ai] = minOff[0 + 2*ai]*(nin->axis[ai].size-1) + minOff[1 + 2*ai]; } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdInset(nout, nin, nsub, min)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error insetting nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(inset, INFO); teem-1.11.0~svn6057/src/unrrdu/flip.c0000664000175000017500000000431312165631065017017 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Reverse order of slices along one axis" static const char *_unrrdu_flipInfoL = (INFO ". Special case of \"unu\tshuffle\".\n " "* Uses nrrdFlip"); int unrrdu_flipMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int pret; unsigned int axis; airArray *mop; OPT_ADD_AXIS(axis, "axis to flip along"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_flipInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdFlip(nout, nin, axis)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error flipping nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(flip, INFO); teem-1.11.0~svn6057/src/unrrdu/affine.c0000664000175000017500000001221312165631065017313 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Affine (lerp) mapping on 5 nrrds or constants" static const char *_unrrdu_affineInfoL = (INFO ". All the 5 arguments can be either nrrds or single " "floating-point values. When all args are single values, this " "is subsuming the functionality of the previous stand-alone " "\"affine\" program. " "Use \"-\" for an operand to signify " "a nrrd to be read from stdin (a pipe). Note, however, " "that \"-\" can probably only be used once (reliably).\n " "* Uses nrrdArithAffine or nrrdArithIterAffine"); int unrrdu_affineMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; NrrdIter *minIn, *in, *maxIn, *minOut, *maxOut, *args[5]; Nrrd *nout, *ntmp=NULL; int type, E, pret, clamp; airArray *mop; unsigned int ai, nn; hestOptAdd(&opt, NULL, "minIn", airTypeOther, 1, 1, &minIn, NULL, "Lower end of input value range.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "in", airTypeOther, 1, 1, &in, NULL, "Input value.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "maxIn", airTypeOther, 1, 1, &maxIn, NULL, "Upper end of input value range.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "minOut", airTypeOther, 1, 1, &minOut, NULL, "Lower end of output value range.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "maxOut", airTypeOther, 1, 1, &maxOut, NULL, "Upper end of output value range.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &type, "default", "type to convert all nrrd inputs to, prior to " "doing operation. This also determines output type. " "By default (not using this option), the types of the input " "nrrds are left unchanged.", NULL, NULL, &unrrduHestMaybeTypeCB); hestOptAdd(&opt, "clamp", "bool", airTypeBool, 1, 1, &clamp, "false", "clamp output values to specified output range"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_affineInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); args[0] = minIn; args[1] = in; args[2] = maxIn; args[3] = minOut; args[4] = maxOut; nn = 0; for (ai=0; ai<5; ai++) { nn += !!args[ai]->ownNrrd; } if (nrrdTypeDefault != type) { /* they wanted to convert nrrds to some other type first */ E = 0; for (ai=0; ai<5; ai++) { if (args[ai]->ownNrrd) { if (!E) E |= nrrdConvert(ntmp=nrrdNew(), args[ai]->ownNrrd, type); if (!E) nrrdIterSetOwnNrrd(args[ai], ntmp); } } if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error converting input nrrd(s):\n%s", me, err); airMopError(mop); return 1; } } if (0 == nn) { /* actually, there are no nrrds; we represent the functionality of the previous stand-alone binary "affine" */ double valOut; valOut = AIR_AFFINE(minIn->val, in->val, maxIn->val, minOut->val, maxOut->val); if (clamp) { valOut = AIR_CLAMP(minOut->val, valOut, maxOut->val); } printf("%g\n", valOut); } else { /* we have a nrrd output */ if (1 == nn && in->ownNrrd) { E = nrrdArithAffine(nout, minIn->val, in->ownNrrd, maxIn->val, minOut->val, maxOut->val, clamp); } else { E = nrrdArithIterAffine(nout, minIn, in, maxIn, minOut, maxOut, clamp); } if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing ternary operation:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); } airMopOkay(mop); return 0; } UNRRDU_CMD(affine, INFO); teem-1.11.0~svn6057/src/unrrdu/lut2.c0000664000175000017500000001346012165631065016756 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Map nrrd through a bivariate lookup table" static const char *_unrrdu_lut2InfoL = (INFO " (itself represented as a nrrd). The lookup table " "can be 2D, in which case the output " "has the same dimension as the input, or 3D, in which case " "the output has one more dimension than the input, and each " "pair of values is mapped to a scanline (along axis 0) from the " "lookup table. In any case, axis 0 of the input must have length two.\n " "* Uses nrrdApply2DLut"); int unrrdu_lut2Main(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nlut, *nout, *ntmp[2]; airArray *mop; int typeOut, rescale[2], pret, blind8BitRange; double min[2], max[2]; NrrdRange *range[2]={NULL,NULL}; unsigned int mapAxis, rai; hestOptAdd(&opt, "m,map", "lut", airTypeOther, 1, 1, &nlut, NULL, "lookup table to map input nrrd through", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "r,rescale", "bool bool", airTypeBool, 2, 2, rescale, "false false", "rescale one or both of the input values from the " "input range to the lut domain. The lut domain is either " "explicitly defined by the axis min,max along axis 0 or 1, " "or, it is implicitly defined as zero to the length of that axis " "minus one."); hestOptAdd(&opt, "min,minimum", "min0 min1", airTypeDouble, 2, 2, min, "nan nan", "Low ends of input range. Defaults to lowest values " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\")"); hestOptAdd(&opt, "max,maximum", "max0 max1", airTypeDouble, 2, 2, max, "nan nan", "High end of input range. Defaults to highest values " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\")"); hestOptAdd(&opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange, nrrdStateBlind8BitRange ? "true" : "false", "Whether to know the range of 8-bit data blindly " "(uchar is always [0,255], signed char is [-128,127]). " "Explicitly setting this is useful only with rescaling (\"-r\")"); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &typeOut, "default", "specify the type (\"int\", \"float\", etc.) of the " "output nrrd. " "By default (not using this option), the output type " "is the lut's type.", NULL, NULL, &unrrduHestMaybeTypeCB); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_lut2InfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (!( nin->dim > 1 && 2 == nin->axis[0].size )) { char stmp[AIR_STRLEN_SMALL]; fprintf(stderr, "%s: input nrrd dim must be > 1, and axis[0].size " "must be 2 (not %s)\n", me, airSprintSize_t(stmp, nin->axis[0].size)); airMopError(mop); return 1; } mapAxis = nlut->dim - 2; if (!(0 == mapAxis || 1 == mapAxis)) { fprintf(stderr, "%s: dimension of lut should be 2 or 3, not %d", me, nlut->dim); airMopError(mop); return 1; } /* see comment in rmap.c */ for (rai=0; rai<=1; rai++) { if (!( AIR_EXISTS(nlut->axis[mapAxis + rai].min) && AIR_EXISTS(nlut->axis[mapAxis + rai].max) )) { rescale[rai] = AIR_TRUE; } if (rescale[rai]) { ntmp[rai] = nrrdNew(); airMopAdd(mop, ntmp[rai], AIR_CAST(airMopper, nrrdNuke), airMopAlways); if (nrrdSlice(ntmp[rai], nin, 0, rai)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble slicing input value %u:\n%s", me, rai, err); airMopError(mop); return 1; } range[rai] = nrrdRangeNew(min[rai], max[rai]); airMopAdd(mop, range[rai], (airMopper)nrrdRangeNix, airMopAlways); nrrdRangeSafeSet(range[rai], ntmp[rai], blind8BitRange); } } if (nrrdTypeDefault == typeOut) { typeOut = nlut->type; } if (nrrdApply2DLut(nout, nin, 0, range[0], range[1], nlut, typeOut, rescale[0], rescale[1])) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble applying 2-D LUT:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(lut2, INFO); teem-1.11.0~svn6057/src/unrrdu/shuffle.c0000664000175000017500000001011012165631065017511 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Permute slices along one axis" static const char *_unrrdu_shuffleInfoL = (INFO ". Slices along one axis are re-arranged as units " "according to the given permutation (or its inverse). " "The permutation tells which old slice to put at each " "new position. For example, the shuffle " "0->1,\t1->2,\t2->0 would be \"2 0 1\". Obviously, " "if you have to rearrange the many slices of a large " "dataset, you should probably store the permutation " "in a plain text file and use it as a " "\"response file\".\n " "* Uses nrrdShuffle"); int unrrdu_shuffleMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; unsigned int di, axis, permLen, *perm, *iperm, *whichperm; size_t *realperm; int inverse, pret; airArray *mop; /* so that long permutations can be read from file */ hparm->respFileEnable = AIR_TRUE; hestOptAdd(&opt, "p,permute", "slc0 slc1", airTypeUInt, 1, -1, &perm, NULL, "new slice ordering", &permLen); hestOptAdd(&opt, "inv,inverse", NULL, airTypeInt, 0, 0, &inverse, NULL, "use inverse of given permutation"); OPT_ADD_AXIS(axis, "axis to shuffle along"); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_shuffleInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); /* we have to do error checking on axis in order to do error checking on length of permutation */ if (!( axis < nin->dim )) { fprintf(stderr, "%s: axis %d not in valid range [0,%d]\n", me, axis, nin->dim-1); airMopError(mop); return 1; } if (!( permLen == nin->axis[axis].size )) { char stmp[AIR_STRLEN_SMALL]; fprintf(stderr, "%s: permutation length (%u) != axis %d's size (%s)\n", me, permLen, axis, airSprintSize_t(stmp, nin->axis[axis].size)); airMopError(mop); return 1; } if (inverse) { iperm = AIR_CALLOC(permLen, unsigned int); airMopAdd(mop, iperm, airFree, airMopAlways); if (nrrdInvertPerm(iperm, perm, permLen)) { fprintf(stderr, "%s: couldn't compute inverse of given permutation\n", me); airMopError(mop); return 1; } whichperm = iperm; } else { whichperm = perm; } realperm = AIR_CALLOC(permLen, size_t); airMopAdd(mop, realperm, airFree, airMopAlways); for (di=0; diaxis[axis].label = (char *)airFree(nout->axis[axis].label); nout->axis[axis].label = airStrdup(label); } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(axinsert, INFO); teem-1.11.0~svn6057/src/unrrdu/dice.c0000664000175000017500000001420012165631065016765 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Save all slices along one axis into separate files" static const char *_unrrdu_diceInfoL = (INFO ". Calls \"unu slice\" for each position " "along the indicated axis, and saves out a different " "file for each sample along that axis.\n " "* Uses repeated calls to nrrdSlice and nrrdSave"); int unrrdu_diceMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *base, *err, fnout[AIR_STRLEN_MED], /* file name out */ fffname[AIR_STRLEN_MED], /* format for filename */ *ftmpl; /* format template */ Nrrd *nin, *nout; int pret, fit; unsigned int axis, start, pos, top, size, sanity; airArray *mop; OPT_ADD_AXIS(axis, "axis to slice along"); OPT_ADD_NIN(nin, "input nrrd"); hestOptAdd(&opt, "s,start", "start", airTypeUInt, 1, 1, &start, "0", "integer value to start numbering with"); hestOptAdd(&opt, "ff,format", "form", airTypeString, 1, 1, &ftmpl, "", "a printf-style format to use for generating all " "filenames. Use this to override the number of characters " "used to represent the slice position, or the file format " "of the output, e.g. \"-ff %03d.ppm\" for 000.ppm, " "001.ppm, etc. By default (not using this option), slices " "are saved in NRRD format (or PNM or PNG where possible) " "with shortest possible filenames."); /* the fact that we're using unsigned int instead of size_t is its own kind of sanity check */ hestOptAdd(&opt, "l,limit", "max#", airTypeUInt, 1, 1, &sanity, "9999", "a sanity check on how many slice files should be saved " "out, to prevent accidentally dicing the wrong axis " "or the wrong array. Can raise this value if needed."); hestOptAdd(&opt, "o,output", "prefix", airTypeString, 1, 1, &base, NULL, "output filename prefix (excluding info set via \"-ff\"), " "basically to set path of output files (so be sure to end " "with \"/\"."); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_diceInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); if (!( axis < nin->dim )) { fprintf(stderr, "%s: given axis (%u) outside range [0,%u]\n", me, axis, nin->dim-1); airMopError(mop); return 1; } if (nin->axis[axis].size > sanity) { char stmp[AIR_STRLEN_SMALL]; fprintf(stderr, "%s: axis %u size %s > sanity limit %u; " "increase via \"-l\"\n", me, axis, airSprintSize_t(stmp, nin->axis[axis].size), sanity); airMopError(mop); return 1; } size = AIR_UINT(nin->axis[axis].size); /* HEY: this should use nrrdSaveMulti(), and if there's additional smarts here, they should be moved into nrrdSaveMulti() */ if (airStrlen(ftmpl)) { if (!( _nrrdContainsPercentThisAndMore(ftmpl, 'd') || _nrrdContainsPercentThisAndMore(ftmpl, 'u') )) { fprintf(stderr, "%s: given filename format \"%s\" doesn't seem to " "have the converstion specification to print an integer\n", me, ftmpl); airMopError(mop); return 1; } sprintf(fffname, "%%s%s", ftmpl); } else { unsigned int dignum=0, tmps; tmps = top = start + size - 1; do { dignum++; tmps /= 10; } while (tmps); /* sprintf the number of digits into the string that will be used to sprintf the slice number into the filename */ sprintf(fffname, "%%s%%0%uu.nrrd", dignum); } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); for (pos=0; posfitsInto(nout, nrrdEncodingRaw, AIR_FALSE)) { strcpy(fffname + strlen(fffname) - 4, "png"); } else { fit = nrrdFormatPNM->fitsInto(nout, nrrdEncodingRaw, AIR_FALSE); if (2 == fit) { strcpy(fffname + strlen(fffname) - 4, "pgm"); } else if (3 == fit) { strcpy(fffname + strlen(fffname) - 4, "ppm"); } } } sprintf(fnout, fffname, base, pos+start); fprintf(stderr, "%s: %s ...\n", me, fnout); if (nrrdSave(fnout, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error writing nrrd to \"%s\":%s\n", me, fnout, err); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } UNRRDU_CMD(dice, INFO); teem-1.11.0~svn6057/src/unrrdu/permute.c0000664000175000017500000000513412165631065017550 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Permute ordering of axes" static const char *_unrrdu_permuteInfoL = (INFO ". The permutation gives the new ordering of the old " "axes (in 0-based numbering). For example, the " "permutation 0->1,\t1->2,\t2->0 would be \"2 0 1\".\n " "* Uses nrrdAxesPermute"); int unrrdu_permuteMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; unsigned int *perm, permLen; int pret; airArray *mop; hestOptAdd(&opt, "p,permute", "ax0 ax1", airTypeUInt, 1, -1, &perm, NULL, "new axis ordering", &permLen); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_permuteInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (!( permLen == nin->dim )) { fprintf(stderr,"%s: # axes in permutation (%u) != nrrd dim (%d)\n", me, permLen, nin->dim); airMopError(mop); return 1; } if (nrrdAxesPermute(nout, nin, perm)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error permuting nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(permute, INFO); teem-1.11.0~svn6057/src/unrrdu/dist.c0000664000175000017500000000767512165631065017046 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Euclidean distance transform" static const char *_unrrdu_distInfoL = (INFO ". Based on \"Distance Transforms of Sampled Functions\" by " "Pedro F. Felzenszwalb and Daniel P. Huttenlocher, " "Cornell Computing and Information Science TR2004-1963. " "This function first thresholds at the specified value and then " "does the distance transform of the resulting binary image. " "The signed distance (negative values inside object) is also available. " "Distances between non-isotropic samples are handled correctly.\n " "* Uses nrrdDistanceL2 or nrrdDistanceL2Signed"); int unrrdu_distMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int pret; int E, typeOut, invert, sign; double thresh, bias; airArray *mop; hestOptAdd(&opt, "th,thresh", "val", airTypeDouble, 1, 1, &thresh, NULL, "threshold value to separate inside from outside"); hestOptAdd(&opt, "b,bias", "val", airTypeDouble, 1, 1, &bias, "0.0", "if non-zero, bias the distance transform by this amount " "times the difference in value from the threshold"); hestOptAdd(&opt, "t,type", "type", airTypeEnum, 1, 1, &typeOut, "float", "type to save output in", NULL, nrrdType); hestOptAdd(&opt, "sgn", NULL, airTypeInt, 0, 0, &sign, NULL, "also compute signed (negative) distances inside objects, " "instead of leaving them as zero"); hestOptAdd(&opt, "inv", NULL, airTypeInt, 0, 0, &invert, NULL, "values *below* threshold are considered interior to object. " "By default (not using this option), values above threshold " "are considered interior. "); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_distInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (bias && sign) { fprintf(stderr, "%s: sorry, signed and biased transform not " "yet implemented\n", me); airMopError(mop); return 1; } if (sign) { E = nrrdDistanceL2Signed(nout, nin, typeOut, NULL, thresh, !invert); } else { if (bias) { E = nrrdDistanceL2Biased(nout, nin, typeOut, NULL, thresh, bias, !invert); } else { E = nrrdDistanceL2(nout, nin, typeOut, NULL, thresh, !invert); } } if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing distance transform:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(dist, INFO); teem-1.11.0~svn6057/src/unrrdu/project.c0000664000175000017500000000604612165631065017540 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Collapse scanlines to scalars along some axis" static const char *_unrrdu_projectInfoL = (INFO ". The scanline is reduced to a single scalar by " "\"measuring\" all the values in the scanline " "with some measure. The output nrrd has dimension " "one less than input (except when the input is itself 1-D); " "the output type depends on " "the measure in a non-trivial way, or it can be set explicitly " "with the \"-t\" option.\n " "* Uses nrrdProject"); int unrrdu_projectMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; unsigned int axis; int measr, pret, type; airArray *mop; OPT_ADD_AXIS(axis, "axis to project along"); hestOptAdd(&opt, "m,measure", "measr", airTypeEnum, 1, 1, &measr, NULL, "How to \"measure\" a scanline, by summarizing all its values " "with a single scalar. " NRRD_MEASURE_DESC, NULL, nrrdMeasure); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &type, "default", "type to use for output. By default (not using this option), " "the output type is determined auto-magically", NULL, NULL, &unrrduHestMaybeTypeCB); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_projectInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdProject(nout, nin, axis, measr, type)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error projecting nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(project, INFO); teem-1.11.0~svn6057/src/unrrdu/untile.c0000664000175000017500000000627412165631065017375 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Undo \"unu tile\": merge slow parts of two axis splits" static const char *_unrrdu_untileInfoL = (INFO ". Untiling an array means spliting two axes, permuting the slow parts " "of those axes to be adjecent in the axis ordering, and then merging " "them. This increases the dimension by one. Undoing a \"unu tile\" " "uses the same \"-s\" argument, and sometimes a different \"-a\" argument, " "as demonstrated here for a 3-D array:\n " "\"unu untile -a 2 0 1\" undoes \"unu tile -a 2 0 1\"\n " "\"unu untile -a 1 0 1\" undoes \"unu tile -a 1 0 2\"\n " "\"unu untile -a 0 0 1\" undoes \"unu tile -a 0 1 2\".\n " "* Uses nrrdUntile2D"); int unrrdu_untileMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; unsigned int axes[3]; int pret; size_t size[2]; airArray *mop; hestOptAdd(&opt, "a,axis", "axMerge ax0 ax1", airTypeUInt, 3, 3, axes, NULL, "the slow parts of axes ax0 and ax1 are merged into a (new) " "axis axMerge, with the axis ax0 part being faster than ax1."); hestOptAdd(&opt, "s,size", "size0 size1", airTypeSize_t, 2, 2, size, NULL, "the slow parts of axes ax0 and ax1 are taken to have size " "size0 and size1, respectively, and axis axMerge will have " "size size0*size1."); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_untileInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdUntile2D(nout, nin, axes[1], axes[2], axes[0], size[0], size[1])) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error tiling nrrd:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(untile, INFO); teem-1.11.0~svn6057/src/unrrdu/TODO.txt0000664000175000017500000000103712026462327017226 0ustar domibeldomibelexamples of usage with the important unu commands make "unu make" into a nrrd function enable the unu 2op and 3op operations that output integers to save the output as, say, signed char, instead of float. This is different than being able to change input type, for operations like "gt". make darn sure that nobody uses printf(), only fprintf() standardize and document use of special single characters for meaning as unrrdu arguments: M : max position along an axis, equal to #samples-1 # : ? - : standard in and standard out any others? teem-1.11.0~svn6057/src/unrrdu/gamma.c0000664000175000017500000000647312165631065017160 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO "Brighten or darken values with a gamma" static const char *_unrrdu_gammaInfoL = (INFO ". Just as in xv, the gamma value here is actually the " "reciprocal of the exponent actually used to transform " "the values.\n " "* Uses nrrdArithGamma"); int unrrdu_gammaMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; double min, max, gamma; airArray *mop; int pret, blind8BitRange; NrrdRange *range; hestOptAdd(&opt, "g,gamma", "gamma", airTypeDouble, 1, 1, &gamma, NULL, "gamma > 1.0 brightens; gamma < 1.0 darkens. " "Negative gammas invert values (like in xv). "); hestOptAdd(&opt, "min,minimum", "value", airTypeDouble, 1, 1, &min, "nan", "Value to implicitly map to 0.0 prior to calling pow(). " "Defaults to lowest value found in input nrrd."); hestOptAdd(&opt, "max,maximum", "value", airTypeDouble, 1, 1, &max, "nan", "Value to implicitly map to 1.0 prior to calling pow(). " "Defaults to highest value found in input nrrd."); hestOptAdd(&opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange, nrrdStateBlind8BitRange ? "true" : "false", "Whether to know the range of 8-bit data blindly " "(uchar is always [0,255], signed char is [-128,127])."); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_gammaInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); range = nrrdRangeNew(min, max); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); nrrdRangeSafeSet(range, nin, blind8BitRange); if (nrrdArithGamma(nout, nin, range, gamma)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing gamma:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(gamma, INFO); teem-1.11.0~svn6057/src/unrrdu/acrop.c0000664000175000017500000001307112165631065017172 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unrrdu.h" #include "privateUnrrdu.h" #define INFO " Automatically crop axes based on given measure" static const char *_unrrdu_acropInfoL = (INFO ". For the axes that are to be cropped, the slices perpendicular " "to that axis are projected down to a scalar with the specified measure. " "The resulting 1D array is analyzed by determining what portions at the " "beginning and end constitute less than some portion of the cumulative " "array sum; these ends are cropped off. The cropping bounds determined " "here can be saved and applied to other arrays via the \"-b\" option.\n " "* Uses nrrdCropAuto"); int unrrdu_acropMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int pret; airArray *mop; size_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX]; unsigned int *axes, axesLen; double frac; int measr, offset; Nrrd *nbounds; char *boundsSave; hestOptAdd(&opt, "a,axes", "ax0", airTypeUInt, 0, -1, &axes, "", "the axes (if any) that should NOT be cropped", &axesLen); hestOptAdd(&opt, "m,measure", "measr", airTypeEnum, 1, 1, &measr, NULL, "How to measure slices (along axes to crop) as scalars, " "to form 1-D array analyzed to determine cropping extent. " "All the measures from \"unu project\" can be used, but " "those that make more sense here include:\n " "\b\bo \"max\", \"mean\", \"median\", " "\"variance\": (self-explanatory)\n " "\b\bo \"stdv\": standard deviation\n " "\b\bo \"cov\": coefficient of variation\n " "\b\bo \"product\", \"sum\": product or sum of all values\n " "\b\bo \"L1\", \"L2\", \"NL2\", \"RMS\", \"Linf\": " "different norms.", NULL, nrrdMeasure); hestOptAdd(&opt, "f,frac", "frac", airTypeDouble, 1, 1, &frac, "0.1", "threshold of cumulative sum of 1-D array at which to crop. " "Needs to be in interval [0.0,0.5)."); hestOptAdd(&opt, "off,offset", "offset", airTypeInt, 1, 1, &offset, "1", "how much to offset the numerically determined cropping; " "positive offsets means expanding the interval of kept " "indices (less cropping)"); hestOptAdd(&opt, "b,bounds", "filename", airTypeString, 1, 1, &boundsSave, "", "if a filename is given here, the automatically determined " "min and max bounds for cropping are saved to this file " "as a 2-D array; first scanline is for -min, second is for -max. " "Unfortunately nothing using the \"m\" and \"M\" semantics " "(above) can currently be saved in the bounds file."); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_acropInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCropAuto(nout, nin, min, max, axes, axesLen, measr, frac, offset)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error cropping nrrd:\n%s", me, err); airMopError(mop); return 1; } if (airStrlen(boundsSave)) { unsigned int axi; airULLong *bounds; nbounds = nrrdNew(); airMopAdd(mop, nbounds, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(nbounds, nrrdTypeULLong, 2, AIR_CAST(airULLong, nin->dim), AIR_CAST(airULLong, 2))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error allocating cropping bounds array:\n%s", me, err); airMopError(mop); return 1; } bounds = AIR_CAST(airULLong*, nbounds->data); for (axi=0; axidim; axi++) { bounds[axi + 0*(nin->dim)] = AIR_CAST(airULLong, min[axi]); bounds[axi + 1*(nin->dim)] = AIR_CAST(airULLong, max[axi]); } if (nrrdSave(boundsSave, nbounds, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error saving cropping bounds array:\n%s", me, err); airMopError(mop); return 1; } } SAVE(out, nout, NULL); airMopOkay(mop); return 0; } UNRRDU_CMD(acrop, INFO); teem-1.11.0~svn6057/src/echo/0000775000175000017500000000000012203513760015311 5ustar domibeldomibelteem-1.11.0~svn6057/src/echo/matter.c0000664000175000017500000000717512165631065016771 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" int echoObjectHasMatter[ECHO_TYPE_NUM] = { 1, /* echoTypeSphere */ 1, /* echoTypeCylinder */ 1, /* echoTypeSuperquad */ 1, /* echoTypeCube */ 1, /* echoTypeTriangle */ 1, /* echoTypeRectangle */ 1, /* echoTypeTriMesh */ 1, /* echoTypeIsosurface */ 0, /* echoTypeAABBox */ 0, /* echoTypeSplit */ 0, /* echoTypeList */ 0, /* echoTypeInstance */ }; void echoColorSet(echoObject *obj, echoCol_t R, echoCol_t G, echoCol_t B, echoCol_t A) { if (obj && echoObjectHasMatter[obj->type]) { ELL_4V_SET(obj->rgba, R, G, B, A); } } void echoMatterPhongSet(echoScene *scene, echoObject *obj, echoCol_t ka, echoCol_t kd, echoCol_t ks, echoCol_t sp) { if (scene && obj && echoObjectHasMatter[obj->type]) { obj->matter = echoMatterPhong; obj->mat[echoMatterPhongKa] = ka; obj->mat[echoMatterPhongKd] = kd; obj->mat[echoMatterPhongKs] = ks; obj->mat[echoMatterPhongSp] = sp; } } void echoMatterGlassSet(echoScene *scene, echoObject *obj, echoCol_t indexr, echoCol_t ka, echoCol_t kd, echoCol_t fuzzy) { if (scene && obj && echoObjectHasMatter[obj->type]) { obj->matter = echoMatterGlass; obj->mat[echoMatterGlassIndex] = indexr; obj->mat[echoMatterGlassKa] = ka; obj->mat[echoMatterGlassKd] = kd; obj->mat[echoMatterGlassFuzzy] = fuzzy; } } void echoMatterMetalSet(echoScene *scene, echoObject *obj, echoCol_t R0, echoCol_t ka, echoCol_t kd, echoCol_t fuzzy) { if (scene && obj && echoObjectHasMatter[obj->type]) { obj->matter = echoMatterMetal; obj->mat[echoMatterMetalR0] = R0; obj->mat[echoMatterMetalKa] = ka; obj->mat[echoMatterMetalKd] = kd; obj->mat[echoMatterMetalFuzzy] = fuzzy; } } void echoMatterLightSet(echoScene *scene, echoObject *obj, echoCol_t power, echoCol_t unit) { if (scene && obj && echoObjectHasMatter[obj->type]) { obj->matter = echoMatterLight; obj->mat[echoMatterLightPower] = power; obj->mat[echoMatterLightUnit] = unit; /* HEY: god forbid we should change the material of the light after this */ _echoSceneLightAdd(scene, obj); } } void echoMatterTextureSet(echoScene *scene, echoObject *obj, Nrrd *ntext) { if (scene && obj && ntext && echoObjectHasMatter[obj->type] && 3 == ntext->dim && nrrdTypeUChar == ntext->type && 4 == ntext->axis[0].size) { obj->ntext = ntext; _echoSceneNrrdAdd(scene, ntext); } } teem-1.11.0~svn6057/src/echo/enumsEcho.c0000664000175000017500000001105112165631065017407 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" /* ------------------------------- jitter --------------------------- */ const char * _echoJitterStr[ECHO_JITTER_NUM+1] = { "(unknown_jitter)", "none", "grid", "jitter", "random" }; const int _echoJitterVal[ECHO_JITTER_NUM+1] = { echoJitterUnknown, echoJitterNone, echoJitterGrid, echoJitterJitter, echoJitterRandom }; const char * _echoJitterDesc[ECHO_JITTER_NUM+1] = { "unknown jitter", "nothing- samples are ALWAYS at center of region", "no jittering- samples are at regular grid vertices", "normal jittering- samples are randomly located within grid cells", "samples are randomly located within region" }; const char * _echoJitterStrEqv[] = { "none", "grid", "regular", "jitter", "random", "" }; const int _echoJitterValEqv[] = { echoJitterNone, echoJitterGrid, echoJitterGrid, echoJitterJitter, echoJitterRandom }; const airEnum _echoJitter = { "jitter", ECHO_JITTER_NUM, _echoJitterStr, _echoJitterVal, _echoJitterDesc, _echoJitterStrEqv, _echoJitterValEqv, AIR_FALSE }; const airEnum *const echoJitter = &_echoJitter; /* ------------------------------- object type --------------------------- */ const char * _echoTypeStr[ECHO_TYPE_NUM+1] = { "(unknown_object)", "sphere", "cylinder", "superquad", "cube", "triangle", "rectangle", "mesh", "isosurface", "AABoundingBox", "split", "list", "instance" }; const int _echoTypeVal[ECHO_TYPE_NUM+1] = { echoTypeUnknown, echoTypeSphere, echoTypeCylinder, echoTypeSuperquad, echoTypeCube, echoTypeTriangle, echoTypeRectangle, echoTypeTriMesh, echoTypeIsosurface, echoTypeAABBox, echoTypeSplit, echoTypeList, echoTypeInstance }; const char * _echoTypeDesc[ECHO_TYPE_NUM+1] = { "unknown_object", "sphere", "axis-aligned cylinder", "superquadric (actually, superellipsoid)", "unit cube, centered at the origin", "triangle", "rectangle", "mesh of triangles", "isosurface of scalar volume", "axis-aligned bounding box", "split", "list", "instance" }; const char * _echoTypeStrEqv[] = { "sphere", "cylinder", "cylind", "rod", "superquad", "squad", "cube", "box", "triangle", "tri", "rectangle", "rect", "mesh", "tri-mesh", "trimesh", "isosurface", "aabbox", "AABoundingBox", "split", "list", "instance", "" }; const int _echoTypeValEqv[] = { echoTypeSphere, echoTypeCylinder, echoTypeCylinder, echoTypeCylinder, echoTypeSuperquad, echoTypeSuperquad, echoTypeCube, echoTypeCube, echoTypeTriangle, echoTypeTriangle, echoTypeRectangle, echoTypeRectangle, echoTypeTriMesh, echoTypeTriMesh, echoTypeTriMesh, echoTypeIsosurface, echoTypeAABBox, echoTypeAABBox, echoTypeSplit, echoTypeList, echoTypeInstance }; const airEnum _echoType = { "object type", ECHO_TYPE_NUM, _echoTypeStr, _echoTypeVal, _echoTypeDesc, _echoTypeStrEqv, _echoTypeValEqv, AIR_FALSE }; const airEnum *const echoType = &_echoType; /* ------------------------------ material types --------------------------- */ const char * _echoMatterStr[ECHO_MATTER_MAX+1] = { "(unknown_matter)", "phong", "glass", "metal", "light" }; const char * _echoMatterDesc[ECHO_MATTER_MAX+1] = { "unknown material", "phong shaded surface", "glass", "metal", "light emitter" }; const airEnum _echoMatter = { "matter", ECHO_MATTER_MAX, _echoMatterStr, NULL, _echoMatterDesc, NULL, NULL, AIR_FALSE }; const airEnum *const echoMatter = &_echoMatter; teem-1.11.0~svn6057/src/echo/set.c0000664000175000017500000001023512165631065016257 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" void echoSphereSet(echoObject *sphere, echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t rad) { if (sphere && echoTypeSphere == sphere->type) { ELL_3V_SET(SPHERE(sphere)->pos, x, y, z); SPHERE(sphere)->rad = rad; } return; } void echoCylinderSet(echoObject *cylind, int axis) { if (cylind && echoTypeCylinder == cylind->type) { CYLINDER(cylind)->axis = axis; } return; } void echoSuperquadSet(echoObject *squad, int axis, echoPos_t A, echoPos_t B) { if (squad && echoTypeSuperquad == squad->type) { SUPERQUAD(squad)->axis = axis; SUPERQUAD(squad)->A = A; SUPERQUAD(squad)->B = B; } return; } void echoRectangleSet(echoObject *rect, echoPos_t ogx, echoPos_t ogy, echoPos_t ogz, echoPos_t e0x, echoPos_t e0y, echoPos_t e0z, echoPos_t e1x, echoPos_t e1y, echoPos_t e1z) { if (rect && echoTypeRectangle == rect->type) { ELL_3V_SET(RECTANGLE(rect)->origin, ogx, ogy, ogz); ELL_3V_SET(RECTANGLE(rect)->edge0, e0x, e0y, e0z); ELL_3V_SET(RECTANGLE(rect)->edge1, e1x, e1y, e1z); } return; } void echoTriangleSet(echoObject *tri, echoPos_t xx0, echoPos_t yy0, echoPos_t zz0, echoPos_t xx1, echoPos_t yy1, echoPos_t zz1, echoPos_t xx2, echoPos_t yy2, echoPos_t zz2) { if (tri && echoTypeTriangle == tri->type) { ELL_3V_SET(TRIANGLE(tri)->vert[0], xx0, yy0, zz0); ELL_3V_SET(TRIANGLE(tri)->vert[1], xx1, yy1, zz1); ELL_3V_SET(TRIANGLE(tri)->vert[2], xx2, yy2, zz2); } return; } /* ******** echoTriMeshSet() ** ** This has to be called any time that the locations of the points are ** changing, even if the connectivity is not changed, because of how ** the bounding box and mean vert position is calculated here. ** ** NB: the TriMesh will directly use the given pos[] and vert[] arrays, ** so don't go freeing them after they've been passed here. */ void echoTriMeshSet(echoObject *trim, int numV, echoPos_t *pos, int numF, int *vert) { int i; if (trim && echoTypeTriMesh == trim->type) { TRIMESH(trim)->numV = numV; TRIMESH(trim)->numF = numF; TRIMESH(trim)->pos = pos; TRIMESH(trim)->vert = vert; ELL_3V_SET(TRIMESH(trim)->min, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX); ELL_3V_SET(TRIMESH(trim)->max, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN); ELL_3V_SET(TRIMESH(trim)->meanvert, 0.0, 0.0, 0.0); for (i=0; imin, TRIMESH(trim)->min, pos + 3*i); ELL_3V_MAX(TRIMESH(trim)->max, TRIMESH(trim)->max, pos + 3*i); ELL_3V_INCR(TRIMESH(trim)->meanvert, pos + 3*i); } ELL_3V_SCALE(TRIMESH(trim)->meanvert, 1.0/numV, TRIMESH(trim)->meanvert); } return; } void echoInstanceSet(echoObject *inst, echoPos_t *M, echoObject *obj) { if (inst && echoTypeInstance == inst->type) { ell_4m_INV(INSTANCE(inst)->Mi, M); ELL_4M_COPY(INSTANCE(inst)->M, M); INSTANCE(inst)->obj = obj; } } teem-1.11.0~svn6057/src/echo/list.c0000664000175000017500000001401012165631065016432 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" void echoListAdd(echoObject *list, echoObject *child) { int idx; if (!( list && child && (echoTypeList == list->type || echoTypeAABBox == list->type) )) return; idx = airArrayLenIncr(LIST(list)->objArr, 1); LIST(list)->obj[idx] = child; return; } int _echoPosCompare(double *A, double *B) { return *A < *B ? -1 : (*A > *B ? 1 : 0); } /* ******** echoListSplit() ** ** returns a echoObjectSplit to point to the same things as pointed ** to by the given echoObjectList */ echoObject * echoListSplit(echoScene *scene, echoObject *list, int axis) { echoPos_t lo[3], hi[3], loest0[3], hiest0[3], loest1[3], hiest1[3]; double *mids; echoObject *o, *split, *list0, *list1; int i, splitIdx, len; if (!( echoTypeList == list->type || echoTypeAABBox == list->type )) { return list; } len = LIST(list)->objArr->len; if (len <= ECHO_LEN_SMALL_ENOUGH) { /* there is nothing or only one object */ return list; } split = echoObjectNew(scene, echoTypeSplit); list0 = echoObjectNew(scene, echoTypeList); list1 = echoObjectNew(scene, echoTypeList); SPLIT(split)->axis = axis; SPLIT(split)->obj0 = list0; SPLIT(split)->obj1 = list1; mids = (double *)malloc(2 * len * sizeof(double)); for (i=0; iobj[i]; echoBoundsGet(lo, hi, o); mids[0 + 2*i] = (lo[axis] + hi[axis])/2; *((unsigned int *)(mids + 1 + 2*i)) = i; } /* overkill, I know, I know */ qsort(mids, len, 2*sizeof(double), (int (*)(const void *, const void *))_echoPosCompare); /* for (i=0; i %g\n", i, mids[0 + 2*i]); } */ splitIdx = len/2; /* printf("splitIdx = %d\n", splitIdx); */ ELL_3V_SET(loest0, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX); ELL_3V_SET(loest1, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX); ELL_3V_SET(hiest0, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN); ELL_3V_SET(hiest1, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN); airArrayLenSet(LIST(list0)->objArr, splitIdx); for (i=0; iobj[*((unsigned int *)(mids + 1 + 2*i))]; LIST(list0)->obj[i] = o; echoBoundsGet(lo, hi, o); /* printf("000 lo = (%g,%g,%g), hi = (%g,%g,%g)\n", lo[0], lo[1], lo[2], hi[0], hi[1], hi[2]); */ ELL_3V_MIN(loest0, loest0, lo); ELL_3V_MAX(hiest0, hiest0, hi); } airArrayLenSet(LIST(list1)->objArr, len-splitIdx); for (i=splitIdx; iobj[*((unsigned int *)(mids + 1 + 2*i))]; LIST(list1)->obj[i-splitIdx] = o; echoBoundsGet(lo, hi, o); /* printf("111 lo = (%g,%g,%g), hi = (%g,%g,%g)\n", lo[0], lo[1], lo[2], hi[0], hi[1], hi[2]); */ ELL_3V_MIN(loest1, loest1, lo); ELL_3V_MAX(hiest1, hiest1, hi); } /* printf("0: loest = (%g,%g,%g); hiest = (%g,%g,%g)\n", loest0[0], loest0[1], loest0[2], hiest0[0], hiest0[1], hiest0[2]); printf("1: loest = (%g,%g,%g); hiest = (%g,%g,%g)\n", loest1[0], loest1[1], loest1[2], hiest1[0], hiest1[1], hiest1[2]); */ ELL_3V_COPY(SPLIT(split)->min0, loest0); ELL_3V_COPY(SPLIT(split)->max0, hiest0); ELL_3V_COPY(SPLIT(split)->min1, loest1); ELL_3V_COPY(SPLIT(split)->max1, hiest1); /* we can't delete the list object here, we just gut it so that there's nothing substantial left of it */ airArrayLenSet(LIST(list)->objArr, 0); mids = (double *)airFree(mids); return split; } echoObject * echoListSplit3(echoScene *scene, echoObject *list, int depth) { echoObject *ret, *tmp0, *tmp1; if (!( echoTypeList == list->type || echoTypeAABBox == list->type )) return NULL; if (!depth) return list; ret = echoListSplit(scene, list, 0); #define DOIT(obj, ax) ((obj) = echoListSplit(scene, (obj), (ax))) #define MORE(obj) echoTypeSplit == (obj)->type if (MORE(ret)) { tmp0 = DOIT(SPLIT(ret)->obj0, 1); if (MORE(tmp0)) { tmp1 = DOIT(SPLIT(tmp0)->obj0, 2); if (MORE(tmp1)) { SPLIT(tmp1)->obj0 = echoListSplit3(scene, SPLIT(tmp1)->obj0, depth-1); SPLIT(tmp1)->obj1 = echoListSplit3(scene, SPLIT(tmp1)->obj1, depth-1); } tmp1 = DOIT(SPLIT(tmp0)->obj1, 2); if (MORE(tmp1)) { SPLIT(tmp1)->obj0 = echoListSplit3(scene, SPLIT(tmp1)->obj0, depth-1); SPLIT(tmp1)->obj1 = echoListSplit3(scene, SPLIT(tmp1)->obj1, depth-1); } } tmp0 = DOIT(SPLIT(ret)->obj1, 1); if (MORE(tmp0)) { tmp1 = DOIT(SPLIT(tmp0)->obj0, 2); if (MORE(tmp1)) { SPLIT(tmp1)->obj0 = echoListSplit3(scene, SPLIT(tmp1)->obj0, depth-1); SPLIT(tmp1)->obj1 = echoListSplit3(scene, SPLIT(tmp1)->obj1, depth-1); } tmp1 = DOIT(SPLIT(tmp0)->obj1, 2); if (MORE(tmp1)) { SPLIT(tmp1)->obj0 = echoListSplit3(scene, SPLIT(tmp1)->obj0, depth-1); SPLIT(tmp1)->obj1 = echoListSplit3(scene, SPLIT(tmp1)->obj1, depth-1); } } } return ret; } teem-1.11.0~svn6057/src/echo/echo.h0000664000175000017500000005020512165631065016410 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ECHO_HAS_BEEN_INCLUDED #define ECHO_HAS_BEEN_INCLUDED /* NOTE: this library has not undergone the changes as other Teem libraries in order to make sure that array lengths and indices are stored in unsigned types */ #include #include #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(echo_EXPORTS) || defined(teem_EXPORTS) # define ECHO_EXPORT extern __declspec(dllexport) # else # define ECHO_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define ECHO_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define ECHO echoBiffKey /* all position and transform information is kept as: ** 1: float ** 0: double */ #if 0 typedef float echoPos_t; # define ECHO_POS_FLOAT 1 #else typedef double echoPos_t; # define ECHO_POS_FLOAT 0 #endif /* all color information is kept as ** 1: float ** 0: double */ #if 1 typedef float echoCol_t; # define echoCol_nt nrrdTypeFloat #else typedef double echoCol_t; # define echoCol_nt nrrdTypeDouble #endif #define ECHO_LIST_OBJECT_INCR 32 #define ECHO_IMG_CHANNELS 5 #define ECHO_EPSILON 0.00005 /* used for adjusting ray positions */ #define ECHO_NEAR0 0.004 /* used for comparing transparency to zero */ #define ECHO_LEN_SMALL_ENOUGH 5 /* to control splitting for split objects */ #define ECHO_THREAD_MAX 512 /* max number of threads */ typedef struct { int jitterType, /* from echoJitter* enum below */ reuseJitter, /* don't recompute jitter offsets per pixel */ permuteJitter, /* properly permute the various jitter arrays */ textureNN, /* use nearest-neighbor for texture lookups (rather than bilinear interpolation) */ numSamples, /* rays per pixel */ imgResU, imgResV, /* horz. and vert. image resolution */ maxRecDepth, /* max recursion depth */ renderLights, /* render the area lights */ renderBoxes, /* faintly render bounding boxes */ seedRand, /* call airSrandMT() (don't if repeatability wanted) */ sqNRI, /* how many iterations of newton-raphson we allow for finding superquadric root (within tolorance sqTol) */ numThreads; /* number of threads to spawn per rendering */ echoPos_t sqTol; /* how close newtwon-raphson must get to zero */ echoCol_t shadow, /* the extent to which shadows are seen: 0: no shadow rays cast >0: shadow rays cast, results weighed by shadow 1: full shadowing */ glassC; /* should really be an additional material parameter: Beer's law attenuation in glass */ float aperture, /* shallowness of field */ timeGamma, /* gamma for values in time image */ boxOpac; /* opacity of bounding boxes with renderBoxes */ echoCol_t maxRecCol[3]; /* color of max recursion depth being hit */ } echoRTParm; struct echoScene_t; typedef struct { int verbose; double time; /* time it took to render image */ Nrrd *nraw; /* copies of arguments to echoRTRender */ limnCamera *cam; struct echoScene_t *scene; echoRTParm *parm; int workIdx; /* next work assignment (such as a scanline) */ airThreadMutex *workMutex; /* mutex around work assignment */ } echoGlobalState; typedef struct { airThread *thread; /* my thread */ echoGlobalState *gstate; int verbose, /* blah blah blah */ threadIdx, /* my thread index */ depth; /* how many recursion levels are we at */ Nrrd *nperm, /* ECHO_JITTABLE_NUM x parm->numSamples array of ints, each column is a (different) random permutation of [0 .. parm->numSamples-1], each row corresponds to the different jittables for a single sample */ *njitt; /* 2 x ECHO_JITTABLE_NUM x parm->numSamples array of echoPos_t's in domain [-1/2,1/2]; like the nperm array, each row is comprised of the jitter vectors (for all possible jittables) to use for 1 sample */ unsigned int *permBuff; /* temp array for creating permutations */ echoPos_t *jitt; /* pointer into njitt, good for current sample */ echoCol_t *chanBuff; /* for storing ray color and other parameters for each of the parm->numSamples rays in current pixel */ airRandMTState *rst; /* random number state */ void *returnPtr; /* for airThreadJoin */ } echoThreadState; /* ******** echoJitter* enum ** ** the different jitter patterns that are supported. This setting is ** global- you can't have different jitter patterns on the lights versus ** the pixels. */ enum { echoJitterUnknown=-1, echoJitterNone, /* 0: N samples all at the square center */ echoJitterGrid, /* 1: N samples exactly on a sqrt(N) x sqrt(N) grid */ echoJitterJitter, /* 2: N jittered samples on a sqrt(N) x sqrt(N) grid */ echoJitterRandom, /* 3: N samples randomly placed in square */ echoJitterLast }; #define ECHO_JITTER_NUM 4 /* ******** echoJittable* enum ** ** the different quantities to which the jitter two-vector may be ** applied. */ enum { echoJittableUnknown=-1, echoJittablePixel, /* 0 */ echoJittableLight, /* 1 */ echoJittableLens, /* 2 */ echoJittableNormalA, /* 3 */ echoJittableNormalB, /* 4 */ echoJittableMotionA, /* 5 */ echoJittableMotionB, /* 6 */ echoJittableLast }; #define ECHO_JITTABLE_NUM 7 /* ******** echoMatter* enum ** ** the different materials that are supported. This setting determines ** the interpretation of the vector of floats/doubles ("mat[]") that ** constitutes material information. All objects have an rgba[] array ** separate from material information. The Light material is currently only ** supported on rectangles. */ enum { echoMatterUnknown=0, echoMatterPhong, /* 1 */ echoMatterGlass, /* 2 */ echoMatterMetal, /* 3 */ echoMatterLight, /* 4 */ echoMatterLast }; #define ECHO_MATTER_MAX 4 enum { echoMatterPhongKa, /* 0 */ echoMatterPhongKd, /* 1 */ echoMatterPhongKs, /* 2 */ echoMatterPhongSp /* 3 */ }; enum { echoMatterGlassIndex, /* 0 */ echoMatterGlassKa, /* 1 */ echoMatterGlassKd, /* 2 */ echoMatterGlassFuzzy /* 3 */ }; enum { echoMatterMetalR0, /* 0 */ echoMatterMetalKa, /* 1 */ echoMatterMetalKd, /* 2 */ echoMatterMetalFuzzy /* 3 */ }; enum { echoMatterLightPower, /* 0 */ echoMatterLightUnit /* 1 : (takes over role of old parm->refDistance) distance to be considered unity when calculating inverse square fall-off of light intensity, or, use 0.0 to mean "this is a directional light" (with no fall-off at all) */ }; #define ECHO_MATTER_PARM_NUM 4 /* ******** echoType* enum ** ** the types of objects that echo supports */ enum { echoTypeUnknown=-1, echoTypeSphere, /* 0 */ echoTypeCylinder, /* 1 */ echoTypeSuperquad, /* 2 */ echoTypeCube, /* 3 */ echoTypeTriangle, /* 4 */ echoTypeRectangle, /* 5 */ echoTypeTriMesh, /* 6: only triangles in the mesh */ echoTypeIsosurface, /* 7 */ echoTypeAABBox, /* 8 */ echoTypeSplit, /* 9 */ echoTypeList, /* 10 */ echoTypeInstance, /* 11 */ echoTypeLast }; #define ECHO_TYPE_NUM 12 /* ******** echoObject (generic) and all other object structs ** ** every starts with ECHO_OBJECT_COMMON, and all the "real" objects ** have a ECHO_OBJECT_MATTER following that */ #define ECHO_OBJECT_COMMON \ signed char type #define ECHO_OBJECT_MATTER \ unsigned char matter; \ echoCol_t rgba[4]; \ echoCol_t mat[ECHO_MATTER_PARM_NUM]; \ Nrrd *ntext typedef struct { ECHO_OBJECT_COMMON; ECHO_OBJECT_MATTER; /* ha! its not actually in every object, but in those cases were we want to access it without knowing object type, then it will be there. */ } echoObject; typedef struct { ECHO_OBJECT_COMMON; ECHO_OBJECT_MATTER; echoPos_t pos[3], rad; } echoSphere; typedef struct { ECHO_OBJECT_COMMON; ECHO_OBJECT_MATTER; int axis; } echoCylinder; typedef struct { ECHO_OBJECT_COMMON; ECHO_OBJECT_MATTER; int axis; echoPos_t A, B; } echoSuperquad; /* edges are unit length, [-0.5, 0.5] on every edge */ typedef struct { ECHO_OBJECT_COMMON; ECHO_OBJECT_MATTER; } echoCube; typedef struct { ECHO_OBJECT_COMMON; ECHO_OBJECT_MATTER; echoPos_t vert[3][3]; /* e0 = vert[1]-vert[0], e1 = vert[2]-vert[0], normal = e0 x e1 */ } echoTriangle; typedef struct { ECHO_OBJECT_COMMON; ECHO_OBJECT_MATTER; echoPos_t origin[3], edge0[3], edge1[3]; } echoRectangle; typedef struct { ECHO_OBJECT_COMMON; ECHO_OBJECT_MATTER; echoPos_t meanvert[3], min[3], max[3]; int numV, numF; echoPos_t *pos; int *vert; } echoTriMesh; typedef struct { ECHO_OBJECT_COMMON; ECHO_OBJECT_MATTER; /* this needs more stuff, perhaps a gageContext */ Nrrd *volume; float value; } echoIsosurface; typedef struct { ECHO_OBJECT_COMMON; echoObject *obj; echoPos_t min[3], max[3]; } echoAABBox; typedef struct { ECHO_OBJECT_COMMON; int axis; /* which axis was split: 0:X, 1:Y, 2:Z */ echoPos_t min0[3], max0[3], min1[3], max1[3]; /* bboxes of two children */ echoObject *obj0, *obj1; /* two splits, or ??? */ } echoSplit; typedef struct { ECHO_OBJECT_COMMON; echoObject **obj; airArray *objArr; } echoList; typedef struct { ECHO_OBJECT_COMMON; echoPos_t Mi[16], M[16]; echoObject *obj; } echoInstance; /* ******** echoScene ** ** this is the central list of all objects in a scene, and all nrrds ** used for textures and isosurface volumes. The scene "owns" all ** the objects it points to, so that nixing it will cause all objects ** and nrrds to be nixed and nuked, respectively. */ typedef struct echoScene_t { echoObject **cat; /* array of ALL objects and all lights */ airArray *catArr; echoObject **rend; /* array of top-level objects to be rendered */ airArray *rendArr; echoObject **light; /* convenience pointers to lights within cat[] */ airArray *lightArr; Nrrd **nrrd; /* nrrds for textures and isosurfaces */ airArray *nrrdArr; Nrrd *envmap; /* 16checker-based diffuse environment map, not touched by echoSceneNix() */ echoCol_t ambi[3], /* color of ambient light */ bkgr[3]; /* color of background */ } echoScene; /* ******** echoRay ** ** all info associated with a ray being intersected against a scene */ typedef struct { echoPos_t from[3], /* ray comes from this point */ dir[3], /* ray goes in this (not normalized) direction */ neer, faar; /* look for intx in this interval */ int shadow; /* this is a shadow ray */ echoCol_t transp; /* for shadow rays, the transparency so far; starts at 1.0, goes down to 0.0 */ } echoRay; /* ******** echoIntx ** ** all info about nature and location of an intersection */ typedef struct { echoObject *obj; /* computed with every intersection */ echoPos_t t, /* computed with every intersection */ u, v; /* sometimes needed for texturing */ echoPos_t norm[3], /* computed with every intersection */ view[3], /* always used with coloring */ refl[3], /* reflection of view across line spanned by normal */ pos[3]; /* always used with coloring (and perhaps texturing) */ int face, /* in intx with cube, which face was hit (used for textures) */ boxhits; /* how many bounding boxes we hit */ } echoIntx; typedef union { echoObject ***obj; Nrrd ***nrd; void **v; } echoPtrPtrUnion; /* enumsEcho.c ------------------------------------------ */ ECHO_EXPORT const airEnum *const echoJitter; ECHO_EXPORT const airEnum *const echoType; ECHO_EXPORT const airEnum *const echoMatter; /* methodsEcho.c --------------------------------------- */ ECHO_EXPORT const int echoPresent; ECHO_EXPORT const char *echoBiffKey; ECHO_EXPORT echoRTParm *echoRTParmNew(void); ECHO_EXPORT echoRTParm *echoRTParmNix(echoRTParm *parm); ECHO_EXPORT echoGlobalState *echoGlobalStateNew(void); ECHO_EXPORT echoGlobalState *echoGlobalStateNix(echoGlobalState *state); ECHO_EXPORT echoThreadState *echoThreadStateNew(void); ECHO_EXPORT echoThreadState *echoThreadStateNix(echoThreadState *state); ECHO_EXPORT echoScene *echoSceneNew(void); ECHO_EXPORT echoScene *echoSceneNix(echoScene *scene); /* objmethods.c --------------------------------------- */ ECHO_EXPORT echoObject *echoObjectNew(echoScene *scene, signed char type); ECHO_EXPORT int echoObjectAdd(echoScene *scene, echoObject *obj); ECHO_EXPORT echoObject *echoObjectNix(echoObject *obj); /* model.c ---------------------------------------- */ ECHO_EXPORT echoObject *echoRoughSphereNew(echoScene *scene, int theRes, int phiRes, echoPos_t *matx); /* bounds.c --------------------------------------- */ ECHO_EXPORT void echoBoundsGet(echoPos_t *lo, echoPos_t *hi, echoObject *obj); /* list.c --------------------------------------- */ ECHO_EXPORT void echoListAdd(echoObject *parent, echoObject *child); ECHO_EXPORT echoObject *echoListSplit(echoScene *scene, echoObject *list, int axis); ECHO_EXPORT echoObject *echoListSplit3(echoScene *scene, echoObject *list, int depth); /* set.c --------------------------------------- */ ECHO_EXPORT void echoSphereSet(echoObject *sphere, echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t rad); ECHO_EXPORT void echoCylinderSet(echoObject *cylind, int axis); ECHO_EXPORT void echoSuperquadSet(echoObject *squad, int axis, echoPos_t A, echoPos_t B); ECHO_EXPORT void echoRectangleSet(echoObject *rect, echoPos_t ogx, echoPos_t ogy, echoPos_t ogz, echoPos_t x0, echoPos_t y0, echoPos_t z0, echoPos_t x1, echoPos_t y1, echoPos_t z1); ECHO_EXPORT void echoTriangleSet(echoObject *tri, echoPos_t x0, echoPos_t y0, echoPos_t z0, echoPos_t x1, echoPos_t y1, echoPos_t z1, echoPos_t x2, echoPos_t y2, echoPos_t z2); ECHO_EXPORT void echoTriMeshSet(echoObject *trim, int numV, echoPos_t *pos, int numF, int *vert); ECHO_EXPORT void echoInstanceSet(echoObject *inst, echoPos_t *M, echoObject *obj); /* matter.c ------------------------------------------ */ ECHO_EXPORT int echoObjectHasMatter[ECHO_TYPE_NUM]; ECHO_EXPORT void echoColorSet(echoObject *obj, echoCol_t R, echoCol_t G, echoCol_t B, echoCol_t A); ECHO_EXPORT void echoMatterPhongSet(echoScene *scene, echoObject *obj, echoCol_t ka, echoCol_t kd, echoCol_t ks, echoCol_t sp); ECHO_EXPORT void echoMatterGlassSet(echoScene *scene, echoObject *obj, echoCol_t index, echoCol_t ka, echoCol_t kd, echoCol_t fuzzy); ECHO_EXPORT void echoMatterMetalSet(echoScene *scene, echoObject *obj, echoCol_t R0, echoCol_t ka, echoCol_t kd, echoCol_t fuzzy); ECHO_EXPORT void echoMatterLightSet(echoScene *scene, echoObject *obj, echoCol_t power, echoCol_t unit); ECHO_EXPORT void echoMatterTextureSet(echoScene *scene, echoObject *obj, Nrrd *ntext); /* lightEcho.c ------------------------------------------- */ ECHO_EXPORT void echoLightPosition(echoPos_t pos[3], echoObject *light, echoThreadState *tstate); ECHO_EXPORT void echoLightColor(echoCol_t rgb[3], echoPos_t Ldist, echoObject *light, echoRTParm *parm, echoThreadState *tstate); ECHO_EXPORT void echoEnvmapLookup(echoCol_t rgb[3], echoPos_t norm[3], Nrrd *envmap); /* color.c ------------------------------------------- */ ECHO_EXPORT void echoTextureLookup(echoCol_t rgba[4], Nrrd *ntext, echoPos_t u, echoPos_t v, echoRTParm *parm); ECHO_EXPORT void echoIntxMaterialColor(echoCol_t rgba[4], echoIntx *intx, echoRTParm *parm); ECHO_EXPORT void echoIntxLightColor(echoCol_t ambi[3], echoCol_t diff[3], echoCol_t spec[3], echoCol_t sp, echoIntx *intx, echoScene *scene, echoRTParm *parm, echoThreadState *tstate); ECHO_EXPORT void echoIntxFuzzify(echoIntx *intx, echoCol_t fuzz, echoThreadState *tstate); /* intx.c ------------------------------------------- */ ECHO_EXPORT int echoRayIntx(echoIntx *intx, echoRay *ray, echoScene *scene, echoRTParm *parm, echoThreadState *tstate); ECHO_EXPORT void echoIntxColor(echoCol_t rgba[4], echoIntx *intx, echoScene *scene, echoRTParm *parm, echoThreadState *tstate); /* renderEcho.c ---------------------------------------- */ ECHO_EXPORT int echoThreadStateInit(int threadIdx, echoThreadState *tstate, echoRTParm *parm, echoGlobalState *gstate); ECHO_EXPORT void echoJitterCompute(echoRTParm *parm, echoThreadState *state); ECHO_EXPORT void echoRayColor(echoCol_t rgba[4], echoRay *ray, echoScene *scene, echoRTParm *parm, echoThreadState *tstate); ECHO_EXPORT void echoChannelAverage(echoCol_t *img, echoRTParm *parm, echoThreadState *tstate); ECHO_EXPORT int echoRTRenderCheck(Nrrd *nraw, limnCamera *cam, echoScene *scene, echoRTParm *parm, echoGlobalState *gstate); ECHO_EXPORT int echoRTRender(Nrrd *nraw, limnCamera *cam, echoScene *scene, echoRTParm *parm, echoGlobalState *gstate); #ifdef __cplusplus } #endif #endif /* ECHO_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/echo/methodsEcho.c0000664000175000017500000001315512165631065017732 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" const int echoPresent = 42; const char * echoBiffKey = "echo"; echoRTParm * echoRTParmNew(void) { echoRTParm *parm; parm = AIR_CALLOC(1, echoRTParm); if (parm) { parm->jitterType = echoJitterNone; parm->reuseJitter = AIR_FALSE; parm->permuteJitter = AIR_TRUE; parm->textureNN = AIR_TRUE; parm->numSamples = 1; parm->imgResU = parm->imgResV = 256; parm->maxRecDepth = 5; parm->renderLights = AIR_TRUE; parm->renderBoxes = AIR_FALSE; parm->seedRand = AIR_TRUE; parm->sqNRI = 15; parm->numThreads = 1; parm->sqTol = 0.0001; parm->aperture = 0.0; /* pinhole camera by default */ parm->timeGamma = 6.0; parm->boxOpac = 0.2f; parm->shadow = 1.0; parm->glassC = 3; ELL_3V_SET(parm->maxRecCol, 1.0, 0.0, 1.0); } return parm; } echoRTParm * echoRTParmNix(echoRTParm *parm) { airFree(parm); return NULL; } echoGlobalState * echoGlobalStateNew(void) { echoGlobalState *state; state = AIR_CALLOC(1, echoGlobalState); if (state) { state->verbose = 0; state->time = 0; state->nraw = NULL; state->cam = NULL; state->scene = NULL; state->parm = NULL; state->workIdx = 0; state->workMutex = NULL; } return state; } echoGlobalState * echoGlobalStateNix(echoGlobalState *state) { airFree(state); /* mutex freed at end of echoRTRender() */ return NULL; } echoThreadState * echoThreadStateNew(void) { echoThreadState *state; state = AIR_CALLOC(1, echoThreadState); if (state) { state->thread = airThreadNew(); state->verbose = 0; state->threadIdx = -1; state->depth = -1; state->njitt = nrrdNew(); state->nperm = nrrdNew(); state->permBuff = NULL; state->jitt = NULL; state->chanBuff = NULL; state->rst = airRandMTStateNew(0); state->returnPtr = NULL; } return state; } echoThreadState * echoThreadStateNix(echoThreadState *state) { if (state) { state->thread = airThreadNix(state->thread); nrrdNuke(state->njitt); nrrdNuke(state->nperm); state->permBuff = AIR_CAST(unsigned int *, airFree(state->permBuff)); state->chanBuff = AIR_CAST(echoCol_t *, airFree(state->chanBuff)); airFree(state); } return NULL; } echoScene * echoSceneNew(void) { echoScene *ret; echoPtrPtrUnion eppu; ret = AIR_CALLOC(1, echoScene); if (ret) { ret->cat = NULL; ret->catArr = airArrayNew((eppu.obj = &(ret->cat), eppu.v), NULL, sizeof(echoObject *), ECHO_LIST_OBJECT_INCR); airArrayPointerCB(ret->catArr, airNull, (void *(*)(void *))echoObjectNix); ret->rend = NULL; ret->rendArr = airArrayNew((eppu.obj = &(ret->rend), eppu.v), NULL, sizeof(echoObject *), ECHO_LIST_OBJECT_INCR); /* no callbacks set, renderable objecs are nixed from catArr */ ret->light = NULL; ret->lightArr = airArrayNew((eppu.obj = &(ret->light), eppu.v), NULL, sizeof(echoObject *), ECHO_LIST_OBJECT_INCR); /* no callbacks set; light objects are nixed from catArr */ ret->nrrd = NULL; ret->nrrdArr = airArrayNew((eppu.nrd = &(ret->nrrd), eppu.v), NULL, sizeof(Nrrd *), ECHO_LIST_OBJECT_INCR); airArrayPointerCB(ret->nrrdArr, airNull, (void *(*)(void *))nrrdNuke); ret->envmap = NULL; ELL_3V_SET(ret->ambi, 1.0, 1.0, 1.0); ELL_3V_SET(ret->bkgr, 0.0, 0.0, 0.0); } return ret; } void _echoSceneLightAdd(echoScene *scene, echoObject *obj) { unsigned int idx; for (idx=0; idxlightArr->len; idx++) { if (obj == scene->light[idx]) { break; } } if (scene->lightArr->len == idx) { idx = airArrayLenIncr(scene->lightArr, 1); scene->light[idx] = obj; } } void _echoSceneNrrdAdd(echoScene *scene, Nrrd *nrrd) { unsigned int idx; for (idx=0; idxnrrdArr->len; idx++) { if (nrrd == scene->nrrd[idx]) { break; } if (scene->nrrdArr->len == idx) { idx = airArrayLenIncr(scene->nrrdArr, 1); scene->nrrd[idx] = nrrd; } } } echoScene * echoSceneNix(echoScene *scene) { if (scene) { airArrayNuke(scene->catArr); airArrayNuke(scene->rendArr); airArrayNuke(scene->lightArr); airArrayNuke(scene->nrrdArr); /* don't touch envmap nrrd */ airFree(scene); } return NULL; } teem-1.11.0~svn6057/src/echo/lightEcho.c0000664000175000017500000000605612165631065017400 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" /* ******* echoLightPosition() ** ** sets "pos" to xyz position for current sample of given light */ void echoLightPosition(echoPos_t pos[3], echoObject *light, echoThreadState *tstate) { char me[]="echoLightPos"; echoPos_t x, y; echoRectangle *rectLight; x = tstate->jitt[0 + 2*echoJittableLight] + 0.5; y = tstate->jitt[1 + 2*echoJittableLight] + 0.5; switch(light->type) { case echoTypeRectangle: rectLight = RECTANGLE(light); ELL_3V_SCALE_ADD3(pos, 1, rectLight->origin, x, rectLight->edge0, y, rectLight->edge1); break; default: fprintf(stderr, "%s: currently only support echoTypeRectangle lights", me); break; } return; } /* ******* echoLightColor() ** ** sets "col" to RGB color for current sample of given light, which ** is at distance Ldist. Knowing distance allows computation of the ** inverse square fall-off of light intensity */ void echoLightColor(echoCol_t rgb[3], echoPos_t Ldist, echoObject *light, echoRTParm *parm, echoThreadState *tstate) { echoCol_t rgba[4], falloff; echoPos_t x, y; x = tstate->jitt[0 + 2*echoJittableLight] + 0.5; y = tstate->jitt[1 + 2*echoJittableLight] + 0.5; if (light->ntext) { echoTextureLookup(rgba, light->ntext, x, y, parm); ELL_3V_COPY(rgb, rgba); } else { ELL_3V_COPY(rgb, light->rgba); } ELL_3V_SCALE(rgb, light->mat[echoMatterLightPower], rgb); if (light->mat[echoMatterLightUnit]) { falloff = AIR_CAST(echoCol_t, light->mat[echoMatterLightUnit]/Ldist); falloff *= falloff; ELL_3V_SCALE(rgb, falloff, rgb); } return; } void echoEnvmapLookup(echoCol_t rgb[3], echoPos_t norm[3], Nrrd *envmap) { int qn; float *data; #if ECHO_POS_FLOAT qn = limnVtoQN_f[limnQN16octa](norm); #else qn = limnVtoQN_d[limnQN16octa](norm); #endif data = (float*)(envmap->data) + 3*qn; ELL_3V_COPY(rgb, data); } teem-1.11.0~svn6057/src/echo/GNUmakefile0000664000175000017500000000364112165631065017375 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := echo #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### $(L).NEED = limn ell nrrd biff air $(L).PUBLIC_HEADERS = echo.h $(L).PRIVATE_HEADERS = privateEcho.h $(L).OBJS = enumsEcho.o methodsEcho.o objmethods.o bounds.o set.o model.o \ matter.o intx.o sqd.o list.o color.o lightEcho.o renderEcho.o $(L).TESTS = test/test test/trend #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/echo/bounds.c0000664000175000017500000001566112165631065016766 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" typedef void (*_echoBoundsGet_t)(echoPos_t lo[3], echoPos_t hi[3], echoObject *obj); extern _echoBoundsGet_t _echoBoundsGet[ECHO_TYPE_NUM]; #define BNDS_TMPL(TYPE, BODY) \ void \ _echo##TYPE##_bounds(echoPos_t lo[3], echoPos_t hi[3], echo##TYPE *obj) { \ int dummy=0; \ \ do { BODY dummy=dummy;} while (0); \ lo[0] -= ECHO_EPSILON; \ lo[1] -= ECHO_EPSILON; \ lo[2] -= ECHO_EPSILON; \ hi[0] += ECHO_EPSILON; \ hi[1] += ECHO_EPSILON; \ hi[2] += ECHO_EPSILON; \ return; \ } BNDS_TMPL(Sphere, lo[0] = obj->pos[0] - obj->rad; lo[1] = obj->pos[1] - obj->rad; lo[2] = obj->pos[2] - obj->rad; hi[0] = obj->pos[0] + obj->rad; hi[1] = obj->pos[1] + obj->rad; hi[2] = obj->pos[2] + obj->rad; ) BNDS_TMPL(Cylinder, AIR_UNUSED(obj); ELL_3V_SET(lo, -1, -1, -1); ELL_3V_SET(hi, 1, 1, 1); ) BNDS_TMPL(Superquad, AIR_UNUSED(obj); ELL_3V_SET(lo, -1, -1, -1); ELL_3V_SET(hi, 1, 1, 1); ) BNDS_TMPL(Cube, AIR_UNUSED(obj); ELL_3V_SET(lo, -1, -1, -1); ELL_3V_SET(hi, 1, 1, 1); ) BNDS_TMPL(Triangle, ELL_3V_COPY(lo, obj->vert[0]); ELL_3V_MIN(lo, lo, obj->vert[1]); ELL_3V_MIN(lo, lo, obj->vert[2]); ELL_3V_COPY(hi, obj->vert[0]); ELL_3V_MAX(hi, hi, obj->vert[1]); ELL_3V_MAX(hi, hi, obj->vert[2]); ) BNDS_TMPL(Rectangle, echoPos_t v[3][3]; ELL_3V_COPY(lo, obj->origin); ELL_3V_ADD2(v[0], lo, obj->edge0); ELL_3V_ADD2(v[1], lo, obj->edge1); ELL_3V_ADD2(v[2], v[0], obj->edge1); ELL_3V_MIN(lo, lo, v[0]); ELL_3V_MIN(lo, lo, v[1]); ELL_3V_MIN(lo, lo, v[2]); ELL_3V_COPY(hi, obj->origin); ELL_3V_MAX(hi, hi, v[0]); ELL_3V_MAX(hi, hi, v[1]); ELL_3V_MAX(hi, hi, v[2]); ) BNDS_TMPL(TriMesh, ELL_3V_COPY(lo, obj->min); ELL_3V_COPY(hi, obj->max); ) BNDS_TMPL(Isosurface, AIR_UNUSED(obj); fprintf(stderr, "_echoIsosurface_bounds: unimplemented!\n"); ) BNDS_TMPL(AABBox, ELL_3V_COPY(lo, obj->min); ELL_3V_COPY(hi, obj->max); ) BNDS_TMPL(List, unsigned int i; echoPos_t l[3]; echoPos_t h[3]; echoObject *o; ELL_3V_SET(lo, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX); ELL_3V_SET(hi, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN); for (i=0; iobjArr->len; i++) { o = obj->obj[i]; _echoBoundsGet[o->type](l, h, o); ELL_3V_MIN(lo, lo, l); ELL_3V_MAX(hi, hi, h); } ) BNDS_TMPL(Split, AIR_UNUSED(obj); fprintf(stderr, "_echoSplit_bounds: unimplemented!\n"); ) BNDS_TMPL(Instance, echoPos_t a[8][4]; echoPos_t b[8][4]; echoPos_t l[3]; echoPos_t h[3]; _echoBoundsGet[obj->obj->type](l, h, obj->obj); ELL_4V_SET(a[0], l[0], l[1], l[2], 1); ELL_4V_SET(a[1], h[0], l[1], l[2], 1); ELL_4V_SET(a[2], l[0], h[1], l[2], 1); ELL_4V_SET(a[3], h[0], h[1], l[2], 1); ELL_4V_SET(a[4], l[0], l[1], h[2], 1); ELL_4V_SET(a[5], h[0], l[1], h[2], 1); ELL_4V_SET(a[6], l[0], h[1], h[2], 1); ELL_4V_SET(a[7], h[0], h[1], h[2], 1); ELL_4MV_MUL(b[0], obj->M, a[0]); ELL_4V_HOMOG(b[0], b[0]); ELL_4MV_MUL(b[1], obj->M, a[1]); ELL_4V_HOMOG(b[1], b[1]); ELL_4MV_MUL(b[2], obj->M, a[2]); ELL_4V_HOMOG(b[2], b[2]); ELL_4MV_MUL(b[3], obj->M, a[3]); ELL_4V_HOMOG(b[3], b[3]); ELL_4MV_MUL(b[4], obj->M, a[4]); ELL_4V_HOMOG(b[4], b[4]); ELL_4MV_MUL(b[5], obj->M, a[5]); ELL_4V_HOMOG(b[5], b[5]); ELL_4MV_MUL(b[6], obj->M, a[6]); ELL_4V_HOMOG(b[6], b[6]); ELL_4MV_MUL(b[7], obj->M, a[7]); ELL_4V_HOMOG(b[7], b[7]); ELL_3V_MIN(lo, b[0], b[1]); ELL_3V_MIN(lo, lo, b[2]); ELL_3V_MIN(lo, lo, b[3]); ELL_3V_MIN(lo, lo, b[4]); ELL_3V_MIN(lo, lo, b[5]); ELL_3V_MIN(lo, lo, b[6]); ELL_3V_MIN(lo, lo, b[7]); ELL_3V_MAX(hi, b[0], b[1]); ELL_3V_MAX(hi, hi, b[2]); ELL_3V_MAX(hi, hi, b[3]); ELL_3V_MAX(hi, hi, b[4]); ELL_3V_MAX(hi, hi, b[5]); ELL_3V_MAX(hi, hi, b[6]); ELL_3V_MAX(hi, hi, b[7]); ) _echoBoundsGet_t _echoBoundsGet[ECHO_TYPE_NUM] = { (_echoBoundsGet_t)_echoSphere_bounds, (_echoBoundsGet_t)_echoCylinder_bounds, (_echoBoundsGet_t)_echoSuperquad_bounds, (_echoBoundsGet_t)_echoCube_bounds, (_echoBoundsGet_t)_echoTriangle_bounds, (_echoBoundsGet_t)_echoRectangle_bounds, (_echoBoundsGet_t)_echoTriMesh_bounds, (_echoBoundsGet_t)_echoIsosurface_bounds, (_echoBoundsGet_t)_echoAABBox_bounds, (_echoBoundsGet_t)_echoSplit_bounds, (_echoBoundsGet_t)_echoList_bounds, (_echoBoundsGet_t)_echoInstance_bounds, }; void echoBoundsGet(echoPos_t *lo, echoPos_t *hi, echoObject *obj) { _echoBoundsGet[obj->type](lo, hi, obj); } teem-1.11.0~svn6057/src/echo/sqd.c0000664000175000017500000003301712165631065016256 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" echoPos_t _echo_SuperquadX_v(echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t A, echoPos_t B) { echoPos_t xxb, yya, zza; xxb = pow(x*x, 1/B); yya = pow(y*y, 1/A); zza = pow(z*z, 1/A); return pow(yya + zza, A/B) + xxb - 1; } echoPos_t _echo_SuperquadY_v(echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t A, echoPos_t B) { echoPos_t xxa, yyb, zza; xxa = pow(x*x, 1/A); yyb = pow(y*y, 1/B); zza = pow(z*z, 1/A); return pow(xxa + zza, A/B) + yyb - 1; } echoPos_t _echo_SuperquadZ_v(echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t A, echoPos_t B) { echoPos_t xxa, yya, zzb; xxa = pow(x*x, 1/A); yya = pow(y*y, 1/A); zzb = pow(z*z, 1/B); return pow(xxa + yya, A/B) + zzb - 1; } /* -------------------------------------------------------- */ echoPos_t _echo_SuperquadX_vg(echoPos_t grad[3], echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t A, echoPos_t B) { echoPos_t R, xxb, yya, zza; xxb = pow(x*x, 1/B); yya = pow(y*y, 1/A); zza = pow(z*z, 1/A); R = pow(yya + zza, (A/B)-1); ELL_3V_SET(grad, 2*xxb/(B*x), 2*R*yya/(B*y), 2*R*zza/(B*z)); return pow(yya + zza, A/B) + xxb - 1; } echoPos_t _echo_SuperquadY_vg(echoPos_t grad[3], echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t A, echoPos_t B) { echoPos_t R, xxa, yyb, zza; xxa = pow(x*x, 1/A); yyb = pow(y*y, 1/B); zza = pow(z*z, 1/A); R = pow(xxa + zza, (A/B)-1); ELL_3V_SET(grad, 2*R*xxa/(B*x), 2*yyb/(B*y), 2*R*zza/(B*z)); return pow(xxa + zza, A/B) + yyb - 1; } echoPos_t _echo_SuperquadZ_vg(echoPos_t grad[3], echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t A, echoPos_t B) { echoPos_t R, xxa, yya, zzb; xxa = pow(x*x, 1/A); yya = pow(y*y, 1/A); zzb = pow(z*z, 1/B); R = pow(xxa + yya, (A/B)-1); ELL_3V_SET(grad, 2*R*xxa/(B*x), 2*R*yya/(B*y), 2*zzb/(B*z)); return pow(xxa + yya, A/B) + zzb - 1; } /* -------------------------------------------------------- */ echoPos_t _echo_SuperquadX_lvg(echoPos_t grad[3], echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t A, echoPos_t B) { echoPos_t R, xxb, yya, zza, larg; echoPos_t ret; xxb = pow(x*x, 1/B); yya = pow(y*y, 1/A); zza = pow(z*z, 1/A); R = pow(yya + zza, 1-(A/B))*xxb; ELL_3V_SET(grad, 2/(B*x*(1 + pow(yya + zza, A/B)/xxb)), 2*yya/(B*y*(yya + zza + R)), 2*zza/(B*z*(yya + zza + R))); larg = pow(yya + zza, A/B) + xxb; ret= larg > 0 ? log(larg) : ECHO_POS_MIN; /* if (!( AIR_EXISTS(grad[0]) && AIR_EXISTS(grad[1]) && AIR_EXISTS(grad[2]) )) { fprintf(stderr, "_echo_SuperquadX_lvg: PANIC\n"); fprintf(stderr, "x = %g, y = %g, z = %g, A = %g, B = %g\n", x, y, z, A, B); fprintf(stderr, "pow(%g * %g = %g, 1/%g = %g) = %g\n", x, x, x*x, B, 1/B, pow(x*x, 1/B)); fprintf(stderr, "xxb = %g, yya = %g, zza = %g\n", xxb, yya, zza); fprintf(stderr, "R: pow(%g + %g = %g, 1-(%g/%g = %g) = %g) = %g*%g = %g\n", yya, zza, yya + zza, A, B, A/B, 1-(A/B), pow(yya + zza, 1-(A/B)), xxb, pow(yya + zza, 1-(A/B))*xxb); fprintf(stderr, "grad[0]: 2/(%g * %g * (1 + pow(%g + %g = %g, %g/%g = %g)/%g = %g)) = %g\n", B, x, yya, zza, yya+zza, A, B, A/B, xxb, pow(yya + zza, A/B)/xxb, grad[0]); fprintf(stderr, "grad[1]: 2*%g/(%g*%g*(%g + %g + %g = %g) = %g) = %g\n", yya, B, y, yya, zza, R, yya + zza + R, B*y*(yya + zza + R), 2*yya/(B*y*(yya + zza + R))); fprintf(stderr, "log(pow(%g + %g = %g, %g) = %g + %g) = %g\n", yya, zza, yya+zza, A/B, pow(yya + zza, A/B), xxb, ret); fprintf(stderr, "grad = %g %g %g\n", grad[0], grad[1], grad[2]); fprintf(stderr, "\n----------\n\n"); } */ return ret; } echoPos_t _echo_SuperquadY_lvg(echoPos_t grad[3], echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t A, echoPos_t B) { echoPos_t R, xxa, yyb, zza, larg; xxa = pow(x*x, 1/A); yyb = pow(y*y, 1/B); zza = pow(z*z, 1/A); R = pow(xxa + zza, 1-(A/B))*yyb; ELL_3V_SET(grad, 2*xxa/(B*x*(xxa + zza + R)), 2/(B*y*(1 + pow(xxa + zza, A/B)/yyb)), 2*zza/(B*z*(xxa + zza + R))); larg = pow(xxa + zza, A/B) + yyb; return larg > 0 ? log(larg) : ECHO_POS_MIN; } echoPos_t _echo_SuperquadZ_lvg(echoPos_t grad[3], echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t A, echoPos_t B) { echoPos_t R, xxa, yya, zzb, larg; echoPos_t ret; xxa = pow(x*x, 1/A); yya = pow(y*y, 1/A); zzb = pow(z*z, 1/B); R = pow(xxa + yya, 1-(A/B))*zzb; ELL_3V_SET(grad, 2*xxa/(B*x*(xxa + yya + R)), 2*yya/(B*y*(xxa + yya + R)), 2/(B*z*(1 + pow(xxa + yya, A/B)/zzb))); larg = pow(xxa + yya, A/B) + zzb; ret = larg > 0 ? log(larg) : ECHO_POS_MIN; /* if (!AIR_EXISTS(ret)) { fprintf(stderr, "_echo_SuperquadZ_lvg: PANIC\n"); fprintf(stderr, "x = %g, y = %g, z = %g, A = %g, B = %g\n", x, y, z, A, B); fprintf(stderr, "pow(%g*%g = %g, %g) = %g\n", x, x, x*x, 1/A, xxa); fprintf(stderr, "pow(%g*%g = %g, %g) = %g\n", y, y, y*y, 1/A, yya); fprintf(stderr, "log(pow(%g, %g) = %g + %g) = %g\n", xxa + yya, A/B, pow(xxa + yya, A/B), zzb, ret); exit(0); } */ return ret; } /* -------------------------------------------------------- */ int _echoRayIntx_Superquad(RAYINTX_ARGS(Superquad)) { char me[]="_echoRayIntx_Superquad"; echoPos_t TT=0, Tmin, Tmax, t0, t1, t2, t3, v1, v2, diff, tol, saveTmin, Vmin, Vmax, VV=0, dV, dVmin, dVmax, tmp, (*v)(echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t), (*vg)(echoPos_t[3], echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t), (*lvg)(echoPos_t[3], echoPos_t, echoPos_t, echoPos_t, echoPos_t, echoPos_t), from[3], grad[3], pos[3]; /* these two used only by macros */ int iter; if (!_echoRayIntx_CubeSolid(&Tmin, &Tmax, -1-2*ECHO_EPSILON, 1+2*ECHO_EPSILON, -1-2*ECHO_EPSILON, 1+2*ECHO_EPSILON, -1-2*ECHO_EPSILON, 1+2*ECHO_EPSILON, ray)) { return AIR_FALSE; } switch(obj->axis) { case 0: v = _echo_SuperquadX_v; vg = _echo_SuperquadX_vg; lvg = _echo_SuperquadX_lvg; break; case 1: v = _echo_SuperquadY_v; vg = _echo_SuperquadY_vg; lvg = _echo_SuperquadY_lvg; break; case 2: default: v = _echo_SuperquadZ_v; vg = _echo_SuperquadZ_vg; lvg = _echo_SuperquadZ_lvg; break; } if (tstate->verbose) { fprintf(stderr, "%s%s: Tmin, Tmax = %g, %g, ax = %d\n", _echoDot(tstate->depth), me, Tmin, Tmax, obj->axis); } #define VAL(TT) \ (ELL_3V_SCALE_ADD2(pos, 1, from, (TT), ray->dir), \ v(pos[0], pos[1], pos[2], obj->A, obj->B)) #define VALGRAD(VV, DV, TT) \ ELL_3V_SCALE_ADD2(pos, 1, from, (TT), ray->dir); \ (VV) = vg(grad, pos[0], pos[1], pos[2], obj->A, obj->B); \ (DV) = ELL_3V_DOT(grad, ray->dir) #define LVALGRAD(VV, DV, TT) \ ELL_3V_SCALE_ADD2(pos, 1, from, (TT), ray->dir); \ (VV) = lvg(grad, pos[0], pos[1], pos[2], obj->A, obj->B); \ (DV) = ELL_3V_DOT(grad, ray->dir) #define RR 0.61803399 #define CC (1.0-RR) #define SHIFT3(a,b,c,d) (a)=(b); (b)=(c); (c)=(d) #define SHIFT2(a,b,c) (a)=(b); (b)=(c) /* testing */ ELL_3V_SCALE_ADD2(from, 1, ray->from, Tmin, ray->dir); saveTmin = Tmin; Tmin = 0; Tmax -= saveTmin; /* ELL_3V_COPY(from, ray->from); saveTmin = 0; */ /* evaluate value and derivatives at Tmin and Tmax */ VALGRAD(Vmin, dVmin, Tmin); VALGRAD(Vmax, dVmax, Tmax); /* if the segment start and end are both positive or both negative, and if the derivatives also don't change sign, there's no root. Also, due to convexity, if values at start and end are negative, then there is no root */ if ( (Vmin*Vmax >= 0 && dVmin*dVmax >= 0) || (Vmin <= 0 && Vmax <= 0) ) { return AIR_FALSE; } if (tstate->verbose) { fprintf(stderr, "%s%s: dVmin = %g, dVmax = %g, Vmin = %g, Vmax = %g\n", _echoDot(tstate->depth), me, dVmin, dVmax, Vmin, Vmax); } /* either the value changed sign, or the derivative changed sign, or both. If, as is common, the derivatives changed sign, but the values didn't (which means they are both positive, due to a test above), we need to limit the interval by minimizing the value until either we see a negative value, or until the minimization converged. Based on Golden Section Search, NR pg.401 */ if (dVmin*dVmax < 0 && Vmin*Vmax >= 0) { t0 = Tmin; t1 = RR*Tmin + CC*Tmax; t2 = CC*Tmin + RR*Tmax; t3 = Tmax; v1 = VAL(t1); v2 = VAL(t2); if (tstate->verbose) { fprintf(stderr, "%s%s: \n" " t0 = % 31.15f\n" " t1 = % 31.15f -> v1 = % 31.15f\n" " t2 = % 31.15f -> v2 = % 31.15f\n" " t3 = % 31.15f\n", _echoDot(tstate->depth), me, t0, t1, v1, t2, v2, t3); } tol = sqrt(ECHO_POS_EPS); while ( (t3-t0 > tol*(t1+t2)) /* still haven't converged */ && (v1 > 0 && v2 > 0) ) { /* v1 and v2 both positive */ diff = v2 - v1; if (v1 < v2) { SHIFT3(t3, t2, t1, CC*t0 + RR*t2); SHIFT2(v2, v1, VAL(t1)); } else { SHIFT3(t0, t1, t2, RR*t1 + CC*t3); SHIFT2(v1, v2, VAL(t2)); } if (tstate->verbose) { fprintf(stderr, "%s%s: %s ---> \n" " t0 = % 31.15f\n" " t1 = % 31.15f -> v1 = % 31.15f\n" " t2 = % 31.15f -> v2 = % 31.15f\n" " t3 = % 31.15f\n", _echoDot(tstate->depth), me, diff > 0 ? "v1 < v2" : "v1 > v2", t0, t1, v1, t2, v2, t3); } } if (v1 > 0 && v2 > 0) { /* the minimization stopped, yet both v1 and v2 are still positive, so there's no way we can have a root */ if (tstate->verbose) { fprintf(stderr, "%s%s: minimization found no root\n", _echoDot(tstate->depth), me); } return AIR_FALSE; } /* else either v1 or v2 <= 0, so there is a root (actually two). By construction, f(t0) is positive, so we can now bracket the root between t0 and t1 or t2, whichever one is associated with a smaller value */ Tmin = t0; /* HEY: shouldn't I just be using whichever one is closer? */ Tmax = v1 < v2 ? t1 : t2; Vmin = VAL(Tmin); Vmax = VAL(Tmax); } /* the value isn't necessarily monotonic between Tmin and Tmax, but we know that there is only one root. Find it with newton-raphson, using the log of function, both for values and for derivatives */ iter = 0; TT = (Tmin + Tmax)/2; LVALGRAD(VV, dV, TT); while (iter < parm->sqNRI && AIR_ABS(VV) > parm->sqTol && AIR_EXISTS(VV) && AIR_EXISTS(dV)) { if (tstate->verbose) { fprintf(stderr, "%s%s: iter = %d: TT = %g, VV = %g, dV = %g\n", _echoDot(tstate->depth), me, iter, TT, VV, dV); } TT -= VV/dV; if (!AIR_IN_OP(Tmin, TT, Tmax)) { /* newton-raphson sent us flying out of bounds; find a tighter [Tmin,Tmax] bracket with bisection and try again */ TT = (Tmin + Tmax)/2; VV = VAL(TT); if (Vmin*VV < 0) { Tmax = TT; Vmax = VV; } else { Tmin = TT; Vmin = VV; } TT = (Tmin + Tmax)/2; } LVALGRAD(VV, dV, TT); iter++; } if (!( AIR_EXISTS(VV) && AIR_EXISTS(dV) )) { /* we bailed out of the loop above because we got screwed by numerical errors --> pretend that there was no intersection, and HEY this will have to be debugged later */ if (tstate->verbose) { fprintf(stderr, "%s%s: SORRY, numerical problems!\n", _echoDot(tstate->depth), me); } return AIR_FALSE; } /* else we succeeded in finding the intersection */ intx->t = TT + saveTmin; VALGRAD(VV, dV, TT); /* puts gradient into grad */ ELL_3V_NORM(intx->norm, grad, tmp); intx->obj = OBJECT(obj); /* set in intx: yes: t, norm no: u, v */ return AIR_TRUE; } teem-1.11.0~svn6057/src/echo/sources.cmake0000664000175000017500000000051511113047450017774 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(ECHO_SOURCES bounds.c color.c echo.h enumsEcho.c intx.c lightEcho.c list.c matter.c methodsEcho.c model.c objmethods.c privateEcho.h renderEcho.c set.c sqd.c ) ADD_TEEM_LIBRARY(echo ${ECHO_SOURCES}) teem-1.11.0~svn6057/src/echo/model.c0000664000175000017500000000615312165631065016570 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" void _echoPosSet(echoPos_t *p3, echoPos_t *matx, echoPos_t *p4) { echoPos_t a[4], b[4]; if (matx) { ELL_4V_SET(a, p4[0], p4[1], p4[2], 1); ELL_4MV_MUL(b, matx, a); ELL_34V_HOMOG(p3, b); } else { ELL_3V_COPY(p3, p4); } } echoObject * echoRoughSphereNew(echoScene *scene, int theRes, int phiRes, echoPos_t *matx) { echoObject *trim; echoPos_t *_pos, *pos, tmp[3]; int *_vert, *vert, thidx, phidx, n; echoPos_t th, ph; trim = echoObjectNew(scene, echoTypeTriMesh); TRIMESH(trim)->numV = 2 + (phiRes-1)*theRes; TRIMESH(trim)->numF = (2 + 2*(phiRes-2))*theRes; _pos = pos = (echoPos_t *)calloc(3*TRIMESH(trim)->numV, sizeof(echoPos_t)); _vert = vert = (int *)calloc(3*TRIMESH(trim)->numF, sizeof(int)); ELL_3V_SET(tmp, 0, 0, 1); _echoPosSet(pos, matx, tmp); pos += 3; for (phidx=1; phidxnumV-1, 1+(phiRes-2)*theRes+n); vert += 3; } echoTriMeshSet(trim, TRIMESH(trim)->numV, _pos, TRIMESH(trim)->numF, _vert); return(trim); } teem-1.11.0~svn6057/src/echo/objmethods.c0000664000175000017500000001562112165631065017626 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" #define NEW_TMPL(TYPE, BODY) \ echo##TYPE * \ _echo##TYPE##_new(void) { \ echo##TYPE *obj; \ \ obj = (echo##TYPE *)calloc(1, sizeof(echo##TYPE)); \ obj->type = echoType##TYPE; \ do { BODY } while (0); \ return obj; \ } #define NIX_TMPL(TYPE, BODY) \ echo##TYPE * \ _echo##TYPE##_nix(echo##TYPE *obj) { \ int dummy=0; \ \ if (obj) { \ do { BODY dummy=dummy;} while (0); \ airFree(obj); \ } \ return NULL; \ } void _echoMatterInit(echoObject *obj) { obj->matter = echoMatterUnknown; ELL_4V_SET(obj->rgba, 0, 0, 0, 0); memset(obj->mat, 0,ECHO_MATTER_PARM_NUM*sizeof(echoCol_t)); obj->ntext = NULL; } NEW_TMPL(Sphere, _echoMatterInit(OBJECT(obj)); ELL_3V_SET(obj->pos, 0, 0, 0); obj->rad = 1.0; ) NEW_TMPL(Cylinder, _echoMatterInit(OBJECT(obj)); obj->axis = 2; ) NEW_TMPL(Superquad, _echoMatterInit(OBJECT(obj)); obj->axis = 2; obj->A = obj->B = 1; ) NEW_TMPL(Cube, _echoMatterInit(OBJECT(obj)); ) NEW_TMPL(Triangle, _echoMatterInit(OBJECT(obj)); ELL_3V_SET(obj->vert[0], 0, 0, 0); ELL_3V_SET(obj->vert[1], 0, 0, 0); ELL_3V_SET(obj->vert[2], 0, 0, 0); ) NEW_TMPL(Rectangle, _echoMatterInit(OBJECT(obj)); ELL_3V_SET(obj->origin, 0, 0, 0); ELL_3V_SET(obj->edge0, 0, 0, 0); ELL_3V_SET(obj->edge1, 0, 0, 0); ) NEW_TMPL(TriMesh, _echoMatterInit(OBJECT(obj)); ELL_3V_SET(obj->meanvert, 0, 0, 0); ELL_3V_SET(obj->min, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX); ELL_3V_SET(obj->max, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN); obj->numV = obj->numF = 0; obj->pos = NULL; obj->vert = NULL; ) NIX_TMPL(TriMesh, obj->pos = (echoPos_t *)airFree(obj->pos); obj->vert = (int *)airFree(obj->vert); ) NEW_TMPL(Isosurface, _echoMatterInit(OBJECT(obj)); obj->volume = NULL; obj->value = 0.0; /* ??? */ ) NEW_TMPL(AABBox, obj->obj = NULL; ELL_3V_SET(obj->min, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX); ELL_3V_SET(obj->max, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN); ) NEW_TMPL(Split, obj->axis = -1; ELL_3V_SET(obj->min0, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX); ELL_3V_SET(obj->max0, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN); ELL_3V_SET(obj->min1, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX); ELL_3V_SET(obj->max1, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN); obj->obj0 = obj->obj1 = NULL; ) NEW_TMPL(List, echoPtrPtrUnion eppu; obj->obj = NULL; obj->objArr = airArrayNew((eppu.obj = &(obj->obj),eppu.v), NULL, sizeof(echoObject *), ECHO_LIST_OBJECT_INCR); ) NIX_TMPL(List, airArrayNuke(obj->objArr); ) NEW_TMPL(Instance, ELL_4M_IDENTITY_SET(obj->M); ELL_4M_IDENTITY_SET(obj->Mi); obj->obj = NULL; ) echoObject *(* _echoObjectNew[ECHO_TYPE_NUM])(void) = { (echoObject *(*)(void))_echoSphere_new, (echoObject *(*)(void))_echoCylinder_new, (echoObject *(*)(void))_echoSuperquad_new, (echoObject *(*)(void))_echoCube_new, (echoObject *(*)(void))_echoTriangle_new, (echoObject *(*)(void))_echoRectangle_new, (echoObject *(*)(void))_echoTriMesh_new, (echoObject *(*)(void))_echoIsosurface_new, (echoObject *(*)(void))_echoAABBox_new, (echoObject *(*)(void))_echoSplit_new, (echoObject *(*)(void))_echoList_new, (echoObject *(*)(void))_echoInstance_new }; echoObject * echoObjectNew(echoScene *scene, signed char type) { echoObject *ret=NULL; int idx; if (scene && AIR_IN_OP(echoTypeUnknown, type, echoTypeLast)) { ret = _echoObjectNew[type](); idx = airArrayLenIncr(scene->catArr, 1); scene->cat[idx] = ret; } return ret; } int echoObjectAdd(echoScene *scene, echoObject *obj) { int idx; if (scene && obj) { idx = airArrayLenIncr(scene->rendArr, 1); scene->rend[idx] = obj; } return 0; } echoObject *(* _echoObjectNix[ECHO_TYPE_NUM])(echoObject *) = { (echoObject *(*)(echoObject *))airFree, /* echoTypeSphere */ (echoObject *(*)(echoObject *))airFree, /* echoTypeCylinder */ (echoObject *(*)(echoObject *))airFree, /* echoTypeSuperquad */ (echoObject *(*)(echoObject *))airFree, /* echoTypeCube */ (echoObject *(*)(echoObject *))airFree, /* echoTypeTriangle */ (echoObject *(*)(echoObject *))airFree, /* echoTypeRectangle */ (echoObject *(*)(echoObject *))_echoTriMesh_nix, /* echoTypeTriMesh */ (echoObject *(*)(echoObject *))airFree, /* echoTypeIsosurface */ (echoObject *(*)(echoObject *))airFree, /* echoTypeAABBox */ (echoObject *(*)(echoObject *))airFree, /* echoTypeSplit */ (echoObject *(*)(echoObject *))_echoList_nix, /* echoTypeList */ (echoObject *(*)(echoObject *))airFree /* echoTypeInstance */ }; echoObject * echoObjectNix(echoObject *obj) { if (obj) { _echoObjectNix[obj->type](obj); } return NULL; } teem-1.11.0~svn6057/src/echo/intx.c0000664000175000017500000005506212165631065016455 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" /* ** TODO: do whatever possible to minimize the amount of work ** needed for shadow rays */ int _echoVerbose = 0; /* ** ALL of the intersection functions are responsible for setting ** 1) intx->norm to a NORMALIZED direction ** 2) intx->t ** 3) intx->obj ** Specifically, these things must be set for non-shadow rays. Setting ** them for shadow rays is optional. Setting intx->u and intx->v texture ** coords is always optional. ** ** Setting intx->pos and intx->view (normalized) is done by echoRayIntx */ int _echoRayIntx_Noop(RAYINTX_ARGS(Object)) { AIR_UNUSED(intx); AIR_UNUSED(ray); AIR_UNUSED(obj); AIR_UNUSED(parm); AIR_UNUSED(tstate); return 0; } int _echoRayIntx_CubeSurf(echoPos_t *tP, int *axP, int *dirP, echoPos_t xmin, echoPos_t xmax, echoPos_t ymin, echoPos_t ymax, echoPos_t zmin, echoPos_t zmax, echoRay *ray) { echoPos_t txmin, tymin, tzmin, txmax, tymax, tzmax, dx, dy, dz, ox, oy, oz, tmin, tmax; int axmin, axmax, sgn[3]; ELL_3V_GET(dx, dy, dz, ray->dir); ELL_3V_GET(ox, oy, oz, ray->from); if (dx >= 0) { txmin = (xmin - ox)/dx; txmax = (xmax - ox)/dx; sgn[0] = -1; } else { txmin = (xmax - ox)/dx; txmax = (xmin - ox)/dx; sgn[0] = 1; } if (dy >= 0) { tymin = (ymin - oy)/dy; tymax = (ymax - oy)/dy; sgn[1] = -1; } else { tymin = (ymax - oy)/dy; tymax = (ymin - oy)/dy; sgn[1] = 1; } if (dz >= 0) { tzmin = (zmin - oz)/dz; tzmax = (zmax - oz)/dz; sgn[2] = -1; } else { tzmin = (zmax - oz)/dz; tzmax = (zmin - oz)/dz; sgn[2] = 1; } if (txmin > tymin) { tmin = txmin; axmin = 0; } else { tmin = tymin; axmin = 1; } if (tzmin > tmin) { tmin = tzmin; axmin = 2; } if (txmax < tymax) { tmax = txmax; axmax = 0; } else { tmax = tymax; axmax = 1; } if (tzmax < tmax) { tmax = tzmax; axmax = 2; } if (tmin >= tmax) return AIR_FALSE; *tP = tmin; *axP = axmin; *dirP = sgn[axmin]; if (!AIR_IN_CL(ray->neer, tmin, ray->faar)) { *tP = tmax; *axP = axmax; *dirP = -sgn[axmax]; if (!AIR_IN_CL(ray->neer, tmax, ray->faar)) { return AIR_FALSE; } } return AIR_TRUE; } int _echoRayIntx_CubeSolid(echoPos_t *tminP, echoPos_t *tmaxP, echoPos_t xmin, echoPos_t xmax, echoPos_t ymin, echoPos_t ymax, echoPos_t zmin, echoPos_t zmax, echoRay *ray) { echoPos_t txmin, tymin, tzmin, txmax, tymax, tzmax, dx, dy, dz, ox, oy, oz, tmin, tmax; ELL_3V_GET(dx, dy, dz, ray->dir); ELL_3V_GET(ox, oy, oz, ray->from); if (dx >= 0) { txmin = (xmin - ox)/dx; txmax = (xmax - ox)/dx; } else { txmin = (xmax - ox)/dx; txmax = (xmin - ox)/dx; } if (dy >= 0) { tymin = (ymin - oy)/dy; tymax = (ymax - oy)/dy; } else { tymin = (ymax - oy)/dy; tymax = (ymin - oy)/dy; } if (dz >= 0) { tzmin = (zmin - oz)/dz; tzmax = (zmax - oz)/dz; } else { tzmin = (zmax - oz)/dz; tzmax = (zmin - oz)/dz; } if (txmin > tymin) tmin = txmin; else tmin = tymin; if (tzmin > tmin) tmin = tzmin; if (txmax < tymax) tmax = txmax; else tmax = tymax; if (tzmax < tmax) tmax = tzmax; if (tmin >= tmax) return AIR_FALSE; if ( ray->faar < tmin || ray->neer > tmax ) return AIR_FALSE; *tminP = AIR_MAX(tmin, ray->neer); *tmaxP = AIR_MIN(tmax, ray->faar); return AIR_TRUE; } int _echoRayIntx_Sphere(RAYINTX_ARGS(Sphere)) { echoPos_t t, A, B, C, r[3], dscr, pos[3], tmp; AIR_UNUSED(parm); AIR_UNUSED(tstate); ELL_3V_SUB(r, ray->from, obj->pos); A = ELL_3V_DOT(ray->dir, ray->dir); B = 2*ELL_3V_DOT(ray->dir, r); C = ELL_3V_DOT(r, r) - obj->rad*obj->rad; dscr = B*B - 4*A*C; if (dscr <= 0) { /* grazes or misses (most common case) */ return AIR_FALSE; } /* else */ dscr = sqrt(dscr); t = (-B - dscr)/(2*A); if (!AIR_IN_CL(ray->neer, t, ray->faar)) { t = (-B + dscr)/(2*A); if (!AIR_IN_CL(ray->neer, t, ray->faar)) { return AIR_FALSE; } } /* else one of the intxs is in [neer,faar] segment */ intx->t = t; ELL_3V_SCALE_ADD2(pos, 1, ray->from, t, ray->dir); ELL_3V_SUB(intx->norm, pos, obj->pos); ELL_3V_NORM(intx->norm, intx->norm, tmp); intx->obj = OBJECT(obj); /* does NOT set u, v */ return AIR_TRUE; } void _echoRayIntxUV_Sphere(echoIntx *intx) { echoPos_t u, v; if (intx->norm[0] || intx->norm[1]) { u = atan2(intx->norm[1], intx->norm[0]); intx->u = AIR_AFFINE(-AIR_PI, u, AIR_PI, 0.0, 1.0); v = -asin(intx->norm[2]); intx->v = AIR_AFFINE(-AIR_PI/2, v, AIR_PI/2, 0.0, 1.0); } else { intx->u = 0; /* this is valid because if we're here, then intx->norm[2] is either 1.0 or -1.0 */ intx->v = AIR_AFFINE(1.0, intx->norm[2], -1.0, 0.0, 1.0); } } int _echoRayIntx_Cylinder(RAYINTX_ARGS(Cylinder)) { echoPos_t A, B, C, aa, bb, cc, dd, ee, ff, dscr, cylt1, cylt2, t, tmax, twot[2], cylp1, cylp2, pos[3], tmp; int tidx, radi0, radi1, twocap[2], cap; AIR_UNUSED(parm); AIR_UNUSED(tstate); if (!_echoRayIntx_CubeSolid(&t, &tmax, -1-ECHO_EPSILON, 1+ECHO_EPSILON, -1-ECHO_EPSILON, 1+ECHO_EPSILON, -1-ECHO_EPSILON, 1+ECHO_EPSILON, ray)) { return AIR_FALSE; } switch(obj->axis) { case 0: radi0 = 1; radi1 = 2; break; case 1: radi0 = 0; radi1 = 2; break; case 2: default: radi0 = 0; radi1 = 1; break; } aa = ray->dir[radi0]; bb = ray->dir[radi1]; ee = ray->dir[obj->axis]; cc = ray->from[radi0]; dd = ray->from[radi1]; ff = ray->from[obj->axis]; A = aa*aa + bb*bb; B = 2*(aa*cc + bb*dd); C = cc*cc + dd*dd - 1; dscr = B*B - 4*A*C; if (dscr <= 0) { /* infinite ray grazes or misses the infinite cylinder (not bounded to [-1,1] along cylinder's axis), so therefore the ray grazes or misses the actual cylinder */ return AIR_FALSE; } /* else infinite ray intersects the infinite cylinder */ dscr = sqrt(dscr); cylt1 = (-B - dscr)/(2*A); cylp1 = ff + cylt1*ee; cylt2 = (-B + dscr)/(2*A); cylp2 = ff + cylt2*ee; if ( (cylp1 <= -1 && cylp2 <= -1) || (cylp1 >= 1 && cylp2 >= 1) ) { /* both intersections with infinite cylinder lie on ONE side of the finite extent, so there can't be an intersection */ return AIR_FALSE; } /* else infinite ray DOES intersect finite cylinder; we have to find if any of the intersections are in the [neer, faar] interval */ tidx = 0; if (AIR_IN_CL(-1, cylp1, 1)) { twot[tidx] = cylt1; twocap[tidx] = 0; tidx++; } if (AIR_IN_CL(-1, cylp2, 1)) { twot[tidx] = cylt2; twocap[tidx] = 0; tidx++; } if (tidx < 2) { /* at least one of the two intersections is with the endcaps */ t = (-ff - 1)/ee; ELL_3V_SCALE_ADD2(pos, 1, ray->from, t, ray->dir); aa = pos[radi0]; bb = pos[radi1]; cc = aa*aa + bb*bb; if (cc <= 1) { twot[tidx] = t; twocap[tidx] = 1; tidx++; } if (tidx < 2) { /* try other endcap */ t = (-ff + 1)/ee; ELL_3V_SCALE_ADD2(pos, 1, ray->from, t, ray->dir); aa = pos[radi0]; bb = pos[radi1]; cc = aa*aa + bb*bb; if (cc <= 1) { twot[tidx] = t; twocap[tidx] = 1; tidx++; } } } if (!tidx) { return AIR_FALSE; } if (2 == tidx && twot[0] > twot[1]) { ELL_SWAP2(twot[0], twot[1], aa); ELL_SWAP2(twocap[0], twocap[1], cap); } t = twot[0]; cap = twocap[0]; if (!AIR_IN_CL(ray->neer, t, ray->faar)) { if (1 == tidx) { return AIR_FALSE; } t = twot[1]; cap = twocap[1]; if (!AIR_IN_CL(ray->neer, t, ray->faar)) { return AIR_FALSE; } } /* else one of the intxs is in [neer,faar] segment */ intx->t = t; ELL_3V_SCALE_ADD2(pos, 1, ray->from, t, ray->dir); switch(obj->axis) { case 0: ELL_3V_SET(intx->norm, cap*pos[0], (1-cap)*pos[1], (1-cap)*pos[2]); break; case 1: ELL_3V_SET(intx->norm, (1-cap)*pos[0], cap*pos[1], (1-cap)*pos[2]); break; case 2: default: ELL_3V_SET(intx->norm, (1-cap)*pos[0], (1-cap)*pos[1], cap*pos[2]); break; } ELL_3V_NORM(intx->norm, intx->norm, tmp); intx->obj = OBJECT(obj); /* does NOT set u, v */ return AIR_TRUE; } int _echoRayIntx_Cube(RAYINTX_ARGS(Cube)) { echoPos_t t; int ax, dir; AIR_UNUSED(parm); if (!_echoRayIntx_CubeSurf(&t, &ax, &dir, -1, 1, -1, 1, -1, 1, ray)) return AIR_FALSE; intx->obj = (echoObject *)obj; intx->t = t; switch(ax) { case 0: ELL_3V_SET(intx->norm, dir, 0, 0); break; case 1: ELL_3V_SET(intx->norm, 0, dir, 0); break; case 2: ELL_3V_SET(intx->norm, 0, 0, dir); break; } intx->face = ax + 3*(dir + 1)/2; if (tstate->verbose) { fprintf(stderr, "%s%s: ax = %d --> norm = (%g,%g,%g)\n", _echoDot(tstate->depth), "_echoRayIntx_Cube", ax, intx->norm[0], intx->norm[1], intx->norm[2]); } /* does NOT set u, v */ return AIR_TRUE; } void _echoRayIntxUV_Cube(echoIntx *intx) { echoPos_t x, y, z; ELL_3V_GET(x, y, z, intx->pos); switch(intx->face) { case 0: intx->u = AIR_AFFINE(-1, y, 1, 0.0, 1.0); intx->v = AIR_AFFINE(-1, -z, 1, 0.0, 1.0); break; case 1: intx->u = AIR_AFFINE(-1, -x, 1, 0.0, 1.0); intx->v = AIR_AFFINE(-1, -z, 1, 0.0, 1.0); break; case 2: intx->u = AIR_AFFINE(-1, -x, 1, 0.0, 1.0); intx->v = AIR_AFFINE(-1, y, 1, 0.0, 1.0); break; case 3: intx->u = AIR_AFFINE(-1, -y, 1, 0.0, 1.0); intx->v = AIR_AFFINE(-1, z, 1, 0.0, 1.0); break; case 4: intx->u = AIR_AFFINE(-1, x, 1, 0.0, 1.0); intx->v = AIR_AFFINE(-1, z, 1, 0.0, 1.0); break; case 5: intx->u = AIR_AFFINE(-1, x, 1, 0.0, 1.0); intx->v = AIR_AFFINE(-1, -y, 1, 0.0, 1.0); break; } } /* ** TRI_INTX ** ** given a triangle in terms of origin, edge0, edge1, this will ** begin the intersection calculation: ** - sets pvec, tvec, qvec (all of them if intx is not ruled out ) ** - sets u, and rules out intx based on (u < 0.0 || u > 1.0) ** - sets v, and rules out intx based on COND ** - sets t, and rules out intx based on (t < neer || t > faar) */ #define TRI_INTX(ray, origin, edge0, edge1, pvec, qvec, tvec, \ det, t, u, v, COND, NOPE) \ ELL_3V_CROSS(pvec, ray->dir, edge1); \ det = ELL_3V_DOT(pvec, edge0); \ if (det > -ECHO_EPSILON && det < ECHO_EPSILON) { \ NOPE; \ } \ /* now det is the reciprocal of the determinant */ \ det = 1.0/det; \ ELL_3V_SUB(tvec, ray->from, origin); \ u = det * ELL_3V_DOT(pvec, tvec); \ if (u < 0.0 || u > 1.0) { \ NOPE; \ } \ ELL_3V_CROSS(qvec, tvec, edge0); \ v = det * ELL_3V_DOT(qvec, ray->dir); \ if (COND) { \ NOPE; \ } \ t = det * ELL_3V_DOT(qvec, edge1); \ if (t < ray->neer || t > ray->faar) { \ NOPE; \ } int _echoRayIntx_Rectangle(RAYINTX_ARGS(Rectangle)) { echoPos_t pvec[3], qvec[3], tvec[3], det, t, u, v, *edge0, *edge1, tmp; AIR_UNUSED(tstate); if (echoMatterLight == obj->matter && (ray->shadow || !parm->renderLights)) { return AIR_FALSE; } edge0 = obj->edge0; edge1 = obj->edge1; TRI_INTX(ray, obj->origin, edge0, edge1, pvec, qvec, tvec, det, t, u, v, (v < 0.0 || v > 1.0), return AIR_FALSE); intx->t = t; intx->u = u; intx->v = v; ELL_3V_CROSS(intx->norm, edge0, edge1); ELL_3V_NORM(intx->norm, intx->norm, tmp); intx->obj = OBJECT(obj); /* DOES set u, v */ return AIR_TRUE; } int _echoRayIntx_Triangle(RAYINTX_ARGS(Triangle)) { echoPos_t pvec[3], qvec[3], tvec[3], det, t, u, v, edge0[3], edge1[3], tmp; AIR_UNUSED(parm); AIR_UNUSED(tstate); ELL_3V_SUB(edge0, obj->vert[1], obj->vert[0]); ELL_3V_SUB(edge1, obj->vert[2], obj->vert[0]); TRI_INTX(ray, obj->vert[0], edge0, edge1, pvec, qvec, tvec, det, t, u, v, (v < 0.0 || u + v > 1.0), return AIR_FALSE); intx->t = t; intx->u = u; intx->v = v; ELL_3V_CROSS(intx->norm, edge0, edge1); ELL_3V_NORM(intx->norm, intx->norm, tmp); intx->obj = (echoObject *)obj; /* DOES set u, v */ return AIR_TRUE; } int _echoRayIntx_TriMesh(RAYINTX_ARGS(TriMesh)) { echoPos_t *pos, vert0[3], edge0[3], edge1[3], pvec[3], qvec[3], tvec[3], det, t, tmax, u, v, tmp; echoTriMesh *trim; int i, ret; AIR_UNUSED(parm); trim = TRIMESH(obj); if (!_echoRayIntx_CubeSolid(&t, &tmax, trim->min[0], trim->max[0], trim->min[1], trim->max[1], trim->min[2], trim->max[2], ray)) { if (tstate->verbose) { fprintf(stderr, "%s%s: trimesh bbox (%g,%g,%g) --> (%g,%g,%g) not hit\n", _echoDot(tstate->depth), "_echoRayIntx_TriMesh", trim->min[0], trim->min[1], trim->min[2], trim->max[0], trim->max[1], trim->max[2]); } return AIR_FALSE; } /* stupid linear search for now */ ret = AIR_FALSE; for (i=0; inumF; i++) { pos = trim->pos + 3*trim->vert[0 + 3*i]; ELL_3V_COPY(vert0, pos); pos = trim->pos + 3*trim->vert[1 + 3*i]; ELL_3V_SUB(edge0, pos, vert0); pos = trim->pos + 3*trim->vert[2 + 3*i]; ELL_3V_SUB(edge1, pos, vert0); TRI_INTX(ray, vert0, edge0, edge1, pvec, qvec, tvec, det, t, u, v, (v < 0.0 || u + v > 1.0), continue); if (ray->shadow) { return AIR_TRUE; } intx->t = ray->faar = t; ELL_3V_CROSS(intx->norm, edge0, edge1); ELL_3V_NORM(intx->norm, intx->norm, tmp); intx->obj = (echoObject *)obj; intx->face = i; ret = AIR_TRUE; } /* does NOT set u, v */ return ret; } void _echoRayIntxUV_TriMesh(echoIntx *intx) { echoPos_t u, v, norm[3], len; echoTriMesh *trim; trim = TRIMESH(intx->obj); ELL_3V_SUB(norm, intx->pos, trim->meanvert); ELL_3V_NORM(norm, norm, len); if (norm[0] || norm[1]) { u = atan2(norm[1], norm[0]); intx->u = AIR_AFFINE(-AIR_PI, u, AIR_PI, 0.0, 1.0); v = -asin(norm[2]); intx->v = AIR_AFFINE(-AIR_PI/2, v, AIR_PI/2, 0.0, 1.0); } else { intx->u = 0; intx->v = AIR_AFFINE(1.0, norm[2], -1.0, 0.0, 1.0); } } int _echoRayIntx_AABBox(RAYINTX_ARGS(AABBox)) { int ret; echoAABBox *box; echoPos_t t, tmax; box = AABBOX(obj); if (_echoRayIntx_CubeSolid(&t, &tmax, box->min[0], box->max[0], box->min[1], box->max[1], box->min[2], box->max[2], ray)) { intx->boxhits++; ret = _echoRayIntx[box->obj->type](intx, ray, box->obj, parm, tstate); } else { ret = AIR_FALSE; } return ret; } int _echoRayIntx_Split(RAYINTX_ARGS(Split)) { char me[]="_echoRayIntx_Split"; echoObject *a, *b; echoPos_t *mina, *minb, *maxa, *maxb, t, tmax; int ret; if (ray->dir[obj->axis] > 0) { a = obj->obj0; mina = obj->min0; maxa = obj->max0; b = obj->obj1; minb = obj->min1; maxb = obj->max1; } else { a = obj->obj1; mina = obj->min1; maxa = obj->max1; b = obj->obj0; minb = obj->min0; maxb = obj->max0; } if (tstate->verbose) { fprintf(stderr, "%s%s: (shadow = %d):\n", _echoDot(tstate->depth), me, ray->shadow); fprintf(stderr, "%s%s: 1st: (%g,%g,%g) -- (%g,%g,%g) (obj %d)\n", _echoDot(tstate->depth), me, mina[0], mina[1], mina[2], maxa[0], maxa[1], maxa[2], a->type); fprintf(stderr, "%s%s: 2nd: (%g,%g,%g) -- (%g,%g,%g) (obj %d)\n", _echoDot(tstate->depth), me, minb[0], minb[1], minb[2], maxb[0], maxb[1], maxb[2], b->type); } ret = AIR_FALSE; if (_echoRayIntx_CubeSolid(&t, &tmax, mina[0], maxa[0], mina[1], maxa[1], mina[2], maxa[2], ray)) { intx->boxhits++; if (_echoRayIntx[a->type](intx, ray, a, parm, tstate)) { if (ray->shadow) { return AIR_TRUE; } ray->faar = intx->t; ret = AIR_TRUE; } } if (_echoRayIntx_CubeSolid(&t, &tmax, minb[0], maxb[0], minb[1], maxb[1], minb[2], maxb[2], ray)) { intx->boxhits++; if (_echoRayIntx[b->type](intx, ray, b, parm, tstate)) { ray->faar = intx->t; ret = AIR_TRUE; } } return ret; } int _echoRayIntx_List(RAYINTX_ARGS(List)) { unsigned int i; int ret; echoObject *kid; ret = AIR_FALSE; for (i=0; iobjArr->len; i++) { kid = obj->obj[i]; if (_echoRayIntx[kid->type](intx, ray, kid, parm, tstate)) { ray->faar = intx->t; ret = AIR_TRUE; if (ray->shadow) { /* no point in testing any further */ return ret; } } } return ret; } int _echoRayIntx_Instance(RAYINTX_ARGS(Instance)) { echoPos_t a[4], b[4], tmp; echoRay iray; /* ELL_3V_COPY(iray.from, ray->from); ELL_3V_COPY(iray.dir, ray->dir); */ ELL_4V_SET(a, ray->from[0], ray->from[1], ray->from[2], 1); ELL_4MV_MUL(b, obj->Mi, a); ELL_34V_HOMOG(iray.from, b); ELL_4V_SET(a, ray->dir[0], ray->dir[1], ray->dir[2], 0); ELL_4MV_MUL(b, obj->Mi, a); ELL_3V_COPY(iray.dir, b); if (tstate->verbose) { fprintf(stderr, "%s%s: dir (%g,%g,%g)\n%s -- Mi --> " "(%g,%g,%g,%g)\n%s --> (%g,%g,%g)\n", _echoDot(tstate->depth), "_echoRayIntx_Instance", a[0], a[1], a[2], _echoDot(tstate->depth), b[0], b[1], b[2], b[3], _echoDot(tstate->depth), iray.dir[0], iray.dir[1], iray.dir[2]); } iray.neer = ray->neer; iray.faar = ray->faar; iray.shadow = ray->shadow; if (_echoRayIntx[obj->obj->type](intx, &iray, obj->obj, parm, tstate)) { ELL_4V_SET(a, intx->norm[0], intx->norm[1], intx->norm[2], 0); ELL_4MV_TMUL(b, obj->Mi, a); ELL_3V_COPY(intx->norm, b); ELL_3V_NORM(intx->norm, intx->norm, tmp); if (tstate->verbose) { fprintf(stderr, "%s%s: hit a %d (at t=%g) with M == \n", _echoDot(tstate->depth), "_echoRayIntx_Instance", obj->obj->type, intx->t); ell_4m_PRINT(stderr, obj->M); fprintf(stderr, "%s ... (det = %f), and Mi == \n", _echoDot(tstate->depth), ell_4m_DET(obj->M)); ell_4m_PRINT(stderr, obj->Mi); } return AIR_TRUE; } /* else */ return AIR_FALSE; } void _echoRayIntxUV_Noop(echoIntx *intx) { AIR_UNUSED(intx); } /* ** NB: the intersections with real objects need to normalize ** intx->norm */ _echoRayIntx_t _echoRayIntx[ECHO_TYPE_NUM] = { (_echoRayIntx_t)_echoRayIntx_Sphere, (_echoRayIntx_t)_echoRayIntx_Cylinder, (_echoRayIntx_t)_echoRayIntx_Superquad, (_echoRayIntx_t)_echoRayIntx_Cube, (_echoRayIntx_t)_echoRayIntx_Triangle, (_echoRayIntx_t)_echoRayIntx_Rectangle, (_echoRayIntx_t)_echoRayIntx_TriMesh, (_echoRayIntx_t)_echoRayIntx_Noop, (_echoRayIntx_t)_echoRayIntx_AABBox, (_echoRayIntx_t)_echoRayIntx_Split, (_echoRayIntx_t)_echoRayIntx_List, (_echoRayIntx_t)_echoRayIntx_Instance, }; _echoRayIntxUV_t _echoRayIntxUV[ECHO_TYPE_NUM] = { _echoRayIntxUV_Sphere, /* echoTypeSphere */ _echoRayIntxUV_Noop, /* echoTypeCylinder */ _echoRayIntxUV_Noop, /* sqd.c: echoTypeSuperquad */ _echoRayIntxUV_Cube, /* echoTypeCube */ _echoRayIntxUV_Noop, /* echoTypeTriangle */ _echoRayIntxUV_Noop, /* echoTypeRectangle */ _echoRayIntxUV_TriMesh, /* echoTypeTriMesh */ _echoRayIntxUV_Noop, /* echoTypeIsosurface */ _echoRayIntxUV_Noop, /* echoTypeAABBox */ _echoRayIntxUV_Noop, /* echoTypeSplit */ _echoRayIntxUV_Noop, /* echoTypeList */ _echoRayIntxUV_Noop /* echoTypeInstance */ }; int echoRayIntx(echoIntx *intx, echoRay *ray, echoScene *scene, echoRTParm *parm, echoThreadState *tstate) { unsigned int idx; int ret; echoObject *kid; echoPos_t tmp; _echoVerbose = tstate->verbose; ret = AIR_FALSE; for (idx=0; idxrendArr->len; idx++) { kid = scene->rend[idx]; if (_echoRayIntx[kid->type](intx, ray, kid, parm, tstate)) { ray->faar = intx->t; ret = AIR_TRUE; if (ray->shadow) { /* no point in testing any further */ return ret; } } } if (ret) { /* being here means we're not a shadow ray */ ELL_3V_SCALE_ADD2(intx->pos, 1, ray->from, intx->t, ray->dir); ELL_3V_SCALE(intx->view, -1, ray->dir); ELL_3V_NORM(intx->view, intx->view, tmp); /* this is needed for phong materials; for glass and metal, it is either used directly, or as a reference in fuzzification */ _ECHO_REFLECT(intx->refl, intx->norm, intx->view, tmp); } return ret; } teem-1.11.0~svn6057/src/echo/renderEcho.c0000664000175000017500000004030612165631065017544 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" int echoThreadStateInit(int threadIdx, echoThreadState *tstate, echoRTParm *parm, echoGlobalState *gstate) { static const char me[]="echoThreadStateInit"; if (!(tstate && parm && gstate)) { biffAddf(ECHO, "%s: got NULL pointer", me); return 1; } /* tstate->thread set by echoThreadStateNew() */ tstate->gstate = gstate; /* this will probably be over-written */ tstate->verbose = gstate->verbose; tstate->threadIdx = threadIdx; if (nrrdMaybeAlloc_va(tstate->nperm, nrrdTypeInt, 2, AIR_CAST(size_t, ECHO_JITTABLE_NUM), AIR_CAST(size_t, parm->numSamples))) { biffMovef(ECHO, NRRD, "%s: couldn't allocate jitter permutation array", me); return 1; } nrrdAxisInfoSet_va(tstate->nperm, nrrdAxisInfoLabel, "jittable", "sample"); if (nrrdMaybeAlloc_va(tstate->njitt, echoPos_nt, 3, AIR_CAST(size_t, 2), AIR_CAST(size_t, ECHO_JITTABLE_NUM), AIR_CAST(size_t, parm->numSamples))) { biffMovef(ECHO, NRRD, "%s: couldn't allocate jitter array", me); return 1; } nrrdAxisInfoSet_va(tstate->njitt, nrrdAxisInfoLabel, "x,y", "jittable", "sample"); tstate->permBuff = AIR_CAST(unsigned int *, airFree(tstate->permBuff)); if (!(tstate->permBuff = AIR_CAST(unsigned int *, calloc(parm->numSamples, sizeof(int))))) { biffAddf(ECHO, "%s: couldn't allocate permutation buffer", me); return 1; } tstate->chanBuff = (echoCol_t *)airFree(tstate->chanBuff); if (!( tstate->chanBuff = (echoCol_t*)calloc(ECHO_IMG_CHANNELS * parm->numSamples, sizeof(echoCol_t)) )) { biffAddf(ECHO, "%s: couldn't allocate img channel sample buffer", me); return 1; } airSrandMT_r(tstate->rst, AIR_CAST(unsigned int, (parm->seedRand ? airTime() : threadIdx))); tstate->returnPtr = NULL; return 0; } /* ******** echoJitterCompute() ** ** */ void echoJitterCompute(echoRTParm *parm, echoThreadState *tstate) { echoPos_t *jitt, w; int s, i, j, xi, yi, n, N, *perm; N = parm->numSamples; n = (int)sqrt(N); w = 1.0/n; /* each row in perm[] is for one sample, for going through all jittables; each column is a different permutation of [0..parm->numSamples-1] */ perm = (int *)tstate->nperm->data; for (j=0; jrst, tstate->permBuff, parm->numSamples, parm->permuteJitter); for (s=0; spermBuff[s]; } } jitt = (echoPos_t *)tstate->njitt->data; for (s=0; sjitterType) { case echoJitterNone: jitt[0 + 2*j] = 0.0; jitt[1 + 2*j] = 0.0; break; case echoJitterGrid: jitt[0 + 2*j] = NRRD_POS(nrrdCenterCell, -0.5, 0.5, n, xi); jitt[1 + 2*j] = NRRD_POS(nrrdCenterCell, -0.5, 0.5, n, yi); break; case echoJitterJitter: jitt[0 + 2*j] = (NRRD_POS(nrrdCenterCell, -0.5, 0.5, n, xi) + w*(airDrandMT_r(tstate->rst) - 0.5)); jitt[1 + 2*j] = (NRRD_POS(nrrdCenterCell, -0.5, 0.5, n, yi) + w*(airDrandMT_r(tstate->rst) - 0.5)); break; case echoJitterRandom: jitt[0 + 2*j] = airDrandMT_r(tstate->rst) - 0.5; jitt[1 + 2*j] = airDrandMT_r(tstate->rst) - 0.5; break; } } jitt += 2*ECHO_JITTABLE_NUM; } return; } /* ******** echoRTRenderCheck ** ** does all the error checking required of echoRTRender and ** everything that it calls */ int echoRTRenderCheck(Nrrd *nraw, limnCamera *cam, echoScene *scene, echoRTParm *parm, echoGlobalState *gstate) { static const char me[]="echoRTRenderCheck"; int tmp; if (!(nraw && cam && scene && parm && gstate)) { biffAddf(ECHO, "%s: got NULL pointer", me); return 1; } if (limnCameraUpdate(cam)) { biffMovef(ECHO, LIMN, "%s: camera trouble", me); return 1; } if (scene->envmap) { if (limnEnvMapCheck(scene->envmap)) { biffMovef(ECHO, LIMN, "%s: environment map not valid", me); return 1; } } if (airEnumValCheck(echoJitter, parm->jitterType)) { biffAddf(ECHO, "%s: jitter method (%d) invalid", me, parm->jitterType); return 1; } if (!(parm->numSamples > 0)) { biffAddf(ECHO, "%s: # samples (%d) invalid", me, parm->numSamples); return 1; } if (!(parm->imgResU > 0 && parm->imgResV)) { biffAddf(ECHO, "%s: image dimensions (%dx%d) invalid", me, parm->imgResU, parm->imgResV); return 1; } if (!AIR_EXISTS(parm->aperture)) { biffAddf(ECHO, "%s: aperture doesn't exist", me); return 1; } switch (parm->jitterType) { case echoJitterNone: case echoJitterRandom: break; case echoJitterGrid: case echoJitterJitter: tmp = (int)sqrt(parm->numSamples); if (tmp*tmp != parm->numSamples) { biffAddf(ECHO, "%s: need a square # samples for %s jitter method (not %d)", me, airEnumStr(echoJitter, parm->jitterType), parm->numSamples); return 1; } break; } /* for the time being things are hard-coded to be r,g,b,a,time */ if (ECHO_IMG_CHANNELS != 5) { biffAddf(ECHO, "%s: ECHO_IMG_CHANNELS != 5", me); return 1; } /* all is well */ return 0; } void echoChannelAverage(echoCol_t *img, echoRTParm *parm, echoThreadState *tstate) { int s; echoCol_t R, G, B, A, T; R = G = B = A = T = 0; for (s=0; snumSamples; s++) { R += tstate->chanBuff[0 + ECHO_IMG_CHANNELS*s]; G += tstate->chanBuff[1 + ECHO_IMG_CHANNELS*s]; B += tstate->chanBuff[2 + ECHO_IMG_CHANNELS*s]; A += tstate->chanBuff[3 + ECHO_IMG_CHANNELS*s]; T += tstate->chanBuff[4 + ECHO_IMG_CHANNELS*s]; } img[0] = R / parm->numSamples; img[1] = G / parm->numSamples; img[2] = B / parm->numSamples; img[3] = A / parm->numSamples; img[4] = T; return; } /* ******** echoRayColor ** ** This is called by echoRTRender and by the various color routines, ** following an intersection with non-phong non-light material (the ** things that require reflection or refraction rays). As such, it is ** never called on shadow rays. */ void echoRayColor(echoCol_t *chan, echoRay *ray, echoScene *scene, echoRTParm *parm, echoThreadState *tstate) { static const char me[]="echoRayColor"; echoCol_t rgba[4]; echoIntx intx; tstate->depth++; if (tstate->depth > parm->maxRecDepth) { /* we've exceeded the recursion depth, so no more rays for you */ ELL_4V_SET(chan, parm->maxRecCol[0], parm->maxRecCol[1], parm->maxRecCol[2], 1.0); goto done; } intx.boxhits = 0; if (!echoRayIntx(&intx, ray, scene, parm, tstate)) { if (tstate->verbose) { fprintf(stderr, "%s%s: (nothing was hit)\n",_echoDot(tstate->depth), me); } /* ray hits nothing in scene */ ELL_4V_SET_TT(chan, echoCol_t, scene->bkgr[0], scene->bkgr[1], scene->bkgr[2], (parm->renderBoxes ? 1.0 - pow(1.0 - parm->boxOpac, intx.boxhits) : 0.0)); goto done; } if (tstate->verbose) { fprintf(stderr, "%s%s: hit a %d (%p) at (%g,%g,%g)\n" "%s = %g along (%g,%g,%g)\n", _echoDot(tstate->depth), me, intx.obj->type, AIR_CAST(void*, intx.obj), intx.pos[0], intx.pos[1], intx.pos[2], _echoDot(tstate->depth), intx.t, ray->dir[0], ray->dir[1], ray->dir[2]); } echoIntxColor(rgba, &intx, scene, parm, tstate); ELL_4V_COPY(chan, rgba); done: tstate->depth--; return; } void * _echoRTRenderThreadBody(void *_arg) { char done[20]; int imgUi, imgVi, /* integral pixel indices */ samp; /* which sample are we doing */ echoPos_t tmp0, tmp1, pixUsz, pixVsz, /* U and V dimensions of a pixel */ U[4], V[4], N[4], /* view space basis (only first 3 elements used) */ imgU, imgV, /* floating point pixel center locations */ eye[3], /* eye center before jittering */ at[3], /* ray destination (pixel center post-jittering) */ imgOrig[3]; /* image origin */ double time0; echoRay ray; /* (not a pointer) */ echoThreadState *arg; echoCol_t *img, *chan; /* current scanline of channel buffer array */ Nrrd *nraw; /* copies of arguments to echoRTRender . . . */ limnCamera *cam; echoScene *scene; echoRTParm *parm; arg = (echoThreadState *)_arg; nraw = arg->gstate->nraw; cam = arg->gstate->cam; scene = arg->gstate->scene; parm = arg->gstate->parm; echoJitterCompute(arg->gstate->parm, arg); if (arg->gstate->verbose > 2) { nrrdSave("jitt.nrrd", arg->njitt, NULL); } /* set eye, U, V, N, imgOrig */ ELL_3V_COPY(eye, arg->gstate->cam->from); ELL_4MV_ROW0_GET(U, cam->W2V); ELL_4MV_ROW1_GET(V, cam->W2V); ELL_4MV_ROW2_GET(N, cam->W2V); ELL_3V_SCALE_ADD2(imgOrig, 1.0, eye, cam->vspDist, N); /* determine size of a single pixel (based on cell-centering) */ pixUsz = (cam->uRange[1] - cam->uRange[0])/(parm->imgResU); pixVsz = (cam->vRange[1] - cam->vRange[0])/(parm->imgResV); arg->depth = 0; ray.shadow = AIR_FALSE; arg->verbose = AIR_FALSE; while (1) { if (arg->gstate->workMutex) { airThreadMutexLock(arg->gstate->workMutex); } imgVi = arg->gstate->workIdx; if (arg->gstate->workIdx < parm->imgResV) { arg->gstate->workIdx += 1; } if (!(imgVi % 5)) { fprintf(stderr, "%s", airDoneStr(0, imgVi, parm->imgResV-1, done)); fflush(stderr); } if (arg->gstate->workMutex) { airThreadMutexUnlock(arg->gstate->workMutex); } if (imgVi == parm->imgResV) { /* we're done! */ break; } imgV = NRRD_POS(nrrdCenterCell, cam->vRange[0], cam->vRange[1], parm->imgResV, imgVi); for (imgUi=0; imgUiimgResU; imgUi++) { imgU = NRRD_POS(nrrdCenterCell, cam->uRange[0], cam->uRange[1], parm->imgResU, imgUi); img = ((echoCol_t *)nraw->data + ECHO_IMG_CHANNELS*(imgUi + parm->imgResU*imgVi)); /* initialize things on first "scanline" */ arg->jitt = (echoPos_t *)arg->njitt->data; chan = arg->chanBuff; /* arg->verbose = ( (48 == imgUi && 13 == imgVi) || (49 == imgUi && 13 == imgVi) ); */ if (arg->verbose) { fprintf(stderr, "\n"); fprintf(stderr, "-----------------------------------------------\n"); fprintf(stderr, "----------------- (%3d, %3d) ------------------\n", imgUi, imgVi); fprintf(stderr, "-----------------------------------------------\n\n"); } /* go through samples */ for (samp=0; sampnumSamples; samp++) { /* set ray.from[] */ ELL_3V_COPY(ray.from, eye); if (parm->aperture) { tmp0 = parm->aperture*(arg->jitt[0 + 2*echoJittableLens]); tmp1 = parm->aperture*(arg->jitt[1 + 2*echoJittableLens]); ELL_3V_SCALE_ADD3(ray.from, 1, ray.from, tmp0, U, tmp1, V); } /* set at[] */ tmp0 = imgU + pixUsz*(arg->jitt[0 + 2*echoJittablePixel]); tmp1 = imgV + pixVsz*(arg->jitt[1 + 2*echoJittablePixel]); ELL_3V_SCALE_ADD3(at, 1, imgOrig, tmp0, U, tmp1, V); /* do it! */ ELL_3V_SUB(ray.dir, at, ray.from); ELL_3V_NORM(ray.dir, ray.dir, tmp0); ray.neer = 0.0; ray.faar = ECHO_POS_MAX; time0 = airTime(); if (0) { memset(chan, 0, ECHO_IMG_CHANNELS*sizeof(echoCol_t)); } else { echoRayColor(chan, &ray, scene, parm, arg); } chan[4] = AIR_CAST(echoCol_t, airTime() - time0); /* move to next "scanline" */ arg->jitt += 2*ECHO_JITTABLE_NUM; chan += ECHO_IMG_CHANNELS; } echoChannelAverage(img, parm, arg); img += ECHO_IMG_CHANNELS; if (!parm->reuseJitter) { echoJitterCompute(parm, arg); } } } return _arg; } /* ******** echoRTRender ** ** top-level call to accomplish all (ray-tracing) rendering. As much ** error checking as possible should be done here and not in the ** lower-level functions. */ int echoRTRender(Nrrd *nraw, limnCamera *cam, echoScene *scene, echoRTParm *parm, echoGlobalState *gstate) { static const char me[]="echoRTRender"; int tid, ret; airArray *mop; echoThreadState *tstate[ECHO_THREAD_MAX]; if (echoRTRenderCheck(nraw, cam, scene, parm, gstate)) { biffAddf(ECHO, "%s: problem with input", me); return 1; } gstate->nraw = nraw; gstate->cam = cam; gstate->scene = scene; gstate->parm = parm; mop = airMopNew(); if (nrrdMaybeAlloc_va(nraw, echoCol_nt, 3, AIR_CAST(size_t, ECHO_IMG_CHANNELS), AIR_CAST(size_t, parm->imgResU), AIR_CAST(size_t, parm->imgResV))) { biffMovef(ECHO, NRRD, "%s: couldn't allocate output image", me); airMopError(mop); return 1; } airMopAdd(mop, nraw, (airMopper)nrrdNix, airMopOnError); nrrdAxisInfoSet_va(nraw, nrrdAxisInfoLabel, "r,g,b,a,t", "x", "y"); nrrdAxisInfoSet_va(nraw, nrrdAxisInfoMin, AIR_NAN, cam->uRange[0], cam->vRange[0]); nrrdAxisInfoSet_va(nraw, nrrdAxisInfoMax, AIR_NAN, cam->uRange[1], cam->vRange[1]); gstate->time = airTime(); if (parm->numThreads > 1) { gstate->workMutex = airThreadMutexNew(); airMopAdd(mop, gstate->workMutex, (airMopper)airThreadMutexNix, airMopAlways); } else { gstate->workMutex = NULL; } for (tid=0; tidnumThreads; tid++) { if (!( tstate[tid] = echoThreadStateNew() )) { biffAddf(ECHO, "%s: failed to create thread state %d", me, tid); airMopError(mop); return 1; } if (echoThreadStateInit(tid, tstate[tid], parm, gstate)) { biffAddf(ECHO, "%s: failed to initialized thread state %d", me, tid); airMopError(mop); return 1; } airMopAdd(mop, tstate[tid], (airMopper)echoThreadStateNix, airMopAlways); } fprintf(stderr, "%s: ", me); /* prep for printing airDoneStr */ gstate->workIdx = 0; for (tid=0; tidnumThreads; tid++) { if (( ret = airThreadStart(tstate[tid]->thread, _echoRTRenderThreadBody, (void *)(tstate[tid])) )) { biffAddf(ECHO, "%s: thread[%d] failed to start: %d", me, tid, ret); airMopError(mop); return 1; } } for (tid=0; tidnumThreads; tid++) { if (( ret = airThreadJoin(tstate[tid]->thread, (void **)(&(tstate[tid]->returnPtr))) )) { biffAddf(ECHO, "%s: thread[%d] failed to join: %d", me, tid, ret); airMopError(mop); return 1; } } gstate->time = airTime() - gstate->time; fprintf(stderr, "\n%s: time = %g\n", me, gstate->time); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/echo/test/0000775000175000017500000000000012203513760016270 5ustar domibeldomibelteem-1.11.0~svn6057/src/echo/test/trend.c0000664000175000017500000006322112042367142017556 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../echo.h" #include "../privateEcho.h" /* bad bad bad Gordon */ void _dyeHSVtoRGB(float *R, float *G, float *B, float H, float S, float V) { float min, fract, vsf, mid1, mid2; int sextant; if (0 == S) { *R = *G = *B = V; return; } /* else there is hue */ if (1 == H) H = 0; H *= 6; sextant = (int) floor(H); fract = H - sextant; vsf = V*S*fract; min = V*(1 - S); mid1 = min + vsf; mid2 = V - vsf; switch (sextant) { case 0: { *R = V; *G = mid1; *B = min; break; } case 1: { *R = mid2; *G = V; *B = min; break; } case 2: { *R = min; *G = V; *B = mid1; break; } case 3: { *R = min; *G = mid2; *B = V; break; } case 4: { *R = mid1; *G = min; *B = V; break; } case 5: { *R = V; *G = min; *B = mid2; break; } } } #if 0 void makeSceneAntialias(limnCamera *cam, echoRTParm *parm, echoObject **sceneP, airArray **lightArrP) { echoObject *scene, *rect; Nrrd *ntext; *sceneP = scene = echoObjectNew(echoList); *lightArrP = echoLightArrayNew(); ELL_3V_SET(cam->from, 0, 0, 10); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 1, 0); cam->uRange[0] = -3.7; cam->uRange[1] = 3.7; cam->vRange[0] = -3.7; cam->vRange[1] = 3.7; parm->jitterType = echoJitterGrid; parm->numSamples = 1; parm->imgResU = 300; parm->imgResV = 300; parm->aperture = 0.0; parm->renderLights = AIR_FALSE; parm->renderBoxes = AIR_FALSE; parm->seedRand = AIR_FALSE; parm->maxRecDepth = 10; parm->shadow = 1.0; nrrdLoad(ntext = nrrdNew(), "chirp.nrrd", NULL); rect = echoObjectNew(echoRectangle); echoRectangleSet(rect, -3, -3, 0, 6, 0, 0, 0, 6, 0); echoMatterPhongSet(rect, 1, 1, 1, 1.0, 1.0, 0.0, 0.0, 1); echoMatterTextureSet(rect, ntext); echoObjectAdd(scene, rect); return; } void makeSceneBVH(limnCamera *cam, echoRTParm *parm, echoObject **sceneP) { echoObject *sphere; int i, N; float r, g, b; echoObject *scene; double time0, time1; *sceneP = scene = echoObjectNew(echoList); ELL_3V_SET(cam->from, 9, 6, 0); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 0, 1); cam->uRange[0] = -3; cam->uRange[1] = 3; cam->vRange[0] = -3; cam->vRange[1] = 3; parm->jitterType = echoJitterNone; parm->numSamples = 1; parm->imgResU = 500; parm->imgResV = 500; parm->aperture = 0.0; parm->renderLights = AIR_TRUE; parm->renderBoxes = AIR_FALSE; parm->seedRand = AIR_FALSE; parm->maxRecDepth = 10; parm->shadow = 0.0; N = 1000000; airArrayLenSet(LIST(scene)->objArr, N); for (i=0; iobj[i] = sphere; } time0 = airTime(); *sceneP = scene = echoListSplit3(scene, 8); time1 = airTime(); printf("BVH build time = %g seconds\n", time1 - time0); } void makeSceneGlass(limnCamera *cam, echoRTParm *parm, echoObject **sceneP) { echoObject *cube, *rect; echoObject *scene; Nrrd *ntext; *sceneP = scene = echoObjectNew(echoList); ELL_3V_SET(cam->from, 2, -3, 8); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 0, 1); cam->uRange[0] = -1.0; cam->uRange[1] = 1.0; cam->vRange[0] = -1.0; cam->vRange[1] = 1.0; parm->jitterType = echoJitterNone; parm->numSamples = 1; parm->imgResU = 300; parm->imgResV = 300; parm->aperture = 0.0; parm->renderLights = AIR_FALSE; parm->shadow = 0.0; parm->seedRand = AIR_FALSE; parm->maxRecDepth = 10; parm->mrR = 1.0; parm->mrG = 0.0; parm->mrB = 1.0; cube = echoObjectNew(echoCube); printf("cube = %p\n", cube); echoMatterGlassSet(cube, 1.0, 1.0, 1.0, 1.5, 0.0, 0.0); echoObjectAdd(scene, cube); nrrdLoad(ntext=nrrdNew(), "psq.nrrd", NULL); rect = echoObjectNew(echoRectangle); printf("rect = %p\n", rect); echoRectangleSet(rect, -1, -1, -0.51, 2, 0, 0, 0, 2, 0); echoMatterPhongSet(rect, 1.0, 1.0, 1.0, 1.0, 0.1, 0.6, 0.3, 40); echoMatterTextureSet(rect, ntext); echoObjectAdd(scene, rect); /* light = echoLightNew(echoLightDirectional); echoLightDirectionalSet(light, 1, 1, 1, 0, 0, 1); echoLightArrayAdd(lightArr, light); */ } void makeSceneGlass2(limnCamera *cam, echoRTParm *parm, echoObject **sceneP) { echoObject *cube, *rect; echoObject *scene; Nrrd *ntext; echoPos_t matx[16]; *sceneP = scene = echoObjectNew(echoList); ELL_3V_SET(cam->from, 0, 0, 100); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 1, 0); cam->uRange[0] = -1.0; cam->uRange[1] = 1.0; cam->vRange[0] = -1.0; cam->vRange[1] = 1.0; parm->jitterType = echoJitterNone; parm->numSamples = 1; parm->imgResU = 300; parm->imgResV = 300; parm->aperture = 0.0; parm->renderLights = AIR_FALSE; parm->shadow = 0.0; parm->seedRand = AIR_FALSE; parm->maxRecDepth = 10; parm->mrR = 1.0; parm->mrG = 0.0; parm->mrB = 1.0; ELL_4M_SET_SCALE(matx, 0.5, 0.5, 0.5); cube = echoRoughSphere(80, 40, matx); /* cube = echoObjectNew(echoSphere); echoSphereSet(cube, 0, 0, 0, 0.5); */ echoMatterGlassSet(cube, 1.0, 1.0, 1.0, 1.33333, 0.0, 0.0); echoObjectAdd(scene, cube); nrrdLoad(ntext=nrrdNew(), "check.nrrd", NULL); rect = echoObjectNew(echoRectangle); printf("rect = %p\n", rect); echoRectangleSet(rect, -1, -1, -0.51, 2, 0, 0, 0, 2, 0); echoMatterPhongSet(rect, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 40); echoMatterTextureSet(rect, ntext); echoObjectAdd(scene, rect); /* light = echoLightNew(echoLightDirectional); echoLightDirectionalSet(light, 1, 1, 1, 0, 0, 1); echoLightArrayAdd(lightArr, light); */ } #endif void makeSceneInstance(limnCamera *cam, echoRTParm *parm, echoScene *scene) { echoObject *trim, *rect, *inst; echoPos_t matx[16], A[16], B[16]; ELL_3V_SET(cam->from, 9*1.3, 9*1.3, 11*1.3); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 0, 1); cam->uRange[0] = -5; cam->uRange[1] = 5; cam->vRange[0] = -5; cam->vRange[1] = 5; parm->jitterType = echoJitterNone; parm->numSamples = 1; parm->imgResU = 300; parm->imgResV = 300; parm->aperture = 0.0; parm->renderLights = AIR_TRUE; parm->renderBoxes = AIR_FALSE; parm->seedRand = AIR_FALSE; parm->maxRecDepth = 10; parm->shadow = 1.0; ELL_4M_IDENTITY_SET(matx); ELL_4M_SCALE_SET(B, 2.5, 1.5, 0.8); ELL_4M_MUL(A, B, matx); ELL_4M_COPY(matx, A); ELL_4M_ROTATE_X_SET(B, 0.2); ELL_4M_MUL(A, B, matx); ELL_4M_COPY(matx, A); ELL_4M_ROTATE_Y_SET(B, 0.2); ELL_4M_MUL(A, B, matx); ELL_4M_COPY(matx, A); ELL_4M_ROTATE_Y_SET(B, 0.2); ELL_4M_MUL(A, B, matx); ELL_4M_COPY(matx, A); ELL_4M_TRANSLATE_SET(B, 0, 0, 1); ELL_4M_MUL(A, B, matx); ELL_4M_COPY(matx, A); /* trim = echoRoughSphere(50, 25, matx); */ /* trim = echoRoughSphere(8, 4, matx); echoMatterGlassSet(trim, 0.8, 0.8, 0.8, 1.3, 0.0, 0.0); echoMatterPhongSet(trim, 1, 1, 1, 1.0, 0.1, 0.5, 0.9, 50); echoObjectAdd(scene, trim); */ trim = echoObjectNew(scene, echoTypeSphere); echoSphereSet(trim, 0, 0, 0, 1); echoColorSet(trim, 0.8, 0.8, 0.8, 1.0); echoMatterGlassSet(scene, trim, 1.3, 0.0, 0.0, 0.0); echoMatterPhongSet(scene, trim, 0.1, 0.5, 0.9, 50); inst = echoObjectNew(scene, echoTypeInstance); echoInstanceSet(inst, matx, trim); echoObjectAdd(scene, inst); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -3.5, -3.5, -3.5, 7, 0, 0, 0, 7, 0); echoColorSet(trim, 1.0, 1.0, 1.0, 1.0); echoMatterPhongSet(scene, rect, 0.1, 0.5, 0.9, 50); echoObjectAdd(scene, rect); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -3.5, -3.5, -3.5, 0, 7, 0, 0, 0, 7); echoColorSet(rect, 1.0, 1.0, 1.0, 1.0); echoMatterPhongSet(scene, rect, 0.1, 0.5, 0.9, 50); echoObjectAdd(scene, rect); /* rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -3.5, -3.5, -3.5, 0, 0, 7, 7, 0, 0); */ rect = echoObjectNew(scene, echoTypeSphere); echoSphereSet(rect, 0, 0, 0, 1); echoColorSet(rect, 1.0, 1.0, 1.0, 1.0); echoMatterPhongSet(scene, rect, 0.1, 0.5, 0.9, 50); inst = echoObjectNew(scene, echoTypeInstance); ELL_4M_SCALE_SET(A, 20, 20, 20); ELL_4M_TRANSLATE_SET(B, 0, -(20+3.5), 0); ELL_4M_MUL(matx, B, A); echoInstanceSet(inst, matx, rect); echoObjectAdd(scene, inst); /* light = echoLightNew(echoLightDirectional); echoLightDirectionalSet(light, 1, 0, 0, 1, 0.001, 0.001); echoLightArrayAdd(lightArr, light); light = echoLightNew(echoLightDirectional); echoLightDirectionalSet(light, 0, 1, 0, 0.001, 1, 0.001); echoLightArrayAdd(lightArr, light); light = echoLightNew(echoLightDirectional); echoLightDirectionalSet(light, 0, 0, 1, 0.001, 0.001, 1); echoLightArrayAdd(lightArr, light); */ return; } void makeSceneGlassTest(limnCamera *cam, echoRTParm *parm, echoScene *scene) { echoObject *cube, *rect, *inst; echoCol_t r=0, g=0, b=0; Nrrd *ntext; int i, N; echoPos_t ma[16], mb[16]; ELL_3V_SET(cam->from, 0, 0, 10); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 1, 0, 0); cam->uRange[0] = -1.1; cam->uRange[1] = 1.1; cam->vRange[0] = -1.1; cam->vRange[1] = 1.1; parm->jitterType = echoJitterNone; parm->numSamples = 1; parm->imgResU = 220; parm->imgResV = 220; parm->aperture = 0.0; parm->renderLights = AIR_FALSE; parm->seedRand = AIR_FALSE; ELL_3V_SET(scene->bkgr, 0.2, 0.3, 0.4); /* parm->shadow = 0.0; */ /* create scene */ N = 11; for (i=0; ifrom, 4, 0, 5); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, -1, 0, 0); cam->uRange[0] = -0.7; cam->uRange[1] = 0.7; cam->vRange[0] = -0.0; cam->vRange[1] = 1.4; parm->jitterType = echoJitterJitter; parm->numSamples = 36; parm->imgResU = 220; parm->imgResV = 220; parm->aperture = 0.0; parm->renderLights = AIR_FALSE; parm->seedRand = AIR_FALSE; ELL_3V_SET(scene->bkgr, 0.2, 0.3, 0.4); /* create scene */ sphere = echoObjectNew(scene, echoTypeSphere); echoSphereSet(sphere, 0.70, -0.3, -0.4, 0.1); echoColorSet(sphere, 1, 0, 0, 1.0); echoMatterPhongSet(scene, sphere, 0.1, 0.6, 0.3, 40); echoObjectAdd(scene, sphere); sphere = echoObjectNew(scene, echoTypeSphere); echoSphereSet(sphere, 0.66, 0.0, -0.4, 0.1); echoColorSet(sphere, 0, 1, 0, 1.0); echoMatterGlassSet(scene, sphere, 1.0, 0, 1.0, 0.0); echoMatterPhongSet(scene, sphere, 0.1, 0.6, 0.3, 40); echoObjectAdd(scene, sphere); sphere = echoObjectNew(scene, echoTypeSphere); echoSphereSet(sphere, 0.62, 0.3, -0.4, 0.1); echoColorSet(sphere, 0, 0, 1, 1.0); echoMatterPhongSet(scene, sphere, 0.1, 0.6, 0.3, 40); echoObjectAdd(scene, sphere); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, 0.5, 0.5, 0.5, 0, -1, 0, 0, 0, -1); echoColorSet(rect, 1.0, 1.0, 1.0, 1.0); echoMatterMetalSet(scene, rect, 0.7, 0.0, 0.0, 0.2); echoObjectAdd(scene, rect); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -1.5, -1.5, -1, 3, 0, 0, 0, 3, 0); echoColorSet(rect, 1.0, 1.0, 1.0, 1.0); echoMatterPhongSet(scene, rect, 0.1, 0.6, 0.3, 40); echoObjectAdd(scene, rect); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, 0.5-0.2, -0.2, 40, 0.4, 0, 0, 0, 0.4, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, 0); return; } void makeSceneTexture(limnCamera *cam, echoRTParm *parm, echoScene *scene) { echoObject /* *trim, */ *rect, /* *inst, */ *sphere; Nrrd *ntext; ELL_3V_SET(cam->from, 9, 9, 11); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 0, 1); cam->uRange[0] = -4; cam->uRange[1] = 4; cam->vRange[0] = -4; cam->vRange[1] = 4; parm->jitterType = echoJitterNone; parm->numSamples = 1; parm->imgResU = 300; parm->imgResV = 300; parm->aperture = 0.0; parm->renderLights = AIR_FALSE; parm->renderBoxes = AIR_FALSE; parm->seedRand = AIR_FALSE; parm->maxRecDepth = 10; parm->shadow = 1.0; rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -2, -2, 0, 4, 0, 0, 0, 4, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterPhongSet(scene, rect, 0.1, 1, 0.9, 50); echoObjectAdd(scene, rect); nrrdLoad(ntext=nrrdNew(), "tmp.png", NULL); echoMatterTextureSet(scene, rect, ntext); sphere = echoObjectNew(scene, echoTypeSphere); echoSphereSet(sphere, 0, 0, 0, 3); echoColorSet(sphere, 1, 1, 1, 1); echoMatterPhongSet(scene, sphere, 0.1, 0.5, 0.9, 50); echoMatterTextureSet(scene, sphere, ntext); echoObjectAdd(scene, sphere); /* ELL_4M_SET_SCALE(matx, 3, 3, 3); trim = echoRoughSphere(80, 40, matx); echoMatterPhongSet(trim, 1, 1, 1, 1.0, 0.1, 0.5, 0.9, 50); echoMatterTextureSet(trim, ntext); echoObjectAdd(scene, trim); */ rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, 0, 0, 60, 0, 2, 0, 0, 0, 2); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, 1); return; } void makeSceneDOF(limnCamera *cam, echoRTParm *parm, echoScene *scene) { echoObject *rect; Nrrd *ntext; ELL_3V_SET(cam->from, 6, 6, 20); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 1, 0); cam->uRange[0] = -3.3; cam->uRange[1] = 3.3; cam->vRange[0] = -3.3; cam->vRange[1] = 3.3; parm->jitterType = echoJitterJitter; parm->numSamples = 4; parm->imgResU = 300; parm->imgResV = 300; parm->aperture = 0.5; parm->renderLights = AIR_FALSE; parm->renderBoxes = AIR_FALSE; parm->seedRand = AIR_FALSE; parm->maxRecDepth = 10; parm->shadow = 1.0; nrrdLoad(ntext = nrrdNew(), "tmp.png", NULL); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -0.5, 1.5, -3, 2, 0, 0, 0, -2, 0); echoColorSet(rect, 1, 0.5, 0.5, 1); echoMatterPhongSet(scene, rect, 1.0, 0.0, 0.0, 1); echoMatterTextureSet(scene, rect, ntext); echoObjectAdd(scene, rect); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -1, 1, 0, 2, 0, 0, 0, -2, 0); echoColorSet(rect, 0.5, 1, 0.5, 1); echoMatterPhongSet(scene, rect, 1.0, 0.0, 0.0, 1); echoMatterTextureSet(scene, rect, ntext); echoObjectAdd(scene, rect); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -1.5, 0.5, 3, 2, 0, 0, 0, -2, 0); echoColorSet(rect, 0.5, 0.5, 1, 1); echoMatterPhongSet(scene, rect, 1.0, 0.0, 0.0, 1); echoMatterTextureSet(scene, rect, ntext); echoObjectAdd(scene, rect); return; } void makeSceneShadow(limnCamera *cam, echoRTParm *parm, echoScene *scene) { echoObject *sphere, *rect, *tri; ELL_3V_SET(cam->from, 2, 0, 20); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, -1, 0, 0); cam->uRange[0] = -1.8; cam->uRange[1] = 1.8; cam->vRange[0] = -1.8; cam->vRange[1] = 1.8; parm->jitterType = echoJitterGrid; parm->numSamples = 9; parm->imgResU = 200; parm->imgResV = 200; parm->aperture = 0.0; parm->renderLights = AIR_FALSE; parm->shadow = 0.5; /* create scene */ sphere = echoObjectNew(scene, echoTypeSphere); echoSphereSet(sphere, 0, -1, -1, 0.2); echoColorSet(sphere, 0.5, 0.5, 1, 1.0); echoMatterPhongSet(scene, sphere, 0.1, 0.6, 0.3, 40); echoObjectAdd(scene, sphere); sphere = echoObjectNew(scene, echoTypeSphere); echoSphereSet(sphere, 0, 1, -1, 0.2); echoColorSet(sphere, 1, 0.5, 0.5, 1.0); echoMatterPhongSet(scene, sphere, 0.1, 0.6, 0.3, 40); echoObjectAdd(scene, sphere); sphere = echoObjectNew(scene, echoTypeSphere); echoSphereSet(sphere, 0, 0, 1, 0.2); echoColorSet(sphere, 0.5, 1, 0.5, 1.0); echoMatterPhongSet(scene, sphere, 0.1, 0.6, 0.3, 40); echoObjectAdd(scene, sphere); tri = echoObjectNew(scene, echoTypeTriangle); echoTriangleSet(tri, 0, -1, -1, 0, 1, -1, 0, 0, 1); echoColorSet(tri, 1, 1, 0, 1.0); echoMatterPhongSet(scene, tri, 0.1, 0.6, 0.3, 40); echoObjectAdd(scene, tri); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, 1.7, 1.7, -2, -3.4, 0, 0, 0, -3.4, 0); echoColorSet(rect, 1.0, 0.8, 1.0, 1.0); echoMatterPhongSet(scene, rect, 0.1, 0.3, 0.7, 3000); echoObjectAdd(scene, rect); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, 1.0, 0.2, 4, 0.2, 0, 0, 0, 0.2, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, 0); echoObjectAdd(scene, rect); } void makeSceneSimple(limnCamera *cam, echoRTParm *parm, echoScene *scene) { echoObject *tri, *rect, *sphere; Nrrd *ntext; ELL_3V_SET(cam->from, 5, -5, 9); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 0, 1); cam->uRange[0] = -3.6; cam->uRange[1] = 3.6; cam->vRange[0] = -3.6; cam->vRange[1] = 3.6; parm->jitterType = echoJitterJitter; parm->numSamples = 9; parm->imgResU = 300; parm->imgResV = 300; parm->aperture = 0.0; parm->textureNN = AIR_FALSE; parm->renderLights = AIR_TRUE; parm->renderBoxes = AIR_FALSE; parm->seedRand = AIR_TRUE; parm->maxRecDepth = 10; ELL_3V_SET(parm->maxRecCol, 0, 0, 0); parm->shadow = 1.0; rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -5, -5, -1.4, 10, 0, 0, 0, 10, 0); echoColorSet(rect, 1, 1, 1, 1.0); echoMatterPhongSet(scene, rect, 0.1, 0.5, 0.6, 50); if (nrrdLoad(ntext=nrrdNew(), "pot.png", NULL)) { /* oops, no pot */ airFree(biffGetDone(NRRD)); nrrdNuke(ntext); } else { echoMatterTextureSet(scene, rect, ntext); } echoObjectAdd(scene, rect); sphere = echoObjectNew(scene, echoTypeSphere); echoSphereSet(sphere, 0, 0, 0, 1.85); echoColorSet(sphere, 1, 1, 1, 1.0); echoMatterGlassSet(scene, sphere, 1.5, 0.0, 0.0, 0.0); echoMatterMetalSet(scene, sphere, 0.8, 0.0, 1.0, 0.15); echoObjectAdd(scene, sphere); tri = echoObjectNew(scene, echoTypeTriangle); echoTriangleSet(tri, 0.1, 0.1, 2, 2, 2, 2, 0, 2, 2); echoColorSet(tri, 1, 0.4, 0.4, 1.0); echoMatterPhongSet(scene, tri, 0.4, 0.6, 0.0, 90); echoObjectAdd(scene, tri); tri = echoObjectNew(scene, echoTypeTriangle); echoTriangleSet(tri, -0.1, 0.1, 2, -2, 2, 2, -2, 0, 2); echoColorSet(tri, 0.4, 1.0, 0.4, 1.0); echoMatterPhongSet(scene, tri, 0.4, 0.6, 0.0, 90); echoObjectAdd(scene, tri); tri = echoObjectNew(scene, echoTypeTriangle); echoTriangleSet(tri, -0.1, -0.1, 2, -2, -2, 2, 0, -2, 2); echoColorSet(tri, 0.4, 0.4, 1.0, 1.0); echoMatterPhongSet(scene, tri, 0.4, 0.6, 0.0, 90); echoObjectAdd(scene, tri); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -0.5, -0.5, 10, 1.0, 0.0, 0, 0.0, 1.0, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, 0); echoObjectAdd(scene, rect); return; } void makeSceneRainLights(limnCamera *cam, echoRTParm *parm, echoScene *scene) { echoObject *sphere, *rect; int i, N; echoPos_t w; float r=0, g=0, b=0; ELL_3V_SET(cam->from, 2.5, 0, 5); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 0, 1); cam->uRange[0] = -1.7; cam->uRange[1] = 1.7; cam->vRange[0] = -1.7; cam->vRange[1] = 1.7; parm->jitterType = echoJitterJitter; parm->numSamples = 36; parm->imgResU = 1000; parm->imgResV = 1000; parm->numSamples = 16; parm->imgResU = 200; parm->imgResV = 200; parm->aperture = 0.0; parm->renderLights = AIR_TRUE; parm->shadow = 0.0; ELL_3V_SET(scene->bkgr, 0.1, 0.1, 0.1); /* create scene */ sphere = echoObjectNew(scene, echoTypeSphere); echoSphereSet(sphere, 0, 0, 0, 1.0); echoColorSet(sphere, 1.0, 1.0, 1.0, 1.0); echoMatterPhongSet(scene, sphere, 0.02, 0.2, 1.0, 400); echoObjectAdd(scene, sphere); N = 8; w = 1.7/N; for (i=0; ineer = 0; cam->dist = 0; cam->faar = 0; cam->atRelative = AIR_TRUE; cam->dist = 0; cam->rightHanded = AIR_TRUE; parm = echoRTParmNew(); airMopAdd(mop, parm, (airMopper)echoRTParmNix, airMopAlways); state = echoGlobalStateNew(); airMopAdd(mop, state, (airMopper)echoGlobalStateNix, airMopAlways); scene = echoSceneNew(); airMopAdd(mop, scene, (airMopper)echoSceneNix, airMopAlways); nraw = nrrdNew(); airMopAdd(mop, nraw, (airMopper)nrrdNuke, airMopAlways); /* makeSceneGlass(cam, parm, scene); */ /* makeSceneGlass2(cam, parm, scene); */ /* makeSceneGlassMetal(cam, parm, scene); */ /* makeSceneGlassTest(cam, parm, scene); */ /* makeSceneBVH(cam, parm, scene); */ /* makeSceneInstance(cam, parm, scene); */ /* makeSceneTexture(cam, parm, scene); */ /* makeSceneSimple(cam, parm, scene); */ /* makeSceneRainLights(cam, parm, scene); */ /* makeSceneAntialias(cam, parm, scene); */ makeSceneShadow(cam, parm, scene); /* makeSceneDOF(cam, parm, scene); */ if ((env = getenv("NT"))) { if (1 == sscanf(env, "%d", &tmp)) { parm->numThreads = tmp; } } else { parm->numThreads = 1; } E = 0; if (!E) E |= echoRTRender(nraw, cam, scene, parm, state); if (E) { airMopAdd(mop, err = biffGetDone(ECHO), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } printf("render time = %g seconds (%g fps)\n", state->time, 1.0/state->time); if (!E) E |= nrrdSave("raw.nrrd", nraw, NULL); if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/echo/test/glyph.c0000664000175000017500000003250312042367142017564 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../echo.h" #include "../privateEcho.h" #include #include typedef struct { int aniso; float anthr, gscale, lrad, ldref, llev, show, lpos[2]; } EchoGlyphParm; void rgbGen(echoCol_t rgb[3], float evec[3], float an) { ELL_3V_ABS(rgb, evec); rgb[0] = AIR_AFFINE(0.0, an, 1.0, 0.5, rgb[0]); rgb[1] = AIR_AFFINE(0.0, an, 1.0, 0.5, rgb[1]); rgb[2] = AIR_AFFINE(0.0, an, 1.0, 0.5, rgb[2]); } /* changed in ELL, below is only usage */ #define NOTELL_3V_AFFINE(v,i,x,I,o,O) ( \ (v)[0] = AIR_AFFINE((i)[0],(x)[0],(I)[0],(o)[0],(O)[0]), \ (v)[1] = AIR_AFFINE((i)[1],(x)[1],(I)[1],(o)[1],(O)[1]), \ (v)[2] = AIR_AFFINE((i)[2],(x)[2],(I)[2],(o)[2],(O)[2])) void makeGlyphScene(limnCam *cam, EchoParm *eparm, Nrrd *nten, EchoGlyphParm *gparm, EchoObject **sceneP, airArray **lightArrP) { char me[]="makeGlyphScene"; int xi, yi, zi, sx, sy, sz, ng; echoPos_t x, y, z, dmat[9], MA[16], MB[16], MC[16], ident[9]; echoCol_t rgb[3]; EchoObject *glf, *inst; float *tdata, xs, ys, zs, eval[3], evec[9], c[TEN_ANISO_MAX+1], imin[3], imax[3], omin[3], omax[3]; EchoObject *scene, *rect; EchoLight *light; scene = echoObjectNew(echoObjectList); *lightArrP = echoLightArrayNew(); eparm->bgR = 0.5; eparm->bgG = 0.5; eparm->bgB = 0.5; eparm->renderLights = AIR_FALSE; eparm->renderBoxes = AIR_FALSE; sx = nten->axis[1].size; sy = nten->axis[2].size; sz = nten->axis[3].size; xs = nten->axis[1].spacing; ys = nten->axis[2].spacing; zs = nten->axis[3].spacing; tdata = nten->data; /* we specifically requested float below */ ELL_3V_SET(imin, -1, -1, -1); ELL_3V_SET(imax, 1, 1, 1); ELL_3V_SET(omin, 0, 0, 0); ELL_3V_SET(omax, xs*(sx-1), ys*(sy-1), zs*(sz-1)); NOTELL_3V_AFFINE(cam->from, imin, cam->from, imax, omin, omax); NOTELL_3V_AFFINE(cam->at, imin, cam->at, imax, omin, omax); ng = 0; for (zi=0; zi 0.5 )) goto onward; /* do eigensystem solve; don't render if aniso threshold not met */ tenEigensolve(eval, evec, tdata); tenAnisoCalc(c, eval); if (!( c[gparm->aniso] > gparm->anthr )) goto onward; rgbGen(rgb, evec, c[gparm->aniso]); if (1) { glf = echoObjectNew(echoObjectSphere); echoObjectSphereSet(glf, 0, 0, 0, 0.5); } else { glf = echoObjectNew(echoObjectCube); } echoMatterMetalSet(glf, rgb[0], rgb[1], rgb[2], 0.8, 1.0, 0.0); echoMatterPhongSet(glf, rgb[0], rgb[1], rgb[2], 1.0, 0.1, 1.0, 0.3, 1); TEN_LIST2MAT(dmat, tdata); ELL_3M_SET_IDENTITY(ident); ELL_3M_SCALEADD(dmat, gparm->gscale*(1-gparm->show), ident, gparm->gscale*gparm->show, dmat); ELL_43M_INSET(MA, dmat); ELL_4M_SET_TRANSLATE(MB, x, y, z); ELL_4M_MUL(MC, MB, MA); inst = echoObjectNew(echoObjectInstance); echoObjectInstanceSet(inst, MC, glf, AIR_TRUE); echoObjectListAdd(scene, inst); ng++; onward: tdata += 7; } } } /* something to cast a shadow on glf = echoObjectNew(echoObjectSphere); echoObjectSphereSet(glf, 35+1000, 40, 35, 1000); echoMatterPhongSet(glf, 1, 1, 1, 1.0, 0.1, 1.0, 0.3, 1); inst = echoObjectNew(echoObjectInstance); echoObjectInstanceSet(inst, MC, glf, AIR_TRUE); echoObjectListAdd(scene, inst); */ printf("%s: generated %d glyphs\n", me, ng); if (gparm->lrad) { rect = echoObjectNew(echoObjectRectangle); echoObjectRectangleSet(rect, AIR_AFFINE(0, gparm->lpos[0], 1, 0, sx*xs) - gparm->lrad, AIR_AFFINE(0, gparm->lpos[1], 1, 0, sy*ys) - gparm->lrad, 0, 2*gparm->lrad, 0, 0, 0, 2*gparm->lrad, 0); eparm->refDistance = sz*(gparm->ldref); printf("llev = %g\n", gparm->llev); echoMatterLightSet(rect, gparm->llev, gparm->llev, gparm->llev); echoObjectListAdd(scene, rect); light = echoLightNew(echoLightArea); echoLightAreaSet(light, rect); echoLightArrayAdd(*lightArrP, light); } else { light = echoLightNew(echoLightDirectional); echoLightDirectionalSet(light, 1, 1, 1, AIR_AFFINE(0, gparm->lpos[0], 1, -sx*xs, sx*xs), AIR_AFFINE(0, gparm->lpos[1], 1, -sy*ys, sy*ys), AIR_AFFINE(0, 0, 1, -sz*zs, sz*zs)); echoLightArrayAdd(*lightArrP, light); } if (1) { printf("%s: making BVH ... ", me); fflush(stdout); *sceneP = echoObjectListSplit3(scene, 8); printf("done\n"); } else { *sceneP = scene; } return; } int echoParseTenNrrd(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[] = "echoParseTenNrrd", *nerr; Nrrd **nrrdP; airArray *mop; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } nrrdP = ptr; mop = airMopNew(); *nrrdP = nrrdNew(); airMopAdd(mop, *nrrdP, (airMopper)nrrdNuke, airMopOnError); if (nrrdLoad(*nrrdP, str)) { airMopAdd(mop, nerr = biffGetDone(NRRD), airFree, airMopOnError); if (strlen(nerr) > AIR_STRLEN_HUGE - 1) nerr[AIR_STRLEN_HUGE - 1] = '\0'; strcpy(err, nerr); airMopError(mop); return 1; } if (!tenValidTensor(*nrrdP, nrrdTypeFloat, AIR_TRUE)) { /* why not use the given err[] as a temp buffer */ sprintf(err, "%s: \"%s\" isn't a valid tensor volume", me, str); biffAdd(TEN, err); airMopAdd(mop, nerr = biffGetDone(TEN), airFree, airMopOnError); if (strlen(nerr) > AIR_STRLEN_HUGE - 1) nerr[AIR_STRLEN_HUGE - 1] = '\0'; strcpy(err, nerr); airMopError(mop); return 1; } if (!( AIR_EXISTS((*nrrdP)->axis[1].spacing) && AIR_EXISTS((*nrrdP)->axis[2].spacing) && AIR_EXISTS((*nrrdP)->axis[3].spacing) )) { sprintf(err, "%s: need existent spacings on x,y,z axes", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } hestCB echoParseTenNrrdCB = { sizeof(Nrrd *), "nrrd", echoParseTenNrrd, (airMopper)nrrdNuke }; int main(int argc, char *argv[]) { airArray *mop, *lightArr; hestOpt *opt = NULL; limnCam *cam; hestParm *hparm; Nrrd *nten, *nraw, *nimg, *nppm, *ntmp, *npgm; echoPos_t ur[2], vr[2]; int E, is[2]; char *me, *outS, *err, info[] = "Generates cool images of tensor glyphs"; EchoParm *eparm; EchoGlobalState *state; EchoObject *scene; EchoGlyphParm gparm; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hparm->respFileEnable = AIR_TRUE; hparm->verbosity = 0; cam = echoLimnCamNew(); airMopAdd(mop, cam, (airMopper)limnCamNix, airMopAlways); cam->neer = 0; cam->dist = 0; cam->faar = 0; cam->atRel = AIR_TRUE; state = echoGlobalStateNew(); airMopAdd(mop, state, (airMopper)echoGlobalStateNix, airMopAlways); eparm = echoParmNew(); airMopAdd(mop, eparm, (airMopper)echoParmNix, airMopAlways); hestOptAdd(&opt, "i", "nin", airTypeOther, 1, 1, &nten, NULL, "diffusion tensor data", NULL, NULL, &echoParseTenNrrdCB); hestOptAdd(&opt, "fr", "eye point", airTypeFloat, 3, 3, cam->from, "40 40 40", "camera eye point"); hestOptAdd(&opt, "at", "lookat", airTypeFloat, 3, 3, cam->at, "0 0 0", "camera look-at point"); hestOptAdd(&opt, "up", "up", airTypeFloat, 3, 3, cam->up, "0 0 1", "camera pseudo up vector"); hestOptAdd(&opt, "ur", "U range", echoPos_airType, 2, 2, ur, "-20 20", "range in U direction of image plane"); hestOptAdd(&opt, "vr", "V range", echoPos_airType, 2, 2, vr, "-20 20", "range in V direction of image plane"); hestOptAdd(&opt, "is", "image size", airTypeInt, 2, 2, is, "256 256", "image dimensions"); hestOptAdd(&opt, "ns", "# samples", airTypeInt, 1, 1, &(eparm->samples), "1", "# of samples per pixel (1 --> no jitter)"); hestOptAdd(&opt, "o", "output", airTypeString, 1, 1, &outS, "out.ppm", "PPM image output"); hestOptAdd(&opt, "ap", "aperture", airTypeFloat, 1, 1, &(eparm->aperture), "0.0", "camera aperture (0.0 --> no depth of field)"); hestOptAdd(&opt, "an", "aniso", airTypeEnum, 1, 1, &(gparm.aniso), "fa", "which anisotropy metric", NULL, &tenAniso); hestOptAdd(&opt, "th", "thresh", airTypeFloat, 1, 1, &(gparm.anthr), "0.8", "threshold on anisotropy"); hestOptAdd(&opt, "gs", "scale", airTypeFloat, 1, 1, &(gparm.gscale), "0.8", "over-all scaling on all glyphs"); hestOptAdd(&opt, "gh", "show", airTypeFloat, 1, 1, &(gparm.show), "1.0", "how much to \"show\" the data:\n " "0.0 --> all identity; 1.0 --> all data"); hestOptAdd(&opt, "lr", "light radius", airTypeFloat, 1, 1, &(gparm.lrad), "5", "\"radius\" of area light over volume"); hestOptAdd(&opt, "ld", "ref dist", airTypeFloat, 1, 1, &(gparm.ldref), "0.5", "\"reference distance\" for light, " "expressed as a fraction of Z dimension of volume"); hestOptAdd(&opt, "ll", "light level", airTypeFloat, 1, 1, &(gparm.llev), "1", "area light intensity"); hestOptAdd(&opt, "lp", "light position", airTypeFloat, 2, 2, &(gparm.lpos), "0.5 0.5", "area light position"); hestOptAdd(&opt, "sh", NULL, airTypeInt, 0, 0, &(eparm->shadow), NULL, "render shadows"); if (hestOptCheck(opt, &err)) { printf("%s\n", err); exit(1); } if (1 == argc) { hestInfo(stderr, argv[0], info, hparm); hestUsage(stderr, opt, argv[0], hparm); hestGlossary(stderr, opt, hparm); opt = hestOptFree(opt); hparm = hestParmFree(hparm); exit(1); } /* parse command line */ if (hestParse(opt, argc-1, argv+1, &err, hparm)) { fprintf(stderr, "ERROR: %s\n", err); free(err); hestUsage(stderr, opt, argv[0], hparm); hestGlossary(stderr, opt, hparm); opt = hestOptFree(opt); hparm = hestParmFree(hparm); exit(1); } /* finish dealing with parsed information */ cam->uRange[0] = ur[0]; cam->uRange[1] = ur[1]; cam->vRange[0] = vr[0]; cam->vRange[1] = vr[1]; eparm->imgResU = is[0]; eparm->imgResV = is[1]; eparm->jitter = (1 == eparm->samples ? echoJitterNone : echoJitterJitter); /* do the glyph thing */ makeGlyphScene(cam, eparm, nten, &gparm, &scene, &lightArr); /* render */ nraw = nrrdNew(); nimg = nrrdNew(); nppm = nrrdNew(); ntmp = nrrdNew(); npgm = nrrdNew(); airMopAdd(mop, nraw, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nimg, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nppm, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, npgm, (airMopper)nrrdNuke, airMopAlways); E = 0; printf("%s: rendering (%d samples)... ", me, eparm->samples); fflush(stdout); if (!E) E |= echoRender(nraw, cam, eparm, state, scene, lightArr); printf("done.\n"); if (!E) E |= echoComposite(nimg, nraw, eparm); if (!E) E |= echoPPM(nppm, nimg, eparm); if (E) { airMopAdd(mop, err = biffGetDone(ECHO), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } printf("%s: render time = %g seconds (%g fps)\n", me, state->time, 1.0/state->time); if (!E) E |= nrrdSave("raw.nrrd", nraw, NULL); if (!E) E |= nrrdSave("out.ppm", nppm, NULL); if (!E) E |= nrrdSlice(ntmp, nraw, 0, 3); ntmp->min = 0.0; ntmp->max = 1.0; if (!E) E |= nrrdQuantize(npgm, ntmp, 8); if (!E) E |= nrrdSave("alpha.pgm", npgm, NULL); if (!E) E |= nrrdSlice(ntmp, nraw, 0, 4); if (!E) E |= nrrdHistoEq(ntmp, ntmp, NULL, 2048, 2); if (!E) E |= nrrdQuantize(npgm, ntmp, 8); if (!E) E |= nrrdSave("time.pgm", npgm, NULL); if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); hestParseFree(opt); opt = hestOptFree(opt); hparm = hestParmFree(hparm); exit(0); } teem-1.11.0~svn6057/src/echo/test/test.c0000664000175000017500000001123012042367142017412 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../echo.h" int main(int argc, char **argv) { char *me, *err; echoScene *scene; echoObject *sph, *rect, *list, *split; Nrrd *nraw; limnCamera *cam; echoRTParm *parm; echoGlobalState *gstate; airArray *mop; int I; float R, G, B; AIR_UNUSED(argc); me = argv[0]; mop = airMopNew(); scene = echoSceneNew(); airMopAdd(mop, scene, (airMopper)echoSceneNix, airMopAlways); list = echoObjectNew(scene, echoTypeList); for (I=0; I<70; I++) { sph = echoObjectNew(scene, echoTypeSphere); R = airDrandMT(); G = airDrandMT(); B = airDrandMT(); echoSphereSet(sph, AIR_AFFINE(0, R, 1, -0.8, 0.8), AIR_AFFINE(0, G, 1, -0.8, 0.8), AIR_AFFINE(0, B, 1, -0.8, 0.8), 0.15); echoColorSet(sph, R, G, B, 1.0); echoMatterPhongSet(scene, sph, 0, 1, 0, 40); echoListAdd(list, sph); } split = echoListSplit3(scene, list, 10); echoObjectAdd(scene, split); /* rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -1, -1, -1, 2, 0, 0, 0, 2, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterPhongSet(scene, rect, 0, 1, 0, 40); echoObjectAdd(scene, rect); */ rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -2, -2, 2, 0, 4, 0, 4, 0, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, 2); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -2, -2, -2, 4, 0, 0, 0, 4, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, 2); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -2, 2, -2, 4, 0, 0, 0, 0, 4); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, 2); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -2, -2, -2, 0, 0, 4, 4, 0, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, 2); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, -2, -2, -2, 0, 4, 0, 0, 0, 4); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, 2); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, 2, -2, -2, 0, 0, 4, 0, 4, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, 2); nraw = nrrdNew(); cam = limnCameraNew(); parm = echoRTParmNew(); gstate = echoGlobalStateNew(); airMopAdd(mop, nraw, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, cam, (airMopper)limnCameraNix, airMopAlways); airMopAdd(mop, parm, (airMopper)echoRTParmNix, airMopAlways); airMopAdd(mop, gstate, (airMopper)echoGlobalStateNix, airMopAlways); ELL_3V_SET(cam->from, 20, 20, 20); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 0, 1); cam->neer = -2; cam->dist = 0; cam->faar = 2; cam->atRelative = AIR_TRUE; cam->rightHanded = AIR_TRUE; cam->uRange[0] = -1.6; cam->vRange[0] = -1.6; cam->uRange[1] = 1.6; cam->vRange[1] = 1.6; parm->imgResU = parm->imgResV = 300; parm->numSamples = 100; parm->jitterType = echoJitterJitter; parm->aperture = 0.0; parm->renderBoxes = AIR_FALSE; if (echoRTRender(nraw, cam, scene, parm, gstate)) { airMopAdd(mop, err = biffGetDone(ECHO), airFree, airMopAlways); fprintf(stderr, "%s: %s\n", me, err); airMopError(mop); return 1; } nrrdSave("nraw.nrrd", nraw, NULL); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/echo/TODO.txt0000664000175000017500000000017307711264523016630 0ustar domibeldomibelz-depth to image channels debug glass (tri-mesh of slab vs slab as instanced cube) motion blur alpha in image texture maps teem-1.11.0~svn6057/src/echo/privateEcho.h0000664000175000017500000000776612165631065017761 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ECHO_PRIVATE_HAS_BEEN_INCLUDED #define ECHO_PRIVATE_HAS_BEEN_INCLUDED #ifdef __cplusplus extern "C" { #endif #if ECHO_POS_FLOAT # define echoPos_nt nrrdTypeFloat # define echoPos_at airTypeFloat # define ell_4m_INV ell_4m_inv_f # define ell_4m_PRINT ell_4m_print_f # define ell_4m_DET ell_4m_det_f # define ell_3v_PERP ell_3v_perp_f # define ell_4m_POST_MUL ell_4m_post_mul_f # define ECHO_POS_MIN (-FLT_MAX) # define ECHO_POS_MAX FLT_MAX # define ECHO_POS_EPS FLT_EPSILON #else # define echoPos_nt nrrdTypeDouble # define echoPos_at airTypeDouble # define ell_4m_INV ell_4m_inv_d # define ell_4m_PRINT ell_4m_print_d # define ell_4m_DET ell_4m_det_d # define ell_3v_PERP ell_3v_perp_d # define ell_4m_POST_MUL ell_4m_post_mul_d # define ECHO_POS_MIN (-DBL_MAX) # define ECHO_POS_MAX DBL_MAX # define ECHO_POS_EPS DBL_EPSILON #endif #define OBJECT(obj) ((echoObject*)obj) #define SPLIT(obj) ((echoSplit*)obj) #define LIST(obj) ((echoList*)obj) #define SPHERE(obj) ((echoSphere*)obj) #define CYLINDER(obj) ((echoCylinder*)obj) #define SUPERQUAD(obj) ((echoSuperquad*)obj) #define RECTANGLE(obj) ((echoRectangle*)obj) #define AABBOX(obj) ((echoAABBox*)obj) #define TRIMESH(obj) ((echoTriMesh*)obj) #define TRIANGLE(obj) ((echoTriangle*)obj) #define INSTANCE(obj) ((echoInstance*)obj) #define _ECHO_REFLECT(refl, norm, view, tmp) \ (tmp) = 2*ELL_3V_DOT((view), (norm)); \ ELL_3V_SCALE_ADD2((refl), -1.0, (view), (tmp), (norm)) #define ECHO_NEW(TYPE) \ (echoObject##TYPE *)echoNew(echoObject##Type) /* methodsEcho.c */ extern void _echoSceneLightAdd(echoScene *scene, echoObject *obj); extern void _echoSceneNrrdAdd(echoScene *scene, Nrrd *nrrd); /* intx.c */ #define RAYINTX_ARGS(TYPE) echoIntx *intx, echoRay *ray, \ echo##TYPE *obj, echoRTParm *parm, \ echoThreadState *tstate typedef int (*_echoRayIntx_t)(RAYINTX_ARGS(Object)); extern _echoRayIntx_t _echoRayIntx[/* object type idx */]; typedef void (*_echoRayIntxUV_t)(echoIntx *intx); extern _echoRayIntxUV_t _echoRayIntxUV[/* object type idx */]; extern int _echoRayIntx_CubeSolid(echoPos_t *tminP, echoPos_t *tmaxP, echoPos_t xmin, echoPos_t xmax, echoPos_t ymin, echoPos_t ymax, echoPos_t zmin, echoPos_t zmax, echoRay *ray); /* sqd.c */ extern int _echoRayIntx_Superquad(RAYINTX_ARGS(Superquad)); /* color.c */ extern char _echoBuff[]; extern char *_echoDot(int depth); #define INTXCOLOR_ARGS echoCol_t rgba[4], echoIntx *intx, \ echoScene *scene, echoRTParm *parm, \ echoThreadState *tstate typedef void (*_echoIntxColor_t)(INTXCOLOR_ARGS); extern _echoIntxColor_t _echoIntxColor[/* matter idx */]; #ifdef __cplusplus } #endif #endif /* ECHO_PRIVATE_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/echo/color.c0000664000175000017500000003764512165631065016620 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "echo.h" #include "privateEcho.h" char _echoBuff[128] = ""; char * _echoDot(int depth) { int i; _echoBuff[0] = '\0'; for (i=1; i<=depth; i++) { strcat(_echoBuff, ". "); } return _echoBuff; } void echoTextureLookup(echoCol_t rgba[4], Nrrd *ntext, echoPos_t u, echoPos_t v, echoRTParm *parm) { int su, sv, ui, vi; float uf, vf; unsigned char *tdata00, *tdata10, *tdata01, *tdata11; su = ntext->axis[1].size; sv = ntext->axis[2].size; if (parm->textureNN) { ui = airIndex(0.0, u, 1.0, su); vi = airIndex(0.0, v, 1.0, sv); tdata00 = (unsigned char*)(ntext->data) + 4*(ui + su*vi); ELL_4V_SET_TT(rgba, echoCol_t, tdata00[0]/255.0, tdata00[1]/255.0, tdata00[2]/255.0, tdata00[3]/255.0); } else { u = AIR_AFFINE(0.0, u, 1.0, 0.0, su-1); u = AIR_CLAMP(0, u, su-1); v = AIR_AFFINE(0.0, v, 1.0, 0.0, sv-1); v = AIR_CLAMP(0, v, sv-1); u -= (u == su-1); ui = (int)u; uf = AIR_CAST(float, u - ui); v -= (v == sv-1); vi = (int)v; vf = AIR_CAST(float, v - vi); tdata00 = (unsigned char*)(ntext->data) + 4*(ui + su*vi); tdata01 = tdata00 + 4; tdata10 = tdata00 + 4*su; tdata11 = tdata10 + 4; ELL_4V_SET_TT(rgba, echoCol_t, ((1-vf)*(1-uf)*tdata00[0] + (1-vf)*uf*tdata01[0] + vf*(1-uf)*tdata10[0] + vf*uf*tdata11[0])/255.0, ((1-vf)*(1-uf)*tdata00[1] + (1-vf)*uf*tdata01[1] + vf*(1-uf)*tdata10[1] + vf*uf*tdata11[1])/255.0, ((1-vf)*(1-uf)*tdata00[2] + (1-vf)*uf*tdata01[2] + vf*(1-uf)*tdata10[2] + vf*uf*tdata11[2])/255.0, ((1-vf)*(1-uf)*tdata00[3] + (1-vf)*uf*tdata01[3] + vf*(1-uf)*tdata10[3] + vf*uf*tdata11[3])/255.0); } } void echoIntxMaterialColor(echoCol_t rgba[4], echoIntx *intx, echoRTParm *parm) { if (intx->obj->ntext) { _echoRayIntxUV[intx->obj->type](intx); echoTextureLookup(rgba, intx->obj->ntext, intx->u, intx->v, parm); rgba[0] *= intx->obj->rgba[0]; rgba[1] *= intx->obj->rgba[1]; rgba[2] *= intx->obj->rgba[2]; rgba[3] *= intx->obj->rgba[3]; } else { ELL_4V_COPY(rgba, intx->obj->rgba); } } /* ******** echoIntxLightColor ** ** determines ambient, diffuse, and (Phong) specular contributions to lighting ** a given intersection. "ambi" and "diff" MUST be given as non-NULL, ** "spec" can be given as NULL in order to bypass specular computations */ void echoIntxLightColor(echoCol_t ambi[3], echoCol_t diff[3], echoCol_t spec[3], echoCol_t sp, echoIntx *intx, echoScene *scene, echoRTParm *parm, echoThreadState *tstate) { unsigned int Lidx; int blocked; echoRay shadRay; echoIntx shadIntx; echoPos_t Ldist, Ldir[3], Lpos[3], Ldot; echoCol_t Lcol[3], fracseen; if (parm->shadow) { /* from, neer, shadow */ shadRay.shadow = AIR_TRUE; ELL_3V_COPY(shadRay.from, intx->pos); /* HEY: this has to be fixed: shadow rays were getting aborted because of this in a scene of instanced superquadrics, and so epsilon had to be increased. Something about this epsilon has to be adjusted according to the instancing matrix */ shadRay.neer = 30*ECHO_EPSILON; } /* set ambient (easy) */ ELL_3V_COPY(ambi, scene->ambi); /* environment contributes only to diffuse */ if (scene->envmap) { echoEnvmapLookup(diff, intx->norm, scene->envmap); } else { ELL_3V_SET(diff, 0, 0, 0); } /* lights contribute to diffuse and specular */ if (spec) { ELL_3V_SET(spec, 0, 0, 0); } for (Lidx=0; LidxlightArr->len; Lidx++) { echoLightPosition(Lpos, scene->light[Lidx], tstate); ELL_3V_SUB(Ldir, Lpos, intx->pos); ELL_3V_NORM(Ldir, Ldir, Ldist); Ldot = ELL_3V_DOT(Ldir, intx->norm); /* HEY: HACK: we have to general per-object-type flag that says, this kind of object has no notion of in-versus-out facing . . . */ if (echoTypeRectangle == intx->obj->type) { Ldot = AIR_ABS(Ldot); } if (Ldot <= 0) { continue; /* to next light, we aren't facing this one. NB: this means that there aren't diffuse or specular contributions on the backsides of even semi-transparent surfaces */ } if (parm->shadow) { ELL_3V_COPY(shadRay.dir, Ldir); shadRay.faar = Ldist; if (echoRayIntx(&shadIntx, &shadRay, scene, parm, tstate)) { if (1.0 == parm->shadow) { /* skip to next light, this one is obscured by something, and we don't do any partial shadowing */ continue; } blocked = AIR_TRUE; } else { blocked = AIR_FALSE; } } else { blocked = AIR_FALSE; } fracseen = AIR_CAST(echoCol_t, blocked ? 1.0 - parm->shadow : 1.0); echoLightColor(Lcol, Ldist, scene->light[Lidx], parm, tstate); ELL_3V_SCALE_INCR_TT(diff, echoCol_t, fracseen*Ldot, Lcol); if (spec) { Ldot = ELL_3V_DOT(Ldir, intx->refl); if (echoTypeRectangle == intx->obj->type) { Ldot = AIR_ABS(Ldot); } if (Ldot > 0) { Ldot = pow(Ldot, sp); ELL_3V_SCALE_INCR_TT(spec, echoCol_t, fracseen*Ldot, Lcol); } } } return; } void _echoIntxColorPhong(INTXCOLOR_ARGS) { echoCol_t ambi[3], diff[3], spec[3], ka, kd, ks, sp; ka = intx->obj->mat[echoMatterPhongKa]; kd = intx->obj->mat[echoMatterPhongKd]; ks = intx->obj->mat[echoMatterPhongKs]; sp = intx->obj->mat[echoMatterPhongSp]; echoIntxMaterialColor(rgba, intx, parm); ELL_3V_SET(spec, 0, 0, 0); echoIntxLightColor(ambi, diff, ks ? spec : NULL, sp, intx, scene, parm, tstate); rgba[0] = rgba[0]*(ka*ambi[0] + kd*diff[0]) + ks*spec[0]; rgba[1] = rgba[1]*(ka*ambi[1] + kd*diff[1]) + ks*spec[1]; rgba[2] = rgba[2]*(ka*ambi[2] + kd*diff[2]) + ks*spec[2]; return; } void _echoIntxColorMetal(INTXCOLOR_ARGS) { echoCol_t ka, kd, kp, RA, RD, RS, ambi[3], diff[3], spec[4]; echoPos_t c; echoRay reflRay; if (0 && tstate->verbose) { fprintf(stderr, "%s%s: t = %g\n", _echoDot(tstate->depth), "_echoIntxColorMetal", intx->t); } ELL_3V_SET(spec, 0, 0, 0); echoIntxMaterialColor(rgba, intx, parm); c = ELL_3V_DOT(intx->view, intx->norm); if (c <= 0) { /* see only surface color on backside of metal */ return; } c = 1 - c; c = c*c*c*c*c; RS = intx->obj->mat[echoMatterMetalR0]; RS = AIR_CAST(echoCol_t, RS + (1 - RS)*c); ka = intx->obj->mat[echoMatterMetalKa]; kd = intx->obj->mat[echoMatterMetalKd]; kp = ka + kd; /* neer, faar, shadow, from, dir */ ELL_3V_COPY(reflRay.from, intx->pos); ELL_3V_COPY(reflRay.dir, intx->refl); reflRay.neer = ECHO_EPSILON; reflRay.faar = ECHO_POS_MAX; reflRay.shadow = AIR_FALSE; echoRayColor(spec, &reflRay, scene, parm, tstate); if (kp) { RA = (1 - RS)*ka/kp; RD = (1 - RS)*kd/kp; echoIntxLightColor(ambi, diff, NULL, 0.0, intx, scene, parm, tstate); /* NB: surface color does attenuate reflected color (unlike phong) */ rgba[0] *= RA*ambi[0] + RD*diff[0] + RS*spec[0]; rgba[1] *= RA*ambi[1] + RD*diff[1] + RS*spec[1]; rgba[2] *= RA*ambi[2] + RD*diff[2] + RS*spec[2]; } else { rgba[0] *= RS*spec[0]; rgba[1] *= RS*spec[1]; rgba[2] *= RS*spec[2]; } return; } /* ** "th" = theta = angle of incidence ** "ph" = phi = angle of refraction ** "index" = (index of outgoing material)/(index of incoming material) */ int _echoRefract(echoPos_t T[3], echoPos_t V[3], echoPos_t N[3], echoCol_t indexr, echoThreadState *tstate) { echoPos_t cosTh, cosPh, sinPhSq, cosPhSq, tmp1, tmp2; cosTh = ELL_3V_DOT(V, N); sinPhSq = (1 - cosTh*cosTh)/(indexr*indexr); cosPhSq = 1 - sinPhSq; if (cosPhSq < 0) { if (tstate->verbose) { fprintf(stderr, "%s%s: cosTh = %g --%g--> TIR!!\n", _echoDot(tstate->depth), "_echoRefract", cosTh, indexr); } return AIR_FALSE; } /* else we do not have total internal reflection */ cosPh = sqrt(cosPhSq); if (tstate->verbose) { fprintf(stderr, "%s%s: cosTh = %g --%g--> cosPh = %g\n", _echoDot(tstate->depth), "_echoRefract", cosTh, indexr, cosPh); } tmp1 = -1.0/indexr; tmp2 = cosTh/indexr - cosPh; ELL_3V_SCALE_ADD2(T, tmp1, V, tmp2, N); ELL_3V_NORM(T, T, tmp1); return AIR_TRUE; } void _echoIntxColorGlass(INTXCOLOR_ARGS) { char me[]="_echoIntxColorGlass"; echoCol_t ambi[3], diff[3], ka, kd, RP, RS, RT, R0, indexr, /* (index of material we're going into) / (index of material we're leaving) */ k[3], /* attenuation of color due to travel through medium */ matlCol[4], /* inherent color */ reflCol[4], /* color from reflected ray */ tranCol[4]; /* color from transmitted ray */ echoPos_t tmp, negnorm[3]; double c; echoRay tranRay, reflRay; echoIntxMaterialColor(matlCol, intx, parm); /* from, neer, faar, shadow */ ELL_3V_COPY(tranRay.from, intx->pos); ELL_3V_COPY(reflRay.from, intx->pos); tranRay.neer = reflRay.neer = ECHO_EPSILON; tranRay.faar = reflRay.faar = ECHO_POS_MAX; tranRay.shadow = reflRay.shadow = AIR_FALSE; ELL_3V_COPY(reflRay.dir, intx->refl); /* tranRay.dir set below */ indexr = intx->obj->mat[echoMatterGlassIndex]; RS = 0.0; /* this is a flag meaning: "AFAIK, there's no total int refl" */ tmp = ELL_3V_DOT(intx->norm, intx->view); if (tmp > 0) { /* "d.n < 0": we're coming from outside the glass, and we assume this means that we're going into a HIGHER index material, which means there is NO total internal reflection */ _echoRefract(tranRay.dir, intx->view, intx->norm, indexr, tstate); if (tstate->verbose) { fprintf(stderr, "%s%s: V=(%g,%g,%g),N=(%g,%g,%g),n=%g -> T=(%g,%g,%g)\n", _echoDot(tstate->depth), me, intx->view[0], intx->view[1], intx->view[2], intx->norm[0], intx->norm[1], intx->norm[2], indexr, tranRay.dir[0], tranRay.dir[1], tranRay.dir[2]); } c = tmp; ELL_3V_SET(k, 1, 1, 1); } else { /* we're coming from inside the glass */ /* the reasoning for my Beer's law implementation is this: if a channel (r, g, or b) is full on (1.0), then there should be no attenuation in its color. The more the color is below 1.0, the more it should be damped with distance. */ k[0] = AIR_CAST(echoCol_t, exp(parm->glassC*(matlCol[0]-1)*intx->t)); k[1] = AIR_CAST(echoCol_t, exp(parm->glassC*(matlCol[1]-1)*intx->t)); k[2] = AIR_CAST(echoCol_t, exp(parm->glassC*(matlCol[2]-1)*intx->t)); if (tstate->verbose) { fprintf(stderr, "%s%s: internal refl @ t = %g -> k = %g %g %g\n", _echoDot(tstate->depth), me, intx->t, k[0], k[1], k[2]); } ELL_3V_SCALE(negnorm, -1, intx->norm); if (_echoRefract(tranRay.dir, intx->view, negnorm, 1/indexr, tstate)) { c = -ELL_3V_DOT(tranRay.dir, negnorm); } else { /* its total internal reflection time! */ c = 0.0; RS = 1.0; } } if (RS) { /* total internal reflection */ RT = 0; } else { R0 = (indexr - 1)/(indexr + 1); R0 *= R0; c = 1 - c; c = c*c*c*c*c; RS = AIR_CAST(echoCol_t, R0 + (1-R0)*c); RT = 1 - RS; } ka = intx->obj->mat[echoMatterMetalKa]; kd = intx->obj->mat[echoMatterMetalKd]; RP = ka + kd; if (RP) { RS *= 1 - RP; RT *= 1 - RP; echoIntxLightColor(ambi, diff, NULL, 0.0, intx, scene, parm, tstate); } else { ELL_3V_SET(ambi, 0, 0, 0); ELL_3V_SET(diff, 0, 0, 0); } if (tstate->verbose) { fprintf(stderr, "%s%s: --- reflRay (reflected)\n", _echoDot(tstate->depth), me); } echoRayColor(reflCol, &reflRay, scene, parm, tstate); if (RT) { if (tstate->verbose) { fprintf(stderr, "%s%s: --- tranRay (refracted)\n", _echoDot(tstate->depth), me); } echoRayColor(tranCol, &tranRay, scene, parm, tstate); } else { ELL_3V_SET(tranCol, 0, 0, 0); } rgba[0] = (matlCol[0]*(ka*ambi[0] + kd*diff[0]) + k[0]*(RS*reflCol[0] + RT*tranCol[0])); rgba[1] = (matlCol[1]*(ka*ambi[1] + kd*diff[1]) + k[1]*(RS*reflCol[1] + RT*tranCol[1])); rgba[2] = (matlCol[2]*(ka*ambi[2] + kd*diff[2]) + k[2]*(RS*reflCol[2] + RT*tranCol[2])); rgba[3] = 1.0; return; } void _echoIntxColorLight(INTXCOLOR_ARGS) { AIR_UNUSED(scene); AIR_UNUSED(tstate); echoIntxMaterialColor(rgba, intx, parm); } void _echoIntxColorUnknown(INTXCOLOR_ARGS) { AIR_UNUSED(rgba); AIR_UNUSED(intx); AIR_UNUSED(scene); AIR_UNUSED(parm); fprintf(stderr, "%s%s: can't color intx with object with unset material\n", _echoDot(tstate->depth), "_echoIntxColorNone"); } _echoIntxColor_t _echoIntxColor[ECHO_MATTER_MAX+1] = { _echoIntxColorUnknown, _echoIntxColorPhong, _echoIntxColorGlass, _echoIntxColorMetal, _echoIntxColorLight, }; /* ******** echoIntxFuzzify() ** ** this modifies the refl and norm fields of a given intx */ void echoIntxFuzzify(echoIntx *intx, echoCol_t fuzz, echoThreadState *tstate) { echoPos_t tmp, *jitt, oldNorm[3], perp0[3], perp1[3], jj0, jj1; int side; /* at some point I thought this was important to avoid bias when going through glass, but now I'm not so sure . . . It is likely totally moot if jitter vectors are NOT reused between pixels. */ if (ELL_3V_DOT(intx->norm, intx->view) > 0) { jitt = tstate->jitt + 2*echoJittableNormalA; } else { jitt = tstate->jitt + 2*echoJittableNormalB; } jj0 = fuzz*jitt[0]; jj1 = fuzz*jitt[1]; ELL_3V_COPY(oldNorm, intx->norm); side = ELL_3V_DOT(intx->refl, oldNorm) > 0; ell_3v_PERP(perp0, oldNorm); ELL_3V_NORM(perp0, perp0, tmp); ELL_3V_CROSS(perp1, perp0, oldNorm); ELL_3V_SCALE_ADD3(intx->norm, 1, oldNorm, jj0, perp0, jj1, perp1); ELL_3V_NORM(intx->norm, intx->norm, tmp); _ECHO_REFLECT(intx->refl, intx->norm, intx->view, tmp); if (side != (ELL_3V_DOT(intx->refl, oldNorm) > 0)) { ELL_3V_SCALE_ADD3(intx->norm, 1, oldNorm, -jj0, perp0, -jj1, perp1); ELL_3V_NORM(intx->norm, intx->norm, tmp); _ECHO_REFLECT(intx->refl, intx->norm, intx->view, tmp); } if (tstate->verbose) { fprintf(stderr, "%s%s: fuzz[%g](%g,%g,%g) --> (%g,%g,%g)\n", _echoDot(tstate->depth), "echoIntxFuzzify", fuzz, oldNorm[0], oldNorm[1], oldNorm[2], intx->norm[0], intx->norm[1], intx->norm[2]); } return; } void echoIntxColor(echoCol_t rgba[4], echoIntx *intx, echoScene *scene, echoRTParm *parm, echoThreadState *tstate) { echoCol_t fuzz; switch(intx->obj->matter) { case echoMatterGlass: fuzz = intx->obj->mat[echoMatterGlassFuzzy]; break; case echoMatterMetal: fuzz = intx->obj->mat[echoMatterMetalFuzzy]; break; default: fuzz = 0; break; } if (fuzz) { echoIntxFuzzify(intx, fuzz, tstate); } _echoIntxColor[intx->obj->matter](rgba, intx, scene, parm, tstate); } teem-1.11.0~svn6057/src/hest/0000775000175000017500000000000012203513754015341 5ustar domibeldomibelteem-1.11.0~svn6057/src/hest/defaultsHest.c0000664000175000017500000000351412165631065020146 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hest.h" #include "privateHest.h" int hestVerbosity = 0; int hestRespFileEnable = AIR_FALSE; unsigned int hestColumns = 79; int hestElideSingleEnumType = AIR_FALSE; int hestElideSingleOtherType = AIR_FALSE; int hestElideSingleOtherDefault = AIR_FALSE; int hestElideSingleNonExistFloatDefault = AIR_FALSE; int hestElideMultipleNonExistFloatDefault = AIR_FALSE; int hestElideSingleEmptyStringDefault = AIR_FALSE; int hestElideMultipleEmptyStringDefault = AIR_FALSE; int hestNoArgsIsNoProblem = AIR_FALSE; int hestGreedySingleString = AIR_TRUE; int hestCleverPluralizeOtherY = AIR_FALSE; char hestRespFileFlag = '@'; char hestRespFileComment = '#'; char hestVarParamStopFlag = '-'; char hestMultiFlagSep = ','; teem-1.11.0~svn6057/src/hest/privateHest.h0000664000175000017500000000373712165631065020025 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HEST_PRIVATE_HAS_BEEN_INCLUDED #define HEST_PRIVATE_HAS_BEEN_INCLUDED #ifdef __cplusplus extern "C" { #endif /* methodsHest.c */ extern char *_hestIdent(char *ident, hestOpt *opt, hestParm *parm, int brief); extern int _hestKind(hestOpt *opt); extern void _hestPrintArgv(int argc, char **argv); extern int _hestWhichFlag(hestOpt *opt, char *flag, hestParm *parm); extern int _hestCase(hestOpt *opt, int *udflt, unsigned int *nprm, int *appr, int op); extern char *_hestExtract(int *argcP, char **argv, unsigned int base, unsigned int pnum); extern int _hestNumOpts(hestOpt *opt); extern int _hestMax(int max); /* parseHest.c */ extern int _hestPanic(hestOpt *opt, char *err, hestParm *parm); extern int _hestErrStrlen(hestOpt *opt, int argc, const char **argv); #ifdef __cplusplus } #endif #endif /* HEST_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/hest/parseHest.c0000664000175000017500000013725612165631065017464 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* learned: well duh: when you send arguments to printf(), they will be evaluated before printf() sees them, so you can't use _hestIdent() twice with differen values */ #include "hest.h" #include "privateHest.h" #include #define ME ((parm && parm->verbosity) ? me : "") /* ** _hestArgsInResponseFiles() ** ** returns the number of args that will be parsed from the response files. ** The role of this function is solely to simplify the task of avoiding ** memory leaks. By knowing exactly how many args we'll get in the response ** file, then hestParse() can allocate its local argv[] for exactly as ** long as it needs to be, and we can avoid using an airArray. The drawback ** is that we open and read through the response files twice. Alas. */ int _hestArgsInResponseFiles(int *argcP, int *nrfP, const char **argv, char *err, hestParm *parm) { FILE *file; char me[]="_hestArgsInResponseFiles: ", line[AIR_STRLEN_HUGE], *pound; int ai, len; *argcP = 0; *nrfP = 0; if (!parm->respFileEnable) { /* don't do response files; we're done */ return 0; } ai = 0; while (argv[ai]) { if (parm->respFileFlag == argv[ai][0]) { if (!(file = fopen(argv[ai]+1, "rb"))) { /* can't open the indicated response file for reading */ sprintf(err, "%scouldn't open \"%s\" for reading as response file", ME, argv[ai]+1); *argcP = 0; *nrfP = 0; return 1; } len = airOneLine(file, line, AIR_STRLEN_HUGE); while (len > 0) { if ( (pound = strchr(line, parm->respFileComment)) ) *pound = '\0'; airOneLinify(line); *argcP += airStrntok(line, AIR_WHITESPACE); len = airOneLine(file, line, AIR_STRLEN_HUGE); } fclose(file); (*nrfP)++; } ai++; } return 0; } /* ** _hestResponseFiles() ** ** This function is badly named. Even if there are no response files, ** even if response files are disabled, this is the function that ** copies from the user's argc,argv to our local copy. */ int _hestResponseFiles(char **newArgv, const char **oldArgv, hestParm *parm, airArray *pmop) { char line[AIR_STRLEN_HUGE], *pound; int len, newArgc, oldArgc, incr, ai; FILE *file; newArgc = oldArgc = 0; while(oldArgv[oldArgc]) { if (parm->verbosity) { printf("!%s:________ newArgc = %d, oldArgc = %d\n", "dammit", newArgc, oldArgc); _hestPrintArgv(newArgc, newArgv); } if (!parm->respFileEnable || parm->respFileFlag != oldArgv[oldArgc][0]) { /* nothing to do with a response file */ newArgv[newArgc] = airStrdup(oldArgv[oldArgc]); airMopAdd(pmop, newArgv[newArgc], airFree, airMopAlways); newArgc += 1; } else { /* It is a response file. Error checking on open-ability should have been done by _hestArgsInResponseFiles() */ file = fopen(oldArgv[oldArgc]+1, "rb"); len = airOneLine(file, line, AIR_STRLEN_HUGE); while (len > 0) { if (parm->verbosity) printf("_hestResponseFiles: line: |%s|\n", line); if ( (pound = strchr(line, parm->respFileComment)) ) *pound = '\0'; if (parm->verbosity) printf("_hestResponseFiles: -0-> line: |%s|\n", line); airOneLinify(line); incr = airStrntok(line, AIR_WHITESPACE); if (parm->verbosity) printf("_hestResponseFiles: -1-> line: |%s|, incr=%d\n", line, incr); airParseStrS(newArgv + newArgc, line, AIR_WHITESPACE, incr, AIR_FALSE); for (ai=0; aiverbosity) { _hestPrintArgv(newArgc, newArgv); printf("!%s: ^^^^^^^ newArgc = %d, oldArgc = %d\n", "dammit", newArgc, oldArgc); } } newArgv[newArgc] = NULL; return 0; } /* ** _hestPanic() ** ** all error checking on the given hest array itself (not the ** command line to be parsed). Also, sets the "kind" field of ** the opt struct */ int _hestPanic(hestOpt *opt, char *err, hestParm *parm) { char me[]="_hestPanic: ", tbuff[AIR_STRLEN_HUGE], *sep; int numvar, op, numOpts; numOpts = _hestNumOpts(opt); numvar = 0; for (op=0; opsize > 0 )) { if (err) sprintf(err, "%s!!!!!! opt[%d]'s \"size\" (%d) invalid", ME, op, (int)(opt[op].CB->size)); else fprintf(stderr, "%s: panic 5\n", me); return 1; } if (!( opt[op].type )) { if (err) sprintf(err, "%s!!!!!! opt[%d]'s \"type\" is NULL", ME, op); else fprintf(stderr, "%s: panic 6\n", me); return 1; } if (!( opt[op].CB->parse )) { if (err) sprintf(err, "%s!!!!!! opt[%d]'s \"parse\" callback NULL", ME, op); else fprintf(stderr, "%s: panic 7\n", me); return 1; } if (opt[op].CB->destroy && (sizeof(void*) != opt[op].CB->size)) { if (err) sprintf(err, "%s!!!!!! opt[%d] has a \"destroy\", but size isn't " "sizeof(void*)", ME, op); else fprintf(stderr, "%s: panic 8\n", me); return 1; } } if (opt[op].flag) { strcpy(tbuff, opt[op].flag); if (( sep = strchr(tbuff, parm->multiFlagSep) )) { *sep = '\0'; if (!( strlen(tbuff) && strlen(sep+1) )) { if (err) sprintf(err, "%s!!!!!! either short (\"%s\") or long (\"%s\") flag" " of opt[%d] is zero length", ME, tbuff, sep+1, op); else fprintf(stderr, "%s: panic 9\n", me); return 1; } } else { if (!strlen(opt[op].flag)) { if (err) sprintf(err, "%s!!!!!! opt[%d].flag is zero length", ME, op); else fprintf(stderr, "%s: panic 10\n", me); return 1; } } if (4 == opt[op].kind) { if (!opt[op].dflt) { if (err) sprintf(err, "%s!!!!!! flagged single variable parameter must " "specify a default", ME); else fprintf(stderr, "%s: panic 11\n", me); return 1; } if (!strlen(opt[op].dflt)) { if (err) sprintf(err, "%s!!!!!! flagged single variable parameter default " "must be non-zero length", ME); else fprintf(stderr, "%s: panic 12\n", me); return 1; } } /* sprintf(tbuff, "-%s", opt[op].flag); if (1 == sscanf(tbuff, "%f", &tmpF)) { if (err) sprintf(err, "%s!!!!!! opt[%d].flag (\"%s\") is numeric, bad news", ME, op, opt[op].flag); return 1; } */ } if (1 == opt[op].kind) { if (!opt[op].flag) { if (err) sprintf(err, "%s!!!!!! flags must have flags", ME); else fprintf(stderr, "%s: panic 13\n", me); return 1; } } else { if (!opt[op].name) { if (err) sprintf(err, "%s!!!!!! opt[%d] isn't a flag: must have \"name\"", ME, op); else fprintf(stderr, "%s: panic 14\n", me); return 1; } } if (4 == opt[op].kind && !opt[op].dflt) { if (err) sprintf(err, "%s!!!!!! opt[%d] is single variable parameter, but " "no default set", ME, op); else fprintf(stderr, "%s: panic 15\n", me); return 1; } numvar += ((int)opt[op].min < _hestMax(opt[op].max) && (NULL == opt[op].flag)); /* HEY scrutinize casts */ } if (numvar > 1) { if (err) sprintf(err, "%s!!!!!! can't have %d unflagged minverbosity) printf("!%s: *argcP = %d\n", me, *argcP); while (a<=*argcP-1) { if (parm->verbosity) printf("!%s: a = %d -> argv[a] = %s\n", me, a, argv[a]); flag = _hestWhichFlag(opt, argv[a], parm); if (parm->verbosity) printf("!%s: A: a = %d -> flag = %d\n", me, a, flag); if (!(0 <= flag)) { /* not a flag, move on */ a++; continue; } /* see if we can associate some parameters with the flag */ np = 0; endflag = 0; while (np < _hestMax(opt[flag].max) && a+np+1 <= *argcP-1 && -1 == (endflag = _hestWhichFlag(opt, argv[a+np+1], parm))) { np++; if (parm->verbosity) printf("!%s: np --> %d with endflag = %d\n", me, np, endflag); } /* we stopped because we got the max number of parameters, or because we hit the end of the command line, or because _hestWhichFlag() returned something other than -1, which means it returned -2, or a valid option index. If we stopped because of _hestWhichFlag()'s return value, endflag has been set to that return value */ if (parm->verbosity) printf("!%s: B: np = %d; endflag = %d\n", me, np, endflag); if (np < (int)opt[flag].min) { /* HEY scrutinize casts */ /* didn't get minimum number of parameters */ if (!( a+np+1 <= *argcP-1 )) { sprintf(err, "%shit end of line before getting %d parameter%s " "for %s (got %d)", ME, opt[flag].min, opt[flag].min > 1 ? "s" : "", _hestIdent(ident1, opt+flag, parm, AIR_TRUE), np); } else { sprintf(err, "%shit %s before getting %d parameter%s for %s (got %d)", ME, _hestIdent(ident1, opt+endflag, parm, AIR_FALSE), opt[flag].min, opt[flag].min > 1 ? "s" : "", _hestIdent(ident2, opt+flag, parm, AIR_FALSE), np); } return 1; } nprm[flag] = np; if (parm->verbosity) { printf("!%s:________ a=%d, *argcP = %d -> flag = %d\n", me, a, *argcP, flag); _hestPrintArgv(*argcP, argv); } /* lose the flag argument */ free(_hestExtract(argcP, argv, a, 1)); /* extract the args after the flag */ if (appr[flag]) { airMopSub(pmop, prms[flag], airFree); prms[flag] = (char *)airFree(prms[flag]); } prms[flag] = _hestExtract(argcP, argv, a, nprm[flag]); airMopAdd(pmop, prms[flag], airFree, airMopAlways); appr[flag] = AIR_TRUE; if (-2 == endflag) { /* we should lose the end-of-variable-parameter marker */ free(_hestExtract(argcP, argv, a, 1)); } if (parm->verbosity) { _hestPrintArgv(*argcP, argv); printf("!%s:^^^^^^^^ *argcP = %d\n", me, *argcP); printf("!%s: prms[%d] = %s\n", me, flag, prms[flag] ? prms[flag] : "(null)"); } } /* make sure that flagged options without default were given */ numOpts = _hestNumOpts(opt); for (op=0; op 1 ? "s" : "", argv[0] ? "starting at \"" : "", argv[0] ? argv[0] : "", argv[0] ? "\" " : "", _hestIdent(ident, opt+op, parm, AIR_TRUE)); return 1; } prms[op] = _hestExtract(argcP, argv, 0, np); airMopAdd(pmop, prms[op], airFree, airMopAlways); nprm[op] = np; } /* _hestPrintArgv(*argcP, argv); */ /* we skip over the variable parameter unflagged option, subtract from *argcP the number of parameters in all the opts which follow it, in order to get the number of parameters in the sole variable parameter option, store this in nvp */ nvp = *argcP; for (op = _hestNextUnflagged(unflagVar+1, opt, numOpts); op < numOpts; op = _hestNextUnflagged(op+1, opt, numOpts)) { nvp -= opt[op].min; /* min == max */ } if (nvp < 0) { op = _hestNextUnflagged(unflagVar+1, opt, numOpts); np = opt[op].min; sprintf(err, "%sdon't have %d parameter%s for %s", ME, np, np > 1 ? "s" : "", _hestIdent(ident, opt+op, parm, AIR_FALSE)); return 1; } /* else we had enough args for all the unflagged options following the sole variable parameter unflagged option, so snarf them up */ for (op = _hestNextUnflagged(unflagVar+1, opt, numOpts); op < numOpts; op = _hestNextUnflagged(op+1, opt, numOpts)) { np = opt[op].min; prms[op] = _hestExtract(argcP, argv, nvp, np); airMopAdd(pmop, prms[op], airFree, airMopAlways); nprm[op] = np; } /* now we grab the parameters of the sole variable parameter unflagged opt, if it exists (unflagVar < numOpts) */ if (unflagVar < numOpts) { /* printf("!%s: unflagVar=%d: min, nvp, max = %d %d %d\n", me, unflagVar, opt[unflagVar].min, nvp, _hestMax(opt[unflagVar].max)); */ /* we'll do error checking for unexpected args later */ nvp = AIR_MIN(nvp, _hestMax(opt[unflagVar].max)); if (nvp < (int)opt[unflagVar].min) { /* HEY scrutinize casts */ sprintf(err, "%sdidn't get minimum of %d arg%s for %s (got %d)", ME, opt[unflagVar].min, opt[unflagVar].min > 1 ? "s" : "", _hestIdent(ident, opt+unflagVar, parm, AIR_TRUE), nvp); return 1; } if (nvp) { prms[unflagVar] = _hestExtract(argcP, argv, 0, nvp); airMopAdd(pmop, prms[unflagVar], airFree, airMopAlways); nprm[unflagVar] = nvp; } else { prms[unflagVar] = NULL; nprm[unflagVar] = 0; } } return 0; } int _hestDefaults(char **prms, int *udflt, unsigned int *nprm, int *appr, hestOpt *opt, char *err, hestParm *parm, airArray *mop) { char *tmpS, me[]="_hestDefaults: ", ident[AIR_STRLEN_HUGE]; int op, numOpts; numOpts = _hestNumOpts(opt); for (op=0; opverbosity) printf("%s op=%d/%d: \"%s\" --> kind=%d, nprm=%u, appr=%d\n", me, op, numOpts-1, prms[op], opt[op].kind, nprm[op], appr[op]); switch(opt[op].kind) { case 1: /* -------- (no-parameter) boolean flags -------- */ /* default is always ignored */ udflt[op] = 0; break; case 2: case 3: /* -------- one required parameter -------- */ /* -------- multiple required parameters -------- */ /* we'll used defaults if the flag didn't appear */ udflt[op] = opt[op].flag && !appr[op]; break; case 4: /* -------- optional single variables -------- */ /* if the flag appeared (if there is a flag) but the paramter didn't, we'll "invert" the default; if the flag didn't appear (or if there isn't a flag) and the parameter also didn't appear, we'll use the default. In either case, nprm[op] will be zero, and in both cases, we need to use the default information. */ udflt[op] = (0 == nprm[op]); /* fprintf(stderr, "%s nprm[%d] = %u --> udflt[%d] = %d\n", me, op, nprm[op], op, udflt[op]); */ break; case 5: /* -------- multiple optional parameters -------- */ /* we'll use the default if the flag didn't appear (if there is a flag) Otherwise, if nprm[op] is zero, then the user is saying, I want zero parameters */ udflt[op] = opt[op].flag && !appr[op]; break; } if (!udflt[op]) continue; prms[op] = airStrdup(opt[op].dflt); /* fprintf(stderr, "%s: prms[%d] = |%s|\n", me, op, prms[op]); */ if (prms[op]) { airMopAdd(mop, prms[op], airFree, airMopAlways); airOneLinify(prms[op]); tmpS = airStrdup(prms[op]); nprm[op] = airStrntok(tmpS, " "); tmpS = (char *)airFree(tmpS); /* printf("!%s: nprm[%d] in default = %u\n", me, op, nprm[op]); */ if ((int)opt[op].min < _hestMax(opt[op].max)) { /* HEY scrutinize casts */ if (!( AIR_IN_CL((int)opt[op].min, (int)nprm[op], _hestMax(opt[op].max)) /* HEY scrutinize casts */ || (airTypeString == opt[op].type && parm->elideMultipleEmptyStringDefault) )) { sprintf(err, "%s# parameters (in default) for %s is %d, " "but need between %d and %d", ME, _hestIdent(ident, opt+op, parm, AIR_TRUE), nprm[op], opt[op].min, _hestMax(opt[op].max)); return 1; } } } } return 0; } /* ** this function moved from air/miscAir; the usage below ** is its only usage in Teem */ static int airIStore(void *v, int t, int i) { switch(t) { case airTypeBool: return (*((int*)v) = !!i); break; case airTypeInt: return (*((int*)v) = i); break; case airTypeUInt: return (int)(*((unsigned int*)v) = i); break; case airTypeLongInt:return (int)(*((long int*)v) = i); break; case airTypeULongInt:return (int)(*((unsigned long int*)v) = i); break; case airTypeSize_t: return (int)(*((size_t*)v) = i); break; case airTypeFloat: return (int)(*((float*)v) = (float)(i)); break; case airTypeDouble: return (int)(*((double*)v) = (double)(i)); break; case airTypeChar: return (*((char*)v) = (char)(i)); break; default: return 0; break; } } /* ** this function moved from air/miscAir; the usage below ** is its only usage in Teem */ double airDLoad(void *v, int t) { switch(t) { case airTypeBool: return AIR_CAST(double,*((int*)v)); break; case airTypeInt: return AIR_CAST(double,*((int*)v)); break; case airTypeUInt: return AIR_CAST(double,*((unsigned int*)v)); break; case airTypeLongInt:return AIR_CAST(double,*((long int*)v)); break; case airTypeULongInt:return AIR_CAST(double,*((unsigned long int*)v)); break; case airTypeSize_t: return AIR_CAST(double, *((size_t*)v)); break; case airTypeFloat: return AIR_CAST(double,*((float*)v)); break; case airTypeDouble: return *((double*)v); break; case airTypeChar: return AIR_CAST(double,*((char*)v)); break; default: return 0; break; } } int _hestSetValues(char **prms, int *udflt, unsigned int *nprm, int *appr, hestOpt *opt, char *err, hestParm *parm, airArray *pmop) { char ident[AIR_STRLEN_HUGE], me[]="_hestSetValues: ", cberr[AIR_STRLEN_HUGE], *tok, *last, *prmsCopy; double tmpD; int op, type, numOpts, p, ret; void *vP; char *cP; size_t size; numOpts = _hestNumOpts(opt); for (op=0; opsize /* HEY scrutinize casts */ : (int)airTypeSize[type])); /* HEY scrutinize casts */ cP = (char *)(vP = opt[op].valueP); if (parm->verbosity) { printf("%s %d of %d: \"%s\": |%s| --> kind=%d, type=%d, size=%d\n", me, op, numOpts-1, prms[op], ident, opt[op].kind, type, (int)size); } /* we may over-write these */ opt[op].alloc = 0; if (opt[op].sawP) { *(opt[op].sawP) = 0; } switch(opt[op].kind) { case 1: /* -------- parameter-less boolean flags -------- */ if (vP) *((int*)vP) = appr[op]; break; case 2: /* -------- one required parameter -------- */ if (prms[op] && vP) { switch (type) { case airTypeEnum: if (1 != airParseStrE((int *)vP, prms[op], " ", 1, opt[op].enm)) { sprintf(err, "%scouldn\'t parse %s\"%s\" as %s for %s", ME, udflt[op] ? "(default) " : "", prms[op], opt[op].enm->name, ident); return 1; } break; case airTypeOther: strcpy(cberr, ""); ret = opt[op].CB->parse(vP, prms[op], cberr); if (ret) { if (strlen(cberr)) { sprintf(err, "%serror parsing \"%s\" as %s for %s:\n%s", ME, prms[op], opt[op].CB->type, ident, cberr); } else { sprintf(err, "%serror parsing \"%s\" as %s for %s: returned %d", ME, prms[op], opt[op].CB->type, ident, ret); } return ret; } if (opt[op].CB->destroy) { /* vP is the address of a void*, we manage the void * */ opt[op].alloc = 1; airMopAdd(pmop, (void**)vP, (airMopper)airSetNull, airMopOnError); airMopAdd(pmop, *((void**)vP), opt[op].CB->destroy, airMopOnError); } break; case airTypeString: if (1 != airParseStrS((char **)vP, prms[op], " ", 1, parm->greedySingleString)) { sprintf(err, "%scouldn't parse %s\"%s\" as %s for %s", ME, udflt[op] ? "(default) " : "", prms[op], airTypeStr[type], ident); return 1; } /* vP is the address of a char* (a char **), but what we manage with airMop is the char * */ opt[op].alloc = 1; airMopMem(pmop, vP, airMopOnError); break; default: /* type isn't string or enum, so no last arg to airParseStr[type] */ if (1 != airParseStr[type](vP, prms[op], " ", 1)) { sprintf(err, "%scouldn't parse %s\"%s\" as %s for %s", ME, udflt[op] ? "(default) " : "", prms[op], airTypeStr[type], ident); return 1; } break; } } break; case 3: /* -------- multiple required parameters -------- */ if (prms[op] && vP) { switch (type) { case airTypeEnum: if (opt[op].min != /* min == max */ airParseStrE((int *)vP, prms[op], " ", opt[op].min, opt[op].enm)) { sprintf(err, "%scouldn't parse %s\"%s\" as %d %s%s for %s", ME, udflt[op] ? "(default) " : "", prms[op], opt[op].min, opt[op].enm->name, opt[op].min > 1 ? "s" : "", ident); return 1; } break; case airTypeOther: prmsCopy = airStrdup(prms[op]); for (p=0; p<(int)opt[op].min; p++) { /* HEY scrutinize casts */ tok = airStrtok(!p ? prmsCopy : NULL, " ", &last); strcpy(cberr, ""); ret = opt[op].CB->parse(cP + p*size, tok, cberr); if (ret) { if (strlen(cberr)) sprintf(err, "%serror parsing \"%s\" (in \"%s\") as %s " "for %s:\n%s", ME, tok, prms[op], opt[op].CB->type, ident, cberr); else sprintf(err, "%serror parsing \"%s\" (in \"%s\") as %s " "for %s: returned %d", ME, tok, prms[op], opt[op].CB->type, ident, ret); free(prmsCopy); return 1; } } free(prmsCopy); if (opt[op].CB->destroy) { /* vP is an array of void*s, we manage the individual void*s */ opt[op].alloc = 2; for (p=0; p<(int)opt[op].min; p++) { /* HEY scrutinize casts */ airMopAdd(pmop, ((void**)vP)+p, (airMopper)airSetNull, airMopOnError); airMopAdd(pmop, *(((void**)vP)+p), opt[op].CB->destroy, airMopOnError); } } break; case airTypeString: if (opt[op].min != /* min == max */ airParseStr[type](vP, prms[op], " ", opt[op].min, AIR_FALSE)) { sprintf(err, "%scouldn't parse %s\"%s\" as %d %s%s for %s", ME, udflt[op] ? "(default) " : "", prms[op], opt[op].min, airTypeStr[type], opt[op].min > 1 ? "s" : "", ident); return 1; } /* vP is an array of char*s, (a char**), and what we manage with airMop are the individual vP[p]. */ opt[op].alloc = 2; for (p=0; p<(int)opt[op].min; p++) { /* HEY scrutinize casts */ airMopMem(pmop, &(((char**)vP)[p]), airMopOnError); } break; default: if (opt[op].min != /* min == max */ airParseStr[type](vP, prms[op], " ", opt[op].min)) { sprintf(err, "%scouldn't parse %s\"%s\" as %d %s%s for %s", ME, udflt[op] ? "(default) " : "", prms[op], opt[op].min, airTypeStr[type], opt[op].min > 1 ? "s" : "", ident); return 1; } break; } } break; case 4: /* -------- optional single variables -------- */ if (prms[op] && vP) { switch (type) { case airTypeEnum: if (1 != airParseStrE((int *)vP, prms[op], " ", 1, opt[op].enm)) { sprintf(err, "%scouldn't parse %s\"%s\" as %s for %s", ME, udflt[op] ? "(default) " : "", prms[op], opt[op].enm->name, ident); return 1; } break; case airTypeOther: /* we're parsing an "other". We will not perform the special flagged single variable parameter games as done above, so whether this option is flagged or unflagged, we're going to treat it like an unflagged single variable parameter option: if the parameter didn't appear, we'll parse it from the default, if it did appear, we'll parse it from the command line. Setting up prms[op] thusly has already been done by _hestDefaults() */ strcpy(cberr, ""); ret = opt[op].CB->parse(vP, prms[op], cberr); if (ret) { if (strlen(cberr)) sprintf(err, "%serror parsing \"%s\" as %s for %s:\n%s", ME, prms[op], opt[op].CB->type, ident, cberr); else sprintf(err, "%serror parsing \"%s\" as %s for %s: returned %d", ME, prms[op], opt[op].CB->type, ident, ret); return 1; } if (opt[op].CB->destroy) { /* vP is the address of a void*, we manage the void* */ opt[op].alloc = 1; airMopAdd(pmop, vP, (airMopper)airSetNull, airMopOnError); airMopAdd(pmop, *((void**)vP), opt[op].CB->destroy, airMopOnError); } break; case airTypeString: if (1 != airParseStr[type](vP, prms[op], " ", 1, parm->greedySingleString)) { sprintf(err, "%scouldn't parse %s\"%s\" as %s for %s", ME, udflt[op] ? "(default) " : "", prms[op], airTypeStr[type], ident); return 1; } opt[op].alloc = 1; if (opt[op].flag && 1 == _hestCase(opt, udflt, nprm, appr, op)) { /* we just parsed the default, but now we want to "invert" it */ *((char**)vP) = (char *)airFree(*((char**)vP)); opt[op].alloc = 0; } /* vP is the address of a char* (a char**), and what we manage with airMop is the char * */ airMopMem(pmop, vP, airMopOnError); break; default: if (1 != airParseStr[type](vP, prms[op], " ", 1)) { sprintf(err, "%scouldn't parse %s\"%s\" as %s for %s", ME, udflt[op] ? "(default) " : "", prms[op], airTypeStr[type], ident); return 1; } opt[op].alloc = 0; if (1 == _hestCase(opt, udflt, nprm, appr, op)) { /* we just parsed the default, but now we want to "invert" it */ tmpD = airDLoad(vP, type); airIStore(vP, type, tmpD ? 0 : 1); } break; } } break; case 5: /* -------- multiple optional parameters -------- */ /* hammerhead problems in this case */ if (prms[op] && vP) { if (1 == _hestCase(opt, udflt, nprm, appr, op)) { *((void**)vP) = NULL; /* alloc and sawP set above */ } else { if (airTypeString == type) { /* this is sneakiness: we allocate one more element so that the resulting char** is, like argv, NULL-terminated */ *((void**)vP) = calloc(nprm[op]+1, size); } else { *((void**)vP) = calloc(nprm[op], size); } if (parm->verbosity) { printf("!%s: nprm[%d] = %u\n", me, op, nprm[op]); printf("!%s: new array is at 0x%p\n", me, *((void**)vP)); } airMopMem(pmop, vP, airMopOnError); *(opt[op].sawP) = nprm[op]; /* so far everything we've done is regardless of type */ switch (type) { case airTypeEnum: opt[op].alloc = 1; if (nprm[op] != airParseStrE((int *)(*((void**)vP)), prms[op], " ", nprm[op], opt[op].enm)) { sprintf(err, "%scouldn't parse %s\"%s\" as %u %s%s for %s", ME, udflt[op] ? "(default) " : "", prms[op], nprm[op], opt[op].enm->name, nprm[op] > 1 ? "s" : "", ident); return 1; } break; case airTypeOther: cP = (char *)(*((void**)vP)); prmsCopy = airStrdup(prms[op]); opt[op].alloc = (opt[op].CB->destroy ? 3 : 1); for (p=0; p<(int)nprm[op]; p++) { /* HEY scrutinize casts */ tok = airStrtok(!p ? prmsCopy : NULL, " ", &last); /* hammerhead problems went away when this line was replaced by the following one: strcpy(cberr, ""); */ cberr[0] = 0; ret = opt[op].CB->parse(cP + p*size, tok, cberr); if (ret) { if (strlen(cberr)) sprintf(err,"%serror parsing \"%s\" (in \"%s\") as %s " "for %s:\n%s", ME, tok, prms[op], opt[op].CB->type, ident, cberr); else sprintf(err, "%serror parsing \"%s\" (in \"%s\") as %s " "for %s: returned %d", ME, tok, prms[op], opt[op].CB->type, ident, ret); free(prmsCopy); return 1; } } free(prmsCopy); if (opt[op].CB->destroy) { for (p=0; p<(int)nprm[op]; p++) { /* HEY scrutinize casts */ /* avert your eyes. vP is the address of an array of void*s. We manage the void*s */ airMopAdd(pmop, (*((void***)vP))+p, (airMopper)airSetNull, airMopOnError); airMopAdd(pmop, *((*((void***)vP))+p), opt[op].CB->destroy, airMopOnError); } } break; case airTypeString: opt[op].alloc = 3; if (nprm[op] != airParseStrS((char **)(*((void**)vP)), prms[op], " ", nprm[op], parm->greedySingleString)) { sprintf(err, "%scouldn't parse %s\"%s\" as %d %s%s for %s", ME, udflt[op] ? "(default) " : "", prms[op], nprm[op], airTypeStr[type], nprm[op] > 1 ? "s" : "", ident); return 1; } /* vP is the address of an array of char*s (a char ***), and what we manage with airMop is the individual (*vP)[p], as well as vP itself (above). */ for (p=0; p<(int)nprm[op]; p++) { /* HEY scrutinize casts */ airMopAdd(pmop, (*((char***)vP))[p], airFree, airMopOnError); } /* do the NULL-termination described above */ (*((char***)vP))[nprm[op]] = NULL; break; default: opt[op].alloc = 1; if (nprm[op] != airParseStr[type](*((void**)vP), prms[op], " ", nprm[op])) { sprintf(err, "%scouldn't parse %s\"%s\" as %d %s%s for %s", ME, udflt[op] ? "(default) " : "", prms[op], nprm[op], airTypeStr[type], nprm[op] > 1 ? "s" : "", ident); return 1; } break; } } } break; } } return 0; } /* ******** hestParse() ** ** documentation? */ int hestParse(hestOpt *opt, int _argc, const char **_argv, char **_errP, hestParm *_parm) { char me[]="hestParse: "; char *param, *param_copy; char **argv, **prms, *err; int a, argc, argr, *appr, *udflt, nrf, numOpts, big, ret, i; unsigned int *nprm; airArray *mop; hestParm *parm; size_t start_index, end_index; numOpts = _hestNumOpts(opt); /* -------- initialize the mop! */ mop = airMopNew(); /* -------- either copy given _parm, or allocate one */ if (_parm) { parm = _parm; } else { parm = hestParmNew(); airMopAdd(mop, parm, (airMopper)hestParmFree, airMopAlways); } /* -------- allocate the err string. To determine its size with total ridiculous safety we have to find the biggest things which can appear in the string. */ big = _hestErrStrlen(opt, _argc, _argv); if (!( err = AIR_CALLOC(big, char) )) { fprintf(stderr, "%s PANIC: couldn't allocate error message " "buffer (size %d)\n", me, big); /* exit(1); */ } if (_errP) { /* if they care about the error string, than it is mopped only when there _wasn't_ an error */ *_errP = err; airMopAdd(mop, _errP, (airMopper)airSetNull, airMopOnOkay); airMopAdd(mop, err, airFree, airMopOnOkay); } else { /* otherwise, we're making the error string just for our own convenience, and we'll always clean it up on exit */ airMopAdd(mop, err, airFree, airMopAlways); } /* -------- check on validity of the hestOpt array */ if (_hestPanic(opt, err, parm)) { airMopError(mop); return 1; } /* -------- Create all the local arrays used to save state during the processing of all the different options */ nprm = AIR_CALLOC(numOpts, unsigned int); airMopMem(mop, &nprm, airMopAlways); appr = AIR_CALLOC(numOpts, int); airMopMem(mop, &appr, airMopAlways); udflt = AIR_CALLOC(numOpts, int); airMopMem(mop, &udflt, airMopAlways); prms = AIR_CALLOC(numOpts, char *); airMopMem(mop, &prms, airMopAlways); for (a=0; averbosity) { printf("!%s: nrf = %d; argr = %d; _argc = %d --> argc = %d\n", me, nrf, argr, _argc, argc); } argv = AIR_CALLOC(argc+1, char *); airMopMem(mop, &argv, airMopAlways); /* -------- process response files (if any) and set the remaining elements of argv */ if (parm->verbosity) printf("%s: #### calling hestResponseFiles\n", me); if (_hestResponseFiles(argv, _argv, parm, mop)) { airMopError(mop); return 1; } if (parm->verbosity) printf("%s: #### hestResponseFiles done!\n", me); /* _hestPrintArgv(argc, argv); */ /* -------- extract flags and their associated parameters from argv */ if (parm->verbosity) printf("%s: #### calling hestExtractFlagged\n", me); if (_hestExtractFlagged(prms, nprm, appr, &argc, argv, opt, err, parm, mop)) { airMopError(mop); return 1; } if (parm->verbosity) printf("%s: #### hestExtractFlagged done!\n", me); /* _hestPrintArgv(argc, argv); */ /* -------- extract args for unflagged options */ if (parm->verbosity) printf("%s: #### calling hestExtractUnflagged\n", me); if (_hestExtractUnflagged(prms, nprm, &argc, argv, opt, err, parm, mop)) { airMopError(mop); return 1; } if (parm->verbosity) printf("%s: #### hestExtractUnflagged done!\n", me); /* currently, any left over arguments indicate error */ if (argc) { sprintf(err, "%sunexpected arg%s: \"%s\"", ME, ('-' == argv[0][0] ? " (or unrecognized flag)" : ""), argv[0]); airMopError(mop); return 1; } /* -------- learn defaults */ if (parm->verbosity) printf("%s: #### calling hestDefaults\n", me); if (_hestDefaults(prms, udflt, nprm, appr, opt, err, parm, mop)) { airMopError(mop); return 1; } if (parm->verbosity) printf("%s: #### hestDefaults done!\n", me); /* remove quotes from strings if greedy wasn't turned on for strings, then we have no hope of capturing filenames with spaces. */ if ( parm->greedySingleString ) { for (i=0; iverbosity) printf("%s: #### calling hestSetValues\n", me); ret = _hestSetValues(prms, udflt, nprm, appr, opt, err, parm, mop); if (ret) { airMopError(mop); return ret; } if (parm->verbosity) printf("%s: #### hestSetValues done!\n", me); airMopOkay(mop); return 0; } /* ******** hestParseFree() ** ** free()s whatever was allocated by hestParse() ** ** returns NULL only to facilitate use with the airMop functions. ** You should probably just ignore this quirk. */ void * hestParseFree(hestOpt *opt) { int op, i, numOpts; unsigned int ui; void **vP; void ***vAP; char **str; char ***strP; numOpts = _hestNumOpts(opt); for (op=0; op kind = %d; type = %d; alloc = %d\n", op, numOpts-1, opt[op].kind, opt[op].type, opt[op].alloc); */ vP = (void **)opt[op].valueP; vAP = (void ***)opt[op].valueP; str = (char **)opt[op].valueP; strP = (char ***)opt[op].valueP; switch (opt[op].alloc) { case 0: /* nothing was allocated */ break; case 1: if (airTypeOther != opt[op].type) { *vP = airFree(*vP); } else { /* alloc is one either because we parsed one thing, and we have a destroy callback, or, because we parsed a dynamically-created array of things, and we don't have a destroy callback */ if (opt[op].CB->destroy) { *vP = opt[op].CB->destroy(*vP); } else { *vP = airFree(*vP); } } break; case 2: if (airTypeString == opt[op].type) { for (i=0; i<(int)opt[op].min; i++) { /* HEY scrutinize casts */ str[i] = (char *)airFree(str[i]); } } else { for (i=0; i<(int)opt[op].min; i++) { /* HEY scrutinize casts */ vP[i] = opt[op].CB->destroy(vP[i]); } } break; case 3: if (airTypeString == opt[op].type) { for (ui=0; ui<*(opt[op].sawP); ui++) { (*strP)[ui] = (char *)airFree((*strP)[ui]); } *strP = (char **)airFree(*strP); } else { for (ui=0; ui<*(opt[op].sawP); ui++) { (*vAP)[ui] = opt[op].CB->destroy((*vAP)[ui]); } *vAP = (void **)airFree(*vAP); } break; } } return NULL; } /* ******** hestParseOrDie() ** ** dumb little function which encapsulate a common usage of hest: ** first, make sure hestOpt is valid with hestOptCheck(). Then, ** if argc is 0 (and !parm->noArgsIsNoProblem): maybe show ** info, usage, and glossary, all according to given flags, then exit(1) ** if parsing failed: show error message, and maybe usage and glossary, ** again according to boolean flags, then exit(1) ** if parsing succeeded: return */ void hestParseOrDie(hestOpt *opt, int argc, const char **argv, hestParm *parm, const char *me, const char *info, int doInfo, int doUsage, int doGlossary) { int E; int argcBad; char *errS; if (opt) { if (hestOptCheck(opt, &errS)) { fprintf(stderr, "ERROR in hest usage:\n%s\n", errS); free(errS); exit(1); } E = 0; /* argc is good if its non-zero, or (else) being zero is ok */ argcBad = !(argc || (parm && parm->noArgsIsNoProblem)); if ( argcBad || (E = hestParse(opt, argc, argv, &errS, parm)) ) { if (E) { if (argv[0] && !strcmp(argv[0], "--version")) { /* print version info and bail */ printf("Teem version %s (%s)\n", airTeemVersion, airTeemReleaseDate); hestParmFree(parm); hestOptFree(opt); exit(0); } else if (argv[0] && !strcmp(argv[0], "--help")) { /* actually, not an error, they were asking for help */ E = 0; } else { fprintf(stderr, "ERROR: %s\n", errS); } free(errS); } if (!E) { /* no error, just !argc */ if (doInfo && info) hestInfo(stdout, me?me:"", info, parm); } if (doUsage) hestUsage(E ? stderr : stdout, opt, me?me:"", parm); if (doGlossary) hestGlossary(E ? stderr : stdout, opt, parm); hestParmFree(parm); hestOptFree(opt); exit(1); } } return; } teem-1.11.0~svn6057/src/hest/methodsHest.c0000664000175000017500000002523612165631065020007 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hest.h" #include "privateHest.h" #include const int hestPresent = 42; hestParm * hestParmNew() { hestParm *parm; parm = AIR_CALLOC(1, hestParm); if (parm) { parm->verbosity = hestVerbosity; parm->respFileEnable = hestRespFileEnable; parm->elideSingleEnumType = hestElideSingleEnumType; parm->elideSingleOtherType = hestElideSingleOtherType; parm->elideSingleOtherDefault = hestElideSingleOtherDefault; parm->greedySingleString = hestGreedySingleString; parm->elideSingleNonExistFloatDefault = hestElideSingleNonExistFloatDefault; parm->elideMultipleNonExistFloatDefault = hestElideMultipleNonExistFloatDefault; parm->elideSingleEmptyStringDefault = hestElideSingleEmptyStringDefault; parm->elideMultipleEmptyStringDefault = hestElideMultipleEmptyStringDefault; parm->cleverPluralizeOtherY = hestCleverPluralizeOtherY; parm->columns = hestColumns; parm->respFileFlag = hestRespFileFlag; parm->respFileComment = hestRespFileComment; parm->varParamStopFlag = hestVarParamStopFlag; parm->multiFlagSep = hestMultiFlagSep; } return parm; } hestParm * hestParmFree(hestParm *parm) { airFree(parm); return NULL; } void _hestOptInit(hestOpt *opt) { opt->flag = opt->name = NULL; opt->type = opt->min = opt->max = 0; opt->valueP = NULL; opt->dflt = opt->info = NULL; opt->sawP = NULL; opt->enm = NULL; opt->CB = NULL; opt->sawP = NULL; opt->kind = opt->alloc = 0; } /* hestOpt * hestOptNew(void) { hestOpt *opt; opt = AIR_CALLOC(1, hestOpt); if (opt) { _hestOptInit(opt); opt->min = 1; } return opt; } */ void hestOptAdd(hestOpt **optP, const char *flag, const char *name, int type, int min, int max, void *valueP, const char *dflt, const char *info, ...) { hestOpt *ret = NULL; int num; va_list ap; void *dummy = NULL; if (!optP) return; num = *optP ? _hestNumOpts(*optP) : 0; if (!( ret = AIR_CALLOC(num+2, hestOpt) )) { return; } if (num) memcpy(ret, *optP, num*sizeof(hestOpt)); ret[num].flag = airStrdup(flag); ret[num].name = airStrdup(name); ret[num].type = type; ret[num].min = min; ret[num].max = max; ret[num].valueP = valueP; ret[num].dflt = airStrdup(dflt); ret[num].info = airStrdup(info); /* initialize the things that may be set below */ ret[num].sawP = NULL; ret[num].enm = NULL; ret[num].CB = NULL; /* deal with var args */ if (5 == _hestKind(&(ret[num]))) { va_start(ap, info); ret[num].sawP = va_arg(ap, unsigned int*); va_end(ap); } if (airTypeEnum == type) { va_start(ap, info); dummy = (void *)(va_arg(ap, unsigned int*)); /* skip sawP */ ret[num].enm = va_arg(ap, airEnum*); va_end(ap); } if (airTypeOther == type) { va_start(ap, info); dummy = (void *)(va_arg(ap, unsigned int*)); /* skip sawP */ dummy = (void *)(va_arg(ap, airEnum*)); /* skip enm */ ret[num].CB = va_arg(ap, hestCB*); va_end(ap); } _hestOptInit(&(ret[num+1])); ret[num+1].min = 1; if (*optP) free(*optP); *optP = ret; AIR_UNUSED(dummy); return; } void _hestOptFree(hestOpt *opt) { opt->flag = (char *)airFree(opt->flag); opt->name = (char *)airFree(opt->name); opt->dflt = (char *)airFree(opt->dflt); opt->info = (char *)airFree(opt->info); return; } hestOpt * hestOptFree(hestOpt *opt) { int op, num; if (!opt) return NULL; num = _hestNumOpts(opt); if (opt[num].min) { /* we only try to free this if it looks like something we allocated */ for (op=0; opflag && (sep = strchr(opt->flag, parm->multiFlagSep))) { strcpy(copy, opt->flag); sep = strchr(copy, parm->multiFlagSep); *sep = '\0'; if (brief) sprintf(ident, "-%s%c--%s option", copy, parm->multiFlagSep, sep+1); else sprintf(ident, "-%s option", copy); } else { sprintf(ident, "%s%s%s option", opt->flag ? "\"-" : "<", opt->flag ? opt->flag : opt->name, opt->flag ? "\"" : ">"); } return ident; } int _hestMax(int max) { if (-1 == max) { max = INT_MAX; } return max; } int _hestKind(hestOpt *opt) { int max; max = _hestMax(opt->max); if (!( (int)opt->min <= max )) { /* HEY scrutinize casts */ /* invalid */ return -1; } if (0 == opt->min && 0 == max) { /* flag */ return 1; } if (1 == opt->min && 1 == max) { /* single fixed parameter */ return 2; } if (2 <= opt->min && 2 <= max && (int)opt->min == max) { /* HEY scrutinize casts */ /* multiple fixed parameters */ return 3; } if (0 == opt->min && 1 == max) { /* single optional parameter */ return 4; } /* else multiple variable parameters */ return 5; } void _hestPrintArgv(int argc, char **argv) { int a; printf("argc=%d : ", argc); for (a=0; avarParamStopFlag) */ int _hestWhichFlag(hestOpt *opt, char *flag, hestParm *parm) { char buff[AIR_STRLEN_HUGE], copy[AIR_STRLEN_HUGE], *sep; int op, numOpts; numOpts = _hestNumOpts(opt); if (parm->verbosity) printf("_hestWhichFlag: flag = %s, numOpts = %d\n", flag, numOpts); for (op=0; opverbosity) printf("_hestWhichFlag: op = %d\n", op); if (!opt[op].flag) continue; if (strchr(opt[op].flag, parm->multiFlagSep) ) { strcpy(copy, opt[op].flag); sep = strchr(copy, parm->multiFlagSep); *sep = '\0'; /* first try the short version */ sprintf(buff, "-%s", copy); if (!strcmp(flag, buff)) return op; /* then try the long version */ sprintf(buff, "--%s", sep+1); if (!strcmp(flag, buff)) return op; } else { /* flag has only the short version */ sprintf(buff, "-%s", opt[op].flag); if (!strcmp(flag, buff)) return op; } } if (parm->verbosity) printf("_hestWhichFlag: numOpts = %d\n", numOpts); if (parm->varParamStopFlag) { sprintf(buff, "-%c", parm->varParamStopFlag); if (parm->verbosity) printf("_hestWhichFlag: flag = %s, buff = %s\n", flag, buff); if (!strcmp(flag, buff)) return -2; } if (parm->verbosity) printf("_hestWhichFlag: numOpts = %d\n", numOpts); return -1; } /* ** _hestCase() ** ** helps figure out logic of interpreting parameters and defaults ** for kind 4 and kind 5 options. */ int _hestCase(hestOpt *opt, int *udflt, unsigned int *nprm, int *appr, int op) { if (opt[op].flag && !appr[op]) { return 0; } else if ( (4 == opt[op].kind && udflt[op]) || (5 == opt[op].kind && !nprm[op]) ) { return 1; } else { return 2; } } /* ** _hestExtract() ** ** takes "pnum" parameters, starting at "base", out of the ** given argv, and puts them into a string WHICH THIS FUNCTION ** ALLOCATES, and also adjusts the argc value given as "*argcP". */ char * _hestExtract(int *argcP, char **argv, unsigned int base, unsigned int pnum) { unsigned int len, pidx; char *ret; if (!pnum) return NULL; len = 0; for (pidx=0; pidxmax); if (O->flag) { strcpy(copy, O->flag); if ((sep = strchr(copy, P->multiFlagSep))) { *sep = 0; if (showshort) { strcat(B, "-"); strcat(B, copy); } if (showlong) { if (showshort) { len = AIR_UINT(strlen(B)); B[len] = P->multiFlagSep; B[len+1] = '\0'; } strcat(B, "--"); strcat(B, sep+1); } } else { strcat(B, "-"); strcat(B, O->flag); } if (O->min || max) { strcat(B, "\t"); } } if (!O->min && max) { strcat(B, "["); } if (O->min || max) { strcat(B, "<"); strcat(B, O->name); if ((int)(O->min) < max && max > 1) { /* HEY scrutinize casts */ strcat(B, "\t..."); } strcat(B, ">"); } if (!O->min && max) { strcat(B, "]"); } } /* early version of _hestSetBuff() function */ #define SETBUFF(B, O) \ strcat(B, O.flag ? "-" : ""), \ strcat(B, O.flag ? O.flag : ""), \ strcat(B, O.flag && (O.min || _hestMax(O.max)) ? "\t" : ""), \ strcat(B, !O.min && _hestMax(O.max) ? "[" : ""), \ strcat(B, O.min || _hestMax(O.max) ? "<" : ""), \ strcat(B, O.min || _hestMax(O.max) ? O.name : ""), \ strcat(B, (O.min < _hestMax(O.max) && (_hestMax(O.max) > 1)) ? " ...": ""), \ strcat(B, O.min || _hestMax(O.max) ? ">" : ""), \ strcat(B, !O.min && _hestMax(O.max) ? "]" : ""); /* ** _hestPrintStr() ** ** not a useful function. Do not use. */ void _hestPrintStr(FILE *f, unsigned int indent, unsigned int already, unsigned int width, const char *_str, int bslash) { char *str, *ws, *last; int newed=AIR_FALSE; unsigned int wrd, nwrd, ii, pos; str = airStrdup(_str); nwrd = airStrntok(str, " "); pos = already; for (wrd=0; wrdrespFileEnable is false. For example, in unrrdu (private.h) ** we find: ** ** if ( (hparm->respFileEnable && !argc) || ** (!hparm->respFileEnable && argc < hestMinNumArgs(opt)) ) { ** ... usage ... ** } ** */ int hestMinNumArgs(hestOpt *opt) { hestParm *parm; int i, count, numOpts; parm = hestParmNew(); if (_hestPanic(opt, NULL, parm)) { parm = hestParmFree(parm); return _hestMax(-1); } count = 0; numOpts = _hestNumOpts(opt); for (i=0; icolumns, info, AIR_FALSE); } else { fprintf(file, "ERROR: hestInfo got NULL argv0\n"); } } parm = !_parm ? hestParmFree(parm) : NULL; } void hestUsage(FILE *f, hestOpt *opt, const char *argv0, hestParm *_parm) { int i, numOpts; char buff[2*AIR_STRLEN_HUGE], tmpS[AIR_STRLEN_HUGE]; hestParm *parm; parm = !_parm ? hestParmNew() : _parm; if (_hestPanic(opt, NULL, parm)) { /* we can't continue; the opt array is botched */ parm = !_parm ? hestParmFree(parm) : NULL; return; } numOpts = _hestNumOpts(opt); fprintf(f, "\n"); strcpy(buff, "Usage: "); strcat(buff, argv0 ? argv0 : ""); if (parm && parm->respFileEnable) { sprintf(tmpS, " [%cfile\t...]", parm->respFileFlag); strcat(buff, tmpS); } for (i=0; icolumns, buff, AIR_TRUE); parm = !_parm ? hestParmFree(parm) : NULL; return; } void hestGlossary(FILE *f, hestOpt *opt, hestParm *_parm) { int i, j, maxlen, numOpts; unsigned int len; char buff[2*AIR_STRLEN_HUGE], tmpS[AIR_STRLEN_HUGE]; hestParm *parm; parm = !_parm ? hestParmNew() : _parm; if (_hestPanic(opt, NULL, parm)) { /* we can't continue; the opt array is botched */ parm = !_parm ? hestParmFree(parm) : NULL; return; } numOpts = _hestNumOpts(opt); maxlen = 0; if (numOpts) { fprintf(f, "\n"); } for (i=0; irespFileEnable) { sprintf(buff, "%cfile ...", parm->respFileFlag); len = AIR_UINT(strlen(buff)); for (j=len; jcolumns, buff, AIR_FALSE); } for (i=0; imultiFlagSep)) { /* there is a long-form flag as well as short */ _hestSetBuff(buff, opt + i, parm, AIR_FALSE, AIR_TRUE); strcat(buff, " = "); fprintf(f, " , "); } else { /* there is only a short-form flag */ fprintf(f, " = "); } #else fprintf(f, " = "); #endif if (opt[i].info) { strcat(buff, opt[i].info); } if ((opt[i].min || _hestMax(opt[i].max)) && (!( 2 == opt[i].kind && airTypeEnum == opt[i].type && parm->elideSingleEnumType )) && (!( 2 == opt[i].kind && airTypeOther == opt[i].type && parm->elideSingleOtherType )) ) { /* if there are newlines in the info, then we want to clarify the type by printing it on its own line */ if (opt[i].info && strchr(opt[i].info, '\n')) { strcat(buff, "\n "); } else { strcat(buff, " "); } strcat(buff, "("); if (opt[i].min == 0 && _hestMax(opt[i].max) == 1) { strcat(buff, "optional\t"); } else { if ((int)opt[i].min == _hestMax(opt[i].max) && _hestMax(opt[i].max) > 1) { /* HEY scrutinize casts */ sprintf(tmpS, "%d\t", _hestMax(opt[i].max)); strcat(buff, tmpS); } else if ((int)opt[i].min < _hestMax(opt[i].max)) { /* HEY scrutinize casts */ if (-1 == opt[i].max) { sprintf(tmpS, "%d\tor\tmore\t", opt[i].min); } else { sprintf(tmpS, "%d..%d\t", opt[i].min, _hestMax(opt[i].max)); } strcat(buff, tmpS); } } sprintf(tmpS, "%s%s", (airTypeEnum == opt[i].type ? opt[i].enm->name : (airTypeOther == opt[i].type ? opt[i].CB->type : airTypeStr[opt[i].type])), (_hestMax(opt[i].max) > 1 ? (airTypeOther == opt[i].type && 'y' == opt[i].CB->type[airStrlen(opt[i].CB->type)-1] && parm->cleverPluralizeOtherY ? "\bies" : "s") : "")); strcat(buff, tmpS); strcat(buff, ")"); } /* fprintf(stderr, "!%s: parm->elideSingleOtherDefault = %d\n", "hestGlossary", parm->elideSingleOtherDefault); */ if (opt[i].dflt && (opt[i].min || _hestMax(opt[i].max)) && (!( 2 == opt[i].kind && (airTypeFloat == opt[i].type || airTypeDouble == opt[i].type) && !AIR_EXISTS(airAtod(opt[i].dflt)) && parm->elideSingleNonExistFloatDefault )) && (!( (3 == opt[i].kind || 5 == opt[i].kind) && (airTypeFloat == opt[i].type || airTypeDouble == opt[i].type) && !AIR_EXISTS(airAtod(opt[i].dflt)) && parm->elideMultipleNonExistFloatDefault )) && (!( 2 == opt[i].kind && airTypeOther == opt[i].type && parm->elideSingleOtherDefault )) && (!( 2 == opt[i].kind && airTypeString == opt[i].type && parm->elideSingleEmptyStringDefault && 0 == airStrlen(opt[i].dflt) )) && (!( (3 == opt[i].kind || 5 == opt[i].kind) && airTypeString == opt[i].type && parm->elideMultipleEmptyStringDefault && 0 == airStrlen(opt[i].dflt) )) ) { /* if there are newlines in the info, then we want to clarify the default by printing it on its own line */ if (opt[i].info && strchr(opt[i].info, '\n')) { strcat(buff, "\n "); } else { strcat(buff, "; "); } strcat(buff, "default:\t"); strcpy(tmpS, opt[i].dflt); airStrtrans(tmpS, ' ', '\t'); strcat(buff, "\""); strcat(buff, tmpS); strcat(buff, "\""); } _hestPrintStr(f, maxlen + 3, maxlen + 3, parm->columns, buff, AIR_FALSE); } parm = !_parm ? hestParmFree(parm) : NULL; return; } teem-1.11.0~svn6057/src/hest/hest.h0000664000175000017500000002563212165631065016470 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HEST_HAS_BEEN_INCLUDED #define HEST_HAS_BEEN_INCLUDED #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(hest_EXPORTS) || defined(teem_EXPORTS) # define HEST_EXPORT extern __declspec(dllexport) # else # define HEST_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define HEST_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif /* ******** hestCB struct ** ** for when the thing you want to parse from the command-line is not a ** simple boolean, number, airEnum, or string. hestParse() will not ** allocate anything to store individual things, though it may ** allocate an array in the case of a multiple variable parameter ** option. If your things are actually pointers to things, then you ** do the allocation in the parse() callback. In this case, you set ** destroy() to be your "destructor", and it will be called on the ** result of derefencing the argument to parse(). */ typedef struct { size_t size; /* sizeof() one thing */ const char *type; /* used by hestGlossary() to describe the type */ int (*parse)(void *ptr, char *str, char err[AIR_STRLEN_HUGE]); /* how to parse one thing from a string. This will be called multiple times for multiple parameter options. A non-zero return value is considered an error. Error message go in the err string */ void *(*destroy)(void *ptr); /* if non-NULL, this is the destructor that will be called by hestParseFree() (or by hestParse() if there is an error midway through parsing). The argument is NOT the same as passed to parse(): it is the result of dereferencing the argument to parse() */ } hestCB; /* ******** hestOpt struct ** ** information which specifies one command-line option */ typedef struct { char *flag, /* how the option is identified on the cmd line */ *name; /* simple description of option's parameter(s) */ int type; /* type of option (from airType enum) */ unsigned int min; int max; /* min and max # of parameters for option */ void *valueP; /* storage of parsed values */ char *dflt, /* default value written out as string */ *info; /* description to be printed with "glossary" info */ unsigned int *sawP; /* used ONLY for multiple variable parameter options (min < max > 2): storage of # of parsed values */ airEnum *enm; /* used ONLY for airTypeEnum options */ hestCB *CB; /* used ONLY for airTypeOther options */ /* --------------------- end of user-defined fields */ int kind, /* what kind of option is this, based on min and max, set by hestParse() (actually _hestPanic()), later used by hestFree(): 1: min == max == 0 stand-alone flag; no parameters 2: min == max == 1 single fixed parameter 3: min == max >= 2 multiple fixed parameters 4: min == 0; max == 1; single variable parameter 5: max - min >= 1; max >= 2 multiple variable parameters */ alloc; /* information about whether flag is non-NULL, and what parameters were used, that determines whether or not memory was allocated by hestParse(); info later used by hestParseFree(): 0: no free()ing needed 1: free(*valueP), either because it is a single string, or because was a dynamically allocated array of non-strings 2: free((*valueP)[i]), because they are elements of a fixed-length array of strings 3: free((*valueP)[i]) and free(*valueP), because it is a dynamically allocated array of strings */ } hestOpt; /* ******** hestParm struct ** ** parameters to control behavior of hest functions. ** ** GK: Don't even think about storing per-parse state in here. */ typedef struct { int verbosity, /* verbose diagnostic messages to stdout */ respFileEnable, /* whether or not to use response files */ elideSingleEnumType, /* if type is airTypeEnum, and if its a single fixed parameter option, then don't bother printing the type information as part of hestGlossary() */ elideSingleOtherType, /* like above, but for airTypeOther */ elideSingleOtherDefault, /* don't display default for single fixed airTypeOther parameter */ elideSingleNonExistFloatDefault, /* if default for a single fixed floating point (float or double) parameter doesn't AIR_EXIST, then don't display the default */ elideMultipleNonExistFloatDefault, elideSingleEmptyStringDefault, /* if default for a single string is empty (""), then don't display default */ elideMultipleEmptyStringDefault, noArgsIsNoProblem, /* if non-zero, having no arguments to parse is not in and of itself a problem; this means that if all options have defaults, it would be *ok* to invoke the problem without any further command-line options. This is counter to pre-Teem-1.11 behavior (for which no arguments *always* meant "show me usage info"). */ greedySingleString, /* when parsing a single string, whether or not to be greedy (as per airParseStrS) */ cleverPluralizeOtherY; /* when printing the type for airTypeOther, when the min number of items is > 1, and the type string ends with "y", then pluralize with "ies" instead of "ys" */ unsigned int columns; /* number of printable columns in output */ char respFileFlag, /* the character at the beginning of an argument indicating that this is a response file name */ respFileComment, /* comment character for the repose files */ varParamStopFlag, /* prefixed by '-' to form the flag which signals the end of a flagged variable parameter option (single or multiple) */ multiFlagSep; /* character in flag which signifies that there is a long and short version, and which separates the two. Or, can be set to '\0' to disable this behavior entirely. */ } hestParm; /* defaultsHest.c */ /* HEY: recent Teem conventions say these should all start with "hestDef" not just "hest" */ HEST_EXPORT int hestVerbosity; HEST_EXPORT int hestRespFileEnable; HEST_EXPORT int hestElideSingleEnumType; HEST_EXPORT int hestElideSingleOtherType; HEST_EXPORT int hestElideSingleOtherDefault; HEST_EXPORT int hestElideSingleNonExistFloatDefault; HEST_EXPORT int hestElideMultipleNonExistFloatDefault; HEST_EXPORT int hestElideSingleEmptyStringDefault; HEST_EXPORT int hestElideMultipleEmptyStringDefault; HEST_EXPORT int hestNoArgsIsNoProblem; HEST_EXPORT int hestGreedySingleString; HEST_EXPORT int hestCleverPluralizeOtherY; HEST_EXPORT unsigned int hestColumns; HEST_EXPORT char hestRespFileFlag; HEST_EXPORT char hestRespFileComment; HEST_EXPORT char hestVarParamStopFlag; HEST_EXPORT char hestMultiFlagSep; /* methodsHest.c */ HEST_EXPORT const int hestPresent; HEST_EXPORT hestParm *hestParmNew(void); HEST_EXPORT hestParm *hestParmFree(hestParm *parm); HEST_EXPORT void hestOptAdd(hestOpt **optP, const char *flag, const char *name, int type, int min, int max, void *valueP, const char *dflt, const char *info, ... /* int *sawP, airEnum *enm , hestCB *CB */); HEST_EXPORT hestOpt *hestOptFree(hestOpt *opt); HEST_EXPORT int hestOptCheck(hestOpt *opt, char **errP); /* parseHest.c */ HEST_EXPORT int hestParse(hestOpt *opt, int argc, const char **argv, char **errP, hestParm *parm); HEST_EXPORT void *hestParseFree(hestOpt *opt); HEST_EXPORT void hestParseOrDie(hestOpt *opt, int argc, const char **argv, hestParm *parm, const char *me, const char *info, int doInfo, int doUsage, int doGlossary); /* usage.c */ HEST_EXPORT void _hestPrintStr(FILE *f, unsigned int indent, unsigned int already, unsigned int width, const char *_str, int bslash); HEST_EXPORT int hestMinNumArgs(hestOpt *opt); HEST_EXPORT void hestUsage(FILE *file, hestOpt *opt, const char *argv0, hestParm *parm); HEST_EXPORT void hestGlossary(FILE *file, hestOpt *opt, hestParm *parm); HEST_EXPORT void hestInfo(FILE *file, const char *argv0, const char *info, hestParm *parm); #ifdef __cplusplus } #endif #endif /* HEST_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/hest/test/0000775000175000017500000000000012203513754016320 5ustar domibeldomibelteem-1.11.0~svn6057/src/hest/test/ex1.c0000664000175000017500000000667412042370260017170 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../hest.h" /* ** this example has been taken out of the build because GLK was sure ** Fri May 13 00:51:11 CDT 2011 how to handle these annoying warnings: ../hest/test/ex1.c: In function 'main': ../hest/test/ex1.c:34: warning: missing initializer ../hest/test/ex1.c:34: warning: (near initialization for 'opt[0].sawP') ../hest/test/ex1.c:36: warning: missing initializer ../hest/test/ex1.c:36: warning: (near initialization for 'opt[1].sawP') */ int main(int argc, char **argv) { static int res[2], v, numIn; static char **in, *out; static int *mm, mmm; int n; hestOpt opt[] = { {"res", "sx sy", airTypeInt, 2, 2, res, NULL, "image resolution"}, {"v", "level", airTypeInt, 0, 1, &v, NULL /* "0" */, "verbosity level"}, {"VV", "level", airTypeInt, 0, 5, &mm, "33 22 11", "gonzo level", &mmm}, {"out", "file", airTypeString, 1, 1, &out, "output.ppm", "PPM image output"}, {NULL, "input", airTypeString, 1, -1, &in, NULL, "input image file(s)", &numIn}, {NULL, NULL, 0} }; hestParm *parm; char *err = NULL, info[] = "This program does nothing in particular, though it does attempt " "to pose as some sort of command-line image processing program. " "Any implied functionality is purely coincidental, especially since " "this software was written by a sleep-deprived grad student."; parm = hestParmNew(); parm->respFileEnable = AIR_TRUE; if (1 == argc) { /* didn't get anything at all on command line */ hestInfo(stderr, argv[0], info, parm); hestUsage(stderr, opt, argv[0], parm); hestGlossary(stderr, opt, parm); parm = hestParmFree(parm); exit(1); } /* else we got something, see if we can parse it */ if (hestParse(opt, argc-1, argv+1, &err, parm)) { fprintf(stderr, "ERROR: %s\n", err); free(err); hestUsage(stderr, opt, argv[0], parm); hestGlossary(stderr, opt, parm); parm = hestParmFree(parm); exit(1); } printf("(err = %s)\n", err); printf("res = %d %d\n", res[0], res[1]); printf(" v = %d\n", v); printf("out = \"%s\"\n", out); printf(" mm = %d ints:", mmm); for (n=0; n<=mmm-1; n++) { printf(" %d", mm[n]); } printf("\n"); printf(" in = %d files:", numIn); for (n=0; n<=numIn-1; n++) { printf(" \"%s\"", in[n]); } printf("\n"); hestParseFree(opt); parm = hestParmFree(parm); exit(0); } teem-1.11.0~svn6057/src/hest/test/bday.c0000664000175000017500000000570112042370260017400 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../hest.h" int runexp(int *year, int N, int M) { int i, m; memset(year, 0, N*sizeof(int)); for (m=0; m 1) { return 1; } } return 0; } char *info = ("simulates M people in a room finding out if two or more " "of them share a birthday. For fun, can vary the number " "of days in the year."); int main(int argc, const char *argv[]) { airArray *mop; hestOpt *hopt=NULL; int i, N, M, P, yes, *year; unsigned int E; const char *me; double crct; me = argv[0]; mop = airMopNew(); hestOptAdd(&hopt, "N", "days", airTypeInt, 1, 1, &N, "365", "# of days in year"); /* E != P */ hestOptAdd(&hopt, "E", "exps", airTypeInt, 1, 1, &P, "100000", "number of experiments after which to print out newly " "computed probability"); hestOptAdd(&hopt, NULL, "people", airTypeInt, 1, 1, &M, NULL, "# of people in room"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( N > 1 && M > 0 && P > 1)) { fprintf(stderr, "%s: need both M, P all > 1, M > 0\n", me); airMopError(mop); exit(1); } if (!(year = (int*)calloc(N, sizeof(int)))) { fprintf(stderr, "%s: couldn't calloc(%d,sizeof(int))\n", me, N); airMopError(mop); exit(1); } airMopMem(mop, year, airMopAlways); crct = 1; for (i=0; irespFileEnable = AIR_TRUE; opt = NULL; hestOptAdd(&opt, "res", "sx sy", airTypeInt, 2, 2, res, NULL, "image resolution"); hestOptAdd(&opt, "v", "level", airTypeInt, 0, 1, &v, "0", "verbosity level"); hestOptAdd(&opt, "out", "file", airTypeString, 1, 1, &out, "output.ppm", "PPM image output"); hestOptAdd(&opt, NULL, "input", airTypeString, 1, -1, &in, NULL, "input image file(s)", &numIn); if (1 == argc) { /* didn't get anything at all on command line */ /* print program information ... */ hestInfo(stderr, argv[0], info, parm); /* ... and usage information ... */ hestUsage(stderr, opt, argv[0], parm); hestGlossary(stderr, opt, parm); /* ... and avoid memory leaks */ opt = hestOptFree(opt); parm = hestParmFree(parm); exit(1); } /* else we got something, see if we can parse it */ if (hestParse(opt, argc-1, argv+1, &err, parm)) { fprintf(stderr, "ERROR: %s\n", err); free(err); /* print usage information ... */ hestUsage(stderr, opt, argv[0], parm); hestGlossary(stderr, opt, parm); /* ... and then avoid memory leaks */ opt = hestOptFree(opt); parm = hestParmFree(parm); exit(1); } printf("(err = %s)\n", err); printf("res = %d %d\n", res[0], res[1]); printf(" v = %d\n", v); printf("out = \"%s\"\n", out); printf(" in = %d files:", numIn); for (n=0; n<=numIn-1; n++) { printf(" \"%s\"", in[n]); } printf("\n"); /* free the memory allocated by parsing ... */ hestParseFree(opt); /* ... and the other stuff */ opt = hestOptFree(opt); parm = hestParmFree(parm); exit(0); } teem-1.11.0~svn6057/src/hest/test/ex3.c0000664000175000017500000001030712042370260017156 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../hest.h" int main(int argc, const char **argv) { int res[2], v, numIn; char **in, *out, *blah[3], *option = NULL; int n, *ints, numN; hestOpt *opt = NULL; hestParm *parm; char *err = NULL, info[] = "This program does nothing in particular, though it does attempt " "to pose as some sort of command-line image processing program. " "As usual, any implied functionality is purely coincidental, " "especially since this is the output of a unicyclist."; parm = hestParmNew(); parm->respFileEnable = AIR_TRUE; parm->verbosity = 3; opt = NULL; hestOptAdd(&opt, "v,verbose", "level", airTypeInt, 0, 1, &v, "0", "verbosity level"); hestOptAdd(&opt, "out", "file", airTypeString, 1, 1, &out, "output.ppm", "PPM image output"); hestOptAdd(&opt, "blah", "input", airTypeString, 3, 3, blah, "a b c", "input image file(s)"); hestOptAdd(&opt, "option","opt", airTypeString, 0, 1, &option, "default", "this is just a test"); hestOptAdd(&opt, NULL, "input", airTypeString, 1, -1, &in, NULL, "input image file(s)", &numIn); hestOptAdd(&opt, "ints", "N", airTypeInt, 1, -1, &ints, "10 20 30", "a list of integers", &numN); hestOptAdd(&opt, "res", "sx sy", airTypeInt, 2, 2, res, NULL, "image resolution"); printf("what 0\n"); if (1 == argc) { /* didn't get anything at all on command line */ /* print program information ... */ hestInfo(stderr, argv[0], info, parm); /* ... and usage information ... */ hestUsage(stderr, opt, argv[0], parm); hestGlossary(stderr, opt, parm); /* ... and avoid memory leaks */ opt = hestOptFree(opt); parm = hestParmFree(parm); exit(1); } printf("what 1\n"); /* else we got something, see if we can parse it */ if (hestParse(opt, argc-1, argv+1, &err, parm)) { fprintf(stderr, "ERROR: %s\n", err); free(err); /* print usage information ... */ hestUsage(stderr, opt, argv[0], parm); hestGlossary(stderr, opt, parm); /* ... and then avoid memory leaks */ opt = hestOptFree(opt); parm = hestParmFree(parm); printf(" ---- in = %lx\n", (unsigned long)in); printf(" ---- blah[0] = %lx\n", (unsigned long)(blah[0])); printf(" ---- option = %lx\n", (unsigned long)option); exit(1); } printf("what 2\n"); printf("(err = %s)\n", err ? err : "(null)"); printf(" v = %d\n", v); printf("out = \"%s\"\n", out ? out : "(null)"); printf("blah = \"%s\" \"%s\" \"%s\"\n", blah[0], blah[1], blah[2]); printf("option = \"%s\"\n", option ? option : "(null)"); printf("res = %d %d\n", res[0], res[1]); /* printf(" ---- in = %lx\n", (unsigned long)in); printf(" in = %d files:", numIn); for (n=0; n<=numIn-1; n++) { printf(" \"%s\"", in[n] ? in[n] : "(null)"); } printf("\n"); */ printf(" ints = %d ints:", numN); for (n=0; n<=numN-1; n++) { printf(" %d", ints[n]); } printf("\n"); printf("what 3\n"); /* free the memory allocated by parsing ... */ hestParseFree(opt); /* ... and the other stuff */ opt = hestOptFree(opt); parm = hestParmFree(parm); exit(0); } teem-1.11.0~svn6057/src/hest/test/ex0.c0000664000175000017500000000424512042370260017157 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../hest.h" /* ** this example has been taken out of the build because GLK was sure ** Fri May 13 00:51:11 CDT 2011 how to handle these annoying warnings: ../hest/test/ex0.c: In function 'main': ../hest/test/ex0.c:32: warning: missing initializer ../hest/test/ex0.c:32: warning: (near initialization for 'opt[0].sawP') ../hest/test/ex0.c:34: warning: missing initializer ../hest/test/ex0.c:34: warning: (near initialization for 'opt[1].sawP') */ int main(int argc, char **argv) { static int res[2], v, numIn; static char **in, *out; hestOpt opt[] = { {"res", "sx sy", airTypeInt, 2, 2, res, NULL, "image resolution"}, {"v", "level", airTypeInt, 1, 1, &v, "0", "verbosity level"}, {"out", "file", airTypeString, 1, 1, &out, "output.ppm", "PPM image output"}, {NULL, "input", airTypeString, 1, -1, &in, NULL, "input image file(s)", &numIn}, {NULL, NULL, 0} }; char *err = NULL; if (hestOptCheck(opt, &err)) { fprintf(stderr, "ERROR: %s\n", err); free(err); exit(1); } printf("hestOpt array looks fine.\n"); exit(0); } teem-1.11.0~svn6057/src/hest/test/ex4.c0000664000175000017500000000613712042370260017165 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../hest.h" int parse(void *_ptr, char *str, char *err) { double *ptr; int ret; ptr = _ptr; ret = sscanf(str, "%lf,%lf", ptr + 0, ptr + 1); if (2 != ret) { sprintf(err, "parsed %d (not 2) doubles", ret); return 1; } return 0; } hestCB cbinfo = { 2*sizeof(double), "location", parse, NULL }; int main(int argc, const char **argv) { double single[2], triple[6], maybe[2], *many; int howMany, i, N; hestOpt *opt = NULL; char *err = NULL; hestOptAdd(&opt, "A", "x,y", airTypeOther, 1, 1, single, "30,50", "testing A", NULL, NULL, &cbinfo); hestOptAdd(&opt, "B", "x1,y1 x2,y2 x3,y3", airTypeOther, 3, 3, triple, "1,2 3,4 5,6", "testing B", NULL, NULL, &cbinfo); hestOptAdd(&opt, "C", "mx,my", airTypeOther, 0, 1, maybe, "-0.1,-0.2", "testing C. The utility of this can be better " "demonstrated in the following manner:\n " "- wash the dishes\n " "- put the dishes in the cupboard\n " "- watch football on TV\n " "- remember to walk the dog", NULL, NULL, &cbinfo); hestOptAdd(&opt, "D", "nx,ny", airTypeOther, 1, -1, &many, "8,8 7,7", "testing D", &howMany, NULL, &cbinfo); hestOptAdd(&opt, "int", "N", airTypeInt, 1, 1, &N, NULL, "an integer"); if (hestParse(opt, argc-1, argv+1, &err, NULL)) { fprintf(stderr, "ERROR: %s\n", err); free(err); hestUsage(stderr, opt, argv[0], NULL); hestGlossary(stderr, opt, NULL); exit(1); } printf("single: (%g,%g)\n", single[0], single[1]); printf("triple: (%g,%g) (%g,%g) (%g,%g)\n", triple[0], triple[1], triple[2], triple[3], triple[4], triple[5]); printf("maybe: (%g,%g)\n", maybe[0], maybe[1]); printf("many(%d):", howMany); for (i=0; i<=howMany-1; i++) { printf(" (%g,%g)", many[0 + 2*i], many[1 + 2*i]); } printf("\n"); hestParseFree(opt); exit(0); } teem-1.11.0~svn6057/src/hest/test/strings.c0000664000175000017500000000613312042370260020152 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../hest.h" char *me; extern void _airCloPrintStr(FILE *f, int indent, int already, int width, char *_str); int main() { char line[1025]; strcpy(line, "bingo \t\t bob \t boasts \n bumperstickers \n "); airOneLinify(line); printf("|%s|\n", line); strcpy(line, " \t \n "); airOneLinify(line); printf("|%s|\n", line); strcpy(line, "Director, writor, and editor John Sayles goes happily " "against the grain with this tale, set in a fictional Latin " "American country and shot almost entirely in Spanish and Indian " "dialects. The story follows a well-to-do physician who has trained " "young doctors to work in the countryside among local Mayan Indians. " "He now wants to find each of the 'ambassadors of health,' but as " "the film unfolds, he comes to realize that a civil war is engulfing " "his country and the Indians are practically ensalved. The\tMen\t" "with\tGuns (Los\tHombres\tArmados) have forever left their mark, " "too, on " "his students. Sayles based his idea for the film, shot in Mexico, " "on the 36-year long civil war in Guatemala, which began in 1960."); printf("airStrlen(line) = %d\n", (int)airStrlen(line)); fprintf(stdout, "This was found on my desk: "); _hestPrintStr(stdout, 10, strlen("This was found on my desk: "), 80, line, AIR_FALSE); fprintf(stdout, "This was found on my desk: "); _hestPrintStr(stdout, 10, strlen("This was found on my desk: "), 79, line, AIR_FALSE); fprintf(stdout, "This was found on my desk: "); _hestPrintStr(stdout, 10, strlen("This was found on my desk: "), 78, line, AIR_FALSE); fprintf(stdout, "This was found on my desk: "); _hestPrintStr(stdout, 10, strlen("This was found on my desk: "), 77, line, AIR_FALSE); fprintf(stdout, "This was found on my desk: "); _hestPrintStr(stdout, 10, strlen("This was found on my desk: "), 76, line, AIR_FALSE); exit(0); } teem-1.11.0~svn6057/src/hest/TODO.txt0000664000175000017500000000304607740220625016654 0ustar domibeldomibelmake new calls for setting the different kinds of options, so that reliance on var-args is minimized for super long things like "tend glyph", it would be nice to have seperator lines between the long versions of options, to visually group them somewhat. currently the "input nrrd" option to unu comes last as a way of allowing hest to do error checking on all other (more error prone) options. But it would be nice to indicate a parsing order seperate from the option order. basic problems with hest: - there's no such thing as a trully optional parameter: data is needed for all options, whether it comes from the default string or the command line, and its an error if no data is found. Not having optional parameters is getting in the way of things like allowing "unu make" taking centerings. - there's no way to tell whether the information for any given option came from the default string, or from the command line - there's no way to access the string that was parsed to get the information for an option (could be from default, or command-line, or response file) - (from tendGlyph.c): learned: a huge problem with hest and its var-arg-based hestOptAdd is that it can't tell when you've based multiple strings for the detailed usage information by accident. I had accidentally inserted a comma into my multi-line string for the "info" arg, relying on the automatic string concatenation, and ended up passing total garbage to hestOptAdd for the airEnum pointer, causing me to think that the tenGlyphType airEnum was malformed, when it was in fact fine ... teem-1.11.0~svn6057/src/tijk/0000775000175000017500000000000012203513757015342 5ustar domibeldomibelteem-1.11.0~svn6057/src/tijk/2dTijk.c0000664000175000017500000007636412042325046016646 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2010, 2009, 2008 Thomas Schultz Copyright (C) 2010, 2009, 2008 Gordon Kindlmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Implementation of two-dimensional tensors */ #include "tijk.h" #include "privateTijk.h" #include "convertQuietPush.h" /* 2nd order 2D unsymmetric */ double _tijk_2o2d_unsym_tsp_d (const double *A, const double *B) { return ELL_4V_DOT(A,B); } float _tijk_2o2d_unsym_tsp_f (const float *A, const float *B) { return ELL_4V_DOT(A,B); } double _tijk_2o2d_unsym_norm_d (const double *A) { return sqrt(ELL_4V_DOT(A,A)); } float _tijk_2o2d_unsym_norm_f (const float *A) { return sqrt(ELL_4V_DOT(A,A)); } void _tijk_2o2d_unsym_trans_d (double *res, const double *A, const double *M) { double _ma[4], _mt[4]; ELL_2M_MUL(_ma, M, A); ELL_2M_TRANSPOSE(_mt, M); ELL_2M_MUL(res, _ma, _mt); } void _tijk_2o2d_unsym_trans_f (float *res, const float *A, const float *M) { float _ma[4], _mt[4]; ELL_2M_MUL(_ma, M, A); ELL_2M_TRANSPOSE(_mt, M); ELL_2M_MUL(res, _ma, _mt); } /* macro-based pseudo-template for type-generic code */ #define _TIJK_2O2D_UNSYM_CONVERT(TYPE, SUF) \ int \ _tijk_2o2d_unsym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_2o2d_unsym) { /* copy over */ \ ELL_4V_COPY(res, A); \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_2o2d_unsym); \ else \ return 1; \ } _TIJK_2O2D_UNSYM_CONVERT(double, d) _TIJK_2O2D_UNSYM_CONVERT(float, f) #define _TIJK_2O2D_UNSYM_APPROX(TYPE, SUF) \ int \ _tijk_2o2d_unsym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_2o2d_sym) { \ res[0]=A[0]; res[1]=0.5*(A[1]+A[2]); res[2]=A[3]; \ return 0; \ } else if (res_type==tijk_2o2d_asym) { \ res[0]=0.5*(A[1]-A[2]); \ return 0; \ } else if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_2o2d_unsym); \ else \ return 1; \ } _TIJK_2O2D_UNSYM_APPROX(double, d) _TIJK_2O2D_UNSYM_APPROX(float, f) TIJK_TYPE_UNSYM(2o2d_unsym, 2, 2, 4) /* 2nd order 2D symmetric */ unsigned int _tijk_2o2d_sym_mult[3] = {1, 2, 1}; int _tijk_2o2d_sym_unsym2uniq[4] = {1, 2, 2, 3}; int _tijk_2o2d_sym_uniq2unsym[4] = {1, 2, 3, 4}; unsigned int _tijk_2o2d_sym_uniq_idx[3] = {0, 1, 3}; #define _TIJK_2O2D_SYM_TSP(A, B) \ ((A)[0]*(B)[0]+2*(A)[1]*(B)[1]+(A)[2]*(B)[2]) double _tijk_2o2d_sym_tsp_d (const double *A, const double *B) { return _TIJK_2O2D_SYM_TSP(A,B); } float _tijk_2o2d_sym_tsp_f (const float *A, const float *B) { return _TIJK_2O2D_SYM_TSP(A,B); } double _tijk_2o2d_sym_norm_d (const double *A) { return sqrt(_TIJK_2O2D_SYM_TSP(A,A)); } float _tijk_2o2d_sym_norm_f (const float *A) { return sqrt(_TIJK_2O2D_SYM_TSP(A,A)); } void _tijk_2o2d_sym_trans_d (double *res, const double *A, const double *M) { /* sym(M*unsym(A)*M^T) written out: */ res[0]=M[0]*M[0]*A[0]+2*M[0]*M[1]*A[1]+M[1]*M[1]*A[2]; res[1]=M[0]*M[2]*A[0]+(M[0]*M[3]+M[1]*M[2])*A[1]+M[1]*M[3]*A[2]; res[2]=M[2]*M[2]*A[0]+2*M[2]*M[3]*A[1]+M[3]*M[3]*A[2]; } void _tijk_2o2d_sym_trans_f (float *res, const float *A, const float *M) { /* sym(M*unsym(A)*M^T) written out: */ res[0]=M[0]*M[0]*A[0]+2*M[0]*M[1]*A[1]+M[1]*M[1]*A[2]; res[1]=M[0]*M[2]*A[0]+(M[0]*M[3]+M[1]*M[2])*A[1]+M[1]*M[3]*A[2]; res[2]=M[2]*M[2]*A[0]+2*M[2]*M[3]*A[1]+M[3]*M[3]*A[2]; } #define _TIJK_2O2D_SYM_CONVERT(TYPE, SUF) \ int \ _tijk_2o2d_sym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_2o2d_sym) { /* copy over */ \ ELL_3V_COPY(res, A); \ return 0; \ } else if (res_type==tijk_2o2d_unsym) { \ res[0]=A[0]; res[1]=res[2]=A[1]; res[3]=A[2]; \ return 0; \ } else if (res_type==tijk_4o2d_sym) { \ res[0]=A[0]; res[1]=res[3]=0.5*A[1]; \ res[2]=(A[0]+A[2])/6.0; res[4]=A[2]; \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_2o2d_sym); \ else \ return 1; \ } _TIJK_2O2D_SYM_CONVERT(double, d) _TIJK_2O2D_SYM_CONVERT(float, f) #define _TIJK_2O2D_SYM_APPROX(TYPE, SUF) \ int \ _tijk_2o2d_sym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_2o2d_sym); \ else \ return 1; \ } _TIJK_2O2D_SYM_APPROX(double, d) _TIJK_2O2D_SYM_APPROX(float, f) double _tijk_2o2d_sym_s_form_d (const double *A, const double *v) { return A[0]*v[0]*v[0]+2*A[1]*v[0]*v[1]+A[2]*v[1]*v[1]; } float _tijk_2o2d_sym_s_form_f (const float *A, const float *v) { return A[0]*v[0]*v[0]+2*A[1]*v[0]*v[1]+A[2]*v[1]*v[1]; } double _tijk_2o2d_sym_mean_d (const double *A) { return 0.5*(A[0]+A[2]); } float _tijk_2o2d_sym_mean_f (const float *A) { return 0.5*(A[0]+A[2]); } double _tijk_2o2d_sym_var_d (const double *A) { return 0.125*(A[0]*A[0]+A[2]*A[2])-0.25*A[0]*A[2]+0.5*A[1]*A[1]; } float _tijk_2o2d_sym_var_f (const float *A) { return 0.125*(A[0]*A[0]+A[2]*A[2])-0.25*A[0]*A[2]+0.5*A[1]*A[1]; } void _tijk_2o2d_sym_v_form_d (double *res, const double *A, const double *v) { res[0]=A[0]*v[0]+A[1]*v[1]; res[1]=A[1]*v[0]+A[2]*v[1]; } void _tijk_2o2d_sym_v_form_f (float *res, const float *A, const float *v) { res[0]=A[0]*v[0]+A[1]*v[1]; res[1]=A[1]*v[0]+A[2]*v[1]; } void _tijk_2o2d_sym_m_form_d (double *res, const double *A, const double *v) { (void) v; /* v is only used in higher-order cases */ res[0]=A[0]; res[1]=A[1]; res[2]=A[2]; } void _tijk_2o2d_sym_m_form_f (float *res, const float *A, const float *v) { (void) v; /* v is only used in higher-order cases */ res[0]=A[0]; res[1]=A[1]; res[2]=A[2]; } void _tijk_2o2d_sym_make_rank1_d (double *res, const double s, const double *v) { res[0]=s*v[0]*v[0]; res[1]=s*v[0]*v[1]; res[2]=s*v[1]*v[1]; } void _tijk_2o2d_sym_make_rank1_f (float *res, const float s, const float *v) { res[0]=s*v[0]*v[0]; res[1]=s*v[0]*v[1]; res[2]=s*v[1]*v[1]; } void _tijk_2o2d_sym_make_iso_d (double *res, const double s) { res[0]=s; res[1]=0; res[2]=s; } void _tijk_2o2d_sym_make_iso_f (float *res, const float s) { res[0]=s; res[1]=0; res[2]=s; } void _tijk_2o2d_sym_grad_d (double *res, const double *A, const double *v) { double proj, projv[2]; res[0]=2*(A[0]*v[0]+A[1]*v[1]); res[1]=2*(A[1]*v[0]+A[2]*v[1]); proj=ELL_2V_DOT(res,v); ELL_2V_SCALE(projv,-proj,v); ELL_2V_INCR(res,projv); } void _tijk_2o2d_sym_grad_f (float *res, const float *A, const float *v) { float proj, projv[2]; res[0]=2*(A[0]*v[0]+A[1]*v[1]); res[1]=2*(A[1]*v[0]+A[2]*v[1]); proj=ELL_2V_DOT(res,v); ELL_2V_SCALE(projv,-proj,v); ELL_2V_INCR(res,projv); } void _tijk_2o2d_sym_hess_d (double *res, const double *A, const double *v) { double tang[2], s; ELL_2V_SET(tang,v[1],-v[0]); s=2*_tijk_2o2d_sym_s_form_d(A,tang)-2*_tijk_2o2d_sym_s_form_d(A, v); _tijk_2o2d_sym_make_rank1_d(res, s, tang); } void _tijk_2o2d_sym_hess_f (float *res, const float *A, const float *v) { float tang[2], s; ELL_2V_SET(tang,v[1],-v[0]); s=2*_tijk_2o2d_sym_s_form_f(A,tang)-2*_tijk_2o2d_sym_s_form_f(A, v); _tijk_2o2d_sym_make_rank1_f(res, s, tang); } TIJK_TYPE_SYM(2o2d_sym, 2, 2, 3) /* 2nd order 2D antisymmetric */ unsigned int _tijk_2o2d_asym_mult[1] = {2}; int _tijk_2o2d_asym_unsym2uniq[4] = {0, 1, -1, 0}; int _tijk_2o2d_asym_uniq2unsym[2] = {2, -3}; unsigned int _tijk_2o2d_asym_uniq_idx[1] = {0}; double _tijk_2o2d_asym_tsp_d (const double *A, const double *B) { return 2*A[0]*B[0]; } float _tijk_2o2d_asym_tsp_f (const float *A, const float *B) { return 2*A[0]*B[0]; } double _tijk_2o2d_asym_norm_d (const double *A) { return sqrt(2*A[0]*A[0]); } float _tijk_2o2d_asym_norm_f (const float *A) { return sqrt(2*A[0]*A[0]); } void _tijk_2o2d_asym_trans_d (double *res, const double *A, const double *M) { /* if M is a rotation, this amounts to the identity */ res[0]=A[0]*(M[0]*M[3]-M[1]*M[2]); } void _tijk_2o2d_asym_trans_f (float *res, const float *A, const float *M) { res[0]=A[0]*(M[0]*M[3]-M[1]*M[2]); } #define _TIJK_2O2D_ASYM_CONVERT(TYPE, SUF) \ int \ _tijk_2o2d_asym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_2o2d_asym) { /* copy over */ \ res[0]=A[0]; \ return 0; \ } else if (res_type==tijk_2o2d_unsym) { \ res[0]=0; res[1]=A[0]; res[2]=-A[0]; res[3]=0; \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_2o2d_asym); \ else \ return 1; \ } _TIJK_2O2D_ASYM_CONVERT(double, d) _TIJK_2O2D_ASYM_CONVERT(float, f) #define _TIJK_2O2D_ASYM_APPROX(TYPE, SUF) \ int \ _tijk_2o2d_asym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_2o2d_asym); \ else \ return 1; \ } _TIJK_2O2D_ASYM_APPROX(double, d) _TIJK_2O2D_ASYM_APPROX(float, f) TIJK_TYPE(2o2d_asym, 2, 2, 1) /* 3rd order 2D symmetric */ /* unsymmetric counterpart currently not implemented */ unsigned int _tijk_3o2d_sym_mult[4] = {1, 3, 3, 1}; #define _tijk_3o2d_sym_unsym2uniq NULL #define _tijk_3o2d_sym_uniq2unsym NULL #define _tijk_3o2d_sym_uniq_idx NULL #define _TIJK_3O2D_SYM_TSP(A, B) \ ((A)[0]*(B)[0]+3*(A)[1]*(B)[1]+3*(A)[2]*(B)[2]+(A)[3]*(B)[3]) double _tijk_3o2d_sym_tsp_d (const double *A, const double *B) { return _TIJK_3O2D_SYM_TSP(A,B); } float _tijk_3o2d_sym_tsp_f (const float *A, const float *B) { return _TIJK_3O2D_SYM_TSP(A,B); } double _tijk_3o2d_sym_norm_d (const double *A) { return sqrt(_TIJK_3O2D_SYM_TSP(A,A)); } float _tijk_3o2d_sym_norm_f (const float *A) { return sqrt(_TIJK_3O2D_SYM_TSP(A,A)); } #define _TIJK_3O2D_SYM_CONVERT(TYPE, SUF) \ int \ _tijk_3o2d_sym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_3o2d_sym) { /* copy over */ \ ELL_4V_COPY(res, A); \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_3o2d_sym); \ else \ return 1; \ } _TIJK_3O2D_SYM_CONVERT(double, d) _TIJK_3O2D_SYM_CONVERT(float, f) #define _TIJK_3O2D_SYM_APPROX(TYPE, SUF) \ int \ _tijk_3o2d_sym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_3o2d_sym); \ else \ return 1; \ } _TIJK_3O2D_SYM_APPROX(double, d) _TIJK_3O2D_SYM_APPROX(float, f) void _tijk_3o2d_sym_trans_d (double *res, const double *A, const double *M) { res[0]=M[0]*M[0]*M[0]*A[0]+3*M[0]*M[0]*M[1]*A[1]+ 3*M[0]*M[1]*M[1]*A[2]+M[1]*M[1]*M[1]*A[3]; res[1]=M[0]*M[0]*M[2]*A[0]+(M[0]*M[0]*M[3]+2*M[0]*M[1]*M[2])*A[1]+ (2*M[0]*M[1]*M[3]+M[1]*M[1]*M[2])*A[2]+M[1]*M[1]*M[3]*A[3]; res[2]=M[0]*M[2]*M[2]*A[0]+(M[1]*M[2]*M[2]+2*M[0]*M[2]*M[3])*A[1]+ (2*M[1]*M[2]*M[3]+M[0]*M[3]*M[3])*A[2]+M[1]*M[3]*M[3]*A[3]; res[3]=M[2]*M[2]*M[2]*A[0]+3*M[2]*M[2]*M[3]*A[1]+ 3*M[2]*M[3]*M[3]*A[2]+M[3]*M[3]*M[3]*A[3]; } void _tijk_3o2d_sym_trans_f (float *res, const float *A, const float *M) { res[0]=M[0]*M[0]*M[0]*A[0]+3*M[0]*M[0]*M[1]*A[1]+ 3*M[0]*M[1]*M[1]*A[2]+M[1]*M[1]*M[1]*A[3]; res[1]=M[0]*M[0]*M[2]*A[0]+(M[0]*M[0]*M[3]+2*M[0]*M[1]*M[2])*A[1]+ (2*M[0]*M[1]*M[3]+M[1]*M[1]*M[2])*A[2]+M[1]*M[1]*M[3]*A[3]; res[2]=M[0]*M[2]*M[2]*A[0]+(M[1]*M[2]*M[2]+2*M[0]*M[2]*M[3])*A[1]+ (2*M[1]*M[2]*M[3]+M[0]*M[3]*M[3])*A[2]+M[1]*M[3]*M[3]*A[3]; res[3]=M[2]*M[2]*M[2]*A[0]+3*M[2]*M[2]*M[3]*A[1]+ 3*M[2]*M[3]*M[3]*A[2]+M[3]*M[3]*M[3]*A[3]; } double _tijk_3o2d_sym_s_form_d (const double *A, const double *v) { return A[0]*v[0]*v[0]*v[0]+3*A[1]*v[0]*v[0]*v[1]+ 3*A[2]*v[0]*v[1]*v[1]+A[3]*v[1]*v[1]*v[1]; } float _tijk_3o2d_sym_s_form_f (const float *A, const float *v) { return A[0]*v[0]*v[0]*v[0]+3*A[1]*v[0]*v[0]*v[1]+ 3*A[2]*v[0]*v[1]*v[1]+A[3]*v[1]*v[1]*v[1]; } double _tijk_3o2d_sym_mean_d (const double *A) { (void) A; /* odd order; mean is zero irrespective of coefficients */ return 0; } float _tijk_3o2d_sym_mean_f (const float *A) { (void) A; /* odd order; mean is zero irrespective of coefficients */ return 0; } double _tijk_3o2d_sym_var_d (const double *A) { return (5*(A[0]*A[0]+A[3]*A[3])+9*(A[1]*A[1]+A[2]*A[2])+ 6*(A[0]*A[2]+A[1]*A[3]))/16.0; } float _tijk_3o2d_sym_var_f (const float *A) { return (5*(A[0]*A[0]+A[3]*A[3])+9*(A[1]*A[1]+A[2]*A[2])+ 6*(A[0]*A[2]+A[1]*A[3]))/16.0; } void _tijk_3o2d_sym_v_form_d (double *res, const double *A, const double *v) { double v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1]; res[0]=A[0]*v00+2*A[1]*v01+A[2]*v11; res[1]=A[1]*v00+2*A[2]*v01+A[3]*v11; } void _tijk_3o2d_sym_v_form_f (float *res, const float *A, const float *v) { float v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1]; res[0]=A[0]*v00+2*A[1]*v01+A[2]*v11; res[1]=A[1]*v00+2*A[2]*v01+A[3]*v11; } void _tijk_3o2d_sym_m_form_d (double *res, const double *A, const double *v) { res[0]=A[0]*v[0]+A[1]*v[1]; res[1]=A[1]*v[0]+A[2]*v[1]; res[2]=A[2]*v[0]+A[3]*v[1]; } void _tijk_3o2d_sym_m_form_f (float *res, const float *A, const float *v) { res[0]=A[0]*v[0]+A[1]*v[1]; res[1]=A[1]*v[0]+A[2]*v[1]; res[2]=A[2]*v[0]+A[3]*v[1]; } void _tijk_3o2d_sym_make_rank1_d (double *res, const double s, const double *v) { res[0]=s*v[0]*v[0]*v[0]; res[1]=s*v[0]*v[0]*v[1]; res[2]=s*v[0]*v[1]*v[1]; res[3]=s*v[1]*v[1]*v[1]; } void _tijk_3o2d_sym_make_rank1_f (float *res, const float s, const float *v) { res[0]=s*v[0]*v[0]*v[0]; res[1]=s*v[0]*v[0]*v[1]; res[2]=s*v[0]*v[1]*v[1]; res[3]=s*v[1]*v[1]*v[1]; } #define _tijk_3o2d_sym_make_iso_d NULL #define _tijk_3o2d_sym_make_iso_f NULL void _tijk_3o2d_sym_grad_d (double *res, const double *A, const double *v) { double proj, projv[2]; _tijk_3o2d_sym_v_form_d (res, A, v); ELL_2V_SCALE(res,3.0,res); proj=ELL_2V_DOT(res,v); ELL_2V_SCALE(projv,-proj,v); ELL_2V_INCR(res,projv); } void _tijk_3o2d_sym_grad_f (float *res, const float *A, const float *v) { float proj, projv[2]; _tijk_3o2d_sym_v_form_f (res, A, v); ELL_2V_SCALE(res,3.0,res); proj=ELL_2V_DOT(res,v); ELL_2V_SCALE(projv,-proj,v); ELL_2V_INCR(res,projv); } void _tijk_3o2d_sym_hess_d (double *res, const double *A, const double *v) { double tang[2]; double hess[3], s; ELL_2V_SET(tang,v[1],-v[0]); _tijk_3o2d_sym_m_form_d(hess,A,v); s=6*_tijk_2o2d_sym_s_form_d(hess,tang)-3*_tijk_2o2d_sym_s_form_d(hess,v); _tijk_2o2d_sym_make_rank1_d(res, s, tang); } void _tijk_3o2d_sym_hess_f (float *res, const float *A, const float *v) { float tang[2]; float hess[3], s; ELL_2V_SET(tang,v[1],-v[0]); _tijk_3o2d_sym_m_form_f(hess,A,v); s=6*_tijk_2o2d_sym_s_form_f(hess,tang)-3*_tijk_2o2d_sym_s_form_f(hess,v); _tijk_2o2d_sym_make_rank1_f(res, s, tang); } TIJK_TYPE_SYM(3o2d_sym, 3, 2, 4) /* 4th order 2D unsymmetric */ double _tijk_4o2d_unsym_tsp_d (const double *A, const double *B) { return ELL_4V_DOT(A,B)+ELL_4V_DOT(A+4,B+4)+ ELL_4V_DOT(A+8,B+8)+ELL_4V_DOT(A+12,B+12); } float _tijk_4o2d_unsym_tsp_f (const float *A, const float *B) { return ELL_4V_DOT(A,B)+ELL_4V_DOT(A+4,B+4)+ ELL_4V_DOT(A+8,B+8)+ELL_4V_DOT(A+12,B+12); } double _tijk_4o2d_unsym_norm_d (const double *A) { return sqrt(ELL_4V_DOT(A,A)+ELL_4V_DOT(A+4,A+4)+ ELL_4V_DOT(A+8,A+8)+ELL_4V_DOT(A+12,A+12)); } float _tijk_4o2d_unsym_norm_f (const float *A) { return sqrt(ELL_4V_DOT(A,A)+ELL_4V_DOT(A+4,A+4)+ ELL_4V_DOT(A+8,A+8)+ELL_4V_DOT(A+12,A+12)); } #define _TIJK_4O2D_UNSYM_TRANS(TYPE, SUF) \ void \ _tijk_4o2d_unsym_trans_##SUF (TYPE *res, const TYPE *A, const TYPE *M) \ { /* Tijkl = Mim Mjn Mko Mlp Tmnop \ * For efficiency, we transform mode by mode, right to left */ \ TYPE tmp[16]={0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; \ int i, init; \ for (i=0; i<16; i+=2) { /* 4th mode */ \ tmp[i] = M[0]*A[i] + M[1]*A[i+1]; \ tmp[i+1]= M[2]*A[i] + M[3]*A[i+1]; \ } \ /* using res as additional tmp space */ \ for (init=0; init<2; init++) { /* 3rd mode */ \ for (i=init; i<16; i+=4) { \ res[i] = M[0]*tmp[i] + M[1]*tmp[i+2]; \ res[i+2]= M[2]*tmp[i] + M[3]*tmp[i+2]; \ } \ } \ for (init=0; init<4; init++) { /* 2nd mode */ \ for (i=init; i<16; i+=8) { \ tmp[i] = M[0]*res[i] + M[1]*res[i+4]; \ tmp[i+4]= M[2]*res[i] + M[3]*res[i+4]; \ } \ } \ for (i=0; i<8; i++) { /* 1st mode */ \ res[i] = M[0]*tmp[i] + M[1]*tmp[i+8]; \ res[i+8]= M[2]*tmp[i] + M[3]*tmp[i+8]; \ } \ } _TIJK_4O2D_UNSYM_TRANS(double, d) _TIJK_4O2D_UNSYM_TRANS(float, f) #define _TIJK_4O2D_UNSYM_CONVERT(TYPE, SUF) \ int \ _tijk_4o2d_unsym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_4o2d_unsym) { /* copy over */ \ ELL_4V_COPY(res, A); ELL_4V_COPY(res+4, A+4); \ ELL_4V_COPY(res+8, A+8); ELL_4V_COPY(res+12, A+12); \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_4o2d_unsym); \ else \ return 1; \ } _TIJK_4O2D_UNSYM_CONVERT(double, d) _TIJK_4O2D_UNSYM_CONVERT(float, f) #define _TIJK_4O2D_UNSYM_APPROX(TYPE, SUF) \ int \ _tijk_4o2d_unsym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_4o2d_sym) { \ res[0]=A[0]; res[1]=0.25*(A[1]+A[2]+A[4]+A[8]); \ res[2]=(A[3]+A[5]+A[6]+A[9]+A[10]+A[12])/6.0; \ res[3]=0.25*(A[7]+A[11]+A[13]+A[14]); res[4]=A[15]; \ return 0; \ } else if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_4o2d_unsym); \ else \ return 1; \ } _TIJK_4O2D_UNSYM_APPROX(double, d) _TIJK_4O2D_UNSYM_APPROX(float, f) TIJK_TYPE_UNSYM(4o2d_unsym, 4, 2, 16) /* 4th order 2D symmetric */ unsigned int _tijk_4o2d_sym_mult[5] = {1, 4, 6, 4, 1}; int _tijk_4o2d_sym_unsym2uniq[16] = {1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5}; int _tijk_4o2d_sym_uniq2unsym[16] = {1, 2, 3, 5, 9, 4, 6, 7, 10, 11, 13, 8, 12, 14, 15, 16}; unsigned int _tijk_4o2d_sym_uniq_idx[5] = {0, 1, 5, 11, 15}; #define _TIJK_4O2D_SYM_TSP(A, B) \ ((A)[0]*(B)[0]+4*(A)[1]*(B)[1]+6*(A)[2]*(B)[2]+4*(A)[3]*(B)[3]+(A)[4]*(B)[4]) double _tijk_4o2d_sym_tsp_d (const double *A, const double *B) { return _TIJK_4O2D_SYM_TSP(A,B); } float _tijk_4o2d_sym_tsp_f (const float *A, const float *B) { return _TIJK_4O2D_SYM_TSP(A,B); } double _tijk_4o2d_sym_norm_d (const double *A) { return sqrt(_TIJK_4O2D_SYM_TSP(A,A)); } float _tijk_4o2d_sym_norm_f (const float *A) { return sqrt(_TIJK_4O2D_SYM_TSP(A,A)); } #define _TIJK_4O2D_SYM_CONVERT(TYPE, SUF) \ int \ _tijk_4o2d_sym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_4o2d_sym) { /* copy over */ \ ELL_4V_COPY(res, A); res[4]=A[4]; \ return 0; \ } else if (res_type==tijk_4o2d_unsym) { \ res[0]=A[0]; res[1]=res[2]=res[4]=res[8]=A[1]; \ res[3]=res[5]=res[6]=res[9]=res[10]=res[12]=A[2]; \ res[7]=res[11]=res[13]=res[14]=A[3]; res[15]=A[4]; \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_4o2d_sym); \ else \ return 1; \ } _TIJK_4O2D_SYM_CONVERT(double, d) _TIJK_4O2D_SYM_CONVERT(float, f) #define _TIJK_4O2D_SYM_APPROX(TYPE, SUF) \ int \ _tijk_4o2d_sym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_2o2d_sym) { \ res[0]=0.875*A[0]+0.75*A[2]-0.125*A[4]; \ res[1]=A[1]+A[3]; \ res[2]=-0.125*A[0]+0.75*A[2]+0.875*A[4]; \ return 0; \ } else if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_4o2d_sym); \ else \ return 1; \ } _TIJK_4O2D_SYM_APPROX(double, d) _TIJK_4O2D_SYM_APPROX(float, f) void _tijk_4o2d_sym_trans_d (double *res, const double *A, const double *M) { /* this code should be optimized at some point */ double tmp[16], tmpout[16]; _tijk_4o2d_sym_convert_d(tmp, tijk_4o2d_unsym, A); _tijk_4o2d_unsym_trans_d(tmpout, tmp, M); _tijk_4o2d_unsym_approx_d(res, tijk_4o2d_sym, tmpout); } void _tijk_4o2d_sym_trans_f (float *res, const float *A, const float *M) { float tmp[16], tmpout[16]; _tijk_4o2d_sym_convert_f(tmp, tijk_4o2d_unsym, A); _tijk_4o2d_unsym_trans_f(tmpout, tmp, M); _tijk_4o2d_unsym_approx_f(res, tijk_4o2d_sym, tmpout); } double _tijk_4o2d_sym_s_form_d (const double *A, const double *v) { double v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1]; return A[0]*v00*v00+4*A[1]*v00*v01+6*A[2]*v00*v11+ 4*A[3]*v01*v11+A[4]*v11*v11; } float _tijk_4o2d_sym_s_form_f (const float *A, const float *v) { float v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1]; return A[0]*v00*v00+4*A[1]*v00*v01+6*A[2]*v00*v11+ 4*A[3]*v01*v11+A[4]*v11*v11; } double _tijk_4o2d_sym_mean_d (const double *A) { return 0.375*(A[0]+A[4])+0.75*A[2]; } float _tijk_4o2d_sym_mean_f (const float *A) { return 0.375*(A[0]+A[4])+0.75*A[2]; } double _tijk_4o2d_sym_var_d (const double *A) { return A[0]*(0.1328125*A[0]-0.09375*A[2]-0.234375*A[4]) + A[1]*(0.625*A[1]+0.75*A[3]) + 0.28125*A[2]*A[2] + 0.625*A[3]*A[3] + A[4]*(0.1328125*A[4]-0.09375*A[2]); } float _tijk_4o2d_sym_var_f (const float *A) { return A[0]*(0.1328125*A[0]-0.09375*A[2]-0.234375*A[4]) + A[1]*(0.625*A[1]+0.75*A[3]) + 0.28125*A[2]*A[2] + 0.625*A[3]*A[3] + A[4]*(0.1328125*A[4]-0.09375*A[2]); } void _tijk_4o2d_sym_v_form_d (double *res, const double *A, const double *v) { double v000=v[0]*v[0]*v[0], v001=v[0]*v[0]*v[1], v011=v[0]*v[1]*v[1], v111=v[1]*v[1]*v[1]; res[0]=A[0]*v000+3*A[1]*v001+3*A[2]*v011+A[3]*v111; res[1]=A[1]*v000+3*A[2]*v001+3*A[3]*v011+A[4]*v111; } void _tijk_4o2d_sym_v_form_f (float *res, const float *A, const float *v) { float v000=v[0]*v[0]*v[0], v001=v[0]*v[0]*v[1], v011=v[0]*v[1]*v[1], v111=v[1]*v[1]*v[1]; res[0]=A[0]*v000+3*A[1]*v001+3*A[2]*v011+A[3]*v111; res[1]=A[1]*v000+3*A[2]*v001+3*A[3]*v011+A[4]*v111; } void _tijk_4o2d_sym_m_form_d (double *res, const double *A, const double *v) { double v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1]; res[0]=A[0]*v00+2*A[1]*v01+A[2]*v11; res[1]=A[1]*v00+2*A[2]*v01+A[3]*v11; res[2]=A[2]*v00+2*A[3]*v01+A[4]*v11; } void _tijk_4o2d_sym_m_form_f (float *res, const float *A, const float *v) { float v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1]; res[0]=A[0]*v00+2*A[1]*v01+A[2]*v11; res[1]=A[1]*v00+2*A[2]*v01+A[3]*v11; res[2]=A[2]*v00+2*A[3]*v01+A[4]*v11; } void _tijk_4o2d_sym_make_rank1_d (double *res, const double s, const double *v) { double v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1]; res[0]=s*v00*v00; res[1]=s*v00*v01; res[2]=s*v00*v11; res[3]=s*v01*v11; res[4]=s*v11*v11; } void _tijk_4o2d_sym_make_rank1_f (float *res, const float s, const float *v) { float v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1]; res[0]=s*v00*v00; res[1]=s*v00*v01; res[2]=s*v00*v11; res[3]=s*v01*v11; res[4]=s*v11*v11; } void _tijk_4o2d_sym_make_iso_d (double *res, const double s) { res[0]=res[4]=s; res[2]=s/3.0; res[1]=res[3]=0; } void _tijk_4o2d_sym_make_iso_f (float *res, const float s) { res[0]=res[4]=s; res[2]=s/3.0; res[1]=res[3]=0; } void _tijk_4o2d_sym_grad_d (double *res, const double *A, const double *v) { double proj, projv[2]; _tijk_4o2d_sym_v_form_d (res, A, v); ELL_2V_SCALE(res,4.0,res); proj=ELL_2V_DOT(res,v); ELL_2V_SCALE(projv,-proj,v); ELL_2V_INCR(res,projv); } void _tijk_4o2d_sym_grad_f (float *res, const float *A, const float *v) { float proj, projv[2]; _tijk_4o2d_sym_v_form_f (res, A, v); ELL_2V_SCALE(res,4.0,res); proj=ELL_2V_DOT(res,v); ELL_2V_SCALE(projv,-proj,v); ELL_2V_INCR(res,projv); } void _tijk_4o2d_sym_hess_d (double *res, const double *A, const double *v) { double tang[2]; double hess[3], s; ELL_2V_SET(tang,v[1],-v[0]); _tijk_4o2d_sym_m_form_d(hess,A,v); s=12*_tijk_2o2d_sym_s_form_d(hess,tang)-4*_tijk_2o2d_sym_s_form_d(hess,v); _tijk_2o2d_sym_make_rank1_d(res, s, tang); } void _tijk_4o2d_sym_hess_f (float *res, const float *A, const float *v) { float tang[2]; float hess[3], s; ELL_2V_SET(tang,v[1],-v[0]); _tijk_4o2d_sym_m_form_f(hess,A,v); s=12*_tijk_2o2d_sym_s_form_f(hess,tang)-4*_tijk_2o2d_sym_s_form_f(hess,v); _tijk_2o2d_sym_make_rank1_f(res, s, tang); } TIJK_TYPE_SYM(4o2d_sym, 4, 2, 5) #include "convertQuietPop.h" teem-1.11.0~svn6057/src/tijk/convertQuietPop.h0000664000175000017500000000225412165630616020666 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef _MSC_VER # pragma warning(pop) #endif teem-1.11.0~svn6057/src/tijk/convertQuietPush.h0000664000175000017500000000307712165630616021053 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* NOTE: these warning suppressions are hopefully only a short-term fix */ /* when using an MS compiler, suppress all warnings about * truncation (4304) or conversion (4244) from double to float */ #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4305) # pragma warning(disable : 4244) #endif /* "-Wconversion" generates analogous warnings in gcc */ #ifdef __GNUC__ # pragma GCC diagnostic ignored "-Wconversion" #endif teem-1.11.0~svn6057/src/tijk/shTijk.c0000664000175000017500000005240212104250015016726 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009, 2008 Thomas Schultz Copyright (C) 2010, 2009, 2008 Gordon Kindlmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Implementation of spherical harmonics and their relation to tensors */ #include "tijk.h" #include "convertQuietPush.h" #define TIJK_TABLE_TYPE 0 /* create double version */ #include "shtables.h" #undef TIJK_TABLE_TYPE /* explicitly undef to avoid compiler warnings */ #define TIJK_TABLE_TYPE 1 /* create float version */ #include "shtables.h" #undef TIJK_TABLE_TYPE const unsigned int tijk_max_esh_order=8; /* for order 8, the maximum number of coefficients is 45 */ #define _TIJK_MAX_ESH_LEN 45 /* number of coefficients for order i/2 */ const unsigned int tijk_esh_len[5]={1,6,15,28,45}; /* We use the following convention: * theta = polar angle from positive z * phi = azimuth from positive x */ #define TIJK_EVAL_ESH_BASIS(TYPE, SUF) \ unsigned int \ tijk_eval_esh_basis_##SUF(TYPE *res, unsigned int order, \ TYPE theta, TYPE phi) { \ TYPE stheta=sin(theta); \ TYPE ctheta=cos(theta); \ TYPE stheta2=stheta*stheta, stheta4=stheta2*stheta2; \ TYPE ctheta2=ctheta*ctheta, ctheta4=ctheta2*ctheta2; \ res[0]=1.0/sqrt(4.0*AIR_PI); \ if (order<2) \ return 0; \ res[5]=res[1]=sqrt(15.0/(16.0*AIR_PI))*stheta2; \ res[5]*=sin(2*phi); res[1]*=cos(2*phi); \ res[4]=res[2]=-sqrt(15.0/(4.0*AIR_PI))*ctheta*stheta; \ res[4]*=sin(phi); res[2]*=cos(phi); \ res[3]=sqrt(5.0/(16.0*AIR_PI))*(3.0*ctheta2-1.0); \ if (order<4) \ return 2; \ res[14]=res[6]=sqrt(315.0/(256.0*AIR_PI))*stheta4; \ res[14]*=sin(4*phi); res[6]*=cos(4*phi); \ res[13]=res[7]=-sqrt(315.0/(32.0*AIR_PI))*ctheta*stheta*stheta2; \ res[13]*=sin(3*phi); res[7]*=cos(3*phi); \ res[12]=res[8]=sqrt(45.0/(64.0*AIR_PI))*(7*ctheta2-1)*stheta2; \ res[12]*=sin(2*phi); res[8]*=cos(2*phi); \ res[11]=res[9]=-sqrt(45.0/(32.0*AIR_PI))*(7*ctheta2*ctheta-3*ctheta)*stheta; \ res[11]*=sin(phi); res[9]*=cos(phi); \ res[10]=sqrt(9.0/(256.0*AIR_PI))*(35.0*ctheta4-30.0*ctheta2+3.0); \ if (order<6) \ return 4; \ res[27]=res[15]=1.0/64.0*sqrt(6006.0/AIR_PI)*stheta4*stheta2; \ res[27]*=sin(6*phi); res[15]*=cos(6*phi); \ res[26]=res[16]=-3.0/32.0*sqrt(2002.0/AIR_PI)*stheta4*stheta*ctheta; \ res[26]*=sin(5*phi); res[16]*=cos(5*phi); \ res[25]=res[17]=3.0/32.0*sqrt(91.0/AIR_PI)*stheta4*(11*ctheta2-1.0); \ res[25]*=sin(4*phi); res[17]*=cos(4*phi); \ res[24]=res[18]=-1.0/32.0*sqrt(2730.0/AIR_PI)*stheta2*stheta*(11*ctheta2*ctheta-3*ctheta); \ res[24]*=sin(3*phi); res[18]*=cos(3*phi); \ res[23]=res[19]=1.0/64.0*sqrt(2730.0/AIR_PI)*stheta2*(33*ctheta4-18*ctheta2+1.0); \ res[23]*=sin(2*phi); res[19]*=cos(2*phi); \ res[22]=res[20]=-1.0/16.0*sqrt(273.0/AIR_PI)*stheta*(33*ctheta4*ctheta-30.0*ctheta2*ctheta+5*ctheta); \ res[22]*=sin(phi); res[20]*=cos(phi); \ res[21]=1.0/32.0*sqrt(13.0/AIR_PI)*(231*ctheta4*ctheta2-315*ctheta4+105*ctheta2-5.0); \ if (order<8) \ return 6; \ res[44]=res[28]=3.0/256.0*sqrt(12155.0/AIR_PI)*stheta4*stheta4; \ res[44]*=sin(8*phi); res[28]*=cos(8*phi); \ res[43]=res[29]=-3.0/64.0*sqrt(12155.0/AIR_PI)*stheta4*stheta2*stheta*ctheta; \ res[43]*=sin(7*phi); res[29]*=cos(7*phi); \ res[42]=res[30]=1.0/128.0*sqrt(2*7293.0/AIR_PI)*stheta4*stheta2*(15*ctheta2-1); \ res[42]*=sin(6*phi); res[30]*=cos(6*phi); \ res[41]=res[31]=-3.0/64.0*sqrt(17017.0/AIR_PI)*stheta4*stheta*ctheta*(5*ctheta2-1); \ res[41]*=sin(5*phi); res[31]*=cos(5*phi); \ res[40]=res[32]=3.0/128.0*sqrt(1309.0/AIR_PI)*stheta4*(65*ctheta4-26*ctheta2+1); \ res[40]*=sin(4*phi); res[32]*=cos(4*phi); \ res[39]=res[33]=-1.0/64.0*sqrt(19635.0/AIR_PI)*stheta2*stheta*ctheta*(39*ctheta4-26*ctheta2+3); \ res[39]*=sin(3*phi); res[33]*=cos(3*phi); \ res[38]=res[34]=3.0/128.0*sqrt(2*595.0/AIR_PI)*stheta2*(143*ctheta4*ctheta2-143*ctheta4+33*ctheta2-1); \ res[38]*=sin(2*phi); res[34]*=cos(2*phi); \ res[37]=res[35]=-3.0/64.0*sqrt(17.0/AIR_PI)*stheta*ctheta*(715*ctheta4*ctheta2-1001*ctheta4+385*ctheta2-35); \ res[37]*=sin(phi); res[35]*=cos(phi); \ res[36]=1.0/256.0*sqrt(17.0/AIR_PI)*(6435*ctheta4*ctheta4-12012*ctheta2*ctheta4+6930*ctheta4-1260*ctheta2+35); \ return 8; /* higher orders currently not implemented */ \ } TIJK_EVAL_ESH_BASIS(double, d) TIJK_EVAL_ESH_BASIS(float, f) #define TIJK_EVAL_ESH(TYPE, SUF) \ TYPE \ tijk_eval_esh_##SUF(TYPE *coeffs, unsigned int order, \ TYPE theta, TYPE phi) { \ TYPE basis[_TIJK_MAX_ESH_LEN]; \ TYPE res=0.0; \ unsigned int i; \ if (order!=tijk_eval_esh_basis_##SUF(basis, order, theta, phi)) \ return 0; /* there has been an error. */ \ for (i=0; inum; \ if (type==tijk_2o3d_sym) { \ m=_tijk_sym2esh_o2_##SUF; \ } else if (type==tijk_4o3d_sym) { \ m=_tijk_sym2esh_o4_##SUF; \ } else if (type==tijk_6o3d_sym) { \ m=_tijk_sym2esh_o6_##SUF; \ } else if (type==tijk_8o3d_sym) { \ m=_tijk_sym2esh_o8_##SUF; \ } else { \ return -1; /* cannot do conversion */ \ } \ for (i=0; iorder; \ } TIJK_3D_SYM_TO_ESH(double, d) TIJK_3D_SYM_TO_ESH(float, f) /* DOES NOT work in-place (with res==sh) */ #define TIJK_ESH_TO_3D_SYM(TYPE, SUF) \ const tijk_type* \ tijk_esh_to_3d_sym_##SUF(TYPE *res, const TYPE *sh, unsigned int order) { \ const TYPE *m; /* conversion matrix */ \ const tijk_type *type; \ unsigned int i, j, n; \ if (res==sh) return NULL; \ switch (order) { \ case 2: \ m=_tijk_esh2sym_o2_##SUF; \ type=tijk_2o3d_sym; \ break; \ case 4: \ m=_tijk_esh2sym_o4_##SUF; \ type=tijk_4o3d_sym; \ break; \ case 6: \ m=_tijk_esh2sym_o6_##SUF; \ type=tijk_6o3d_sym; \ break; \ case 8: \ m=_tijk_esh2sym_o8_##SUF; \ type=tijk_8o3d_sym; \ break; \ default: \ return NULL; /* cannot do the conversion */ \ } \ n=tijk_esh_len[order/2]; \ for (i=0; isym->make_rank1_##SUF)(rank1, 1.0, v); \ tijk_3d_sym_to_esh_##SUF(rank1sh, rank1, type); \ for (i=0; i<1+order/2; i++) \ if (signl[i]==0) { \ return 2; \ } \ kernel[0]=signl[0]/rank1sh[0]; \ if (order>=2) { \ kernel[1]=signl[1]/rank1sh[3]; \ if (order>=4) { \ kernel[2]=signl[2]/rank1sh[10]; \ if (order>=6) { \ kernel[3]=signl[3]/rank1sh[21]; \ if (order>=8) { \ kernel[4]=signl[4]/rank1sh[36]; \ } \ } \ } \ } \ return 0; \ } TIJK_ESH_MAKE_KERNEL_RANK1(double, d) TIJK_ESH_MAKE_KERNEL_RANK1(float, f) /* Make a deconvolution kernel that turns a given signal (rotationally * symmetric in "compressed" form, i.e., one coefficient per SH order) * into a truncated delta peak of given order. * Return 0 upon success * 1 if order is unsupported * 2 if any of the given signal values is zero */ #define TIJK_ESH_MAKE_KERNEL_DELTA(TYPE, SUF) \ int tijk_esh_make_kernel_delta_##SUF(TYPE *kernel, const TYPE *signl, \ unsigned int order) { \ /* we need a truncated delta peak of given order */ \ TYPE deltash[TIJK_TYPE_MAX_NUM]; \ unsigned int i; \ if (order>tijk_max_esh_order || order%2!=0) \ return 1; \ for (i=0; i<1+order/2; i++) \ if (signl[i]==0) { \ return 2; \ } \ tijk_eval_esh_basis_##SUF(deltash, order, 0, 0); \ kernel[0]=signl[0]/deltash[0]; \ if (order>=2) { \ kernel[1]=signl[1]/deltash[3]; \ if (order>=4) { \ kernel[2]=signl[2]/deltash[10]; \ if (order>=6) { \ kernel[3]=signl[3]/deltash[21]; \ if (order>=8) { \ kernel[4]=signl[4]/deltash[36]; \ } \ } \ } \ } \ return 0; \ } TIJK_ESH_MAKE_KERNEL_DELTA(double, d) TIJK_ESH_MAKE_KERNEL_DELTA(float, f) #include "convertQuietPop.h" teem-1.11.0~svn6057/src/tijk/fsTijk.c0000664000175000017500000001530512042325046016735 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2010, 2009, 2008 Thomas Schultz Copyright (C) 2010, 2009, 2008 Gordon Kindlmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Implementation of fourier series and their relation to tensors */ #include "tijk.h" #include "convertQuietPush.h" const unsigned int tijk_max_efs_order=4; /* for order 4, the maximum number of coefficients is 5 */ #define _TIJK_MAX_EFS_LEN 5 #define TIJK_EVAL_EFS_BASIS(TYPE, SUF) \ unsigned int \ tijk_eval_efs_basis_##SUF(TYPE *res, unsigned int order, TYPE phi) { \ res[0]=0.5; \ if (order<2) \ return 0; \ res[1]=cos(phi); \ res[2]=sin(phi); \ if (order<4) \ return 2; \ res[3]=cos(2*phi); \ res[4]=sin(2*phi); \ return 4; /* higher orders not implemented. */ \ } TIJK_EVAL_EFS_BASIS(double, d) TIJK_EVAL_EFS_BASIS(float, f) #define TIJK_EVAL_EFS(TYPE, SUF) \ TYPE \ tijk_eval_efs_##SUF(TYPE *coeffs, unsigned int order, TYPE phi) { \ TYPE basis[_TIJK_MAX_EFS_LEN]; \ TYPE res=0.0; \ unsigned int i; \ if (order!=tijk_eval_efs_basis_##SUF(basis, order, phi)) \ return 0; /* there has been an error. */ \ for (i=0; inum; \ if (type==tijk_2o2d_sym) { \ m=_tijk_sym2efs_o2; \ } else if (type==tijk_4o2d_sym) { \ m=_tijk_sym2efs_o4; \ } else { \ return -1; /* cannot do conversion */ \ } \ for (i=0; iorder; \ } TIJK_2D_SYM_TO_EFS(double, d) TIJK_2D_SYM_TO_EFS(float, f) #define TIJK_EFS_TO_2D_SYM(TYPE, SUF) \ const tijk_type* \ tijk_efs_to_2d_sym_##SUF(TYPE *res, const TYPE *fs, unsigned int order) { \ const double *m; /* conversion matrix */ \ const tijk_type *type; \ unsigned int i, j, n; \ switch (order) { \ case 2: \ m=_tijk_efs2sym_o2; \ type=tijk_2o2d_sym; \ break; \ case 4: \ m=_tijk_efs2sym_o4; \ type=tijk_4o2d_sym; \ break; \ default: \ return NULL; /* cannot do the conversion */ \ } \ n=order+1; \ for (i=0; inum; i++) *(res++)=*(A++)+*(B++); } void tijk_add_f (float *res, const float *A, const float *B, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)=*(A++)+*(B++); } void tijk_sub_d (double *res, const double *A, const double *B, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)=*(A++)-*(B++); } void tijk_sub_f (float *res, const float *A, const float *B, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)=*(A++)-*(B++); } void tijk_incr_d (double *res, const double *A, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)+=*(A++); } void tijk_incr_f (float *res, const float *A, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)+=*(A++); } void tijk_negate_d (double *res, const double *A, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)=-*(A++); } void tijk_negate_f (float *res, const float *A, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)=-*(A++); } void tijk_scale_d (double *res, const double s, const double *A, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)=s*(*A++); } void tijk_scale_f (float *res, const float s, const float *A, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)=s*(*A++); } void tijk_zero_d (double *res, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)=0.0; } void tijk_zero_f (float *res, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)=0.0; } void tijk_copy_d (double *res, const double *A, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)=*(A++); } void tijk_copy_f (float *res, const float *A, const tijk_type *type) { unsigned int i; for (i=0; inum; i++) *(res++)=*(A++); } teem-1.11.0~svn6057/src/tijk/3dTijk.c0000664000175000017500000042506212042325046016640 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2011, 2010, 2009, 2008 Thomas Schultz Copyright (C) 2010, 2009, 2008 Gordon Kindlmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Implementation of three-dimensional tensors */ #include "tijk.h" #include "privateTijk.h" #include "convertQuietPush.h" /* 1st order 3D - a simple vector */ /* (un)symmetric doesn't really mean anything in this case */ unsigned int _tijk_1o3d_mult[3]={1,1,1}; int _tijk_1o3d_unsym2uniq[3] = {1,2,3}; int _tijk_1o3d_uniq2unsym[3] = {1,2,3}; unsigned int _tijk_1o3d_uniq_idx[3] = {0,1,2}; double _tijk_1o3d_tsp_d (const double *A, const double *B) { return ELL_3V_DOT(A,B); } float _tijk_1o3d_tsp_f (const float *A, const float *B) { return ELL_3V_DOT(A,B); } double _tijk_1o3d_norm_d (const double *A) { return sqrt(ELL_3V_DOT(A,A)); } float _tijk_1o3d_norm_f (const float *A) { return sqrt(ELL_3V_DOT(A,A)); } void _tijk_1o3d_trans_d (double *res, const double *A, const double *M) { ell_3mv_mul_d(res,M,A); } void _tijk_1o3d_trans_f (float *res, const float *A, const float *M) { ell_3mv_mul_f(res,M,A); } /* macro-based pseudo-template for type-generic code */ #define _TIJK_1O3D_CONVERT(TYPE, SUF) \ int \ _tijk_1o3d_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_1o3d) { /* copy over */ \ ELL_3V_COPY(res, A); \ return 0; \ } else if (res_type==tijk_3o3d_sym) { \ res[0]=A[0]; res[1]=A[1]/3.0; res[2]=A[2]/3.0; \ res[3]=A[0]/3.0; res[4]=0; res[5]=A[0]/3.0; res[6]=A[1]; \ res[7]=A[2]/3.0; res[8]=A[1]/3.0; res[9]=A[2]; \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_1o3d); \ else \ return 1; \ } _TIJK_1O3D_CONVERT(double, d) _TIJK_1O3D_CONVERT(float, f) #define _TIJK_1O3D_APPROX(TYPE, SUF) \ int \ _tijk_1o3d_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_1o3d); \ else \ return 1; \ } _TIJK_1O3D_APPROX(double, d) _TIJK_1O3D_APPROX(float, f) double _tijk_1o3d_s_form_d (const double *A, const double *v) { return ELL_3V_DOT(A,v); } float _tijk_1o3d_s_form_f (const float *A, const float *v) { return ELL_3V_DOT(A,v); } double _tijk_1o3d_mean_d (const double *A) { (void) A; /* odd order; mean does not depend on coeffs */ return 0.0; } float _tijk_1o3d_mean_f (const float *A) { (void) A; /* odd order; mean does not depend on coeffs */ return 0.0f; } double _tijk_1o3d_var_d (const double *A) { /* result from MATHEMATICA */ return ELL_3V_DOT(A,A)/3.0; } float _tijk_1o3d_var_f (const float *A) { /* result from MATHEMATICA */ return ELL_3V_DOT(A,A)/3.0; } void _tijk_1o3d_v_form_d (double *res, const double *A, const double *v) { (void) v; /* not used in this case */ ELL_3V_COPY(res,A); } void _tijk_1o3d_v_form_f (float *res, const float *A, const float *v) { (void) v; /* not used in this case */ ELL_3V_COPY(res,A); } void _tijk_1o3d_m_form_d (double *res, const double *A, const double *v) { (void) A; (void) v; /* not used in this case */ ELL_3V_SET(res,0,0,0); ELL_3V_SET(res+3,0,0,0); } void _tijk_1o3d_m_form_f (float *res, const float *A, const float *v) { (void) A; (void) v; /* not used in this case */ ELL_3V_SET(res,0,0,0); ELL_3V_SET(res+3,0,0,0); } void _tijk_1o3d_make_rank1_d (double *res, const double s, const double *v) { ELL_3V_SCALE(res,s,v); } void _tijk_1o3d_make_rank1_f (float *res, const float s, const float *v) { ELL_3V_SCALE(res,s,v); } #define _tijk_1o3d_make_iso_d NULL #define _tijk_1o3d_make_iso_f NULL void _tijk_1o3d_grad_d (double *res, const double *A, const double *v) { (void) v; /* not used in this case */ ELL_3V_COPY(res,A); } void _tijk_1o3d_grad_f (float *res, const float *A, const float *v) { (void) v; /* not used in this case */ ELL_3V_COPY(res,A); } void _tijk_1o3d_hess_d (double *res, const double *A, const double *v) { (void) A; (void) v; /* not used in this case */ ELL_3V_SET(res,0,0,0); ELL_3V_SET(res+3,0,0,0); } void _tijk_1o3d_hess_f (float *res, const float *A, const float *v) { (void) A; (void) v; /* not used in this case */ ELL_3V_SET(res,0,0,0); ELL_3V_SET(res+3,0,0,0); } TIJK_TYPE_SYM(1o3d, 1, 3, 3) /* 2nd order 3D unsymmetric */ double _tijk_2o3d_unsym_tsp_d (const double *A, const double *B) { return ELL_3V_DOT(A,B)+ELL_3V_DOT(A+3,B+3)+ELL_3V_DOT(A+6,B+6); } float _tijk_2o3d_unsym_tsp_f (const float *A, const float *B) { return ELL_3V_DOT(A,B)+ELL_3V_DOT(A+3,B+3)+ELL_3V_DOT(A+6,B+6); } double _tijk_2o3d_unsym_norm_d (const double *A) { return sqrt(ELL_3V_DOT(A,A)+ELL_3V_DOT(A+3,A+3)+ELL_3V_DOT(A+6,A+6)); } float _tijk_2o3d_unsym_norm_f (const float *A) { return sqrt(ELL_3V_DOT(A,A)+ELL_3V_DOT(A+3,A+3)+ELL_3V_DOT(A+6,A+6)); } void _tijk_2o3d_unsym_trans_d (double *res, const double *A, const double *M) { double _ma[9], _mt[9]; ELL_3M_MUL(_ma, M, A); ELL_3M_TRANSPOSE(_mt, M); ELL_3M_MUL(res, _ma, _mt); } void _tijk_2o3d_unsym_trans_f (float *res, const float *A, const float *M) { float _ma[9], _mt[9]; ELL_3M_MUL(_ma, M, A); ELL_3M_TRANSPOSE(_mt, M); ELL_3M_MUL(res, _ma, _mt); } /* macro-based pseudo-template for type-generic code */ #define _TIJK_2O3D_UNSYM_CONVERT(TYPE, SUF) \ int \ _tijk_2o3d_unsym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_2o3d_unsym) { /* copy over */ \ ELL_3M_COPY(res, A); \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_2o3d_unsym); \ else \ return 1; \ } _TIJK_2O3D_UNSYM_CONVERT(double, d) _TIJK_2O3D_UNSYM_CONVERT(float, f) #define _TIJK_2O3D_UNSYM_APPROX(TYPE, SUF) \ int \ _tijk_2o3d_unsym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_2o3d_sym) { \ res[0]=A[0]; res[1]=0.5*(A[1]+A[3]); res[2]=0.5*(A[2]+A[6]); \ res[3]=A[4]; res[4]=0.5*(A[5]+A[7]); res[5]=A[8]; \ return 0; \ } else if (res_type==tijk_2o3d_asym) { \ res[0]=0.5*(A[1]-A[3]); res[1]=0.5*(A[2]-A[6]); \ res[2]=0.5*(A[5]-A[7]); \ return 0; \ } else if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_2o3d_unsym); \ else \ return 1; \ } _TIJK_2O3D_UNSYM_APPROX(double, d) _TIJK_2O3D_UNSYM_APPROX(float, f) TIJK_TYPE_UNSYM(2o3d_unsym, 2, 3, 9) /* 2nd order 3D symmetric */ unsigned int _tijk_2o3d_sym_mult[6] = {1, 2, 2, 1, 2, 1}; int _tijk_2o3d_sym_unsym2uniq[9] = {1, 2, 3, 2, 4, 5, 3, 5, 6}; int _tijk_2o3d_sym_uniq2unsym[9] = {1, 2, 4, 3, 7, 5, 6, 7, 9}; unsigned int _tijk_2o3d_sym_uniq_idx[6] = {0, 1, 3, 5, 6, 8}; #define _TIJK_2O3D_SYM_TSP(A, B) \ ((A)[0]*(B)[0]+2*(A)[1]*(B)[1]+2*(A)[2]*(B)[2]+ \ (A)[3]*(B)[3]+2*(A)[4]*(B)[4]+(A)[5]*(B)[5]) double _tijk_2o3d_sym_tsp_d (const double *A, const double *B) { return _TIJK_2O3D_SYM_TSP(A,B); } float _tijk_2o3d_sym_tsp_f (const float *A, const float *B) { return _TIJK_2O3D_SYM_TSP(A,B); } double _tijk_2o3d_sym_norm_d (const double *A) { return sqrt(_TIJK_2O3D_SYM_TSP(A,A)); } float _tijk_2o3d_sym_norm_f (const float *A) { return sqrt(_TIJK_2O3D_SYM_TSP(A,A)); } #define _TIJK_2O3D_SYM_CONVERT(TYPE, SUF) \ int \ _tijk_2o3d_sym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_2o3d_sym) { /* copy over */ \ ELL_3V_COPY(res, A); ELL_3V_COPY(res+3, A+3); \ return 0; \ } else if (res_type==tijk_2o3d_unsym) { \ res[0]=A[0]; res[1]=res[3]=A[1]; res[2]=res[6]=A[2]; \ res[4]=A[3]; res[5]=res[7]=A[4]; res[8]=A[5]; \ return 0; \ } else if (res_type==tijk_4o3d_sym || \ res_type==tijk_6o3d_sym) { \ /* do this by going to SH and zero-padding */ \ TYPE tmp[28]; \ memset(tmp,0,sizeof(tmp)); \ tijk_3d_sym_to_esh_##SUF (tmp, A, tijk_2o3d_sym); \ tijk_esh_to_3d_sym_##SUF (res, tmp, res_type->order); \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_2o3d_sym); \ return 1; \ } _TIJK_2O3D_SYM_CONVERT(double, d) _TIJK_2O3D_SYM_CONVERT(float, f) #define _TIJK_2O3D_SYM_APPROX(TYPE, SUF) \ int \ _tijk_2o3d_sym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_2o3d_sym); \ else \ return 1; \ } _TIJK_2O3D_SYM_APPROX(double, d) _TIJK_2O3D_SYM_APPROX(float, f) void _tijk_2o3d_sym_trans_d (double *res, const double *A, const double *M) { /* this code could be optimized at some point */ double tmp[9], tmpout[9]; _tijk_2o3d_sym_convert_d(tmp, tijk_2o3d_unsym, A); _tijk_2o3d_unsym_trans_d(tmpout, tmp, M); _tijk_2o3d_unsym_approx_d(res, tijk_2o3d_sym, tmpout); } void _tijk_2o3d_sym_trans_f (float *res, const float *A, const float *M) { /* this code could be optimized at some point */ float tmp[9], tmpout[9]; _tijk_2o3d_sym_convert_f(tmp, tijk_2o3d_unsym, A); _tijk_2o3d_unsym_trans_f(tmpout, tmp, M); _tijk_2o3d_unsym_approx_f(res, tijk_2o3d_sym, tmpout); } double _tijk_2o3d_sym_s_form_d (const double *A, const double *v) { return A[0]*v[0]*v[0]+2*A[1]*v[0]*v[1]+2*A[2]*v[0]*v[2]+ A[3]*v[1]*v[1]+2*A[4]*v[1]*v[2]+A[5]*v[2]*v[2]; } float _tijk_2o3d_sym_s_form_f (const float *A, const float *v) { return A[0]*v[0]*v[0]+2*A[1]*v[0]*v[1]+2*A[2]*v[0]*v[2]+ A[3]*v[1]*v[1]+2*A[4]*v[1]*v[2]+A[5]*v[2]*v[2]; } double _tijk_2o3d_sym_mean_d (const double *A) { return (A[0]+A[3]+A[5])/3.0; } float _tijk_2o3d_sym_mean_f (const float *A) { return (A[0]+A[3]+A[5])/3.0f; } double _tijk_2o3d_sym_var_d (const double *A) { return 4.0/45.0*(A[0]*A[0]+A[3]*A[3]+A[5]*A[5]+ 3*(A[1]*A[1]+A[2]*A[2]+A[4]*A[4])- A[3]*A[5]-A[0]*(A[3]+A[5])); } float _tijk_2o3d_sym_var_f (const float *A) { return 4.0f/45.0f*(A[0]*A[0]+A[3]*A[3]+A[5]*A[5]+ 3.0f*(A[1]*A[1]+A[2]*A[2]+A[4]*A[4])- A[3]*A[5]-A[0]*(A[3]+A[5])); } void _tijk_2o3d_sym_v_form_d (double *res, const double *A, const double *v) { res[0]=A[0]*v[0]+A[1]*v[1]+A[2]*v[2]; res[1]=A[1]*v[0]+A[3]*v[1]+A[4]*v[2]; res[2]=A[2]*v[0]+A[4]*v[1]+A[5]*v[2]; } void _tijk_2o3d_sym_v_form_f (float *res, const float *A, const float *v) { res[0]=A[0]*v[0]+A[1]*v[1]+A[2]*v[2]; res[1]=A[1]*v[0]+A[3]*v[1]+A[4]*v[2]; res[2]=A[2]*v[0]+A[4]*v[1]+A[5]*v[2]; } void _tijk_2o3d_sym_m_form_d (double *res, const double *A, const double *v) { (void) v; /* v is only used in higher-order cases */ ELL_3V_COPY(res,A); ELL_3V_COPY(res+3,A+3); } void _tijk_2o3d_sym_m_form_f (float *res, const float *A, const float *v) { (void) v; /* v is only used in higher-order cases */ ELL_3V_COPY(res,A); ELL_3V_COPY(res+3,A+3); } void _tijk_2o3d_sym_make_rank1_d (double *res, const double s, const double *v) { res[0]=s*v[0]*v[0]; res[1]=s*v[0]*v[1]; res[2]=s*v[0]*v[2]; res[3]=s*v[1]*v[1]; res[4]=s*v[1]*v[2]; res[5]=s*v[2]*v[2]; } void _tijk_2o3d_sym_make_rank1_f (float *res, const float s, const float *v) { res[0]=s*v[0]*v[0]; res[1]=s*v[0]*v[1]; res[2]=s*v[0]*v[2]; res[3]=s*v[1]*v[1]; res[4]=s*v[1]*v[2]; res[5]=s*v[2]*v[2]; } void _tijk_2o3d_sym_make_iso_d (double *res, const double s) { res[0]=res[3]=res[5]=s; res[1]=res[2]=res[4]=0; } void _tijk_2o3d_sym_make_iso_f (float *res, const float s) { res[0]=res[3]=res[5]=s; res[1]=res[2]=res[4]=0; } void _tijk_2o3d_sym_grad_d (double *res, const double *A, const double *v) { double proj, projv[3]; res[0]=2*(A[0]*v[0]+A[1]*v[1]+A[2]*v[2]); res[1]=2*(A[1]*v[0]+A[3]*v[1]+A[4]*v[2]); res[2]=2*(A[2]*v[0]+A[4]*v[1]+A[5]*v[2]); proj=ELL_3V_DOT(res,v); ELL_3V_SCALE(projv,-proj,v); ELL_3V_INCR(res,projv); } void _tijk_2o3d_sym_grad_f (float *res, const float *A, const float *v) { float proj, projv[3]; res[0]=2.0f*(A[0]*v[0]+A[1]*v[1]+A[2]*v[2]); res[1]=2.0f*(A[1]*v[0]+A[3]*v[1]+A[4]*v[2]); res[2]=2.0f*(A[2]*v[0]+A[4]*v[1]+A[5]*v[2]); proj=ELL_3V_DOT(res,v); ELL_3V_SCALE(projv,-proj,v); ELL_3V_INCR(res,projv); } #define _TIJK_2O3D_SYM_HESS(TYPE, SUF) \ void \ _tijk_2o3d_sym_hess_##SUF (TYPE *res, const TYPE *A, const TYPE *v) { \ /* get two orthonormal tangents */ \ TYPE t[2][3], h[4], der, norm, tmp[6]; \ int r,c; \ ell_3v_perp_##SUF(t[0], v); \ ELL_3V_NORM(t[0],t[0],norm); \ ELL_3V_CROSS(t[1],v,t[0]); \ ELL_3V_NORM(t[1],t[1],norm); \ /* compute Hessian w.r.t. t1/t2 */ \ der=2*_tijk_2o3d_sym_s_form_##SUF(A, v); /* first der in direction v*/ \ h[0]=2*_tijk_2o3d_sym_s_form_##SUF(A,t[0])-der; \ h[1]=2*(A[0]*t[0][0]*t[1][0]+A[1]*t[0][0]*t[1][1]+A[2]*t[0][0]*t[1][2]+ \ A[1]*t[0][1]*t[1][0]+A[3]*t[0][1]*t[1][1]+A[4]*t[0][1]*t[1][2]+ \ A[2]*t[0][2]*t[1][0]+A[4]*t[0][2]*t[1][1]+A[5]*t[0][2]*t[1][2]); \ h[2]=h[1]; \ h[3]=2*_tijk_2o3d_sym_s_form_##SUF(A,t[1])-der; \ /* now turn this into a symmetric order-2 rank-2 3D tensor */ \ for (r=0; r<2; r++) { \ for (c=0; c<3; c++) { \ tmp[3*r+c]=h[2*r]*t[0][c]+h[2*r+1]*t[1][c]; \ } \ } \ res[0]=t[0][0]*tmp[0]+t[1][0]*tmp[3]; \ res[1]=t[0][0]*tmp[1]+t[1][0]*tmp[4]; \ res[2]=t[0][0]*tmp[2]+t[1][0]*tmp[5]; \ res[3]=t[0][1]*tmp[1]+t[1][1]*tmp[4]; \ res[4]=t[0][1]*tmp[2]+t[1][1]*tmp[5]; \ res[5]=t[0][2]*tmp[2]+t[1][2]*tmp[5]; \ } _TIJK_2O3D_SYM_HESS(double,d) _TIJK_2O3D_SYM_HESS(float,f) TIJK_TYPE_SYM(2o3d_sym, 2, 3, 6) /* 2nd order 3D antisymmetric */ unsigned int _tijk_2o3d_asym_mult[3] = {2,2,2}; int _tijk_2o3d_asym_unsym2uniq[9] = {0, 1, 2, -1, 0, 3, -2, -3, 0}; int _tijk_2o3d_asym_uniq2unsym[6] = {2, -4, 3, -7, 6, -8}; unsigned int _tijk_2o3d_asym_uniq_idx[3] = {0,2,4}; double _tijk_2o3d_asym_tsp_d (const double *A, const double *B) { return 2*ELL_3V_DOT(A,B); } float _tijk_2o3d_asym_tsp_f (const float *A, const float *B) { return 2*ELL_3V_DOT(A,B); } double _tijk_2o3d_asym_norm_d (const double *A) { return sqrt(2*ELL_3V_DOT(A,A)); } float _tijk_2o3d_asym_norm_f (const float *A) { return sqrt(2*ELL_3V_DOT(A,A)); } #define _TIJK_2O3D_ASYM_CONVERT(TYPE, SUF) \ int \ _tijk_2o3d_asym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_2o3d_asym) { /* copy over */ \ ELL_3V_COPY(res,A); \ return 0; \ } else if (res_type==tijk_2o3d_unsym) { \ res[0]=0; res[1]=A[0]; res[2]=A[1]; \ res[3]=-A[0]; res[4]=0; res[5]=A[2]; \ res[6]=-A[1]; res[7]=-A[2]; res[8]=0; \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_2o3d_asym); \ else \ return 1; \ } _TIJK_2O3D_ASYM_CONVERT(double, d) _TIJK_2O3D_ASYM_CONVERT(float, f) #define _TIJK_2O3D_ASYM_APPROX(TYPE, SUF) \ int \ _tijk_2o3d_asym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_2o3d_asym); \ else \ return 1; \ } _TIJK_2O3D_ASYM_APPROX(double, d) _TIJK_2O3D_ASYM_APPROX(float, f) void _tijk_2o3d_asym_trans_d (double *res, const double *A, const double *M) { /* this code could be optimized at some point */ double tmp[9], tmpout[9]; _tijk_2o3d_asym_convert_d(tmp, tijk_2o3d_unsym, A); _tijk_2o3d_unsym_trans_d(tmpout, tmp, M); _tijk_2o3d_unsym_approx_d(res, tijk_2o3d_asym, tmpout); } void _tijk_2o3d_asym_trans_f (float *res, const float *A, const float *M) { /* this code could be optimized at some point */ float tmp[9], tmpout[9]; _tijk_2o3d_asym_convert_f(tmp, tijk_2o3d_unsym, A); _tijk_2o3d_unsym_trans_f(tmpout, tmp, M); _tijk_2o3d_unsym_approx_f(res, tijk_2o3d_asym, tmpout); } TIJK_TYPE(2o3d_asym, 2, 3, 3) /* 3rd order 3D unsymmetric */ double _tijk_3o3d_unsym_tsp_d (const double *A, const double *B) { double retval=0; unsigned int i; for (i=0; i<27; i++) retval+=(*A++)*(*B++); return retval; } float _tijk_3o3d_unsym_tsp_f (const float *A, const float *B) { float retval=0; unsigned int i; for (i=0; i<27; i++) retval+=(*A++)*(*B++); return retval; } double _tijk_3o3d_unsym_norm_d (const double *A) { return sqrt(_tijk_3o3d_unsym_tsp_d(A,A)); } float _tijk_3o3d_unsym_norm_f (const float *A) { return sqrt(_tijk_3o3d_unsym_tsp_f(A,A)); } void _tijk_3o3d_unsym_trans_d (double *res, const double *A, const double *M) { double _tmp1[27], _tmp2[27]; unsigned int i,j,k; for (i=0; i<3; i++) for (j=0; j<3; j++) for (k=0; k<3; k++) _tmp1[3*(3*i+j)+k]=M[3*k]*A[3*(3*i+j)]+ M[3*k+1]*A[3*(3*i+j)+1]+ M[3*k+2]*A[3*(3*i+j)+2]; for (i=0; i<3; i++) for (j=0; j<3; j++) for (k=0; k<3; k++) _tmp2[3*(3*i+j)+k]=M[3*j]*_tmp1[3*(3*i)+k]+ M[3*j+1]*_tmp1[3*(3*i+1)+k]+ M[3*j+2]*_tmp1[3*(3*i+2)+k]; for (i=0; i<3; i++) for (j=0; j<3; j++) for (k=0; k<3; k++) res[3*(3*i+j)+k]=M[3*i]*_tmp2[3*j+k]+ M[3*i+1]*_tmp2[3*(3+j)+k]+ M[3*i+2]*_tmp2[3*(6+j)+k]; } void _tijk_3o3d_unsym_trans_f (float *res, const float *A, const float *M) { float _tmp1[27], _tmp2[27]; unsigned int i,j,k; for (i=0; i<3; i++) for (j=0; j<3; j++) for (k=0; k<3; k++) _tmp1[3*(3*i+j)+k]=M[3*k]*A[3*(3*i+j)]+ M[3*k+1]*A[3*(3*i+j)+1]+ M[3*k+2]*A[3*(3*i+j)+2]; for (i=0; i<3; i++) for (j=0; j<3; j++) for (k=0; k<3; k++) _tmp2[3*(3*i+j)+k]=M[3*j]*_tmp1[3*(3*i)+k]+ M[3*j+1]*_tmp1[3*(3*i+1)+k]+ M[3*j+2]*_tmp1[3*(3*i+2)+k]; for (i=0; i<3; i++) for (j=0; j<3; j++) for (k=0; k<3; k++) res[3*(3*i+j)+k]=M[3*i]*_tmp2[3*j+k]+ M[3*i+1]*_tmp2[3*(3+j)+k]+ M[3*i+2]*_tmp2[3*(6+j)+k]; } #define _TIJK_3O3D_UNSYM_CONVERT(TYPE, SUF) \ int \ _tijk_3o3d_unsym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_3o3d_unsym) { /* copy over */ \ unsigned int i; \ for (i=0; i<27; i++) \ (*res++)=(*A++); \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_3o3d_unsym); \ else \ return 1; \ } _TIJK_3O3D_UNSYM_CONVERT(double, d) _TIJK_3O3D_UNSYM_CONVERT(float, f) #define _TIJK_3O3D_UNSYM_APPROX(TYPE, SUF) \ int \ _tijk_3o3d_unsym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_3o3d_sym) { \ res[0]=A[0]; res[1]=(A[1]+A[3]+A[9])/3.0; \ res[2]=(A[2]+A[6]+A[18])/3.0; res[3]=(A[4]+A[10]+A[12])/3.0; \ res[4]=(A[5]+A[7]+A[11]+A[15]+A[19]+A[21])/6.0; \ res[5]=(A[8]+A[20]+A[24])/3.0; res[6]=A[13]; \ res[7]=(A[14]+A[16]+A[25])/3.0; res[8]=(A[17]+A[23]+A[25])/3.0; \ res[9]=A[26]; \ return 0; \ } else if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_3o3d_unsym); \ else \ return 1; \ } _TIJK_3O3D_UNSYM_APPROX(double, d) _TIJK_3O3D_UNSYM_APPROX(float, f) TIJK_TYPE_UNSYM(3o3d_unsym, 3, 3, 27) /* 3rd order 3D symmetric */ unsigned int _tijk_3o3d_sym_mult[10]={1,3,3,3,6,3,1,3,3,1}; int _tijk_3o3d_sym_unsym2uniq[27] = {1,2,3,2,4,5,3,5,6, 2,4,5,4,7,8,5,8,9, 3,5,6,5,8,9,6,9,10}; int _tijk_3o3d_sym_uniq2unsym[27] = {1, 2,4,10, 3,7,19, 5,11,13, 6,8,12,16,20,22, 9,21,25, 14, 15,17,23, 18,24,26, 27}; unsigned int _tijk_3o3d_sym_uniq_idx[10] = {0,1,4,7,10,16,19,20,23,26}; #define _TIJK_3O3D_SYM_TSP(A, B) \ ((A)[0]*(B)[0]+(A)[6]*(B)[6]+(A)[9]*(B)[9]+ \ 3*((A)[1]*(B)[1]+(A)[2]*(B)[2]+(A)[3]*(B)[3]+(A)[5]*(B)[5]+ \ (A)[7]*(B)[7]+(A)[8]*(B)[8])+ \ 6*(A)[4]*(B)[4]) double _tijk_3o3d_sym_tsp_d (const double *A, const double *B) { return _TIJK_3O3D_SYM_TSP(A,B); } float _tijk_3o3d_sym_tsp_f (const float *A, const float *B) { return _TIJK_3O3D_SYM_TSP(A,B); } double _tijk_3o3d_sym_norm_d (const double *A) { return sqrt(_TIJK_3O3D_SYM_TSP(A,A)); } float _tijk_3o3d_sym_norm_f (const float *A) { return sqrt(_TIJK_3O3D_SYM_TSP(A,A)); } #define _TIJK_3O3D_SYM_CONVERT(TYPE, SUF) \ int \ _tijk_3o3d_sym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_3o3d_sym) { /* copy over */ \ tijk_copy_##SUF(res, A, tijk_3o3d_sym); \ return 0; \ } else if (res_type==tijk_3o3d_unsym) { \ unsigned int i; \ for (i=0; i<27; i++) { \ res[i]=A[_tijk_3o3d_sym_unsym2uniq[i]-1]; \ } \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_3o3d_sym); \ return 1; \ } _TIJK_3O3D_SYM_CONVERT(double, d) _TIJK_3O3D_SYM_CONVERT(float, f) #define _TIJK_3O3D_SYM_APPROX(TYPE, SUF) \ int \ _tijk_3o3d_sym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A){ \ if (res_type==tijk_1o3d) { \ res[0]=0.6*(A[0]+A[3]+A[5]); \ res[1]=0.6*(A[1]+A[6]+A[8]); \ res[2]=0.6*(A[2]+A[7]+A[9]); \ return 0; \ } else if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_3o3d_sym); \ else \ return 1; \ } _TIJK_3O3D_SYM_APPROX(double, d) _TIJK_3O3D_SYM_APPROX(float, f) void _tijk_3o3d_sym_trans_d (double *res, const double *A, const double *M) { /* this code could be optimized at some point */ double tmp[27], tmpout[27]; _tijk_3o3d_sym_convert_d(tmp, tijk_3o3d_unsym, A); _tijk_3o3d_unsym_trans_d(tmpout, tmp, M); _tijk_3o3d_unsym_approx_d(res, tijk_3o3d_sym, tmpout); } void _tijk_3o3d_sym_trans_f (float *res, const float *A, const float *M) { /* this code could be optimized at some point */ float tmp[27], tmpout[27]; _tijk_3o3d_sym_convert_f(tmp, tijk_3o3d_unsym, A); _tijk_3o3d_unsym_trans_f(tmpout, tmp, M); _tijk_3o3d_unsym_approx_f(res, tijk_3o3d_sym, tmpout); } double _tijk_3o3d_sym_s_form_d (const double *A, const double *v) { double v00=v[0]*v[0], v11=v[1]*v[1], v22=v[2]*v[2]; return A[0]*v00*v[0]+3*A[1]*v00*v[1]+3*A[2]*v00*v[2]+3*A[3]*v11*v[0]+ 6*A[4]*v[0]*v[1]*v[2]+3*A[5]*v22*v[0]+A[6]*v11*v[1]+3*A[7]*v11*v[2]+ 3*A[8]*v[1]*v22+A[9]*v22*v[2]; } float _tijk_3o3d_sym_s_form_f (const float *A, const float *v) { float v00=v[0]*v[0], v11=v[1]*v[1], v22=v[2]*v[2]; return A[0]*v00*v[0]+3*A[1]*v00*v[1]+3*A[2]*v00*v[2]+3*A[3]*v11*v[0]+ 6*A[4]*v[0]*v[1]*v[2]+3*A[5]*v22*v[0]+A[6]*v11*v[1]+3*A[7]*v11*v[2]+ 3*A[8]*v[1]*v22+A[9]*v22*v[2]; } double _tijk_3o3d_sym_mean_d (const double *A) { (void) A; /* odd order; mean does not depend on coeffs */ return 0.0; } float _tijk_3o3d_sym_mean_f (const float *A) { (void) A; /* odd order; mean does not depend on coeffs */ return 0.0f; } double _tijk_3o3d_sym_var_d (const double *A) { /* numerical result taken from MATHEMATICA */ return 1.0/35.0*(5*(A[0]*A[0]+A[6]*A[6]+A[9]*A[9])+ 6*(A[3]*A[5]+A[2]*(A[7]+A[9])+A[6]*A[8]+A[0]*(A[3]+A[5])+ A[1]*(A[6]+A[8])+A[7]*A[9])+ 9*(A[1]*A[1]+A[2]*A[2]+A[3]*A[3]+A[5]*A[5]+A[7]*A[7]+ A[8]*A[8])+ 12*A[4]*A[4]); } float _tijk_3o3d_sym_var_f (const float *A) { /* numerical result taken from MATHEMATICA */ return 1.0/35.0*(5*(A[0]*A[0]+A[6]*A[6]+A[9]*A[9])+ 6*(A[3]*A[5]+A[2]*(A[7]+A[9])+A[6]*A[8]+A[0]*(A[3]+A[5])+ A[1]*(A[6]+A[8])+A[7]*A[9])+ 9*(A[1]*A[1]+A[2]*A[2]+A[3]*A[3]+A[5]*A[5]+A[7]*A[7]+ A[8]*A[8])+ 12*A[4]*A[4]); } void _tijk_3o3d_sym_v_form_d (double *res, const double *A, const double *v) { double v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; res[0] = A[0]*v00+A[3]*v11+A[5]*v22+ 2*(A[1]*v01+A[2]*v02+A[4]*v12); res[1] = A[1]*v00+A[6]*v11+A[8]*v22+ 2*(A[3]*v01+A[4]*v02+A[7]*v12); res[2] = A[2]*v00+A[7]*v11+A[9]*v22+ 2*(A[4]*v01+A[5]*v02+A[8]*v12); } void _tijk_3o3d_sym_v_form_f (float *res, const float *A, const float *v) { float v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; res[0] = A[0]*v00+A[3]*v11+A[5]*v22+ 2*(A[1]*v01+A[2]*v02+A[4]*v12); res[1] = A[1]*v00+A[6]*v11+A[8]*v22+ 2*(A[3]*v01+A[4]*v02+A[7]*v12); res[2] = A[2]*v00+A[7]*v11+A[9]*v22+ 2*(A[4]*v01+A[5]*v02+A[8]*v12); } void _tijk_3o3d_sym_m_form_d (double *res, const double *A, const double *v) { res[0]=A[0]*v[0]+A[1]*v[1]+A[2]*v[2]; res[1]=A[1]*v[0]+A[3]*v[1]+A[4]*v[2]; res[2]=A[2]*v[0]+A[4]*v[1]+A[5]*v[2]; res[3]=A[3]*v[0]+A[6]*v[1]+A[7]*v[2]; res[4]=A[4]*v[0]+A[7]*v[1]+A[8]*v[2]; res[5]=A[5]*v[0]+A[8]*v[1]+A[9]*v[2]; } void _tijk_3o3d_sym_m_form_f (float *res, const float *A, const float *v) { res[0]=A[0]*v[0]+A[1]*v[1]+A[2]*v[2]; res[1]=A[1]*v[0]+A[3]*v[1]+A[4]*v[2]; res[2]=A[2]*v[0]+A[4]*v[1]+A[5]*v[2]; res[3]=A[3]*v[0]+A[6]*v[1]+A[7]*v[2]; res[4]=A[4]*v[0]+A[7]*v[1]+A[8]*v[2]; res[5]=A[5]*v[0]+A[8]*v[1]+A[9]*v[2]; } void _tijk_3o3d_sym_make_rank1_d (double *res, const double s, const double *v) { double v00=v[0]*v[0], v11=v[1]*v[1], v22=v[2]*v[2]; res[0]=s*v00*v[0]; res[1]=s*v00*v[1]; res[2]=s*v00*v[2]; res[3]=s*v[0]*v11; res[4]=s*v[0]*v[1]*v[2]; res[5]=s*v[0]*v22; res[6]=s*v[1]*v11; res[7]=s*v11*v[2]; res[8]=s*v[1]*v22; res[9]=s*v[2]*v22; } void _tijk_3o3d_sym_make_rank1_f (float *res, const float s, const float *v) { float v00=v[0]*v[0], v11=v[1]*v[1], v22=v[2]*v[2]; res[0]=s*v00*v[0]; res[1]=s*v00*v[1]; res[2]=s*v00*v[2]; res[3]=s*v[0]*v11; res[4]=s*v[0]*v[1]*v[2]; res[5]=s*v[0]*v22; res[6]=s*v[1]*v11; res[7]=s*v11*v[2]; res[8]=s*v[1]*v22; res[9]=s*v[2]*v22; } #define _tijk_3o3d_sym_make_iso_d NULL #define _tijk_3o3d_sym_make_iso_f NULL void _tijk_3o3d_sym_grad_d (double *res, const double *A, const double *v) { double proj, projv[3]; _tijk_3o3d_sym_v_form_d (res, A, v); ELL_3V_SCALE(res,3.0,res); proj=ELL_3V_DOT(res,v); ELL_3V_SCALE(projv,-proj,v); ELL_3V_INCR(res,projv); } void _tijk_3o3d_sym_grad_f (float *res, const float *A, const float *v) { float proj, projv[3]; _tijk_3o3d_sym_v_form_f (res, A, v); ELL_3V_SCALE(res,3.0,res); proj=ELL_3V_DOT(res,v); ELL_3V_SCALE(projv,-proj,v); ELL_3V_INCR(res,projv); } #define _TIJK_3O3D_SYM_HESS(TYPE, SUF) \ void \ _tijk_3o3d_sym_hess_##SUF (TYPE *res, const TYPE *A, const TYPE *v) { \ /* get two orthonormal tangents */ \ TYPE t[2][3], cv[2][3], h[6], der, norm, tmp[6]; \ int r,c; \ ell_3v_perp_##SUF(t[0], v); \ ELL_3V_NORM(t[0],t[0],norm); \ ELL_3V_CROSS(t[1],v,t[0]); \ ELL_3V_NORM(t[1],t[1],norm); \ /* compute Hessian w.r.t. t1/t2 */ \ _tijk_3o3d_sym_m_form_##SUF(h, A, v); \ der=3*_tijk_3o3d_sym_s_form_##SUF(A, v); /* first der in direction v*/ \ _tijk_2o3d_sym_v_form_##SUF(cv[0],h,t[0]); \ _tijk_2o3d_sym_v_form_##SUF(cv[1],h,t[1]); \ h[0]=6*ELL_3V_DOT(cv[0],t[0])-der; \ h[1]=6*ELL_3V_DOT(cv[0],t[1]); \ h[2]=h[1]; \ h[3]=6*ELL_3V_DOT(cv[1],t[1])-der; \ /* now turn this into a symmetric order-2 rank-2 3D tensor */ \ for (r=0; r<2; r++) { \ for (c=0; c<3; c++) { \ tmp[3*r+c]=h[2*r]*t[0][c]+h[2*r+1]*t[1][c]; \ } \ } \ res[0]=t[0][0]*tmp[0]+t[1][0]*tmp[3]; \ res[1]=t[0][0]*tmp[1]+t[1][0]*tmp[4]; \ res[2]=t[0][0]*tmp[2]+t[1][0]*tmp[5]; \ res[3]=t[0][1]*tmp[1]+t[1][1]*tmp[4]; \ res[4]=t[0][1]*tmp[2]+t[1][1]*tmp[5]; \ res[5]=t[0][2]*tmp[2]+t[1][2]*tmp[5]; \ } _TIJK_3O3D_SYM_HESS(double,d) _TIJK_3O3D_SYM_HESS(float,f) TIJK_TYPE_SYM(3o3d_sym, 3, 3, 10) /* 4th order 3D symmetric */ /* (unsymmetric counterpart currently not implemented) */ unsigned int _tijk_4o3d_sym_mult[15]={1,4,4,6,12,6,4,12,12,4,1,4,6,4,1}; #define _tijk_4o3d_sym_unsym2uniq NULL #define _tijk_4o3d_sym_uniq2unsym NULL #define _tijk_4o3d_sym_uniq_idx NULL #define _TIJK_4O3D_SYM_TSP(A, B) \ ((A)[0]*(B)[0]+(A)[10]*(B)[10]+(A)[14]*(B)[14]+ \ 4*((A)[1]*(B)[1]+(A)[2]*(B)[2]+(A)[6]*(B)[6]+(A)[9]*(B)[9]+ \ (A)[11]*(B)[11]+(A)[13]*(B)[13])+ \ 6*((A)[3]*(B)[3]+(A)[5]*(B)[5]+(A)[12]*(B)[12])+ \ 12*((A)[4]*(B)[4]+(A)[7]*(B)[7]+(A)[8]*(B)[8])) double _tijk_4o3d_sym_tsp_d (const double *A, const double *B) { return _TIJK_4O3D_SYM_TSP(A,B); } float _tijk_4o3d_sym_tsp_f (const float *A, const float *B) { return _TIJK_4O3D_SYM_TSP(A,B); } double _tijk_4o3d_sym_norm_d (const double *A) { return sqrt(_TIJK_4O3D_SYM_TSP(A,A)); } float _tijk_4o3d_sym_norm_f (const float *A) { return sqrt(_TIJK_4O3D_SYM_TSP(A,A)); } #define _TIJK_4O3D_SYM_TRANS(TYPE, SUF) \ void \ _tijk_4o3d_sym_trans_##SUF (TYPE *res, const TYPE *A, const TYPE *M) { \ /* Tijkl = Mim Mjn Mko Mlp Tmnop \ * For efficiency, we transform mode by mode; the intermediate results \ * have incomplete symmetries! */ \ TYPE tmps[30], tmpl[36]; \ int i; \ { /* mode 4 */ \ int m[30]={0,3,6,0,3,6,0,3,6,0,3,6,0,3,6,0,3,6,0,3,6,0,3,6,0,3,6,0,3,6}; \ int idx[90]={0,1,2,0,1,2,0,1,2,1,3,4,1,3,4,1,3,4,2,4,5,2,4,5,2,4,5,3,6,7,3,6,7,3,6,7,4,7,8,4,7,8,4,7,8,5,8,9,5,8,9,5,8,9,6,10,11,6,10,11,6,10,11,7,11,12,7,11,12,7,11,12,8,12,13,8,12,13,8,12,13,9,13,14,9,13,14,9,13,14}; \ for (i=0; i<30; i++) \ tmps[i]=M[m[i]]*A[idx[3*i]]+ \ M[m[i]+1]*A[idx[3*i+1]]+ \ M[m[i]+2]*A[idx[3*i+2]]; \ } \ { /* mode 3 */ \ int m[36]={0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6}; \ int idx[108]={0,3,6,1,4,7,2,5,8,1,4,7,2,5,8,2,5,8,3,9,12,4,10,13,5,11,14,4,10,13,5,11,14,5,11,14,6,12,15,7,13,16,8,14,17,7,13,16,8,14,17,8,14,17,9,18,21,10,19,22,11,20,23,10,19,22,11,20,23,11,20,23,12,21,24,13,22,25,14,23,26,13,22,25,14,23,26,14,23,26,15,24,27,16,25,28,17,26,29,16,25,28,17,26,29,17,26,29}; \ for (i=0; i<36; i++) \ tmpl[i]=M[m[i]]*tmps[idx[3*i]]+ \ M[m[i]+1]*tmps[idx[3*i+1]]+ \ M[m[i]+2]*tmps[idx[3*i+2]]; \ } \ { /* mode 2 */ \ int m[30]={0,0,0,0,0,0,3,3,3,6,0,0,0,0,0,0,3,3,3,6,0,0,0,0,0,0,3,3,3,6}; \ int idx[90]={0,6,12,1,7,13,2,8,14,3,9,15,4,10,16,5,11,17,3,9,15,4,10,16,5,11,17,5,11,17,6,18,24,7,19,25,8,20,26,9,21,27,10,22,28,11,23,29,9,21,27,10,22,28,11,23,29,11,23,29,12,24,30,13,25,31,14,26,32,15,27,33,16,28,34,17,29,35,15,27,33,16,28,34,17,29,35,17,29,35}; \ for (i=0; i<30; i++) \ tmps[i]=M[m[i]]*tmpl[idx[3*i]]+ \ M[m[i]+1]*tmpl[idx[3*i+1]]+ \ M[m[i]+2]*tmpl[idx[3*i+2]]; \ } \ { /* mode 1 */ \ int m[15]={0,0,0,0,0,0,0,0,0,0,3,3,3,3,6}; \ int idx[45]={0,10,20,1,11,21,2,12,22,3,13,23,4,14,24,5,15,25,6,16,26,7,17,27,8,18,28,9,19,29,6,16,26,7,17,27,8,18,28,9,19,29,9,19,29}; \ for (i=0; i<15; i++) \ res[i]=M[m[i]]*tmps[idx[3*i]]+ \ M[m[i]+1]*tmps[idx[3*i+1]]+ \ M[m[i]+2]*tmps[idx[3*i+2]]; \ } \ } _TIJK_4O3D_SYM_TRANS(double, d) _TIJK_4O3D_SYM_TRANS(float, f) #define _TIJK_4O3D_SYM_CONVERT(TYPE, SUF) \ int \ _tijk_4o3d_sym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_4o3d_sym) { /* copy over */ \ tijk_copy_##SUF(res, A, tijk_4o3d_sym); \ return 0; \ } else if (res_type==tijk_6o3d_sym) { \ /* do this by going to SH and zero-padding */ \ TYPE tmp[28]; \ memset(tmp,0,sizeof(tmp)); \ tijk_3d_sym_to_esh_##SUF (tmp, A, tijk_4o3d_sym); \ tijk_esh_to_3d_sym_##SUF (res, tmp, res_type->order); \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_4o3d_sym); \ return 1; \ } _TIJK_4O3D_SYM_CONVERT(double, d) _TIJK_4O3D_SYM_CONVERT(float, f) #define _TIJK_4O3D_SYM_APPROX(TYPE, SUF) \ int \ _tijk_4o3d_sym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A){ \ if (res_type==tijk_2o3d_sym) { \ res[0]=3.0/35.0*(9*A[0]+8*A[3]+8*A[5]-A[10]-A[14]-2*A[12]); \ res[1]=6.0/7.0*(A[1]+A[6]+A[8]); \ res[2]=6.0/7.0*(A[2]+A[9]+A[7]); \ res[3]=3.0/35.0*(9*A[10]+8*A[3]+8*A[12]-A[0]-A[14]-2*A[5]); \ res[4]=6.0/7.0*(A[11]+A[13]+A[4]); \ res[5]=3.0/35.0*(9*A[14]+8*A[5]+8*A[12]-A[0]-A[10]-2*A[3]); \ return 0; \ } else if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_4o3d_sym); \ else \ return 1; \ } _TIJK_4O3D_SYM_APPROX(double, d) _TIJK_4O3D_SYM_APPROX(float, f) double _tijk_4o3d_sym_s_form_d (const double *A, const double *v) { double v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; return A[0]*v00*v00+4*A[1]*v00*v01+4*A[2]*v00*v02+6*A[3]*v00*v11+ 12*A[4]*v00*v12+6*A[5]*v00*v22+4*A[6]*v01*v11+12*A[7]*v01*v12+ 12*A[8]*v01*v22+4*A[9]*v02*v22+A[10]*v11*v11+4*A[11]*v11*v12+ 6*A[12]*v11*v22+4*A[13]*v12*v22+A[14]*v22*v22; } float _tijk_4o3d_sym_s_form_f (const float *A, const float *v) { float v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; return A[0]*v00*v00+4*A[1]*v00*v01+4*A[2]*v00*v02+6*A[3]*v00*v11+ 12*A[4]*v00*v12+6*A[5]*v00*v22+4*A[6]*v01*v11+12*A[7]*v01*v12+ 12*A[8]*v01*v22+4*A[9]*v02*v22+A[10]*v11*v11+4*A[11]*v11*v12+ 6*A[12]*v11*v22+4*A[13]*v12*v22+A[14]*v22*v22; } double _tijk_4o3d_sym_mean_d (const double *A) { return 0.2*(A[0]+A[10]+A[14]+2*(A[3]+A[5]+A[12])); } float _tijk_4o3d_sym_mean_f (const float *A) { return 0.2f*(A[0]+A[10]+A[14]+2.0f*(A[3]+A[5]+A[12])); } double _tijk_4o3d_sym_var_d (const double *A) { /* numerical result taken from MATHEMATICA */ return 0.0795775*(-1.5319*(A[14]*A[3]+A[10]*A[5]+A[0]*A[12]) -1.14893*(A[12]*A[3]+A[12]*A[5]+A[3]*A[5]) -0.76595*(A[10]*A[14]+A[10]*A[0]+A[0]*A[14]) +0.382975*(A[10]*A[12]+A[12]*A[14]+A[10]*A[3]+A[14]*A[5]+ A[0]*A[3]+A[0]*A[5]) +0.893609*(A[0]*A[0]+A[10]*A[10]+A[14]*A[14]) +2.29785*(A[12]*A[12] + A[3]*A[3] + A[5]*A[5]) +3.19146*(A[1]*A[1]+A[2]*A[2]+A[11]*A[11]+A[13]*A[13]+ A[6]*A[6]+A[9]*A[9]) +3.82975*(A[11]*A[13]+A[11]*A[4]+A[13]*A[4]+A[1]*A[6]+ A[2]*A[7]+A[1]*A[8]+A[6]*A[8]+A[2]*A[9]+ A[7]*A[9]) +5.74463*(A[4]*A[4]+A[7]*A[7]+A[8]*A[8])); } float _tijk_4o3d_sym_var_f (const float *A) { /* numerical result taken from MATHEMATICA */ return 0.0795775*(-1.5319*(A[14]*A[3]+A[10]*A[5]+A[0]*A[12]) -1.14893*(A[12]*A[3]+A[12]*A[5]+A[3]*A[5]) -0.76595*(A[10]*A[14]+A[10]*A[0]+A[0]*A[14]) +0.382975*(A[10]*A[12]+A[12]*A[14]+A[10]*A[3]+A[14]*A[5]+ A[0]*A[3]+A[0]*A[5]) +0.893609*(A[0]*A[0]+A[10]*A[10]+A[14]*A[14]) +2.29785*(A[12]*A[12] + A[3]*A[3] + A[5]*A[5]) +3.19146*(A[1]*A[1]+A[2]*A[2]+A[11]*A[11]+A[13]*A[13]+ A[6]*A[6]+A[9]*A[9]) +3.82975*(A[11]*A[13]+A[11]*A[4]+A[13]*A[4]+A[1]*A[6]+ A[2]*A[7]+A[1]*A[8]+A[6]*A[8]+A[2]*A[9]+ A[7]*A[9]) +5.74463*(A[4]*A[4]+A[7]*A[7]+A[8]*A[8])); } void _tijk_4o3d_sym_v_form_d (double *res, const double *A, const double *v) { double v000=v[0]*v[0]*v[0], v001=v[0]*v[0]*v[1], v002=v[0]*v[0]*v[2], v011=v[0]*v[1]*v[1], v012=v[0]*v[1]*v[2], v022=v[0]*v[2]*v[2], v111=v[1]*v[1]*v[1], v112=v[1]*v[1]*v[2], v122=v[1]*v[2]*v[2], v222=v[2]*v[2]*v[2]; res[0] = A[0]*v000+A[6]*v111+A[9]*v222+6*A[4]*v012+ 3*(A[1]*v001+A[2]*v002+A[3]*v011+A[5]*v022+A[7]*v112+A[8]*v122); res[1] = A[1]*v000+A[10]*v111+A[13]*v222+6*A[7]*v012+ 3*(A[3]*v001+A[4]*v002+A[6]*v011+A[8]*v022+A[11]*v112+A[12]*v122); res[2] = A[2]*v000+A[11]*v111+A[14]*v222+6*A[8]*v012+ 3*(A[4]*v001+A[5]*v002+A[7]*v011+A[9]*v022+A[12]*v112+A[13]*v122); } void _tijk_4o3d_sym_v_form_f (float *res, const float *A, const float *v) { float v000=v[0]*v[0]*v[0], v001=v[0]*v[0]*v[1], v002=v[0]*v[0]*v[2], v011=v[0]*v[1]*v[1], v012=v[0]*v[1]*v[2], v022=v[0]*v[2]*v[2], v111=v[1]*v[1]*v[1], v112=v[1]*v[1]*v[2], v122=v[1]*v[2]*v[2], v222=v[2]*v[2]*v[2]; res[0] = A[0]*v000+A[6]*v111+A[9]*v222+6*A[4]*v012+ 3*(A[1]*v001+A[2]*v002+A[3]*v011+A[5]*v022+A[7]*v112+A[8]*v122); res[1] = A[1]*v000+A[10]*v111+A[13]*v222+6*A[7]*v012+ 3*(A[3]*v001+A[4]*v002+A[6]*v011+A[8]*v022+A[11]*v112+A[12]*v122); res[2] = A[2]*v000+A[11]*v111+A[14]*v222+6*A[8]*v012+ 3*(A[4]*v001+A[5]*v002+A[7]*v011+A[9]*v022+A[12]*v112+A[13]*v122); } void _tijk_4o3d_sym_m_form_d (double *res, const double *A, const double *v) { double v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; res[0]=A[0]*v00+A[3]*v11+A[5]*v22+2*(A[1]*v01+A[2]*v02+A[4]*v12); res[1]=A[1]*v00+A[6]*v11+A[8]*v22+2*(A[3]*v01+A[4]*v02+A[7]*v12); res[2]=A[2]*v00+A[7]*v11+A[9]*v22+2*(A[4]*v01+A[5]*v02+A[8]*v12); res[3]=A[3]*v00+A[10]*v11+A[12]*v22+2*(A[6]*v01+A[7]*v02+A[11]*v12); res[4]=A[4]*v00+A[11]*v11+A[13]*v22+2*(A[7]*v01+A[8]*v02+A[12]*v12); res[5]=A[5]*v00+A[12]*v11+A[14]*v22+2*(A[8]*v01+A[9]*v02+A[13]*v12); } void _tijk_4o3d_sym_m_form_f (float *res, const float *A, const float *v) { float v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; res[0]=A[0]*v00+A[3]*v11+A[5]*v22+2*(A[1]*v01+A[2]*v02+A[4]*v12); res[1]=A[1]*v00+A[6]*v11+A[8]*v22+2*(A[3]*v01+A[4]*v02+A[7]*v12); res[2]=A[2]*v00+A[7]*v11+A[9]*v22+2*(A[4]*v01+A[5]*v02+A[8]*v12); res[3]=A[3]*v00+A[10]*v11+A[12]*v22+2*(A[6]*v01+A[7]*v02+A[11]*v12); res[4]=A[4]*v00+A[11]*v11+A[13]*v22+2*(A[7]*v01+A[8]*v02+A[12]*v12); res[5]=A[5]*v00+A[12]*v11+A[14]*v22+2*(A[8]*v01+A[9]*v02+A[13]*v12); } void _tijk_4o3d_sym_make_rank1_d (double *res, const double s, const double *v) { double v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; res[0]=s*v00*v00; res[1]=s*v00*v01; res[2]=s*v00*v02; res[3]=s*v00*v11; res[4]=s*v00*v12; res[5]=s*v00*v22; res[6]=s*v01*v11; res[7]=s*v01*v12; res[8]=s*v01*v22; res[9]=s*v02*v22; res[10]=s*v11*v11; res[11]=s*v11*v12; res[12]=s*v11*v22; res[13]=s*v12*v22; res[14]=s*v22*v22; } void _tijk_4o3d_sym_make_rank1_f (float *res, const float s, const float *v) { float v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; res[0]=s*v00*v00; res[1]=s*v00*v01; res[2]=s*v00*v02; res[3]=s*v00*v11; res[4]=s*v00*v12; res[5]=s*v00*v22; res[6]=s*v01*v11; res[7]=s*v01*v12; res[8]=s*v01*v22; res[9]=s*v02*v22; res[10]=s*v11*v11; res[11]=s*v11*v12; res[12]=s*v11*v22; res[13]=s*v12*v22; res[14]=s*v22*v22; } void _tijk_4o3d_sym_make_iso_d (double *res, const double s) { res[0]=res[10]=res[14]=s; res[3]=res[5]=res[12]=s/3.0; res[1]=res[2]=res[4]=res[6]=res[7]=res[8]=res[9]=res[11]=res[13]=0.0; } void _tijk_4o3d_sym_make_iso_f (float *res, const float s) { res[0]=res[10]=res[14]=s; res[3]=res[5]=res[12]=s/3.0f; res[1]=res[2]=res[4]=res[6]=res[7]=res[8]=res[9]=res[11]=res[13]=0.0f; } void _tijk_4o3d_sym_grad_d (double *res, const double *A, const double *v) { double proj, projv[3]; _tijk_4o3d_sym_v_form_d (res, A, v); ELL_3V_SCALE(res,4.0,res); proj=ELL_3V_DOT(res,v); ELL_3V_SCALE(projv,-proj,v); ELL_3V_INCR(res,projv); } void _tijk_4o3d_sym_grad_f (float *res, const float *A, const float *v) { float proj, projv[3]; _tijk_4o3d_sym_v_form_f (res, A, v); ELL_3V_SCALE(res,4.0,res); proj=ELL_3V_DOT(res,v); ELL_3V_SCALE(projv,-proj,v); ELL_3V_INCR(res,projv); } #define _TIJK_4O3D_SYM_HESS(TYPE, SUF) \ void \ _tijk_4o3d_sym_hess_##SUF (TYPE *res, const TYPE *A, const TYPE *v) { \ /* get two orthonormal tangents */ \ TYPE t[2][3], cv[2][3], h[6], der, norm, tmp[6]; \ int r,c; \ ell_3v_perp_##SUF(t[0], v); \ ELL_3V_NORM(t[0],t[0],norm); \ ELL_3V_CROSS(t[1],v,t[0]); \ ELL_3V_NORM(t[1],t[1],norm); \ /* compute Hessian w.r.t. t1/t2 */ \ _tijk_4o3d_sym_m_form_##SUF(h, A, v); \ der=4*_tijk_4o3d_sym_s_form_##SUF(A, v); /* first der in direction v*/ \ _tijk_2o3d_sym_v_form_##SUF(cv[0],h,t[0]); \ _tijk_2o3d_sym_v_form_##SUF(cv[1],h,t[1]); \ h[0]=12*ELL_3V_DOT(cv[0],t[0])-der; \ h[1]=12*ELL_3V_DOT(cv[0],t[1]); \ h[2]=h[1]; \ h[3]=12*ELL_3V_DOT(cv[1],t[1])-der; \ /* now turn this into a symmetric order-2 rank-2 3D tensor */ \ for (r=0; r<2; r++) { \ for (c=0; c<3; c++) { \ tmp[3*r+c]=h[2*r]*t[0][c]+h[2*r+1]*t[1][c]; \ } \ } \ res[0]=t[0][0]*tmp[0]+t[1][0]*tmp[3]; \ res[1]=t[0][0]*tmp[1]+t[1][0]*tmp[4]; \ res[2]=t[0][0]*tmp[2]+t[1][0]*tmp[5]; \ res[3]=t[0][1]*tmp[1]+t[1][1]*tmp[4]; \ res[4]=t[0][1]*tmp[2]+t[1][1]*tmp[5]; \ res[5]=t[0][2]*tmp[2]+t[1][2]*tmp[5]; \ } _TIJK_4O3D_SYM_HESS(double,d) _TIJK_4O3D_SYM_HESS(float,f) TIJK_TYPE_SYM(4o3d_sym, 4, 3, 15) /* 6th order 3D symmetric */ /* (unsymmetric counterpart currently not implemented) */ unsigned int _tijk_6o3d_sym_mult[28]={1,6,6,15,30,15,20,60,60,20,15,60,90, 60,15,6,30,60,60,30,6,1,6,15,20,15,6,1}; #define _tijk_6o3d_sym_unsym2uniq NULL #define _tijk_6o3d_sym_uniq2unsym NULL #define _tijk_6o3d_sym_uniq_idx NULL #define _TIJK_6O3D_SYM_TSP(A, B) \ ((A)[0]*(B)[0]+(A)[21]*(B)[21]+(A)[27]*(B)[27]+ \ 6*((A)[1]*(B)[1]+(A)[2]*(B)[2]+(A)[15]*(B)[15]+(A)[20]*(B)[20]+ \ (A)[22]*(B)[22]+(A)[26]*(B)[26])+ \ 15*((A)[3]*(B)[3]+(A)[5]*(B)[5]+(A)[10]*(B)[10]+(A)[14]*(B)[14]+ \ (A)[23]*(B)[23]+(A)[25]*(B)[25])+ \ 30*((A)[4]*(B)[4]+(A)[16]*(B)[16]+(A)[19]*(B)[19])+ \ 20*((A)[6]*(B)[6]+(A)[9]*(B)[9]+(A)[24]*(B)[24])+ \ 60*((A)[7]*(B)[7]+(A)[8]*(B)[8]+(A)[11]*(B)[11]+ \ (A)[13]*(B)[13]+(A)[17]*(B)[17]+(A)[18]*(B)[18])+ \ 90*(A)[12]*(B)[12]) \ double _tijk_6o3d_sym_tsp_d (const double *A, const double *B) { return _TIJK_6O3D_SYM_TSP(A,B); } float _tijk_6o3d_sym_tsp_f (const float *A, const float *B) { return _TIJK_6O3D_SYM_TSP(A,B); } double _tijk_6o3d_sym_norm_d (const double *A) { return sqrt(_TIJK_6O3D_SYM_TSP(A,A)); } float _tijk_6o3d_sym_norm_f (const float *A) { return sqrt(_TIJK_6O3D_SYM_TSP(A,A)); } #define _TIJK_6O3D_SYM_TRANS(TYPE, SUF) \ void \ _tijk_6o3d_sym_trans_##SUF (TYPE *res, const TYPE *A, const TYPE *M) { \ /* Tijklmn = Mio Mjp Mkq Mlr Mms Mnt Topqrst \ * For efficiency, we transform mode by mode; the intermediate results \ * have incomplete symmetries! */ \ TYPE tmpl[100], tmpr[100]; \ int i; \ { /* mode 6 */ \ int m[3]={0,3,6}; \ int idx[189]={0,1,2,0,1,2,0,1,2,1,3,4,1,3,4,1,3,4,2,4,5,2,4,5,2,4,5,3,6,7,3,6,7,3,6,7,4,7,8,4,7,8,4,7,8,5,8,9,5,8,9,5,8,9,6,10,11,6,10,11,6,10,11,7,11,12,7,11,12,7,11,12,8,12,13,8,12,13,8,12,13,9,13,14,9,13,14,9,13,14,10,15,16,10,15,16,10,15,16,11,16,17,11,16,17,11,16,17,12,17,18,12,17,18,12,17,18,13,18,19,13,18,19,13,18,19,14,19,20,14,19,20,14,19,20,15,21,22,15,21,22,15,21,22,16,22,23,16,22,23,16,22,23,17,23,24,17,23,24,17,23,24,18,24,25,18,24,25,18,24,25,19,25,26,19,25,26,19,25,26,20,26,27,20,26,27,20,26,27}; \ for (i=0; i<63; i++) \ tmpl[i]=M[m[i%3]]*A[idx[3*i]]+ \ M[m[i%3]+1]*A[idx[3*i+1]]+ \ M[m[i%3]+2]*A[idx[3*i+2]]; \ } \ { /* mode 5 */ \ int m[90]={0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6,0,0,0,3,3,6}; \ int idx[270]={0,3,6,1,4,7,2,5,8,1,4,7,2,5,8,2,5,8,3,9,12,4,10,13,5,11,14,4,10,13,5,11,14,5,11,14,6,12,15,7,13,16,8,14,17,7,13,16,8,14,17,8,14,17,9,18,21,10,19,22,11,20,23,10,19,22,11,20,23,11,20,23,12,21,24,13,22,25,14,23,26,13,22,25,14,23,26,14,23,26,15,24,27,16,25,28,17,26,29,16,25,28,17,26,29,17,26,29,18,30,33,19,31,34,20,32,35,19,31,34,20,32,35,20,32,35,21,33,36,22,34,37,23,35,38,22,34,37,23,35,38,23,35,38,24,36,39,25,37,40,26,38,41,25,37,40,26,38,41,26,38,41,27,39,42,28,40,43,29,41,44,28,40,43,29,41,44,29,41,44,30,45,48,31,46,49,32,47,50,31,46,49,32,47,50,32,47,50,33,48,51,34,49,52,35,50,53,34,49,52,35,50,53,35,50,53,36,51,54,37,52,55,38,53,56,37,52,55,38,53,56,38,53,56,39,54,57,40,55,58,41,56,59,40,55,58,41,56,59,41,56,59,42,57,60,43,58,61,44,59,62,43,58,61,44,59,62,44,59,62}; \ for (i=0; i<90; i++) \ tmpr[i]=M[m[i]]*tmpl[idx[3*i]]+ \ M[m[i]+1]*tmpl[idx[3*i+1]]+ \ M[m[i]+2]*tmpl[idx[3*i+2]]; \ } \ { /* mode 4 */ \ int m[100]={0,0,0,0,0,0,3,3,3,6,0,0,0,0,0,0,3,3,3,6,0,0,0,0,0,0,3,3,3,6,0,0,0,0,0,0,3,3,3,6,0,0,0,0,0,0,3,3,3,6,0,0,0,0,0,0,3,3,3,6,0,0,0,0,0,0,3,3,3,6,0,0,0,0,0,0,3,3,3,6,0,0,0,0,0,0,3,3,3,6,0,0,0,0,0,0,3,3,3,6}; \ int idx[300]={0,6,12,1,7,13,2,8,14,3,9,15,4,10,16,5,11,17,3,9,15,4,10,16,5,11,17,5,11,17,6,18,24,7,19,25,8,20,26,9,21,27,10,22,28,11,23,29,9,21,27,10,22,28,11,23,29,11,23,29,12,24,30,13,25,31,14,26,32,15,27,33,16,28,34,17,29,35,15,27,33,16,28,34,17,29,35,17,29,35,18,36,42,19,37,43,20,38,44,21,39,45,22,40,46,23,41,47,21,39,45,22,40,46,23,41,47,23,41,47,24,42,48,25,43,49,26,44,50,27,45,51,28,46,52,29,47,53,27,45,51,28,46,52,29,47,53,29,47,53,30,48,54,31,49,55,32,50,56,33,51,57,34,52,58,35,53,59,33,51,57,34,52,58,35,53,59,35,53,59,36,60,66,37,61,67,38,62,68,39,63,69,40,64,70,41,65,71,39,63,69,40,64,70,41,65,71,41,65,71,42,66,72,43,67,73,44,68,74,45,69,75,46,70,76,47,71,77,45,69,75,46,70,76,47,71,77,47,71,77,48,72,78,49,73,79,50,74,80,51,75,81,52,76,82,53,77,83,51,75,81,52,76,82,53,77,83,53,77,83,54,78,84,55,79,85,56,80,86,57,81,87,58,82,88,59,83,89,57,81,87,58,82,88,59,83,89,59,83,89}; \ for (i=0; i<100; i++) \ tmpl[i]=M[m[i]]*tmpr[idx[3*i]]+ \ M[m[i]+1]*tmpr[idx[3*i+1]]+ \ M[m[i]+2]*tmpr[idx[3*i+2]]; \ } \ { /* mode 3 */ \ int m[90]={0,0,0,0,0,0,0,0,0,0,3,3,3,3,6,0,0,0,0,0,0,0,0,0,0,3,3,3,3,6,0,0,0,0,0,0,0,0,0,0,3,3,3,3,6,0,0,0,0,0,0,0,0,0,0,3,3,3,3,6,0,0,0,0,0,0,0,0,0,0,3,3,3,3,6,0,0,0,0,0,0,0,0,0,0,3,3,3,3,6}; \ int idx[270]={0,10,20,1,11,21,2,12,22,3,13,23,4,14,24,5,15,25,6,16,26,7,17,27,8,18,28,9,19,29,6,16,26,7,17,27,8,18,28,9,19,29,9,19,29,10,30,40,11,31,41,12,32,42,13,33,43,14,34,44,15,35,45,16,36,46,17,37,47,18,38,48,19,39,49,16,36,46,17,37,47,18,38,48,19,39,49,19,39,49,20,40,50,21,41,51,22,42,52,23,43,53,24,44,54,25,45,55,26,46,56,27,47,57,28,48,58,29,49,59,26,46,56,27,47,57,28,48,58,29,49,59,29,49,59,30,60,70,31,61,71,32,62,72,33,63,73,34,64,74,35,65,75,36,66,76,37,67,77,38,68,78,39,69,79,36,66,76,37,67,77,38,68,78,39,69,79,39,69,79,40,70,80,41,71,81,42,72,82,43,73,83,44,74,84,45,75,85,46,76,86,47,77,87,48,78,88,49,79,89,46,76,86,47,77,87,48,78,88,49,79,89,49,79,89,50,80,90,51,81,91,52,82,92,53,83,93,54,84,94,55,85,95,56,86,96,57,87,97,58,88,98,59,89,99,56,86,96,57,87,97,58,88,98,59,89,99,59,89,99}; \ for (i=0; i<90; i++) \ tmpr[i]=M[m[i]]*tmpl[idx[3*i]]+ \ M[m[i]+1]*tmpl[idx[3*i+1]]+ \ M[m[i]+2]*tmpl[idx[3*i+2]]; \ } \ { /* mode 2 */ \ int m[63]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,6}; \ int idx[189]={0,15,30,1,16,31,2,17,32,3,18,33,4,19,34,5,20,35,6,21,36,7,22,37,8,23,38,9,24,39,10,25,40,11,26,41,12,27,42,13,28,43,14,29,44,10,25,40,11,26,41,12,27,42,13,28,43,14,29,44,14,29,44,15,45,60,16,46,61,17,47,62,18,48,63,19,49,64,20,50,65,21,51,66,22,52,67,23,53,68,24,54,69,25,55,70,26,56,71,27,57,72,28,58,73,29,59,74,25,55,70,26,56,71,27,57,72,28,58,73,29,59,74,29,59,74,30,60,75,31,61,76,32,62,77,33,63,78,34,64,79,35,65,80,36,66,81,37,67,82,38,68,83,39,69,84,40,70,85,41,71,86,42,72,87,43,73,88,44,74,89,40,70,85,41,71,86,42,72,87,43,73,88,44,74,89,44,74,89}; \ for (i=0; i<63; i++) \ tmpl[i]=M[m[i]]*tmpr[idx[3*i]]+ \ M[m[i]+1]*tmpr[idx[3*i+1]]+ \ M[m[i]+2]*tmpr[idx[3*i+2]]; \ } \ { /* mode 1 */ \ int m[28]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,6}; \ int idx[84]={0,21,42,1,22,43,2,23,44,3,24,45,4,25,46,5,26,47,6,27,48,7,28,49,8,29,50,9,30,51,10,31,52,11,32,53,12,33,54,13,34,55,14,35,56,15,36,57,16,37,58,17,38,59,18,39,60,19,40,61,20,41,62,15,36,57,16,37,58,17,38,59,18,39,60,19,40,61,20,41,62,20,41,62}; \ for (i=0; i<28; i++) \ res[i]=M[m[i]]*tmpl[idx[3*i]]+ \ M[m[i]+1]*tmpl[idx[3*i+1]]+ \ M[m[i]+2]*tmpl[idx[3*i+2]]; \ } \ } _TIJK_6O3D_SYM_TRANS(double, d) _TIJK_6O3D_SYM_TRANS(float, f) #define _TIJK_6O3D_SYM_CONVERT(TYPE, SUF) \ int \ _tijk_6o3d_sym_convert_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A) { \ if (res_type==tijk_6o3d_sym) { /* copy over */ \ tijk_copy_##SUF(res, A, tijk_6o3d_sym); \ return 0; \ } else if (NULL!=res_type->_convert_from_##SUF) \ return (*res_type->_convert_from_##SUF)(res,A,tijk_6o3d_sym); \ return 1; \ } _TIJK_6O3D_SYM_CONVERT(double, d) _TIJK_6O3D_SYM_CONVERT(float, f) #define _TIJK_6O3D_SYM_APPROX(TYPE, SUF) \ int \ _tijk_6o3d_sym_approx_##SUF (TYPE *res, const tijk_type *res_type, \ const TYPE *A){ \ if (res_type==tijk_2o3d_sym) { \ /* do this in two steps */ \ TYPE tmp[15]; \ _tijk_6o3d_sym_approx_##SUF(tmp, tijk_4o3d_sym, A); \ _tijk_4o3d_sym_approx_##SUF(res, tijk_2o3d_sym, tmp); \ return 0; \ } else if (res_type==tijk_4o3d_sym) { \ res[0]=5.0/231.0*(43*A[0]+A[21]+A[27]+24*(A[3]+A[5])+ \ 3*(A[23]+A[25])-18*(A[10]+A[14])-36*A[12]); \ res[1]=5.0/22.0*(5*A[1]+4*(A[6]+A[8])-A[15]-A[19]-2*A[17]); \ res[2]=5.0/22.0*(5*A[2]+4*(A[9]+A[7])-A[20]-A[16]-2*A[18]); \ res[3]=5.0/1386.0*(321*(A[3]+A[10])+306*A[12]-36*(A[5]+A[23])- \ 19*(A[0]+A[21])-15*(A[14]+A[25])+2*A[27]); \ res[4]=5.0/66.0*(17*A[4]+16*(A[11]+A[13])-A[22]-A[26]-2*A[24]); \ res[5]=5.0/1386.0*(321*(A[5]+A[14])+306*A[12]-36*(A[3]+A[25])- \ 19*(A[0]+A[27])-15*(A[10]+A[23])+2*A[21]); \ res[6]=5.0/22.0*(5*A[15]+4*(A[6]+A[17])-A[1]-A[19]-2*A[8]); \ res[7]=5.0/66.0*(17*A[16]+16*(A[7]+A[18])-A[2]-A[20]-2*A[9]); \ res[8]=5.0/66.0*(17*A[19]+16*(A[8]+A[17])-A[1]-A[15]-2*A[6]); \ res[9]=5.0/22.0*(5*A[20]+4*(A[9]+A[18])-A[2]-A[16]-2*A[7]); \ res[10]=5.0/231.0*(43*A[21]+A[0]+A[27]+24*(A[10]+A[23])+ \ 3*(A[5]+A[14])-18*(A[3]+A[25])-36*A[12]); \ res[11]=5.0/22.0*(5*A[22]+4*(A[23]+A[11])-A[26]-A[4]-2*A[13]); \ res[12]=5.0/1386.0*(312*(A[23]+A[25])+306*A[12]-36*(A[10]+A[14])- \ 19*(A[21]+A[27])-15*(A[5]-A[3])+2*A[0]); \ res[13]=5.0/22.0*(5*A[26]+4*(A[24]+A[13])-A[22]-A[4]-2*A[11]); \ res[14]=5.0/231.0*(43*A[27]+A[0]+A[21]+24*(A[14]+A[25])+ \ 3*(A[3]+A[10])-18*(A[5]+A[23])-36*A[12]); \ return 0; \ } else if (NULL!=res_type->_approx_from_##SUF) \ return (*res_type->_approx_from_##SUF)(res,A,tijk_6o3d_sym); \ return 1; \ } _TIJK_6O3D_SYM_APPROX(double, d) _TIJK_6O3D_SYM_APPROX(float, f) double _tijk_6o3d_sym_s_form_d (const double *A, const double *v) { double v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; return A[0]*v00*v00*v00+ A[21]*v11*v11*v11+ A[27]*v22*v22*v22+ 6*(A[1]*v00*v00*v01+ A[2]*v00*v00*v02+ A[15]*v01*v11*v11+ A[20]*v02*v22*v22+ A[22]*v11*v11*v12+ A[26]*v12*v22*v22)+ 15*(A[3]*v00*v00*v11+ A[5]*v00*v00*v22+ A[10]*v00*v11*v11+ A[14]*v00*v22*v22+ A[23]*v11*v11*v22+ A[25]*v11*v22*v22)+ 30*(A[4]*v00*v00*v12+ A[16]*v01*v11*v12+ A[19]*v01*v22*v22)+ 20*(A[6]*v00*v01*v11+ A[9]*v00*v02*v22+ A[24]*v11*v12*v22)+ 60*(A[7]*v00*v01*v12+ A[8]*v00*v01*v22+ A[11]*v00*v11*v12+ A[13]*v00*v12*v22+ A[17]*v01*v11*v22+ A[18]*v01*v12*v22)+ 90*A[12]*v00*v11*v22; } float _tijk_6o3d_sym_s_form_f (const float *A, const float *v) { float v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; return A[0]*v00*v00*v00+ A[21]*v11*v11*v11+ A[27]*v22*v22*v22+ 6*(A[1]*v00*v00*v01+ A[2]*v00*v00*v02+ A[15]*v01*v11*v11+ A[20]*v02*v22*v22+ A[22]*v11*v11*v12+ A[26]*v12*v22*v22)+ 15*(A[3]*v00*v00*v11+ A[5]*v00*v00*v22+ A[10]*v00*v11*v11+ A[14]*v00*v22*v22+ A[23]*v11*v11*v22+ A[25]*v11*v22*v22)+ 30*(A[4]*v00*v00*v12+ A[16]*v01*v11*v12+ A[19]*v01*v22*v22)+ 20*(A[6]*v00*v01*v11+ A[9]*v00*v02*v22+ A[24]*v11*v12*v22)+ 60*(A[7]*v00*v01*v12+ A[8]*v00*v01*v22+ A[11]*v00*v11*v12+ A[13]*v00*v12*v22+ A[17]*v01*v11*v22+ A[18]*v01*v12*v22)+ 90*A[12]*v00*v11*v22; } double _tijk_6o3d_sym_mean_d (const double *A) { return (A[0]+A[21]+A[27]+ 3*(A[3]+A[5]+A[10]+A[14]+A[23]+A[25])+ 6*A[12])/7.0; } float _tijk_6o3d_sym_mean_f (const float *A) { return (A[0]+A[21]+A[27]+ 3*(A[3]+A[5]+A[10]+A[14]+A[23]+A[25])+ 6*A[12])/7.0; } double _tijk_6o3d_sym_var_d (const double *A) { double mean=(A[0]+A[21]+A[27]+ 3*(A[3]+A[5]+A[10]+A[14]+A[23]+A[25])+ 6*A[12])/7.0; return -mean*mean + (1.0/3003.0)* (+ 10*A[21]*A[27] + 30*(A[14]*A[21]+A[27]*A[3]) + 210*(A[21]*(A[3]+A[25])+A[23]*A[27]) + 231*(A[0]*A[0]+A[21]*A[21]+A[27]*A[27]) + 270*A[25]*A[3] + 420*A[12]*(A[21]+A[27]) + 450*(A[14]*(A[3]+A[23])+A[23]*A[3]) + 630*(A[21]*A[23]+A[14]*A[27]+A[25]*A[27]) + 1050*A[14]*A[25] + 1575*(A[3]*A[3]+A[5]*A[5]+A[10]*A[10]+A[14]*A[14]+ A[23]*A[23]+A[25]*A[25]) + 2250*A[23]*A[25] + 2700*A[12]*(A[3]+A[14]+A[23]+A[25]) + 4860*A[12]*A[12] + 30*A[5]*(90*A[12]+ 75*A[14]+ A[21]+ 9*A[23]+ 15*A[25]+ 7*A[27]+ 35*A[3]) + 30*A[10]*(90*A[12]+9*A[14]+21*A[21]+35*A[23]+15*A[25]+A[27]+75*A[3]+ 15*A[5]) + 10*A[0]*(21*A[10]+42*A[12]+21*A[14]+A[21]+3*A[23]+3*A[25]+A[27]+ 63*(A[3]+A[5])) + 4*(+30*A[1]*(3*A[15] + 6*A[17] + 3*A[19] + 14*(A[6] + A[8])) +60*(A[8]*(3*(A[15] + 6*A[17] + 5*A[19]) + 10*A[6])+ A[9]*(3*A[16] + 10*A[18] + 7*(A[2] + A[20]) + 10*A[7])+ A[11]*(18*A[13] + 7*A[22] + 10*A[24] + 3*A[26] + 15*A[4])+ A[13]*(3*A[22] + 10*A[24] + 7*A[26] + 15*A[4])+ A[18]*(3*A[2]+7*A[20]+18*A[7])) +90*(A[2]*A[20]+A[15]*A[19]+A[22]*A[26]+ A[4]*(A[22]+2*A[24]+A[26])+ A[16]*(10*A[18]+A[2]+A[20]+10*A[7])) +180*(A[20]*A[7]+A[19]*A[6]) +189*(A[1]*A[1]+A[2]*A[2]+A[15]*A[15]+A[20]*A[20]+ A[22]*A[22]+A[26]*A[26]) +420*(A[2]*A[7]+A[15]*A[17]+A[22]*A[24]+A[24]*A[26]+A[15]*A[6]) +500*(A[9]*A[9]+A[24]*A[24]+A[6]*A[6]) +525*(A[4]*A[4]+A[16]*A[16]+A[19]*A[19]) +600*A[17]*A[6] +900*(A[7]*A[7]+A[8]*A[8]+A[11]*A[11]+A[13]*A[13]+A[17]*A[17]+ A[18]*A[18]+A[17]*A[19]))); } float _tijk_6o3d_sym_var_f (const float *A) { float mean=(A[0]+A[21]+A[27]+ 3*(A[3]+A[5]+A[10]+A[14]+A[23]+A[25])+ 6*A[12])/7.0; return -mean*mean + (1.0/3003.0)* (+ 10*A[21]*A[27] + 30*(A[14]*A[21]+A[27]*A[3]) + 210*(A[21]*(A[3]+A[25])+A[23]*A[27]) + 231*(A[0]*A[0]+A[21]*A[21]+A[27]*A[27]) + 270*A[25]*A[3] + 420*A[12]*(A[21]+A[27]) + 450*(A[14]*(A[3]+A[23])+A[23]*A[3]) + 630*(A[21]*A[23]+A[14]*A[27]+A[25]*A[27]) + 1050*A[14]*A[25] + 1575*(A[3]*A[3]+A[5]*A[5]+A[10]*A[10]+A[14]*A[14]+ A[23]*A[23]+A[25]*A[25]) + 2250*A[23]*A[25] + 2700*A[12]*(A[3]+A[14]+A[23]+A[25]) + 4860*A[12]*A[12] + 30*A[5]*(90*A[12]+ 75*A[14]+ A[21]+ 9*A[23]+ 15*A[25]+ 7*A[27]+ 35*A[3]) + 30*A[10]*(90*A[12]+9*A[14]+21*A[21]+35*A[23]+15*A[25]+A[27]+75*A[3]+ 15*A[5]) + 10*A[0]*(21*A[10]+42*A[12]+21*A[14]+A[21]+3*A[23]+3*A[25]+A[27]+ 63*(A[3]+A[5])) + 4*(+30*A[1]*(3*A[15] + 6*A[17] + 3*A[19] + 14*(A[6] + A[8])) +60*(A[8]*(3*(A[15] + 6*A[17] + 5*A[19]) + 10*A[6])+ A[9]*(3*A[16] + 10*A[18] + 7*(A[2] + A[20]) + 10*A[7])+ A[11]*(18*A[13] + 7*A[22] + 10*A[24] + 3*A[26] + 15*A[4])+ A[13]*(3*A[22] + 10*A[24] + 7*A[26] + 15*A[4])+ A[18]*(3*A[2]+7*A[20]+18*A[7])) +90*(A[2]*A[20]+A[15]*A[19]+A[22]*A[26]+ A[4]*(A[22]+2*A[24]+A[26])+ A[16]*(10*A[18]+A[2]+A[20]+10*A[7])) +180*(A[20]*A[7]+A[19]*A[6]) +189*(A[1]*A[1]+A[2]*A[2]+A[15]*A[15]+A[20]*A[20]+ A[22]*A[22]+A[26]*A[26]) +420*(A[2]*A[7]+A[15]*A[17]+A[22]*A[24]+A[24]*A[26]+A[15]*A[6]) +500*(A[9]*A[9]+A[24]*A[24]+A[6]*A[6]) +525*(A[4]*A[4]+A[16]*A[16]+A[19]*A[19]) +600*A[17]*A[6] +900*(A[7]*A[7]+A[8]*A[8]+A[11]*A[11]+A[13]*A[13]+A[17]*A[17]+ A[18]*A[18]+A[17]*A[19]))); } #define _TIJK_6O3D_SYM_V_FORM(TYPE, SUF) \ void \ _tijk_6o3d_sym_v_form_##SUF (TYPE *res, const TYPE *A, const TYPE *v) { \ TYPE v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], \ v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; \ TYPE v00000=v00*v00*v[0], v00001=v00*v00*v[1], v00002=v00*v00*v[2], \ v00011=v00*v01*v[1], v00012=v00*v01*v[2], v00022=v00*v02*v[2], \ v00111=v00*v11*v[1], v00112=v00*v11*v[2], v00122=v00*v12*v[2], \ v00222=v00*v22*v[2], v01111=v01*v11*v[1], v01112=v01*v11*v[2], \ v01122=v01*v12*v[2], v01222=v01*v22*v[2], v02222=v02*v22*v[2], \ v11111=v11*v11*v[1], v11112=v11*v11*v[2], v11122=v11*v12*v[2], \ v11222=v11*v22*v[2], v12222=v12*v22*v[2], v22222=v22*v22*v[2]; \ res[0] = A[0]*v00000+ \ 5*A[1]*v00001+ \ 5*A[2]*v00002+ \ 10*A[3]*v00011+ \ 20*A[4]*v00012+ \ 10*A[5]*v00022+ \ 10*A[6]*v00111+ \ 30*A[7]*v00112+ \ 30*A[8]*v00122+ \ 10*A[9]*v00222+ \ 5*A[10]*v01111+ \ 20*A[11]*v01112+ \ 30*A[12]*v01122+ \ 20*A[13]*v01222+ \ 5*A[14]*v02222+ \ A[15]*v11111+ \ 5*A[16]*v11112+ \ 10*A[17]*v11122+ \ 10*A[18]*v11222+ \ 5*A[19]*v12222+ \ A[20]*v22222; \ res[1] = A[1]*v00000+ \ 5*A[3]*v00001+ \ 5*A[4]*v00002+ \ 10*A[6]*v00011+ \ 20*A[7]*v00012+ \ 10*A[8]*v00022+ \ 10*A[10]*v00111+ \ 30*A[11]*v00112+ \ 30*A[12]*v00122+ \ 10*A[13]*v00222+ \ 5*A[15]*v01111+ \ 20*A[16]*v01112+ \ 30*A[17]*v01122+ \ 20*A[18]*v01222+ \ 5*A[19]*v02222+ \ A[21]*v11111+ \ 5*A[22]*v11112+ \ 10*A[23]*v11122+ \ 10*A[24]*v11222+ \ 5*A[25]*v12222+ \ A[26]*v22222; \ res[2] = A[2]*v00000+ \ 5*A[4]*v00001+ \ 5*A[5]*v00002+ \ 10*A[7]*v00011+ \ 20*A[8]*v00012+ \ 10*A[9]*v00022+ \ 10*A[11]*v00111+ \ 30*A[12]*v00112+ \ 30*A[13]*v00122+ \ 10*A[14]*v00222+ \ 5*A[16]*v01111+ \ 20*A[17]*v01112+ \ 30*A[18]*v01122+ \ 20*A[19]*v01222+ \ 5*A[20]*v02222+ \ A[22]*v11111+ \ 5*A[23]*v11112+ \ 10*A[24]*v11122+ \ 10*A[25]*v11222+ \ 5*A[26]*v12222+ \ A[27]*v22222; \ } _TIJK_6O3D_SYM_V_FORM(double, d) _TIJK_6O3D_SYM_V_FORM(float, f) #define _TIJK_6O3D_SYM_M_FORM(TYPE, SUF) \ void \ _tijk_6o3d_sym_m_form_##SUF (TYPE *res, const TYPE *A, const TYPE *v) { \ TYPE v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], \ v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; \ TYPE v0000=v00*v00, v0001=v00*v01, v0002=v00*v02, v0011=v00*v11, \ v0012=v00*v12, v0022=v00*v22, v0111=v01*v11, v0112=v01*v12, \ v0122=v01*v22, v0222=v02*v22, v1111=v11*v11, v1112=v11*v12, \ v1122=v11*v22, v1222=v12*v22, v2222=v22*v22; \ res[0] = A[0]*v0000+ \ 4*A[1]*v0001+ \ 4*A[2]*v0002+ \ 6*A[3]*v0011+ \ 12*A[4]*v0012+ \ 6*A[5]*v0022+ \ 4*A[6]*v0111+ \ 12*A[7]*v0112+ \ 12*A[8]*v0122+ \ 4*A[9]*v0222+ \ A[10]*v1111+ \ 4*A[11]*v1112+ \ 6*A[12]*v1122+ \ 4*A[13]*v1222+ \ A[14]*v2222; \ res[1] = A[1]*v0000+ \ 4*A[3]*v0001+ \ 4*A[4]*v0002+ \ 6*A[6]*v0011+ \ 12*A[7]*v0012+ \ 6*A[8]*v0022+ \ 4*A[10]*v0111+ \ 12*A[11]*v0112+ \ 12*A[12]*v0122+ \ 4*A[13]*v0222+ \ A[15]*v1111+ \ 4*A[16]*v1112+ \ 6*A[17]*v1122+ \ 4*A[18]*v1222+ \ A[19]*v2222; \ res[2] = A[2]*v0000+ \ 4*A[4]*v0001+ \ 4*A[5]*v0002+ \ 6*A[7]*v0011+ \ 12*A[8]*v0012+ \ 6*A[9]*v0022+ \ 4*A[11]*v0111+ \ 12*A[12]*v0112+ \ 12*A[13]*v0122+ \ 4*A[14]*v0222+ \ A[16]*v1111+ \ 4*A[17]*v1112+ \ 6*A[18]*v1122+ \ 4*A[19]*v1222+ \ A[20]*v2222; \ res[3] = A[3]*v0000+ \ 4*A[6]*v0001+ \ 4*A[7]*v0002+ \ 6*A[10]*v0011+ \ 12*A[11]*v0012+ \ 6*A[12]*v0022+ \ 4*A[15]*v0111+ \ 12*A[16]*v0112+ \ 12*A[17]*v0122+ \ 4*A[18]*v0222+ \ A[21]*v1111+ \ 4*A[22]*v1112+ \ 6*A[23]*v1122+ \ 4*A[24]*v1222+ \ A[25]*v2222; \ res[4] = A[4]*v0000+ \ 4*A[7]*v0001+ \ 4*A[8]*v0002+ \ 6*A[11]*v0011+ \ 12*A[12]*v0012+ \ 6*A[13]*v0022+ \ 4*A[16]*v0111+ \ 12*A[17]*v0112+ \ 12*A[18]*v0122+ \ 4*A[19]*v0222+ \ A[22]*v1111+ \ 4*A[23]*v1112+ \ 6*A[24]*v1122+ \ 4*A[25]*v1222+ \ A[26]*v2222; \ res[5] = A[5]*v0000+ \ 4*A[8]*v0001+ \ 4*A[9]*v0002+ \ 6*A[12]*v0011+ \ 12*A[13]*v0012+ \ 6*A[14]*v0022+ \ 4*A[17]*v0111+ \ 12*A[18]*v0112+ \ 12*A[19]*v0122+ \ 4*A[20]*v0222+ \ A[23]*v1111+ \ 4*A[24]*v1112+ \ 6*A[25]*v1122+ \ 4*A[26]*v1222+ \ A[27]*v2222; \ } _TIJK_6O3D_SYM_M_FORM(double, d) _TIJK_6O3D_SYM_M_FORM(float, f) #define _TIJK_6O3D_SYM_MAKE_RANK1(TYPE, SUF) \ void \ _tijk_6o3d_sym_make_rank1_##SUF (TYPE *res, const TYPE s, const TYPE *v) { \ TYPE v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], \ v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; \ res[0]=s*v00*v00*v00; res[1]=s*v00*v00*v01; res[2]=s*v00*v00*v02; \ res[3]=s*v00*v00*v11; res[4]=s*v00*v00*v12; res[5]=s*v00*v00*v22; \ res[6]=s*v00*v01*v11; res[7]=s*v00*v01*v12; res[8]=s*v00*v01*v22; \ res[9]=s*v00*v02*v22; res[10]=s*v00*v11*v11; res[11]=s*v00*v11*v12; \ res[12]=s*v00*v11*v22; res[13]=s*v00*v12*v22; res[14]=s*v00*v22*v22; \ res[15]=s*v01*v11*v11; res[16]=s*v01*v11*v12; res[17]=s*v01*v11*v22; \ res[18]=s*v01*v12*v22; res[19]=s*v01*v22*v22; res[20]=s*v02*v22*v22; \ res[21]=s*v11*v11*v11; res[22]=s*v11*v11*v12; res[23]=s*v11*v11*v22; \ res[24]=s*v11*v12*v22; res[25]=s*v11*v22*v22; res[26]=s*v12*v22*v22; \ res[27]=s*v22*v22*v22; \ } _TIJK_6O3D_SYM_MAKE_RANK1(double, d) _TIJK_6O3D_SYM_MAKE_RANK1(float, f) void _tijk_6o3d_sym_make_iso_d (double *res, const double s) { /* initialize to zero, then set non-zero elements */ unsigned int i; for (i=0; i<28; i++) res[i]=0; res[0]=res[21]=res[27]=s; res[3]=res[5]=res[10]=res[14]=res[23]=res[25]=0.2*s; res[12]=s/15.0; } void _tijk_6o3d_sym_make_iso_f (float *res, const float s) { /* initialize to zero, then set non-zero elements */ unsigned int i; for (i=0; i<28; i++) res[i]=0; res[0]=res[21]=res[27]=s; res[3]=res[5]=res[10]=res[14]=res[23]=res[25]=0.2*s; res[12]=s/15.0; } void _tijk_6o3d_sym_grad_d (double *res, const double *A, const double *v) { double proj, projv[3]; _tijk_6o3d_sym_v_form_d (res, A, v); ELL_3V_SCALE(res,6.0,res); proj=ELL_3V_DOT(res,v); ELL_3V_SCALE(projv,-proj,v); ELL_3V_INCR(res,projv); } void _tijk_6o3d_sym_grad_f (float *res, const float *A, const float *v) { float proj, projv[3]; _tijk_6o3d_sym_v_form_f (res, A, v); ELL_3V_SCALE(res,6.0,res); proj=ELL_3V_DOT(res,v); ELL_3V_SCALE(projv,-proj,v); ELL_3V_INCR(res,projv); } #define _TIJK_6O3D_SYM_HESS(TYPE, SUF) \ void \ _tijk_6o3d_sym_hess_##SUF (TYPE *res, const TYPE *A, const TYPE *v) { \ /* get two orthonormal tangents */ \ TYPE t[2][3], cv[2][3], h[6], der, norm, tmp[6]; \ int r,c; \ ell_3v_perp_##SUF(t[0], v); \ ELL_3V_NORM(t[0],t[0],norm); \ ELL_3V_CROSS(t[1],v,t[0]); \ ELL_3V_NORM(t[1],t[1],norm); \ /* compute Hessian w.r.t. t1/t2 */ \ _tijk_6o3d_sym_m_form_##SUF(h, A, v); \ der=6*_tijk_6o3d_sym_s_form_##SUF(A, v); /* first der in direction v*/ \ _tijk_2o3d_sym_v_form_##SUF(cv[0],h,t[0]); \ _tijk_2o3d_sym_v_form_##SUF(cv[1],h,t[1]); \ h[0]=30*ELL_3V_DOT(cv[0],t[0])-der; \ h[1]=30*ELL_3V_DOT(cv[0],t[1]); \ h[2]=h[1]; \ h[3]=30*ELL_3V_DOT(cv[1],t[1])-der; \ /* now turn this into a symmetric order-2 rank-2 3D tensor */ \ for (r=0; r<2; r++) { \ for (c=0; c<3; c++) { \ tmp[3*r+c]=h[2*r]*t[0][c]+h[2*r+1]*t[1][c]; \ } \ } \ res[0]=t[0][0]*tmp[0]+t[1][0]*tmp[3]; \ res[1]=t[0][0]*tmp[1]+t[1][0]*tmp[4]; \ res[2]=t[0][0]*tmp[2]+t[1][0]*tmp[5]; \ res[3]=t[0][1]*tmp[1]+t[1][1]*tmp[4]; \ res[4]=t[0][1]*tmp[2]+t[1][1]*tmp[5]; \ res[5]=t[0][2]*tmp[2]+t[1][2]*tmp[5]; \ } _TIJK_6O3D_SYM_HESS(double,d) _TIJK_6O3D_SYM_HESS(float,f) TIJK_TYPE_SYM(6o3d_sym, 6, 3, 28) /* 8th order 3D symmetric */ /* (unsymmetric counterpart currently not implemented) */ unsigned int _tijk_8o3d_sym_mult[45]={1, 8, 8, 28, 56, 28, 56, 168, 168, 56, 70, 280, 420, 280, 70, 56, 280, 560, 560, 280, 56, 28, 168, 420, 560, 420, 168, 28, 8, 56, 168, 280, 280, 168, 56, 8, 1, 8, 28, 56, 70, 56, 28, 8, 1}; #define _tijk_8o3d_sym_unsym2uniq NULL #define _tijk_8o3d_sym_uniq2unsym NULL #define _tijk_8o3d_sym_uniq_idx NULL double _tijk_8o3d_sym_tsp_d (const double *A, const double *B) { double retval=0.0; int i; for (i=0; i<45; i++) { retval+=_tijk_8o3d_sym_mult[i]*A[i]*B[i]; } return retval; } float _tijk_8o3d_sym_tsp_f (const float *A, const float *B) { float retval=0.0; int i; for (i=0; i<45; i++) { retval+=_tijk_8o3d_sym_mult[i]*A[i]*B[i]; } return retval; } double _tijk_8o3d_sym_norm_d (const double *A) { return sqrt(_tijk_8o3d_sym_tsp_d(A,A)); } float _tijk_8o3d_sym_norm_f (const float *A) { return sqrt(_tijk_8o3d_sym_tsp_f(A,A)); } #define _TIJK_8O3D_SYM_TRANS(TYPE, SUF) \ void \ _tijk_8o3d_sym_trans_##SUF (TYPE *res, const TYPE *A, const TYPE *M) { \ /* Tijklmnop = Miq Mjr Mks Mlt Mmu Mnv Mow Mpx Tqrstuvwx \ * For efficiency, we transform mode by mode; the intermediate results \ * have incomplete symmetries! */ \ TYPE tmpl[225], tmpr[225]; \ int i; \ { /* mode 8: */ \ int m[108]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6}; \ int idx[324]={0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,15,21,22,16,22,23,17,23,24,18,24,25,19,25,26,20,26,27,21,28,29,22,29,30,23,30,31,24,31,32,25,32,33,26,33,34,27,34,35,28,36,37,29,37,38,30,38,39,31,39,40,32,40,41,33,41,42,34,42,43,35,43,44,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,15,21,22,16,22,23,17,23,24,18,24,25,19,25,26,20,26,27,21,28,29,22,29,30,23,30,31,24,31,32,25,32,33,26,33,34,27,34,35,28,36,37,29,37,38,30,38,39,31,39,40,32,40,41,33,41,42,34,42,43,35,43,44,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,15,21,22,16,22,23,17,23,24,18,24,25,19,25,26,20,26,27,21,28,29,22,29,30,23,30,31,24,31,32,25,32,33,26,33,34,27,34,35,28,36,37,29,37,38,30,38,39,31,39,40,32,40,41,33,41,42,34,42,43,35,43,44}; \ for (i=0; i<108; i++) \ tmpl[i]=M[m[i]]*A[idx[3*i]]+ \ M[m[i]+1]*A[idx[3*i+1]]+ \ M[m[i]+2]*A[idx[3*i+2]]; \ } \ { /* mode 7: */ \ int m[168]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6}; \ int idx[504]={0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,15,21,22,16,22,23,17,23,24,18,24,25,19,25,26,20,26,27,21,28,29,22,29,30,23,30,31,24,31,32,25,32,33,26,33,34,27,34,35,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,15,21,22,16,22,23,17,23,24,18,24,25,19,25,26,20,26,27,21,28,29,22,29,30,23,30,31,24,31,32,25,32,33,26,33,34,27,34,35,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,15,21,22,16,22,23,17,23,24,18,24,25,19,25,26,20,26,27,21,28,29,22,29,30,23,30,31,24,31,32,25,32,33,26,33,34,27,34,35,36,37,38,37,39,40,38,40,41,39,42,43,40,43,44,41,44,45,42,46,47,43,47,48,44,48,49,45,49,50,46,51,52,47,52,53,48,53,54,49,54,55,50,55,56,51,57,58,52,58,59,53,59,60,54,60,61,55,61,62,56,62,63,57,64,65,58,65,66,59,66,67,60,67,68,61,68,69,62,69,70,63,70,71,36,37,38,37,39,40,38,40,41,39,42,43,40,43,44,41,44,45,42,46,47,43,47,48,44,48,49,45,49,50,46,51,52,47,52,53,48,53,54,49,54,55,50,55,56,51,57,58,52,58,59,53,59,60,54,60,61,55,61,62,56,62,63,57,64,65,58,65,66,59,66,67,60,67,68,61,68,69,62,69,70,63,70,71,72,73,74,73,75,76,74,76,77,75,78,79,76,79,80,77,80,81,78,82,83,79,83,84,80,84,85,81,85,86,82,87,88,83,88,89,84,89,90,85,90,91,86,91,92,87,93,94,88,94,95,89,95,96,90,96,97,91,97,98,92,98,99,93,100,101,94,101,102,95,102,103,96,103,104,97,104,105,98,105,106,99,106,107}; \ for (i=0; i<168; i++) \ tmpr[i]=M[m[i]]*tmpl[idx[3*i]]+ \ M[m[i]+1]*tmpl[idx[3*i+1]]+ \ M[m[i]+2]*tmpl[idx[3*i+2]]; \ } \ { /* mode 6: */ \ int m[210]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6}; \ int idx[630]={0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,15,21,22,16,22,23,17,23,24,18,24,25,19,25,26,20,26,27,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,15,21,22,16,22,23,17,23,24,18,24,25,19,25,26,20,26,27,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,15,21,22,16,22,23,17,23,24,18,24,25,19,25,26,20,26,27,28,29,30,29,31,32,30,32,33,31,34,35,32,35,36,33,36,37,34,38,39,35,39,40,36,40,41,37,41,42,38,43,44,39,44,45,40,45,46,41,46,47,42,47,48,43,49,50,44,50,51,45,51,52,46,52,53,47,53,54,48,54,55,28,29,30,29,31,32,30,32,33,31,34,35,32,35,36,33,36,37,34,38,39,35,39,40,36,40,41,37,41,42,38,43,44,39,44,45,40,45,46,41,46,47,42,47,48,43,49,50,44,50,51,45,51,52,46,52,53,47,53,54,48,54,55,56,57,58,57,59,60,58,60,61,59,62,63,60,63,64,61,64,65,62,66,67,63,67,68,64,68,69,65,69,70,66,71,72,67,72,73,68,73,74,69,74,75,70,75,76,71,77,78,72,78,79,73,79,80,74,80,81,75,81,82,76,82,83,84,85,86,85,87,88,86,88,89,87,90,91,88,91,92,89,92,93,90,94,95,91,95,96,92,96,97,93,97,98,94,99,100,95,100,101,96,101,102,97,102,103,98,103,104,99,105,106,100,106,107,101,107,108,102,108,109,103,109,110,104,110,111,84,85,86,85,87,88,86,88,89,87,90,91,88,91,92,89,92,93,90,94,95,91,95,96,92,96,97,93,97,98,94,99,100,95,100,101,96,101,102,97,102,103,98,103,104,99,105,106,100,106,107,101,107,108,102,108,109,103,109,110,104,110,111,112,113,114,113,115,116,114,116,117,115,118,119,116,119,120,117,120,121,118,122,123,119,123,124,120,124,125,121,125,126,122,127,128,123,128,129,124,129,130,125,130,131,126,131,132,127,133,134,128,134,135,129,135,136,130,136,137,131,137,138,132,138,139,140,141,142,141,143,144,142,144,145,143,146,147,144,147,148,145,148,149,146,150,151,147,151,152,148,152,153,149,153,154,150,155,156,151,156,157,152,157,158,153,158,159,154,159,160,155,161,162,156,162,163,157,163,164,158,164,165,159,165,166,160,166,167}; \ for (i=0; i<210; i++) \ tmpl[i]=M[m[i]]*tmpr[idx[3*i]]+ \ M[m[i]+1]*tmpr[idx[3*i+1]]+ \ M[m[i]+2]*tmpr[idx[3*i+2]]; \ } \ { /* mode 5: */ \ int m[225]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6}; \ int idx[675]={0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,10,15,16,11,16,17,12,17,18,13,18,19,14,19,20,21,22,23,22,24,25,23,25,26,24,27,28,25,28,29,26,29,30,27,31,32,28,32,33,29,33,34,30,34,35,31,36,37,32,37,38,33,38,39,34,39,40,35,40,41,21,22,23,22,24,25,23,25,26,24,27,28,25,28,29,26,29,30,27,31,32,28,32,33,29,33,34,30,34,35,31,36,37,32,37,38,33,38,39,34,39,40,35,40,41,42,43,44,43,45,46,44,46,47,45,48,49,46,49,50,47,50,51,48,52,53,49,53,54,50,54,55,51,55,56,52,57,58,53,58,59,54,59,60,55,60,61,56,61,62,63,64,65,64,66,67,65,67,68,66,69,70,67,70,71,68,71,72,69,73,74,70,74,75,71,75,76,72,76,77,73,78,79,74,79,80,75,80,81,76,81,82,77,82,83,63,64,65,64,66,67,65,67,68,66,69,70,67,70,71,68,71,72,69,73,74,70,74,75,71,75,76,72,76,77,73,78,79,74,79,80,75,80,81,76,81,82,77,82,83,84,85,86,85,87,88,86,88,89,87,90,91,88,91,92,89,92,93,90,94,95,91,95,96,92,96,97,93,97,98,94,99,100,95,100,101,96,101,102,97,102,103,98,103,104,105,106,107,106,108,109,107,109,110,108,111,112,109,112,113,110,113,114,111,115,116,112,116,117,113,117,118,114,118,119,115,120,121,116,121,122,117,122,123,118,123,124,119,124,125,126,127,128,127,129,130,128,130,131,129,132,133,130,133,134,131,134,135,132,136,137,133,137,138,134,138,139,135,139,140,136,141,142,137,142,143,138,143,144,139,144,145,140,145,146,126,127,128,127,129,130,128,130,131,129,132,133,130,133,134,131,134,135,132,136,137,133,137,138,134,138,139,135,139,140,136,141,142,137,142,143,138,143,144,139,144,145,140,145,146,147,148,149,148,150,151,149,151,152,150,153,154,151,154,155,152,155,156,153,157,158,154,158,159,155,159,160,156,160,161,157,162,163,158,163,164,159,164,165,160,165,166,161,166,167,168,169,170,169,171,172,170,172,173,171,174,175,172,175,176,173,176,177,174,178,179,175,179,180,176,180,181,177,181,182,178,183,184,179,184,185,180,185,186,181,186,187,182,187,188,189,190,191,190,192,193,191,193,194,192,195,196,193,196,197,194,197,198,195,199,200,196,200,201,197,201,202,198,202,203,199,204,205,200,205,206,201,206,207,202,207,208,203,208,209}; \ for (i=0; i<225; i++) \ tmpr[i]=M[m[i]]*tmpl[idx[3*i]]+ \ M[m[i]+1]*tmpl[idx[3*i+1]]+ \ M[m[i]+2]*tmpl[idx[3*i+2]]; \ } \ { /* mode 4: */ \ int m[210]={0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6}; \ int idx[630]={0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,6,10,11,7,11,12,8,12,13,9,13,14,15,16,17,16,18,19,17,19,20,18,21,22,19,22,23,20,23,24,21,25,26,22,26,27,23,27,28,24,28,29,15,16,17,16,18,19,17,19,20,18,21,22,19,22,23,20,23,24,21,25,26,22,26,27,23,27,28,24,28,29,30,31,32,31,33,34,32,34,35,33,36,37,34,37,38,35,38,39,36,40,41,37,41,42,38,42,43,39,43,44,45,46,47,46,48,49,47,49,50,48,51,52,49,52,53,50,53,54,51,55,56,52,56,57,53,57,58,54,58,59,45,46,47,46,48,49,47,49,50,48,51,52,49,52,53,50,53,54,51,55,56,52,56,57,53,57,58,54,58,59,60,61,62,61,63,64,62,64,65,63,66,67,64,67,68,65,68,69,66,70,71,67,71,72,68,72,73,69,73,74,75,76,77,76,78,79,77,79,80,78,81,82,79,82,83,80,83,84,81,85,86,82,86,87,83,87,88,84,88,89,90,91,92,91,93,94,92,94,95,93,96,97,94,97,98,95,98,99,96,100,101,97,101,102,98,102,103,99,103,104,90,91,92,91,93,94,92,94,95,93,96,97,94,97,98,95,98,99,96,100,101,97,101,102,98,102,103,99,103,104,105,106,107,106,108,109,107,109,110,108,111,112,109,112,113,110,113,114,111,115,116,112,116,117,113,117,118,114,118,119,120,121,122,121,123,124,122,124,125,123,126,127,124,127,128,125,128,129,126,130,131,127,131,132,128,132,133,129,133,134,135,136,137,136,138,139,137,139,140,138,141,142,139,142,143,140,143,144,141,145,146,142,146,147,143,147,148,144,148,149,150,151,152,151,153,154,152,154,155,153,156,157,154,157,158,155,158,159,156,160,161,157,161,162,158,162,163,159,163,164,150,151,152,151,153,154,152,154,155,153,156,157,154,157,158,155,158,159,156,160,161,157,161,162,158,162,163,159,163,164,165,166,167,166,168,169,167,169,170,168,171,172,169,172,173,170,173,174,171,175,176,172,176,177,173,177,178,174,178,179,180,181,182,181,183,184,182,184,185,183,186,187,184,187,188,185,188,189,186,190,191,187,191,192,188,192,193,189,193,194,195,196,197,196,198,199,197,199,200,198,201,202,199,202,203,200,203,204,201,205,206,202,206,207,203,207,208,204,208,209,210,211,212,211,213,214,212,214,215,213,216,217,214,217,218,215,218,219,216,220,221,217,221,222,218,222,223,219,223,224}; \ for (i=0; i<210; i++) \ tmpl[i]=M[m[i]]*tmpr[idx[3*i]]+ \ M[m[i]+1]*tmpr[idx[3*i+1]]+ \ M[m[i]+2]*tmpr[idx[3*i+2]]; \ } \ { /* mode 3: */ \ int m[168]={0,0,0,0,0,0,3,3,3,3,3,3,6,6,6,6,6,6,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6}; \ int idx[504]={0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,0,1,2,1,3,4,2,4,5,3,6,7,4,7,8,5,8,9,10,11,12,11,13,14,12,14,15,13,16,17,14,17,18,15,18,19,10,11,12,11,13,14,12,14,15,13,16,17,14,17,18,15,18,19,20,21,22,21,23,24,22,24,25,23,26,27,24,27,28,25,28,29,30,31,32,31,33,34,32,34,35,33,36,37,34,37,38,35,38,39,30,31,32,31,33,34,32,34,35,33,36,37,34,37,38,35,38,39,40,41,42,41,43,44,42,44,45,43,46,47,44,47,48,45,48,49,50,51,52,51,53,54,52,54,55,53,56,57,54,57,58,55,58,59,60,61,62,61,63,64,62,64,65,63,66,67,64,67,68,65,68,69,60,61,62,61,63,64,62,64,65,63,66,67,64,67,68,65,68,69,70,71,72,71,73,74,72,74,75,73,76,77,74,77,78,75,78,79,80,81,82,81,83,84,82,84,85,83,86,87,84,87,88,85,88,89,90,91,92,91,93,94,92,94,95,93,96,97,94,97,98,95,98,99,100,101,102,101,103,104,102,104,105,103,106,107,104,107,108,105,108,109,100,101,102,101,103,104,102,104,105,103,106,107,104,107,108,105,108,109,110,111,112,111,113,114,112,114,115,113,116,117,114,117,118,115,118,119,120,121,122,121,123,124,122,124,125,123,126,127,124,127,128,125,128,129,130,131,132,131,133,134,132,134,135,133,136,137,134,137,138,135,138,139,140,141,142,141,143,144,142,144,145,143,146,147,144,147,148,145,148,149,150,151,152,151,153,154,152,154,155,153,156,157,154,157,158,155,158,159,150,151,152,151,153,154,152,154,155,153,156,157,154,157,158,155,158,159,160,161,162,161,163,164,162,164,165,163,166,167,164,167,168,165,168,169,170,171,172,171,173,174,172,174,175,173,176,177,174,177,178,175,178,179,180,181,182,181,183,184,182,184,185,183,186,187,184,187,188,185,188,189,190,191,192,191,193,194,192,194,195,193,196,197,194,197,198,195,198,199,200,201,202,201,203,204,202,204,205,203,206,207,204,207,208,205,208,209}; \ for (i=0; i<168; i++) \ tmpr[i]=M[m[i]]*tmpl[idx[3*i]]+ \ M[m[i]+1]*tmpl[idx[3*i+1]]+ \ M[m[i]+2]*tmpl[idx[3*i+2]]; \ } \ { /* mode 2: */ \ int m[108]={0,0,0,3,3,3,6,6,6,3,3,3,6,6,6,6,6,6,3,3,3,6,6,6,6,6,6,6,6,6,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6}; \ int idx[324]={0,1,2,1,3,4,2,4,5,0,1,2,1,3,4,2,4,5,0,1,2,1,3,4,2,4,5,6,7,8,7,9,10,8,10,11,6,7,8,7,9,10,8,10,11,12,13,14,13,15,16,14,16,17,18,19,20,19,21,22,20,22,23,18,19,20,19,21,22,20,22,23,24,25,26,25,27,28,26,28,29,30,31,32,31,33,34,32,34,35,36,37,38,37,39,40,38,40,41,36,37,38,37,39,40,38,40,41,42,43,44,43,45,46,44,46,47,48,49,50,49,51,52,50,52,53,54,55,56,55,57,58,56,58,59,60,61,62,61,63,64,62,64,65,60,61,62,61,63,64,62,64,65,66,67,68,67,69,70,68,70,71,72,73,74,73,75,76,74,76,77,78,79,80,79,81,82,80,82,83,84,85,86,85,87,88,86,88,89,90,91,92,91,93,94,92,94,95,90,91,92,91,93,94,92,94,95,96,97,98,97,99,100,98,100,101,102,103,104,103,105,106,104,106,107,108,109,110,109,111,112,110,112,113,114,115,116,115,117,118,116,118,119,120,121,122,121,123,124,122,124,125,126,127,128,127,129,130,128,130,131,126,127,128,127,129,130,128,130,131,132,133,134,133,135,136,134,136,137,138,139,140,139,141,142,140,142,143,144,145,146,145,147,148,146,148,149,150,151,152,151,153,154,152,154,155,156,157,158,157,159,160,158,160,161,162,163,164,163,165,166,164,166,167}; \ for (i=0; i<108; i++) \ tmpl[i]=M[m[i]]*tmpr[idx[3*i]]+ \ M[m[i]+1]*tmpr[idx[3*i+1]]+ \ M[m[i]+2]*tmpr[idx[3*i+2]]; \ } \ { /* mode 1: */ \ int m[45]={0,3,6,3,6,6,3,6,6,6,3,6,6,6,6,3,6,6,6,6,6,3,6,6,6,6,6,6,3,6,6,6,6,6,6,6,3,6,6,6,6,6,6,6,6}; \ int idx[135]={0,1,2,0,1,2,0,1,2,3,4,5,3,4,5,6,7,8,9,10,11,9,10,11,12,13,14,15,16,17,18,19,20,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107}; \ for (i=0; i<45; i++) \ res[i]=M[m[i]]*tmpl[idx[3*i]]+ \ M[m[i]+1]*tmpl[idx[3*i+1]]+ \ M[m[i]+2]*tmpl[idx[3*i+2]]; \ } \ } _TIJK_8O3D_SYM_TRANS(double, d) _TIJK_8O3D_SYM_TRANS(float, f) /* many routines left to implement - trying to call them will cause a segfault! */ #define _tijk_8o3d_sym_convert_f NULL #define _tijk_8o3d_sym_convert_d NULL #define _tijk_8o3d_sym_approx_f NULL #define _tijk_8o3d_sym_approx_d NULL double _tijk_8o3d_sym_s_form_d (const double *A, const double *v) { double v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; return A[0]*v00*v00*v00*v00+ A[36]*v11*v11*v11*v11+ A[44]*v22*v22*v22*v22+ 8*(A[1]*v00*v00*v00*v01+ A[2]*v00*v00*v00*v02+ A[28]*v01*v11*v11*v11+ A[35]*v02*v22*v22*v22+ A[37]*v11*v11*v11*v12+ A[43]*v12*v22*v22*v22)+ 28*(A[3]*v00*v00*v00*v11+ A[5]*v00*v00*v00*v22+ A[21]*v00*v11*v11*v11+ A[27]*v00*v22*v22*v22+ A[38]*v11*v11*v11*v22+ A[42]*v11*v22*v22*v22)+ 56*(A[4]*v00*v00*v00*v12+ A[6]*v00*v00*v01*v11+ A[9]*v00*v00*v02*v22+ A[15]*v00*v01*v11*v11+ A[20]*v00*v02*v22*v22+ A[29]*v01*v11*v11*v12+ A[34]*v01*v22*v22*v22+ A[39]*v11*v11*v12*v22+ A[41]*v11*v12*v22*v22)+ 70*(A[10]*v00*v00*v11*v11+ A[14]*v00*v00*v22*v22+ A[40]*v11*v11*v22*v22)+ 168*(A[7]*v00*v00*v01*v12+ A[8]*v00*v00*v01*v22+ A[22]*v00*v11*v11*v12+ A[26]*v00*v12*v22*v22+ A[30]*v01*v11*v11*v22+ A[33]*v01*v12*v22*v22)+ 280*(A[11]*v00*v00*v11*v12+ A[13]*v00*v00*v12*v22+ A[16]*v00*v01*v11*v12+ A[19]*v00*v01*v22*v22+ A[31]*v01*v11*v12*v22+ A[32]*v01*v11*v22*v22)+ 420*(A[12]*v00*v00*v11*v22+ A[23]*v00*v11*v11*v22+ A[25]*v00*v11*v22*v22)+ 560*(A[17]*v00*v01*v11*v22+ A[18]*v00*v01*v12*v22+ A[24]*v00*v11*v12*v22); } float _tijk_8o3d_sym_s_form_f (const float *A, const float *v) { float v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; return A[0]*v00*v00*v00*v00+ A[36]*v11*v11*v11*v11+ A[44]*v22*v22*v22*v22+ 8*(A[1]*v00*v00*v00*v01+ A[2]*v00*v00*v00*v02+ A[28]*v01*v11*v11*v11+ A[35]*v02*v22*v22*v22+ A[37]*v11*v11*v11*v12+ A[43]*v12*v22*v22*v22)+ 28*(A[3]*v00*v00*v00*v11+ A[5]*v00*v00*v00*v22+ A[21]*v00*v11*v11*v11+ A[27]*v00*v22*v22*v22+ A[38]*v11*v11*v11*v22+ A[42]*v11*v22*v22*v22)+ 56*(A[4]*v00*v00*v00*v12+ A[6]*v00*v00*v01*v11+ A[9]*v00*v00*v02*v22+ A[15]*v00*v01*v11*v11+ A[20]*v00*v02*v22*v22+ A[29]*v01*v11*v11*v12+ A[34]*v01*v22*v22*v22+ A[39]*v11*v11*v12*v22+ A[41]*v11*v12*v22*v22)+ 70*(A[10]*v00*v00*v11*v11+ A[14]*v00*v00*v22*v22+ A[40]*v11*v11*v22*v22)+ 168*(A[7]*v00*v00*v01*v12+ A[8]*v00*v00*v01*v22+ A[22]*v00*v11*v11*v12+ A[26]*v00*v12*v22*v22+ A[30]*v01*v11*v11*v22+ A[33]*v01*v12*v22*v22)+ 280*(A[11]*v00*v00*v11*v12+ A[13]*v00*v00*v12*v22+ A[16]*v00*v01*v11*v12+ A[19]*v00*v01*v22*v22+ A[31]*v01*v11*v12*v22+ A[32]*v01*v11*v22*v22)+ 420*(A[12]*v00*v00*v11*v22+ A[23]*v00*v11*v11*v22+ A[25]*v00*v11*v22*v22)+ 560*(A[17]*v00*v01*v11*v22+ A[18]*v00*v01*v12*v22+ A[24]*v00*v11*v12*v22); } double _tijk_8o3d_sym_mean_d (const double *A) { return (A[0]+A[36]+A[44]+ 4*(A[3]+A[5]+A[21]+A[27]+A[38]+A[42])+ 6*(A[10]+A[14]+A[40])+ 12*(A[12]+A[23]+A[25]))/9.0; } float _tijk_8o3d_sym_mean_f (const float *A) { return (A[0]+A[36]+A[44]+ 4*(A[3]+A[5]+A[21]+A[27]+A[38]+A[42])+ 6*(A[10]+A[14]+A[40])+ 12*(A[12]+A[23]+A[25]))/9.0; } #define _tijk_8o3d_sym_var_f NULL #define _tijk_8o3d_sym_var_d NULL #define _TIJK_8O3D_SYM_V_FORM(TYPE, SUF) \ void \ _tijk_8o3d_sym_v_form_##SUF (TYPE *res, const TYPE *A, const TYPE *v) { \ TYPE v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], \ v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; \ TYPE v00000=v00*v00*v[0], v00001=v00*v00*v[1], v00002=v00*v00*v[2], \ v00011=v00*v01*v[1], v00012=v00*v01*v[2], v00022=v00*v02*v[2], \ v00111=v00*v11*v[1], v00112=v00*v11*v[2], v00122=v00*v12*v[2], \ v00222=v00*v22*v[2], v01111=v01*v11*v[1], v01112=v01*v11*v[2], \ v01122=v01*v12*v[2], v01222=v01*v22*v[2], v02222=v02*v22*v[2], \ v11111=v11*v11*v[1], v11112=v11*v11*v[2], v11122=v11*v12*v[2], \ v11222=v11*v22*v[2], v12222=v12*v22*v[2], v22222=v22*v22*v[2]; \ res[0] = A[0]*v00*v00000+ \ 7*A[1]*v00*v00001+ \ 7*A[2]*v00*v00002+ \ 21*A[3]*v00*v00011+ \ 42*A[4]*v00*v00012+ \ 21*A[5]*v00*v00022+ \ 35*A[6]*v00*v00111+ \ 105*A[7]*v00*v00112+ \ 105*A[8]*v00*v00122+ \ 35*A[9]*v00*v00222+ \ 35*A[10]*v00*v01111+ \ 140*A[11]*v00*v01112+ \ 210*A[12]*v00*v01122+ \ 140*A[13]*v00*v01222+ \ 35*A[14]*v00*v02222+ \ 21*A[15]*v00*v11111+ \ 105*A[16]*v00*v11112+ \ 210*A[17]*v00*v11122+ \ 210*A[18]*v00*v11222+ \ 105*A[19]*v00*v12222+ \ 21*A[20]*v00*v22222+ \ 7*A[21]*v01*v11111+ \ 42*A[22]*v01*v11112+ \ 105*A[23]*v01*v11122+ \ 140*A[24]*v01*v11222+ \ 105*A[25]*v01*v12222+ \ 42*A[26]*v01*v22222+ \ 7*A[27]*v02*v22222+ \ A[28]*v11*v11111+ \ 7*A[29]*v11*v11112+ \ 21*A[30]*v11*v11122+ \ 35*A[31]*v11*v11222+ \ 35*A[32]*v11*v12222+ \ 21*A[33]*v11*v22222+ \ 7*A[34]*v12*v22222+ \ A[35]*v22*v22222; \ res[1] = A[1]*v00*v00000+ \ 7*A[3]*v00*v00001+ \ 7*A[4]*v00*v00002+ \ 21*A[6]*v00*v00011+ \ 42*A[7]*v00*v00012+ \ 21*A[8]*v00*v00022+ \ 35*A[10]*v00*v00111+ \ 105*A[11]*v00*v00112+ \ 105*A[12]*v00*v00122+ \ 35*A[13]*v00*v00222+ \ 35*A[15]*v00*v01111+ \ 140*A[16]*v00*v01112+ \ 210*A[17]*v00*v01122+ \ 140*A[18]*v00*v01222+ \ 35*A[19]*v00*v02222+ \ 21*A[21]*v00*v11111+ \ 105*A[22]*v00*v11112+ \ 210*A[23]*v00*v11122+ \ 210*A[24]*v00*v11222+ \ 105*A[25]*v00*v12222+ \ 21*A[26]*v00*v22222+ \ 7*A[28]*v01*v11111+ \ 42*A[29]*v01*v11112+ \ 105*A[30]*v01*v11122+ \ 140*A[31]*v01*v11222+ \ 105*A[32]*v01*v12222+ \ 42*A[33]*v01*v22222+ \ 7*A[34]*v02*v22222+ \ A[36]*v11*v11111+ \ 7*A[37]*v11*v11112+ \ 21*A[38]*v11*v11122+ \ 35*A[39]*v11*v11222+ \ 35*A[40]*v11*v12222+ \ 21*A[41]*v11*v22222+ \ 7*A[42]*v12*v22222+ \ A[43]*v22*v22222; \ res[2] = A[2]*v00*v00000+ \ 7*A[4]*v00*v00001+ \ 7*A[5]*v00*v00002+ \ 21*A[7]*v00*v00011+ \ 42*A[8]*v00*v00012+ \ 21*A[9]*v00*v00022+ \ 35*A[11]*v00*v00111+ \ 105*A[12]*v00*v00112+ \ 105*A[13]*v00*v00122+ \ 35*A[14]*v00*v00222+ \ 35*A[16]*v00*v01111+ \ 140*A[17]*v00*v01112+ \ 210*A[18]*v00*v01122+ \ 140*A[19]*v00*v01222+ \ 35*A[20]*v00*v02222+ \ 21*A[22]*v00*v11111+ \ 105*A[23]*v00*v11112+ \ 210*A[24]*v00*v11122+ \ 210*A[25]*v00*v11222+ \ 105*A[26]*v00*v12222+ \ 21*A[27]*v00*v22222+ \ 7*A[29]*v01*v11111+ \ 42*A[30]*v01*v11112+ \ 105*A[31]*v01*v11122+ \ 140*A[32]*v01*v11222+ \ 105*A[33]*v01*v12222+ \ 42*A[34]*v01*v22222+ \ 7*A[35]*v02*v22222+ \ A[37]*v11*v11111+ \ 7*A[38]*v11*v11112+ \ 21*A[39]*v11*v11122+ \ 35*A[40]*v11*v11222+ \ 35*A[41]*v11*v12222+ \ 21*A[42]*v11*v22222+ \ 7*A[43]*v12*v22222+ \ A[44]*v22*v22222; \ } _TIJK_8O3D_SYM_V_FORM(double, d) _TIJK_8O3D_SYM_V_FORM(float, f) #define _TIJK_8O3D_SYM_M_FORM(TYPE, SUF) \ void \ _tijk_8o3d_sym_m_form_##SUF (TYPE *res, const TYPE *A, const TYPE *v) { \ TYPE v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], \ v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; \ TYPE v0000=v00*v00, v0001=v00*v01, v0002=v00*v02, v0011=v00*v11, \ v0012=v00*v12, v0022=v00*v22, v0111=v01*v11, v0112=v01*v12, \ v0122=v01*v22, v0222=v02*v22, v1111=v11*v11, v1112=v11*v12, \ v1122=v11*v22, v1222=v12*v22, v2222=v22*v22; \ res[0] = A[0]*v00*v0000+ \ 6*A[1]*v00*v0001+ \ 6*A[2]*v00*v0002+ \ 15*A[3]*v00*v0011+ \ 30*A[4]*v00*v0012+ \ 15*A[5]*v00*v0022+ \ 20*A[6]*v00*v0111+ \ 60*A[7]*v00*v0112+ \ 60*A[8]*v00*v0122+ \ 20*A[9]*v00*v0222+ \ 15*A[10]*v00*v1111+ \ 60*A[11]*v00*v1112+ \ 90*A[12]*v00*v1122+ \ 60*A[13]*v00*v1222+ \ 15*A[14]*v00*v2222+ \ 6*A[15]*v01*v1111+ \ 30*A[16]*v01*v1112+ \ 60*A[17]*v01*v1122+ \ 60*A[18]*v01*v1222+ \ 30*A[19]*v01*v2222+ \ 6*A[20]*v02*v2222+ \ A[21]*v11*v1111+ \ 6*A[22]*v11*v1112+ \ 15*A[23]*v11*v1122+ \ 20*A[24]*v11*v1222+ \ 15*A[25]*v11*v2222+ \ 6*A[26]*v12*v2222+ \ A[27]*v22*v2222; \ res[1] = A[1]*v00*v0000+ \ 6*A[3]*v00*v0001+ \ 6*A[4]*v00*v0002+ \ 15*A[6]*v00*v0011+ \ 30*A[7]*v00*v0012+ \ 15*A[8]*v00*v0022+ \ 20*A[10]*v00*v0111+ \ 60*A[11]*v00*v0112+ \ 60*A[12]*v00*v0122+ \ 20*A[13]*v00*v0222+ \ 15*A[15]*v00*v1111+ \ 60*A[16]*v00*v1112+ \ 90*A[17]*v00*v1122+ \ 60*A[18]*v00*v1222+ \ 15*A[19]*v00*v2222+ \ 6*A[21]*v01*v1111+ \ 30*A[22]*v01*v1112+ \ 60*A[23]*v01*v1122+ \ 60*A[24]*v01*v1222+ \ 30*A[25]*v01*v2222+ \ 6*A[26]*v02*v2222+ \ A[28]*v11*v1111+ \ 6*A[29]*v11*v1112+ \ 15*A[30]*v11*v1122+ \ 20*A[31]*v11*v1222+ \ 15*A[32]*v11*v2222+ \ 6*A[33]*v12*v2222+ \ A[34]*v22*v2222; \ res[2] = A[2]*v00*v0000+ \ 6*A[4]*v00*v0001+ \ 6*A[5]*v00*v0002+ \ 15*A[7]*v00*v0011+ \ 30*A[8]*v00*v0012+ \ 15*A[9]*v00*v0022+ \ 20*A[11]*v00*v0111+ \ 60*A[12]*v00*v0112+ \ 60*A[13]*v00*v0122+ \ 20*A[14]*v00*v0222+ \ 15*A[16]*v00*v1111+ \ 60*A[17]*v00*v1112+ \ 90*A[18]*v00*v1122+ \ 60*A[19]*v00*v1222+ \ 15*A[20]*v00*v2222+ \ 6*A[22]*v01*v1111+ \ 30*A[23]*v01*v1112+ \ 60*A[24]*v01*v1122+ \ 60*A[25]*v01*v1222+ \ 30*A[26]*v01*v2222+ \ 6*A[27]*v02*v2222+ \ A[29]*v11*v1111+ \ 6*A[30]*v11*v1112+ \ 15*A[31]*v11*v1122+ \ 20*A[32]*v11*v1222+ \ 15*A[33]*v11*v2222+ \ 6*A[34]*v12*v2222+ \ A[35]*v22*v2222; \ res[3] = A[3]*v00*v0000+ \ 6*A[6]*v00*v0001+ \ 6*A[7]*v00*v0002+ \ 15*A[10]*v00*v0011+ \ 30*A[11]*v00*v0012+ \ 15*A[12]*v00*v0022+ \ 20*A[15]*v00*v0111+ \ 60*A[16]*v00*v0112+ \ 60*A[17]*v00*v0122+ \ 20*A[18]*v00*v0222+ \ 15*A[21]*v00*v1111+ \ 60*A[22]*v00*v1112+ \ 90*A[23]*v00*v1122+ \ 60*A[24]*v00*v1222+ \ 15*A[25]*v00*v2222+ \ 6*A[28]*v01*v1111+ \ 30*A[29]*v01*v1112+ \ 60*A[30]*v01*v1122+ \ 60*A[31]*v01*v1222+ \ 30*A[32]*v01*v2222+ \ 6*A[33]*v02*v2222+ \ A[36]*v11*v1111+ \ 6*A[37]*v11*v1112+ \ 15*A[38]*v11*v1122+ \ 20*A[39]*v11*v1222+ \ 15*A[40]*v11*v2222+ \ 6*A[41]*v12*v2222+ \ A[42]*v22*v2222; \ res[4] = A[4]*v00*v0000+ \ 6*A[7]*v00*v0001+ \ 6*A[8]*v00*v0002+ \ 15*A[11]*v00*v0011+ \ 30*A[12]*v00*v0012+ \ 15*A[13]*v00*v0022+ \ 20*A[16]*v00*v0111+ \ 60*A[17]*v00*v0112+ \ 60*A[18]*v00*v0122+ \ 20*A[19]*v00*v0222+ \ 15*A[22]*v00*v1111+ \ 60*A[23]*v00*v1112+ \ 90*A[24]*v00*v1122+ \ 60*A[25]*v00*v1222+ \ 15*A[26]*v00*v2222+ \ 6*A[29]*v01*v1111+ \ 30*A[30]*v01*v1112+ \ 60*A[31]*v01*v1122+ \ 60*A[32]*v01*v1222+ \ 30*A[33]*v01*v2222+ \ 6*A[34]*v02*v2222+ \ A[37]*v11*v1111+ \ 6*A[38]*v11*v1112+ \ 15*A[39]*v11*v1122+ \ 20*A[40]*v11*v1222+ \ 15*A[41]*v11*v2222+ \ 6*A[42]*v12*v2222+ \ A[43]*v22*v2222; \ res[5] = A[5]*v00*v0000+ \ 6*A[8]*v00*v0001+ \ 6*A[9]*v00*v0002+ \ 15*A[12]*v00*v0011+ \ 30*A[13]*v00*v0012+ \ 15*A[14]*v00*v0022+ \ 20*A[17]*v00*v0111+ \ 60*A[18]*v00*v0112+ \ 60*A[19]*v00*v0122+ \ 20*A[20]*v00*v0222+ \ 15*A[23]*v00*v1111+ \ 60*A[24]*v00*v1112+ \ 90*A[25]*v00*v1122+ \ 60*A[26]*v00*v1222+ \ 15*A[27]*v00*v2222+ \ 6*A[30]*v01*v1111+ \ 30*A[31]*v01*v1112+ \ 60*A[32]*v01*v1122+ \ 60*A[33]*v01*v1222+ \ 30*A[34]*v01*v2222+ \ 6*A[35]*v02*v2222+ \ A[38]*v11*v1111+ \ 6*A[39]*v11*v1112+ \ 15*A[40]*v11*v1122+ \ 20*A[41]*v11*v1222+ \ 15*A[42]*v11*v2222+ \ 6*A[43]*v12*v2222+ \ A[44]*v22*v2222; \ } _TIJK_8O3D_SYM_M_FORM(double, d) _TIJK_8O3D_SYM_M_FORM(float, f) #define _TIJK_8O3D_SYM_MAKE_RANK1(TYPE, SUF) \ void \ _tijk_8o3d_sym_make_rank1_##SUF (TYPE *res, const TYPE s, const TYPE *v) { \ TYPE v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], \ v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; \ res[0]=s*v00*v00*v00*v00; res[1]=s*v00*v00*v00*v01; res[2]=s*v00*v00*v00*v02; \ res[3]=s*v00*v00*v00*v11; res[4]=s*v00*v00*v00*v12; res[5]=s*v00*v00*v00*v22; \ res[6]=s*v00*v00*v01*v11; res[7]=s*v00*v00*v01*v12; res[8]=s*v00*v00*v01*v22; \ res[9]=s*v00*v00*v02*v22; res[10]=s*v00*v00*v11*v11; res[11]=s*v00*v00*v11*v12; \ res[12]=s*v00*v00*v11*v22; res[13]=s*v00*v00*v12*v22; res[14]=s*v00*v00*v22*v22; \ res[15]=s*v00*v01*v11*v11; res[16]=s*v00*v01*v11*v12; res[17]=s*v00*v01*v11*v22; \ res[18]=s*v00*v01*v12*v22; res[19]=s*v00*v01*v22*v22; res[20]=s*v00*v02*v22*v22; \ res[21]=s*v00*v11*v11*v11; res[22]=s*v00*v11*v11*v12; res[23]=s*v00*v11*v11*v22; \ res[24]=s*v00*v11*v12*v22; res[25]=s*v00*v11*v22*v22; res[26]=s*v00*v12*v22*v22; \ res[27]=s*v00*v22*v22*v22; res[28]=s*v01*v11*v11*v11; res[29]=s*v01*v11*v11*v12; \ res[30]=s*v01*v11*v11*v22; res[31]=s*v01*v11*v12*v22; res[32]=s*v01*v11*v22*v22; \ res[33]=s*v01*v12*v22*v22; res[34]=s*v01*v22*v22*v22; res[35]=s*v02*v22*v22*v22; \ res[36]=s*v11*v11*v11*v11; res[37]=s*v11*v11*v11*v12; res[38]=s*v11*v11*v11*v22; \ res[39]=s*v11*v11*v12*v22; res[40]=s*v11*v11*v22*v22; res[41]=s*v11*v12*v22*v22; \ res[42]=s*v11*v22*v22*v22; res[43]=s*v12*v22*v22*v22; res[44]=s*v22*v22*v22*v22; \ } _TIJK_8O3D_SYM_MAKE_RANK1(double, d) _TIJK_8O3D_SYM_MAKE_RANK1(float, f) void _tijk_8o3d_sym_make_iso_d (double *res, const double s) { /* initialize to zero, then set non-zero elements */ unsigned int i; for (i=0; i<45; i++) res[i]=0; res[0]=res[36]=res[44]=s; res[3]=res[5]=res[21]=res[27]=res[38]=res[42]=s/7.0; res[10]=res[14]=res[40]=0.085714285714285396*s; res[12]=res[23]=res[25]=0.028571428571428508*s; } void _tijk_8o3d_sym_make_iso_f (float *res, const float s) { /* initialize to zero, then set non-zero elements */ unsigned int i; for (i=0; i<45; i++) res[i]=0; res[0]=res[36]=res[44]=s; res[3]=res[5]=res[21]=res[27]=res[38]=res[42]=s/7.0; res[10]=res[14]=res[40]=0.085714285714285396*s; res[12]=res[23]=res[25]=0.028571428571428508*s; } void _tijk_8o3d_sym_grad_d (double *res, const double *A, const double *v) { double proj, projv[3]; _tijk_8o3d_sym_v_form_d (res, A, v); ELL_3V_SCALE(res,8.0,res); proj=ELL_3V_DOT(res,v); ELL_3V_SCALE(projv,-proj,v); ELL_3V_INCR(res,projv); } void _tijk_8o3d_sym_grad_f (float *res, const float *A, const float *v) { float proj, projv[3]; _tijk_8o3d_sym_v_form_f (res, A, v); ELL_3V_SCALE(res,8.0,res); proj=ELL_3V_DOT(res,v); ELL_3V_SCALE(projv,-proj,v); ELL_3V_INCR(res,projv); } #define _TIJK_8O3D_SYM_HESS(TYPE, SUF) \ void \ _tijk_8o3d_sym_hess_##SUF (TYPE *res, const TYPE *A, const TYPE *v) { \ /* get two orthonormal tangents */ \ TYPE t[2][3], cv[2][3], h[6], der, norm, tmp[6]; \ int r,c; \ ell_3v_perp_##SUF(t[0], v); \ ELL_3V_NORM(t[0],t[0],norm); \ ELL_3V_CROSS(t[1],v,t[0]); \ ELL_3V_NORM(t[1],t[1],norm); \ /* compute Hessian w.r.t. t1/t2 */ \ _tijk_8o3d_sym_m_form_##SUF(h, A, v); \ der=8*_tijk_8o3d_sym_s_form_##SUF(A, v); /* first der in direction v*/ \ _tijk_2o3d_sym_v_form_##SUF(cv[0],h,t[0]); \ _tijk_2o3d_sym_v_form_##SUF(cv[1],h,t[1]); \ h[0]=56*ELL_3V_DOT(cv[0],t[0])-der; \ h[1]=56*ELL_3V_DOT(cv[0],t[1]); \ h[2]=h[1]; \ h[3]=56*ELL_3V_DOT(cv[1],t[1])-der; \ /* now turn this into a symmetric order-2 rank-2 3D tensor */ \ for (r=0; r<2; r++) { \ for (c=0; c<3; c++) { \ tmp[3*r+c]=h[2*r]*t[0][c]+h[2*r+1]*t[1][c]; \ } \ } \ res[0]=t[0][0]*tmp[0]+t[1][0]*tmp[3]; \ res[1]=t[0][0]*tmp[1]+t[1][0]*tmp[4]; \ res[2]=t[0][0]*tmp[2]+t[1][0]*tmp[5]; \ res[3]=t[0][1]*tmp[1]+t[1][1]*tmp[4]; \ res[4]=t[0][1]*tmp[2]+t[1][1]*tmp[5]; \ res[5]=t[0][2]*tmp[2]+t[1][2]*tmp[5]; \ } _TIJK_8O3D_SYM_HESS(double,d) _TIJK_8O3D_SYM_HESS(float,f) TIJK_TYPE_SYM(8o3d_sym, 8, 3, 45) #include "convertQuietPop.h" teem-1.11.0~svn6057/src/tijk/sources.cmake0000664000175000017500000000052012027771151020022 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(TIJK_SOURCES 2dTijk.c 3dTijk.c approxTijk.c enumsTijk.c fsTijk.c miscTijk.c nrrdTijk.c shTijk.c privateTijk.h convertQuietPush.h convertQuietPop.h shtables.h tijk.h ) ADD_TEEM_LIBRARY(tijk ${TIJK_SOURCES}) teem-1.11.0~svn6057/src/tijk/tijk.h0000664000175000017500000006042312104250015016442 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2011, 2010, 2009, 2008 Thomas Schultz Copyright (C) 2010, 2009, 2008 Gordon Kindlmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TIJK_HAS_BEEN_INCLUDED #define TIJK_HAS_BEEN_INCLUDED #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(tijk_EXPORTS) || defined(teem_EXPORTS) # define TIJK_EXPORT extern __declspec(dllexport) # else # define TIJK_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define TIJK_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif typedef struct tijk_sym_fun_t { /* Functions that are specific to completely symmetric tensors. */ /* homogeneous scalar form */ double (*s_form_d) (const double *A, const double *v); float (*s_form_f) (const float *A, const float *v); /* mean value of scalar form */ double (*mean_d) (const double *A); float (*mean_f) (const float *A); /* variance of scalar form */ double (*var_d) (const double *A); float (*var_f) (const float *A); /* vector- and matrix-valued forms, proportional to gradient and * Hessian of scalar homogeneous forms * res and v should not point to the same data! */ void (*v_form_d) (double *res, const double *A, const double *v); void (*v_form_f) (float *res, const float *A, const float *v); /* returns a symmetric matrix (in non-redundant representation) */ void (*m_form_d) (double *res, const double *A, const double *v); void (*m_form_f) (float *res, const float *A, const float *v); /* gradient of the homogeneous form when restricted to the unit * hypersphere; assumes (but does not verify) that v is unit-length */ void (*grad_d) (double *res, const double *A, const double *v); void (*grad_f) (float *res, const float *A, const float *v); /* Hessian of the homogeneous form when restricted to the unit * hypersphere; assumes (but does not verify) that v is unit-length */ void (*hess_d) (double *res, const double *A, const double *v); void (*hess_f) (float *res, const float *A, const float *v); /* make a symmetric rank-1 tensor from the given scalar and vector */ void (*make_rank1_d) (double *res, const double s, const double *v); void (*make_rank1_f) (float *res, const float s, const float *v); /* make a symmetric isotropic tensor from the given scalar; * NULL if order is odd (i.e., only isotropic tensor is the zero tensor) */ void (*make_iso_d) (double *res, const double s); void (*make_iso_f) (float *res, const float s); } tijk_sym_fun; typedef struct tijk_type_t { /* Holds information about (and functions needed for processing) a * specific type of tensor */ const char *name; /* description of the tensor type */ unsigned int order; /* number of tensor indices */ unsigned int dim; /* dimension of each axis (only square tensors supported) */ unsigned int num; /* unique number of components */ #define TIJK_TYPE_MAX_NUM 45 const unsigned int *mult; /* multiplicity of each unique component; * NULL indicates that tensor is unsymmetric */ /* The following fields are used to map the elements of an * unsymmetric tensor to the unique elements of the symmetric one. * A value i in these arrays means: * i==0: element is unmapped (due to antisymmetry) * i>0 : element maps to index (i-1) * i<0 : element maps to index -(i+1), with negated sign * They are NULL if the tensor is unsymmetric or the order is so * high that the unsymmetric variant is not even implemented. */ const int *unsym2uniq; /* unsymmetric to unique; length: pow(dim,order) */ const int *uniq2unsym; /* unique to unsymmetric; length: sum(mult) */ const unsigned int *uniq_idx; /* index into uniq2unsym for each * unique component */ /* tensor scalar product */ double (*tsp_d) (const double *A, const double *B); float (*tsp_f) (const float *A, const float *B); /* norm */ double (*norm_d) (const double *A); float (*norm_f) (const float *A); /* transformation under change of basis; * applies the same matrix M to all tensor modes * assumes that res!=A */ void (*trans_d) (double *res, const double *A, const double *M); void (*trans_f) (float *res, const float *A, const float *M); /* converts to a different tensor type. Supported conversions are: * - exact same type (identity conversion) * - same order & dim, (partially) symmetric -> unsymmetric * - same dim & completely symmetric; lower -> higher order * (preserves the homogeneous form) * res needs to have length res_type->num * Returns a non-zero value if requested conversion is not available. */ int (*convert_d) (double *res, const struct tijk_type_t *res_type, const double *A); int (*convert_f) (float *res, const struct tijk_type_t *res_type, const float *A); /* approximates a tensor with one of the given target type. * Supported approximations are: * - same order & dim, unsymmetric -> (partially) symmetric * (minimizes the norm of the residual) * - same dim & completely symmetric; higher -> lower order * (preserves the frequencies that can be expressed in the low order) * res needs to have length res_type->num * Returns a non-zero value if requested approximation is not implemented. */ int (*approx_d) (double *res, const struct tijk_type_t *res_type, const double *A); int (*approx_f) (float *res, const struct tijk_type_t *res_type, const float *A); /* convert/approximate from a different tensor type. * This should not be called in user code (instead, use convert/approx). * Needed if libraries other than tijk want to define new tensor types. */ int (*_convert_from_d) (double *res, const double *A, const struct tijk_type_t *from_type); int (*_convert_from_f) (float *res, const float *A, const struct tijk_type_t *from_type); int (*_approx_from_d) (double *res, const double *A, const struct tijk_type_t *from_type); int (*_approx_from_f) (float *res, const float *A, const struct tijk_type_t *from_type); /* sym holds additional functions which are only useful for * completely symmetric tensors. In other cases, sym==NULL */ const tijk_sym_fun *sym; } tijk_type; /* OBS: If you add a tijk_type, modify nrrdTijk.c:tijk_get_axis_type! */ /* 2dTijk.c */ TIJK_EXPORT const tijk_type *const tijk_2o2d_unsym; TIJK_EXPORT const tijk_type *const tijk_2o2d_sym; TIJK_EXPORT const tijk_type *const tijk_2o2d_asym; TIJK_EXPORT const tijk_type *const tijk_3o2d_sym; TIJK_EXPORT const tijk_type *const tijk_4o2d_unsym; TIJK_EXPORT const tijk_type *const tijk_4o2d_sym; /* 3dTijk.c */ TIJK_EXPORT const tijk_type *const tijk_1o3d; TIJK_EXPORT const tijk_type *const tijk_2o3d_unsym; TIJK_EXPORT const tijk_type *const tijk_2o3d_sym; TIJK_EXPORT const tijk_type *const tijk_2o3d_asym; TIJK_EXPORT const tijk_type *const tijk_3o3d_unsym; TIJK_EXPORT const tijk_type *const tijk_3o3d_sym; TIJK_EXPORT const tijk_type *const tijk_4o3d_sym; TIJK_EXPORT const tijk_type *const tijk_6o3d_sym; TIJK_EXPORT const tijk_type *const tijk_8o3d_sym; /* still VERY incomplete! */ /* miscTijk.c */ TIJK_EXPORT const int tijkPresent; TIJK_EXPORT void tijk_add_d(double *res, const double *A, const double *B, const tijk_type *type); TIJK_EXPORT void tijk_add_f(float *res, const float *A, const float *B, const tijk_type *type); TIJK_EXPORT void tijk_sub_d(double *res, const double *A, const double *B, const tijk_type *type); TIJK_EXPORT void tijk_sub_f(float *res, const float *A, const float *B, const tijk_type *type); TIJK_EXPORT void tijk_incr_d(double *res, const double *A, const tijk_type *type); TIJK_EXPORT void tijk_incr_f(float *res, const float *A, const tijk_type *type); TIJK_EXPORT void tijk_negate_d(double *res, const double *A, const tijk_type *type); TIJK_EXPORT void tijk_negate_f(float *res, const float *A, const tijk_type *type); TIJK_EXPORT void tijk_scale_d(double *res, const double s, const double *A, const tijk_type *type); TIJK_EXPORT void tijk_scale_f(float *res, const float s, const float *A, const tijk_type *type); TIJK_EXPORT void tijk_zero_d(double *res, const tijk_type *type); TIJK_EXPORT void tijk_zero_f(float *res, const tijk_type *type); TIJK_EXPORT void tijk_copy_d(double *res, const double *A, const tijk_type *type); TIJK_EXPORT void tijk_copy_f(float *res, const float *A, const tijk_type *type); /* approxTijk.c */ /* These parameters control optimization of rank-1 terms */ typedef struct tijk_refine_rank1_parm_t { /* only do optimization if norm of deviatoric is larger than eps_start */ double eps_start; /* declare convergence if improvement is less than eps_impr times the * norm of deviatoric (not residual) */ double eps_impr; /* Parameters associated with Armijo stepsize control */ double beta; /* initial stepsize (divided by norm of deviatoric) */ double gamma; /* stepsize reduction factor (0,1) */ double sigma; /* corridor of values that lead to acceptance (0,1) */ unsigned int maxtry; /* number of stepsize reductions before giving up */ } tijk_refine_rank1_parm; TIJK_EXPORT tijk_refine_rank1_parm *tijk_refine_rank1_parm_new(void); TIJK_EXPORT tijk_refine_rank1_parm *tijk_refine_rank1_parm_nix(tijk_refine_rank1_parm *parm); /* These parameters control optimization of rank-k approximations */ typedef struct tijk_refine_rankk_parm_t { double eps_res; /* stop optimization if the residual is smaller than this */ /* declare convergence if tensor norm improved less than eps_impr times * the original norm */ double eps_impr; char pos; /* if non-zero, allow positive terms only */ tijk_refine_rank1_parm *rank1_parm; /* used for rank1-optimization */ } tijk_refine_rankk_parm; TIJK_EXPORT tijk_refine_rankk_parm *tijk_refine_rankk_parm_new(void); TIJK_EXPORT tijk_refine_rankk_parm *tijk_refine_rankk_parm_nix(tijk_refine_rankk_parm *parm); typedef struct tijk_approx_heur_parm_t { double eps_res; /* stop adding terms if the residual is smaller than eps_res * times the original norm */ double eps_impr; /* stop adding terms if it would reduce the residual * less than eps_impr times the original norm */ /* If ratios is non-NULL, it should have k-1 entries for a rank-k approx. * Do not add the ith rank-1 term when the ratio of largest/smallest * coefficient would be greater than ratios[i-2] */ double *ratios; tijk_refine_rankk_parm *refine_parm; /* used for rank-k refinement */ } tijk_approx_heur_parm; TIJK_EXPORT tijk_approx_heur_parm *tijk_approx_heur_parm_new(void); TIJK_EXPORT tijk_approx_heur_parm *tijk_approx_heur_parm_nix(tijk_approx_heur_parm *parm); TIJK_EXPORT int tijk_init_rank1_2d_d(double *s, double *v, const double *ten, const tijk_type *type); TIJK_EXPORT int tijk_init_rank1_2d_f(float *s, float *v, const float *ten, const tijk_type *type); TIJK_EXPORT int tijk_init_rank1_3d_d(double *s, double *v, const double *ten, const tijk_type *type); TIJK_EXPORT int tijk_init_rank1_3d_f(float *s, float *v, const float *ten, const tijk_type *type); TIJK_EXPORT int tijk_init_max_2d_d(double *s, double *v, const double *ten, const tijk_type *type); TIJK_EXPORT int tijk_init_max_2d_f(float *s, float *v, const float *ten, const tijk_type *type); TIJK_EXPORT int tijk_init_max_3d_d(double *s, double *v, const double *ten, const tijk_type *type); TIJK_EXPORT int tijk_init_max_3d_f(float *s, float *v, const float *ten, const tijk_type *type); /* For ANSI C compatibility, these routines rely on * type->num<=TIJK_TYPE_MAX_NUM !*/ TIJK_EXPORT int tijk_refine_rank1_2d_d(double *s, double *v, const double *ten, const tijk_type *type, const tijk_refine_rank1_parm *parm); TIJK_EXPORT int tijk_refine_rank1_2d_f(float *s, float *v, const float *ten, const tijk_type *type, const tijk_refine_rank1_parm *parm); TIJK_EXPORT int tijk_refine_rank1_3d_d(double *s, double *v, const double *ten, const tijk_type *type, const tijk_refine_rank1_parm *parm); TIJK_EXPORT int tijk_refine_rank1_3d_f(float *s, float *v, const float *ten, const tijk_type *type, const tijk_refine_rank1_parm *parm); TIJK_EXPORT int tijk_refine_max_2d_d(double *s, double *v, const double *ten, const tijk_type *type, const tijk_refine_rank1_parm *parm); TIJK_EXPORT int tijk_refine_max_2d_f(float *s, float *v, const float *ten, const tijk_type *type, const tijk_refine_rank1_parm *parm); TIJK_EXPORT int tijk_refine_max_3d_d(double *s, double *v, const double *ten, const tijk_type *type, const tijk_refine_rank1_parm *parm); TIJK_EXPORT int tijk_refine_max_3d_f(float *s, float *v, const float *ten, const tijk_type *type, const tijk_refine_rank1_parm *parm); TIJK_EXPORT int tijk_refine_rankk_2d_d(double *ls, double *vs, double *tens, double *res, double *resnorm, const double orignorm, const tijk_type *type, const unsigned int k, const tijk_refine_rankk_parm *parm); TIJK_EXPORT int tijk_refine_rankk_2d_f(float *ls, float *vs, float *tens, float *res, float *resnorm, const float orignorm, const tijk_type *type, const unsigned int k, const tijk_refine_rankk_parm *parm); TIJK_EXPORT int tijk_refine_rankk_3d_d(double *ls, double *vs, double *tens, double *res, double *resnorm, const double orignorm, const tijk_type *type, const unsigned int k, const tijk_refine_rankk_parm *parm); TIJK_EXPORT int tijk_refine_rankk_3d_f(float *ls, float *vs, float *tens, float *res, float *resnorm, const float orignorm, const tijk_type *type, const unsigned int k, const tijk_refine_rankk_parm *parm); TIJK_EXPORT int tijk_approx_rankk_2d_d(double *ls, double *vs, double *res, const double *ten, const tijk_type *type, const unsigned int k, const tijk_refine_rankk_parm *parm); TIJK_EXPORT int tijk_approx_rankk_2d_f(float *ls, float *vs, float *res, const float *ten, const tijk_type *type, const unsigned int k, const tijk_refine_rankk_parm *parm); TIJK_EXPORT int tijk_approx_rankk_3d_d(double *ls, double *vs, double *res, const double *ten, const tijk_type *type, const unsigned int k, const tijk_refine_rankk_parm *parm); TIJK_EXPORT int tijk_approx_rankk_3d_f(float *ls, float *vs, float *res, const float *ten, const tijk_type *type, const unsigned int k, const tijk_refine_rankk_parm *parm); TIJK_EXPORT int tijk_approx_heur_2d_d(double *ls, double *vs, double *res, const double *ten, const tijk_type *type, const unsigned int k, const tijk_approx_heur_parm *parm); TIJK_EXPORT int tijk_approx_heur_2d_f(float *ls, float *vs, float *res, const float *ten, const tijk_type *type, const unsigned int k, const tijk_approx_heur_parm *parm); TIJK_EXPORT int tijk_approx_heur_3d_d(double *ls, double *vs, double *res, const double *ten, const tijk_type *type, const unsigned int k, const tijk_approx_heur_parm *parm); TIJK_EXPORT int tijk_approx_heur_3d_f(float *ls, float *vs, float *res, const float *ten, const tijk_type *type, const unsigned int k, const tijk_approx_heur_parm *parm); /* shTijk.c */ /* at position i, number of coefficients for order 2*i */ TIJK_EXPORT const unsigned int tijk_esh_len[]; TIJK_EXPORT const unsigned int tijk_max_esh_order; TIJK_EXPORT unsigned int tijk_eval_esh_basis_d(double *res, unsigned int order, double theta, double phi); TIJK_EXPORT unsigned int tijk_eval_esh_basis_f(float *res, unsigned int order, float theta, float phi); TIJK_EXPORT double tijk_eval_esh_d(double *coeffs, unsigned int order, double theta, double phi); TIJK_EXPORT float tijk_eval_esh_f(float *coeffs, unsigned int order, float theta, float phi); TIJK_EXPORT double tijk_esh_sp_d(double *A, double *B, unsigned int order); TIJK_EXPORT float tijk_esh_sp_f(float *A, float *B, unsigned int order); TIJK_EXPORT int tijk_3d_sym_to_esh_d(double *res, const double *ten, const tijk_type *type); TIJK_EXPORT int tijk_3d_sym_to_esh_f(float *res, const float *ten, const tijk_type *type); TIJK_EXPORT const tijk_type *tijk_esh_to_3d_sym_d(double *res, const double *sh, unsigned int order); TIJK_EXPORT const tijk_type *tijk_esh_to_3d_sym_f(float *res, const float *sh, unsigned int order); TIJK_EXPORT const double *tijk_3d_sym_to_esh_matrix_d(const tijk_type *type); TIJK_EXPORT const float *tijk_3d_sym_to_esh_matrix_f(const tijk_type *type); TIJK_EXPORT const double *tijk_esh_to_3d_sym_matrix_d(unsigned int order); TIJK_EXPORT const float *tijk_esh_to_3d_sym_matrix_f(unsigned int order); TIJK_EXPORT void tijk_esh_convolve_d(double *out, const double *in, const double *kernel, unsigned int order); TIJK_EXPORT void tijk_esh_convolve_f(float *out, const float *in, const float *kernel, unsigned int order); TIJK_EXPORT void tijk_esh_deconvolve_d(double *out, const double *in, const double *kernel, unsigned int order); TIJK_EXPORT void tijk_esh_deconvolve_f(float *out, const float *in, const float *kernel, unsigned int order); TIJK_EXPORT int tijk_esh_make_kernel_rank1_f(float *kernel, const float *signal, unsigned int order); TIJK_EXPORT int tijk_esh_make_kernel_rank1_d(double *kernel, const double *signal, unsigned int order); TIJK_EXPORT int tijk_esh_make_kernel_delta_f(float *kernel, const float *signal, unsigned int order); TIJK_EXPORT int tijk_esh_make_kernel_delta_d(double *kernel, const double *signal, unsigned int order); /* fsTijk.c */ /* for any given order, length is simply order+1; no need for a table */ TIJK_EXPORT const unsigned int tijk_max_efs_order; TIJK_EXPORT unsigned int tijk_eval_efs_basis_d(double *res, unsigned int order, double phi); TIJK_EXPORT unsigned int tijk_eval_efs_basis_f(float *res, unsigned int order, float phi); TIJK_EXPORT double tijk_eval_efs_d(double *coeffs, unsigned int order, double phi); TIJK_EXPORT float tijk_eval_efs_f(float *coeffs, unsigned int order, float phi); TIJK_EXPORT int tijk_2d_sym_to_efs_d(double *res, const double *ten, const tijk_type *type); TIJK_EXPORT int tijk_2d_sym_to_efs_f(float *res, const float *ten, const tijk_type *type); TIJK_EXPORT const tijk_type *tijk_efs_to_2d_sym_d(double *res, const double *fs, unsigned int order); TIJK_EXPORT const tijk_type *tijk_efs_to_2d_sym_f(float *res, const float *fs, unsigned int order); /* enumsTijk.c */ /* tijk_class_* enum * * classes of objects Tijk deals with (e.g., tensors, SH series) */ enum { tijk_class_unknown, tijk_class_tensor, /* 1: a tijk_type */ tijk_class_esh, /* 2: even-order spherical harmonic */ tijk_class_efs, /* 3: even-order fourier series */ tijk_class_last }; #define TIJK_CLASS_MAX 3 TIJK_EXPORT const airEnum *const tijk_class; /* nrrdTijk.c */ TIJK_EXPORT int tijk_set_axis_tensor(Nrrd *nrrd, unsigned int axis, const tijk_type *type); TIJK_EXPORT int tijk_set_axis_esh(Nrrd *nrrd, unsigned int axis, unsigned int order); TIJK_EXPORT int tijk_set_axis_efs(Nrrd *nrrd, unsigned int axis, unsigned int order); typedef struct tijk_axis_info_t { int tclass; /* class of Tijk object, from the tijk_class enum */ unsigned int masked; /* whether or not values are masked */ const tijk_type *type; unsigned int order; } tijk_axis_info; TIJK_EXPORT int tijk_get_axis_type(tijk_axis_info *info, const Nrrd *nrrd, unsigned int axis); #ifdef __cplusplus } #endif #endif /* TIJK_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/tijk/approxTijk.c0000664000175000017500000007244312042325046017644 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2011, 2010, 2009, 2008 Thomas Schultz Copyright (C) 2010, 2009, 2008 Gordon Kindlmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "tijk.h" #include "privateTijk.h" #include "convertQuietPush.h" /* Functions for symmetric tensor approximation */ /* a coarse sampling of the unit semicircle */ const unsigned int _tijk_max_candidates_2d=8; #define _CANDIDATES_2D(TYPE, SUF) \ static TYPE _candidates_2d_##SUF[16] = { \ 1.0, 0.0, \ 0.92387953251128674, 0.38268343236508978, \ 0.70710678118654757, 0.70710678118654746, \ 0.38268343236508984, 0.92387953251128674, \ 0.0, 1.0, \ -0.38268343236508973, 0.92387953251128674, \ -0.70710678118654746, 0.70710678118654757, \ -0.92387953251128674, 0.38268343236508989 \ }; _CANDIDATES_2D(double, d) _CANDIDATES_2D(float, f) /* a coarse sampling of the unit sphere */ const unsigned int _tijk_max_candidates_3d=30; #define _CANDIDATES_3D(TYPE, SUF) \ static TYPE _candidates_3d_##SUF[90] = { \ -0.546405, 0.619202, 0.563943, \ -0.398931,-0.600006, 0.693432, \ 0.587973, 0.521686, 0.618168, \ 0.055894,-0.991971,-0.113444, \ -0.666933,-0.677984, 0.309094, \ 0.163684, 0.533013, 0.830123, \ 0.542826, 0.133898, 0.829102, \ -0.074751,-0.350412, 0.933608, \ 0.845751, -0.478624,-0.235847, \ 0.767148,-0.610673, 0.196372, \ -0.283810, 0.381633, 0.879663, \ 0.537228,-0.616249, 0.575868, \ -0.711387, 0.197778, 0.674398, \ 0.886511, 0.219025, 0.407586, \ 0.296061, 0.842985, 0.449136, \ -0.937540,-0.340990, 0.068877, \ 0.398833, 0.917023, 0.000835, \ 0.097278,-0.711949, 0.695460, \ -0.311534, 0.908623,-0.278121, \ -0.432043,-0.089758, 0.897375, \ -0.949980, 0.030810, 0.310788, \ 0.146722,-0.811981,-0.564942, \ -0.172201,-0.908573, 0.380580, \ 0.507209,-0.848578,-0.150513, \ -0.730808,-0.654136,-0.194999, \ 0.077744, 0.094961, 0.992441, \ 0.383976,-0.293959, 0.875300, \ 0.788208,-0.213656, 0.577130, \ -0.752333,-0.301447, 0.585769, \ -0.975732, 0.165497,-0.143382 \ }; _CANDIDATES_3D(double, d) _CANDIDATES_3D(float, f) /* Produces a rough estimate of the position and value of the ABSOLUTE * maximum of the homogeneous form. * s and v will be set to the corresponding value and (unit-length) direction. * ten is the input tensor, type is its type. * returns 0 upon success, 1 upon erroneous parameters. */ #define _TIJK_INIT_RANK1(TYPE, SUF, DIM) \ int \ tijk_init_rank1_##DIM##d_##SUF(TYPE *s, TYPE *v, const TYPE *ten, \ const tijk_type *type) { \ TYPE absmax=-1; \ unsigned int i; \ TYPE *candidate=_candidates_##DIM##d_##SUF; \ if (type->dim!=DIM || type->sym==NULL) \ return 1; \ for (i=0; i<_tijk_max_candidates_##DIM##d; i++) { \ TYPE val=(*type->sym->s_form_##SUF)(ten, candidate); \ TYPE absval=fabs(val); \ if (absval>absmax) { \ absmax=absval; \ *s=val; \ ELL_##DIM##V_COPY(v, candidate); \ } \ candidate+=DIM; \ } \ return 0; \ } _TIJK_INIT_RANK1(double, d, 2) _TIJK_INIT_RANK1(float, f, 2) _TIJK_INIT_RANK1(double, d, 3) _TIJK_INIT_RANK1(float, f, 3) /* Produces a rough estimate of the position and value of the SIGNED * maximum of the homogeneous form. * s and v will be set to the corresponding value and (unit-length) direction. * ten is the input tensor, type is its type. * returns 0 upon success, 1 upon erroneous parameters. */ #define _TIJK_INIT_MAX(TYPE, SUF, DIM) \ int \ tijk_init_max_##DIM##d_##SUF(TYPE *s, TYPE *v, const TYPE *ten, \ const tijk_type *type) { \ TYPE max=0; \ unsigned int i; \ TYPE *candidate=_candidates_##DIM##d_##SUF; \ if (type->dim!=DIM || type->sym==NULL) \ return 1; \ *s=max=(*type->sym->s_form_##SUF)(ten, candidate); \ ELL_##DIM##V_COPY(v, candidate); \ for (i=1; i<_tijk_max_candidates_##DIM##d; i++) { \ TYPE val; \ candidate+=DIM; \ val=(*type->sym->s_form_##SUF)(ten, candidate); \ if (val>max) { \ max=val; \ *s=val; \ ELL_##DIM##V_COPY(v, candidate); \ } \ } \ return 0; \ } _TIJK_INIT_MAX(double, d, 2) _TIJK_INIT_MAX(float, f, 2) _TIJK_INIT_MAX(double, d, 3) _TIJK_INIT_MAX(float, f, 3) static const tijk_refine_rank1_parm refine_rank1_parm_default = { 1e-10, 1e-4, 0.3, 0.9, 0.5, 50}; tijk_refine_rank1_parm *tijk_refine_rank1_parm_new() { tijk_refine_rank1_parm *parm; parm = (tijk_refine_rank1_parm *)malloc(sizeof(tijk_refine_rank1_parm)); if (parm) { memcpy(parm,&refine_rank1_parm_default,sizeof(tijk_refine_rank1_parm)); } return parm; } tijk_refine_rank1_parm *tijk_refine_rank1_parm_nix(tijk_refine_rank1_parm *parm) { airFree(parm); return NULL; } /* Refines a given rank-1 tensor to a locally optimal rank-1 approximation, * using a gradient descent scheme with Armijo stepsize. * s and v are scalar and (unit-len) vector part of the rank-1 tensor and * will be updated by this function. * ten is the input tensor, type is its type. * parm: if non-NULL, used to change the default optimization parameters * We improve the rank-1 approximation by moving s away from zero * (unless refinemax is nonzero, in which case we try to make the * signed value of s as large as possible) * returns 0 upon success * 1 when given wrong parameters * 2 when the Armijo scheme failed to produce a valid stepsize */ #define _TIJK_REFINE_RANK1ORMAX(TYPE, SUF, DIM) \ int \ _tijk_refine_rank1ormax_##DIM##d_##SUF(TYPE *s, TYPE *v, const TYPE *ten, \ const tijk_type *type, \ const tijk_refine_rank1_parm *parm, \ const int refinemax) { \ TYPE isoten[TIJK_TYPE_MAX_NUM], anisoten[TIJK_TYPE_MAX_NUM]; \ TYPE der[DIM], iso, anisonorm, anisonorminv, oldval; \ char sign=(refinemax || *s>0)?1:-1; \ TYPE alpha, beta; \ if (type->dim!=DIM || type->sym==NULL) \ return 1; \ if (parm==NULL) parm=&refine_rank1_parm_default; \ /* It's easier to do the optimization on the deviatoric */ \ iso=(*type->sym->mean_##SUF)(ten); \ (*type->sym->make_iso_##SUF)(isoten, iso); \ tijk_sub_##SUF(anisoten, ten, isoten, type); \ anisonorm=(*type->norm_##SUF)(anisoten); \ if (anisonormeps_start) { \ return 0; /* nothing to do */ \ } else { \ anisonorminv=1.0/anisonorm; \ } \ alpha=beta=parm->beta*anisonorminv; \ oldval=*s-iso; \ /* set initial derivative */ \ (*type->sym->grad_##SUF)(der, anisoten, v); \ while (1) { /* refine until convergence */ \ /* stepsize needs to depend on norm to make the descent */ \ /* scale invariant */ \ unsigned int armijoct=0; \ TYPE testv[DIM], val; \ TYPE dist, derlen=ELL_##DIM##V_LEN(der); \ /* determine stepsize based on Armijo's rule */ \ while (1) { \ ++armijoct; \ if (armijoct>parm->maxtry) { \ /* failed to find a valid stepsize */ \ return 2; \ } \ ELL_##DIM##V_SCALE_ADD2(testv,1.0,v,sign*alpha,der); \ ELL_##DIM##V_NORM(testv,testv,dist); \ dist=1-ELL_##DIM##V_DOT(v,testv); \ val=(*type->sym->s_form_##SUF)(anisoten,testv); \ if (sign*val>=sign*oldval+parm->sigma*derlen*dist) { \ /* accept step */ \ ELL_##DIM##V_COPY(v,testv); \ *s=val+iso; \ (*type->sym->grad_##SUF)(der, anisoten, v); \ if (alphagamma; \ break; \ } \ alpha *= parm->gamma; /* try again with decreased stepsize */ \ } \ if (sign*(val-oldval)<=parm->eps_impr*anisonorm) { \ break; /* declare convergence */ \ } \ oldval=val; \ } \ return 0; \ } _TIJK_REFINE_RANK1ORMAX(double, d, 2) _TIJK_REFINE_RANK1ORMAX(float, f, 2) _TIJK_REFINE_RANK1ORMAX(double, d, 3) _TIJK_REFINE_RANK1ORMAX(float, f, 3) #define _TIJK_REFINE_RANK1(TYPE, SUF, DIM) \ int \ tijk_refine_rank1_##DIM##d_##SUF(TYPE *s, TYPE *v, const TYPE *ten, \ const tijk_type *type, \ const tijk_refine_rank1_parm *parm) { \ return _tijk_refine_rank1ormax_##DIM##d_##SUF(s,v,ten,type,parm,0); \ } _TIJK_REFINE_RANK1(double, d, 2) _TIJK_REFINE_RANK1(float, f, 2) _TIJK_REFINE_RANK1(double, d, 3) _TIJK_REFINE_RANK1(float, f, 3) #define _TIJK_REFINE_MAX(TYPE, SUF, DIM) \ int \ tijk_refine_max_##DIM##d_##SUF(TYPE *s, TYPE *v, const TYPE *ten, \ const tijk_type *type, \ const tijk_refine_rank1_parm *parm) { \ return _tijk_refine_rank1ormax_##DIM##d_##SUF(s,v,ten,type,parm,1); \ } _TIJK_REFINE_MAX(double, d, 2) _TIJK_REFINE_MAX(float, f, 2) _TIJK_REFINE_MAX(double, d, 3) _TIJK_REFINE_MAX(float, f, 3) static const tijk_refine_rankk_parm refine_rankk_parm_default = { 1e-10, 1e-4, 0, NULL}; tijk_refine_rankk_parm *tijk_refine_rankk_parm_new() { tijk_refine_rankk_parm *parm; parm = (tijk_refine_rankk_parm *)malloc(sizeof(tijk_refine_rankk_parm)); if (parm) { memcpy(parm,&refine_rankk_parm_default,sizeof(tijk_refine_rankk_parm)); } return parm; } tijk_refine_rankk_parm *tijk_refine_rankk_parm_nix(tijk_refine_rankk_parm *parm) { if (parm!=NULL) { parm->rank1_parm=tijk_refine_rank1_parm_nix(parm->rank1_parm); free(parm); } return NULL; } /* Refines an existing rank-k approximation, using the iterative algorithm * described in Schultz and Seidel, TVCG/Vis 2008. * This is mostly used as a helper routine for tijk_approx_heur and * tijk_approx_rankk. It should only appear in user code if you know * what you are doing. * * ls and vs are the scalar and (unit-len) vector parts of the rank-1 tensors. * tens has the rank-1 tensors themselves (same information as ls and vs, but * "multiplied out"). * res is the approximation residual. * resnorm is the norm of the approximation residual. * orignorm is the original norm of the tensor * type is its type of tens and res * k is the rank (determines the length of ls, vs, and tens. * returns 0 upon success, 1 upon erroneous parameters. */ #define _TIJK_REFINE_RANKK(TYPE, SUF, DIM) \ int \ tijk_refine_rankk_##DIM##d_##SUF(TYPE *ls, TYPE *vs, TYPE *tens, \ TYPE *res, TYPE *resnorm, \ const TYPE orignorm, \ const tijk_type *type, \ const unsigned int k, \ const tijk_refine_rankk_parm *parm) \ { \ TYPE newnorm=(*resnorm); \ unsigned int i; \ if (type->dim!=DIM || type->sym==NULL) \ return 1; \ if (parm==NULL) parm=&refine_rankk_parm_default; \ if (*resnormeps_res || k==0) { \ return 0; /* nothing to do */ \ } \ do { \ *resnorm=newnorm; \ for (i=0; inum, type); \ ls[i]=(*type->sym->s_form_##SUF)(res, vs+DIM*i); \ if (parm->pos && ls[i]<0.0) { /* try a new one */ \ tijk_init_max_##DIM##d_##SUF(ls+i, vs+DIM*i, res, type); \ } \ } else { /* add a new term */ \ if (parm->pos) \ tijk_init_max_##DIM##d_##SUF(ls+i, vs+DIM*i, res, type); \ else \ tijk_init_rank1_##DIM##d_##SUF(ls+i, vs+DIM*i, res, type); \ } \ tijk_refine_rank1_##DIM##d_##SUF(ls+i, vs+DIM*i, res, type, \ parm->rank1_parm); \ if (!parm->pos || ls[i]>0.0) { \ (*type->sym->make_rank1_##SUF)(tens+i*type->num, ls[i], vs+DIM*i); \ tijk_sub_##SUF(res, res, tens+i*type->num, type); \ } else { \ ls[i]=0.0; \ } \ } \ newnorm=(*type->norm_##SUF)(res); \ } while (newnorm>parm->eps_res && \ (*resnorm)-newnorm>parm->eps_impr*orignorm); \ *resnorm=newnorm; \ return 0; \ } _TIJK_REFINE_RANKK(double, d, 2) _TIJK_REFINE_RANKK(float, f, 2) _TIJK_REFINE_RANKK(double, d, 3) _TIJK_REFINE_RANKK(float, f, 3) static const tijk_approx_heur_parm approx_heur_parm_default = { 1e-10, 0.1, NULL, NULL}; tijk_approx_heur_parm *tijk_approx_heur_parm_new() { tijk_approx_heur_parm *parm; parm = (tijk_approx_heur_parm *)malloc(sizeof(tijk_approx_heur_parm)); if (parm) { memcpy(parm,&approx_heur_parm_default,sizeof(tijk_approx_heur_parm)); } return parm; } tijk_approx_heur_parm *tijk_approx_heur_parm_nix(tijk_approx_heur_parm *parm) { if (parm!=NULL) { parm->refine_parm=tijk_refine_rankk_parm_nix(parm->refine_parm); free(parm); } return NULL; } /* Approximates a given tensor with a rank-k approximation, guessing the best * rank by using a heuristic similar to Schultz and Seidel, TVCG/Vis 2008. * ls and vs are the scalar and (unit-len) vector parts of the rank-1 tensors. * res is the approximation residual. * ten is the input tensor, type is its type. * k is the maximum rank that should be used (ls and vs need to have enough * space for k and DIM*k values, respectively). * parms: if non-NULL, allows to modify the parameters of the heuristic * returns the rank determined by the heuristic. */ #define _TIJK_APPROX_HEUR(TYPE, SUF, DIM) \ int \ tijk_approx_heur_##DIM##d_##SUF(TYPE *ls, TYPE *vs, TYPE *res, \ const TYPE *ten, const tijk_type *type, \ const unsigned int k, \ const tijk_approx_heur_parm *parm) \ { \ TYPE *lstmp=NULL, *vstmp=NULL, *tens=NULL, *restmp=NULL; \ TYPE oldnorm, newnorm, orignorm; \ unsigned int currank=1; \ if (type->dim!=DIM || type->sym==NULL) \ return 0; \ if (parm==NULL) parm=&approx_heur_parm_default; \ /* initializations */ \ orignorm=newnorm=oldnorm=(*type->norm_##SUF)(ten); \ if (k==0) { \ if (res!=NULL) memcpy(res,ten,sizeof(TYPE)*type->num); \ return 0; /* nothing to do */ \ } \ lstmp=AIR_CALLOC(k,TYPE); \ if (lstmp==NULL) goto cleanup_and_exit; \ vstmp=AIR_CALLOC(DIM*k,TYPE); \ if (vstmp==NULL) goto cleanup_and_exit; \ tens=AIR_CALLOC(k*type->num,TYPE); \ if (tens==NULL) goto cleanup_and_exit; \ restmp=AIR_CALLOC(type->num,TYPE); \ if (restmp==NULL) goto cleanup_and_exit; \ memcpy(restmp, ten, sizeof(TYPE)*type->num); \ for (currank=1; currank<=k; currank++) { \ int accept=1; \ tijk_refine_rankk_##DIM##d_##SUF(lstmp, vstmp, tens, restmp, \ &newnorm, orignorm, type, \ currank, parm->refine_parm); \ if (currank>1 && parm->ratios!=NULL) { \ /* make sure the desired ratio is fulfilled */ \ TYPE largest=fabs(lstmp[0]), smallest=largest; \ unsigned int i; \ for (i=1; ilargest) largest=fabs(lstmp[i]); \ if (fabs(lstmp[i])parm->ratios[currank-2]) \ accept=0; \ } \ if (accept && oldnorm-newnorm>parm->eps_impr*orignorm) { \ /* copy over */ \ memcpy(vs, vstmp, sizeof(TYPE)*DIM*currank); \ memcpy(ls, lstmp, sizeof(TYPE)*currank); \ if (res!=NULL) \ memcpy(res, restmp, sizeof(TYPE)*type->num); \ oldnorm=newnorm; \ } else { \ break; \ } \ if (newnormeps_res*orignorm) { \ currank++; \ break; /* mission accomplished */ \ } \ } \ cleanup_and_exit: \ if (lstmp!=NULL) free(lstmp); \ if (vstmp!=NULL) free(vstmp); \ if (tens!=NULL) free(tens); \ if (restmp!=NULL) free(restmp); \ return currank-1; \ } _TIJK_APPROX_HEUR(double, d, 2) _TIJK_APPROX_HEUR(float, f, 2) _TIJK_APPROX_HEUR(double, d, 3) _TIJK_APPROX_HEUR(float, f, 3) /* Approximates a given tensor with a rank-k approximation, using the * iterative algorithm described in Schultz and Seidel, TVCG/Vis 2008. * ls and vs are the scalar and (unit-len) vector parts of the rank-1 tensors. * They can be NULL if you're just interested in the residual. * res is the approximation residual. Can be NULL if you're not interested. * ten is the input tensor, type is its type. * k is the rank that should be used (if ls and vs are non-NULL, they need * to have enough space for k and DIM*k values, respectively). * returns 0 upon success, 1 upon error */ #define _TIJK_APPROX_RANKK(TYPE, SUF, DIM) \ int \ tijk_approx_rankk_##DIM##d_##SUF(TYPE *ls, TYPE *vs, TYPE *res, \ const TYPE *ten, const tijk_type *type, \ const unsigned int k, \ const tijk_refine_rankk_parm *parm) \ { \ char hadls=1, hadvs=1, hadres=1; \ TYPE *tens=NULL; \ TYPE newnorm, orignorm; \ int retval=0; \ if (type->dim!=DIM || type->sym==NULL) \ return 1; \ if (parm==NULL) parm=&refine_rankk_parm_default; \ /* initializations */ \ orignorm=newnorm=(*type->norm_##SUF)(ten); \ if (orignormeps_res || k==0) { \ if (ls!=NULL) { \ unsigned int i; \ for (i=0; inum); \ return 0; /* nothing to do */ \ } \ if (ls==NULL) { \ ls=AIR_CALLOC(k,TYPE); \ if (ls==NULL) {retval=1; goto cleanup_and_exit;} \ hadls=0; \ } else { \ unsigned int i; \ for (i=0; inum,TYPE); \ if (res==NULL) {retval=1; goto cleanup_and_exit;} \ hadres=0; \ } \ tens=AIR_CALLOC(k*type->num,TYPE); \ if (tens==NULL) {retval=1; goto cleanup_and_exit;} \ memcpy(res, ten, sizeof(TYPE)*type->num); \ tijk_refine_rankk_##DIM##d_##SUF(ls, vs, tens, res, &newnorm, \ orignorm, type, k, parm); \ cleanup_and_exit: \ if (!hadls) free(ls); \ if (!hadvs) free(vs); \ if (!hadres) free(res); \ if (tens!=NULL) free(tens); \ return retval; \ } _TIJK_APPROX_RANKK(double, d, 2) _TIJK_APPROX_RANKK(float, f, 2) _TIJK_APPROX_RANKK(double, d, 3) _TIJK_APPROX_RANKK(float, f, 3) #include "convertQuietPop.h" teem-1.11.0~svn6057/src/tijk/shtables.h0000664000175000017500000014466412042325046017330 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2011, 2010, 2009, 2008 Thomas Schultz Copyright (C) 2010, 2009, 2008 Gordon Kindlmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The idea is that you will #define TIJK_TABLE_TYPE to 0 (double) or 1 (float), * and then #include this file. * The issue here is that having separate double and float versions turned * out to give a 3-4fold speedup when computing with float. */ /* conversion matrices computed in Mathematica, stored in double precision */ #if TIJK_TABLE_TYPE == 0 static const double _tijk_sym2esh_o2_d[6*6] ={ #else static const float _tijk_sym2esh_o2_f[6*6] ={ #endif 1.1816359006036771806, 0, 0, 1.1816359006036771806, 0, 1.1816359006036771806, 0.9152912328637688999, 0, 0, -0.9152912328637688999, 0, 0, 0, 0, -1.8305824657275377998, 0, 0, 0, -0.52844363968080143579, 0, 0, -0.52844363968080143579, 0, 1.0568872793616028716, 0, 0, 0, 0, -1.8305824657275377998, 0, 0, 1.8305824657275377998, 0, 0, 0, 0 }; #if TIJK_TABLE_TYPE == 0 static const double _tijk_esh2sym_o2_d[6*6] ={ #else static const float _tijk_esh2sym_o2_f[6*6] ={ #endif 0.28209479177387819515, 0.54627421529603958916, 0, -0.31539156525252004526, 0, 0, 0, 0, 0, 0, 0, 0.54627421529603958916, 0, 0, -0.54627421529603958916, 0, 0, 0, 0.28209479177387819515, -0.54627421529603958916, 0, -0.31539156525252004526, 0, 0, 0, 0, 0, 0, -0.54627421529603958916, 0, 0.28209479177387819515, 0, 0, 0.63078313050504009052, 0, 0 }; #if TIJK_TABLE_TYPE == 0 static const double _tijk_sym2esh_o4_d[15*15] ={ #else static const float _tijk_sym2esh_o4_f[15*15] ={ #endif 0.70898154036220639718, 0, 0, 1.4179630807244127944, 0, 1.4179630807244127944, 0, 0, 0, 0, 0.70898154036220639718, 0, 1.4179630807244127944, 0, 0.70898154036220639718, 0.78453534245465905705, 0, 0, 0, 0, 0.78453534245465905705, 0, 0, 0, 0, -0.78453534245465905705, 0, -0.78453534245465905705, 0, 0, 0, 0, -1.5690706849093181141, 0, 0, 0, 0, -1.5690706849093181141, 0, -1.5690706849093181141, 0, 0, 0, 0, 0, -0.45295169115497263546, 0, 0, -0.90590338230994527091, 0, 0.45295169115497263546, 0, 0, 0, 0, -0.45295169115497263546, 0, 0.45295169115497263546, 0, 0.90590338230994527091, 0, 0, 0, 0, -1.5690706849093181141, 0, 0, 0, 0, 0, 0, -1.5690706849093181141, 0, -1.5690706849093181141, 0, 0, 1.5690706849093181141, 0, 0, 0, 0, 1.5690706849093181141, 0, 1.5690706849093181141, 0, 0, 0, 0, 0, 0, 0.1997329217870320861, 0, 0, -1.1983975307221925721, 0, 0, 0, 0, 0, 0, 0.1997329217870320861, 0, 0, 0, 0, 0, 0, -0.56493001368725082045, 0, 0, 0, 0, 1.6947900410617524614, 0, 0, 0, 0, 0, 0, 0, -0.15098389705165754515, 0, 0, 0, 0, 0.90590338230994538193, 0, 0, 0, 0, 0.15098389705165754515, 0, -0.90590338230994538193, 0, 0, 0, 0, 0.64057042473119185644, 0, 0, 0, 0, 0.64057042473119185644, 0, -0.85409389964158910491, 0, 0, 0, 0, 0, 0.10128307719460090397, 0, 0, 0.20256615438920180794, 0, -0.81026461755680723176, 0, 0, 0, 0, 0.10128307719460090397, 0, -0.81026461755680723176, 0, 0.27008820585226911426, 0, 0, 0, 0, 0.64057042473119185644, 0, 0, 0, 0, 0, 0, 0.64057042473119185644, 0, -0.85409389964158910491, 0, 0, -0.3019677941033150903, 0, 0, 0, 0, -0.3019677941033150903, 0, 1.8118067646198907639, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1.6947900410617524614, 0, 0, 0, 0, 0, 0, 0.56493001368725082045, 0, 0, 0, 0, 0.79893168714812834441, 0, 0, 0, 0, -0.79893168714812834441, 0, 0, 0, 0, 0, 0, 0, 0 }; #if TIJK_TABLE_TYPE == 0 static const double _tijk_esh2sym_o4_d[15*15] ={ #else static const float _tijk_esh2sym_o4_f[15*15] ={ #endif 0.28209479177387819515, 0.54627421529603970018, 0, -0.31539156525252004526, 0, 0, 0.62583573544917614484, 0, -0.47308734787878004013, 0, 0.31735664074561298342, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.27313710764801985009, 0, 0, 0, 0, 0, 0, -0.23654367393939002007, 0, 0.62583573544917614484, 0, 0, -0.27313710764801979458, 0, 0, 0, 0, -0.44253269244498261159, 0, 0.50178490766796690625, 0, 0, 0, 0, 0, 0.094031597257959328995, -2.6239092990136782376e-17, 0, -0.10513052175084000583, 0, 0, -0.62583573544917614484, 0, -1.1695830935687379082e-17, 0, 0.10578554691520429543, 0, 0, 0, 0, 0, 0, 0, 0, -0.091045702549339968535, 0, 0, 0, 0, 0, 0, 0.16726163588932230208, 0, -0.44253269244498261159, 0, 0.094031597257959398384, 0.091045702549339926901, 0, 0.052565260875419995978, 0, 0, 9.2621981986301462932e-18, 0, 0.47308734787878004013, 0, -0.42314218766081729273, 0, 0, 0, 0, -0, -0, -0, -0, -0, 0.27313710764801979458, -0, -0, 0, 0, 0, 0, -0.23654367393939002007, 0, -0.62583573544917614484, 0, 0, -0.091045702549339954657, 0, 0, 0, 0, 0.44253269244498261159, 0, 0.16726163588932227433, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.091045702549339926901, 0, 0, 0, 0, 0, 0, 0.47308734787878004013, 0, 0, 0, 0, -0.27313710764801979458, 0, 0, 0, 0, 0, 0, -0.66904654355728920834, 0, 0, 0, 0, 0, 0.28209479177387819515, -0.54627421529603958916, 0, -0.31539156525252004526, 0, 0, 0.62583573544917614484, 0, 0.47308734787878004013, 0, 0.31735664074561298342, 0, 0, 0, 0, 0, 0, 0, 0, -0.27313710764801979458, 0, 0, 0, 0, 0, 0, 0.50178490766796690625, 0, 0.44253269244498261159, 0, 0.094031597257959370628, -0.091045702549339926901, 0, 0.052565260875420016795, 0, 0, 9.262198198630141671e-18, 0, -0.47308734787878004013, 0, -0.42314218766081729273, 0, 0, 0, 0, -0, -0, -0, -0, -0.27313710764801979458, -0, -0, -0, 0, 0, 0, -0.66904654355728920834, 0, 0, 0, 0.28209479177387819515, 4.5022398477899912361e-18, -0, 0.63078313050504009052, -0, 0, 2.7789509525674121332e-17, -0, 2.3394324492700281759e-17, 0, 0.84628437532163458545, 0, 0, 0, 0 }; #if TIJK_TABLE_TYPE == 0 static const double _tijk_sym2esh_o6_d[28*28] ={ #else static const float _tijk_sym2esh_o6_f[28*28] ={ #endif 0.50641538597300450597, 0, 0, 1.5192461579190135179, 0, 1.5192461579190135179, 0, 0, 0, 0, 1.5192461579190135179, 0, 3.0384923158380270358, 0, 1.5192461579190135179, 0, 0, 0, 0, 0, 0, 0.50641538597300450597, 0, 1.5192461579190135179, 0, 1.5192461579190135179, 0, 0.50641538597300450597, 0.65377945204554921421, 0, 0, 0.65377945204554921421, 0, 1.3075589040910984284, 0, 0, 0, 0, -0.65377945204554921421, 0, 0, 0, 0.65377945204554921421, 0, 0, 0, 0, 0, 0, -0.65377945204554921421, 0, -1.3075589040910984284, 0, -0.65377945204554921421, 0, 0, 0, 0, -1.3075589040910984284, 0, 0, 0, 0, -2.6151178081821968568, 0, -2.6151178081821968568, 0, 0, 0, 0, 0, 0, -1.3075589040910984284, 0, -2.6151178081821968568, 0, -1.3075589040910984284, 0, 0, 0, 0, 0, 0, 0, -0.37745974262914383512, 0, 0, -1.1323792278874316164, 0, 0, 0, 0, 0, 0, -1.1323792278874316164, 0, 0, 0, 1.1323792278874316164, 0, 0, 0, 0, 0, 0, -0.37745974262914383512, 0, 0, 0, 1.1323792278874316164, 0, 0.75491948525828767025, 0, 0, 0, 0, -1.3075589040910984284, 0, 0, 0, 0, 0, 0, -2.6151178081821968568, 0, -2.6151178081821968568, 0, 0, 0, 0, 0, 0, 0, 0, -1.3075589040910984284, 0, -2.6151178081821968568, 0, -1.3075589040910984284, 0, 0, 1.3075589040910984284, 0, 0, 0, 0, 2.6151178081821968568, 0, 2.6151178081821968568, 0, 0, 0, 0, 0, 0, 1.3075589040910984284, 0, 2.6151178081821968568, 0, 1.3075589040910984284, 0, 0, 0, 0, 0, 0, 0, 0, 0.27236307516413466034, 0, 0, -1.3618153758206730242, 0, 0.27236307516413466034, 0, 0, 0, 0, -1.3618153758206730242, 0, -1.634178450984807851, 0, 0, 0, 0, 0, 0, 0, 0, 0.27236307516413466034, 0, 0.27236307516413466034, 0, 0, 0, 0, 0, 0, -0.77035910957352382589, 0, 0, 0, 0, 1.5407182191470476518, 0, -0.77035910957352382589, 0, 0, 0, 0, 0, 0, 2.3110773287205716997, 0, 2.3110773287205716997, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.20588713234316941003, 0, 0, -0.20588713234316941003, 0, 1.0294356617158468836, 0, 0, 0, 0, 0.20588713234316941003, 0, 0, 0, 1.2353227940590163492, 0, 0, 0, 0, 0, 0, 0.20588713234316941003, 0, -1.0294356617158468836, 0, -1.2353227940590163492, 0, 0, 0, 0, 0.8735051246334434305, 0, 0, 0, 0, 1.747010249266886861, 0, -0.29116837487781449534, 0, 0, 0, 0, 0, 0, 0.8735051246334434305, 0, -0.29116837487781449534, 0, -1.1646734995112579814, 0, 0, 0, 0, 0, 0, 0, 0.13811328708354669859, 0, 0, 0.41433986125064004025, 0, -0.96679300958482672357, 0, 0, 0, 0, 0.41433986125064004025, 0, -1.9335860191696534471, 0, -0.73660419777891572579, 0, 0, 0, 0, 0, 0, 0.13811328708354669859, 0, -0.96679300958482672357, 0, -0.73660419777891572579, 0, 0.3683020988894578629, 0, 0, 0, 0, 0.8735051246334434305, 0, 0, 0, 0, 0, 0, 1.747010249266886861, 0, -0.29116837487781449534, 0, 0, 0, 0, 0, 0, 0, 0, 0.8735051246334434305, 0, -0.29116837487781449534, 0, -1.1646734995112579814, 0, 0, -0.41177426468633882006, 0, 0, 0, 0, -0.82354852937267764013, 0, 2.0588713234316937672, 0, 0, 0, 0, 0, 0, -0.41177426468633882006, 0, 2.0588713234316937672, 0, 2.4706455881180326983, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2.3110773287205716997, 0, 0, 0, 0, 0, 0, -1.5407182191470476518, 0, -2.3110773287205716997, 0, 0, 0, 0, 0, 0, 0, 0, 0.77035910957352382589, 0, 0.77035910957352382589, 0, 0, 0, 0, 1.0894523006565386414, 0, 0, 0, 0, 0, 0, 1.0894523006565386414, 0, 0, 0, 0, 0, 0, -1.0894523006565386414, 0, -1.0894523006565386414, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.045741696509788543068, 0, 0, -0.68612544764682814602, 0, 0, 0, 0, 0, 0, 0.68612544764682814602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.045741696509788543068, 0, 0, 0, 0, 0, 0, 0, 0, -0.15845388475869950917, 0, 0, 0, 0, 1.5845388475869950362, 0, 0, 0, 0, 0, 0, 0, 0, -0.7922694237934975181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.033782481739884073768, 0, 0, 0.16891240869942036884, 0, 0.33782481739884073768, 0, 0, 0, 0, 0.16891240869942036884, 0, -2.0269489043930444261, 0, 0, 0, 0, 0, 0, 0, 0, -0.033782481739884073768, 0, 0.33782481739884073768, 0, 0, 0, 0, 0, 0, 0.18503427297440877553, 0, 0, 0, 0, -0.37006854594881755105, 0, -0.49342472793175679024, 0, 0, 0, 0, 0, 0, -0.5551028189232264376, 0, 1.4802741837952702042, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.03083904549573479939, 0, 0, 0.03083904549573479939, 0, -0.49342472793175679024, 0, 0, 0, 0, -0.03083904549573479939, 0, 0, 0, 0.49342472793175679024, 0, 0, 0, 0, 0, 0, -0.03083904549573479939, 0, 0.49342472793175679024, 0, -0.49342472793175679024, 0, 0, 0, 0, -0.19504324926415683716, 0, 0, 0, 0, -0.39008649852831367433, 0, 0.78017299705662734866, 0, 0, 0, 0, 0, 0, -0.19504324926415683716, 0, 0.78017299705662734866, 0, -0.31206919882265093946, 0, 0, 0, 0, 0, 0, 0, -0.021280963179598540863, 0, 0, -0.063842889538795632998, 0, 0.38305733723277379799, 0, 0, 0, 0, -0.063842889538795632998, 0, 0.76611467446554759597, 0, -0.51074311631036506398, 0, 0, 0, 0, 0, 0, -0.021280963179598540863, 0, 0.38305733723277379799, 0, -0.51074311631036506398, 0, 0.068099082174715330762, 0, 0, 0, 0, -0.19504324926415683716, 0, 0, 0, 0, 0, 0, -0.39008649852831367433, 0, 0.78017299705662734866, 0, 0, 0, 0, 0, 0, 0, 0, -0.19504324926415683716, 0, 0.78017299705662734866, 0, -0.31206919882265093946, 0, 0, 0.061678090991469598781, 0, 0, 0, 0, 0.12335618198293919756, 0, -0.98684945586351358049, 0, 0, 0, 0, 0, 0, 0.061678090991469598781, 0, -0.98684945586351358049, 0, 0.98684945586351358049, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5551028189232264376, 0, 0, 0, 0, 0, 0, 0.37006854594881755105, 0, -1.4802741837952702042, 0, 0, 0, 0, 0, 0, 0, 0, -0.18503427297440877553, 0, 0.49342472793175679024, 0, 0, 0, 0, -0.13512992695953629507, 0, 0, 0, 0, 0, 0, 1.3512992695953629507, 0, 0, 0, 0, 0, 0, 0.13512992695953629507, 0, -1.3512992695953629507, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.7922694237934975181, 0, 0, 0, 0, 0, 0, 1.5845388475869950362, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.15845388475869950917, 0, 0, 0, 0, 0, 0, 0.27445017905873125841, 0, 0, 0, 0, -0.91483393019577097238, 0, 0, 0, 0, 0, 0, 0, 0, 0.27445017905873125841, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; #if TIJK_TABLE_TYPE == 0 static const double _tijk_esh2sym_o6_d[28*28] ={ #else static const float _tijk_esh2sym_o6_f[28*28] ={ #endif 0.28209479177387813964, 0.54627421529603947814, -0, -0.31539156525252010077, 0, 0, 0.62583573544917625586, 0, -0.47308734787877981809, 0, 0.31735664074561292791, 0, 0, 0, 0, 0.68318410519191441477, -0, -0.50456490072872428598, -0, 0.46060262975746169012, -0, -0.3178460113381422758, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1820914050986798538, 0, 0, 0, 0, 0, 0, -0.15769578262626005039, 0, 0.41722382363278409656, 0, 0, 0, 0, 0, 0, 0, 0, 0.15353420991915392446, 0, -0.33637660048581613514, 0, 0.68318410519191441477, 0, 0, -0.1820914050986798538, 0, 0, 0, 0, -0.29502179496332175956, 0, 0.33452327177864454866, 0, 0, 0, 0, 0, 0, -0.39443652703862541742, 0, 0.46060262975746169012, 0, -0.48551780209894301876, 0, 0, 0, 0, 0, 0, 0, 0.056418958354775637642, 0.03641828101973600823, 0, -0.063078313050504070114, 0, 0, -0.20861191181639207604, 0, -0.031539156525252097507, 0, 0.063471328149122610562, 0, 0, 0, 0, -0.68318410519191441477, 0, 0.16818830024290809533, 0, 0.030706841983830814036, 0, -0.063569202267628480141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.036418281019735959658, 0, 0, 0, 0, 0, 0, 0.066904654355728898629, 0, -0.17701307697799306684, 0, 0, 0, 0, 0, 0, 0, 0, -0.097103560419788570446, 0, 0.27636157785447712509, 0, -0.3944365270386253064, 0, 0.056418958354775700093, 0.072836562039471974828, 0, 8.9555099447302666249e-17, 0, 0, 0.041722382363278368023, 0, 0.15769578262625988385, 0, -0.14809976568128604968, 0, 0, 0, 0, 8.2399365108898336985e-18, 0, 0.33637660048581619066, 0, -0.49130947174129246946, 0, 0.38141521360577063104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.10925484305920790673, 0, 0, 0, 0, 0, 0, -0.094617469575755980271, 0, 9.9611074597105719874e-19, 0, 0, 0, 0, 0, 0, 0, 0, 0.092120525951492338024, 0, 8.0230960763927328117e-18, 0, -0.68318410519191441477, 0, 0, -0.036418281019735994353, 0, 0, 0, 0, 0.059004358992664346362, 0, 0.066904654355728898629, 0, 0, 0, 0, 0, 0, 0.3944365270386253064, 0, -0.092120525951492393535, 0, -0.097103560419788514935, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0.036418281019735987414, 0, 0, 0, 0, 0, 0, 0.078847891313130025193, 0, 0.041722382363278409656, 0, -0, 0, -0, 0, -0, 0, 0, -0.24565473587064623473, 0, 0.33637660048581613514, 0, 0, 0, 0, -0.10925484305920793449, 0, 0, 0, 0, -0.088506538488996491787, 0, -0.033452327177864449315, 0, 0, 0, 0, 0, 0, 2.6319007737085620136e-17, 0, -0.36848210380596935209, 0, 0.5826213625187315337, 0, 0, 0, 0, 0, 0, 0, 0.056418958354775637642, -0.03641828101973600823, 0, -0.063078313050504000725, 0, 0, -0.20861191181639210379, 0, 0.031539156525251986485, 0, 0.063471328149122596685, 0, 0, 0, 0, 0.68318410519191441477, 0, 0.16818830024290809533, 0, -0.030706841983830793219, 0, -0.063569202267628466263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.036418281019736042925, 0, 0, 0, 0, 0, 0, 0.06690465435572895414, 0, -0.059004358992664346362, 0, 0, 0, 0, 0, 0, 0, 0, -0.097103560419788625957, 0, 0.092120525951492338024, 0, 0.3944365270386253064, 0, 0.018806319451591866493, 1.0110185258427328847e-17, 0, -1.3230654636112171296e-17, 0, 0, -0.0417223823632783819, 0, 1.3002294553532411925e-16, 0, -0.049366588560428713295, 0, 0, 0, 0, -1.3356015512305807746e-17, 0, -0.33637660048581619066, 0, -1.5700602710305711085e-17, 0, 0.12713840453525690477, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.036418281019735904147, 0, 0, 0, 0, 0, 0, -0.011150775725954853446, 0, -0.088506538488996519543, 0, 0, 0, 0, 0, 0, 0, 0, 0.19420712083957714089, 0, -0.36848210380596946312, 0, 2.1900883884207189567e-17, 0, 0.056418958354775637642, 0.036418281019735987414, 0, 0.063078313050504000725, 0, 0, -3.4196600136376111153e-18, 0, 0.1892349391515119883, 0, -0.11283791670955127528, 0, 0, 0, 0, -2.5949050786116869342e-18, 0, 2.7570180441496557009e-18, 0, 0.49130947174129246946, 0, -0.50855361814102739704, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1820914050986798538, 0, 0, 0, 0, 0, 0, -0.15769578262626005039, 0, -0.41722382363278409656, 0, 0, 0, 0, 0, 0, 0, 0, 0.15353420991915392446, 0, 0.33637660048581613514, 0, 0.68318410519191441477, -0, 0, -0.036418281019735987414, 0, 0, 0, -0, 0.17701307697799303909, -0, 0.066904654355728884751, -0, 0, 0, 0, 0, 0, -0.3944365270386253064, 0, -0.27636157785447706958, 0, -0.097103560419788570446, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.036418281019735959658, 0, 0, 0, 0, 0, 0, 0.078847891313130025193, 0, -0.041722382363278409656, 0, 0, 0, 0, 0, 0, 0, 0, -0.24565473587064623473, 0, -0.33637660048581613514, 0, 0, 0, 0, -0.036418281019735924964, 0, 0, 0, 0, 0.08850653848899653342, 0, -0.011150775725954797934, 0, 0, 0, 0, 0, 0, -1.1722935989999516693e-17, 0, 0.36848210380596940761, 0, 0.19420712083957716865, 0, 0, 0, 0, 0, 0, 0, -0, -0, -0, -0, -0, 0.036418281019735966597, -0, -0, -0, -0, -0, -0, 0.18923493915151196054, -0, -1.9913267072906534227e-18, -0, -0, -0, -0, -0, -0, -0, -0, 0.49130947174129246946, 0, -1.6054589175247916872e-17, 0, 0, 0, 0, -0.1820914050986798538, 0, 0, 0, 0, -6.4878668978875960741e-17, 0, -0.44603102903819275005, 0, 0, 0, 0, 0, 0, 1.3271784590120033334e-17, 0, 3.4095831958807153212e-17, 0, -0.77682848335830878561, 0, 0, 0, 0, 0, 0, 0, 0.28209479177387813964, -0.54627421529603947814, -0, -0.31539156525252015628, -0, -0, 0.62583573544917625586, -0, 0.47308734787878015116, -0, 0.31735664074561292791, -0, -0, -0, -0, -0.68318410519191441477, -0, -0.50456490072872428598, -0, -0.46060262975746169012, -0, -0.3178460113381422758, -0, -0, 0, 0, 0, 0, -0, -0, -0, -0, -0.18209140509867988156, -0, -0, -0, -0, -0, -0, 0.33452327177864449315, 0, 0.29502179496332175956, 0, -0, -0, -0, -0, -0, -0, -0, -0.48551780209894290774, -0, -0.46060262975746180114, 0, -0.3944365270386253064, 0, 0.056418958354775693154, -0.072836562039471988705, 0, 8.9555099447302666249e-17, 0, 0, 0.0417223823632783819, 0, -0.15769578262626013365, 0, -0.14809976568128604968, 0, 0, 0, 0, 1.4203048459560108085e-17, 0, 0.33637660048581613514, 0, 0.49130947174129246946, 0, 0.38141521360577063104, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, -0.10925484305920793449, 0, 0, 0, 0, 0, 0, -0.033452327177864449315, 0, 0.088506538488996519543, 0, 0, -0, 0, -0, 0, -0, 0, 0.5826213625187315337, 0, 0.36848210380596946312, 0, -2.1900883884207189567e-17, 0, 0.056418958354775637642, -0.036418281019735987414, 0, 0.063078313050503986847, 0, 0, -3.4113257779765173673e-18, 0, -0.1892349391515119883, 0, -0.11283791670955127528, 0, 0, 0, 0, -1.4522307270911557893e-17, 0, 2.7502987685461758766e-18, 0, -0.49130947174129246946, 0, -0.50855361814102739704, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.18209140509867982605, -0, 0, 0, 0, 0, 0, -0.44603102903819269454, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.77682848335830867459, -0, 0, 0, 0, 0, 0.28209479177387813964, 2.9630111695848276965e-22, 0, 0.63078313050504009052, 0, 0, -4.080980515248682738e-17, 0, 1.5216940301200840144e-17, 0, 0.84628437532163458545, 0, 0, 0, 0, 6.4189546310712161745e-17, 0, 3.2901916779719101699e-17, 0, 3.1736161082457363656e-17, 0, 1.017107236282054572, 0, 0, 0, 0, 0, 0 }; #if TIJK_TABLE_TYPE == 0 static const double _tijk_sym2esh_o8_d[45*45] ={ #else static const float _tijk_sym2esh_o8_f[45*45] ={ #endif 0.3938786335345590417, 0, 0, 1.5755145341382361668, 0, 1.5755145341382361668, 0, 0, 0, 0, 2.3632718012073543612, 0, 4.7265436024147087224, 0, 2.3632718012073543612, 0, 0, 0, 0, 0, 0, 1.5755145341382361668, 0, 4.7265436024147087224, 0, 4.7265436024147087224, 0, 1.5755145341382361668, 0, 0, 0, 0, 0, 0, 0, 0, 0.3938786335345590417, 0, 1.5755145341382361668, 0, 2.3632718012073543612, 0, 1.5755145341382361668, 0, 0.3938786335345590417, 0.5547219593113751346, 0, 0, 1.1094439186227502692, 0, 1.6641658779341255148, 0, 0, 0, 0, 0, 0, 1.6641658779341255148, 0, 1.6641658779341255148, 0, 0, 0, 0, 0, 0, -1.1094439186227502692, 0, -1.6641658779341255148, 0, 0, 0, 0.5547219593113751346, 0, 0, 0, 0, 0, 0, 0, 0, -0.5547219593113751346, 0, -1.6641658779341255148, 0, -1.6641658779341255148, 0, -0.5547219593113751346, 0, 0, 0, 0, -1.1094439186227502692, 0, 0, 0, 0, -3.3283317558682510295, 0, -3.3283317558682510295, 0, 0, 0, 0, 0, 0, -3.3283317558682510295, 0, -6.6566635117365020591, 0, -3.3283317558682510295, 0, 0, 0, 0, 0, 0, 0, 0, -1.1094439186227502692, 0, -3.3283317558682510295, 0, -3.3283317558682510295, 0, -1.1094439186227502692, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.3202688725338190823, 0, 0, -1.2810754901352763291, 0, -0.3202688725338190823, 0, 0, 0, 0, -1.9216132352029144936, 0, -0.9608066176014572468, 0, 0.9608066176014572468, 0, 0, 0, 0, 0, 0, -1.2810754901352763291, 0, -0.9608066176014572468, 0, 1.9216132352029144936, 0, 1.6013443626690953003, 0, 0, 0, 0, 0, 0, 0, 0, -0.3202688725338190823, 0, -0.3202688725338190823, 0, 0.9608066176014572468, 0, 1.6013443626690953003, 0, 0.6405377450676381645, 0, 0, 0, 0, -1.1094439186227502692, 0, 0, 0, 0, 0, 0, -3.3283317558682510295, 0, -3.3283317558682510295, 0, 0, 0, 0, 0, 0, 0, 0, -3.3283317558682510295, 0, -6.6566635117365020591, 0, -3.3283317558682510295, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1.1094439186227502692, 0, -3.3283317558682510295, 0, -3.3283317558682510295, 0, -1.1094439186227502692, 0, 0, 1.1094439186227502692, 0, 0, 0, 0, 3.3283317558682510295, 0, 3.3283317558682510295, 0, 0, 0, 0, 0, 0, 3.3283317558682510295, 0, 6.6566635117365020591, 0, 3.3283317558682510295, 0, 0, 0, 0, 0, 0, 0, 0, 1.1094439186227502692, 0, 3.3283317558682510295, 0, 3.3283317558682510295, 0, 1.1094439186227502692, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2933140809459911513, 0, 0, -1.1732563237839646053, 0, 0.5866281618919823027, 0, 0, 0, 0, -2.9331408094599118463, 0, -2.9331408094599118463, 0, 0.2933140809459911513, 0, 0, 0, 0, 0, 0, -1.1732563237839646053, 0, -2.9331408094599118463, 0, -1.7598844856759472410, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2933140809459911513, 0, 0.5866281618919823027, 0, 0.2933140809459911513, 0, 0, 0, 0, 0, 0, -0.8296175026176411116, 0, 0, 0, 0, 0.8296175026176411116, 0, -1.6592350052352822232, 0, 0, 0, 0, 0, 0, 4.1480875130882060020, 0, 3.3184700104705644463, 0, -0.8296175026176411116, 0, 0, 0, 0, 0, 0, 0, 0, 2.4888525078529233348, 0, 4.9777050157058466695, 0, 2.4888525078529233348, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.2217246040618747471, 0, 0, -0.4434492081237494943, 0, 0.8868984162474989885, 0, 0, 0, 0, 0, 0, 0.8868984162474989885, 0, 2.4389706446806220796, 0, 0, 0, 0, 0, 0, 0.4434492081237494943, 0, -0.8868984162474989885, 0, 0, 0, 1.3303476243712484273, 0, 0, 0, 0, 0, 0, 0, 0, 0.2217246040618747471, 0, -0.8868984162474989885, 0, -2.4389706446806220796, 0, -1.3303476243712484273, 0, 0, 0, 0, 0.9406978265283237883, 0, 0, 0, 0, 2.8220934795849714760, 0, 0.6271318843522157849, 0, 0, 0, 0, 0, 0, 2.8220934795849714760, 0, 1.2542637687044315697, 0, -1.5678297108805396842, 0, 0, 0, 0, 0, 0, 0, 0, 0.9406978265283237883, 0, 0.6271318843522157849, 0, -1.5678297108805396842, 0, -1.2542637687044315697, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1487373860899733613, 0, 0, 0.5949495443598934452, 0, -0.8924243165398402233, 0, 0, 0, 0, 0.8924243165398402233, 0, -2.6772729496195202259, 0, -1.8344277617763382615, 0, 0, 0, 0, 0, 0, 0.5949495443598934452, 0, -2.6772729496195202259, 0, -3.6688555235526765230, 0, -0.3966330295732622968, 0, 0, 0, 0, 0, 0, 0, 0, 0.1487373860899733613, 0, -0.8924243165398402233, 0, -1.8344277617763382615, 0, -0.3966330295732622968, 0, 0.3966330295732622968, 0, 0, 0, 0, 0.9406978265283237883, 0, 0, 0, 0, 0, 0, 2.8220934795849714760, 0, 0.6271318843522157849, 0, 0, 0, 0, 0, 0, 0, 0, 2.8220934795849714760, 0, 1.2542637687044315697, 0, -1.5678297108805396842, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.9406978265283237883, 0, 0.6271318843522157849, 0, -1.5678297108805396842, 0, -1.2542637687044315697, 0, 0, -0.4434492081237494943, 0, 0, 0, 0, -1.3303476243712484273, 0, 1.7737968324949979770, 0, 0, 0, 0, 0, 0, -1.3303476243712484273, 0, 3.5475936649899959541, 0, 4.8779412893612441593, 0, 0, 0, 0, 0, 0, 0, 0, -0.4434492081237494943, 0, 1.7737968324949979770, 0, 4.8779412893612441593, 0, 2.6606952487424968545, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2.4888525078529233348, 0, 0, 0, 0, 0, 0, -4.1480875130882060020, 0, -4.9777050157058466695, 0, 0, 0, 0, 0, 0, 0, 0, -0.8296175026176411116, 0, -3.3184700104705644463, 0, -2.4888525078529233348, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.8296175026176411116, 0, 1.6592350052352822232, 0, 0.8296175026176411116, 0, 0, 0, 0, 1.1732563237839646053, 0, 0, 0, 0, 1.1732563237839646053, 0, 2.3465126475679292106, 0, 0, 0, 0, 0, 0, -1.1732563237839646053, 0, 0, 0, 1.1732563237839646053, 0, 0, 0, 0, 0, 0, 0, 0, -1.1732563237839646053, 0, -2.3465126475679292106, 0, -1.1732563237839646053, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0853845001516052859, 0, 0, -1.1953830021224738367, 0, 0.0853845001516052859, 0, 0, 0, 0, 0, 0, -1.2807675022740792059, 0, 0, 0, 0, 0, 0, 0, 0, 1.1953830021224738367, 0, 1.2807675022740792059, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0853845001516052859, 0, -0.0853845001516052859, 0, 0, 0, 0, 0, 0, 0, 0, -0.2957805848829057060, 0, 0, 0, 0, 2.6620252639461514654, 0, -0.2957805848829057060, 0, 0, 0, 0, 0, 0, 1.4789029244145286413, 0, 2.9578058488290572825, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1.4789029244145286413, 0, -1.4789029244145286413, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0630606325811169377, 0, 0, 0.2522425303244677508, 0, 0.5675456932300524393, 0, 0, 0, 0, 0.6306063258111693770, 0, -2.8377284661502617524, 0, 0.6306063258111693770, 0, 0, 0, 0, 0, 0, 0.2522425303244677508, 0, -2.8377284661502617524, 0, -3.7836379548670162620, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0630606325811169377, 0, 0.5675456932300524393, 0, 0.6306063258111693770, 0, 0, 0, 0, 0, 0, 0.3453973095522297476, 0, 0, 0, 0, -0.3453973095522297476, 0, -0.5756621825870495979, 0, 0, 0, 0, 0, 0, -1.7269865477611487936, 0, 1.1513243651740991957, 0, -0.9210594921392794010, 0, 0, 0, 0, 0, 0, 0, 0, -1.0361919286566891873, 0, 1.7269865477611487936, 0, 2.7631784764178379810, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0575662182587049626, 0, 0, 0.1151324365174099251, 0, -0.8634932738805743968, 0, 0, 0, 0, 0, 0, -0.8634932738805743968, 0, 0, 0, 0, 0, 0, 0, 0, -0.1151324365174099251, 0, 0.8634932738805743968, 0, 0, 0, 0.9210594921392794010, 0, 0, 0, 0, 0, 0, 0, 0, -0.0575662182587049626, 0, 0.8634932738805743968, 0, 0, 0, -0.9210594921392794010, 0, 0, 0, 0, -0.3640807319597594849, 0, 0, 0, 0, -1.0922421958792785102, 0, 1.0922421958792785102, 0, 0, 0, 0, 0, 0, -1.0922421958792785102, 0, 2.1844843917585570203, 0, 0.8737937567034227415, 0, 0, 0, 0, 0, 0, 0, 0, -0.3640807319597594849, 0, 1.0922421958792785102, 0, 0.8737937567034227415, 0, -0.5825291711356151980, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0397244646019172878, 0, 0, -0.1588978584076691514, 0, 0.6753158982325937476, 0, 0, 0, 0, -0.2383467876115037132, 0, 2.0259476946977814649, 0, -0.2383467876115037132, 0, 0, 0, 0, 0, 0, -0.1588978584076691514, 0, 2.0259476946977814649, 0, -0.4766935752230074264, 0, -0.8262688637198795094, 0, 0, 0, 0, 0, 0, 0, 0, -0.0397244646019172878, 0, 0.6753158982325937476, 0, -0.2383467876115037132, 0, -0.8262688637198795094, 0, 0.1271182867261353155, 0, 0, 0, 0, -0.3640807319597594849, 0, 0, 0, 0, 0, 0, -1.0922421958792785102, 0, 1.0922421958792785102, 0, 0, 0, 0, 0, 0, 0, 0, -1.0922421958792785102, 0, 2.1844843917585570203, 0, 0.8737937567034227415, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.3640807319597594849, 0, 1.0922421958792785102, 0, 0.8737937567034227415, 0, -0.5825291711356151980, 0, 0, 0.1151324365174099251, 0, 0, 0, 0, 0.3453973095522297476, 0, -1.7269865477611487936, 0, 0, 0, 0, 0, 0, 0.3453973095522297476, 0, -3.4539730955222975872, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1151324365174099251, 0, -1.7269865477611487936, 0, 0, 0, 1.8421189842785588020, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0361919286566891873, 0, 0, 0, 0, 0, 0, 1.7269865477611487936, 0, -1.7269865477611487936, 0, 0, 0, 0, 0, 0, 0, 0, 0.3453973095522297476, 0, -1.1513243651740991957, 0, -2.7631784764178379810, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.3453973095522297476, 0, 0.5756621825870495979, 0, 0.9210594921392794010, 0, 0, 0, 0, -0.2522425303244677508, 0, 0, 0, 0, -0.2522425303244677508, 0, 2.2701827729202097572, 0, 0, 0, 0, 0, 0, 0.2522425303244677508, 0, 0, 0, 2.5224253032446775080, 0, 0, 0, 0, 0, 0, 0, 0, 0.2522425303244677508, 0, -2.2701827729202097572, 0, -2.5224253032446775080, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1.4789029244145286413, 0, 0, 0, 0, 0, 0, 1.4789029244145286413, 0, -1.4789029244145286413, 0, 0, 0, 0, 0, 0, 0, 0, 2.6620252639461514654, 0, 2.9578058488290572825, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.2957805848829057060, 0, -0.2957805848829057060, 0, 0, 0, 0, 0, 0, 0.5123070009096316602, 0, 0, 0, 0, -1.1953830021224738367, 0, 0.5123070009096316602, 0, 0, 0, 0, 0, 0, -1.1953830021224738367, 0, -1.7076900030321056079, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5123070009096316602, 0, 0.5123070009096316602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0107178135014655736, 0, 0, -0.3000987780410361294, 0, 0, 0, 0, 0, 0, 0.7502469451025902680, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.3000987780410361294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0107178135014655736, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0428712540058622943, 0, 0, 0, 0, 0.9002963341231082772, 0, 0, 0, 0, 0, 0, 0, 0, -1.5004938902051805361, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.3000987780410361294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0078271842958481646, 0, 0, 0.1095805801418743008, 0, 0.1095805801418743008, 0, 0, 0, 0, 0, 0, -1.6437087021281144850, 0, 0, 0, 0, 0, 0, 0, 0, -0.1095805801418743008, 0, 1.6437087021281144850, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0078271842958481646, 0, -0.1095805801418743008, 0, 0, 0, 0, 0, 0, 0, 0, 0.0507259518200420742, 0, 0, 0, 0, -0.4565335663803786748, 0, -0.2029038072801682968, 0, 0, 0, 0, 0, 0, -0.2536297591002103502, 0, 2.0290380728016828016, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2536297591002103502, 0, -1.0145190364008414008, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0070344238570722133, 0, 0, -0.0281376954282888531, 0, -0.1688261725697331461, 0, 0, 0, 0, -0.0703442385707221396, 0, 0.8441308628486656751, 0, 0.2813769542828885584, 0, 0, 0, 0, 0, 0, -0.0281376954282888531, 0, 0.8441308628486656751, 0, -1.6882617256973313502, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0070344238570722133, 0, -0.1688261725697331461, 0, 0.2813769542828885584, 0, 0, 0, 0, 0, 0, -0.0544884128972096607, 0, 0, 0, 0, 0.0544884128972096607, 0, 0.3632560859813976362, 0, 0, 0, 0, 0, 0, 0.2724420644860482410, 0, -0.7265121719627952723, 0, -0.2906048687851181533, 0, 0, 0, 0, 0, 0, 0, 0, 0.1634652386916289613, 0, -1.0897682579441929640, 0, 0.8718146063553545710, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0067070599846150393, 0, 0, -0.0134141199692300785, 0, 0.2012117995384511693, 0, 0, 0, 0, 0, 0, 0.2012117995384511693, 0, -0.5365647987692031551, 0, 0, 0, 0, 0, 0, 0.0134141199692300785, 0, -0.2012117995384511693, 0, 0, 0, 0.2146259195076812565, 0, 0, 0, 0, 0, 0, 0, 0, 0.0067070599846150393, 0, -0.2012117995384511693, 0, 0.5365647987692031551, 0, -0.2146259195076812565, 0, 0, 0, 0, 0.0561152898469365544, 0, 0, 0, 0, 0.1683458695408096562, 0, -0.4489223187754924349, 0, 0, 0, 0, 0, 0, 0.1683458695408096562, 0, -0.8978446375509848698, 0, 0.5387067825305910107, 0, 0, 0, 0, 0, 0, 0, 0, 0.0561152898469365544, 0, -0.4489223187754924349, 0, 0.5387067825305910107, 0, -0.1026108157201125609, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0046762741539113795, 0, 0, 0.0187050966156455181, 0, -0.1496407729251641450, 0, 0, 0, 0, 0.0280576449234682772, 0, -0.4489223187754924349, 0, 0.4489223187754924349, 0, 0, 0, 0, 0, 0, 0.0187050966156455181, 0, -0.4489223187754924349, 0, 0.8978446375509848698, 0, -0.2394252366802626375, 0, 0, 0, 0, 0, 0, 0, 0, 0.0046762741539113795, 0, -0.1496407729251641450, 0, 0.4489223187754924349, 0, -0.2394252366802626375, 0, 0.0171018026200187613, 0, 0, 0, 0, 0.0561152898469365544, 0, 0, 0, 0, 0, 0, 0.1683458695408096562, 0, -0.4489223187754924349, 0, 0, 0, 0, 0, 0, 0, 0, 0.1683458695408096562, 0, -0.8978446375509848698, 0, 0.5387067825305910107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0561152898469365544, 0, -0.4489223187754924349, 0, 0.5387067825305910107, 0, -0.1026108157201125609, 0, 0, -0.0134141199692300785, 0, 0, 0, 0, -0.0402423599076902339, 0, 0.4024235990769023386, 0, 0, 0, 0, 0, 0, -0.0402423599076902339, 0, 0.8048471981538046771, 0, -1.0731295975384063102, 0, 0, 0, 0, 0, 0, 0, 0, -0.0134141199692300785, 0, 0.4024235990769023386, 0, -1.0731295975384063102, 0, 0.4292518390153625130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.1634652386916289613, 0, 0, 0, 0, 0, 0, -0.2724420644860482410, 0, 1.0897682579441929640, 0, 0, 0, 0, 0, 0, 0, 0, -0.0544884128972096607, 0, 0.7265121719627952723, 0, -0.8718146063553545710, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0544884128972096607, 0, -0.3632560859813976362, 0, 0.2906048687851181533, 0, 0, 0, 0, 0.0281376954282888531, 0, 0, 0, 0, 0.0281376954282888531, 0, -0.6753046902789325845, 0, 0, 0, 0, 0, 0, -0.0281376954282888531, 0, 0, 0, 1.1255078171315542335, 0, 0, 0, 0, 0, 0, 0, 0, -0.0281376954282888531, 0, 0.6753046902789325845, 0, -1.1255078171315542335, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2536297591002103502, 0, 0, 0, 0, 0, 0, -0.2536297591002103502, 0, -1.0145190364008414008, 0, 0, 0, 0, 0, 0, 0, 0, -0.4565335663803786748, 0, 2.0290380728016828016, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0507259518200420742, 0, -0.2029038072801682968, 0, 0, 0, 0, 0, 0, -0.0469631057750889910, 0, 0, 0, 0, 0.1095805801418743008, 0, 0.6574834808512457496, 0, 0, 0, 0, 0, 0, 0.1095805801418743008, 0, -2.1916116028374861280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0469631057750889910, 0, 0.6574834808512457496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.3000987780410361294, 0, 0, 0, 0, 0, 0, 1.5004938902051805361, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.9002963341231082772, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0428712540058622943, 0, 0, 0, 0, 0, 0, 0, 0, 0.0857425080117245886, 0, 0, 0, 0, -0.6001975560820722588, 0, 0, 0, 0, 0, 0, 0, 0, 0.6001975560820722588, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0857425080117245886, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; #if TIJK_TABLE_TYPE == 0 static const double _tijk_esh2sym_o8_d[45*45] ={ #else static const float _tijk_esh2sym_o8_f[45*45] ={ #endif 0.2820947917738783062, 0.5462742152960393671, 0, -0.3153915652525198787, 0, 0, 0.6258357354491760338, 0, -0.4730873478787803732, 0, 0.3173566407456129279, 0, 0, 0, 0, 0.6831841051919145258, 0, -0.5045649007287240639, 0, 0.4606026297574616346, 0, -0.3178460113381420538, 0, 0, 0, 0, 0, 0, 0.7289266601748296548, 0, -0.5323327660595424948, 0, 0.4784165247593306636, 0, -0.4561522584349099185, 0, 0.3180369672047742569, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1365685538240098695, 0, 0, 0, 0, 0, 0, -0.1182718369696949268, 0, 0.3129178677245880724, 0, 0, 0, 0, 0, 0, 0, 0, 0.1151506574393653948, 0, -0.2522824503643620875, 0, 0.5123880788939360054, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.1140380646087272576, 0, 0.2392082623796658314, 0, -0.3992495745446568156, 0, 0.7289266601748292107, 0, 0, -0.1365685538240099806, 0, 0, 0, 0, -0.2212663462224912780, 0, 0.2508924538339833976, 0, 0, 0, 0, 0, 0, -0.2958273952789691186, 0, 0.3454519723180963231, 0, -0.3641383515742072086, 0, 0, 0, 0, 0, 0, 0, 0, -0.3644633300874148829, 0, 0.4312388277622636323, 0, -0.4632248082358288999, 0, 0.4770554508071623845, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0402992559676969356, 0.0390195868068598586, 0, -0.0450559378932172336, 0, 0, -0.0894051050641678779, 0, -0.0337919534199127569, 0, 0.0453366629636590412, 0, 0, 0, 0, -0.3415920525959573739, 0, 0.0720807001041033624, 0, 0.0329001878398185940, 0, -0.0454065730483060453, 0, 0, 0, 0, 0, 0, -0.7289266601748296548, 0, 0.2661663830297712474, 0, -0.0683452178227613449, 0, -0.0325823041739216890, 0, 0.0454338524578253994, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0195097934034299814, 0, 0, 0, 0, 0, 0, 0.0358417791191404933, 0, -0.0948284340953533256, 0, 0, 0, 0, 0, 0, 0, 0, -0.0520197645106010090, 0, 0.1480508452791841623, 0, -0.2113052823421207871, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0681507786867375787, 0, -0.1985249178153554173, 0, 0.3080277341159026183, 0, -0.3644633300874147719, 0, 0.0402992559676968176, 0.0585293802102899718, 0, -0.0112639844733044003, 0, 0, 0.0447025525320839945, 0, 0.0675839068398257081, 0, -0.0680049944454885757, 0, 0, 0, 0, 0.0243994323282826740, 0, 0.1621815752342328221, 0, -0.2467514087986400761, 0, 0.1929779354553007309, 0, 0, 0, 0, 0, 0, -0.0000000000000000083, 0, 0.2661663830297713584, 0, -0.4100713069365690688, 0, 0.4887345626088321904, 0, -0.3634708196625991428, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0585293802102899094, 0, 0, 0, 0, 0, 0, -0.0506879301298692603, 0, 0.0447025525320840708, 0, 0, 0, 0, 0, 0, 0, 0, 0.0493502817597281027, 0, -0.0360403500520517089, 0, -0.1707960262979787147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0488734562608832898, 0, 0.0341726089113805059, 0, 0.1330831915148854849, 0, -0.7289266601748296548, 0, 0, -0.0195097934034299848, 0, 0, 0, 0, 0.0105364926772614741, 0, 0.0358417791191404655, 0, 0, 0, 0, 0, 0, 0.1267831694052724278, 0, -0.0164500939199093248, 0, -0.0520197645106009882, 0, 0, 0, 0, 0, 0, 0, 0, 0.3644633300874148829, 0, -0.1848166404695416043, 0, 0.0220583242017060796, 0, 0.0681507786867374815, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0195097934034299848, 0, 0, 0, 0, 0, 0, 0.0225279689466085474, 0, 0.0298017016880559905, 0, 0, 0, 0, 0, 0, 0, 0, -0.0822504695995468216, 0, 0.1081210501561551129, 0, 0.0243994323282826706, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1629115208696105821, 0, -0.2733808712910460459, 0, 0.2661663830297714139, 0, 0.0000000000000000048, 0, 0, -0.0585293802102899163, 0, 0, 0, 0, -0.0632189560635689624, 0, 0.0238945194127604121, 0, 0, 0, 0, 0, 0, -0.0422610564684241588, 0, -0.0822504695995467661, 0, 0.1560592935318031449, 0, 0, 0, 0, 0, 0, 0, 0, -0.0000000000000000093, 0, -0.2464221872927218893, 0, 0.4411664840341230076, 0, -0.5452062294938996301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0241795535806180080, 0.0000000000000001414, 0, -0.0270335627359302930, 0, 0, -0.0894051050641681139, 0, 0.0000000000000000117, 0, 0.0272019977781953783, 0, 0, 0, 0, 0.0000000000000000469, 0, 0.0720807001041035289, 0, 0.0000000000000001514, 0, -0.0272439438289834787, 0, 0, 0, 0, 0, 0, 0.7289266601748297658, 0, -0.0000000000000000097, 0, -0.0683452178227615809, 0, -0.0000000000000001077, 0, 0.0272603114746949211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0117058760420579878, 0, 0, 0, 0, 0, 0, 0.0215050674714842988, 0, -0.0316094780317844465, 0, 0, 0, 0, 0, 0, 0, 0, -0.0312118587063606109, 0, 0.0493502817597280610, 0, 0.0422610564684241310, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0408904672120424598, 0, -0.0661749726051184539, 0, -0.0616055468231805556, 0, 0.3644633300874148274, 0, 0.0080598511935393580, 0.0039019586806860237, 0, -0.0022527968946608450, 0, 0, -0.0149008508440280161, 0, 0.0045055937893217646, 0, -0.0136009988890976180, 0, 0, 0, 0, -0.0243994323282826567, 0, -0.0540605250780776467, 0, -0.0164500939199094080, 0, 0.0385955870910601531, 0, 0, 0, 0, 0, 0, -0.0000000000000000016, 0, -0.2661663830297713029, 0, 0.1366904356455231062, 0, 0.0325823041739220845, 0, -0.0726941639325199285, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0117058760420579843, 0, 0, 0, 0, 0, 0, 0.0047789038825520637, 0, -0.0379313736381413774, 0, 0, 0, 0, 0, 0, 0, 0, 0.0312118587063606040, 0, -0.0493502817597280818, 0, -0.0422610564684241588, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.1090412458987799343, 0, 0.2646998904204737046, 0, -0.2464221872927220558, 0, -0.0000000000000000017, 0, 0.0241795535806180392, 0.0234117520841159929, 0, 0.0135167813679649557, 0, 0, 0.0089405105064168100, 0, 0.0743422975238083317, 0, -0.0559152176551795943, 0, 0, 0, 0, 0.0000000000000000039, 0, 0.0720807001041034734, 0, 0.0000000000000001283, 0, -0.0272439438289837077, 0, 0, 0, 0, 0, 0, 0.0000000000000000034, 0, -0.0000000000000000071, 0, 0.2733808712910461014, 0, -0.5213168667827535740, 0, 0.4361649835951196819, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0585293802102899996, 0, 0, 0, 0, 0, 0, -0.0506879301298693019, 0, -0.0447025525320840847, 0, 0, 0, 0, 0, 0, 0, 0, 0.0493502817597280402, 0, 0.0360403500520517228, 0, -0.1707960262979785204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0488734562608831649, 0, -0.0341726089113807349, 0, 0.1330831915148857070, 0, 0.7289266601748297658, 0, 0, -0.0117058760420580034, 0, 0, 0, 0, 0.0316094780317845020, 0, 0.0215050674714842988, 0, 0, 0, 0, 0, 0, 0.0422610564684241241, 0, -0.0493502817597280333, 0, -0.0312118587063606109, 0, 0, 0, 0, 0, 0, 0, 0, -0.3644633300874148829, 0, -0.0616055468231805001, 0, 0.0661749726051184400, 0, 0.0408904672120424528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0117058760420579878, 0, 0, 0, 0, 0, 0, 0.0135167813679651361, 0, 0.0000000000000000009, 0, 0, 0, 0, 0, 0, 0, 0, -0.0493502817597280402, 0, 0.0000000000000000194, 0, -0.0243994323282826601, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0977469125217662743, 0, -0.0000000000000000244, 0, -0.2661663830297713584, 0, 0.0000000000000000139, 0, 0, -0.0117058760420579930, 0, 0, 0, 0, 0.0126437912127137994, 0, 0.0047789038825520672, 0, 0, 0, 0, 0, 0, 0.0422610564684241449, 0, 0.0164500939199093595, 0, 0.0312118587063606318, 0, 0, 0, 0, 0, 0, 0, 0, 0.0000000000000000102, 0, 0.2464221872927219725, 0, -0.0882332968068246654, 0, -0.1090412458987799343, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0117058760420579947, 0, 0, 0, 0, 0, 0, 0.0371711487619041658, 0, 0.0089405105064168013, 0, 0, 0, 0, 0, 0, 0, 0, 0.0000000000000000278, 0, 0.0720807001041034595, 0, 0.0000000000000000033, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.2606584333913767870, 0, 0.2733808712910461569, 0, 0.0000000000000000168, 0, 0.0000000000000000070, 0, 0, -0.0585293802102899718, 0, 0, 0, 0, -0.0316094780317844812, 0, -0.0597362985319008916, 0, 0, 0, 0, 0, 0, 0.0000000000000000051, 0, -0.1316007513592747924, 0, 0.1248474348254423050, 0, 0, 0, 0, 0, 0, 0, 0, 0.0000000000000000025, 0, -0.0000000000000000292, 0, -0.3529331872272983839, 0, 0.6542474753926794673, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0402992559676970882, -0.0390195868068601917, 0, -0.0450559378932170809, 0, 0, -0.0894051050641678502, 0, 0.0337919534199128332, 0, 0.0453366629636590968, 0, 0, 0, 0, 0.3415920525959571519, 0, 0.0720807001041034456, 0, -0.0329001878398188230, 0, -0.0454065730483060037, 0, 0, 0, 0, 0, 0, -0.7289266601748298768, 0, -0.2661663830297711919, 0, -0.0683452178227612200, 0, 0.0325823041739225980, 0, 0.0454338524578254827, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0195097934034299744, 0, 0, 0, 0, 0, 0, 0.0358417791191405002, 0, -0.0105364926772614949, 0, 0, 0, 0, 0, 0, 0, 0, -0.0520197645106010020, 0, 0.0164500939199093248, 0, 0.1267831694052724278, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0681507786867374676, 0, -0.0220583242017061421, 0, -0.1848166404695414655, 0, -0.3644633300874148274, 0, 0.0080598511935393892, -0.0039019586806860198, 0, -0.0022527968946608728, 0, 0, -0.0149008508440279831, 0, -0.0045055937893217187, 0, -0.0136009988890977290, 0, 0, 0, 0, 0.0243994323282826428, 0, -0.0540605250780775218, 0, 0.0164500939199095086, 0, 0.0385955870910602294, 0, 0, 0, 0, 0, 0, -0.0000000000000000045, 0, 0.2661663830297711364, 0, 0.1366904356455228564, 0, -0.0325823041739222719, 0, -0.0726941639325201089, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0117058760420579930, 0, 0, 0, 0, 0, 0, 0.0047789038825520533, 0, -0.0126437912127138011, 0, 0, 0, 0, 0, 0, 0, 0, 0.0312118587063605901, 0, -0.0164500939199093664, 0, 0.0422610564684241588, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.1090412458987799760, 0, 0.0882332968068245682, 0, 0.2464221872927220280, 0, -0.0000000000000000139, 0, 0.0080598511935393961, -0.0000000000000000059, 0, 0.0045055937893217456, 0, 0, -0.0089405105064168065, 0, -0.0000000000000000416, 0, -0.0186384058850598139, 0, 0, 0, 0, 0.0000000000000000015, 0, -0.0720807001041034456, 0, -0.0000000000000000757, 0, -0.0090813146096612660, 0, 0, 0, 0, 0, 0, -0.0000000000000000064, 0, 0.0000000000000000030, 0, -0.2733808712910460459, 0, -0.0000000000000000148, 0, 0.1453883278650399125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0195097934034299814, 0, 0, 0, 0, 0, 0, -0.0199120995106335935, 0, -0.0316094780317844742, 0, 0, 0, 0, 0, 0, 0, 0, 0.0416158116084808377, 0, -0.1316007513592747924, 0, -0.0000000000000000017, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2180824917975599520, 0, -0.3529331872272983284, 0, -0.0000000000000000139, 0, 0.0000000000000000035, 0, 0.0402992559676969009, 0.0195097934034299675, 0, 0.0563199223665214743, 0, 0, -0.0000000000000000017, 0, 0.1013758602597385344, 0, -0.0302244419757725646, 0, 0, 0, 0, 0.0000000000000000117, 0, 0.0000000000000000278, 0, 0.2632015027185496403, 0, -0.2361141798511910417, 0, 0, 0, 0, 0, 0, -0.0000000000000000020, 0, 0.0000000000000000331, 0, 0.0000000000000000172, 0, 0.5213168667827536851, 0, -0.5815533114601594278, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1365685538240098695, 0, 0, 0, 0, 0, 0, -0.1182718369696950378, 0, -0.3129178677245881279, 0, 0, 0, 0, 0, 0, 0, 0, 0.1151506574393654087, 0, 0.2522824503643621430, 0, 0.5123880788939357833, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.1140380646087272715, 0, -0.2392082623796652485, 0, -0.3992495745446570377, 0, -0.7289266601748297658, 0, 0, -0.0195097934034300091, 0, 0, 0, 0, 0.0948284340953534088, 0, 0.0358417791191404655, 0, 0, 0, 0, 0, 0, -0.2113052823421207038, 0, -0.1480508452791841345, 0, -0.0520197645106010437, 0, 0, 0, 0, 0, 0, 0, 0, 0.3644633300874149384, 0, 0.3080277341159025073, 0, 0.1985249178153552230, 0, 0.0681507786867373566, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0195097934034299814, 0, 0, 0, 0, 0, 0, 0.0225279689466085682, 0, -0.0298017016880560114, 0, 0, 0, 0, 0, 0, 0, 0, -0.0822504695995467522, 0, -0.1081210501561551962, 0, 0.0243994323282826636, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1629115208696104433, 0, 0.2733808712910460459, 0, 0.2661663830297713029, 0, 0.0000000000000000048, 0, 0, -0.0117058760420579808, 0, 0, 0, 0, 0.0379313736381413635, 0, 0.0047789038825520741, 0, 0, 0, 0, 0, 0, -0.0422610564684241519, 0, 0.0493502817597280333, 0, 0.0312118587063606318, 0, 0, 0, 0, 0, 0, 0, 0, -0.0000000000000000256, 0, -0.2464221872927220003, 0, -0.2646998904204737602, 0, -0.1090412458987798788, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0117058760420579791, 0, 0, 0, 0, 0, 0, 0.0371711487619041242, 0, -0.0089405105064167961, 0, 0, 0, 0, 0, 0, 0, 0, 0.0000000000000000278, 0, -0.0720807001041034456, 0, 0.0000000000000000002, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.2606584333913767315, 0, -0.2733808712910461014, 0, -0.0000000000000000168, 0, 0.0000000000000000070, 0, 0, -0.0195097934034300056, 0, 0, 0, 0, 0.0316094780317844881, 0, -0.0199120995106336213, 0, 0, 0, 0, 0, 0, -0.0000000000000000052, 0, 0.1316007513592748202, 0, 0.0416158116084807961, 0, 0, 0, 0, 0, 0, 0, 0, -0.0000000000000000048, 0, 0.0000000000000000159, 0, 0.3529331872272984394, 0, 0.2180824917975598409, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0195097934034299744, 0, 0, 0, 0, 0, 0, 0.1013758602597385483, 0, 0.0000000000000000035, 0, 0, 0, 0, 0, 0, 0, 0, 0.2632015027185495293, 0, 0.0000000000000000083, 0, 0.0000000000000000016, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5213168667827535740, 0, 0.0000000000000000316, 0, -0.0000000000000000165, 0, 0.0000000000000000126, 0, 0, -0.1365685538240098418, 0, 0, 0, 0, -0.0000000000000000118, 0, -0.3345232717786444931, 0, 0, 0, 0, 0, 0, 0.0000000000000000048, 0, -0.0000000000000000659, 0, -0.5826213625187313117, 0, 0, 0, 0, 0, 0, 0, 0, 0.0000000000000000192, 0, -0.0000000000000000636, 0, -0.0000000000000001025, 0, -0.8723299671902394747, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2820947917738782507, -0.5462742152960395892, 0, -0.3153915652525199897, 0, 0, 0.6258357354491761448, 0, 0.4730873478787797071, 0, 0.3173566407456127614, 0, 0, 0, 0, -0.6831841051919145258, 0, -0.5045649007287241750, 0, -0.4606026297574615791, 0, -0.3178460113381420538, 0, 0, 0, 0, 0, 0, 0.7289266601748299879, 0, 0.5323327660595423838, 0, 0.4784165247593303860, 0, 0.4561522584349090859, 0, 0.3180369672047743679, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.1365685538240098418, 0, 0, 0, 0, 0, 0, 0.2508924538339835086, 0, 0.2212663462224912780, 0, 0, 0, 0, 0, 0, 0, 0, -0.3641383515742071530, 0, -0.3454519723180963786, 0, -0.2958273952789690076, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.4770554508071619959, 0, 0.4632248082358287888, 0, 0.4312388277622635768, 0, 0.3644633300874149384, 0, 0.0402992559676968592, -0.0585293802102898955, 0, -0.0112639844733042546, 0, 0, 0.0447025525320839806, 0, -0.0675839068398256110, 0, -0.0680049944454883815, 0, 0, 0, 0, -0.0243994323282826289, 0, 0.1621815752342327666, 0, 0.2467514087986401872, 0, 0.1929779354553006476, 0, 0, 0, 0, 0, 0, -0.0000000000000000346, 0, -0.2661663830297711919, 0, -0.4100713069365691243, 0, -0.4887345626088315798, 0, -0.3634708196625995869, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0585293802102899510, 0, 0, 0, 0, 0, 0, 0.0238945194127603011, 0, 0.0632189560635689485, 0, 0, 0, 0, 0, 0, 0, 0, 0.1560592935318029784, 0, 0.0822504695995467522, 0, -0.0422610564684241657, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.5452062294938998521, 0, -0.4411664840341228966, 0, -0.2464221872927220558, 0, 0.0000000000000000139, 0, 0.0241795535806181086, -0.0234117520841159790, 0, 0.0135167813679650944, 0, 0, 0.0089405105064167996, 0, -0.0743422975238082762, 0, -0.0559152176551794833, 0, 0, 0, 0, 0.0000000000000000048, 0, 0.0720807001041034595, 0, 0.0000000000000000173, 0, -0.0272439438289836799, 0, 0, 0, 0, 0, 0, -0.0000000000000000019, 0, 0.0000000000000000149, 0, 0.2733808712910461014, 0, 0.5213168667827537961, 0, 0.4361649835951196819, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0585293802102899510, 0, 0, 0, 0, 0, 0, -0.0597362985319007944, 0, 0.0316094780317844742, 0, 0, 0, 0, 0, 0, 0, 0, 0.1248474348254425270, 0, 0.1316007513592747924, 0, -0.0000000000000000087, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.6542474753926799114, 0, 0.3529331872272983284, 0, -0.0000000000000000278, 0, -0.0000000000000000139, 0, 0.0402992559676969356, -0.0195097934034299883, 0, 0.0563199223665215437, 0, 0, -0.0000000000000000182, 0, -0.1013758602597386038, 0, -0.0302244419757725646, 0, 0, 0, 0, 0.0000000000000000043, 0, -0.0000000000000000416, 0, -0.2632015027185496958, 0, -0.2361141798511912915, 0, 0, 0, 0, 0, 0, 0.0000000000000000051, 0, 0.0000000000000000310, 0, 0.0000000000000000414, 0, -0.5213168667827539071, 0, -0.5815533114601596498, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.1365685538240098695, 0, 0, 0, 0, 0, 0, -0.3345232717786446042, 0, -0.0000000000000000069, 0, 0, 0, 0, 0, 0, 0, 0, -0.5826213625187316447, 0, 0, 0, 0.0000000000000000104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.8723299671902401409, 0, -0.0000000000000001110, 0, 0.0000000000000001110, 0, -0.0000000000000000208, 0, 0.2820947917738780841, 0.0000000000000000746, 0, 0.6307831305050397575, 0, 0, -0.0000000000000000312, 0, 0.0000000000000000555, 0, 0.8462843753216341414, 0, 0, 0, 0, 0.0000000000000000165, 0, 0.0000000000000000555, 0, -0.0000000000000000656, 0, 1.0171072362820543500, 0, 0, 0, 0, 0, 0, -0.0000000000000000985, 0, -0.0000000000000002266, 0, -0.0000000000000000735, 0, 0.0000000000000003443, 0, 1.1631066229203190776, 0, 0, 0, 0, 0, 0, 0, 0 }; teem-1.11.0~svn6057/src/tijk/privateTijk.h0000664000175000017500000001124512042325046020003 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2010, 2009, 2008 Thomas Schultz Copyright (C) 2010, 2009, 2008 Gordon Kindlmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TIJK_PRIVATE_HAS_BEEN_INCLUDED #define TIJK_PRIVATE_HAS_BEEN_INCLUDED /* macros to facilitate definition of new tensor types */ /* for unsymmetric tensors */ #define TIJK_TYPE_UNSYM(name, order, dim, num) \ tijk_type \ _tijk_##name = { \ #name, order, dim, num, \ NULL, NULL, NULL, NULL, \ _tijk_##name##_tsp_d, _tijk_##name##_tsp_f, \ _tijk_##name##_norm_d, _tijk_##name##_norm_f, \ _tijk_##name##_trans_d, _tijk_##name##_trans_f, \ _tijk_##name##_convert_d, _tijk_##name##_convert_f, \ _tijk_##name##_approx_d, _tijk_##name##_approx_f, \ NULL, NULL, NULL, NULL, \ NULL \ }; \ const tijk_type *const tijk_##name = &_tijk_##name; /* for partially symmetric and antisymmetric tensors */ #define TIJK_TYPE(name, order, dim, num) \ tijk_type \ _tijk_##name = { \ #name, order, dim, num, \ _tijk_##name##_mult, _tijk_##name##_unsym2uniq, \ _tijk_##name##_uniq2unsym, _tijk_##name##_uniq_idx, \ _tijk_##name##_tsp_d, _tijk_##name##_tsp_f, \ _tijk_##name##_norm_d, _tijk_##name##_norm_f, \ _tijk_##name##_trans_d, _tijk_##name##_trans_f, \ _tijk_##name##_convert_d, _tijk_##name##_convert_f, \ _tijk_##name##_approx_d, _tijk_##name##_approx_f, \ NULL, NULL, NULL, NULL, \ NULL \ }; \ const tijk_type *const tijk_##name = &_tijk_##name; /* for totally symmetric tensors */ #define TIJK_TYPE_SYM(name, order, dim, num) \ tijk_sym_fun \ _tijk_sym_fun_##name = { \ _tijk_##name##_s_form_d, _tijk_##name##_s_form_f, \ _tijk_##name##_mean_d, _tijk_##name##_mean_f, \ _tijk_##name##_var_d, _tijk_##name##_var_f, \ _tijk_##name##_v_form_d, _tijk_##name##_v_form_f, \ _tijk_##name##_m_form_d, _tijk_##name##_m_form_f, \ _tijk_##name##_grad_d, _tijk_##name##_grad_f, \ _tijk_##name##_hess_d, _tijk_##name##_hess_f, \ _tijk_##name##_make_rank1_d, _tijk_##name##_make_rank1_f, \ _tijk_##name##_make_iso_d, _tijk_##name##_make_iso_f \ }; \ tijk_type \ _tijk_##name = { \ #name, order, dim, num, \ _tijk_##name##_mult, _tijk_##name##_unsym2uniq, \ _tijk_##name##_uniq2unsym, _tijk_##name##_uniq_idx, \ _tijk_##name##_tsp_d, _tijk_##name##_tsp_f, \ _tijk_##name##_norm_d, _tijk_##name##_norm_f, \ _tijk_##name##_trans_d, _tijk_##name##_trans_f, \ _tijk_##name##_convert_d, _tijk_##name##_convert_f, \ _tijk_##name##_approx_d, _tijk_##name##_approx_f, \ NULL, NULL, NULL, NULL, \ &_tijk_sym_fun_##name \ }; \ const tijk_type *const tijk_##name = &_tijk_##name; #endif /* TIJK_PRIVATE_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/tijk/nrrdTijk.c0000664000175000017500000001623712042325046017277 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2011 Thomas Schultz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Reading and setting axis labels to encode Tijk types */ #include "tijk.h" /* tijk_set_axis_tensor * * Marks a given nrrd axis as containing tensors of a given type * Based on axis size, determines if values are masked. * Returns 0 on success * 1 if given NULL pointer * 2 if nrrd has wrong type (neither float nor double) * 3 if axis doesn't exist * 4 if axis has wrong size */ int tijk_set_axis_tensor(Nrrd *nrrd, unsigned int axis, const tijk_type *type) { NrrdAxisInfo *axinfo = NULL; unsigned int masked = 0, lablen; if (nrrd==NULL || type==NULL) return 1; if (nrrd->type!=nrrdTypeFloat && nrrd->type!=nrrdTypeDouble) return 2; if (axis>=nrrd->dim) return 3; axinfo = nrrd->axis+axis; if (axinfo->size==type->num+1) masked = 1; else if (axinfo->size!=type->num) return 4; axinfo->label = (char*) airFree(axinfo->label); lablen = strlen("tijk__") + strlen(type->name) + (masked?strlen("mask_"):0) + 1; axinfo->label = AIR_CALLOC(lablen, char); sprintf(axinfo->label, "tijk_%s%s", masked?"mask_":"", type->name); return 0; } /* tijk_set_axis_esh * * Marks a given nrrd axis as containing even-order spherical harmonics of * a given order. Based on axis size, determines if values are masked. * Returns 0 on success * 1 if given NULL pointer * 2 if nrrd has wrong type (neither float nor double) * 3 if axis doesn't exist * 4 if order is unsupported * 5 if axis has wrong size */ int tijk_set_axis_esh(Nrrd *nrrd, unsigned int axis, unsigned int order) { NrrdAxisInfo *axinfo = NULL; unsigned int masked = 0, lablen; if (nrrd==NULL) return 1; if (nrrd->type!=nrrdTypeFloat && nrrd->type!=nrrdTypeDouble) return 2; if (axis>=nrrd->dim) return 3; axinfo = nrrd->axis+axis; if (order>tijk_max_esh_order) return 4; if (axinfo->size==tijk_esh_len[order/2]+1) masked = 1; else if (axinfo->size!=tijk_esh_len[order/2]) return 5; axinfo->label = (char*) airFree(axinfo->label); lablen = strlen("tijk_esh_") + (masked?strlen("mask_"):0) + 3; axinfo->label = AIR_CALLOC(lablen, char); sprintf(axinfo->label, "tijk_%sesh_%02u", masked?"mask_":"", order); return 0; } /* tijk_set_axis_efs * * Marks a given nrrd axis as containing even-order fourier series of * a given order. Based on axis size, determines if values are masked. * Returns 0 on success * 1 if given NULL pointer * 2 if nrrd has wrong type (neither float nor double) * 3 if axis doesn't exist * 4 if order is unsupported * 5 if axis has wrong size */ int tijk_set_axis_efs(Nrrd *nrrd, unsigned int axis, unsigned int order) { NrrdAxisInfo *axinfo = NULL; unsigned int masked = 0, lablen; if (nrrd==NULL) return 1; if (nrrd->type!=nrrdTypeFloat && nrrd->type!=nrrdTypeDouble) return 2; if (axis>=nrrd->dim) return 3; axinfo = nrrd->axis+axis; if (order>tijk_max_efs_order) return 4; if (axinfo->size==order+2) masked = 1; else if (axinfo->size!=order+1) return 5; axinfo->label = (char*) airFree(axinfo->label); lablen = strlen("tijk_efs_") + (masked?strlen("mask_"):0) + 3; axinfo->label = AIR_CALLOC(lablen, char); sprintf(axinfo->label, "tijk_%sefs_%02u", masked?"mask_":"", order); return 0; } /* tijk_get_axis_type * * Extracts Tijk information from a given nrrd axis and writes it to info. * Returns 0 on success * no "tijk_*" label counts as success -> tijk_class_unknown * 1 if given NULL pointer * 2 if nrrd has wrong type (neither float nor double) * 3 if axis doesn't exist * 4 if "tijk_*" label couldn't be parsed * 5 if label didn't fit axis size */ int tijk_get_axis_type(tijk_axis_info *info, const Nrrd *nrrd, unsigned int axis) { const tijk_type * tijk_types[] = { NULL, /* 0: tijk_2o2d_unsym */ NULL, /* 1: tijk_2o2d_sym */ NULL, /* 2: tijk_2o2d_asym */ NULL, /* 3: tijk_3o2d_sym */ NULL, /* 4: tijk_4o2d_unsym */ NULL, /* 5: tijk_4o2d_sym */ NULL, /* 6: tijk_1o3d */ NULL, /* 7: tijk_2o3d_unsym */ NULL, /* 8: tijk_2o3d_sym */ NULL, /* 9: tijk_2o3d_asym */ NULL, /* 10: tijk_3o3d_unsym */ NULL, /* 11: tijk_3o3d_sym */ NULL, /* 12: tijk_4o3d_sym */ NULL, /* 13: tijk_6o3d_sym */ NULL, /* 14: tijk_8o3d_sym */ NULL }; const NrrdAxisInfo *axinfo = NULL; const char *labelp = NULL; unsigned int i=0; if (info==NULL || nrrd==NULL) return 1; if (nrrd->type!=nrrdTypeFloat && nrrd->type!=nrrdTypeDouble) return 2; if (axis>=nrrd->dim) return 3; axinfo = nrrd->axis+axis; if (axinfo->label==NULL || strncmp(axinfo->label, "tijk_", 5)) { info->tclass = tijk_class_unknown; return 0; } labelp = axinfo->label+5; if (!strncmp(labelp, "mask_", 5)) { info->masked = 1; labelp += 5; } else info->masked = 0; if (!strncmp(labelp, "esh_", 4)) { info->tclass = tijk_class_esh; if (1!=sscanf(labelp+4, "%02u", &(info->order)) || info->order>tijk_max_esh_order || info->order%2!=0) return 4; if (axinfo->size!=tijk_esh_len[info->order/2]+info->masked) return 5; return 0; } if (!strncmp(labelp, "efs_", 4)) { info->tclass = tijk_class_efs; if (1!=sscanf(labelp+4, "%02u", &(info->order)) || info->order>tijk_max_efs_order || info->order%2!=0) return 4; if (axinfo->size!=info->order+1+info->masked) return 5; return 0; } tijk_types[ 0] = tijk_2o2d_unsym; tijk_types[ 1] = tijk_2o2d_sym; tijk_types[ 2] = tijk_2o2d_asym; tijk_types[ 3] = tijk_3o2d_sym; tijk_types[ 4] = tijk_4o2d_unsym; tijk_types[ 5] = tijk_4o2d_sym; tijk_types[ 6] = tijk_1o3d; tijk_types[ 7] = tijk_2o3d_unsym; tijk_types[ 8] = tijk_2o3d_sym; tijk_types[ 9] = tijk_2o3d_asym; tijk_types[10] = tijk_3o3d_unsym; tijk_types[11] = tijk_3o3d_sym; tijk_types[12] = tijk_4o3d_sym; tijk_types[13] = tijk_6o3d_sym; tijk_types[14] = tijk_8o3d_sym; while (tijk_types[i]!=NULL) { if (!strcmp(labelp, tijk_types[i]->name)) { info->tclass = tijk_class_tensor; info->type = tijk_types[i]; if (axinfo->size!=tijk_types[i]->num+info->masked) return 5; return 0; } i++; } return 4; } teem-1.11.0~svn6057/src/tijk/enumsTijk.c0000664000175000017500000000326112042325046017452 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2011 Thomas Schultz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "tijk.h" const char * _tijk_class_str[TIJK_CLASS_MAX+1] = { "(unknown_class)", "tensor", "esh", "efs", }; const char * _tijk_class_desc[TIJK_CLASS_MAX+1] = { "unknown class", "tensor, specified by a tijk_type", "even-order spherical harmonics, specified by order", "even-order fourier series, specified by order", }; const char * _tijk_class_str_eqv[] = { "tensor", "esh", "efs", "" }; int _tijk_class_val_eqv[] = { tijk_class_tensor, tijk_class_esh, tijk_class_efs, }; airEnum _tijk_class_enum = { "class", TIJK_CLASS_MAX, _tijk_class_str, NULL, _tijk_class_desc, _tijk_class_str_eqv, _tijk_class_val_eqv, AIR_FALSE }; const airEnum *const tijk_class = &_tijk_class_enum; teem-1.11.0~svn6057/src/alan/0000775000175000017500000000000012203513755015312 5ustar domibeldomibelteem-1.11.0~svn6057/src/alan/alan.h0000664000175000017500000001461512165631065016407 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ALAN_HAS_BEEN_INCLUDED #define ALAN_HAS_BEEN_INCLUDED #include #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(alan_EXPORTS) || defined(teem_EXPORTS) # define ALAN_EXPORT extern __declspec(dllexport) # else # define ALAN_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define ALAN_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define ALAN alanBiffKey #define ALAN_THREAD_MAX 256 enum { alanTextureTypeUnknown, alanTextureTypeTuring, alanTextureTypeGrayScott, alanTextureTypeLast }; enum { alanParmUnknown, alanParmVerbose, alanParmTextureType, alanParmNumThreads, alanParmFrameInterval, alanParmHomogAniso, alanParmSaveInterval, alanParmMaxIteration, alanParmRandRange, alanParmDeltaT, alanParmDeltaX, alanParmDiffA, alanParmDiffB, alanParmReact, alanParmK, alanParmF, alanParmMinAverageChange, alanParmMaxPixelChange, alanParmAlpha, alanParmBeta, alanParmConstantFilename, alanParmWrapAround, alanParmLast }; enum { alanStopUnknown=0, alanStopNot, /* 1 */ alanStopMaxIteration, /* 2 */ alanStopNonExist, /* 3 */ alanStopConverged, /* 4 */ alanStopDiverged, /* 5 */ alanStopLast }; #define ALAN_STOP_MAX 5 /* all morphogen values are stored as ** 1: floats ** 0: doubles */ #if 1 typedef float alan_t; # define alan_nt nrrdTypeFloat # define ALAN_FLOAT 1 #else typedef double alan_t; # define alan_nt nrrdTypeDouble # define ALAN_FLOAT 0 #endif typedef struct alanContext_t { /* INPUT ----------------------------- */ unsigned int dim, /* either 2 or 3 */ size[3]; /* number of texels in X, Y, (Z) */ int verbose, wrap, /* do toroidal boundary wrapping */ textureType, /* what kind are we (from alanTextureType* enum) */ oversample, /* oversampling of tensors to texels */ homogAniso, /* homogenous anisotropy approximation */ numThreads, /* # of threads, if airThreadCapable */ frameInterval, /* # of iterations between which to an image */ saveInterval, /* # of iterations between which to save all state */ maxIteration, /* cap on # of iterations, or 0 if there is no limit */ constFilename; /* always use the same filename when saving frames */ alan_t K, F, /* simulation variables */ deltaX, /* size of spatial grid discretization */ minAverageChange, /* min worthwhile "avergageChange" value (see below), assume convergence if it falls below this */ maxPixelChange, /* maximum allowed change in the first morphogen (on any single pixels), assume unstable divergence if this is exceeded */ alpha, beta, /* variables for turing */ react, /* additional scaling of reaction term */ deltaT, /* euler integration step size */ initA, initB, /* initial (constant) values for each morphogen */ diffA, diffB, /* base diffusion rates for each morphogen */ randRange; /* amplitude of noise to destabalize Turing */ Nrrd *nten; /* tensors guiding texture. May have 1+3 or 1+6 values per sample, depending on dim */ /* if non-NULL, this is called once per iteration, at its completion */ int (*perIteration)(struct alanContext_t *, int iter); /* INTERNAL -------------------------- */ int iter; /* current iteration */ Nrrd *_nlev[2], /* levels of morphogens, alternating buffers */ *nlev; /* pointer to last iterations output */ Nrrd *nparm; /* alpha, beta values for all texels */ alan_t averageChange; /* average amount of "change" in last iteration */ int changeCount; /* # of contributions to averageChange */ /* to control update of averageChange and changeCount */ airThreadMutex *changeMutex; /* to synchronize separate iterations of simulation */ airThreadBarrier *iterBarrier; /* OUTPUT ---------------------------- */ int stop; /* why we stopped */ } alanContext; /* methodsAlan.c */ ALAN_EXPORT const int alanPresent; ALAN_EXPORT const char *alanBiffKey; ALAN_EXPORT alanContext *alanContextNew(void); ALAN_EXPORT alanContext *alanContextNix(alanContext *actx); ALAN_EXPORT int alanDimensionSet(alanContext *actx, int dim); ALAN_EXPORT int alan2DSizeSet(alanContext *actx, int sizeX, int sizeY); ALAN_EXPORT int alan3DSizeSet(alanContext *actx, int sizeX, int sizeY, int sizeZ); ALAN_EXPORT int alanTensorSet(alanContext *actx, Nrrd *nten, int oversample); ALAN_EXPORT int alanParmSet(alanContext *actx, int whichParm, double parm); /* enumsAlan.c */ ALAN_EXPORT const airEnum *const alanStop; /* coreAlan.c */ ALAN_EXPORT int alanUpdate(alanContext *actx); ALAN_EXPORT int alanInit(alanContext *actx, const Nrrd *nlevInit, const Nrrd *nparmInit); ALAN_EXPORT int alanPriorityParm(alanContext *actx, const Nrrd *npri); ALAN_EXPORT int alanRun(alanContext *actx); #ifdef __cplusplus } #endif #endif /* ALAN_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/alan/enumsAlan.c0000664000175000017500000000325512165631065017410 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "alan.h" const char * _alanStopStr[ALAN_STOP_MAX+1] = { "(unknown_stop)", "not", "iter", "nonexist", "converged", "diverged" }; const char * _alanStopDesc[ALAN_STOP_MAX+1] = { "unknown_stop", "there is no reason to stop", "hit the maximum number of iterations", "got non-existent values", "simulation converged", "simulation hit divergent instability" }; const airEnum _alanStop = { "stop", ALAN_STOP_MAX, _alanStopStr, NULL, _alanStopDesc, NULL, NULL, AIR_FALSE }; const airEnum *const alanStop = &_alanStop; teem-1.11.0~svn6057/src/alan/coreAlan.c0000664000175000017500000004271212165631065017212 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* ** learned: valgrind is sometimes far off when reporting the line ** number for an invalid read- have to comment out various lines in ** order to find the real offending line */ #include "alan.h" int _alanCheck(alanContext *actx) { static const char me[]="alanCheck"; if (!actx) { biffAddf(ALAN, "%s: got NULL pointer", me); return 1; } if (0 == actx->dim) { biffAddf(ALAN, "%s: dimension of texture not set", me); return 1; } if (alanTextureTypeUnknown == actx->textureType) { biffAddf(ALAN, "%s: texture type not set", me); return 1; } if (!( actx->size[0] > 0 && actx->size[1] > 0 && (2 == actx->dim || actx->size[2] > 0) )) { biffAddf(ALAN, "%s: texture sizes invalid", me); return 1; } if (0 == actx->deltaT) { biffAddf(ALAN, "%s: deltaT == 0", me); return 1; } return 0; } int alanUpdate(alanContext *actx) { static const char me[]="alanUpdate"; int ret; if (_alanCheck(actx)) { biffAddf(ALAN, "%s: ", me); return 1; } if (actx->_nlev[0] || actx->_nlev[0]) { biffAddf(ALAN, "%s: confusion: _nlev[0,1] already allocated?", me); return 1; } actx->_nlev[0] = nrrdNew(); actx->_nlev[1] = nrrdNew(); actx->nparm = nrrdNew(); if (2 == actx->dim) { ret = (nrrdMaybeAlloc_va(actx->_nlev[0], alan_nt, 3, AIR_CAST(size_t, 2), AIR_CAST(size_t, actx->size[0]), AIR_CAST(size_t, actx->size[1])) || nrrdCopy(actx->_nlev[1], actx->_nlev[0]) || nrrdMaybeAlloc_va(actx->nparm, alan_nt, 3, AIR_CAST(size_t, 3), AIR_CAST(size_t, actx->size[0]), AIR_CAST(size_t, actx->size[1]))); } else { ret = (nrrdMaybeAlloc_va(actx->_nlev[0], alan_nt, 4, AIR_CAST(size_t, 2), AIR_CAST(size_t, actx->size[0]), AIR_CAST(size_t, actx->size[1]), AIR_CAST(size_t, actx->size[2])) || nrrdCopy(actx->_nlev[1], actx->_nlev[0]) || nrrdMaybeAlloc_va(actx->nparm, alan_nt, 4, AIR_CAST(size_t, 3), AIR_CAST(size_t, actx->size[0]), AIR_CAST(size_t, actx->size[1]), AIR_CAST(size_t, actx->size[2]))); } if (ret) { biffMovef(ALAN, NRRD, "%s: trouble allocating buffers", me); return 1; } return 0; } int alanInit(alanContext *actx, const Nrrd *nlevInit, const Nrrd *nparmInit) { static const char me[]="alanInit"; alan_t *levInit=NULL, *lev0, *parmInit=NULL, *parm; size_t I, N; if (_alanCheck(actx)) { biffAddf(ALAN, "%s: ", me); return 1; } if (!( actx->_nlev[0] && actx->_nlev[0] && actx->nparm )) { biffAddf(ALAN, "%s: _nlev[0,1] not allocated: call alanUpdate", me); return 1; } if (nlevInit) { if (nrrdCheck(nlevInit)) { biffMovef(ALAN, NRRD, "%s: given nlevInit has problems", me); return 1; } if (!( alan_nt == nlevInit->type && nlevInit->dim == 1 + actx->dim && actx->_nlev[0]->axis[0].size == nlevInit->axis[0].size && actx->size[0] == nlevInit->axis[1].size && actx->size[1] == nlevInit->axis[2].size && (2 == actx->dim || actx->size[2] == nlevInit->axis[3].size) )) { biffAddf(ALAN, "%s: type/size mismatch with given nlevInit", me); return 1; } levInit = (alan_t*)(nlevInit->data); } if (nparmInit) { if (nrrdCheck(nparmInit)) { biffMovef(ALAN, NRRD, "%s: given nparmInit has problems", me); return 1; } if (!( alan_nt == nparmInit->type && nparmInit->dim == 1 + actx->dim && 3 == nparmInit->axis[0].size && actx->size[0] == nparmInit->axis[1].size && actx->size[1] == nparmInit->axis[2].size && (2 == actx->dim || actx->size[2] == nparmInit->axis[3].size) )) { biffAddf(ALAN, "%s: type/size mismatch with given nparmInit", me); return 1; } parmInit = (alan_t*)(nparmInit->data); } #define RAND AIR_AFFINE(0, airDrandMT(), 1, -actx->randRange, actx->randRange) N = nrrdElementNumber(actx->_nlev[0])/actx->_nlev[0]->axis[0].size; lev0 = (alan_t*)(actx->_nlev[0]->data); parm = (alan_t*)(actx->nparm->data); for (I=0; IinitA + RAND); lev0[1 + 2*I] = AIR_CAST(alan_t, actx->initB + RAND); } if (parmInit) { parm[0 + 3*I] = parmInit[0 + 3*I]; parm[1 + 3*I] = parmInit[1 + 3*I]; parm[2 + 3*I] = parmInit[2 + 3*I]; } else { parm[0 + 3*I] = actx->deltaT; parm[1 + 3*I] = actx->alpha; parm[2 + 3*I] = actx->beta; } } return 0; } int _alanPerIteration(alanContext *actx, int iter) { static const char me[]="_alanPerIteration"; char fname[AIR_STRLEN_MED]; Nrrd *nslc, *nimg; if (!(actx->saveInterval || actx->frameInterval)) { if (actx->verbose && !(iter % 100)) { fprintf(stderr, "%s: iter = %d, averageChange = %g\n", me, iter, actx->averageChange); } } if (actx->saveInterval && !(iter % actx->saveInterval)) { sprintf(fname, "%06d.nrrd", actx->constFilename ? 0 : iter); nrrdSave(fname, actx->_nlev[(iter+1) % 2], NULL); fprintf(stderr, "%s: iter = %d, averageChange = %g, saved %s\n", me, iter, actx->averageChange, fname); } if (actx->frameInterval && !(iter % actx->frameInterval)) { nrrdSlice(nslc=nrrdNew(), actx->_nlev[(iter+1) % 2], 0, 0); nrrdQuantize(nimg=nrrdNew(), nslc, NULL, 8); sprintf(fname, (2 == actx->dim ? "%06d.png" : "%06d.nrrd"), actx->constFilename ? 0 : iter); nrrdSave(fname, nimg, NULL); fprintf(stderr, "%s: iter = %d, averageChange = %g, saved %s\n", me, iter, actx->averageChange, fname); nrrdNuke(nslc); nrrdNuke(nimg); } return 0; } typedef struct { /* these two are genuine input to each worker thread */ alanContext *actx; int idx; /* this is just a convenient place to put airThread (so that alanRun() doesn't have to make multiple arrays of per-thread items) */ airThread *thread; /* pointless: a pointer to this is passed to airThreadJoin for its return, and currently that will just end up pointing back to this struct */ void *me; } alanTask; void * _alanTuringWorker(void *_task) { alan_t *tendata, *ten, react, conf, Dxx, Dxy, Dyy, /* Dxz, Dyz, */ *tpx, *tmx, *tpy, *tmy, /* *tpz, *tmz, */ *lev0, *lev1, *parm, deltaT, alpha, beta, A, B, *v[27], lapA, lapB, corrA, corrB, deltaA, deltaB, diffA, diffB, change; int dim, iter, stop, startW, endW, idx, px, mx, py, my, pz, mz, startY, endY, startZ, endZ, sx, sy, sz, x, y, z; alanTask *task; task = (alanTask *)_task; dim = task->actx->dim; sx = task->actx->size[0]; sy = task->actx->size[1]; sz = (2 == dim ? 1 : task->actx->size[2]); parm = (alan_t*)(task->actx->nparm->data); diffA = AIR_CAST(alan_t, task->actx->diffA/pow(task->actx->deltaX, dim)); diffB = AIR_CAST(alan_t, task->actx->diffB/pow(task->actx->deltaX, dim)); startW = task->idx*sy/task->actx->numThreads; endW = (task->idx+1)*sy/task->actx->numThreads; tendata = task->actx->nten ? (alan_t *)task->actx->nten->data : NULL; react = task->actx->react; if (2 == dim) { startZ = 0; endZ = 1; startY = startW; endY = endW; } else { startZ = startW; endZ = endW; startY = 0; endY = sy; } for (iter = 0; (alanStopNot == task->actx->stop && (0 == task->actx->maxIteration || iter < task->actx->maxIteration)); iter++) { if (0 == task->idx) { task->actx->iter = iter; task->actx->nlev = task->actx->_nlev[(iter+1) % 2]; } lev0 = (alan_t*)(task->actx->_nlev[iter % 2]->data); lev1 = (alan_t*)(task->actx->_nlev[(iter+1) % 2]->data); stop = alanStopNot; change = 0; conf = 1; /* if you have no data; this will stay 1 */ for (z = startZ; z < endZ; z++) { if (task->actx->wrap) { pz = AIR_MOD(z+1, sz); mz = AIR_MOD(z-1, sz); } else { pz = AIR_MIN(z+1, sz-1); mz = AIR_MAX(z-1, 0); } for (y = startY; y < endY; y++) { if (task->actx->wrap) { py = AIR_MOD(y+1, sy); my = AIR_MOD(y-1, sy); } else { py = AIR_MIN(y+1, sy-1); my = AIR_MAX(y-1, 0); } for (x = 0; x < sx; x++) { if (task->actx->wrap) { px = AIR_MOD(x+1, sx); mx = AIR_MOD(x-1, sx); } else { px = AIR_MIN(x+1, sx-1); mx = AIR_MAX(x-1, 0); } idx = x + sx*(y + sy*z); A = lev0[0 + 2*idx]; B = lev0[1 + 2*idx]; deltaT = parm[0 + 3*idx]; alpha = parm[1 + 3*idx]; beta = parm[2 + 3*idx]; lapA = lapB = corrA = corrB = 0; if (2 == dim) { /* ** 0 1 2 ----> X ** 3 4 5 ** 6 7 8 ** | ** v Y */ v[1] = lev0 + 2*( x + sx*(my)); v[3] = lev0 + 2*(mx + sx*( y)); v[5] = lev0 + 2*(px + sx*( y)); v[7] = lev0 + 2*( x + sx*(py)); if (tendata) { /* ** 0 1 2 Dxy/2 Dyy -Dxy/2 ** 3 4 5 Dxx -2*(Dxx + Dyy) Dxx ** 6 7 8 -Dxy/2 Dyy Dxy/2 */ v[0] = lev0 + 2*(mx + sx*(my)); v[2] = lev0 + 2*(px + sx*(my)); v[6] = lev0 + 2*(mx + sx*(py)); v[8] = lev0 + 2*(px + sx*(py)); ten = tendata + 4*idx; conf = AIR_CAST(alan_t, (AIR_CLAMP(0.3, ten[0], 1) - 0.3)/0.7); if (conf) { Dxx = ten[1]; Dxy = ten[2]; Dyy = ten[3]; lapA = (Dxy*(v[0][0] + v[8][0] - v[2][0] - v[6][0])/2 + Dxx*(v[3][0] + v[5][0]) + Dyy*(v[1][0] + v[7][0]) - 2*(Dxx + Dyy)*A); lapB = (Dxy*(v[0][1] + v[8][1] - v[2][1] - v[6][1])/2 + Dxx*(v[3][1] + v[5][1]) + Dyy*(v[1][1] + v[7][1]) - 2*(Dxx + Dyy)*B); if (!(task->actx->homogAniso)) { tpx = tendata + 4*(px + sx*( y + sy*( z))); tmx = tendata + 4*(mx + sx*( y + sy*( z))); tpy = tendata + 4*( x + sx*(py + sy*( z))); tmy = tendata + 4*( x + sx*(my + sy*( z))); corrA = ((tpx[1]-tmx[1])*(v[5][0]-v[3][0])/4+ /* Dxx,x*A,x */ (tpx[2]-tmx[2])*(v[7][0]-v[1][0])/4+ /* Dxy,x*A,y */ (tpy[2]-tmy[2])*(v[5][0]-v[3][0])/4+ /* Dxy,y*A,x */ (tpy[3]-tmy[3])*(v[7][0]-v[1][0])); /* Dyy,y*A,y */ corrB = ((tpx[1]-tmx[1])*(v[5][1]-v[3][1])/4+ /* Dxx,x*B,x */ (tpx[2]-tmx[2])*(v[7][1]-v[1][1])/4+ /* Dxy,x*B,y */ (tpy[2]-tmy[2])*(v[5][1]-v[3][1])/4+ /* Dxy,y*B,x */ (tpy[3]-tmy[3])*(v[7][1]-v[1][1])); /* Dyy,y*B,y */ } } else { /* no confidence; you diffuse */ lapA = v[1][0] + v[3][0] + v[5][0] + v[7][0] - 4*A; lapB = v[1][1] + v[3][1] + v[5][1] + v[7][1] - 4*B; } } else { /* no data; you diffuse */ lapA = v[1][0] + v[3][0] + v[5][0] + v[7][0] - 4*A; lapB = v[1][1] + v[3][1] + v[5][1] + v[7][1] - 4*B; } } else { /* 3 == dim */ /* ** 0 1 2 ---- X ** 3 4 5 ** 6 7 8 ** / ** / 9 10 11 ** Y 12 13 14 ** 15 16 17 ** ** 18 19 20 ** 21 22 23 ** 24 25 26 ** | ** | ** Z */ v[ 4] = lev0 + 2*( x + sx*( y + sy*(mz))); v[10] = lev0 + 2*( x + sx*(my + sy*( z))); v[12] = lev0 + 2*(mx + sx*( y + sy*( z))); v[14] = lev0 + 2*(px + sx*( y + sy*( z))); v[16] = lev0 + 2*( x + sx*(py + sy*( z))); v[22] = lev0 + 2*( x + sx*( y + sy*(pz))); if (tendata) { if (!(task->actx->homogAniso)) { } } else { lapA = (v[ 4][0] + v[10][0] + v[12][0] + v[14][0] + v[16][0] + v[22][0] - 6*A); lapB = (v[ 4][1] + v[10][1] + v[12][1] + v[14][1] + v[16][1] + v[22][1] - 6*B); } } deltaA = deltaT*(react*conf*task->actx->K*(alpha - A*B) + diffA*(lapA + corrA)); if (AIR_ABS(deltaA) > task->actx->maxPixelChange) { stop = alanStopDiverged; } change += AIR_ABS(deltaA); deltaB = deltaT*(react*conf*task->actx->K*(A*B - B - beta) + diffB*(lapB + corrB)); if (!( AIR_EXISTS(deltaA) && AIR_EXISTS(deltaB) )) { stop = alanStopNonExist; } A += deltaA; B = AIR_MAX(0, B + deltaB); lev1[0 + 2*idx] = A; lev1[1 + 2*idx] = B; } } } /* add change to global sum in a threadsafe way */ airThreadMutexLock(task->actx->changeMutex); task->actx->averageChange += change/(sx*sy*sz); task->actx->changeCount += 1; if (task->actx->changeCount == task->actx->numThreads) { /* I must be the last thread to reach this point; all others must have passed the mutex unlock, and are sitting at the barrier */ if (alanStopNot != stop) { /* there was some problem in going from lev0 to lev1, which we deal with now by setting actx->stop */ task->actx->stop = stop; } else if (task->actx->averageChange < task->actx->minAverageChange) { /* we converged */ task->actx->stop = alanStopConverged; } else { /* we keep going */ _alanPerIteration(task->actx, iter); if (task->actx->perIteration) { task->actx->perIteration(task->actx, iter); } } task->actx->averageChange = 0; task->actx->changeCount = 0; } airThreadMutexUnlock(task->actx->changeMutex); /* force all threads to line up here, once per iteration */ airThreadBarrierWait(task->actx->iterBarrier); } if (iter == task->actx->maxIteration) { /* HEY: all threads will agree on this, right? */ task->actx->stop = alanStopMaxIteration; } /* else: the non-alanStopNot value of task->actx->stop made us stop */ return _task; } int alanRun(alanContext *actx) { static const char me[]="alanRun"; int tid, hack=AIR_FALSE; alanTask task[ALAN_THREAD_MAX]; if (_alanCheck(actx)) { biffAddf(ALAN, "%s: ", me); return 1; } if (!( actx->_nlev[0] && actx->_nlev[0] )) { biffAddf(ALAN, "%s: _nlev[0,1] not allocated: " "call alanUpdate + alanInit", me); return 1; } if (!airThreadCapable && 1 == actx->numThreads) { hack = airThreadNoopWarning; airThreadNoopWarning = AIR_FALSE; } actx->changeMutex = airThreadMutexNew(); actx->iterBarrier = airThreadBarrierNew(actx->numThreads); actx->averageChange = 0; actx->changeCount = 0; actx->stop = alanStopNot; for (tid=0; tidnumThreads; tid++) { task[tid].actx = actx; task[tid].idx = tid; task[tid].thread = airThreadNew(); airThreadStart(task[tid].thread, _alanTuringWorker, (void *)&(task[tid])); } for (tid=0; tidnumThreads; tid++) { airThreadJoin(task[tid].thread, &(task[tid].me)); task[tid].thread = airThreadNix(task[tid].thread); } actx->iterBarrier = airThreadBarrierNix(actx->iterBarrier); actx->changeMutex = airThreadMutexNix(actx->changeMutex); if (!airThreadCapable && 1 == actx->numThreads) { airThreadNoopWarning = hack; } /* we assume that someone set actx->stop */ return 0; } teem-1.11.0~svn6057/src/alan/methodsAlan.c0000664000175000017500000001763712165631065017735 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "alan.h" const int alanPresent = 42; const char * alanBiffKey = "alan"; void alanContextInit(alanContext *actx) { if (actx) { actx->verbose = 0; actx->wrap = AIR_FALSE; actx->textureType = alanTextureTypeUnknown; actx->dim = 0; ELL_3V_SET(actx->size, 0, 0, 0); actx->oversample = 0; actx->homogAniso = AIR_FALSE; actx->numThreads = 1; actx->frameInterval = 10; actx->saveInterval = 100; actx->maxIteration = 1000000; actx->minAverageChange = 0.00002f; actx->maxPixelChange = 6; actx->K = AIR_NAN; actx->F = AIR_NAN; actx->deltaX = 1.25; actx->alpha = 16; actx->beta = 12; actx->deltaT = 1.0; actx->react = 1.0; actx->initA = actx->initB = 0; actx->diffA = actx->diffB = 0; actx->perIteration = NULL; actx->randRange = 3; actx->_nlev[0] = nrrdNuke(actx->_nlev[0]); actx->_nlev[1] = nrrdNuke(actx->_nlev[1]); actx->nlev = actx->_nlev[0]; actx->nparm = nrrdNuke(actx->nparm); actx->nten = nrrdNuke(actx->nten); actx->constFilename = AIR_FALSE; } return; } alanContext * alanContextNew(void) { alanContext *actx; actx = (alanContext *)calloc(1, sizeof(alanContext)); actx->_nlev[0] = actx->_nlev[1] = NULL; actx->nlev = NULL; actx->nparm = NULL; actx->nten = NULL; alanContextInit(actx); return actx; } alanContext * alanContextNix(alanContext *actx) { if (actx) { actx->_nlev[0] = nrrdNuke(actx->_nlev[0]); actx->_nlev[1] = nrrdNuke(actx->_nlev[1]); actx->nparm = nrrdNuke(actx->nparm); actx->nten = nrrdNuke(actx->nten); free(actx); } return NULL; } #define GOT_NULL \ if (!actx) { \ biffAddf(ALAN, "%s: got NULL pointer", me); \ return 1; \ } #define DIM_SET \ if (0 == actx->dim) { \ biffAddf(ALAN, "%s: dimension of texture not set", me); \ return 1; \ } int alanDimensionSet(alanContext *actx, int dim) { static const char me[]="alanDimensionSet"; GOT_NULL; if (!( dim == 2 || dim == 3 )) { biffAddf(ALAN, "%s: dimension must be 2 or 3, not %d", me, dim); return 1; } actx->dim = dim; return 0; } int alan2DSizeSet(alanContext *actx, int sizeX, int sizeY) { static const char me[]="alan2DSizeSet"; GOT_NULL; DIM_SET; if (2 != actx->dim) { biffAddf(ALAN, "%s: texture not two-dimensional", me); return 1; } if (!( sizeX >= 10 && sizeY >= 10 )) { biffAddf(ALAN, "%s: sizes (%d,%d) invalid (too small?)", me, sizeX, sizeY); return 1; } actx->size[0] = sizeX; actx->size[1] = sizeY; return 0; } int alan3DSizeSet(alanContext *actx, int sizeX, int sizeY, int sizeZ) { static const char me[]="alan2DSizeSet"; GOT_NULL; DIM_SET; if (3 != actx->dim) { biffAddf(ALAN, "%s: texture not three-dimensional", me); return 1; } if (!( sizeX >= 10 && sizeY >= 10 && sizeZ >= 10 )) { biffAddf(ALAN, "%s: sizes (%d,%d,%d) invalid (too small?)", me, sizeX, sizeY, sizeZ); return 1; } actx->size[0] = sizeX; actx->size[1] = sizeY; actx->size[2] = sizeZ; return 0; } int alanTensorSet(alanContext *actx, Nrrd *nten, int oversample) { static const char me[]="alanTensorSet"; if (!( actx && nten )) { biffAddf(ALAN, "%s: got NULL pointer", me); return 1; } DIM_SET; if (!( oversample > 0 )) { biffAddf(ALAN, "%s: oversample %d invalid", me, oversample); return 1; } if (2 == actx->dim) { if (!( 3 == nten->dim && 4 == nten->axis[0].size )) { biffAddf(ALAN, "%s: didn't get 3-D (4,X,Y) nrrd", me); return 1; } } else { if (!( 4 == nten->dim && 7 == nten->axis[0].size )) { biffAddf(ALAN, "%s: didn't get 4-D (7,X,Y,Z) nrrd", me); return 1; } } if (1 != oversample) { biffAddf(ALAN, "%s: sorry, can only handle oversample==1 now", me); return 1; } actx->nten = nrrdNuke(actx->nten); actx->nten = nrrdNew(); if (nrrdConvert(actx->nten, nten, alan_nt)) { biffMovef(ALAN, NRRD, "%s: trouble converting tensors to alan_t", me); return 1; } actx->size[0] = AIR_UINT(oversample*nten->axis[1].size); actx->size[1] = AIR_UINT(oversample*nten->axis[2].size); if (3 == actx->dim) { actx->size[2] = AIR_UINT(oversample*nten->axis[3].size); } else { actx->size[2] = 1; } return 0; } int alanParmSet(alanContext *actx, int whichParm, double parm) { static const char me[]="alanParmSet"; int parmI; GOT_NULL; DIM_SET; switch (whichParm) { case alanParmVerbose: parmI = !!parm; actx->verbose = parmI; break; case alanParmTextureType: parmI = !!parm; switch(parmI) { case alanTextureTypeTuring: actx->initA = 4.0; actx->initB = 4.0; actx->diffA = 0.25; actx->diffB = 0.0625; break; case alanTextureTypeGrayScott: actx->initA = 1; actx->initB = 0; actx->diffA = 0.00002f; actx->diffB = 0.00002f; break; default: biffAddf(ALAN, "%s: texture type %d invalid", me, parmI); return 1; break; } actx->textureType = parmI; break; case alanParmNumThreads: parmI = !!parm; if (!airThreadCapable) { fprintf(stderr, "%s: WARNING: no multi-threading available, so 1 thread " "will be used, not %d\n", me, parmI); parmI = 1; } actx->numThreads = parmI; break; case alanParmHomogAniso: parmI = !!parm; actx->homogAniso = parmI; break; case alanParmSaveInterval: parmI = !!parm; actx->saveInterval = parmI; break; case alanParmFrameInterval: parmI = !!parm; actx->frameInterval = parmI; break; case alanParmMaxIteration: parmI = !!parm; actx->maxIteration = parmI; break; case alanParmConstantFilename: parmI = !!parm; actx->constFilename = parmI; break; case alanParmDeltaT: actx->deltaT = AIR_CAST(alan_t, parm); break; case alanParmDeltaX: actx->deltaX = AIR_CAST(alan_t, parm); break; case alanParmReact: actx->react = AIR_CAST(alan_t, parm); break; case alanParmDiffA: actx->diffA = AIR_CAST(alan_t, parm); break; case alanParmDiffB: actx->diffB = AIR_CAST(alan_t, parm); break; case alanParmRandRange: actx->randRange = AIR_CAST(alan_t, parm); break; case alanParmK: actx->K = AIR_CAST(alan_t, parm); break; case alanParmF: actx->F = AIR_CAST(alan_t, parm); break; case alanParmMinAverageChange: actx->minAverageChange = AIR_CAST(alan_t, parm); break; case alanParmMaxPixelChange: actx->maxPixelChange = AIR_CAST(alan_t, parm); break; case alanParmAlpha: actx->alpha = AIR_CAST(alan_t, parm); break; case alanParmBeta: actx->beta = AIR_CAST(alan_t, parm); break; case alanParmWrapAround: parmI = !!parm; actx->wrap = parmI; break; default: biffAddf(ALAN, "%s: parameter %d invalid", me, whichParm); return 1; break; } return 0; } teem-1.11.0~svn6057/src/alan/GNUmakefile0000664000175000017500000000343412165631065017372 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := alan #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### $(L).NEED = air biff ell nrrd $(L).PUBLIC_HEADERS = alan.h $(L).PRIVATE_HEADERS = $(L).OBJS = methodsAlan.o enumsAlan.o coreAlan.o $(L).TESTS = #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/alan/sources.cmake0000664000175000017500000000032211113047450017765 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(ALAN_SOURCES alan.h coreAlan.c enumsAlan.c methodsAlan.c ) ADD_TEEM_LIBRARY(alan ${ALAN_SOURCES}) teem-1.11.0~svn6057/src/alan/test/0000775000175000017500000000000012203513755016271 5ustar domibeldomibelteem-1.11.0~svn6057/src/alan/test/tspot.c0000664000175000017500000000640512042323644017610 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../alan.h" int main(int argc, char *argv[]) { alanContext *actx; char *err, *me; Nrrd *ninit, *nparm, *npri; me = argv[0]; ninit = nrrdNew(); if (nrrdLoad(ninit, "init.nrrd", NULL)) { fprintf(stderr, "%s: load(init.nrrd) failed\n", me); free(biffGetDone(NRRD)); ninit = nrrdNuke(ninit); } nparm = nrrdNew(); if (nrrdLoad(nparm, "parm.nrrd", NULL)) { fprintf(stderr, "%s: load(parm.nrrd) failed\n", me); free(biffGetDone(NRRD)); nparm = nrrdNuke(nparm); } npri = nrrdNew(); if (nrrdLoad(npri, "pri.nrrd", NULL)) { fprintf(stderr, "%s: load(pri.nrrd) failed\n", me); free(biffGetDone(NRRD)); npri = nrrdNuke(npri); } actx = alanContextNew(); if (alanDimensionSet(actx, 2) || alan2DSizeSet(actx, 100, 100) || alanParmSet(actx, alanParmMaxIteration, 100000) || alanParmSet(actx, alanParmVerbose, 1) || alanParmSet(actx, alanParmTextureType, alanTextureTypeTuring) || alanParmSet(actx, alanParmRandRange, 4.0) || alanParmSet(actx, alanParmK, 0.0125) || alanParmSet(actx, alanParmH, 1.2) || alanParmSet(actx, alanParmAlpha, 16.0+0.07) || alanParmSet(actx, alanParmBeta, 12.0-0.07) || alanParmSet(actx, alanParmSpeed, 1.38) || alanParmSet(actx, alanParmMinAverageChange, 0.00002) || alanParmSet(actx, alanParmSaveInterval, 500) || alanParmSet(actx, alanParmFrameInterval,500) || alanParmSet(actx, alanParmConstantFilename, AIR_TRUE) || alanParmSet(actx, alanParmWrapAround, AIR_TRUE) || alanParmSet(actx, alanParmNumThreads, 10) ) { err = biffGetDone(ALAN); fprintf(stderr, "%s: trouble: %s\n", me, err); free(err); return 1; } if (alanUpdate(actx) || alanInit(actx, ninit, nparm)) { err = biffGetDone(ALAN); fprintf(stderr, "%s: trouble: %s\n", me, err); free(err); return 1; } fprintf(stderr, "%s: going to run (%d threads) . . .\n", me, actx->numThreads); alanRun(actx); fprintf(stderr, "%s: stop = %d: %s\n", me, actx->stop, airEnumDesc(alanStop, actx->stop)); /* nrrdSave("lev0.nrrd", actx->nlev[0], NULL); nrrdSave("lev1.nrrd", actx->nlev[1], NULL); */ actx = alanContextNix(actx); return 0; } teem-1.11.0~svn6057/src/limn/0000775000175000017500000000000012203513753015334 5ustar domibeldomibelteem-1.11.0~svn6057/src/limn/splineMethods.c0000664000175000017500000003623212165631065020330 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" limnSplineTypeSpec * limnSplineTypeSpecNew(int type, ...) { static const char me[]="limnSplineTypeSpecNew"; limnSplineTypeSpec *spec; va_list ap; if (airEnumValCheck(limnSplineType, type)) { biffAddf(LIMN, "%s: given type %d not a valid limnSplineType", me, type); return NULL; } spec = AIR_CALLOC(1, limnSplineTypeSpec); spec->type = type; va_start(ap, type); if (limnSplineTypeBC == type) { spec->B = va_arg(ap, double); spec->C = va_arg(ap, double); } va_end(ap); return spec; } limnSplineTypeSpec * limnSplineTypeSpecNix(limnSplineTypeSpec *spec) { airFree(spec); return NULL; } /* ** _limnSplineTimeWarpSet ** ** implements GK's patented time warping technology */ int _limnSplineTimeWarpSet(limnSpline *spline) { static const char me[]="_limnSplineTimeWarpSet"; double *cpt, *time, ss; int ii, N; cpt = (double*)(spline->ncpt->data); N = spline->ncpt->axis[2].size; time = spline->time; for (ii=0; iiloop) { ss = ((cpt[1+3*1] - cpt[1+3*0] + cpt[1+3*(N-1)] - cpt[1+3*(N-2)]) / (time[1] - time[0] + time[N-1] - time[N-2])); cpt[2 + 3*0] = ss*(time[1] - time[0]); cpt[0 + 3*(N-1)] = ss*(time[N-1] - time[N-2]); } else { cpt[2 + 3*0] = ((cpt[1+3*1] - cpt[1+3*0]) * (time[1] - time[0])); cpt[0 + 3*(N-1)] = ((cpt[1+3*(N-1)] - cpt[1+3*(N-2)]) * (time[N-1] - time[N-2])); } /* fprintf(stderr, "s[0]=%g, post = %g; s[1]=%g pre = %g\n", cpt[1 + 3*0], cpt[2 + 3*0], cpt[1 + 3*1], cpt[0 + 3*1]); */ return 0; } /* ******** limnSplineNew ** ** constructor for limnSplines. We take all the control point information ** here, and copy it internally, in an effort to simplify the management of ** state. The control point nrrd is 3-D, as explained in limn.h ** ** To confuse matters, the Time type of spline doesn't need the control ** point information in the traditional sense, but it still needs to know ** "when" the control points are. So, the _ncpt nrrd is still needed, but ** it is only a 1-D array of times. In this case, the internal ncpt values ** are set automatically. ** ** The benefit of this approach is that if this constructor returns ** successfully, then there is no more information or state that needs to ** be set-- the returned spline can be passed to evaluate or sample. ** LIES LIES LIES: For BC-splines, you still have to call limnSplineBCSet, ** but that's the only exception... */ limnSpline * limnSplineNew(Nrrd *_ncpt, int info, limnSplineTypeSpec *spec) { static const char me[]="limnSplineNew"; limnSpline *spline; size_t N; unsigned int size; airArray *mop; Nrrd *nin; char stmp[2][AIR_STRLEN_SMALL]; if (airEnumValCheck(limnSplineInfo, info)) { biffAddf(LIMN, "%s: info %d not a valid limnSplineInfo", me, info); return NULL; } if (nrrdCheck(_ncpt)) { biffMovef(LIMN, NRRD, "%s: given nrrd has problems", me); return NULL; } if (limnSplineTypeTimeWarp == spec->type) { if (!(limnSplineInfoScalar == info)) { biffAddf(LIMN, "%s: can only time warp scalars", me); return NULL; } if (!( 1 == _ncpt->dim )) { biffAddf(LIMN, "%s: given nrrd has dimension %d, not 1", me, _ncpt->dim); return NULL; } N = _ncpt->axis[0].size; } else { if (!( 3 == _ncpt->dim )) { biffAddf(LIMN, "%s: given nrrd has dimension %d, not 3", me, _ncpt->dim); return NULL; } size = limnSplineInfoSize[info]; if (!( size == _ncpt->axis[0].size && 3 == _ncpt->axis[1].size )) { biffAddf(LIMN, "%s: expected %ux3xN nrrd, not %sx%sxN", me, size, airSprintSize_t(stmp[0], _ncpt->axis[0].size), airSprintSize_t(stmp[1], _ncpt->axis[1].size)); return NULL; } N = _ncpt->axis[2].size; } if (1 == N) { biffAddf(LIMN, "%s: need at least two control points", me); return NULL; } mop = airMopNew(); if (!( spline = AIR_CALLOC(1, limnSpline) )) { biffAddf(LIMN, "%s: couldn't allocate new spline", me); airMopError(mop); return NULL; } airMopAdd(mop, spline, (airMopper)limnSplineNix, airMopOnError); spline->time = NULL; spline->ncpt = NULL; spline->type = spec->type; spline->info = info; spline->loop = AIR_FALSE; spline->B = spec->B; spline->C = spec->C; nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopOnError); if (nrrdConvert(nin, _ncpt, nrrdTypeDouble)) { biffMovef(LIMN, NRRD, "%s: trouble allocating internal nrrd", me); airMopError(mop); return NULL; } if (limnSplineTypeTimeWarp == spec->type) { /* set the time array to the data of the double-converted nin, but the nin itself is scheduled to be nixed */ airMopAdd(mop, nin, (airMopper)nrrdNix, airMopOnOkay); spline->time = (double*)(nin->data); /* now allocate the real control point information */ spline->ncpt = nrrdNew(); airMopAdd(mop, spline->ncpt, (airMopper)nrrdNuke, airMopOnError); if (nrrdMaybeAlloc_va(spline->ncpt, nrrdTypeDouble, 3, AIR_CAST(size_t, 1), AIR_CAST(size_t, 3), _ncpt->axis[0].size)) { biffMovef(LIMN, NRRD, "%s: trouble allocating real control points", me); airMopError(mop); return NULL; } /* and set it all to something useful */ if (_limnSplineTimeWarpSet(spline)) { biffAddf(LIMN, "%s: trouble setting time warp", me); airMopError(mop); return NULL; } } else { /* we set the control point to the double-converted nin, and we're done */ spline->ncpt = nin; } airMopOkay(mop); return spline; } limnSpline * limnSplineNix(limnSpline *spline) { if (spline) { spline->ncpt = (Nrrd *)nrrdNuke(spline->ncpt); spline->time = (double *)airFree(spline->time); airFree(spline); } return NULL; } /* ******** limnSplineNrrdCleverFix ** ** given that the ncpt nrrd for limnSplineNew() has to be a particular ** dimension and shape, and given that convenient ways of creating nrrds ** don't always lead to such configurations, we supply some minimal ** cleverness to bridge the gap. As the name implies, you should be ** wary of this function. ** ** The job of this function is NOT to check the validity of a nrrd for ** a given spline. That is up to limnSplineNew(). ** ** Currently, this function is used by limnHestSpline, but it probably ** won't be used anywhere else within limn. ** ** If requested, we also take a stab at guessing limnSplineInfo. */ int limnSplineNrrdCleverFix(Nrrd *nout, Nrrd *nin, int info, int type) { static const char me[]="limnSplineNrrdCleverFix"; ptrdiff_t min[3], max[3]; size_t N; unsigned int wantSize; Nrrd *ntmpA, *ntmpB; airArray *mop; char stmp[AIR_STRLEN_SMALL]; if (!(nout && nin)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(limnSplineInfo, info) || airEnumValCheck(limnSplineType, type)) { biffAddf(LIMN, "%s: invalid spline info (%d) or type (%d)", me, info, type); return 1; } if (nrrdCheck(nin)) { biffMovef(LIMN, NRRD, "%s: nrrd has problems", me); return 1; } mop = airMopNew(); airMopAdd(mop, ntmpA=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmpB=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); wantSize = limnSplineInfoSize[info]; switch(nin->dim) { case 3: /* we assume that things are okay */ if (nrrdCopy(nout, nin)) { biffMovef(LIMN, NRRD, "%s: trouble setting output", me); airMopError(mop); return 1; } break; case 2: N = nin->axis[1].size; if (wantSize != nin->axis[0].size) { biffAddf(LIMN, "%s: expected axis[0].size %d for info %s, but got %s", me, wantSize, airEnumStr(limnSplineInfo, info), airSprintSize_t(stmp, nin->axis[0].size)); airMopError(mop); return 1; } if (limnSplineTypeTimeWarp == type) { /* time-warp handled differently */ if (nrrdAxesDelete(nout, nin, 0)) { biffMovef(LIMN, NRRD, "%s: couldn't make data 1-D", me); airMopError(mop); return 1; } } else { if (limnSplineTypeHasImplicitTangents[type]) { ELL_3V_SET(min, 0, -1, 0); ELL_3V_SET(max, wantSize-1, 1, N-1); if (nrrdAxesInsert(ntmpA, nin, 1) || nrrdPad_va(nout, ntmpA, min, max, nrrdBoundaryPad, 0.0)) { biffMovef(LIMN, NRRD, "%s: trouble with axinsert/pad", me); airMopError(mop); return 1; } } else { /* the post- and pre- point information may be interlaced with the main control point values */ if (1 != AIR_MOD((int)N, 3)) { biffAddf(LIMN, "%s: axis[1].size must be 1+(multiple of 3) when using " "interlaced tangent information, not %s", me, airSprintSize_t(stmp, N)); airMopError(mop); return 1; } ELL_2V_SET(min, 0, -1); ELL_2V_SET(max, wantSize-1, N); if (nrrdPad_va(ntmpA, nin, min, max, nrrdBoundaryPad, 0.0) || nrrdAxesSplit(nout, ntmpA, 1, 3, (N+2)/3)) { biffMovef(LIMN, NRRD, "%s: trouble with pad/axsplit", me); airMopError(mop); return 1; } } } break; case 1: N = nin->axis[0].size; if (limnSplineInfoScalar != info) { biffAddf(LIMN, "%s: can't have %s spline with 1-D nrrd", me, airEnumStr(limnSplineInfo, info)); airMopError(mop); return 1; } if (limnSplineTypeTimeWarp == type) { /* nothing fancey needed for time-warp */ if (nrrdCopy(nout, nin)) { biffMovef(LIMN, NRRD, "%s: trouble setting output", me); airMopError(mop); return 1; } } else { if (limnSplineTypeHasImplicitTangents[type]) { ELL_3V_SET(min, 0, -1, 0); ELL_3V_SET(max, 0, 1, N-1); if (nrrdAxesInsert(ntmpA, nin, 0) || nrrdAxesInsert(ntmpB, ntmpA, 0) || nrrdPad_va(nout, ntmpB, min, max, nrrdBoundaryPad, 0.0)) { biffMovef(LIMN, NRRD, "%s: trouble with axinsert/axinsert/pad", me); airMopError(mop); return 1; } } else { /* the post- and pre- point information may be interlaced with the main control point values */ if (1 != AIR_MOD((int)N, 3)) { biffAddf(LIMN, "%s: axis[1].size must be 1+(multiple of 3) when using " "interlaced tangent information, not %s", me, airSprintSize_t(stmp, N)); airMopError(mop); return 1; } ELL_2V_SET(min, 0, -1); ELL_2V_SET(max, 0, N+1); if (nrrdAxesInsert(ntmpA, nin, 0) || nrrdPad_va(ntmpB, ntmpA, min, max, nrrdBoundaryPad, 0.0) || nrrdAxesSplit(nout, ntmpB, 1, 3, (N+2)/3)) { biffMovef(LIMN, NRRD, "%s: trouble with axinsert/pad/axsplit", me); airMopError(mop); return 1; } } } break; default: biffAddf(LIMN, "%s: input nrrd dim %d baffling", me, nin->dim); return 1; } if (nrrdCheck(nout)) { biffMovef(LIMN, NRRD, "%s: oops: didn't create valid output", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } limnSpline * limnSplineCleverNew(Nrrd *ncpt, int info, limnSplineTypeSpec *spec) { static const char me[]="limnSplineCleverNew"; limnSpline *spline; Nrrd *ntmp; airArray *mop; if (!( ncpt && spec )) { biffAddf(LIMN, "%s: got NULL pointer", me); return NULL; } mop = airMopNew(); airMopAdd(mop, ntmp = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (limnSplineNrrdCleverFix(ntmp, ncpt, info, spec->type)) { biffAddf(LIMN, "%s: couldn't fix up given control point nrrd", me); airMopError(mop); return NULL; } if (!( spline = limnSplineNew(ntmp, info, spec) )) { biffAddf(LIMN, "%s: couldn't create spline", me); airMopError(mop); return NULL; } airMopOkay(mop); return spline; } int limnSplineUpdate(limnSpline *spline, Nrrd *_ncpt) { static const char me[]="limnSplineUpdate"; Nrrd *ntmp; char stmp[2][AIR_STRLEN_SMALL]; if (!(spline && _ncpt)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (nrrdCheck(_ncpt)) { biffMovef(LIMN, NRRD, "%s: given nrrd has problems", me); return 1; } if (limnSplineTypeTimeWarp == spline->type) { if (!( 1 == _ncpt->dim )) { biffAddf(LIMN, "%s: given nrrd has dimension %d, not 1", me, _ncpt->dim); return 1; } if (!( spline->ncpt->axis[2].size == _ncpt->axis[0].size )) { biffAddf(LIMN, "%s: have %s time points, but got %s", me, airSprintSize_t(stmp[0], spline->ncpt->axis[2].size), airSprintSize_t(stmp[1], _ncpt->axis[0].size)); return 1; } } else { if (!( nrrdSameSize(spline->ncpt, _ncpt, AIR_TRUE) )) { biffMovef(LIMN, NRRD, "%s: given ncpt doesn't match original one", me); return 1; } } if (limnSplineTypeTimeWarp == spline->type) { ntmp = nrrdNew(); if (nrrdWrap_va(ntmp, spline->time, nrrdTypeDouble, 1, _ncpt->axis[0].size) || nrrdConvert(ntmp, _ncpt, nrrdTypeDouble)) { biffMovef(LIMN, NRRD, "%s: trouble copying info", me); nrrdNix(ntmp); return 1; } if (_limnSplineTimeWarpSet(spline)) { biffAddf(LIMN, "%s: trouble setting time warp", me); nrrdNix(ntmp); return 1; } nrrdNix(ntmp); } else { if (nrrdConvert(spline->ncpt, _ncpt, nrrdTypeDouble)) { biffMovef(LIMN, NRRD, "%s: trouble converting to internal nrrd", me); return 1; } } return 0; } teem-1.11.0~svn6057/src/limn/enumsLimn.c0000664000175000017500000000753012165631065017460 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" const char * _limnSpaceStr[LIMN_SPACE_MAX+1] = { "(unknown space)", "world", "view", "screen", "device" }; const airEnum _limnSpace = { "limn space", LIMN_SPACE_MAX, _limnSpaceStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const limnSpace = &_limnSpace; /* ------------------------------------------------------------ */ const char * _limnPolyDataInfoStr[LIMN_POLY_DATA_INFO_MAX+1] = { "(unknown info)", "rgba", "norm", "tex2", "tang" }; const airEnum _limnPolyDataInfo = { "limn polydata info", LIMN_POLY_DATA_INFO_MAX, _limnPolyDataInfoStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const limnPolyDataInfo = &_limnPolyDataInfo; /* ------------------------------------------------------------ */ const char * _limnCameraPathTrackStr[] = { "(unknown limnCameraPathTrack)", "from", "at", "both" }; const char * _limnCameraPathTrackDesc[] = { "unknown limnCameraPathTrack", "track through eye points, quaternions for camera orientation", "track through look-at points, quaternions for camera orientation", "track eye point, look-at point, and up vector with separate splines" }; const char * _limnCameraPathTrackStrEqv[] = { "from", "fr", "at", "look-at", "lookat", "both", "" }; const int _limnCameraPathTrackValEqv[] = { limnCameraPathTrackFrom, limnCameraPathTrackFrom, limnCameraPathTrackAt, limnCameraPathTrackAt, limnCameraPathTrackAt, limnCameraPathTrackBoth }; const airEnum _limnCameraPathTrack = { "limnCameraPathTrack", LIMN_CAMERA_PATH_TRACK_MAX, _limnCameraPathTrackStr, NULL, _limnCameraPathTrackDesc, _limnCameraPathTrackStrEqv, _limnCameraPathTrackValEqv, AIR_FALSE }; const airEnum *const limnCameraPathTrack = &_limnCameraPathTrack; /* ------------------------------------------------------------ */ const char * _limnPrimitiveStr[] = { "(unknown limnPrimitive)", "noop", "triangles", "tristrip", "trifan", "quads", "linestrip", "lines" }; const char * _limnPrimitiveDesc[] = { "unknown limnPrimitive", "no-op", "triangle soup", "triangle strip", "triangle fan", "quad soup", "line strip", "lines" }; const char * _limnPrimitiveStrEqv[] = { "noop", "triangles", "tristrip", "trifan", "quads", "linestrip", "lines", "" }; const int _limnPrimitiveValEqv[] = { limnPrimitiveNoop, limnPrimitiveTriangles, limnPrimitiveTriangleStrip, limnPrimitiveTriangleFan, limnPrimitiveQuads, limnPrimitiveLineStrip, limnPrimitiveLines }; const airEnum _limnPrimitive = { "limnPrimitive", LIMN_PRIMITIVE_MAX, _limnPrimitiveStr, NULL, _limnPrimitiveDesc, _limnPrimitiveStrEqv, _limnPrimitiveValEqv, AIR_FALSE }; const airEnum *const limnPrimitive = &_limnPrimitive; teem-1.11.0~svn6057/src/limn/hestLimn.c0000664000175000017500000000671512165631065017300 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" /* ******** limnHestCameraOptAdd() ** ** calls hestOptAdd a bunch of times to set up command-line options ** useful for specifying a limnCamera. The flags used are as follows: ** fr: cam->from ** at: cam->at ** up: cam->up ** rh: cam->rightHanded ** or: cam->orthographic ** dn: cam->neer ** di: cam->dist ** df: cam->faar ** ar: cam->atRelative ** ur: cam->uRange ** vr: cam->vRange ** fv: cam->fov */ void limnHestCameraOptAdd(hestOpt **hoptP, limnCamera *cam, const char *frDef, const char *atDef, const char *upDef, const char *dnDef, const char *diDef, const char *dfDef, const char *urDef, const char *vrDef, const char *fvDef) { hestOpt *hopt; hopt = *hoptP; hestOptAdd(&hopt, "fr", "eye pos", airTypeDouble, 3, 3, cam->from, frDef, "camera eye point"); hestOptAdd(&hopt, "at", "at pos", airTypeDouble, 3, 3, cam->at, atDef, "camera look-at point"); hestOptAdd(&hopt, "up", "up dir", airTypeDouble, 3, 3, cam->up, upDef, "camera pseudo-up vector"); hestOptAdd(&hopt, "rh", NULL, airTypeInt, 0, 0, &(cam->rightHanded), NULL, "use a right-handed UVN frame (V points down)"); hestOptAdd(&hopt, "or", NULL, airTypeInt, 0, 0, &(cam->orthographic), NULL, "orthogonal (not perspective) projection"); hestOptAdd(&hopt, "dn", "near", airTypeDouble, 1, 1, &(cam->neer), dnDef, "distance to near clipping plane"); hestOptAdd(&hopt, "di", "image", airTypeDouble, 1, 1, &(cam->dist), diDef, "distance to image plane"); hestOptAdd(&hopt, "df", "far", airTypeDouble, 1, 1, &(cam->faar), dfDef, "distance to far clipping plane"); hestOptAdd(&hopt, "ar", NULL, airTypeInt, 0, 0, &(cam->atRelative), NULL, "near, image, and far plane distances are relative to " "the *at* point, instead of the eye point"); hestOptAdd(&hopt, "ur", "uMin uMax", airTypeDouble, 2, 2, cam->uRange, urDef, "range in U direction of image plane"); hestOptAdd(&hopt, "vr", "vMin vMax", airTypeDouble, 2, 2, cam->vRange, vrDef, "range in V direction of image plane"); hestOptAdd(&hopt, "fv", "field of view", airTypeDouble, 1, 1, &(cam->fov), fvDef, "angle (in degrees) vertically subtended by view window"); *hoptP = hopt; return; } teem-1.11.0~svn6057/src/limn/lpuFlotsam.c0000664000175000017500000000445712165631065017644 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" /* ******** limnpuCmdList[] ** ** NULL-terminated array of unrrduCmd pointers, as ordered by ** LIMN_MAP macro */ unrrduCmd * limnpuCmdList[] = { LIMN_MAP(LIMN_LIST) NULL }; /* ******** limnpuUsage ** ** prints out a little banner, and a listing of all available commands ** with their one-line descriptions */ void limnpuUsage(char *me, hestParm *hparm) { int i, maxlen, len, c; char buff[AIR_STRLEN_LARGE], fmt[AIR_STRLEN_LARGE]; maxlen = 0; for (i=0; limnpuCmdList[i]; i++) { maxlen = AIR_MAX(maxlen, (int)strlen(limnpuCmdList[i]->name)); } sprintf(buff, "--- LimnPolyData Hacking ---"); sprintf(fmt, "%%%ds\n", (int)((hparm->columns-strlen(buff))/2 + strlen(buff) - 1)); fprintf(stderr, fmt, buff); for (i=0; limnpuCmdList[i]; i++) { len = strlen(limnpuCmdList[i]->name); strcpy(buff, ""); for (c=len; cname); strcat(buff, " ... "); len = strlen(buff); fprintf(stderr, "%s", buff); _hestPrintStr(stderr, len, len, hparm->columns, limnpuCmdList[i]->info, AIR_FALSE); } } teem-1.11.0~svn6057/src/limn/envmap.c0000664000175000017500000001030512165631065016771 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" int limnEnvMapFill(Nrrd *map, limnEnvMapCB cb, int qnMethod, void *data) { static const char me[]="limnEnvMapFill"; unsigned int sx, sy, qn; float vec[3], *mapData; if (!(map && cb)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (!AIR_IN_OP(limnQNUnknown, qnMethod, limnQNLast)) { biffAddf(LIMN, "%s: QN method %d invalid", me, qnMethod); return 1; } switch(qnMethod) { case limnQN16checker: case limnQN16octa: sx = sy = 256; break; case limnQN14checker: case limnQN14octa: sx = sy = 128; break; case limnQN12checker: case limnQN12octa: sx = sy = 64; break; case limnQN10checker: case limnQN10octa: sx = sy = 32; break; case limnQN8checker: case limnQN8octa: sx = sy = 16; break; case limnQN15octa: sx = 128; sy = 256; break; case limnQN13octa: sx = 64; sy = 128; break; case limnQN11octa: sx = 32; sy = 64; break; case limnQN9octa: sx = 16; sy = 32; break; default: biffAddf(LIMN, "%s: sorry, QN method %d not implemented", me, qnMethod); return 1; } if (nrrdMaybeAlloc_va(map, nrrdTypeFloat, 3, AIR_CAST(size_t, 3), AIR_CAST(size_t, sx), AIR_CAST(size_t, sy))) { biffMovef(LIMN, NRRD, "%s: couldn't alloc output", me); return 1; } mapData = (float *)map->data; for (qn=0; qnamb[0]; g = lit->amb[1]; b = lit->amb[2]; for (i=0; ion[i]) continue; dot = ELL_3V_DOT(vec, lit->dir[i]); dot = AIR_MAX(0, dot); r += dot*lit->col[i][0]; g += dot*lit->col[i][1]; b += dot*lit->col[i][2]; } /* not really our job to be doing clamping here ... */ rgb[0] = r; rgb[1] = g; rgb[2] = b; } int limnEnvMapCheck(Nrrd *envMap) { static const char me[]="limnEnvMapCheck"; if (nrrdCheck(envMap)) { biffMovef(LIMN, NRRD, "%s: basic nrrd validity check failed", me); return 1; } if (!(nrrdTypeFloat == envMap->type)) { biffAddf(LIMN, "%s: type should be %s, not %s", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, envMap->type)); return 1; } if (!(3 == envMap->dim)) { biffAddf(LIMN, "%s: dimension should be 3, not %d", me, envMap->dim); return 1; } if (!(3 == envMap->axis[0].size && 256 == envMap->axis[1].size && 256 == envMap->axis[2].size)) { char stmp[3][AIR_STRLEN_SMALL]; biffAddf(LIMN, "%s: dimension should be 3x256x256, not " "%s x %s x %s", me, airSprintSize_t(stmp[0], envMap->axis[0].size), airSprintSize_t(stmp[1], envMap->axis[1].size), airSprintSize_t(stmp[2], envMap->axis[2].size)); return 1; } return 0; } teem-1.11.0~svn6057/src/limn/light.c0000664000175000017500000000647412165631065016626 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" /* ******** limnLightSet() ** ** turns on a light ** */ void limnLightSet(limnLight *lit, int which, int vsp, float r, float g, float b, float x, float y, float z) { if (lit && AIR_IN_CL(0, which, LIMN_LIGHT_NUM-1)) { lit->on[which] = 1; lit->vsp[which] = vsp; ELL_4V_SET(lit->col[which], r, g, b, 1.0); ELL_4V_SET(lit->_dir[which], x, y, z, 0.0); } } /* ******** limnLightAmbientSet() ** ** sets the ambient light color */ void limnLightAmbientSet(limnLight *lit, float r, float g, float b) { if (lit) { ELL_4V_SET(lit->amb, r, g, b, 1.0); } } /* ******** limnLightUpdate() ** ** copies information from the _dir vectors to the dir vectors. This ** needs to be called even if there are no viewspace lights, so that ** the dir vectors are set and normalized. If there are no viewspace ** lights, "cam" can actually be passed as NULL, but don't get carried ** away... ** ** returns 1 if there was a problem in the camera, otherwise 0. */ int limnLightUpdate(limnLight *lit, limnCamera *cam) { static const char me[]="limnLightUpdate"; double dir[3], _dir[3], uvn[9]={0,0,0,0,0,0,0,0,0}, norm; int i; if (cam) { if (limnCameraUpdate(cam)) { biffAddf(LIMN, "%s: trouble in camera", me); return 1; } ELL_34M_EXTRACT(uvn, cam->V2W); } for (i=0; i_dir[i]); if (cam && lit->vsp[i]) { ELL_3MV_MUL(dir, uvn, _dir); } else { ELL_3V_COPY(dir, _dir); } ELL_3V_NORM(dir, dir, norm); ELL_4V_SET_TT(lit->dir[i], float, dir[0], dir[1], dir[2], 0.0); } return 0; } /* ******** limnLightSwitch ** ** can toggle a light on/off ** ** returns 1 on error, 0 if okay */ void limnLightSwitch(limnLight *lit, int which, int on) { if (lit && AIR_IN_CL(0, which, LIMN_LIGHT_NUM-1)) { lit->on[which] = on; } } void limnLightReset(limnLight *lit) { int i; if (lit) { ELL_4V_SET(lit->amb, 0, 0, 0, 1); for (i=0; i_dir[i], 0, 0, 0, 0); ELL_4V_SET(lit->dir[i], 0, 0, 0, 0); ELL_4V_SET(lit->col[i], 0, 0, 0, 1); lit->on[i] = AIR_FALSE; lit->vsp[i] = AIR_FALSE; } } } teem-1.11.0~svn6057/src/limn/limn.h0000664000175000017500000010727712165631065016466 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah Copyright (C) 2012, 2011, 2010 Thomas Schultz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef LIMN_HAS_BEEN_INCLUDED #define LIMN_HAS_BEEN_INCLUDED #include #include #include #include #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(limn_EXPORTS) || defined(teem_EXPORTS) # define LIMN_EXPORT extern __declspec(dllexport) # else # define LIMN_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define LIMN_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define LIMN limnBiffKey #define LIMN_LIGHT_NUM 8 /* ******** #define LIMN_SPLINE_Q_AVG_EPS ** ** The convergence tolerance used for Buss/Fillmore quaternion ** averaging */ #define LIMN_SPLINE_Q_AVG_EPS 0.00001 /* ****** limnCamera struct ** ** for all standard graphics camera parameters. Image plane is ** spanned by U and V; N always points away from the viewer, U ** always points to the right, V can point up or down, if the ** camera is left- or right-handed, respectively. ** ** Has no dynamically allocated information or pointers. */ typedef struct limnCamera_t { double from[3], /* location of eyepoint */ at[3], /* what eye is looking at */ up[3], /* what is up direction for eye (this is not updated to the "true" up) */ uRange[2], /* range of U values to put on horiz. image axis */ vRange[2], /* range of V values to put on vert. image axis */ fov, /* if non-NaN, and aspect is non-NaN, then {u,v}Range will be set accordingly by limnCameraUpdate(). "fov" is the angle, in degrees, vertically subtended by the view window */ aspect, /* the ratio of horizontal to vertical size of the view window */ neer, faar, /* near and far clipping plane distances (misspelled for the sake of a McRosopht compiler) */ dist; /* distance to image plane */ int atRelative, /* if non-zero: given neer, faar, and dist quantities indicate distance relative to the _at_ point, instead of the usual (in computer graphics) sense if being relative to the eye point */ orthographic, /* no perspective projection: just orthographic */ rightHanded; /* if rightHanded, V = NxU (V points "downwards"), otherwise, V = UxN (V points "upwards") */ /* -------------------------------------------------------------------- End of user-set parameters. Things below are set by limnCameraUpdate -------------------------------------------------------------------- */ double W2V[16], /* World to view transform. The _rows_ of this matrix (its 3x3 submatrix) are the U, V, N vectors which form the view-space coordinate frame. The column-major ordering of elements in the matrix is from ell: 0 4 8 12 1 5 9 13 2 6 10 14 3 7 11 15 */ V2W[16], /* View to world transform */ U[4], V[4], N[4], /* View space basis vectors (in world coords) last element always zero */ vspNeer, vspFaar, /* not usually user-set: near, far, and image plane distances, in view space */ vspDist; } limnCamera; /* ******** struct limnLight ** ** information for directional lighting and the ambient light. All ** the vectors are length 4 (instead of 3) for the sake of passing them ** directly to OpenGL. For colors, the last coefficient (alpha) is ** always 1.0, and for directions it is 0.0 (w, homog coord). ** ** ** Has no dynamically allocated information or pointers */ typedef struct { float amb[4], /* RGBA ambient light color */ _dir[LIMN_LIGHT_NUM][4], /* direction of light[i] (view or world space). This is what the user sets via limnLightSet */ dir[LIMN_LIGHT_NUM][4], /* direction of light[i] (ONLY world space) Not user-set: calculated/copied from _dir[] */ col[LIMN_LIGHT_NUM][4]; /* RGBA color of light[i] */ int on[LIMN_LIGHT_NUM], /* light[i] is on */ vsp[LIMN_LIGHT_NUM]; /* light[i] lives in view space */ } limnLight; enum { limnDeviceUnknown, limnDevicePS, limnDeviceGL, limnDeviceLast }; enum { limnEdgeTypeUnknown, /* 0 */ limnEdgeTypeBackFacet, /* 1: back-facing non-crease */ limnEdgeTypeBackCrease, /* 2: back-facing crease */ limnEdgeTypeContour, /* 3: silhoette edge */ limnEdgeTypeFrontCrease, /* 4: front-facing crease */ limnEdgeTypeFrontFacet, /* 5: front-facing non-crease */ limnEdgeTypeBorder, /* 6: attached to only one face */ limnEdgeTypeLone, /* 7: attached to no faces */ limnEdgeTypeLast }; #define LIMN_EDGE_TYPE_MAX 7 typedef struct { float lineWidth[LIMN_EDGE_TYPE_MAX+1], creaseAngle, /* difference between crease and facet, in *degrees* */ bg[3], /* background color */ edgeColor[3]; /* edge color */ int showpage, /* finish with "showpage" */ wireFrame, /* just render wire-frame */ noBackground; /* refrain from initially filling with bg[] color */ } limnOptsPS; typedef struct { limnOptsPS ps; int device; float scale, bbox[4]; /* minX minY maxX maxY */ int yFlip; FILE *file; } limnWindow; enum { limnSpaceUnknown, /* 0 */ limnSpaceWorld, /* 1 */ limnSpaceView, /* 2 */ limnSpaceScreen, /* 3 */ limnSpaceDevice, /* 4 */ limnSpaceLast }; #define LIMN_SPACE_MAX 4 /* ******** limnPrimitive* enum ** ** primitive types in the limnPolyData (should probably called ** limnPolyDataPrimitiveType). ** ** keep in sync: Deft/src/PolyData.cxx/PolyData::drawImmediate() */ enum { limnPrimitiveUnknown, /* 0 */ limnPrimitiveNoop, /* 1: no-op primitive, nothing drawn */ limnPrimitiveTriangles, /* 2: triangle soup (for GL_TRIANGLES) */ limnPrimitiveTriangleStrip, /* 3: triangle strip (for GL_TRIANGLE_STRIP) */ limnPrimitiveTriangleFan, /* 4: triangle fan (for GL_TRIANGLE_FAN) */ limnPrimitiveQuads, /* 5: quad soup (for GL_QUADS) */ limnPrimitiveLineStrip, /* 6: line strip (for GL_LINE_STRIP) */ limnPrimitiveLines, /* 7: line soup (for GL_LINES) */ limnPrimitiveLast }; #define LIMN_PRIMITIVE_MAX 7 /* ******** struct limnLook ** ** surface properties: pretty much anything having to do with ** appearance, for points, edges, faces, etc. */ typedef struct { float rgba[4]; float kads[3], /* phong: ka, kd, ks */ spow; /* specular power */ } limnLook; /* ******** struct limnVertex ** ** all the information you might want for a point. ** ** This used to have separate coordinate arrays for view, screen, and ** device space, but these have been collapsed (in the interest of space) ** into coord, with obj->vertSpace indicating which space these are in. ** This also used to have a lookIdx (now just rgba[4]), and a partIdx ** (which was never actually used). ** ** Has no dynamically allocated information or pointers */ typedef struct { float world[4], /* world coordinates (homogeneous) */ rgba[4], /* RGBA color */ coord[4], /* coordinates in some space */ worldNormal[3]; /* vertex normal (world coords only) */ } limnVertex; /* ******** struct limnEdge ** ** all the information about an edge ** ** Has no dynamically allocated information or pointers */ typedef struct limnEdge_t { unsigned int vertIdx[2], /* indices into object's master vert array */ lookIdx, /* index into parent's look array */ partIdx; /* which part do we belong to */ int type, /* from the limnEdgeType enum */ faceIdx[2], /* indices into object's master face array */ once; /* flag used for certain kinds of rendering */ } limnEdge; /* ******** struct limnFace ** ** all the information about a face */ typedef struct limnFace_t { float worldNormal[3], screenNormal[3]; unsigned int *vertIdx, /* regular array (*not* airArray) of vertex indices in object's master vert array */ *edgeIdx, /* likewise for edges */ sideNum, /* number of sides (allocated length of {vert,edge}Idx arrays */ lookIdx, partIdx; int visible; /* non-zero if face currently visible */ float depth; } limnFace; /* ******** struct limnPart ** ** one connected part of an object */ typedef struct limnPart_t { /* (air)arrays of indices in object's vert array */ unsigned int *vertIdx, vertIdxNum; airArray *vertIdxArr; /* (air)arrays of indices in object's edge array */ unsigned int *edgeIdx, edgeIdxNum; airArray *edgeIdxArr; /* (air)arrays of indices in object's face array */ unsigned int *faceIdx, faceIdxNum; airArray *faceIdxArr; int lookIdx; float depth; } limnPart; /* ******** struct limnObject ** ** the beast used to represent polygonal objects ** ** Relies on many dynamically allocated arrays ** ** learned: I used to have an array of limnParts inside here, and that ** array was managed by an airArray. Inside the limnPart, are more ** airArrays, for example the faceIdxArr which internally stores the ** *address* of faceIdx. When the array of limnParts is resized, the ** limnPart's faceIdx pointer is still valid, and faceIdxArr is still ** valid, but the faceIdxArr's internal pointer to the faceIdx pointer ** is now bogus. Thus: the weakness of airArrays (as long as they ** aren't giving the data pointer anew for EACH ACCESS), is that you ** must not confuse the airArrays by changing the address of its user ** data pointer. Putting user data pointers inside of a bigger ** airArray is a fine way to create such confusion. */ typedef struct { limnVertex *vert; unsigned int vertNum; airArray *vertArr; limnEdge *edge; unsigned int edgeNum; airArray *edgeArr; limnFace *face; unsigned int faceNum; airArray *faceArr; limnFace **faceSort; /* pointers into "face", sorted by depth */ limnPart **part; unsigned int partNum; /* double indirection, see above */ airArray *partArr; limnPart **partPool; unsigned int partPoolNum; airArray *partPoolArr; limnLook *look; unsigned int lookNum; airArray *lookArr; int vertSpace, /* which space limnVertex->coord is in */ setVertexRGBAFromLook, /* when possible, copy vertex RGB values from limnLook of part (not face) */ doEdges; /* if non-zero, build edges as faces are added */ unsigned incr; /* increment to use with airArrays */ } limnObject; /* ******** struct limnVrt ** ** a very abbreviated limnVertex ** ** this was killed Sun Feb 5 16:50:23 EST 2006 with the re-organization of ** limnPolyData to have per-attribute arrays ** typedef struct { float xyzw[4], / * homogeneous coordinates * / norm[3]; / * normal * / unsigned char rgba[4]; / * RGBA color * / } limnVrt; */ /* ******** limnPolyDataInfo* enum ** ** information that may be known per-vertex in limnPolyData ** ** NOTE: The xyzw position data is always required, and that is always ** allocated and present. This is the optional extra per-vertex information. */ enum { limnPolyDataInfoUnknown, /* 0: nobody knows */ limnPolyDataInfoRGBA, /* 1: RGBA 4-tuple */ limnPolyDataInfoNorm, /* 2: (x,y,z) unit-length 3-vector */ limnPolyDataInfoTex2, /* 3: (s,t) 2D texture coordinates */ limnPolyDataInfoTang, /* 4: unit-length surface tangent 3-vector */ limnPolyDataInfoLast }; #define LIMN_POLY_DATA_INFO_MAX 4 /* ******** limnPolyData ** ** A simpler beast for representing polygonal surfaces and other things ** ** There is no notion of "part" here; there may be multiple ** disconnected pieces inside the surface, but there is no way of ** accessing just one such piece (because this is organized in terms ** of primitives, and each piece may be made of multiple primitives). ** Having separate parts is important for PostScript rendering, but ** the limnPolyData is more OpenGL oriented. ** ** Experimenting with *not* having airArrays here . . . ** ** There need to be per-attribute "xxxNum" variables because otherwise ** it is impossible to unambiguously manage both changes in the number ** of vertices, and changes in the set of attributes required. ** ** The idea is that at some point general gage info could be stored ** per-vertex here, until that happens there is probably not going ** to be explicit dependence of limn on gage (now that limnFeature ** stuff moved to the "seek" library) */ typedef struct { float *xyzw; /* (always allocated) xyzwNum position 4-tuples */ unsigned int xyzwNum; /* logical size of xyzw */ unsigned char *rgba; /* if non-NULL, rgbaNum RGBA color 4-tuples */ unsigned int rgbaNum; /* logical size of rgba */ float *norm; /* if non-NULL, normNum (x,y,z) unit normals */ unsigned int normNum; /* logical size of norm */ float *tex2; /* if non-NULL, tex2Num (s,t) 2D texture coords */ unsigned int tex2Num; /* logical size of tex2 */ float *tang; /* if non-NULL, tangNum unit surface tangents */ unsigned int tangNum; /* logical size of tang */ unsigned int indxNum; /* there are indxNum vertex indices in indx[] */ unsigned int *indx; /* all indices (into above arrays) for all primitives, concatenated together into one array */ unsigned int primNum; /* there are primNum primitives (e.g. tristrips) */ unsigned char *type; /* prim ii is a type[ii] (limnPrimitive* enum) */ unsigned int *icnt; /* prim ii has icnt[ii] vertex indices */ } limnPolyData; /* ******** limnQN enum ** ** the different quantized normal schemes currently supported */ enum { limnQNUnknown, /* 0 */ limnQN16simple, /* 1 */ limnQN16border1, /* 2 */ limnQN16checker, /* 3 */ limnQN16octa, /* 4 */ limnQN15octa, /* 5 */ limnQN14checker, /* 6 */ limnQN14octa, /* 7 */ limnQN13octa, /* 8 */ limnQN12checker, /* 9 */ limnQN12octa, /* 10 */ limnQN11octa, /* 11 */ limnQN10checker, /* 12 */ limnQN10octa, /* 13 */ limnQN9octa, /* 14 */ limnQN8checker, /* 15 */ limnQN8octa, /* 16 */ limnQNLast }; #define LIMN_QN_MAX 16 enum { limnSplineTypeUnknown, /* 0 */ limnSplineTypeLinear, /* 1 */ limnSplineTypeTimeWarp, /* 2 */ limnSplineTypeHermite, /* 3 */ limnSplineTypeCubicBezier, /* 4 */ limnSplineTypeBC, /* 5 */ limnSplineTypeLast }; #define LIMN_SPLINE_TYPE_MAX 5 enum { limnSplineInfoUnknown, /* 0 */ limnSplineInfoScalar, /* 1 */ limnSplineInfo2Vector, /* 2 */ limnSplineInfo3Vector, /* 3 */ limnSplineInfoNormal, /* 4 */ limnSplineInfo4Vector, /* 5 */ limnSplineInfoQuaternion, /* 6 */ limnSplineInfoLast }; #define LIMN_SPLINE_INFO_MAX 6 enum { limnCameraPathTrackUnknown, /* 0 */ limnCameraPathTrackFrom, /* 1: 3-D spline for *from* points, quaternion spline for camera directions towards at */ limnCameraPathTrackAt, /* 2: 3-D spline for *at* points, quaternion spline for directions back to camera */ limnCameraPathTrackBoth, /* 3: three 3-D splines: for from point, at point, and the up vector */ limnCameraPathTrackLast }; #define LIMN_CAMERA_PATH_TRACK_MAX 3 /* ******** limnSpline ** ** the ncpt nrrd stores control point information in a 3-D nrrd, with ** sizes C by 3 by N, where C is the number of values needed for each ** point (3 for 3Vecs, 1 for scalars), and N is the number of control ** points. The 3 things per control point are 0) the pre-point info ** (either inward tangent or an internal control point), 1) the control ** point itself, 2) the post-point info (e.g., outward tangent). ** ** NOTE: for the sake of simplicity, the ncpt nrrd is always "owned" ** by the limnSpline, that is, it is COPIED from the one given in ** limnSplineNew() (and is converted to type double along the way), ** and it will is deleted with limnSplineNix. */ typedef struct limnSpline_t { int type, /* from limnSplineType* enum */ info, /* from limnSplineInfo* enum */ loop; /* the last (implicit) control point is the first */ double B, C; /* B,C values for BC-splines */ Nrrd *ncpt; /* the control point info, ALWAYS a 3-D nrrd */ double *time; /* ascending times for non-uniform control points. Currently, only used for limnSplineTypeTimeWarp */ } limnSpline; typedef struct limnSplineTypeSpec_t { int type; /* from limnSplineType* enum */ double B, C; /* B,C values for BC-splines */ } limnSplineTypeSpec; /* defaultsLimn.c */ LIMN_EXPORT const int limnPresent; LIMN_EXPORT const char *limnBiffKey; LIMN_EXPORT int limnDefCameraAtRelative; LIMN_EXPORT int limnDefCameraOrthographic; LIMN_EXPORT int limnDefCameraRightHanded; /* enumsLimn.c */ LIMN_EXPORT const airEnum *const limnSpace; LIMN_EXPORT const airEnum *const limnPolyDataInfo; LIMN_EXPORT const airEnum *const limnCameraPathTrack; LIMN_EXPORT const airEnum *const limnPrimitive; /* qn.c */ LIMN_EXPORT unsigned int limnQNBins[LIMN_QN_MAX+1]; LIMN_EXPORT void (*limnQNtoV_f[LIMN_QN_MAX+1])(float *vec, unsigned int qn); LIMN_EXPORT void (*limnQNtoV_d[LIMN_QN_MAX+1])(double *vec, unsigned int qn); LIMN_EXPORT unsigned int (*limnVtoQN_f[LIMN_QN_MAX+1])(const float *vec); LIMN_EXPORT unsigned int (*limnVtoQN_d[LIMN_QN_MAX+1])(const double *vec); LIMN_EXPORT int limnQNDemo(Nrrd *nqn, unsigned int reso, int qni); /* light.c */ LIMN_EXPORT void limnLightSet(limnLight *lit, int which, int vsp, float r, float g, float b, float x, float y, float z); LIMN_EXPORT void limnLightAmbientSet(limnLight *lit, float r, float g, float b); LIMN_EXPORT void limnLightSwitch(limnLight *lit, int which, int on); LIMN_EXPORT void limnLightReset(limnLight *lit); LIMN_EXPORT int limnLightUpdate(limnLight *lit, limnCamera *cam); /* envmap.c */ typedef void (*limnEnvMapCB)(float rgb[3], float vec[3], void *data); LIMN_EXPORT int limnEnvMapFill(Nrrd *envMap, limnEnvMapCB cb, int qnMethod, void *data); LIMN_EXPORT void limnLightDiffuseCB(float rgb[3], float vec[3], void *_lit); LIMN_EXPORT int limnEnvMapCheck(Nrrd *envMap); /* methodsLimn.c */ LIMN_EXPORT limnLight *limnLightNew(void); LIMN_EXPORT void limnCameraInit(limnCamera *cam); LIMN_EXPORT limnLight *limnLightNix(limnLight *); LIMN_EXPORT limnCamera *limnCameraNew(void); LIMN_EXPORT limnCamera *limnCameraNix(limnCamera *cam); LIMN_EXPORT limnWindow *limnWindowNew(int device); LIMN_EXPORT limnWindow *limnWindowNix(limnWindow *win); /* hestLimn.c */ LIMN_EXPORT void limnHestCameraOptAdd(hestOpt **hoptP, limnCamera *cam, const char *frDef, const char *atDef, const char *upDef, const char *dnDef, const char *diDef, const char *dfDef, const char *urDef, const char *vrDef, const char *fvDef); /* cam.c */ LIMN_EXPORT int limnCameraAspectSet(limnCamera *cam, unsigned int horz, unsigned int vert, int centering); LIMN_EXPORT int limnCameraUpdate(limnCamera *cam); LIMN_EXPORT int limnCameraPathMake(limnCamera *cam, int numFrames, limnCamera *keycam, double *time, int numKeys, int trackFrom, limnSplineTypeSpec *quatType, limnSplineTypeSpec *posType, limnSplineTypeSpec *distType, limnSplineTypeSpec *viewType); /* obj.c */ LIMN_EXPORT int limnObjectLookAdd(limnObject *obj); LIMN_EXPORT limnObject *limnObjectNew(int incr, int doEdges); LIMN_EXPORT limnObject *limnObjectNix(limnObject *obj); LIMN_EXPORT void limnObjectEmpty(limnObject *obj); LIMN_EXPORT int limnObjectPreSet(limnObject *obj, unsigned int partNum, unsigned int lookNum, unsigned int vertPerPart, unsigned int edgePerPart, unsigned int facePerPart); LIMN_EXPORT int limnObjectPartAdd(limnObject *obj); LIMN_EXPORT int limnObjectVertexNumPreSet(limnObject *obj, unsigned int partIdx, unsigned int vertNum); LIMN_EXPORT int limnObjectVertexAdd(limnObject *obj, unsigned int partIdx, float x, float y, float z); LIMN_EXPORT int limnObjectEdgeAdd(limnObject *obj, unsigned int partIdx, unsigned int lookIdx, unsigned int faceIdx, unsigned int vertIdx0, unsigned int vertIdx1); LIMN_EXPORT int limnObjectFaceNumPreSet(limnObject *obj, unsigned int partIdx, unsigned int faceNum); LIMN_EXPORT int limnObjectFaceAdd(limnObject *obj, unsigned int partIdx, unsigned int lookIdx, unsigned int sideNum, unsigned int *vertIdx); /* polydata.c */ LIMN_EXPORT limnPolyData *limnPolyDataNew(void); LIMN_EXPORT limnPolyData *limnPolyDataNix(limnPolyData *pld); LIMN_EXPORT unsigned int limnPolyDataInfoBitFlag(const limnPolyData *pld); LIMN_EXPORT int limnPolyDataAlloc(limnPolyData *pld, unsigned int infoBitFlag, unsigned int vertNum, unsigned int indxNum, unsigned int primNum); LIMN_EXPORT size_t limnPolyDataSize(const limnPolyData *pld); LIMN_EXPORT int limnPolyDataCopy(limnPolyData *pldB, const limnPolyData *pldA); LIMN_EXPORT int limnPolyDataCopyN(limnPolyData *pldB, const limnPolyData *pldA, unsigned int num); LIMN_EXPORT void limnPolyDataTransform_f(limnPolyData *pld, const float homat[16]); LIMN_EXPORT void limnPolyDataTransform_d(limnPolyData *pld, const double homat[16]); LIMN_EXPORT unsigned int limnPolyDataPolygonNumber(const limnPolyData *pld); LIMN_EXPORT int limnPolyDataVertexNormals(limnPolyData *pld); LIMN_EXPORT int limnPolyDataVertexNormalsNO(limnPolyData *pld); LIMN_EXPORT unsigned int limnPolyDataPrimitiveTypes(const limnPolyData *pld); LIMN_EXPORT int limnPolyDataPrimitiveVertexNumber(Nrrd *nout, limnPolyData *pld); LIMN_EXPORT int limnPolyDataPrimitiveArea(Nrrd *nout, limnPolyData *pld); LIMN_EXPORT int limnPolyDataRasterize(Nrrd *nout, limnPolyData *pld, double min[3], double max[3], size_t size[3], int type); LIMN_EXPORT void limnPolyDataColorSet(limnPolyData *pld, unsigned char RR, unsigned char GG, unsigned char BB, unsigned char AA); /* polyshapes.c */ LIMN_EXPORT int limnPolyDataCube(limnPolyData *pld, unsigned int infoBitFlag, int sharpEdge); LIMN_EXPORT int limnPolyDataCubeTriangles(limnPolyData *pld, unsigned int infoBitFlag, int sharpEdge); LIMN_EXPORT int limnPolyDataOctahedron(limnPolyData *pld, unsigned int infoBitFlag, int sharpEdge); LIMN_EXPORT int limnPolyDataCone(limnPolyData *pld, unsigned int infoBitFlag, unsigned int res, int sharpEdge); LIMN_EXPORT int limnPolyDataCylinder(limnPolyData *pld, unsigned int infoBitFlag, unsigned int res, int sharpEdge); LIMN_EXPORT int limnPolyDataSuperquadric(limnPolyData *pld, unsigned int infoBitFlag, float A, float B, unsigned int thetaRes, unsigned int phiRes); LIMN_EXPORT int limnPolyDataSpiralBetterquadric(limnPolyData *pld, unsigned int infoBitFlag, float alpha, float beta, float cee, float minRad, unsigned int thetaRes, unsigned int phiRes); LIMN_EXPORT int limnPolyDataSpiralSuperquadric(limnPolyData *pld, unsigned int infoBitFlag, float A, float B, unsigned int thetaRes, unsigned int phiRes); LIMN_EXPORT int limnPolyDataPolarSphere(limnPolyData *pld, unsigned int infoBitFlag, unsigned int thetaRes, unsigned int phiRes); LIMN_EXPORT int limnPolyDataSpiralSphere(limnPolyData *pld, unsigned int infoBitFlag, unsigned int thetaRes, unsigned int phiRes); LIMN_EXPORT int limnPolyDataIcoSphere(limnPolyData *pld, unsigned int infoBitFlag, unsigned int level); LIMN_EXPORT int limnPolyDataPlane(limnPolyData *pld, unsigned int infoBitFlag, unsigned int uRes, unsigned int vRes); LIMN_EXPORT int limnPolyDataSquare(limnPolyData *pld, unsigned int infoBitFlag); /* polymod.c */ LIMN_EXPORT int limnPolyDataEdgeHalve(limnPolyData *pldOut, const limnPolyData *pldIn); LIMN_EXPORT int limnPolyDataVertexWindingFix(limnPolyData *pld, int allowSplitting); LIMN_EXPORT int limnPolyDataClip(limnPolyData *pld, Nrrd *nval, double thresh); LIMN_EXPORT int limnPolyDataClipMulti(limnPolyData *pld, Nrrd *nval, double *thresh); LIMN_EXPORT limnPolyData *limnPolyDataCompress(const limnPolyData *pld); LIMN_EXPORT limnPolyData *limnPolyDataJoin(const limnPolyData **plds, unsigned int num); LIMN_EXPORT int limnPolyDataVertexWindingFlip(limnPolyData *pld); LIMN_EXPORT int limnPolyDataCCFind(limnPolyData *pld); LIMN_EXPORT int limnPolyDataPrimitiveSort(limnPolyData *pld, const Nrrd *nval); LIMN_EXPORT int limnPolyDataPrimitiveSelect(limnPolyData *pldOut, const limnPolyData *pldIn, const Nrrd *nmask); LIMN_EXPORT int limnPolyDataNeighborList(unsigned int **nblist, size_t *len, unsigned int *maxnb, const limnPolyData *pld); LIMN_EXPORT int limnPolyDataNeighborArray(int **neighbors, unsigned int *maxnb, const limnPolyData *pld); LIMN_EXPORT int limnPolyDataNeighborArrayComp(int **neighbors, int **idx, const limnPolyData *pld); /* polyfilter.c */ LIMN_EXPORT int limnPolyDataSpiralTubeWrap(limnPolyData *pldOut, const limnPolyData *pldIn, unsigned int infoBitFlag, Nrrd *nvertMap, unsigned int tubeFacet, unsigned int endFacet, double radius); LIMN_EXPORT int limnPolyDataSmoothHC(limnPolyData *pld, int *neighbors, int *idx, double alpha, double beta, int iter); /* io.c */ LIMN_EXPORT int limnObjectDescribe(FILE *file, const limnObject *obj); LIMN_EXPORT int limnObjectReadOFF(limnObject *obj, FILE *file); LIMN_EXPORT int limnObjectWriteOFF(FILE *file, const limnObject *obj); LIMN_EXPORT int limnPolyDataWriteIV(FILE *file, const limnPolyData *pld); LIMN_EXPORT int limnPolyDataWriteLMPD(FILE *file, const limnPolyData *pld); LIMN_EXPORT int limnPolyDataReadLMPD(limnPolyData *pld, FILE *file); LIMN_EXPORT int limnPolyDataWriteVTK(FILE *file, const limnPolyData *pld); LIMN_EXPORT int limnPolyDataReadOFF(limnPolyData *pld, FILE *file); LIMN_EXPORT int limnPolyDataSave(const char *fname, const limnPolyData *lpld); LIMN_EXPORT hestCB *limnHestPolyDataLMPD; LIMN_EXPORT hestCB *limnHestPolyDataOFF; /* shapes.c */ LIMN_EXPORT int limnObjectCubeAdd(limnObject *obj, unsigned int lookIdx); LIMN_EXPORT int limnObjectSquareAdd(limnObject *obj, unsigned int lookIdx); LIMN_EXPORT int limnObjectCylinderAdd(limnObject *obj, unsigned int lookIdx, unsigned int axis, unsigned int res); LIMN_EXPORT int limnObjectPolarSphereAdd(limnObject *obj, unsigned int lookIdx, unsigned int axis, unsigned int thetaRes, unsigned int phiRes); LIMN_EXPORT int limnObjectConeAdd(limnObject *obj, unsigned int lookIdx, unsigned int axis, unsigned int res); LIMN_EXPORT int limnObjectPolarSuperquadAdd(limnObject *obj, unsigned int lookIdx, unsigned int axis, float A, float B, unsigned int thetaRes, unsigned int phiRes); LIMN_EXPORT int limnObjectPolarSuperquadFancyAdd(limnObject *obj, unsigned int lookIdx, unsigned int axis, float A, float B, float C, float R, unsigned int thetaRes, unsigned int phiRes); /* transform.c */ LIMN_EXPORT int limnObjectWorldHomog(limnObject *obj); LIMN_EXPORT int limnObjectFaceNormals(limnObject *obj, int space); LIMN_EXPORT int limnObjectVertexNormals(limnObject *obj); LIMN_EXPORT int limnObjectSpaceTransform(limnObject *obj, limnCamera *cam, limnWindow *win, int space); LIMN_EXPORT int limnObjectPartTransform(limnObject *obj, unsigned int partIdx, float tx[16]); LIMN_EXPORT int limnObjectDepthSortParts(limnObject *obj); LIMN_EXPORT int limnObjectDepthSortFaces(limnObject *obj); LIMN_EXPORT int limnObjectFaceReverse(limnObject *obj); /* renderLimn.c */ LIMN_EXPORT int limnObjectRender(limnObject *obj, limnCamera *cam, limnWindow *win); LIMN_EXPORT int limnObjectPSDraw(limnObject *obj, limnCamera *cam, Nrrd *envMap, limnWindow *win); LIMN_EXPORT int limnObjectPSDrawConcave(limnObject *obj, limnCamera *cam, Nrrd *envMap, limnWindow *win); /* splineMethods.c */ LIMN_EXPORT limnSplineTypeSpec *limnSplineTypeSpecNew(int type, ...); LIMN_EXPORT limnSplineTypeSpec * limnSplineTypeSpecNix(limnSplineTypeSpec *spec); LIMN_EXPORT limnSpline *limnSplineNew(Nrrd *ncpt, int info, limnSplineTypeSpec *spec); LIMN_EXPORT limnSpline *limnSplineNix(limnSpline *spline); LIMN_EXPORT int limnSplineNrrdCleverFix(Nrrd *nout, Nrrd *nin, int info, int type); LIMN_EXPORT limnSpline *limnSplineCleverNew(Nrrd *ncpt, int info, limnSplineTypeSpec *spec); LIMN_EXPORT int limnSplineUpdate(limnSpline *spline, Nrrd *ncpt); /* splineMisc.c */ LIMN_EXPORT const airEnum *const limnSplineType; LIMN_EXPORT const airEnum *const limnSplineInfo; LIMN_EXPORT limnSpline *limnSplineParse(char *str); LIMN_EXPORT limnSplineTypeSpec *limnSplineTypeSpecParse(char *str); LIMN_EXPORT hestCB *limnHestSpline; LIMN_EXPORT hestCB *limnHestSplineTypeSpec; LIMN_EXPORT unsigned int limnSplineInfoSize[LIMN_SPLINE_INFO_MAX+1]; LIMN_EXPORT int limnSplineTypeHasImplicitTangents[LIMN_SPLINE_TYPE_MAX+1]; LIMN_EXPORT int limnSplineNumPoints(limnSpline *spline); LIMN_EXPORT double limnSplineMinT(limnSpline *spline); LIMN_EXPORT double limnSplineMaxT(limnSpline *spline); LIMN_EXPORT void limnSplineBCSet(limnSpline *spline, double B, double C); /* splineEval.c */ LIMN_EXPORT void limnSplineEvaluate(double *out, limnSpline *spline, double time); LIMN_EXPORT int limnSplineNrrdEvaluate(Nrrd *nout, limnSpline *spline, Nrrd *nin); LIMN_EXPORT int limnSplineSample(Nrrd *nout, limnSpline *spline, double minT, size_t M, double maxT); /* lpu{Flotsam,. . .}.c */ #define LIMN_DECLARE(C) LIMN_EXPORT unrrduCmd limnpu_##C##Cmd; #define LIMN_LIST(C) &limnpu_##C##Cmd, /* F(clip) \ */ /* F(vwflip) \ */ /* F(vwfix) */ #define LIMN_MAP(F) \ F(about) \ F(ccfind) \ F(psel) \ F(rast) \ F(verts) \ F(meas) \ F(sort) LIMN_MAP(LIMN_DECLARE) LIMN_EXPORT unrrduCmd *limnpuCmdList[]; LIMN_EXPORT void limnpuUsage(char *me, hestParm *hparm); #ifdef __cplusplus } #endif #endif /* LIMN_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/limn/obj.c0000664000175000017500000002647612165631065016275 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" /* restricted to this file now since here is the only place its needed */ typedef union { limnVertex **vert; limnEdge **edge; limnFace **face; limnLook **look; limnPart ***partp; void **v; } limnPtrPtrUnion; int limnObjectLookAdd(limnObject *obj) { int lookIdx; limnLook *look; lookIdx = airArrayLenIncr(obj->lookArr, 1); look = &(obj->look[lookIdx]); ELL_4V_SET(look->rgba, 1, 1, 1, 1); ELL_3V_SET(look->kads, 0.0, 1.0, 0.0); look->spow = 50; return lookIdx; } limnObject * limnObjectNew(int incr, int doEdges) { limnObject *obj; limnPtrPtrUnion lppu; obj = AIR_CALLOC(1, limnObject); obj->vert = NULL; obj->edge = NULL; obj->face = NULL; obj->faceSort = NULL; obj->part = NULL; obj->partPool = NULL; obj->look = NULL; /* create all various airArrays */ obj->vertArr = airArrayNew((lppu.vert = &(obj->vert), lppu.v), &(obj->vertNum), sizeof(limnVertex), incr); obj->edgeArr = airArrayNew((lppu.edge = &(obj->edge), lppu.v), &(obj->edgeNum), sizeof(limnEdge), incr); obj->faceArr = airArrayNew((lppu.face = &(obj->face), lppu.v), &(obj->faceNum), sizeof(limnFace), incr); obj->partArr = airArrayNew((lppu.partp = &(obj->part), lppu.v), &(obj->partNum), sizeof(limnPart*), incr); obj->partPoolArr = airArrayNew((lppu.partp = &(obj->partPool), lppu.v), &(obj->partPoolNum), sizeof(limnPart*), incr); obj->lookArr = airArrayNew((lppu.look = &(obj->look), lppu.v), &(obj->lookNum), sizeof(limnLook), incr); /* create (default) look 0 */ limnObjectLookAdd(obj); obj->vertSpace = limnSpaceUnknown; obj->setVertexRGBAFromLook = AIR_FALSE; obj->doEdges = doEdges; obj->incr = incr; return obj; } limnPart * _limnObjectPartNew(int incr) { limnPart *part; airPtrPtrUnion appu; part = AIR_CALLOC(1, limnPart); if (part) { part->vertIdx = NULL; part->edgeIdx = NULL; part->faceIdx = NULL; part->vertIdxArr = airArrayNew((appu.ui = &(part->vertIdx), appu.v), &(part->vertIdxNum), sizeof(int), incr); part->edgeIdxArr = airArrayNew((appu.ui = &(part->edgeIdx), appu.v), &(part->edgeIdxNum), sizeof(int), incr); part->faceIdxArr = airArrayNew((appu.ui = &(part->faceIdx), appu.v), &(part->faceIdxNum), sizeof(int), incr); } return part; } limnPart * _limnObjectPartNix(limnPart *part) { if (part) { airArrayNuke(part->vertIdxArr); airArrayNuke(part->edgeIdxArr); airArrayNuke(part->faceIdxArr); airFree(part); } return NULL; } void _limnObjectFaceEmpty(limnFace *face) { if (face) { airFree(face->vertIdx); airFree(face->edgeIdx); } return; } limnObject * limnObjectNix(limnObject *obj) { unsigned int partIdx, faceIdx; if (obj) { for (partIdx=0; partIdxpartNum; partIdx++) { _limnObjectPartNix(obj->part[partIdx]); } airArrayNuke(obj->partArr); for (partIdx=0; partIdxpartPoolNum; partIdx++) { _limnObjectPartNix(obj->partPool[partIdx]); } airArrayNuke(obj->partPoolArr); for (faceIdx=0; faceIdxfaceNum; faceIdx++) { _limnObjectFaceEmpty(obj->face + faceIdx); } airArrayNuke(obj->faceArr); airArrayNuke(obj->vertArr); airArrayNuke(obj->edgeArr); airFree(obj->faceSort); airArrayNuke(obj->lookArr); airFree(obj); } return NULL; } void limnObjectEmpty(limnObject *obj) { unsigned int partIdx, faceIdx; for (partIdx=0; partIdxpartNum; partIdx++) { _limnObjectPartNix(obj->part[partIdx]); } airArrayLenSet(obj->partArr, 0); for (partIdx=0; partIdxpartPoolNum; partIdx++) { _limnObjectPartNix(obj->partPool[partIdx]); } airArrayLenSet(obj->partPoolArr, 0); for (faceIdx=0; faceIdxfaceNum; faceIdx++) { _limnObjectFaceEmpty(obj->face + faceIdx); } airArrayLenSet(obj->faceArr, 0); airArrayLenSet(obj->vertArr, 0); airArrayLenSet(obj->edgeArr, 0); airFree(obj->faceSort); /* leaves (default) look 0 */ airArrayLenSet(obj->lookArr, 1); /* don't touch state flags */ return; } /* ******** limnObjectPreSet ** ** an attempt at pre-allocating everything that will be needed in a ** limnObject, so that there will be no calloc/memcpy overhead associated ** with growing any of the airArrays inside */ int limnObjectPreSet(limnObject *obj, unsigned int partNum, unsigned int lookNum, unsigned int vertPerPart, unsigned int edgePerPart, unsigned int facePerPart) { limnPart *part; unsigned int partIdx; limnObjectEmpty(obj); airArrayLenPreSet(obj->vertArr, partNum*vertPerPart); airArrayLenPreSet(obj->edgeArr, partNum*edgePerPart); airArrayLenPreSet(obj->faceArr, partNum*facePerPart); airArrayLenPreSet(obj->lookArr, lookNum); airArrayLenPreSet(obj->partArr, partNum); airArrayLenSet(obj->partPoolArr, partNum); for (partIdx=0; partIdxpartPool[partIdx] = _limnObjectPartNew(obj->incr); airArrayLenPreSet(part->vertIdxArr, vertPerPart); airArrayLenPreSet(part->edgeIdxArr, edgePerPart); airArrayLenPreSet(part->faceIdxArr, facePerPart); } return 0; } int limnObjectPartAdd(limnObject *obj) { unsigned int partIdx; limnPart *part; partIdx = airArrayLenIncr(obj->partArr, 1); if (obj->partPoolNum > 0) { part = obj->part[partIdx] = obj->partPool[obj->partPoolNum - 1]; airArrayLenIncr(obj->partPoolArr, -1); airArrayLenSet(part->vertIdxArr, 0); airArrayLenSet(part->edgeIdxArr, 0); airArrayLenSet(part->faceIdxArr, 0); } else { /* there are no available parts in the pool */ part = obj->part[partIdx] = _limnObjectPartNew(obj->incr); } part->lookIdx = 0; part->depth = AIR_NAN; return partIdx; } int limnObjectVertexNumPreSet(limnObject *obj, unsigned int partIdx, unsigned int vertNum) { limnPart *part; part = obj->part[partIdx]; airArrayLenPreSet(obj->vertArr, vertNum); airArrayLenPreSet(part->vertIdxArr, vertNum); return 0; } int limnObjectVertexAdd(limnObject *obj, unsigned int partIdx, float x, float y, float z) { limnPart *part; limnVertex *vert; int vertIdx, vertIdxIdx; part = obj->part[partIdx]; vertIdx = airArrayLenIncr(obj->vertArr, 1); vert = obj->vert + vertIdx; vertIdxIdx = airArrayLenIncr(part->vertIdxArr, 1); part->vertIdx[vertIdxIdx] = vertIdx; ELL_4V_SET(vert->world, x, y, z, 1); ELL_4V_SET(vert->coord, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); /* HEY: this is kind of lame: this information is set in a rather sneaky way, and the setVertexRGBAFromLook is pretty clearly a hack */ if (obj->setVertexRGBAFromLook) { ELL_4V_COPY(vert->rgba, obj->look[part->lookIdx].rgba); } else { ELL_4V_SET(vert->rgba, 1, 1, 1, 1); } /* ELL_3V_SET(vert->view, AIR_NAN, AIR_NAN, AIR_NAN); */ /* ELL_3V_SET(vert->screen, AIR_NAN, AIR_NAN, AIR_NAN); */ ELL_3V_SET(vert->worldNormal, AIR_NAN, AIR_NAN, AIR_NAN); return vertIdx; } int limnObjectEdgeAdd(limnObject *obj, unsigned int partIdx, unsigned int lookIdx, unsigned int faceIdx, unsigned int vertIdx0, unsigned int vertIdx1) { int tmp, edgeIdx=-42; unsigned int edgeIdxIdx; limnEdge *edge=NULL; limnPart *part; part = obj->part[partIdx]; if (vertIdx0 > vertIdx1) { ELL_SWAP2(vertIdx0, vertIdx1, tmp); } /* do a linear search through this part's existing edges */ for (edgeIdxIdx=0; edgeIdxIdxedgeIdxNum; edgeIdxIdx++) { edgeIdx = part->edgeIdx[edgeIdxIdx]; edge = obj->edge + edgeIdx; if (edge->vertIdx[0] == vertIdx0 && edge->vertIdx[1] == vertIdx1) { break; } } if (edgeIdxIdx == part->edgeIdxNum) { /* edge not found, add it */ edgeIdx = airArrayLenIncr(obj->edgeArr, 1); edge = obj->edge + edgeIdx; edgeIdxIdx = airArrayLenIncr(part->edgeIdxArr, 1); part->edgeIdx[edgeIdxIdx] = edgeIdx; edge->vertIdx[0] = vertIdx0; edge->vertIdx[1] = vertIdx1; edge->faceIdx[0] = faceIdx; edge->faceIdx[1] = -1; edge->lookIdx = lookIdx; edge->partIdx = partIdx; edge->type = limnEdgeTypeUnknown; edge->once = AIR_FALSE; } else { /* edge already exists; "edge", "edgeIdx", and "edgeIdxIdx" are all set */ edge->faceIdx[1] = faceIdx; } return edgeIdx; } int limnObjectFaceNumPreSet(limnObject *obj, unsigned int partIdx, unsigned int faceNum) { limnPart *part; part = obj->part[partIdx]; airArrayLenPreSet(obj->faceArr, faceNum); airArrayLenPreSet(part->faceIdxArr, faceNum); return 0; } int limnObjectFaceAdd(limnObject *obj, unsigned int partIdx, unsigned int lookIdx, unsigned int sideNum, unsigned int *vertIdx) { limnFace *face; limnPart *part; unsigned int faceIdx, faceIdxIdx, sideIdx; part = obj->part[partIdx]; faceIdx = airArrayLenIncr(obj->faceArr, 1); face = obj->face + faceIdx; faceIdxIdx = airArrayLenIncr(part->faceIdxArr, 1); part->faceIdx[faceIdxIdx] = faceIdx; face->vertIdx = AIR_CALLOC(sideNum, unsigned int); face->sideNum = sideNum; if (obj->doEdges) { face->edgeIdx = AIR_CALLOC(sideNum, unsigned int); } for (sideIdx=0; sideIdxvertIdx[sideIdx] = vertIdx[sideIdx]; if (obj->doEdges) { face->edgeIdx[sideIdx] = limnObjectEdgeAdd(obj, partIdx, 0, faceIdx, vertIdx[sideIdx], vertIdx[AIR_MOD((int)sideIdx+1, (int)sideNum)]); } } ELL_3V_SET(face->worldNormal, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(face->screenNormal, AIR_NAN, AIR_NAN, AIR_NAN); /* HEY: its potentially confusing that obj->setVertexRGBAFromLook only has an effect with whole parts, and not individual faces */ face->lookIdx = lookIdx; face->partIdx = partIdx; face->visible = AIR_FALSE; face->depth = AIR_NAN; return faceIdx; } teem-1.11.0~svn6057/src/limn/privateLimn.h0000664000175000017500000000357412165631065020014 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef LIMN_PRIVATE_HAS_BEEN_INCLUDED #define LIMN_PRIVATE_HAS_BEEN_INCLUDED #ifdef __cplusplus extern "C" { #endif /* USAGE, PARSE: both copied verbatim from unrrdu/privateUnrrdu.h */ #define USAGE(info) \ if (!argc) { \ hestInfo(stderr, me, (info), hparm); \ hestUsage(stderr, hopt, me, hparm); \ hestGlossary(stderr, hopt, hparm); \ airMopError(mop); \ return 2; \ } #define PARSE() \ if ((pret=hestParse(hopt, argc, argv, &perr, hparm))) { \ if (1 == pret) { \ fprintf(stderr, "%s: %s\n", me, perr); free(perr); \ hestUsage(stderr, hopt, me, hparm); \ airMopError(mop); \ return 2; \ } else { \ exit(1); \ } \ } #ifdef __cplusplus } #endif #endif /* LIMN_PRIVATE_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/limn/polyshapes.c0000664000175000017500000014661612165631065017711 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2010, 2009, 2008 Thomas Schultz Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" int limnPolyDataCube(limnPolyData *pld, unsigned int infoBitFlag, int sharpEdge) { static const char me[]="limnPolyDataCube"; unsigned int vertNum, vertIdx, primNum, indxNum, cnum, ci; vertNum = sharpEdge ? 6*4 : 8; primNum = 1; indxNum = 6*4; if (limnPolyDataAlloc(pld, infoBitFlag, vertNum, indxNum, primNum)) { biffAddf(LIMN, "%s: couldn't allocate output", me); return 1; } pld->type[0] = limnPrimitiveQuads; pld->icnt[0] = indxNum; vertIdx = 0; cnum = sharpEdge ? 3 : 1; for (ci=0; cixyzw + 4*vertIdx, -1, -1, -1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, -1, 1, -1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, 1, 1, -1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, 1, -1, -1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, -1, -1, 1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, -1, 1, 1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, 1, 1, 1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, 1, -1, 1, 1); vertIdx++; } vertIdx = 0; if (sharpEdge) { ELL_4V_SET(pld->indx + vertIdx, 0, 3, 6, 9); vertIdx += 4; ELL_4V_SET(pld->indx + vertIdx, 2, 14, 16, 5); vertIdx += 4; ELL_4V_SET(pld->indx + vertIdx, 4, 17, 18, 8); vertIdx += 4; ELL_4V_SET(pld->indx + vertIdx, 7, 19, 21, 10); vertIdx += 4; ELL_4V_SET(pld->indx + vertIdx, 1, 11, 23, 13); vertIdx += 4; ELL_4V_SET(pld->indx + vertIdx, 12, 22, 20, 15); vertIdx += 4; } else { ELL_4V_SET(pld->indx + vertIdx, 0, 1, 2, 3); vertIdx += 4; ELL_4V_SET(pld->indx + vertIdx, 0, 4, 5, 1); vertIdx += 4; ELL_4V_SET(pld->indx + vertIdx, 1, 5, 6, 2); vertIdx += 4; ELL_4V_SET(pld->indx + vertIdx, 2, 6, 7, 3); vertIdx += 4; ELL_4V_SET(pld->indx + vertIdx, 0, 3, 7, 4); vertIdx += 4; ELL_4V_SET(pld->indx + vertIdx, 4, 7, 6, 5); vertIdx += 4; } if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { if (sharpEdge) { ELL_3V_SET(pld->norm + 3*0, 0, 0, -1); ELL_3V_SET(pld->norm + 3*3, 0, 0, -1); ELL_3V_SET(pld->norm + 3*6, 0, 0, -1); ELL_3V_SET(pld->norm + 3*9, 0, 0, -1); ELL_3V_SET(pld->norm + 3*2, -1, 0, 0); ELL_3V_SET(pld->norm + 3*5, -1, 0, 0); ELL_3V_SET(pld->norm + 3*14, -1, 0, 0); ELL_3V_SET(pld->norm + 3*16, -1, 0, 0); ELL_3V_SET(pld->norm + 3*4, 0, 1, 0); ELL_3V_SET(pld->norm + 3*8, 0, 1, 0); ELL_3V_SET(pld->norm + 3*17, 0, 1, 0); ELL_3V_SET(pld->norm + 3*18, 0, 1, 0); ELL_3V_SET(pld->norm + 3*7, 1, 0, 0); ELL_3V_SET(pld->norm + 3*10, 1, 0, 0); ELL_3V_SET(pld->norm + 3*19, 1, 0, 0); ELL_3V_SET(pld->norm + 3*21, 1, 0, 0); ELL_3V_SET(pld->norm + 3*1, 0, -1, 0); ELL_3V_SET(pld->norm + 3*11, 0, -1, 0); ELL_3V_SET(pld->norm + 3*13, 0, -1, 0); ELL_3V_SET(pld->norm + 3*23, 0, -1, 0); ELL_3V_SET(pld->norm + 3*12, 0, 0, 1); ELL_3V_SET(pld->norm + 3*15, 0, 0, 1); ELL_3V_SET(pld->norm + 3*20, 0, 0, 1); ELL_3V_SET(pld->norm + 3*22, 0, 0, 1); } else { float cn; cn = AIR_CAST(float, sqrt(3.0)); ELL_3V_SET(pld->norm + 3*0, -cn, -cn, -cn); ELL_3V_SET(pld->norm + 3*1, -cn, cn, -cn); ELL_3V_SET(pld->norm + 3*2, cn, cn, -cn); ELL_3V_SET(pld->norm + 3*3, cn, -cn, -cn); ELL_3V_SET(pld->norm + 3*4, -cn, -cn, cn); ELL_3V_SET(pld->norm + 3*5, -cn, cn, cn); ELL_3V_SET(pld->norm + 3*6, cn, cn, cn); ELL_3V_SET(pld->norm + 3*7, cn, -cn, cn); } } if ((1 << limnPolyDataInfoRGBA) & infoBitFlag) { for (vertIdx=0; vertIdxrgbaNum; vertIdx++) { ELL_4V_SET(pld->rgba + 4*vertIdx, 255, 255, 255, 255); } } return 0; } int limnPolyDataCubeTriangles(limnPolyData *pld, unsigned int infoBitFlag, int sharpEdge) { static const char me[]="limnPolyDataCubeTriangles"; unsigned int vertNum, vertIdx, primNum, indxNum, cnum, ci; vertNum = sharpEdge ? 6*4 : 8; primNum = 1; indxNum = 6*6; if (limnPolyDataAlloc(pld, infoBitFlag, vertNum, indxNum, primNum)) { biffAddf(LIMN, "%s: couldn't allocate output", me); return 1; } pld->type[0] = limnPrimitiveTriangles; pld->icnt[0] = indxNum; vertIdx = 0; cnum = sharpEdge ? 3 : 1; for (ci=0; cixyzw + 4*vertIdx, 1, -1, 1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, -1, -1, 1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, 1, 1, 1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, -1, 1, 1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, 1, 1, -1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, -1, 1, -1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, 1, -1, -1, 1); vertIdx++; } for (ci=0; cixyzw + 4*vertIdx, -1, -1, -1, 1); vertIdx++; } vertIdx = 0; if (sharpEdge) { ELL_3V_SET(pld->indx + vertIdx, 0, 6, 3); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 3, 6, 9); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 7, 12, 10); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 10, 12, 16); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 11, 15, 4); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 4, 15, 21); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 17, 14, 22); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 22, 14, 20); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 2, 5, 19); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 19, 5, 23); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 13, 8, 18); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 18, 8, 1); vertIdx += 3; } else { ELL_3V_SET(pld->indx + vertIdx, 0, 2, 1); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 1, 2, 3); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 2, 4, 3); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 3, 4, 5); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 3, 5, 1); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 1, 5, 7); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 5, 4, 7); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 7, 4, 6); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 7, 6, 1); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 1, 6, 0); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 4, 2, 6); vertIdx += 3; ELL_3V_SET(pld->indx + vertIdx, 2, 6, 0); vertIdx += 3; } if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { if (sharpEdge) { ELL_3V_SET(pld->norm + 3*14, 0, 0, -1); ELL_3V_SET(pld->norm + 3*17, 0, 0, -1); ELL_3V_SET(pld->norm + 3*20, 0, 0, -1); ELL_3V_SET(pld->norm + 3*22, 0, 0, -1); ELL_3V_SET(pld->norm + 3* 4, -1, 0, 0); ELL_3V_SET(pld->norm + 3*11, -1, 0, 0); ELL_3V_SET(pld->norm + 3*15, -1, 0, 0); ELL_3V_SET(pld->norm + 3*21, -1, 0, 0); ELL_3V_SET(pld->norm + 3* 7, 0, 1, 0); ELL_3V_SET(pld->norm + 3*10, 0, 1, 0); ELL_3V_SET(pld->norm + 3*12, 0, 1, 0); ELL_3V_SET(pld->norm + 3*16, 0, 1, 0); ELL_3V_SET(pld->norm + 3* 1, 1, 0, 0); ELL_3V_SET(pld->norm + 3* 8, 1, 0, 0); ELL_3V_SET(pld->norm + 3*13, 1, 0, 0); ELL_3V_SET(pld->norm + 3*18, 1, 0, 0); ELL_3V_SET(pld->norm + 3* 2, 0, -1, 0); ELL_3V_SET(pld->norm + 3* 5, 0, -1, 0); ELL_3V_SET(pld->norm + 3*19, 0, -1, 0); ELL_3V_SET(pld->norm + 3*23, 0, -1, 0); ELL_3V_SET(pld->norm + 3* 0, 0, 0, 1); ELL_3V_SET(pld->norm + 3* 3, 0, 0, 1); ELL_3V_SET(pld->norm + 3* 6, 0, 0, 1); ELL_3V_SET(pld->norm + 3* 9, 0, 0, 1); } else { float cn; cn = AIR_CAST(float, 1.0/sqrt(3.0)); ELL_3V_SET(pld->norm + 3*0, cn, -cn, cn); ELL_3V_SET(pld->norm + 3*1, -cn, -cn, cn); ELL_3V_SET(pld->norm + 3*2, cn, cn, cn); ELL_3V_SET(pld->norm + 3*3, -cn, cn, cn); ELL_3V_SET(pld->norm + 3*4, cn, cn, -cn); ELL_3V_SET(pld->norm + 3*5, -cn, cn, -cn); ELL_3V_SET(pld->norm + 3*6, cn, -cn, -cn); ELL_3V_SET(pld->norm + 3*7, -cn, -cn, -cn); } } if ((1 << limnPolyDataInfoTex2) & infoBitFlag) { if (sharpEdge) { ELL_2V_SET(pld->tex2 + 2*14, 1, 1); ELL_2V_SET(pld->tex2 + 2*17, 0, 1); ELL_2V_SET(pld->tex2 + 2*20, 1, 0); ELL_2V_SET(pld->tex2 + 2*22, 0, 0); ELL_2V_SET(pld->tex2 + 2* 4, 1, 0); ELL_2V_SET(pld->tex2 + 2*11, 0, 0); ELL_2V_SET(pld->tex2 + 2*15, 0, 1); ELL_2V_SET(pld->tex2 + 2*21, 1, 1); ELL_2V_SET(pld->tex2 + 2* 7, 0, 0); ELL_2V_SET(pld->tex2 + 2*10, 1, 0); ELL_2V_SET(pld->tex2 + 2*12, 0, 1); ELL_2V_SET(pld->tex2 + 2*16, 1, 1); ELL_2V_SET(pld->tex2 + 2* 1, 0, 0); ELL_2V_SET(pld->tex2 + 2* 8, 1, 0); ELL_2V_SET(pld->tex2 + 2*13, 1, 1); ELL_2V_SET(pld->tex2 + 2*18, 0, 1); ELL_2V_SET(pld->tex2 + 2* 2, 1, 0); ELL_2V_SET(pld->tex2 + 2* 5, 0, 0); ELL_2V_SET(pld->tex2 + 2*19, 1, 1); ELL_2V_SET(pld->tex2 + 2*23, 0, 1); ELL_2V_SET(pld->tex2 + 2* 0, 1, 1); ELL_2V_SET(pld->tex2 + 2* 3, 0, 1); ELL_2V_SET(pld->tex2 + 2* 6, 1, 0); ELL_2V_SET(pld->tex2 + 2* 9, 0, 0); } else { ELL_2V_SET(pld->tex2 + 2*0, 1, 1); ELL_2V_SET(pld->tex2 + 2*1, 0, 1); ELL_2V_SET(pld->tex2 + 2*2, 1, 0); ELL_2V_SET(pld->tex2 + 2*3, 0, 0); ELL_2V_SET(pld->tex2 + 2*4, 1, 0); ELL_2V_SET(pld->tex2 + 2*5, 0, 0); ELL_2V_SET(pld->tex2 + 2*6, 1, 1); ELL_2V_SET(pld->tex2 + 2*7, 0, 1); } } if ((1 << limnPolyDataInfoTang) & infoBitFlag) { if (sharpEdge) { ELL_3V_SET(pld->tang + 3*14, 1, 0, 0); ELL_3V_SET(pld->tang + 3*17, 1, 0, 0); ELL_3V_SET(pld->tang + 3*20, 1, 0, 0); ELL_3V_SET(pld->tang + 3*22, 1, 0, 0); ELL_3V_SET(pld->tang + 3* 4, 0, -1, 0); ELL_3V_SET(pld->tang + 3*11, 0, -1, 0); ELL_3V_SET(pld->tang + 3*15, 0, -1, 0); ELL_3V_SET(pld->tang + 3*21, 0, -1, 0); ELL_3V_SET(pld->tang + 3* 7, -1, 0, 0); ELL_3V_SET(pld->tang + 3*10, -1, 0, 0); ELL_3V_SET(pld->tang + 3*12, -1, 0, 0); ELL_3V_SET(pld->tang + 3*16, -1, 0, 0); ELL_3V_SET(pld->tang + 3* 1, 0, 1, 0); ELL_3V_SET(pld->tang + 3* 8, 0, 1, 0); ELL_3V_SET(pld->tang + 3*13, 0, 1, 0); ELL_3V_SET(pld->tang + 3*18, 0, 1, 0); ELL_3V_SET(pld->tang + 3* 2, 1, 0, 0); ELL_3V_SET(pld->tang + 3* 5, 1, 0, 0); ELL_3V_SET(pld->tang + 3*19, 1, 0, 0); ELL_3V_SET(pld->tang + 3*23, 1, 0, 0); ELL_3V_SET(pld->tang + 3* 0, 1, 0, 0); ELL_3V_SET(pld->tang + 3* 3, 1, 0, 0); ELL_3V_SET(pld->tang + 3* 6, 1, 0, 0); ELL_3V_SET(pld->tang + 3* 9, 1, 0, 0); } else { float sn; sn = AIR_CAST(float, 1.0/sqrt(2.0)); ELL_3V_SET(pld->tang + 3*0, -sn, sn, 0); ELL_3V_SET(pld->tang + 3*1, sn, sn, 0); ELL_3V_SET(pld->tang + 3*2, -sn, -sn, 0); ELL_3V_SET(pld->tang + 3*3, sn, -sn, 0); ELL_3V_SET(pld->tang + 3*4, -sn, -sn, 0); ELL_3V_SET(pld->tang + 3*5, sn, -sn, 0); ELL_3V_SET(pld->tang + 3*6, -sn, sn, 0); ELL_3V_SET(pld->tang + 3*7, sn, sn, 0); } } if ((1 << limnPolyDataInfoRGBA) & infoBitFlag) { for (vertIdx=0; vertIdxrgbaNum; vertIdx++) { ELL_4V_SET(pld->rgba + 4*vertIdx, 255, 255, 255, 255); } } return 0; } int limnPolyDataOctahedron(limnPolyData *pld, unsigned int infoBitFlag, int sharpEdge) { static const char me[]="limnPolyDataOctahedron"; unsigned int vertNum, vertIdx, primNum, indxNum, cnum, ci; vertNum = sharpEdge ? 4*6 : 6; primNum = 1; indxNum = 8*3; if (limnPolyDataAlloc(pld, infoBitFlag, vertNum, indxNum, primNum)) { biffAddf(LIMN, "%s: couldn't allocate output", me); return 1; } pld->type[0] = limnPrimitiveTriangles; pld->icnt[0] = indxNum; vertIdx = 0; cnum = sharpEdge ? 4 : 1; for (ci=0; cixyzw + 4*vertIdx, 0, 0, 1, 1); vertIdx++; /* 0 */ } for (ci=0; cixyzw + 4*vertIdx, 0, 1, 0, 1); vertIdx++; /* 1 */ } for (ci=0; cixyzw + 4*vertIdx, 1, 0, 0, 1); vertIdx++; /* 2 */ } for (ci=0; cixyzw + 4*vertIdx, 0, -1, 0, 1); vertIdx++; /* 3 */ } for (ci=0; cixyzw + 4*vertIdx, -1, 0, 0, 1); vertIdx++; /* 4 */ } for (ci=0; cixyzw + 4*vertIdx, 0, 0, -1, 1); vertIdx++; /* 5 */ } vertIdx = 0; if (sharpEdge) { ELL_3V_SET(pld->indx + vertIdx, 0, 8, 4); vertIdx += 3; /* 0 */ ELL_3V_SET(pld->indx + vertIdx, 1, 15, 9); vertIdx += 3; /* 1 */ ELL_3V_SET(pld->indx + vertIdx, 2, 16, 12); vertIdx += 3; /* 2 */ ELL_3V_SET(pld->indx + vertIdx, 3, 6, 19); vertIdx += 3; /* 3 */ ELL_3V_SET(pld->indx + vertIdx, 5, 11, 23); vertIdx += 3; /* 4 */ ELL_3V_SET(pld->indx + vertIdx, 10, 14, 20); vertIdx += 3; /* 5 */ ELL_3V_SET(pld->indx + vertIdx, 17, 21, 13); vertIdx += 3; /* 6 */ ELL_3V_SET(pld->indx + vertIdx, 7, 22, 18); vertIdx += 3; /* 7 */ } else { ELL_3V_SET(pld->indx + vertIdx, 0, 2, 1); vertIdx += 3; /* 0 */ ELL_3V_SET(pld->indx + vertIdx, 0, 3, 2); vertIdx += 3; /* 1 */ ELL_3V_SET(pld->indx + vertIdx, 0, 4, 3); vertIdx += 3; /* 2 */ ELL_3V_SET(pld->indx + vertIdx, 0, 1, 4); vertIdx += 3; /* 3 */ ELL_3V_SET(pld->indx + vertIdx, 1, 2, 5); vertIdx += 3; /* 4 */ ELL_3V_SET(pld->indx + vertIdx, 2, 3, 5); vertIdx += 3; /* 5 */ ELL_3V_SET(pld->indx + vertIdx, 4, 5, 3); vertIdx += 3; /* 6 */ ELL_3V_SET(pld->indx + vertIdx, 1, 5, 4); vertIdx += 3; /* 7 */ } if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { if (sharpEdge) { float cn; cn = AIR_CAST(float, 1.0/sqrt(3)); /* 0 */ ELL_3V_SET(pld->norm + 3*0, cn, cn, cn); ELL_3V_SET(pld->norm + 3*8, cn, cn, cn); ELL_3V_SET(pld->norm + 3*4, cn, cn, cn); /* 1 */ ELL_3V_SET(pld->norm + 3*1, cn, -cn, cn); ELL_3V_SET(pld->norm + 3*15, cn, -cn, cn); ELL_3V_SET(pld->norm + 3*9, cn, -cn, cn); /* 2 */ ELL_3V_SET(pld->norm + 3*2, -cn, -cn, cn); ELL_3V_SET(pld->norm + 3*16, -cn, -cn, cn); ELL_3V_SET(pld->norm + 3*12, -cn, -cn, cn); /* 3 */ ELL_3V_SET(pld->norm + 3*3, -cn, cn, cn); ELL_3V_SET(pld->norm + 3*6, -cn, cn, cn); ELL_3V_SET(pld->norm + 3*19, -cn, cn, cn); /* 4 */ ELL_3V_SET(pld->norm + 3*5, cn, cn, -cn); ELL_3V_SET(pld->norm + 3*11, cn, cn, -cn); ELL_3V_SET(pld->norm + 3*23, cn, cn, -cn); /* 5 */ ELL_3V_SET(pld->norm + 3*10, cn, -cn, -cn); ELL_3V_SET(pld->norm + 3*14, cn, -cn, -cn); ELL_3V_SET(pld->norm + 3*20, cn, -cn, -cn); /* 6 */ ELL_3V_SET(pld->norm + 3*17, -cn, -cn, -cn); ELL_3V_SET(pld->norm + 3*21, -cn, -cn, -cn); ELL_3V_SET(pld->norm + 3*13, -cn, -cn, -cn); /* 7 */ ELL_3V_SET(pld->norm + 3*7, -cn, cn, -cn); ELL_3V_SET(pld->norm + 3*22, -cn, cn, -cn); ELL_3V_SET(pld->norm + 3*18, -cn, cn, -cn); } else { ELL_3V_SET(pld->norm + 3*0, 0, 0, 1); /* 0 */ ELL_3V_SET(pld->norm + 3*1, 0, 1, 0); /* 1 */ ELL_3V_SET(pld->norm + 3*2, 1, 0, 0); /* 2 */ ELL_3V_SET(pld->norm + 3*3, 0, -1, 0); /* 3 */ ELL_3V_SET(pld->norm + 3*4, -1, 0, 0); /* 4 */ ELL_3V_SET(pld->norm + 3*5, 0, 0, -1); /* 5 */ } } if ((1 << limnPolyDataInfoRGBA) & infoBitFlag) { for (vertIdx=0; vertIdxrgbaNum; vertIdx++) { ELL_4V_SET(pld->rgba + 4*vertIdx, 255, 255, 255, 255); } } return 0; } int limnPolyDataCylinder(limnPolyData *pld, unsigned int infoBitFlag, unsigned int thetaRes, int sharpEdge) { static const char me[]="limnPolyDataCylinder"; unsigned int vertNum, primNum, primIdx, indxNum, thetaIdx, vertIdx, blah; double theta, cth, sth, sq2; /* sanity bounds */ thetaRes = AIR_MAX(3, thetaRes); vertNum = sharpEdge ? 4*thetaRes : 2*thetaRes; primNum = 3; indxNum = 2*thetaRes + 2*(thetaRes+1); /* 2 fans + 1 strip */ if (limnPolyDataAlloc(pld, infoBitFlag, vertNum, indxNum, primNum)) { biffAddf(LIMN, "%s: couldn't allocate output", me); return 1; } vertIdx = 0; for (blah=0; blah < (sharpEdge ? 2u : 1u); blah++) { for (thetaIdx=0; thetaIdxxyzw + 4*vertIdx, float, cos(theta), sin(theta), 1, 1); /* fprintf(stderr, "!%s: vert[%u] = %g %g %g\n", me, vertIdx, (pld->xyzw + 4*vertIdx)[0], (pld->xyzw + 4*vertIdx)[1], (pld->xyzw + 4*vertIdx)[2]); */ ++vertIdx; } } for (blah=0; blah < (sharpEdge ? 2u : 1u); blah++) { for (thetaIdx=0; thetaIdxxyzw + 4*vertIdx, float, cos(theta), sin(theta), -1, 1); /* fprintf(stderr, "!%s: vert[%u] = %g %g %g\n", me, vertIdx, (pld->xyzw + 4*vertIdx)[0], (pld->xyzw + 4*vertIdx)[1], (pld->xyzw + 4*vertIdx)[2]); */ ++vertIdx; } } primIdx = 0; vertIdx = 0; /* fan on top */ /* fprintf(stderr, "!%s: fan on top:\n", me); */ for (thetaIdx=0; thetaIdxindx[vertIdx++] = thetaIdx; } pld->type[primIdx] = limnPrimitiveTriangleFan; pld->icnt[primIdx] = thetaRes; primIdx++; /* single strip around */ /* fprintf(stderr, "!%s: strip around:\n", me); */ for (thetaIdx=0; thetaIdxindx[vertIdx++] = (sharpEdge ? 1 : 0)*thetaRes + thetaIdx; /* fprintf(stderr, "!%s: indx[%u] = %u\n", me, vertIdx, (sharpEdge ? 2 : 1)*thetaRes + thetaIdx); */ pld->indx[vertIdx++] = (sharpEdge ? 2 : 1)*thetaRes + thetaIdx; } /* fprintf(stderr, "!%s: indx[%u] = %u\n", me, vertIdx, (sharpEdge ? 1 : 0)*thetaRes); */ pld->indx[vertIdx++] = (sharpEdge ? 1 : 0)*thetaRes; /* fprintf(stderr, "!%s: indx[%u] = %u\n", me, vertIdx, (sharpEdge ? 2 : 1)*thetaRes); */ pld->indx[vertIdx++] = (sharpEdge ? 2 : 1)*thetaRes; pld->type[primIdx] = limnPrimitiveTriangleStrip; pld->icnt[primIdx] = 2*(thetaRes+1); primIdx++; /* fan on bottom */ /* fprintf(stderr, "!%s: fan on bottom:\n", me); */ for (thetaIdx=0; thetaIdxindx[vertIdx++] = (sharpEdge ? 3 : 1)*thetaRes + thetaIdx; } pld->type[primIdx] = limnPrimitiveTriangleFan; pld->icnt[primIdx] = thetaRes; primIdx++; if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { sq2 = sqrt(2.0); if (sharpEdge) { for (thetaIdx=0; thetaIdxnorm + 3*(thetaIdx + 0*thetaRes), float, 0, 0, 1); ELL_3V_SET_TT(pld->norm + 3*(thetaIdx + 1*thetaRes), float, cth, sth, 0); ELL_3V_SET_TT(pld->norm + 3*(thetaIdx + 2*thetaRes), float, cth, sth, 0); ELL_3V_SET_TT(pld->norm + 3*(thetaIdx + 3*thetaRes), float, 0, 0, -1); } } else { for (thetaIdx=0; thetaIdxnorm + 3*(thetaIdx + 0*thetaRes), float, cth, sth, sq2); ELL_3V_SET_TT(pld->norm + 3*(thetaIdx + 1*thetaRes), float, cth, sth, -sq2); } } } if ((1 << limnPolyDataInfoTex2) & infoBitFlag) { if (sharpEdge) { for (thetaIdx=0; thetaIdxtex2 + 2*(thetaIdx + 0*thetaRes), float, theta, 0); ELL_2V_SET_TT(pld->tex2 + 2*(thetaIdx + 1*thetaRes), float, theta, 0); ELL_2V_SET_TT(pld->tex2 + 2*(thetaIdx + 2*thetaRes), float, theta, 1); ELL_2V_SET_TT(pld->tex2 + 2*(thetaIdx + 3*thetaRes), float, theta, 1); } } else { for (thetaIdx=0; thetaIdxtex2 + 2*(thetaIdx + 0*thetaRes), float, theta, 0); ELL_2V_SET_TT(pld->tex2 + 2*(thetaIdx + 1*thetaRes), float, theta, 1); } } } if ((1 << limnPolyDataInfoTang) & infoBitFlag) { float xx, yy, tang[3], tlen; if (sharpEdge) { for (thetaIdx=0; thetaIdxxyzw + 4*(thetaIdx + 0*thetaRes))[0]; yy = (pld->xyzw + 4*(thetaIdx + 0*thetaRes))[1]; ELL_3V_SET(tang, -yy, xx, 0.0); ELL_3V_NORM_TT(tang, float, tang, tlen); ELL_3V_COPY(pld->tang + 3*(thetaIdx + 0*thetaRes), tang); ELL_3V_COPY(pld->tang + 3*(thetaIdx + 1*thetaRes), tang); ELL_3V_COPY(pld->tang + 3*(thetaIdx + 2*thetaRes), tang); ELL_3V_COPY(pld->tang + 3*(thetaIdx + 3*thetaRes), tang); } } else { for (thetaIdx=0; thetaIdxxyzw + 4*(thetaIdx + 0*thetaRes))[0]; yy = (pld->xyzw + 4*(thetaIdx + 0*thetaRes))[1]; ELL_3V_SET(tang, -yy, xx, 0.0); ELL_3V_NORM_TT(tang, float, tang, tlen); ELL_3V_COPY(pld->tang + 3*(thetaIdx + 0*thetaRes), tang); ELL_3V_COPY(pld->tang + 3*(thetaIdx + 1*thetaRes), tang); } } } if ((1 << limnPolyDataInfoRGBA) & infoBitFlag) { for (vertIdx=0; vertIdxrgbaNum; vertIdx++) { ELL_4V_SET(pld->rgba + 4*vertIdx, 255, 255, 255, 255); } } return 0; } int limnPolyDataCone(limnPolyData *pld, unsigned int infoBitFlag, unsigned int thetaRes, int sharpEdge) { static const char me[]="limnPolyDataCone"; unsigned int vertNum, primNum, primIdx, indxNum, thetaIdx, vertIdx, blah; double theta, cth, sth; /* sanity bounds */ thetaRes = AIR_MAX(3, thetaRes); vertNum = sharpEdge ? 3*thetaRes : 1 + thetaRes; primNum = 2; indxNum = thetaRes + 2*(thetaRes+1); /* 1 fans + 1 strip */ if (limnPolyDataAlloc(pld, infoBitFlag, vertNum, indxNum, primNum)) { biffAddf(LIMN, "%s: couldn't allocate output", me); return 1; } /* top point(s) */ vertIdx = 0; if (sharpEdge) { for (thetaIdx=0; thetaIdxxyzw + 4*vertIdx, float, 0, 0, 1, 1); ++vertIdx; } } else { ELL_4V_SET_TT(pld->xyzw + 4*vertIdx, float, 0, 0, 1, 1); ++vertIdx; } /* bottom edge */ for (blah=0; blah < (sharpEdge ? 2u : 1u); blah++) { for (thetaIdx=0; thetaIdxxyzw + 4*vertIdx, float, cos(theta), sin(theta), -1, 1); ++vertIdx; } } primIdx = 0; vertIdx = 0; /* single strip around */ for (thetaIdx=0; thetaIdxindx[vertIdx++] = sharpEdge ? thetaIdx : 0; pld->indx[vertIdx++] = (sharpEdge ? thetaRes : 1) + thetaIdx; } pld->indx[vertIdx++] = 0; pld->indx[vertIdx++] = sharpEdge ? thetaRes : 1; pld->type[primIdx] = limnPrimitiveTriangleStrip; pld->icnt[primIdx] = 2*(thetaRes+1); primIdx++; /* fan on bottom */ for (thetaIdx=0; thetaIdxindx[vertIdx++] = (sharpEdge ? 2*thetaRes : 1) + thetaIdx; } pld->type[primIdx] = limnPrimitiveTriangleFan; pld->icnt[primIdx] = thetaRes; primIdx++; if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { double isq3; isq3 = 1/sqrt(3.0); if (sharpEdge) { for (thetaIdx=0; thetaIdxnorm + 3*(thetaIdx + 0*thetaRes), float, cth*isq3, sth*isq3, isq3); ELL_3V_SET_TT(pld->norm + 3*(thetaIdx + 1*thetaRes), float, cth*isq3, sth*isq3, isq3); ELL_3V_SET_TT(pld->norm + 3*(thetaIdx + 2*thetaRes), float, 0, 0, -1); } } else { ELL_3V_SET_TT(pld->norm + 3*(0), float, 0, 0, 1); for (thetaIdx=0; thetaIdxnorm + 3*(thetaIdx + 1), float, cth*isq3, sth*isq3, -isq3); /* close enough */ } } } if ((1 << limnPolyDataInfoRGBA) & infoBitFlag) { for (vertIdx=0; vertIdxrgbaNum; vertIdx++) { ELL_4V_SET(pld->rgba + 4*vertIdx, 255, 255, 255, 255); } } return 0; } /* ******** limnPolyDataSuperquadric ** ** makes a superquadric parameterized around the Z axis */ int limnPolyDataSuperquadric(limnPolyData *pld, unsigned int infoBitFlag, float alpha, float beta, unsigned int thetaRes, unsigned int phiRes) { static const char me[]="limnPolyDataSuperquadric"; unsigned int vertIdx, vertNum, fanNum, stripNum, primNum, indxNum, thetaIdx, phiIdx, primIdx; double theta, phi; /* sanity bounds */ thetaRes = AIR_MAX(3u, thetaRes); phiRes = AIR_MAX(2u, phiRes); alpha = AIR_MAX(0.00001f, alpha); beta = AIR_MAX(0.00001f, beta); vertNum = 2 + thetaRes*(phiRes-1); fanNum = 2; stripNum = phiRes-2; primNum = fanNum + stripNum; indxNum = (thetaRes+2)*fanNum + 2*(thetaRes+1)*stripNum; if (limnPolyDataAlloc(pld, infoBitFlag, vertNum, indxNum, primNum)) { biffAddf(LIMN, "%s: couldn't allocate output", me); return 1; } vertIdx = 0; ELL_4V_SET(pld->xyzw + 4*vertIdx, 0, 0, 1, 1); if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { ELL_3V_SET(pld->norm + 3*vertIdx, 0, 0, 1); } ++vertIdx; for (phiIdx=1; phiIdxxyzw + 4*vertIdx, float, airSgnPow(cost,alpha) * airSgnPow(sinp,beta), airSgnPow(sint,alpha) * airSgnPow(sinp,beta), airSgnPow(cosp,beta), 1.0); if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { if (1 == alpha && 1 == beta) { ELL_3V_COPY(pld->norm + 3*vertIdx, pld->xyzw + 4*vertIdx); } else { ELL_3V_SET_TT(pld->norm + 3*vertIdx, float, 2*airSgnPow(cost,2-alpha)*airSgnPow(sinp,2-beta)/beta, 2*airSgnPow(sint,2-alpha)*airSgnPow(sinp,2-beta)/beta, 2*airSgnPow(cosp,2-beta)/beta); } } ++vertIdx; } } ELL_4V_SET(pld->xyzw + 4*vertIdx, 0, 0, -1, 1); if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { ELL_3V_SET(pld->norm + 3*vertIdx, 0, 0, -1); } ++vertIdx; /* triangle fan at top */ vertIdx = 0; primIdx = 0; pld->indx[vertIdx++] = 0; for (thetaIdx=0; thetaIdxindx[vertIdx++] = thetaIdx + 1; } pld->indx[vertIdx++] = 1; pld->type[primIdx] = limnPrimitiveTriangleFan; pld->icnt[primIdx++] = thetaRes + 2; /* tristrips around */ for (phiIdx=1; phiIdxindx[vertIdx++] = (phiIdx-1)*thetaRes + thetaIdx + 1; pld->indx[vertIdx++] = phiIdx*thetaRes + thetaIdx + 1; } /* fprintf(stderr, " [%u %u] %u %u (%u verts)\n", vertIdx, vertIdx + 1, (phiIdx-1)*thetaRes + 1, phiIdx*thetaRes + 1, 2*(thetaRes+1)); */ pld->indx[vertIdx++] = (phiIdx-1)*thetaRes + 1; pld->indx[vertIdx++] = phiIdx*thetaRes + 1; pld->type[primIdx] = limnPrimitiveTriangleStrip; pld->icnt[primIdx++] = 2*(thetaRes+1); } /* triangle fan at bottom */ pld->indx[vertIdx++] = vertNum-1; for (thetaIdx=0; thetaIdxindx[vertIdx++] = thetaRes*(phiRes-2) + thetaRes - thetaIdx; } pld->indx[vertIdx++] = thetaRes*(phiRes-2) + thetaRes; pld->type[primIdx] = limnPrimitiveTriangleFan; pld->icnt[primIdx++] = thetaRes + 2; if ((1 << limnPolyDataInfoRGBA) & infoBitFlag) { for (vertIdx=0; vertIdxrgbaNum; vertIdx++) { ELL_4V_SET(pld->rgba + 4*vertIdx, 255, 255, 255, 255); } } return 0; } /* ******** limnPolyDataSpiralBetterquadric ** ** puts a "betterquadric" into a single spiral triangle strip */ int limnPolyDataSpiralBetterquadric(limnPolyData *pld, unsigned int infoBitFlag, float alpha, float beta, float cee, float minRad, unsigned int thetaRes, unsigned int phiRes) { static const char me[]="limnPolyDataSpiralBetterquadric"; unsigned int vertIdx, vertNum, indxNum, thetaIdx, phiIdx; /* sanity bounds */ thetaRes = AIR_MAX(3u, thetaRes); phiRes = AIR_MAX(2u, phiRes); alpha = AIR_MAX(0.00001f, alpha); beta = AIR_MAX(0.00001f, beta); vertNum = thetaRes*phiRes + 1; indxNum = 2*thetaRes*(phiRes+1) - 2; if (limnPolyDataAlloc(pld, infoBitFlag, vertNum, indxNum, 1)) { biffAddf(LIMN, "%s: couldn't allocate output", me); return 1; } vertIdx = 0; for (phiIdx=0; phiIdxxyzw + 4*vertIdx, float, xx, yy, zz, 1.0); if (minRad > 0.0) { /* add thickness to small radius */ double rr; xx = (pld->xyzw + 4*vertIdx)[0]; yy = (pld->xyzw + 4*vertIdx)[1]; rr = sqrt(xx*xx + yy*yy); if (rr) { (pld->xyzw + 4*vertIdx)[0] *= AIR_CAST(float, AIR_AFFINE(0, rr, 1, minRad/rr, 1/rr)); (pld->xyzw + 4*vertIdx)[1] *= AIR_CAST(float, AIR_AFFINE(0, rr, 1, minRad/rr, 1/rr)); } } if (((1 << limnPolyDataInfoNorm) & infoBitFlag) || ((1 << limnPolyDataInfoTang) & infoBitFlag)) { double norm[3], nlen; if (1 == alpha && 1 == beta) { ELL_3V_COPY(norm, pld->xyzw + 4*vertIdx); } else { if (!vertIdx) { ELL_3V_SET(norm, 0.0, 0.0, 1.0); } else { ELL_3V_SET(norm, (2*airSgnPow(cost,2-alpha)*airSgnPow(sinp,2-beta)/beta), (2*airSgnPow(sint,2-alpha)*airSgnPow(sinp,2-beta)/beta), 2*airSgnPow(cosp,2-beta)/beta); } } if ((nlen = ELL_3V_LEN(norm))) { ELL_3V_SCALE(norm, 1.0/nlen, norm); } else { ELL_3V_SET(norm, 0.0, 0.0, 1.0); } if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { ELL_3V_COPY_TT(pld->norm + 3*vertIdx, float, norm); } if ((1 << limnPolyDataInfoTang) & infoBitFlag) { double tang[3], tlen; ELL_3V_SET(tang, -norm[1], norm[0], 0.0); if ((tlen = ELL_3V_LEN(tang))) { ELL_3V_SCALE(tang, 1.0/tlen, tang); } else { ELL_3V_SET(tang, 1.0, 0.0, 0.0); } ELL_3V_COPY_TT(pld->tang + 3*vertIdx, float, tang); } } if ((1 << limnPolyDataInfoTex2) & infoBitFlag) { ELL_2V_SET_TT(pld->tex2 + 2*vertIdx, float, AIR_AFFINE(0.0, theta, 2*AIR_PI, 0.0, 1.0), AIR_AFFINE(0.0, phi, AIR_PI, 0.0, 1.0)); } ++vertIdx; } } ELL_4V_SET(pld->xyzw + 4*vertIdx, 0, 0, -1, 1); if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { ELL_3V_SET(pld->norm + 3*vertIdx, 0.0, 0.0, -1.0); } if ((1 << limnPolyDataInfoTex2) & infoBitFlag) { ELL_2V_SET(pld->tex2 + 2*vertIdx, 0.5, 1.0); } if ((1 << limnPolyDataInfoTang) & infoBitFlag) { ELL_3V_SET(pld->tang + 3*vertIdx, 1.0, 0.0, 0.0); } ++vertIdx; /* single triangle strip */ pld->type[0] = limnPrimitiveTriangleStrip; pld->icnt[0] = indxNum; vertIdx = 0; for (thetaIdx=1; thetaIdxindx[vertIdx++] = 0; pld->indx[vertIdx++] = thetaIdx; } for (phiIdx=0; phiIdxindx[vertIdx++] = ((phiIdx + 0) * thetaRes) + thetaIdx; pld->indx[vertIdx++] = ((phiIdx + 1) * thetaRes) + thetaIdx; } } for (thetaIdx=0; thetaIdxindx[vertIdx++] = (phiRes - 1)*thetaRes + thetaIdx; pld->indx[vertIdx++] = (phiRes - 0)*thetaRes; } #if 0 if ( (cee != beta || minRad > 0.0) && ((1 << limnPolyDataInfoNorm) & infoBitFlag) ) { /* have deformed object in some way that confounds analytic normals */ if (limnPolyDataVertexNormals(pld)) { biffAddf(LIMN, "%s: trouble getting normals", me); return 1; } } #endif if ((1 << limnPolyDataInfoRGBA) & infoBitFlag) { for (vertIdx=0; vertIdxrgbaNum; vertIdx++) { ELL_4V_SET(pld->rgba + 4*vertIdx, 255, 255, 255, 255); } } return 0; } /* ******** limnPolyDataSpiralSuperquadric ** ** puts a superquadric into a single spiral triangle strip */ int limnPolyDataSpiralSuperquadric(limnPolyData *pld, unsigned int infoBitFlag, float alpha, float beta, unsigned int thetaRes, unsigned int phiRes) { static const char me[]="limnPolyDataSpiralSuperquadric"; if (limnPolyDataSpiralBetterquadric(pld, infoBitFlag, alpha, beta, beta, 0.0, thetaRes, phiRes)) { biffAddf(LIMN, "%s: trouble", me); return 1; } return 0; } /* ******** limnPolyDataPolarSphere ** ** makes a unit sphere, centered at the origin, parameterized around Z axis */ int limnPolyDataPolarSphere(limnPolyData *pld, unsigned int infoBitFlag, unsigned int thetaRes, unsigned int phiRes) { static const char me[]="limnPolyDataPolarSphere"; if (limnPolyDataSuperquadric(pld, infoBitFlag, 1.0, 1.0, thetaRes, phiRes)) { biffAddf(LIMN, "%s: trouble", me); return 1; } return 0; } int limnPolyDataSpiralSphere(limnPolyData *pld, unsigned int infoBitFlag, unsigned int thetaRes, unsigned int phiRes) { static const char me[]="limnPolyDataSpiralSphere"; if (limnPolyDataSpiralSuperquadric(pld, infoBitFlag, 1.0, 1.0, thetaRes, phiRes)) { biffAddf(LIMN, "%s: trouble", me); return 1; } return 0; } /* Geometry for an icosahedron */ #define ICO_ONE 0.5257311121f #define ICO_TAU 0.8506508084f static float icovertices[36] = { 0.0f, ICO_ONE, ICO_TAU, 0.0f, -ICO_ONE, -ICO_TAU, 0.0f, -ICO_ONE, ICO_TAU, 0.0f, ICO_ONE, -ICO_TAU, ICO_ONE, ICO_TAU, 0.0f, -ICO_ONE, -ICO_TAU, 0.0f, ICO_ONE, -ICO_TAU, 0.0f, -ICO_ONE, ICO_TAU, 0.0f, ICO_TAU, 0.0f, ICO_ONE, -ICO_TAU, 0.0f, -ICO_ONE, -ICO_TAU, 0.0f, ICO_ONE, ICO_TAU, 0.0f, -ICO_ONE }; #undef ICO_ONE #undef ICO_TAU static unsigned int icoedges[60] = { 0, 2, 1, 3, 0, 4, 1, 5, 0, 8, 1, 9, 0, 10, 1, 11, /* 0-7 */ 0, 7, 1, 6, 2, 6, 3, 7, 2, 8, 3, 9, 2, 10, 3, 11, /* 8-15 */ 2, 5, 3, 4, 4, 8, 5, 9, 4, 7, 5, 6, 4, 11, 5, 10, /* 16-23*/ 6, 8, 7, 9, 6, 11, 7, 10, 8, 11, 9, 10 /* 24-29 */ }; static unsigned int icofaces[60] = { 0, 4, 12, 1, 5, 13, 0, 6, 14, 1, 7, 15, 6, 8, 27, 7, 9, 26, 2, 4, 18, 3, 5, 19, 10, 12, 24, 11, 13, 25, 14, 16, 23, 15, 17, 22, 2, 8, 20, 3, 9, 21, 10, 16, 21, 11, 17, 20, 24, 26, 28, 25, 27, 29, 18, 22, 28, 19, 23, 29 }; /* ******** limnPolyDataIcoSphere ** ** Makes a unit sphere, centered at the origin, by refining an icosahedron ** level times. Each refinement step subdivides each edge at its center, ** turning each triangle into four smaller ones. ** ** In the output, vertex 2*i and 2*i+1 are antipodal points, which allows for ** a more efficient implementation of operations that produce the same or ** a mirrored result for antipodal points (e.g., tensor glyphs). */ int limnPolyDataIcoSphere(limnPolyData *pld, unsigned int infoBitFlag, unsigned int level) { static const char me[]="limnPolyDataIcoSphere"; unsigned int vertNum=12, edgeNum=30, faceNum=20, center; float *verts, *xyzwp, *vertp; unsigned char *rgbap; unsigned int *edges, *faces; unsigned int i,e,f; /* loop counters */ airArray *mop; /* free memory in case of allocation error */ /* sanity checks */ if (!pld) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); /* x/y/z positions */ verts = (float *) malloc (sizeof(float)*12*3); if (verts==NULL) goto error_and_exit; airMopAdd(mop, verts, airFree, airMopAlways); memcpy(verts,icovertices,sizeof(float)*12*3); /* endpoints for each edge */ edges = (unsigned int *) malloc (sizeof(unsigned int)*30*2); if (edges==NULL) goto error_and_exit; airMopAdd(mop, edges, airFree, airMopAlways); memcpy(edges,icoedges,sizeof(unsigned int)*60); /* vertices of each face */ faces = (unsigned int *) malloc (sizeof(unsigned int)*20*3); if (faces==NULL) goto error_and_exit; airMopAdd(mop, faces, airFree, airMopAlways); memcpy(faces,icofaces,sizeof(unsigned int)*60); for (i=0; ixyzw; vertp=verts; for (i=0; inorm, verts, sizeof(float)*3*vertNum); } if ((1 << limnPolyDataInfoRGBA) & infoBitFlag) { rgbap=pld->rgba; vertp=verts; for (i=0; ixyzw + 4*i); rr = sqrt(xyz[0]*xyz[0] + xyz[1]*xyz[1]); phi = atan2(rr, xyz[2]); theta = atan2(xyz[1], xyz[0]); ELL_2V_SET_TT(pld->tex2 + 2*i, float, AIR_AFFINE(-AIR_PI, theta, AIR_PI, 0.0, 1.0), AIR_AFFINE(0.0, phi, AIR_PI, 0.0, 1.0)); } } if ((1 << limnPolyDataInfoTang) & infoBitFlag) { for (i=0; ixyzw + 4*i); rr = sqrt(xyz[0]*xyz[0] + xyz[1]*xyz[1]); if (rr) { ELL_3V_SET(tang, -xyz[1], xyz[0], 0.0); ELL_3V_NORM_TT(pld->tang + 3*i, float, tang, len); } else { ELL_3V_SET(pld->tang + 3*i, 1.0f, 0.0f, 0.0f); } } } /* We need to replace reference to edges in faces with references to * vertices. Make sure that they are ordered CCW */ pld->type[0] = limnPrimitiveTriangles; pld->icnt[0] = faceNum*3; for (f=0; findx[3*f]=vertices[0]; if (ELL_3V_DOT(cross,verts+3*vertices[0])<0) { pld->indx[3*f+1]=vertices[2]; pld->indx[3*f+2]=vertices[1]; } else { pld->indx[3*f+1]=vertices[1]; pld->indx[3*f+2]=vertices[2]; } } /* re-order the triangles */ center=3*(faceNum/2); for (i=0; iindx+6*i); ELL_3V_COPY(faces+center+3*i,pld->indx+6*i+3); } airMopAdd(mop, pld->indx, airFree, airMopAlways); airMopSub(mop, faces, airFree); pld->indx=faces; /* done */ airMopOkay(mop); return 0; error_and_exit: biffAddf(LIMN, "%s: memory allocation failed", me); airMopError(mop); return 1; } int limnPolyDataPlane(limnPolyData *pld, unsigned int infoBitFlag, unsigned int uRes, unsigned int vRes) { static const char me[]="limnPolyDataPlane"; unsigned int vertNum, indxNum, primNum, uIdx, vIdx, vertIdx, primIdx; float uu, vv; /* sanity */ uRes = AIR_MAX(2, uRes); vRes = AIR_MAX(2, vRes); vertNum = uRes*vRes; primNum = vRes-1; indxNum = primNum*2*uRes; if (limnPolyDataAlloc(pld, infoBitFlag, vertNum, indxNum, primNum)) { biffAddf(LIMN, "%s: couldn't allocate output", me); return 1; } vertIdx = 0; for (vIdx=0; vIdxxyzw + 4*vertIdx, uu, vv, 0.0, 1.0); if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { ELL_3V_SET_TT(pld->norm + 3*vertIdx, float, 0.0, 0.0, 1.0); } if ((1 << limnPolyDataInfoRGBA) & infoBitFlag) { ELL_4V_SET(pld->rgba + 4*vertIdx, 255, 255, 255, 255); } if ((1 << limnPolyDataInfoTex2) & infoBitFlag) { ELL_2V_SET_TT(pld->tex2 + 2*vertIdx, float, (uu+1.0)/2.0, (vv+1.0)/2.0); } if ((1 << limnPolyDataInfoTang) & infoBitFlag) { ELL_3V_SET_TT(pld->tang + 3*vertIdx, float, 1.0, 0.0, 0.0); } ++vertIdx; } } vertIdx = 0; for (primIdx=0; primIdxindx[vertIdx++] = uIdx + uRes*(primIdx+1); pld->indx[vertIdx++] = uIdx + uRes*(primIdx); } pld->type[primIdx] = limnPrimitiveTriangleStrip; pld->icnt[primIdx] = 2*uRes; } return 0; } int limnPolyDataSquare(limnPolyData *pld, unsigned int infoBitFlag) { static const char me[]="limnPolyDataSquare"; if (limnPolyDataAlloc(pld, infoBitFlag, 4, 4, 1)) { biffAddf(LIMN, "%s: couldn't allocate output", me); return 1; } ELL_4V_SET(pld->xyzw + 4*0, -1.0, -1.0, 0.0, 1.0); ELL_4V_SET(pld->xyzw + 4*1, 1.0, -1.0, 0.0, 1.0); ELL_4V_SET(pld->xyzw + 4*2, -1.0, 1.0, 0.0, 1.0); ELL_4V_SET(pld->xyzw + 4*3, 1.0, 1.0, 0.0, 1.0); if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { ELL_3V_SET(pld->norm + 3*0, 0.0, 0.0, 1.0); ELL_3V_SET(pld->norm + 3*1, 0.0, 0.0, 1.0); ELL_3V_SET(pld->norm + 3*2, 0.0, 0.0, 1.0); ELL_3V_SET(pld->norm + 3*3, 0.0, 0.0, 1.0); } if ((1 << limnPolyDataInfoRGBA) & infoBitFlag) { ELL_4V_SET(pld->rgba + 4*0, 255, 255, 255, 255); ELL_4V_SET(pld->rgba + 4*1, 255, 255, 255, 255); ELL_4V_SET(pld->rgba + 4*2, 255, 255, 255, 255); ELL_4V_SET(pld->rgba + 4*3, 255, 255, 255, 255); } if ((1 << limnPolyDataInfoTex2) & infoBitFlag) { ELL_2V_SET(pld->tex2 + 2*0, 0.0, 1.0); ELL_2V_SET(pld->tex2 + 2*1, 1.0, 1.0); ELL_2V_SET(pld->tex2 + 2*2, 0.0, 0.0); ELL_2V_SET(pld->tex2 + 2*3, 1.0, 0.0); } if ((1 << limnPolyDataInfoTang) & infoBitFlag) { ELL_3V_SET(pld->tang + 3*0, 1.0, 0.0, 0.0); ELL_3V_SET(pld->tang + 3*1, 1.0, 0.0, 0.0); ELL_3V_SET(pld->tang + 3*2, 1.0, 0.0, 0.0); ELL_3V_SET(pld->tang + 3*3, 1.0, 0.0, 0.0); } pld->type[0] = limnPrimitiveTriangleStrip; ELL_4V_SET(pld->indx, 0, 1, 2, 3); pld->icnt[0] = 4; return 0; } teem-1.11.0~svn6057/src/limn/transform.c0000664000175000017500000002542612165631065017530 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" int limnObjectWorldHomog(limnObject *obj) { static const char me[]="limnObjectWorldHomog"; unsigned int vertIdx; limnVertex *vert; float h; if (!obj) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } for (vertIdx=0; vertIdxvertNum; vertIdx++) { vert = obj->vert + vertIdx; h = AIR_CAST(float, 1.0/vert->world[3]); ELL_3V_SCALE(vert->world, h, vert->world); vert->world[3] = 1.0; ELL_3V_NORM_TT(vert->worldNormal, float, vert->worldNormal, h); } return 0; } int limnObjectFaceNormals(limnObject *obj, int space) { static const char me[]="limnObjectFaceNormals"; unsigned int vii, faceIdx; limnFace *face; limnVertex *vert0, *vert1, *vert2; float vec1[3], vec2[3], cross[3], nn[3], norm; if (space != limnSpaceWorld && space != obj->vertSpace) { biffAddf(LIMN, "%s: desired (%s) != object (%s) space", me, airEnumStr(limnSpace, space), airEnumStr(limnSpace, obj->vertSpace)); return 1; } for (faceIdx=0; faceIdxfaceNum; faceIdx++) { face = obj->face + faceIdx; /* add up cross products at all vertices */ ELL_3V_SET(nn, 0, 0, 0); for (vii=0; viisideNum; vii++) { vert0 = obj->vert + face->vertIdx[vii]; vert1 = (obj->vert + face->vertIdx[AIR_MOD((int)vii+1, (int)face->sideNum)]); vert2 = (obj->vert + face->vertIdx[AIR_MOD((int)vii-1, (int)face->sideNum)]); if (limnSpaceWorld == space) { ELL_3V_SUB(vec1, vert1->world, vert0->world); ELL_3V_SUB(vec2, vert2->world, vert0->world); } else { ELL_3V_SUB(vec1, vert1->coord, vert0->coord); ELL_3V_SUB(vec2, vert2->coord, vert0->coord); } ELL_3V_CROSS(cross, vec1, vec2); ELL_3V_ADD2(nn, nn, cross); } if (limnSpaceWorld == space) { ELL_3V_NORM_TT(face->worldNormal, float, nn, norm); /* printf("%s: worldNormal[%d] = %g %g %g\n", me, faceIdx, face->worldNormal[0], face->worldNormal[1], face->worldNormal[2]); */ } else { ELL_3V_NORM_TT(face->screenNormal, float, nn, norm); /* printf("%s: sn[%d] = %g %g %g\n", me, faceIdx, f->sn[0], f->sn[1], f->sn[2]); */ } } return 0; } int limnObjectVertexNormals(limnObject *obj) { /* static const char me[]="limnObjectVertexNormals"; */ unsigned int vertIdx, vertIdxIdx, faceIdx; limnVertex *vert; limnFace *face; double norm; for (vertIdx=0; vertIdxvertNum; vertIdx++) { vert = obj->vert + vertIdx; ELL_3V_SET(vert->worldNormal, 0, 0, 0); } for (faceIdx=0; faceIdxfaceNum; faceIdx++) { face = obj->face + faceIdx; for (vertIdxIdx=0; vertIdxIdxsideNum; vertIdxIdx++) { vert = obj->vert + face->vertIdx[vertIdxIdx]; ELL_3V_INCR(vert->worldNormal, face->worldNormal); } } for (vertIdx=0; vertIdxvertNum; vertIdx++) { vert = obj->vert + vertIdx; ELL_3V_NORM_TT(vert->worldNormal, float, vert->worldNormal, norm); } return 0; } int _limnObjectViewTransform(limnObject *obj, limnCamera *cam) { unsigned int vertIdx; limnVertex *vert; float d; for (vertIdx=0; vertIdxvertNum; vertIdx++) { vert = obj->vert + vertIdx; ELL_4MV_MUL_TT(vert->coord, float, cam->W2V, vert->world); d = AIR_CAST(float, 1.0/vert->world[3]); ELL_4V_SCALE(vert->coord, d, vert->coord); /* printf("%s: w[%d] = %g %g %g %g --> v = %g %g %g\n", "_limnObjectVTransform", pi, p->w[0], p->w[1], p->w[2], p->w[3], p->v[0], p->v[1], p->v[2]); */ } obj->vertSpace = limnSpaceView; return 0; } int _limnObjectScreenTransform(limnObject *obj, limnCamera *cam) { static const char me[]="_limnObjectScreenTransform"; unsigned int vertIdx; limnVertex *vert; float d; if (limnSpaceView != obj->vertSpace) { biffAddf(LIMN, "%s: object's verts in %s (not %s) space", me, airEnumStr(limnSpace, obj->vertSpace), airEnumStr(limnSpace, limnSpaceView)); return 1; } for (vertIdx=0; vertIdxvertNum; vertIdx++) { vert = obj->vert + vertIdx; d = (cam->orthographic ? 1.0f : AIR_CAST(float, cam->vspDist/vert->coord[2])); vert->coord[0] *= d; vert->coord[1] *= d; /* coord[2] unchanged */ /* printf("%s: v[%d] = %g %g %g --> s = %g %g %g\n", "_limnObjectSTransform", pi, p->v[0], p->v[1], p->v[2], p->s[0], p->s[1], p->s[2]); */ } obj->vertSpace = limnSpaceScreen; return 0; } int _limnObjectDeviceTransform(limnObject *obj, limnCamera *cam, limnWindow *win) { static const char me[]="_limnObjectDeviceTransform"; unsigned int vertIdx; limnVertex *vert; float wy0, wy1, wx0, wx1, t; if (limnSpaceScreen != obj->vertSpace) { biffAddf(LIMN, "%s: object's verts in %s (not %s) space", me, airEnumStr(limnSpace, obj->vertSpace), airEnumStr(limnSpace, limnSpaceScreen)); return 1; } wx0 = 0; wx1 = AIR_CAST(float, (cam->uRange[1] - cam->uRange[0])*win->scale); wy0 = 0; wy1 = AIR_CAST(float, (cam->vRange[1] - cam->vRange[0])*win->scale); ELL_4V_SET(win->bbox, wx0, wy0, wx1, wy1); if (win->yFlip) { ELL_SWAP2(wy0, wy1, t); } for (vertIdx=0; vertIdxvertNum; vertIdx++) { vert = obj->vert + vertIdx; vert->coord[0] = AIR_CAST(float, AIR_AFFINE(cam->uRange[0], vert->coord[0], cam->uRange[1], wx0, wx1)); vert->coord[1] = AIR_CAST(float, AIR_AFFINE(cam->vRange[0], vert->coord[1], cam->vRange[1], wy0, wy1)); /* coord[2] unchanged */ /* printf("%s: s[%d] = %g %g --> s = %g %g\n", "_limnObjectDTransform", pi, p->s[0], p->s[1], p->d[0], p->d[1]); */ } obj->vertSpace = limnSpaceDevice; return 0; } int limnObjectSpaceTransform(limnObject *obj, limnCamera *cam, limnWindow *win, int space) { static const char me[]="limnObjectSpaceTransform"; int E=0; /* HEY: deal with cam->orthographic */ switch(space) { case limnSpaceView: E = _limnObjectViewTransform(obj, cam); break; case limnSpaceScreen: E = _limnObjectScreenTransform(obj, cam); break; case limnSpaceDevice: E = _limnObjectDeviceTransform(obj, cam, win); break; default: biffAddf(LIMN, "%s: space %d unknown or unimplemented\n", me, space); return 1; break; } if (E) { biffAddf(LIMN, "%s: trouble", me); return 1; } return 0; } int limnObjectPartTransform(limnObject *obj, unsigned int partIdx, float xform[16]) { unsigned int vertIdxIdx; limnPart *part; limnVertex *vert; float tmp[4]; part= obj->part[partIdx]; for (vertIdxIdx=0; vertIdxIdxvertIdxNum; vertIdxIdx++) { vert = obj->vert + part->vertIdx[vertIdxIdx]; ELL_4MV_MUL(tmp, xform, vert->world); ELL_4V_COPY(vert->world, tmp); } return 0; } int _limnPartDepthCompare(const void *_a, const void *_b) { limnPart *const *a; limnPart *const *b; a = (limnPart *const *)_a; b = (limnPart *const *)_b; return AIR_COMPARE((*b)->depth, (*a)->depth); } int limnObjectDepthSortParts(limnObject *obj) { limnPart *part; limnVertex *vert; limnFace *face; limnEdge *edge; unsigned int partIdx, ii; for (partIdx=0; partIdxpartNum; partIdx++) { part = obj->part[partIdx]; part->depth = 0; for (ii=0; iivertIdxNum; ii++) { vert = obj->vert + part->vertIdx[ii]; part->depth += vert->coord[2]; } part->depth /= part->vertIdxNum; } qsort(obj->part, obj->partNum, sizeof(limnPart*), _limnPartDepthCompare); /* re-assign partIdx, post-sorting */ for (partIdx=0; partIdxpartNum; partIdx++) { part = obj->part[partIdx]; for (ii=0; iiedgeIdxNum; ii++) { edge = obj->edge + part->edgeIdx[ii]; edge->partIdx = partIdx; } for (ii=0; iifaceIdxNum; ii++) { face = obj->face + part->faceIdx[ii]; face->partIdx = partIdx; } } return 0; } int _limnFaceDepthCompare(const void *_a, const void *_b) { limnFace *const*a; limnFace *const*b; a = (limnFace *const*)_a; b = (limnFace *const*)_b; return -AIR_COMPARE((*a)->depth, (*b)->depth); } int limnObjectDepthSortFaces(limnObject *obj) { limnFace *face; limnVertex *vert; unsigned int faceIdx, vii; obj->faceSort = AIR_MALLOC(obj->faceNum, limnFace *); for (faceIdx=0; faceIdxfaceNum; faceIdx++) { face = obj->face + faceIdx; face->depth = 0; for (vii=0; viisideNum; vii++) { vert = obj->vert + face->vertIdx[vii]; face->depth += vert->coord[2]; } face->depth /= face->sideNum; obj->faceSort[faceIdx] = face; } qsort(obj->faceSort, obj->faceNum, sizeof(limnFace *), _limnFaceDepthCompare); return 0; } int limnObjectFaceReverse(limnObject *obj) { static const char me[]="limnObjectFaceReverse"; limnFace *face; unsigned int faceIdx, sii; int *buff; if (!obj) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } buff = NULL; for (faceIdx=0; faceIdxfaceNum; faceIdx++) { face = obj->face + faceIdx; buff = (int *)calloc(face->sideNum, sizeof(int)); if (!(buff)) { biffAddf(LIMN, "%s: couldn't allocate %d side buffer for face %d\n", me, face->sideNum, faceIdx); return 1; } memcpy(buff, face->vertIdx, face->sideNum*sizeof(int)); for (sii=0; siisideNum; sii++) { face->vertIdx[sii] = buff[face->sideNum-1-sii]; } memcpy(buff, face->edgeIdx, face->sideNum*sizeof(int)); for (sii=0; siisideNum; sii++) { face->edgeIdx[sii] = buff[face->sideNum-1-sii]; } free(buff); } return 0; } teem-1.11.0~svn6057/src/limn/lpu_sort.c0000664000175000017500000000506512173673666017375 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" #include "privateLimn.h" #define INFO "Sorts primitives according to given values" static const char *myinfo = (INFO ". "); int limnpu_sortMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *hopt = NULL; char *err, *perr; airArray *mop; int pret; limnPolyData *pld; Nrrd *nin; char *out; hestOptAdd(&hopt, NULL, "input lpld", airTypeOther, 1, 1, &pld, NULL, "input polydata filename", NULL, NULL, limnHestPolyDataLMPD); hestOptAdd(&hopt, NULL, "input nrrd", airTypeOther, 1, 1, &nin, NULL, "input nrrd filename", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, NULL, "output", airTypeString, 1, 1, &out, NULL, "output lpld filename"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(myinfo); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (limnPolyDataPrimitiveSort(pld, nin)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:%s", me, err); airMopError(mop); return 1; } if (limnPolyDataSave(out, pld)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } unrrduCmd limnpu_sortCmd = { "sort", INFO, limnpu_sortMain, AIR_FALSE }; teem-1.11.0~svn6057/src/limn/shapes.c0000664000175000017500000003343712165631065017001 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" int limnObjectCubeAdd(limnObject *obj, unsigned int lookIdx) { unsigned int vII[4], vII0, partIdx; partIdx = limnObjectPartAdd(obj); /* HEY: we have to set this first so that obj->setVertexRGBAFromLook can do the right thing */ obj->part[partIdx]->lookIdx = lookIdx; /* 7 6 z 4 5 | y | / 3 2 | / | / 0 1 ------ x */ vII0 = limnObjectVertexAdd(obj, partIdx, -1, -1, -1); limnObjectVertexAdd(obj, partIdx, 1, -1, -1); limnObjectVertexAdd(obj, partIdx, 1, 1, -1); limnObjectVertexAdd(obj, partIdx, -1, 1, -1); limnObjectVertexAdd(obj, partIdx, -1, -1, 1); limnObjectVertexAdd(obj, partIdx, 1, -1, 1); limnObjectVertexAdd(obj, partIdx, 1, 1, 1); limnObjectVertexAdd(obj, partIdx, -1, 1, 1); ELL_4V_SET(vII, vII0+3, vII0+2, vII0+1, vII0+0); limnObjectFaceAdd(obj, partIdx, lookIdx, 4, vII); ELL_4V_SET(vII, vII0+1, vII0+5, vII0+4, vII0+0); limnObjectFaceAdd(obj, partIdx, lookIdx, 4, vII); ELL_4V_SET(vII, vII0+2, vII0+6, vII0+5, vII0+1); limnObjectFaceAdd(obj, partIdx, lookIdx, 4, vII); ELL_4V_SET(vII, vII0+3, vII0+7, vII0+6, vII0+2); limnObjectFaceAdd(obj, partIdx, lookIdx, 4, vII); ELL_4V_SET(vII, vII0+0, vII0+4, vII0+7, vII0+3); limnObjectFaceAdd(obj, partIdx, lookIdx, 4, vII); ELL_4V_SET(vII, vII0+5, vII0+6, vII0+7, vII0+4); limnObjectFaceAdd(obj, partIdx, lookIdx, 4, vII); return partIdx; } int limnObjectSquareAdd(limnObject *obj, unsigned int lookIdx) { unsigned int vII0, vII[4], partIdx; partIdx = limnObjectPartAdd(obj); /* HEY: we have to set this first so that obj->setVertexRGBAFromLook can do the right thing */ obj->part[partIdx]->lookIdx = lookIdx; vII0 = limnObjectVertexAdd(obj, partIdx, 0, 0, 0); limnObjectVertexAdd(obj, partIdx, 1, 0, 0); limnObjectVertexAdd(obj, partIdx, 1, 1, 0); limnObjectVertexAdd(obj, partIdx, 0, 1, 0); ELL_4V_SET(vII, vII0+0, vII0+1, vII0+2, vII0+3); limnObjectFaceAdd(obj, partIdx, lookIdx, 4, vII); return partIdx; } /* ******** limnObjectCylinderAdd ** ** adds a cylinder that fills up the bi-unit cube [-1,1]^3, ** with axis "axis" (0:X, 1:Y, 2:Z), with discretization "res" */ int limnObjectCylinderAdd(limnObject *obj, unsigned int lookIdx, unsigned int axis, unsigned int res) { unsigned int partIdx, ii, jj, tmp, vII0=0, *vII; double theta; partIdx = limnObjectPartAdd(obj); /* HEY: we have to set this first so that obj->setVertexRGBAFromLook can do the right thing */ obj->part[partIdx]->lookIdx = lookIdx; vII = (unsigned int *)calloc(res, sizeof(unsigned int)); for (ii=0; ii<=res-1; ii++) { theta = AIR_AFFINE(0, ii, res, 0, 2*AIR_PI); switch(axis) { case 0: tmp = limnObjectVertexAdd(obj, partIdx, 1, AIR_CAST(float, -sin(theta)), AIR_CAST(float, cos(theta))); limnObjectVertexAdd(obj, partIdx, -1, AIR_CAST(float, -sin(theta)), AIR_CAST(float, cos(theta))); break; case 1: tmp = limnObjectVertexAdd(obj, partIdx, AIR_CAST(float, sin(theta)), 1, AIR_CAST(float, cos(theta))); limnObjectVertexAdd(obj, partIdx, AIR_CAST(float, sin(theta)), -1, AIR_CAST(float, cos(theta))); break; case 2: default: tmp = limnObjectVertexAdd(obj, partIdx, AIR_CAST(float, cos(theta)), AIR_CAST(float, sin(theta)), 1); limnObjectVertexAdd(obj, partIdx, AIR_CAST(float, cos(theta)), AIR_CAST(float, sin(theta)), -1); break; } if (!ii) { vII0 = tmp; } } /* add all side faces */ for (ii=0; ii<=res-1; ii++) { jj = (ii+1) % res; ELL_4V_SET(vII, vII0 + 2*ii, vII0 + 2*ii + 1, vII0 + 2*jj + 1, vII0 + 2*jj); limnObjectFaceAdd(obj, partIdx, lookIdx, 4, vII); } /* add top */ for (ii=0; ii<=res-1; ii++) { vII[ii] = vII0 + 2*ii; } limnObjectFaceAdd(obj, partIdx, lookIdx, res, vII); /* add bottom */ for (ii=0; ii<=res-1; ii++) { vII[ii] = vII0 + 2*(res-1-ii) + 1; } limnObjectFaceAdd(obj, partIdx, lookIdx, res, vII); free(vII); return partIdx; } int limnObjectConeAdd(limnObject *obj, unsigned int lookIdx, unsigned int axis, unsigned int res) { double th; unsigned int partIdx, tmp, vII0=0, ii, jj, *vII; vII = (unsigned int *)calloc(res, sizeof(unsigned int)); partIdx = limnObjectPartAdd(obj); /* HEY: we have to set this first so that obj->setVertexRGBAFromLook can do the right thing */ obj->part[partIdx]->lookIdx = lookIdx; for (ii=0; ii<=res-1; ii++) { th = AIR_AFFINE(0, ii, res, 0, 2*AIR_PI); switch(axis) { case 0: tmp = limnObjectVertexAdd(obj, partIdx, 0, AIR_CAST(float, -sin(th)), AIR_CAST(float, cos(th))); break; case 1: tmp = limnObjectVertexAdd(obj, partIdx, AIR_CAST(float, sin(th)), 0, AIR_CAST(float, cos(th))); break; case 2: default: tmp = limnObjectVertexAdd(obj, partIdx, AIR_CAST(float, cos(th)), AIR_CAST(float, sin(th)), 0); break; } if (!ii) { vII0 = tmp; } } switch(axis) { case 0: limnObjectVertexAdd(obj, partIdx, 1, 0, 0); break; case 1: limnObjectVertexAdd(obj, partIdx, 0, 1, 0); break; case 2: default: limnObjectVertexAdd(obj, partIdx, 0, 0, 1); break; } for (ii=0; ii<=res-1; ii++) { jj = (ii+1) % res; ELL_3V_SET(vII, vII0+ii, vII0+jj, vII0+res); limnObjectFaceAdd(obj, partIdx, lookIdx, 3, vII); } for (ii=0; ii<=res-1; ii++) { vII[ii] = vII0+res-1-ii; } limnObjectFaceAdd(obj, partIdx, lookIdx, res, vII); free(vII); return partIdx; } int limnObjectPolarSphereAdd(limnObject *obj, unsigned int lookIdx, unsigned int axis, unsigned int thetaRes, unsigned int phiRes) { unsigned int partIdx, vII0, nti, ti, pi, vII[4], pl; double t, p; thetaRes = AIR_MAX(thetaRes, 3); phiRes = AIR_MAX(phiRes, 2); partIdx = limnObjectPartAdd(obj); /* HEY: we have to set this first so that obj->setVertexRGBAFromLook can do the right thing */ obj->part[partIdx]->lookIdx = lookIdx; switch(axis) { case 0: vII0 = limnObjectVertexAdd(obj, partIdx, 1, 0, 0); break; case 1: vII0 = limnObjectVertexAdd(obj, partIdx, 0, 1, 0); break; case 2: default: vII0 = limnObjectVertexAdd(obj, partIdx, 0, 0, 1); break; } for (pi=1; pi<=phiRes-1; pi++) { p = AIR_AFFINE(0, pi, phiRes, 0, AIR_PI); for (ti=0; ti<=thetaRes-1; ti++) { t = AIR_AFFINE(0, ti, thetaRes, 0, 2*AIR_PI); switch(axis) { case 0: limnObjectVertexAdd(obj, partIdx, AIR_CAST(float, cos(p)), AIR_CAST(float, -sin(t)*sin(p)), AIR_CAST(float, cos(t)*sin(p))); break; case 1: limnObjectVertexAdd(obj, partIdx, AIR_CAST(float, sin(t)*sin(p)), AIR_CAST(float, cos(p)), AIR_CAST(float, cos(t)*sin(p))); break; case 2: default: limnObjectVertexAdd(obj, partIdx, AIR_CAST(float, cos(t)*sin(p)), AIR_CAST(float, sin(t)*sin(p)), AIR_CAST(float, cos(p))); break; } } } switch(axis) { case 0: pl = limnObjectVertexAdd(obj, partIdx, -1, 0, 0); break; case 1: pl = limnObjectVertexAdd(obj, partIdx, 0, -1, 0); break; case 2: default: pl = limnObjectVertexAdd(obj, partIdx, 0, 0, -1); break; } for (ti=1; ti<=thetaRes; ti++) { nti = ti < thetaRes ? ti+1 : 1; ELL_3V_SET(vII, vII0+ti, vII0+nti, vII0+0); limnObjectFaceAdd(obj, partIdx, lookIdx, 3, vII); } for (pi=0; pi<=phiRes-3; pi++) { for (ti=1; ti<=thetaRes; ti++) { nti = ti < thetaRes ? ti+1 : 1; ELL_4V_SET(vII, vII0+pi*thetaRes + ti, vII0+(pi+1)*thetaRes + ti, vII0+(pi+1)*thetaRes + nti, vII0+pi*thetaRes + nti); limnObjectFaceAdd(obj, partIdx, lookIdx, 4, vII); } } for (ti=1; ti<=thetaRes; ti++) { nti = ti < thetaRes ? ti+1 : 1; ELL_3V_SET(vII, vII0+pi*thetaRes + ti, pl, vII0+pi*thetaRes + nti); limnObjectFaceAdd(obj, partIdx, lookIdx, 3, vII); } return partIdx; } int limnObjectPolarSuperquadFancyAdd(limnObject *obj, unsigned int lookIdx, unsigned int axis, float A, float B, float C, float R, unsigned int thetaRes, unsigned int phiRes) { unsigned int partIdx, vII0, nti, ti, pi, vII[4], pl; double x, y, z, t, p; AIR_UNUSED(R); thetaRes = AIR_MAX(thetaRes, 3); phiRes = AIR_MAX(phiRes, 2); partIdx = limnObjectPartAdd(obj); /* HEY: we have to set this first so that obj->setVertexRGBAFromLook can do the right thing */ obj->part[partIdx]->lookIdx = lookIdx; switch(axis) { case 0: vII0 = limnObjectVertexAdd(obj, partIdx, 1, 0, 0); break; case 1: vII0 = limnObjectVertexAdd(obj, partIdx, 0, 1, 0); break; case 2: default: vII0 = limnObjectVertexAdd(obj, partIdx, 0, 0, 1); break; } for (pi=1; pi<=phiRes-1; pi++) { p = AIR_AFFINE(0, pi, phiRes, 0, AIR_PI); for (ti=0; ti<=thetaRes-1; ti++) { t = AIR_AFFINE(0, ti, thetaRes, 0, 2*AIR_PI); switch(axis) { case 0: x = airSgnPow(cos(p),B); y = -airSgnPow(sin(t),A) * airSgnPow(sin(p),B); z = airSgnPow(cos(t),A) * airSgnPow(sin(p),B); if (C != B) { /* modify profile along y axis to create beta=C */ double yp, ymax; yp = airSgnPow(sin(acos(airSgnPow(x, 1/C))), C); ymax = airSgnPow(sin(p), B); if (ymax) { y *= yp/ymax; } } break; case 1: x = airSgnPow(sin(t),A) * airSgnPow(sin(p),B); y = airSgnPow(cos(p),B); z = airSgnPow(cos(t),A) * airSgnPow(sin(p),B); break; case 2: default: x = airSgnPow(cos(t),A) * airSgnPow(sin(p),B); y = airSgnPow(sin(t),A) * airSgnPow(sin(p),B); z = airSgnPow(cos(p),B); if (C != B) { /* modify profile along y axis to create beta=C */ double yp, ymax; yp = airSgnPow(sin(acos(airSgnPow(z, 1/C))), C); ymax = airSgnPow(sin(p), B); if (ymax) { y *= yp/ymax; } } break; } limnObjectVertexAdd(obj, partIdx, AIR_CAST(float, x), AIR_CAST(float, y), AIR_CAST(float, z)); } } switch(axis) { case 0: pl = limnObjectVertexAdd(obj, partIdx, -1, 0, 0); break; case 1: pl = limnObjectVertexAdd(obj, partIdx, 0, -1, 0); break; case 2: default: pl = limnObjectVertexAdd(obj, partIdx, 0, 0, -1); break; } for (ti=1; ti<=thetaRes; ti++) { nti = ti < thetaRes ? ti+1 : 1; ELL_3V_SET(vII, vII0+ti, vII0+nti, vII0+0); limnObjectFaceAdd(obj, partIdx, lookIdx, 3, vII); } for (pi=0; pi<=phiRes-3; pi++) { for (ti=1; ti<=thetaRes; ti++) { nti = ti < thetaRes ? ti+1 : 1; ELL_4V_SET(vII, vII0+pi*thetaRes + ti, vII0+(pi+1)*thetaRes + ti, vII0+(pi+1)*thetaRes + nti, vII0+pi*thetaRes + nti); limnObjectFaceAdd(obj, partIdx, lookIdx, 4, vII); } } for (ti=1; ti<=thetaRes; ti++) { nti = ti < thetaRes ? ti+1 : 1; ELL_3V_SET(vII, vII0+pi*thetaRes + ti, pl, vII0+pi*thetaRes + nti); limnObjectFaceAdd(obj, partIdx, lookIdx, 3, vII); } return partIdx; } int limnObjectPolarSuperquadAdd(limnObject *obj, unsigned int lookIdx, unsigned int axis, float A, float B, unsigned int thetaRes, unsigned int phiRes) { return limnObjectPolarSuperquadFancyAdd(obj, lookIdx, axis, A, B, B, 0, thetaRes, phiRes); } teem-1.11.0~svn6057/src/limn/lpu_about.c0000664000175000017500000000427112173673666017516 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" #define INFO "Information about this program and its use" int limnpu_aboutMain(int argc, const char **argv, const char *me, hestParm *hparm) { char buff[AIR_STRLEN_MED], fmt[AIR_STRLEN_MED]; char par1[] = "\t\t\t\t" "\"lpu\" is a complete hack.\n"; char par2[] = "\t\t\t\t" "Users are encouraged to take cover.\n"; AIR_UNUSED(argc); AIR_UNUSED(argv); AIR_UNUSED(me); fprintf(stderr, "\n"); sprintf(buff, "--- lpu: LimnPolyData command-line interface ---"); sprintf(fmt, "%%%ds\n", (int)((hparm->columns-strlen(buff))/2 + strlen(buff) - 1)); fprintf(stderr, fmt, buff); sprintf(buff, "(Teem version %s, %s)", airTeemVersion, airTeemReleaseDate); sprintf(fmt, "%%%ds\n", (int)((hparm->columns-strlen(buff))/2 + strlen(buff) - 1)); fprintf(stderr, fmt, buff); fprintf(stderr, "\n"); _hestPrintStr(stderr, 1, 0, 78, par1, AIR_FALSE); _hestPrintStr(stderr, 1, 0, 78, par2, AIR_FALSE); return 0; } unrrduCmd limnpu_aboutCmd = { "about", INFO, limnpu_aboutMain, AIR_FALSE }; teem-1.11.0~svn6057/src/limn/splineEval.c0000664000175000017500000002747012165631065017620 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" void _limnSplineIntervalFind_Unknown(int *ii, double *ff, limnSpline *spline, double tt) { static const char me[]="_limnSplineIntervalFind_Unknown"; AIR_UNUSED(ii); AIR_UNUSED(ff); AIR_UNUSED(spline); AIR_UNUSED(tt); fprintf(stderr, "%s: WARNING: spline type unset somewhere\n", me); return; } void _limnSplineIntervalFind_NonWarp(int *ii, double *ff, limnSpline *spline, double tt) { int N; N = spline->ncpt->axis[2].size + (spline->loop ? 1 : 0); tt = AIR_CLAMP(0, tt, N-1); *ii = (int)tt; *ff = tt - *ii; return; } void _limnSplineIntervalFind_Warp(int *ii, double *ff, limnSpline *spline, double tt) { int N; N = spline->ncpt->axis[2].size; tt = AIR_CLAMP(spline->time[0], tt, spline->time[N-1]); *ii = AIR_CLAMP(0, *ii, N-2); /* the last value of ii may be the right one */ if (!AIR_IN_CL(spline->time[*ii], tt, spline->time[*ii+1])) { /* HEY: make this a binary search */ for (*ii=0; *iitime[*ii], tt, spline->time[*ii+1])) { break; } } } *ff = (tt - spline->time[*ii])/(spline->time[*ii+1] - spline->time[*ii]); return; } typedef void (*_limnSplineIntervalFind_t)(int *, double *, limnSpline *, double); _limnSplineIntervalFind_t _limnSplineIntervalFind[LIMN_SPLINE_TYPE_MAX+1] = { _limnSplineIntervalFind_Unknown, _limnSplineIntervalFind_NonWarp, _limnSplineIntervalFind_Warp, _limnSplineIntervalFind_NonWarp, _limnSplineIntervalFind_NonWarp, _limnSplineIntervalFind_NonWarp }; void _limnSplineWeightsFind_Unknown(double *wght, limnSpline *spline, double f) { static const char me[]="_limnSplineWeights_Unknown"; AIR_UNUSED(wght); AIR_UNUSED(spline); AIR_UNUSED(f); fprintf(stderr, "%s: WARNING: spline type unset somewhere\n", me); return; } void _limnSplineWeightsFind_Linear(double *wght, limnSpline *spline, double f) { AIR_UNUSED(spline); ELL_4V_SET(wght, 0, 1-f, f, 0); /* fprintf(stderr, "%g ----> %g %g %g %g\n", f, wght[0], wght[1], wght[2], wght[3]); */ return; } void _limnSplineWeightsFind_Hermite(double *wght, limnSpline *spline, double f) { double f3, f2; AIR_UNUSED(spline); f3 = f*(f2 = f*f); ELL_4V_SET(wght, 2*f3 - 3*f2 + 1, f3 - 2*f2 + f, f3 - f2, -2*f3 + 3*f2); return; } void _limnSplineWeightsFind_CubicBezier(double *wght, limnSpline *spline, double f) { double g; AIR_UNUSED(spline); g = 1 - f; ELL_4V_SET(wght, g*g*g, 3*g*g*f, 3*g*f*f, f*f*f); return; } /* lifted from nrrd/kernel.c */ #define _BCCUBIC(x, B, C) \ ((x) >= 2.0 ? 0 : \ ((x) >= 1.0 \ ? (((-B/6 - C)*(x) + B + 5*C)*(x) -2*B - 8*C)*(x) + 4*B/3 + 4*C \ : ((2 - 3*B/2 - C)*(x) - 3 + 2*B + C)*(x)*(x) + 1 - B/3)) void _limnSplineWeightsFind_BC(double *wght, limnSpline *spline, double f) { double B, C, f0, f1, f2, f3; B = spline->B; C = spline->C; f0 = f+1; f1 = f; f2 = AIR_ABS(f-1); f3 = AIR_ABS(f-2); ELL_4V_SET(wght, _BCCUBIC(f0, B, C), _BCCUBIC(f1, B, C), _BCCUBIC(f2, B, C), _BCCUBIC(f3, B, C)); return; } typedef void (*_limnSplineWeightsFind_t)(double *, limnSpline *, double); _limnSplineWeightsFind_t _limnSplineWeightsFind[LIMN_SPLINE_TYPE_MAX+1] = { _limnSplineWeightsFind_Unknown, _limnSplineWeightsFind_Linear, _limnSplineWeightsFind_Hermite, /* TimeWarp */ _limnSplineWeightsFind_Hermite, _limnSplineWeightsFind_CubicBezier, _limnSplineWeightsFind_BC }; void _limnSplineIndexFind(int *idx, limnSpline *spline, int ii) { int N, ti[4]; N = spline->ncpt->axis[2].size; if (limnSplineTypeHasImplicitTangents[spline->type]) { if (spline->loop) { ELL_4V_SET(ti, AIR_MOD(ii-1, N), AIR_MOD(ii+0, N), AIR_MOD(ii+1, N), AIR_MOD(ii+2, N)); } else { ELL_4V_SET(ti, AIR_CLAMP(0, ii-1, N-1), AIR_CLAMP(0, ii+0, N-1), AIR_CLAMP(0, ii+1, N-1), AIR_CLAMP(0, ii+2, N-1)); } ELL_4V_SET(idx, 1 + 3*ti[0], 1 + 3*ti[1], 1 + 3*ti[2], 1 + 3*ti[3]); } else { if (spline->loop) { ELL_4V_SET(ti, AIR_MOD(ii+0, N), AIR_MOD(ii+0, N), AIR_MOD(ii+1, N), AIR_MOD(ii+1, N)); } else { ELL_4V_SET(ti, AIR_CLAMP(0, ii+0, N-1), AIR_CLAMP(0, ii+0, N-1), AIR_CLAMP(0, ii+1, N-1), AIR_CLAMP(0, ii+1, N-1)); } ELL_4V_SET(idx, 1 + 3*ti[0], 2 + 3*ti[1], 0 + 3*ti[2], 1 + 3*ti[3]); } } void _limnSplineFinish_Unknown(double *out, limnSpline *spline, int ii, double *wght) { static const char me[]="_limnSplineFinish_Unknown"; AIR_UNUSED(out); AIR_UNUSED(spline); AIR_UNUSED(ii); AIR_UNUSED(wght); fprintf(stderr, "%s: WARNING: spline info unset somewhere\n", me); return; } void _limnSplineFinish_Scalar(double *out, limnSpline *spline, int ii, double *wght) { int idx[4]; double *cpt; cpt = (double*)(spline->ncpt->data); _limnSplineIndexFind(idx, spline, ii); *out = ( wght[0]*cpt[idx[0]] + wght[1]*cpt[idx[1]] + wght[2]*cpt[idx[2]] + wght[3]*cpt[idx[3]]); return; } void _limnSplineFinish_2Vec(double *out, limnSpline *spline, int ii, double *wght) { int idx[4]; double *cpt; cpt = (double*)(spline->ncpt->data); _limnSplineIndexFind(idx, spline, ii); out[0] = ( wght[0]*cpt[0 + 2*idx[0]] + wght[1]*cpt[0 + 2*idx[1]] + wght[2]*cpt[0 + 2*idx[2]] + wght[3]*cpt[0 + 2*idx[3]]); out[1] = ( wght[0]*cpt[1 + 2*idx[0]] + wght[1]*cpt[1 + 2*idx[1]] + wght[2]*cpt[1 + 2*idx[2]] + wght[3]*cpt[1 + 2*idx[3]]); return; } void _limnSplineFinish_3Vec(double *out, limnSpline *spline, int ii, double *wght) { int idx[4]; double *cpt; cpt = (double*)(spline->ncpt->data); _limnSplineIndexFind(idx, spline, ii); out[0] = ( wght[0]*cpt[0 + 3*idx[0]] + wght[1]*cpt[0 + 3*idx[1]] + wght[2]*cpt[0 + 3*idx[2]] + wght[3]*cpt[0 + 3*idx[3]]); out[1] = ( wght[0]*cpt[1 + 3*idx[0]] + wght[1]*cpt[1 + 3*idx[1]] + wght[2]*cpt[1 + 3*idx[2]] + wght[3]*cpt[1 + 3*idx[3]]); out[2] = ( wght[0]*cpt[2 + 3*idx[0]] + wght[1]*cpt[2 + 3*idx[1]] + wght[2]*cpt[2 + 3*idx[2]] + wght[3]*cpt[2 + 3*idx[3]]); return; } void _limnSplineFinish_Normal(double *out, limnSpline *spline, int ii, double *wght) { AIR_UNUSED(out); AIR_UNUSED(spline); AIR_UNUSED(ii); AIR_UNUSED(wght); fprintf(stderr, "%s: NOT IMPLEMENTED\n", "_limnSplineFinish_Normal"); return; } void _limnSplineFinish_4Vec(double *out, limnSpline *spline, int ii, double *wght) { int idx[4]; double *cpt; cpt = (double*)(spline->ncpt->data); _limnSplineIndexFind(idx, spline, ii); out[0] = ( wght[0]*cpt[0 + 4*idx[0]] + wght[1]*cpt[0 + 4*idx[1]] + wght[2]*cpt[0 + 4*idx[2]] + wght[3]*cpt[0 + 4*idx[3]]); out[1] = ( wght[0]*cpt[1 + 4*idx[0]] + wght[1]*cpt[1 + 4*idx[1]] + wght[2]*cpt[1 + 4*idx[2]] + wght[3]*cpt[1 + 4*idx[3]]); out[2] = ( wght[0]*cpt[2 + 4*idx[0]] + wght[1]*cpt[2 + 4*idx[1]] + wght[2]*cpt[2 + 4*idx[2]] + wght[3]*cpt[2 + 4*idx[3]]); out[3] = ( wght[0]*cpt[3 + 4*idx[0]] + wght[1]*cpt[3 + 4*idx[1]] + wght[2]*cpt[3 + 4*idx[2]] + wght[3]*cpt[3 + 4*idx[3]]); return; } /* ** HEY: I have no whether Hermite splines work with this */ void _limnSplineFinish_Quaternion(double *out, limnSpline *spline, int ii, double *wght) { int idx[4]; double *cpt; cpt = (double*)(spline->ncpt->data); _limnSplineIndexFind(idx, spline, ii); ell_q_avg4_d(out, NULL, cpt + 4*idx[0], cpt + 4*idx[1], cpt + 4*idx[2], cpt + 4*idx[3], wght, LIMN_SPLINE_Q_AVG_EPS, 30 /* maxIter */); return; } typedef void (*_limnSplineFinish_t)(double *, limnSpline *, int, double *); _limnSplineFinish_t _limnSplineFinish[LIMN_SPLINE_INFO_MAX+1] = { _limnSplineFinish_Unknown, _limnSplineFinish_Scalar, _limnSplineFinish_2Vec, _limnSplineFinish_3Vec, _limnSplineFinish_Normal, _limnSplineFinish_4Vec, _limnSplineFinish_Quaternion }; void limnSplineEvaluate(double *out, limnSpline *spline, double tt) { int ii=0; double ff, wght[4]; if (out && spline) { _limnSplineIntervalFind[spline->type](&ii, &ff, spline, tt); _limnSplineWeightsFind[spline->type](wght, spline, ff); _limnSplineFinish[spline->info](out, spline, ii, wght); } return; } int limnSplineNrrdEvaluate(Nrrd *nout, limnSpline *spline, Nrrd *nin) { static const char me[]="limnSplineNrrdEvaluate"; double tt, *out, (*lup)(const void *, size_t); int odim, infoSize; size_t I, M, size[NRRD_DIM_MAX+1]; if (!(nout && spline && nin)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (limnSplineInfoScalar == spline->info) { nrrdAxisInfoGet_va(nin, nrrdAxisInfoSize, size); infoSize = 1; odim = nin->dim; } else { nrrdAxisInfoGet_va(nin, nrrdAxisInfoSize, size+1); infoSize = size[0] = limnSplineInfoSize[spline->info]; odim = 1 + nin->dim; } if (nrrdMaybeAlloc_nva(nout, nrrdTypeDouble, odim, size)) { biffMovef(LIMN, NRRD, "%s: output allocation failed", me); return 1; } lup = nrrdDLookup[nin->type]; out = (double*)(nout->data); M = nrrdElementNumber(nin); for (I=0; Idata, I); limnSplineEvaluate(out, spline, tt); out += infoSize; } /* HEY: peripheral info copying? */ return 0; } int limnSplineSample(Nrrd *nout, limnSpline *spline, double minT, size_t M, double maxT) { static const char me[]="limnSplineSample"; airArray *mop; Nrrd *ntt; double *tt; size_t I; if (!(nout && spline)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); airMopAdd(mop, ntt=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(ntt, nrrdTypeDouble, 1, M)) { biffMovef(LIMN, NRRD, "%s: trouble allocating tmp nrrd", me); airMopError(mop); return 1; } tt = (double*)(ntt->data); for (I=0; IprimNum-1 && prange[1] <= pldIn->primNum-1 )) { fprintf(stderr, "%s: prange[0] %u or [1] %u outside range [0,%u]", me, prange[0], prange[1], pldIn->primNum-1); airMopError(mop); return 1; } if (!( prange[0] <= prange[1] )) { fprintf(stderr, "%s: need prange[0] %u <= [1] %u", me, prange[0], prange[1]); airMopError(mop); return 1; } nsel = nrrdNew(); airMopAdd(mop, nsel, (airMopper)nrrdNuke, airMopAlways); size[0] = pldIn->primNum; if (nrrdMaybeAlloc_nva(nsel, nrrdTypeDouble, 1, size)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble allocating buffer:%s", me, err); airMopError(mop); return 1; } sel = AIR_CAST(double *, nsel->data); for (pi=prange[0]; pi<=prange[1]; pi++) { sel[pi] = 1; } pldOut = limnPolyDataNew(); airMopAdd(mop, pldOut, (airMopper)limnPolyDataNix, airMopAlways); if (limnPolyDataPrimitiveSelect(pldOut, pldIn, nsel) || limnPolyDataSave(out, pldOut)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } unrrduCmd limnpu_pselCmd = { "psel", INFO, limnpu_pselMain, AIR_FALSE }; teem-1.11.0~svn6057/src/limn/splineMisc.c0000664000175000017500000002660112165631065017617 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" const char * _limnSplineTypeStr[LIMN_SPLINE_TYPE_MAX+1] = { "(unknown_spline_type)", "linear", "timewarp", "hermite", "cubic-bezier", "BC" }; const char * _limnSplineTypeDesc[LIMN_SPLINE_TYPE_MAX+1] = { "unknown spline type", "simple linear interpolation between control points", "pseudo-Hermite spline for warping time to uniform (integral) " "control point locations", "Hermite cubic interpolating spline", "cubic Bezier spline", "Mitchell-Netravalli BC-family of cubic splines" }; const char * _limnSplineTypeStrEqv[] = { "linear", "lin", "line", "tent", "timewarp", "time-warp", "warp", "hermite", "cubicbezier", "cubic-bezier", "bezier", "bez", "BC", "BC-spline", "" }; const int _limnSplineTypeValEqv[] = { limnSplineTypeLinear, limnSplineTypeLinear, limnSplineTypeLinear, limnSplineTypeLinear, limnSplineTypeTimeWarp, limnSplineTypeTimeWarp, limnSplineTypeTimeWarp, limnSplineTypeHermite, limnSplineTypeCubicBezier, limnSplineTypeCubicBezier, limnSplineTypeCubicBezier, limnSplineTypeCubicBezier, limnSplineTypeBC, limnSplineTypeBC }; const airEnum _limnSplineType = { "spline-type", LIMN_SPLINE_TYPE_MAX, _limnSplineTypeStr, NULL, _limnSplineTypeDesc, _limnSplineTypeStrEqv, _limnSplineTypeValEqv, AIR_FALSE }; const airEnum *const limnSplineType = &_limnSplineType; const char * _limnSplineInfoStr[LIMN_SPLINE_INFO_MAX+1] = { "(unknown_spline_info)", "scalar", "2vector", "3vector", "normal", "4vector", "quaternion" }; const char * _limnSplineInfoDesc[LIMN_SPLINE_INFO_MAX+1] = { "unknown spline info", "scalar", "2-vector", "3-vector", "surface normal, interpolated in S^2", "4-vector, interpolated in R^4", "quaternion, interpolated in S^3" }; const char * _limnSplineInfoStrEqv[] = { "scalar", "scale", "s", "t", "2-vector", "2vector", "2vec", "2v", "v2", "vec2", "vector2", "vector-2", "3-vector", "3vector", "3vec", "3v", "v3", "vec3", "vector3", "vector-3", "normal", "norm", "n", "4-vector", "4vector", "4vec", "4v", "v4", "vec4", "vector4", "vector-4", "quaternion", "quat", "q", "" }; #define SISS limnSplineInfoScalar #define SI2V limnSplineInfo2Vector #define SI3V limnSplineInfo3Vector #define SINN limnSplineInfoNormal #define SI4V limnSplineInfo4Vector #define SIQQ limnSplineInfoQuaternion const int _limnSplineInfoValEqv[] = { SISS, SISS, SISS, SISS, SI2V, SI2V, SI2V, SI2V, SI2V, SI2V, SI2V, SI2V, SI3V, SI3V, SI3V, SI3V, SI3V, SI3V, SI3V, SI3V, SINN, SINN, SINN, SI4V, SI4V, SI4V, SI4V, SI4V, SI4V, SI4V, SI4V, SIQQ, SIQQ, SIQQ }; const airEnum _limnSplineInfo = { "spline-info", LIMN_SPLINE_INFO_MAX, _limnSplineInfoStr, NULL, _limnSplineInfoDesc, _limnSplineInfoStrEqv, _limnSplineInfoValEqv, AIR_FALSE }; const airEnum *const limnSplineInfo = &_limnSplineInfo; /* ******** limnSplineInfoSize[] ** ** gives the number of scalars per "value" for each splineInfo */ unsigned int limnSplineInfoSize[LIMN_SPLINE_INFO_MAX+1] = { 0, /* limnSplineInfoUnknown */ 1, /* limnSplineInfoScalar */ 2, /* limnSplineInfo2Vector */ 3, /* limnSplineInfo3Vector */ 3, /* limnSplineInfoNormal */ 4, /* limnSplineInfo4Vector */ 4 /* limnSplineInfoQuaternion */ }; /* ******** limnSplineTypeHasImplicitTangents[] ** ** this is non-zero when the spline path is determined solely the ** main control point values, without needing additional control ** points (as in cubic Bezier) or tangent information (as in Hermite) */ int limnSplineTypeHasImplicitTangents[LIMN_SPLINE_TYPE_MAX+1] = { AIR_FALSE, /* limnSplineTypeUnknown */ AIR_TRUE, /* limnSplineTypeLinear */ AIR_FALSE, /* limnSplineTypeTimeWarp */ AIR_FALSE, /* limnSplineTypeHermite */ AIR_FALSE, /* limnSplineTypeCubicBezier */ AIR_TRUE /* limnSplineTypeBC */ }; int limnSplineNumPoints(limnSpline *spline) { int ret; ret = -1; if (spline) { ret = spline->ncpt->axis[2].size; } return ret; } double limnSplineMinT(limnSpline *spline) { double ret; ret = AIR_NAN; if (spline) { ret = spline->time ? spline->time[0] : 0; } return ret; } double limnSplineMaxT(limnSpline *spline) { double ret; int N; ret = AIR_NAN; if (spline) { N = spline->ncpt->axis[2].size; if (spline->time) { ret = spline->time[N-1]; } else { ret = spline->loop ? N : N-1; } } return ret; } void limnSplineBCSet(limnSpline *spline, double B, double C) { if (spline) { spline->B = B; spline->C = C; } } limnSplineTypeSpec * limnSplineTypeSpecParse(char *_str) { static const char me[]="limnSplineTypeSpecParse"; limnSplineTypeSpec *spec; int type; double B, C; char *str, *col, *bcS; airArray *mop; if (!( _str && airStrlen(_str) )) { biffAddf(LIMN, "%s: got NULL or emptry string", me); return NULL; } mop = airMopNew(); airMopAdd(mop, str=airStrdup(_str), airFree, airMopAlways); col = strchr(str, ':'); if (col) { *col = 0; bcS = col+1; } else { bcS = NULL; } if (limnSplineTypeUnknown == (type = airEnumVal(limnSplineType, str))) { biffAddf(LIMN, "%s: couldn't parse \"%s\" as spline type", me, str); airMopError(mop); return NULL; } if (!( (limnSplineTypeBC == type) == !!bcS )) { biffAddf(LIMN, "%s: spline type %s %s, but %s a parameter string %s%s%s", me, (limnSplineTypeBC == type) ? "is" : "is not", airEnumStr(limnSplineType, limnSplineTypeBC), !!bcS ? "got unexpected" : "did not get", !!bcS ? "\"" : "", !!bcS ? bcS : "", !!bcS ? "\"" : ""); airMopError(mop); return NULL; } if (limnSplineTypeBC == type) { if (2 != sscanf(bcS, "%lg,%lg", &B, &C)) { biffAddf(LIMN, "%s: couldn't parse \"B,C\" parameters from \"%s\"", me, bcS); airMopError(mop); return NULL; } } spec = (limnSplineTypeBC == type ? limnSplineTypeSpecNew(type, B, C) : limnSplineTypeSpecNew(type)); if (!spec) { biffAddf(LIMN, "%s: limnSplineTypeSpec allocation failed", me); airMopError(mop); return NULL; } airMopOkay(mop); return spec; } limnSpline * limnSplineParse(char *_str) { static const char me[]="limnSplineParse"; char *str, *col, *fnameS, *infoS, *typeS, *tmpS; int info; limnSpline *spline; limnSplineTypeSpec *spec; Nrrd *ninA, *ninB; airArray *mop; if (!( _str && airStrlen(_str) )) { biffAddf(LIMN, "%s: got NULL or empty string", me); return NULL; } mop = airMopNew(); airMopAdd(mop, str=airStrdup(_str), airFree, airMopAlways); /* find separation between filename and ":[:B,C]" */ col = strchr(str, ':'); if (!col) { biffAddf(LIMN, "%s: saw no colon separator (between nrrd filename and " "spline info) in \"%s\"", me, _str); airMopError(mop); return NULL; } fnameS = str; *col = 0; tmpS = col+1; airMopAdd(mop, ninA = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdLoad(ninA, fnameS, NULL)) { biffMovef(LIMN, NRRD, "%s: couldn't read control point nrrd:\n", me); airMopError(mop); return NULL; } /* find separation between splineInfo and "[:B,C]" */ col = strchr(tmpS, ':'); if (!col) { biffAddf(LIMN, "%s: saw no colon separator (between spline info " "and spline type) in \"%s\"", me, tmpS); airMopError(mop); return NULL; } infoS = tmpS; *col = 0; typeS = col+1; if (limnSplineInfoUnknown == (info = airEnumVal(limnSplineInfo, infoS))) { biffAddf(LIMN, "%s: couldn't parse \"%s\" as spline info", me, infoS); airMopError(mop); return NULL; } /* now parse [:B,C] */ if (!( spec = limnSplineTypeSpecParse(typeS) )) { biffAddf(LIMN, "%s: couldn't parse spline type in \"%s\":\n", me, typeS); airMopError(mop); return NULL; } if (limnSplineTypeTimeWarp == spec->type && limnSplineInfoScalar != info) { biffAddf(LIMN, "%s: can only time-warp %s info, not %s", me, airEnumStr(limnSplineInfo, limnSplineInfoScalar), airEnumStr(limnSplineInfo, info)); airMopError(mop); return NULL; } airMopAdd(mop, ninB = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (limnSplineNrrdCleverFix(ninB, ninA, info, spec->type)) { biffAddf(LIMN, "%s: couldn't reshape given nrrd:\n", me); airMopError(mop); return NULL; } if (!( spline = limnSplineNew(ninB, info, spec) )) { biffAddf(LIMN, "%s: couldn't create spline:\n", me); airMopError(mop); return NULL; } airMopOkay(mop); return spline; } /* ** the spline command-line spline type specification is of the form ** [:B,C] */ int _limnHestSplineTypeSpecParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { static const char me[] = "_limnHestSplineTypeSpecParse"; char *err2; limnSplineTypeSpec **specP; if (!(ptr && str && airStrlen(str))) { sprintf(err, "%s: got NULL pointer", me); return 1; } specP = (limnSplineTypeSpec **)ptr; if (!( *specP = limnSplineTypeSpecParse(str) )) { err2 = biffGetDone(LIMN); sprintf(err, "%s: couldn't parse \"%s\":\n", me, str); strncat(err, err2, AIR_STRLEN_HUGE-1-strlen(err)); free(err2); return 1; } return 0; } hestCB _limnHestSplineTypeSpec = { sizeof(limnSplineTypeSpec *), "spline type specification", _limnHestSplineTypeSpecParse, (airMopper)limnSplineTypeSpecNix }; hestCB * limnHestSplineTypeSpec = &_limnHestSplineTypeSpec; /* ** the spline command-line specification is of the form ** ::[:B,C] */ int _limnHestSplineParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { static const char me[] = "_limnHestSplineParse"; char *err2; limnSpline **splineP; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } splineP = (limnSpline **)ptr; if (!airStrlen(str)) { /* got an empty string, which for now we take as an okay way to ask for NO spline */ *splineP = NULL; return 0; } if (!( *splineP = limnSplineParse(str) )) { err2 = biffGetDone(LIMN); sprintf(err, "%s: couldn't parse \"%s\":\n", me, str); strncat(err, err2, AIR_STRLEN_HUGE-1-strlen(err)); free(err2); return 1; } return 0; } hestCB _limnHestSpline = { sizeof(limnSpline *), "spline specification", _limnHestSplineParse, (airMopper)limnSplineNix }; hestCB * limnHestSpline = &_limnHestSpline; teem-1.11.0~svn6057/src/limn/lpu_verts.c0000664000175000017500000000456012173673666017550 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" #include "privateLimn.h" #define INFO "Extract the vertex array" static const char *myinfo = (INFO ". "); int limnpu_vertsMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *hopt = NULL; char *err, *perr; airArray *mop; int pret; limnPolyData *pld; Nrrd *nout; char *out; hestOptAdd(&hopt, NULL, "input", airTypeOther, 1, 1, &pld, NULL, "input polydata filename", NULL, NULL, limnHestPolyDataLMPD); hestOptAdd(&hopt, NULL, "output", airTypeString, 1, 1, &out, NULL, "output nrrd filename"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(myinfo); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNix, airMopAlways); /* NOT nrrdNuke */ if (nrrdWrap_va(nout, pld->xyzw, nrrdTypeFloat, 2, 4, pld->xyzwNum) || nrrdSave(out, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } unrrduCmd limnpu_vertsCmd = { "verts", INFO, limnpu_vertsMain, AIR_FALSE }; teem-1.11.0~svn6057/src/limn/GNUmakefile0000664000175000017500000000442012165631065017412 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := limn #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### $(L).NEED = gage ell unrrdu nrrd biff hest air $(L).PUBLIC_HEADERS = limn.h $(L).PRIVATE_HEADERS = privateLimn.h $(L).OBJS = defaultsLimn.o qn.o light.o envmap.o cam.o methodsLimn.o \ obj.o transform.o shapes.o renderLimn.o io.o hestLimn.o \ splineMisc.o splineMethods.o splineEval.o enumsLimn.o \ polydata.o polyshapes.o polymod.o polyfilter.o \ lpuFlotsam.o lpu_about.o lpu_ccfind.o lpu_psel.o lpu_rast.o \ lpu_verts.o lpu_meas.o lpu_sort.o $(L).TESTS = test/map test/light test/tcam test/tps test/tspline test/tbc \ test/tcamanim test/soid test/off2eps test/triimg test/plot test/intx \ test/tio test/clip test/lpu test/tqn #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/limn/cam.c0000664000175000017500000003676312165631065016263 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" /* ******** limnCameraUpdate() ** ** sets in cam: W2V, V2W, U, V, N, vspNeer, vspFaar, vspDist ** and, if fov and aspect are set, this also sets uRange and vRange ** ** This does use biff to describe problems with camera settings */ int limnCameraUpdate(limnCamera *cam) { static const char me[] = "limnCameraUpdate"; double len, bb[4], uu[4], vv[4], nn[4], TT[16], RR[16]; if (!cam) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } ELL_4V_SET(uu, 0, 0, 0, 0); ELL_4V_SET(vv, 0, 0, 0, 0); ELL_4V_SET(nn, 0, 0, 0, 0); ELL_4V_SET(bb, 0, 0, 0, 1); ELL_3V_SUB(nn, cam->at, cam->from); len = ELL_3V_LEN(nn); if (!len) { biffAddf(LIMN, "%s: cam->at (%g,%g,%g) == cam->from", me, cam->at[0], cam->at[1], cam->at[2]); return 1; } if (cam->atRelative) { /* ctx->cam->{neer,dist} are "at" relative */ cam->vspNeer = cam->neer + len; cam->vspFaar = cam->faar + len; cam->vspDist = cam->dist + len; } else { /* ctx->cam->{neer,dist} are eye relative */ cam->vspNeer = cam->neer; cam->vspFaar = cam->faar; cam->vspDist = cam->dist; } if (!(cam->vspNeer > 0 && cam->vspDist > 0 && cam->vspFaar > 0)) { biffAddf(LIMN, "%s: eye-relative near (%g), dist (%g), or far (%g) <= 0", me, cam->vspNeer, cam->vspDist, cam->vspFaar); return 1; } if (!(cam->vspNeer <= cam->vspFaar)) { biffAddf(LIMN, "%s: eye-relative near (%g) further than far (%g)", me, cam->vspNeer, cam->vspFaar); return 1 ; } if (AIR_EXISTS(cam->fov)) { if (!( AIR_IN_OP(0.0, cam->fov, 180.0) )) { biffAddf(LIMN, "%s: cam->fov (%g) not in valid range between 0 and 180", me, cam->fov); return 1 ; } if (!AIR_EXISTS(cam->aspect)) { biffAddf(LIMN, "%s: cam->fov set, but cam->aspect isn't", me); return 1; } /* "fov" is half vertical angle */ cam->vRange[0] = -tan(cam->fov*AIR_PI/360)*(cam->vspDist); cam->vRange[1] = -cam->vRange[0]; cam->uRange[0] = cam->vRange[0]*(cam->aspect); cam->uRange[1] = -cam->uRange[0]; } /* else cam->fov isn't set, but we're not going to complain if uRange and vRange aren't both set ... */ ELL_3V_SCALE(nn, 1.0/len, nn); ELL_3V_CROSS(uu, nn, cam->up); len = ELL_3V_LEN(uu); if (!len) { biffAddf(LIMN, "%s: cam->up is co-linear with view direction", me); return 1 ; } ELL_3V_SCALE(uu, 1.0/len, uu); if (cam->rightHanded) { ELL_3V_CROSS(vv, nn, uu); } else { ELL_3V_CROSS(vv, uu, nn); } ELL_4V_COPY(cam->U, uu); ELL_4V_COPY(cam->V, vv); ELL_4V_COPY(cam->N, nn); ELL_4M_TRANSLATE_SET(TT, -cam->from[0], -cam->from[1], -cam->from[2]); ELL_4M_ROWS_SET(RR, uu, vv, nn, bb); ELL_4M_MUL(cam->W2V, RR, TT); ell_4m_inv_d(cam->V2W, cam->W2V); return 0; } /* ******** limnCameraAspectSet ** ** simply sets the "aspect" field of the cam. Note that calling this ** does *not* automatically mean that the uRange and vRange in the camera ** will be set according to the "fov"- the "fov" has to actually be set ** (be non-NaN) for that to happen. This allows dumber functions to ** call this whenever they have the information required to do so, even ** if the "aspect" is not going to be needed for a given camera use */ int limnCameraAspectSet(limnCamera *cam, unsigned int horz, unsigned int vert, int centering) { static const char me[] = "limnCameraAspectSet"; if (!cam) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (!( horz > 0 && vert > 0 )) { biffAddf(LIMN, "%s: bad image dimensions %ux%u", me, horz, vert); return 1; } if (airEnumValCheck(nrrdCenter, centering)) { biffAddf(LIMN, "%s: centering %d not valid", me, centering); return 1; } if (nrrdCenterCell == centering) { cam->aspect = ((double)horz)/vert; } else { cam->aspect = ((double)(horz-1))/(vert-1); } return 0; } /* ******** limnCameraPathMake ** ** uses limnSplines to do camera paths based on key-frames ** ** output: cameras at all "numFrames" frames are set in the ** PRE-ALLOCATED array of output cameras, "cam". ** ** input: ** keycam: array of keyframe cameras ** time: times associated with the key frames ** ---> both of these arrays are length "numKeys" <--- ** trackWhat: takes values from the limnCameraPathTrack* enum ** quatType: spline to control camera orientations. This is needed for ** tracking at or from, but not needed for limnCameraPathTrackBoth. ** This is the only limnSplineTypeSpec* argument that can be NULL. ** posType: spline to control whichever of from, at, and up are needed for ** the given style of tracking. ** distType: spline to control neer, faar, dist: positions of near clipping, ** far clipping, and image plane, as well as the ** distance between from and at (which is used if not doing ** limnCameraPathTrackBoth) ** viewType: spline to control fov (and aspect, if you're crazy) ** ** NOTE: The "atRelative", "orthographic", and "rightHanded" fields ** are copied from keycam[0] into all output cam[i], but you still need ** to correctly set them for all keycam[i] for limnCameraUpdate to work ** as expected. Also, for the sake of simplicity, this function only works ** with fov and aspect, instead of {u,v}Range, and hence both "fov" and ** "aspect" need to set in *all* the keycams, even if neither of them ** ever changes! */ int limnCameraPathMake(limnCamera *cam, int numFrames, limnCamera *keycam, double *time, int numKeys, int trackWhat, limnSplineTypeSpec *quatType, limnSplineTypeSpec *posType, limnSplineTypeSpec *distType, limnSplineTypeSpec *viewType) { static const char me[]="limnCameraPathMake"; char which[AIR_STRLEN_MED]; airArray *mop; Nrrd *nquat, *nfrom, *natpt, *nupvc, *ndist, *nfova, *ntime, *nsample; double fratVec[3], *quat, *from, *atpt, *upvc, *dist, *fova, W2V[9], N[3], fratDist; limnSpline *timeSpline, *quatSpline, *fromSpline, *atptSpline, *upvcSpline, *distSpline, *fovaSpline; limnSplineTypeSpec *timeType; int ii, E; if (!( cam && keycam && time && posType && distType && viewType )) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (!( AIR_IN_OP(limnCameraPathTrackUnknown, trackWhat, limnCameraPathTrackLast) )) { biffAddf(LIMN, "%s: trackWhat %d not in valid range [%d,%d]", me, trackWhat, limnCameraPathTrackUnknown+1, limnCameraPathTrackLast-1); return 1; } if (limnCameraPathTrackBoth != trackWhat && !quatType) { biffAddf(LIMN, "%s: need the quaternion limnSplineTypeSpec if not " "doing trackBoth", me); return 1; } /* create and allocate nrrds. For the time being, we're allocating more different nrrds, and filling their contents, than we need to-- nquat is not needed if we're doing limnCameraPathTrackBoth, for example. However, we do make an effort to only do the spline evaluation on the things we actually need to know. */ mop = airMopNew(); airMopAdd(mop, nquat = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nfrom = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, natpt = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nupvc = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ndist = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nfova = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntime = nrrdNew(), (airMopper)nrrdNix, airMopAlways); if (nrrdWrap_va(ntime, time, nrrdTypeDouble, 1, AIR_CAST(size_t, numKeys))) { biffMovef(LIMN, NRRD, "%s: trouble wrapping time values", me); airMopError(mop); return 1; } airMopAdd(mop, nsample = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); timeType = limnSplineTypeSpecNew(limnSplineTypeTimeWarp); airMopAdd(mop, timeType, (airMopper)limnSplineTypeSpecNix, airMopAlways); if (nrrdMaybeAlloc_va(nquat, nrrdTypeDouble, 2, AIR_CAST(size_t, 4), AIR_CAST(size_t, numKeys)) || nrrdMaybeAlloc_va(nfrom, nrrdTypeDouble, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys)) || nrrdMaybeAlloc_va(natpt, nrrdTypeDouble, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys)) || nrrdMaybeAlloc_va(nupvc, nrrdTypeDouble, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys)) || nrrdMaybeAlloc_va(ndist, nrrdTypeDouble, 2, AIR_CAST(size_t, 4), AIR_CAST(size_t, numKeys)) || nrrdMaybeAlloc_va(nfova, nrrdTypeDouble, 2, AIR_CAST(size_t, 2), AIR_CAST(size_t, numKeys))) { biffMovef(LIMN, NRRD, "%s: couldn't allocate buffer nrrds", me); airMopError(mop); return 1; } quat = (double*)(nquat->data); from = (double*)(nfrom->data); atpt = (double*)(natpt->data); upvc = (double*)(nupvc->data); dist = (double*)(ndist->data); fova = (double*)(nfova->data); /* check cameras, and put camera information into nrrds */ for (ii=0; ii ELL_4V_DOT(quat + 4*ii, quat + 4*(ii-1))) { ELL_4V_SCALE(quat + 4*ii, -1, quat + 4*ii); } } ELL_3V_COPY(from + 3*ii, keycam[ii].from); ELL_3V_COPY(atpt + 3*ii, keycam[ii].at); ELL_3V_COPY(upvc + 3*ii, keycam[ii].up); ELL_3V_SUB(fratVec, keycam[ii].from, keycam[ii].at); fratDist = ELL_3V_LEN(fratVec); ELL_4V_SET(dist + 4*ii, fratDist, keycam[ii].neer, keycam[ii].dist, keycam[ii].faar); ELL_2V_SET(fova + 2*ii, keycam[ii].fov, keycam[ii].aspect); } /* create splines from nrrds */ if (!( (strcpy(which, "quaternion"), quatSpline = limnSplineCleverNew(nquat, limnSplineInfoQuaternion, quatType)) && (strcpy(which, "from point"), fromSpline = limnSplineCleverNew(nfrom, limnSplineInfo3Vector, posType)) && (strcpy(which, "at point"), atptSpline = limnSplineCleverNew(natpt, limnSplineInfo3Vector, posType)) && (strcpy(which, "up vector"), upvcSpline = limnSplineCleverNew(nupvc, limnSplineInfo3Vector, posType)) && (strcpy(which, "plane distances"), distSpline = limnSplineCleverNew(ndist, limnSplineInfo4Vector, distType)) && (strcpy(which, "field-of-view"), fovaSpline = limnSplineCleverNew(nfova, limnSplineInfo2Vector, viewType)) && (strcpy(which, "time warp"), timeSpline = limnSplineCleverNew(ntime, limnSplineInfoScalar, timeType)) )) { biffAddf(LIMN, "%s: trouble creating %s spline", me, which); airMopError(mop); return 1; } airMopAdd(mop, quatSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, fromSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, atptSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, upvcSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, distSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, fovaSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, timeSpline, (airMopper)limnSplineNix, airMopAlways); /* evaluate splines */ E = AIR_FALSE; if (!E) E |= limnSplineSample(nsample, timeSpline, limnSplineMinT(timeSpline), numFrames, limnSplineMaxT(timeSpline)); quat = NULL; from = NULL; atpt = NULL; upvc = NULL; switch(trackWhat) { case limnCameraPathTrackAt: if (!E) E |= limnSplineNrrdEvaluate(natpt, atptSpline, nsample); if (!E) atpt = (double*)(natpt->data); if (!E) E |= limnSplineNrrdEvaluate(nquat, quatSpline, nsample); if (!E) quat = (double*)(nquat->data); break; case limnCameraPathTrackFrom: if (!E) E |= limnSplineNrrdEvaluate(nfrom, fromSpline, nsample); if (!E) from = (double*)(nfrom->data); if (!E) E |= limnSplineNrrdEvaluate(nquat, quatSpline, nsample); if (!E) quat = (double*)(nquat->data); break; case limnCameraPathTrackBoth: if (!E) E |= limnSplineNrrdEvaluate(nfrom, fromSpline, nsample); if (!E) from = (double*)(nfrom->data); if (!E) E |= limnSplineNrrdEvaluate(natpt, atptSpline, nsample); if (!E) atpt = (double*)(natpt->data); if (!E) E |= limnSplineNrrdEvaluate(nupvc, upvcSpline, nsample); if (!E) upvc = (double*)(nupvc->data); break; } dist = NULL; if (!E) E |= limnSplineNrrdEvaluate(ndist, distSpline, nsample); if (!E) dist = (double*)(ndist->data); fova = NULL; if (!E) E |= limnSplineNrrdEvaluate(nfova, fovaSpline, nsample); if (!E) fova = (double*)(nfova->data); if (E) { biffAddf(LIMN, "%s: trouble evaluating splines", me); airMopError(mop); return 1; } /* copy information from nrrds back into cameras */ for (ii=0; iidata); triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data); maxTriPerVert = nTriWithVert->axis[0].size - 1; for (ii=0; ii<3; ii++) { vertA = (vertWithTri + 3*triIdx)[ii]; vertB = (vertWithTri + 3*triIdx)[AIR_MOD(ii+1, 3)]; /* fprintf(stderr, "!%s: %u edge %u: vert{A,B} = %u %u\n", me, triIdx, ii, vertA, vertB); */ /* find the intersection of the sets of {triangles using vertA} and {triangles using vertB}: for reasonable surfaces should be either 0 or 2 triangles, and if its 2, then triIdx should be one of them */ intxNum = flipListIntx(intxBuff, triWithVert + (1+maxTriPerVert)*vertA, triWithVert + (1+maxTriPerVert)*vertB); if (2 == intxNum) { neighIdx = intxBuff[0]; if (neighIdx == triIdx) { neighIdx = intxBuff[1]; } neighGot[ii] = AIR_TRUE; neighInfo[ii][0] = neighIdx; neighInfo[ii][1] = vertB; neighInfo[ii][2] = vertA; } else { neighGot[ii] = AIR_FALSE; } } return; } /* ** determines if triIdx needs to be flipped, given that it should ** be seeing vertices vertA and vertB in that order */ static int flipNeed(Nrrd *nVertWithTri, unsigned int triIdx, unsigned int vertA, unsigned int vertB) { unsigned int *vertWithTri, vert[3]; int ai, bi; vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data); ELL_3V_COPY(vert, vertWithTri + 3*triIdx); for (ai=0; vert[ai] != vertA; ai++) ; for (bi=0; vert[bi] != vertB; bi++) ; return (1 != AIR_MOD(bi - ai, 3)); } /* ** this is a weird dual-personality function that is the inner ** loop of both vertex winding fixing, and the learning stage of ** vertex splitting ** ** for flipping (!splitting) ** assumes that triIdx was just popped from "okay" stack ** (triIdx has just been fixed to have correct winding) ** then goes through the not-yet-done neighbors of triIdx, ** flipping them if needed, and ** then adding those neighbors to the stack. ** returns the number of tris added to stack ** ** NOTE: the "flipping" is done within the nVertWithTri representation, ** but *not* in the limnPolyData itself. */ static unsigned int neighborsCheckPush(Nrrd *nTriWithVert, Nrrd *nVertWithTri, unsigned char *triDone, airArray *okayArr, unsigned int *intxBuff, airArray *splitArr, unsigned int triIdx, int splitting) { /* static const char me[]="neighborsCheckPush"; */ unsigned int neighGot[3], neighInfo[3][3], ii, *okay, okayIdx, *vertWithTri, pushedNum; vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data); flipNeighborsGet(nTriWithVert, nVertWithTri, neighGot, neighInfo, intxBuff, triIdx); /* for (ii=0; ii<3; ii++) { fprintf(stderr, "!%s: %u neigh[%u]: ", me, triIdx, ii); if (neighGot[ii]) { fprintf(stderr, "%u (%u %u) (done %u)\n", neighInfo[ii][0], neighInfo[ii][1], neighInfo[ii][2], triDone[neighInfo[ii][0]]); } else { fprintf(stderr, "nope\n"); } } */ pushedNum = 0; for (ii=0; ii<3; ii++) { /* WARNING: complicated logic WRT triDone, splitting, and need */ if (neighGot[ii]) { unsigned int tmp, *idxLine, need; if (!splitting) { if (!triDone[neighInfo[ii][0]]) { /* we only take time to learn need if as yet undone */ need = flipNeed(nVertWithTri, neighInfo[ii][0], neighInfo[ii][1], neighInfo[ii][2]); if (need) { idxLine = vertWithTri + 3*neighInfo[ii][0]; /* here is the vertex winding flip */ ELL_SWAP2(idxLine[0], idxLine[1], tmp); } } } else { /* we're here for splitting */ /* we have to learn need regardless of done-ness */ need = flipNeed(nVertWithTri, neighInfo[ii][0], neighInfo[ii][1], neighInfo[ii][2]); if (need && triDone[neighInfo[ii][0]]) { /* we "need" to flip and yet we've already visited that triangle ==> edge between triIdx and neighInfo[ii][0] needs splitting. See if its a new split, and add it if so */ unsigned int *split, splitIdx, splitNum, vert0, vert1; vert0 = AIR_MIN(neighInfo[ii][1], neighInfo[ii][2]); vert1 = AIR_MAX(neighInfo[ii][1], neighInfo[ii][2]); splitNum = splitArr->len; split = AIR_CAST(unsigned int*, splitArr->data); for (splitIdx=0; splitIdxlen); */ splitIdx = airArrayLenIncr(splitArr, 1); split = AIR_CAST(unsigned int*, splitArr->data); split[0 + 5*splitIdx] = triIdx; split[1 + 5*splitIdx] = neighInfo[ii][0]; split[2 + 5*splitIdx] = vert0; split[3 + 5*splitIdx] = vert1; split[4 + 5*splitIdx] = AIR_FALSE; } } } /* regardless of splitting, we push onto the okay stack all the un-done neighbors that we just processed */ if (!triDone[neighInfo[ii][0]]) { triDone[neighInfo[ii][0]] = AIR_TRUE; okayIdx = airArrayLenIncr(okayArr, 1); okay = AIR_CAST(unsigned int*, okayArr->data); okay[okayIdx] = neighInfo[ii][0]; ++pushedNum; } } /* if (neighGot[ii]) */ } /* for ii */ return pushedNum; } /* ** ONLY GOOD FOR limnPrimitiveTriangles!! */ static unsigned int maxTrianglePerPrimitive(limnPolyData *pld) { unsigned int ret, primIdx; ret = 0; for (primIdx=0; primIdxprimNum; primIdx++) { ret = AIR_MAX(ret, pld->icnt[primIdx]/3); } return ret; } /* ** fills nTriWithVert with 2D array about which triangles use which vertices */ static int triangleWithVertex(Nrrd *nTriWithVert, limnPolyData *pld) { static const char me[]="triangleWithVertex"; unsigned int *triWithVertNum, /* vert ii has triWithVertNum[ii] tris */ *triWithVert, baseVertIdx, primIdx, vertIdx, maxTriPerVert, totTriIdx; airArray *mop; if (!(nTriWithVert && pld)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) { biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me, airEnumStr(limnPrimitive, limnPrimitiveTriangles)); return 1; } triWithVertNum = AIR_CAST(unsigned int*, calloc(pld->xyzwNum, sizeof(unsigned int))); if (!triWithVertNum) { biffAddf(LIMN, "%s: couldn't allocate temp array", me); return 1; } mop = airMopNew(); airMopAdd(mop, triWithVertNum, airFree, airMopAlways); /* fill in triWithVertNum */ baseVertIdx = 0; for (primIdx=0; primIdxprimNum; primIdx++) { unsigned int triNum, triIdx, *indxLine, ii; triNum = pld->icnt[primIdx]/3; for (triIdx=0; triIdxindx + baseVertIdx + 3*triIdx; for (ii=0; ii<3; ii++) { triWithVertNum[indxLine[ii]]++; } } baseVertIdx += pld->icnt[primIdx]; } /* find max # tris per vert, allocate output */ maxTriPerVert = 0; for (vertIdx=0; vertIdxxyzwNum; vertIdx++) { maxTriPerVert = AIR_MAX(maxTriPerVert, triWithVertNum[vertIdx]); } if (nrrdMaybeAlloc_va(nTriWithVert, nrrdTypeUInt, 2, AIR_CAST(size_t, 1 + maxTriPerVert), AIR_CAST(size_t, pld->xyzwNum))) { biffMovef(LIMN, NRRD, "%s: couldn't allocate output", me); airMopError(mop); return 1; } triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data); baseVertIdx = 0; totTriIdx = 0; for (primIdx=0; primIdxprimNum; primIdx++) { unsigned int triNum, *indxLine, *twvLine, ii, triIdx; triNum = pld->icnt[primIdx]/3; for (triIdx=0; triIdxindx + baseVertIdx + 3*triIdx; for (ii=0; ii<3; ii++) { twvLine = triWithVert + (1+maxTriPerVert)*indxLine[ii]; twvLine[1+twvLine[0]] = totTriIdx; twvLine[0]++; } ++totTriIdx; } baseVertIdx += pld->icnt[primIdx]; } airMopOkay(mop); return 0; } /* ** learns which (three vertices) are with which triangle */ static int vertexWithTriangle(Nrrd *nVertWithTri, limnPolyData *pld) { static const char me[]="vertexWithTriangle"; unsigned int baseVertIdx, primIdx, *vertWithTri, triNum; if (!(nVertWithTri && pld)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) { biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me, airEnumStr(limnPrimitive, limnPrimitiveTriangles)); return 1; } triNum = limnPolyDataPolygonNumber(pld); if (nrrdMaybeAlloc_va(nVertWithTri, nrrdTypeUInt, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, triNum))) { biffMovef(LIMN, NRRD, "%s: couldn't allocate output", me); return 1; } vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data); baseVertIdx = 0; for (primIdx=0; primIdxprimNum; primIdx++) { unsigned int triIdx, *indxLine, totTriIdx, ii; triNum = pld->icnt[primIdx]/3; for (triIdx=0; triIdxindx + baseVertIdx + 3*triIdx; for (ii=0; ii<3; ii++) { (vertWithTri + 3*totTriIdx)[ii] = indxLine[ii]; } } baseVertIdx += pld->icnt[primIdx]; } return 0; } static int splitListExtract(unsigned int *listLenP, airArray *edgeArr, unsigned char *hitCount, unsigned int firstVertIdx, unsigned int edgeDoneNum) { static const char me[]="splitListExtract"; unsigned int *edgeData, edgeNum, *edgeLine, edgeIdx, edgeTmp[5], tmp, nextVertIdx, listLen; edgeNum = edgeArr->len; edgeData = AIR_CAST(unsigned int*, edgeArr->data); edgeNum -= edgeDoneNum; edgeData += 5*edgeDoneNum; /* put first edge in first position */ for (edgeIdx=0; edgeIdx %u (tris %u %u)\n", me, firstVertIdx, nextVertIdx, edgeData[0], edgeData[1]); */ /* the search start progresses so that we don't see the same edge twice */ #define SEARCH \ for (edgeIdx=listLen; edgeIdx %u (tris %u %u)\n", me, listLen, nextVertIdx, (edgeData + 5*listLen)[3], (edgeData + 5*listLen)[0], (edgeData + 5*listLen)[1]); */ nextVertIdx = (edgeData + 5*listLen)[3]; hitCount[nextVertIdx]--; listLen++; SEARCH; } /* fprintf(stderr, "!%s: finishing with Len %u, ended at %u\n", me, listLen, nextVertIdx); */ *listLenP = listLen; return 0; #undef SEARCH } /* ** returns the element of vert[] that is not v0 or v1 */ static unsigned int sweepVertNext(unsigned int *vert, unsigned int v0, unsigned int v1) { unsigned int v2; v2 = vert[0]; if (v2 == v0 || v2 == v1) { v2 = vert[1]; } if (v2 == v0 || v2 == v1) { v2 = vert[2]; } return v2; } /* ** returns non-zero iff A and B are in {v[0],v[1],v[2]} */ static int sweepHave2(unsigned int v[3], unsigned int A, unsigned B) { int haveA, haveB; haveA = (A == v[0] || A == v[1] || A == v[2]); haveB = (B == v[0] || B == v[1] || B == v[2]); return (haveA && haveB); } /* ** returns UINT_MAX if there is no other triangle */ static unsigned int sweepTriNext(unsigned int *triLine, unsigned int v0, unsigned int v1, unsigned int triNot, Nrrd *nVertWithTri) { unsigned int triIdx, ret, *vertLine, *vertWithTri; vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data); for (triIdx=0; triIdxaxis[0].size-1); triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data); vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data); /* fprintf(stderr, "!%s: hi, triStart %u, pivot %u, start %u, " "stop = %u, %u\n", me, triStart, vertPivot, vertStart, triStop0, triStop1); */ if (triStart == triStop0 || triStart == triStop1) { /* nowhere to go */ return 0; } triLine = triWithVert + (1+maxTriPerVert)*vertPivot; vertLast = vertStart; triCurr = triStart; sweepLen = 0; do { if (!(triCurr == triStart)) { sweep[sweepLen++] = triCurr; /* fprintf(stderr, "!%s: saving sweep[%u] = %u\n", me, sweepLen-1, triCurr); */ } vertLine = vertWithTri + 3*triCurr; vertNext = sweepVertNext(vertLine, vertPivot, vertLast); /* fprintf(stderr, "!%s: vertNext(%u,%u) = %u\n", me, vertPivot, vertLast, vertNext); */ triCurr = sweepTriNext(triLine, vertPivot, vertNext, triCurr, nVertWithTri); /* fprintf(stderr, "!%s: triNext(%u,%u) = %u\n", me, vertPivot, vertNext, triCurr); */ vertLast = vertNext; } while (!( UINT_MAX == triCurr || triStart == triCurr || triStop0 == triCurr || triStop1 == triCurr )); if (!( UINT_MAX == triCurr )) { sweep[sweepLen++] = triCurr; /* fprintf(stderr, "!%s: saving sweep[%u] = %u\n", me, sweepLen-1, triCurr); */ } return sweepLen; } /* ** track0: first triangle track, length *track0LenP ** track1: first triangle track, length *track1LenP ** sweep: buffer for sweep ** ** NOTE: triangles may be internally repeated in a track ** ** when vert path a loop on a non-orientable surface (e.g. mobius strip), ** then track0 will NOT include the endpoint triangles ** (or its not supposed to), and track1 will include them. */ static int splitTriTrack(unsigned int *track0, unsigned int *track0LenP, unsigned int *track1, unsigned int *track1LenP, unsigned int *sweep, Nrrd *nTriWithVert, Nrrd *nVertWithTri, airArray *edgeArr, unsigned startIdx, unsigned int listLen, int looping) { static const char me[]="splitTriTrack"; unsigned int len0, len1, *edgeData, *edgeLine, edgeIdx, triIdx, /* maxTriPerVert, *triWithVert, *vertWithTri, */ sweepLen, loopEnd0, loopEnd1, loopStart0, loopStart1; int doBack0, doBack1; len0 = len1 = 0; edgeData = AIR_CAST(unsigned int*, edgeArr->data); edgeData += 5*startIdx; /* maxTriPerVert = AIR_CAST(unsigned int, nTriWithVert->axis[0].size-1); */ /* triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data); */ /* vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data); */ if (looping) { loopStart0 = (edgeData)[0]; loopStart1 = (edgeData)[1]; loopEnd0 = (edgeData + 5*(listLen - 1))[0]; loopEnd1 = (edgeData + 5*(listLen - 1))[1]; /* fprintf(stderr, "!%s: loop start = %u, %u, end = %u,%u\n", me, loopStart0, loopStart1, loopEnd0, loopEnd1); */ } else { loopStart0 = loopStart1 = UINT_MAX; loopEnd0 = loopEnd1 = UINT_MAX; } /* ,,,,,,,,,,,,,,,,,,,,, fprintf(stderr, "!%s: 1st 2 tris %u %u, verts %u %u\n", me, edgeData[0], edgeData[1], edgeData[2], edgeData[3]); fprintf(stderr, "!%s: triangles at start vert %u:\n", me, edgeData[2]); triLine = triWithVert + edgeData[2]*(1+maxTriPerVert); for (triIdx=0; triIdx%u, tris %u, %u\n", me, edgeIdx, edgeLine[2], edgeLine[3], edgeLine[0], edgeLine[1]); fprintf(stderr, "!%s: triangles at next vert %u:\n", me, edgeLine[3]); triLine = triWithVert + edgeLine[3]*(1+maxTriPerVert); for (triIdx=0; triIdx 0) { /* don't include either stop triangle on track 0 */ for (triIdx=0; triIdx0, 1->1\n", me); */ doBack0 = doBack1 = AIR_FALSE; } else if (track0[len0-1] == nextLine[1] && track1[len1-1] == nextLine[0]) { /* fprintf(stderr, "!%s: tracking went 0->1, 0->1\n", me); */ ELL_SWAP2(nextLine[0], nextLine[1], tmp); doBack0 = doBack1 = AIR_FALSE; } else if (track0[len0-1] == nextLine[0]) { /* fprintf(stderr, "!%s: tracking went 0->0, 1->x\n", me); */ doBack0 = AIR_FALSE; doBack1 = AIR_TRUE; } else if (track1[len1-1] == nextLine[1]) { /* fprintf(stderr, "!%s: tracking went 0->x, 1->1\n", me); */ doBack0 = AIR_TRUE; doBack1 = AIR_FALSE; } else if (track0[len0-1] == nextLine[1]) { /* fprintf(stderr, "!%s: tracking went 0->1, 1->x\n", me); */ ELL_SWAP2(nextLine[0], nextLine[1], tmp); doBack0 = AIR_FALSE; doBack1 = AIR_TRUE; } else if (track1[len1-1] == nextLine[0]) { /* fprintf(stderr, "!%s: tracking went 0->x, 1->0\n", me); */ ELL_SWAP2(nextLine[0], nextLine[1], tmp); doBack0 = AIR_TRUE; doBack1 = AIR_FALSE; } else { biffAddf(LIMN, "%s: edge %u/%u, sweep ends %u,%u != want %u,%u", me, edgeIdx, listLen, track0[len0-1], track1[len1-1], nextLine[0], nextLine[1]); return 1; } } else { doBack0 = doBack1 = AIR_FALSE; } } if (looping) { /* the end of track0 shouldn't include the stop */ len0--; } *track0LenP = len0; *track1LenP = len1; return 0; } static int splitVertDup(limnPolyData *pld, airArray *edgeArr, unsigned int edgeDoneNum, unsigned int listLen, unsigned int *track, unsigned int trackLen, int looping) { static const char me[]="splitVertDup"; unsigned int *vixLut, ii, vixLutLen, oldVertNum, newVertNum, *edgeData, bitflag, trackIdx, vert0, vert1; airArray *mop; limnPolyData pldTmp; mop = airMopNew(); edgeData = AIR_CAST(unsigned int*, edgeArr->data); edgeData += 5*edgeDoneNum; oldVertNum = pld->xyzwNum; vixLutLen = looping ? listLen : listLen+1; newVertNum = oldVertNum + vixLutLen; /* quiet compiler warnings */ pldTmp.rgba = NULL; pldTmp.norm = NULL; pldTmp.tex2 = NULL; pldTmp.tang = NULL; if (looping) { vert0 = edgeData[2]; /* don't use dupe of this on first triangle */ vert1 = edgeData[3]; /* don't use dupe of this on last triangle */ } else { vert0 = vert1 = UINT_MAX; } /* HEY: sneakily preserve the old per-vertex arrays; we own them now */ pldTmp.xyzw = pld->xyzw; airMopAdd(mop, pldTmp.xyzw, airFree, airMopAlways); pld->xyzw = NULL; pld->xyzwNum = 0; bitflag = limnPolyDataInfoBitFlag(pld); if ((1 << limnPolyDataInfoRGBA) & bitflag) { pldTmp.rgba = pld->rgba; airMopAdd(mop, pldTmp.rgba, airFree, airMopAlways); pld->rgba = NULL; pld->rgbaNum = 0; } if ((1 << limnPolyDataInfoNorm) & bitflag) { pldTmp.norm = pld->norm; airMopAdd(mop, pldTmp.norm, airFree, airMopAlways); pld->norm = NULL; pld->normNum = 0; } if ((1 << limnPolyDataInfoTex2) & bitflag) { pldTmp.tex2 = pld->tex2; airMopAdd(mop, pldTmp.tex2, airFree, airMopAlways); pld->tex2 = NULL; pld->tex2Num = 0; } if ((1 << limnPolyDataInfoTang) & bitflag) { pldTmp.tang = pld->tang; airMopAdd(mop, pldTmp.tang, airFree, airMopAlways); pld->tang = NULL; pld->tangNum = 0; } if (limnPolyDataAlloc(pld, bitflag, newVertNum, pld->indxNum, pld->primNum)) { biffAddf(LIMN, "%s: couldn't allocate new vert # %u", me, newVertNum); airMopError(mop); return 1; } /* copy old data */ memcpy(pld->xyzw, pldTmp.xyzw, oldVertNum*4*sizeof(float)); if ((1 << limnPolyDataInfoRGBA) & bitflag) { memcpy(pld->rgba, pldTmp.rgba, oldVertNum*4*sizeof(unsigned char)); } if ((1 << limnPolyDataInfoNorm) & bitflag) { memcpy(pld->norm, pldTmp.norm, oldVertNum*3*sizeof(float)); } if ((1 << limnPolyDataInfoTex2) & bitflag) { memcpy(pld->tex2, pldTmp.tex2, oldVertNum*2*sizeof(float)); } if ((1 << limnPolyDataInfoTang) & bitflag) { memcpy(pld->tang, pldTmp.tang, oldVertNum*3*sizeof(float)); } vixLut = AIR_CAST(unsigned int *, calloc(2*vixLutLen, sizeof(unsigned int))); airMopAdd(mop, vixLut, airFree, airMopAlways); if (looping) { for (ii=0; iixyzw + 4*vixLut[1 + 2*ii], pld->xyzw + 4*vixLut[0 + 2*ii]); if ((1 << limnPolyDataInfoRGBA) & bitflag) { ELL_4V_COPY(pld->rgba + 4*vixLut[1 + 2*ii], pld->rgba + 4*vixLut[0 + 2*ii]); } if ((1 << limnPolyDataInfoNorm) & bitflag) { ELL_3V_COPY(pld->norm + 3*vixLut[1 + 2*ii], pld->norm + 3*vixLut[0 + 2*ii]); } if ((1 << limnPolyDataInfoTex2) & bitflag) { ELL_2V_COPY(pld->tex2 + 2*vixLut[1 + 2*ii], pld->tex2 + 2*vixLut[0 + 2*ii]); } if ((1 << limnPolyDataInfoTang) & bitflag) { ELL_3V_COPY(pld->tang + 3*vixLut[1 + 2*ii], pld->tang + 3*vixLut[0 + 2*ii]); } } /* for triangles in track, update indices of duped vertices */ /* we do this updating ONLY in the limnPolyData, and that's okay: the split information is computed entirely from nVertWithTri and nTriWithVert (which were based on the original polydata), but not the current polydata */ /* HEY: this is one place where we really exploit the fact that we only have triangles: it makes the indxLine computation much much easier */ for (trackIdx=0; trackIdxindx + 3*track[trackIdx]; for (ii=0; iilen) { /* actually, no splitting was required! */ return 0; } mop = airMopNew(); /* NOTE: It is necessary to save out the number of (initial) number of vertices here, because as we do the splitting (which is done once per track, as tracks are computed), pld->xyzwNum will increase ... */ vertNum = pld->xyzwNum; hitCount = AIR_CAST(unsigned char *, calloc(vertNum, sizeof(unsigned char))); maxTriPerVert = AIR_CAST(unsigned int, nTriWithVert->axis[0].size - 1); track0 = AIR_CAST(unsigned int *, calloc(maxTriPerVert*edgeArr->len, sizeof(unsigned int))); track1 = AIR_CAST(unsigned int *, calloc(maxTriPerVert*edgeArr->len, sizeof(unsigned int))); sweep = AIR_CAST(unsigned int *, calloc(maxTriPerVert, sizeof(unsigned int))); if (!(hitCount && track0 && track1 && sweep)) { biffAddf(LIMN, "%s: couldn't alloc buffers", me); airMopError(mop); return 1; } airMopAdd(mop, hitCount, airFree, airMopAlways); airMopAdd(mop, track0, airFree, airMopAlways); airMopAdd(mop, track1, airFree, airMopAlways); airMopAdd(mop, sweep, airFree, airMopAlways); edgeData = AIR_CAST(unsigned int*, edgeArr->data); /* initialize hitCount */ for (edgeIdx=0; edgeIdxlen; edgeIdx++) { unsigned int ha, hb; edgeLine = edgeData + 5*edgeIdx; ha = hitCount[edgeLine[2]]++; hb = hitCount[edgeLine[3]]++; if (ha > 2 || hb > 2) { biffAddf(LIMN, "%s: edge %u (vert %u %u) created hit counts %u %u", me, edgeIdx, edgeLine[2], edgeLine[3], ha, hb); airMopError(mop); return 1; } } /* scan hitCount */ #define SEARCH(x) \ for (vertIdx=0; vertIdx non-loop tracks pass 1: look for hitCount[2] ==> loop tracks */ for (passIdx=0; passIdx<2; passIdx++) { if (0 == passIdx) { SEARCH(1); } else { SEARCH(2); } while (vertIdx < vertNum) { unsigned int E; E = 0; if (1) { unsigned int hitIdx, hitSum; hitSum = 0; for (hitIdx=0; hitIdxlen)); return 1; } edgeDoneNum += listLen; /* fprintf(stderr, "!%s: edgeDoneNum now %u (%u)\n", me, edgeDoneNum, AIR_CAST(unsigned int, edgeArr->len)); */ if (0 == passIdx) { SEARCH(1); } else { SEARCH(2); } } } #undef SEARCH airMopOkay(mop); return 0; } int _limnPolyDataVertexWindingProcess(limnPolyData *pld, int splitting) { static const char me[]="limnPolyDataVertexWindingProcess"; unsigned int primIdx, /* for indexing through primitives */ triIdx, /* for indexing through triangles in each primitive */ maxTriPerPrim, /* max # triangles per primitive, which is essential for the indexing of each triangle (in each primitive) into a single triangle index */ totTriIdx, /* another triangle index */ totTriNum, /* total # triangles */ trueTriNum, /* correct total # triangles in all primitives */ baseVertIdx, /* first vertex for current primitive */ maxTriPerVert, /* max # of tris on single vertex */ /* *triWithVert, 2D array ((1+maxTriPerVert) x pld->xyzwNum) of per-vertex triangles */ *vertWithTri, /* 3D array (3 x maxTriPerPrim x pld->primNum) of per-tri vertices (vertex indices), which is just a repackaging of the information in the lpld */ doneTriNum, /* # triangles finished so far */ *intxBuff, /* stupid buffer */ *okay, /* the stack of triangles with okay (possibly fixed) winding, but with some neighbors that may as yet need fixing */ *split; /* stack of 5-tuples about edges needing vertex splits: split[0, 1]: two neighboring triangles, split[2, 3]: their shared vertices split[4]: non-zero if this split has been processed */ unsigned char *triDone; /* 1D array (len totTriNum) record of done-ness */ Nrrd *nTriWithVert, *nVertWithTri; airArray *mop, /* house-keeping */ *okayArr, /* airArray around "okay" */ *splitArr; /* airArray around "split" */ airPtrPtrUnion appu; /* fprintf(stderr, "!%s: hi\n", me); */ if (!pld) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (!(pld->xyzwNum && pld->primNum)) { /* this is empty? */ return 0; } if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) { biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me, airEnumStr(limnPrimitive, limnPrimitiveTriangles)); return 1; } maxTriPerPrim = maxTrianglePerPrimitive(pld); totTriNum = limnPolyDataPolygonNumber(pld); mop = airMopNew(); triDone = AIR_CAST(unsigned char *, calloc(totTriNum, sizeof(unsigned char))); airMopAdd(mop, triDone, airFree, airMopAlways); if (!triDone) { biffAddf(LIMN, "%s: couldn't allocate temp array", me); airMopError(mop); return 1; } /* allocate TriWithVert, VertWithTri, intxBuff */ nTriWithVert = nrrdNew(); airMopAdd(mop, nTriWithVert, (airMopper)nrrdNuke, airMopAlways); nVertWithTri = nrrdNew(); airMopAdd(mop, nVertWithTri, (airMopper)nrrdNuke, airMopAlways); if (triangleWithVertex(nTriWithVert, pld) || vertexWithTriangle(nVertWithTri, pld)) { biffAddf(LIMN, "%s: couldn't set nTriWithVert or nVertWithTri", me); airMopError(mop); return 1; } vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data); /* triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data); */ maxTriPerVert = nTriWithVert->axis[0].size - 1; intxBuff = AIR_CAST(unsigned int*, calloc(maxTriPerVert, sizeof(unsigned int))); if (!intxBuff) { biffAddf(LIMN, "%s: failed to alloc an itty bitty buffer", me); airMopError(mop); return 1; } airMopAdd(mop, intxBuff, airFree, airMopAlways); /* nrrdSave("triWithVert.nrrd", nTriWithVert, NULL); nrrdSave("vertWithTri.nrrd", nVertWithTri, NULL); */ /* create the stack of recently fixed triangles */ appu.ui = &okay; okayArr = airArrayNew(appu.v, NULL, sizeof(unsigned int), maxTriPerPrim); airMopAdd(mop, okayArr, (airMopper)airArrayNuke, airMopAlways); if (splitting) { appu.ui = &split; splitArr = airArrayNew(appu.v, NULL, 5*sizeof(unsigned int), maxTriPerPrim); /* split set as it is used */ } else { splitArr = NULL; split = NULL; } /* the skinny */ doneTriNum = 0; trueTriNum = 0; for (primIdx=0; primIdxprimNum; primIdx++) { trueTriNum += pld->icnt[primIdx]/3; } /* fprintf(stderr, "!%s: trueTriNum %u; other tri num %u\n", me, trueTriNum, limnPolyDataPolygonNumber(pld)); */ while (doneTriNum < trueTriNum) { /* find first undone triangle, which should be on a different connected component than any processed so far */ for (totTriIdx=0; triDone[totTriIdx]; totTriIdx++) ; /* we use the winding of this triangle to determine the correct winding of all neighboring trianges, so this one is now done */ triDone[totTriIdx] = AIR_TRUE; ++doneTriNum; /* fprintf(stderr, "!%s: considering tri %u done (%u)\n", me, totTriIdx, doneTriNum); */ doneTriNum += neighborsCheckPush(nTriWithVert, nVertWithTri, triDone, okayArr, intxBuff, splitArr, totTriIdx, splitting); while (okayArr->len) { unsigned int popped; popped = okay[okayArr->len-1]; airArrayLenIncr(okayArr, -1); /* fprintf(stderr, "!%s: popped %u\n", me, popped); */ doneTriNum += neighborsCheckPush(nTriWithVert, nVertWithTri, triDone, okayArr, intxBuff, splitArr, popped, splitting); } } if (splitting) { if (doSplitting(pld, nTriWithVert, nVertWithTri, splitArr)) { biffAddf(LIMN, "%s: problem doing vertex splitting", me); return 1; } } else { /* Copy from nVertWithTri back into polydata */ baseVertIdx = 0; for (primIdx=0; primIdxprimNum; primIdx++) { unsigned int triNum, *indxLine, ii; triNum = pld->icnt[primIdx]/3; for (triIdx=0; triIdxindx + baseVertIdx + 3*triIdx; for (ii=0; ii<3; ii++) { indxLine[ii] = (vertWithTri + 3*totTriIdx)[ii]; } } baseVertIdx += pld->icnt[primIdx]; } } airMopOkay(mop); return 0; } /* ** with non-zero splitting, this does vertex splitting so that ** non-orientable surfaces can be rendered without seams. Took longer ** to implement than intended. ** ** HEY: still has a bug in handling which triangles get which ** (new) vertices when the seam in the non-orientable surface ** is a closed loop. Can be debugged later... */ int limnPolyDataVertexWindingFix(limnPolyData *pld, int splitting) { static const char me[]="limnPolyDataVertexWindingFix"; if (!splitting) { if (_limnPolyDataVertexWindingProcess(pld, AIR_FALSE)) { biffAddf(LIMN, "%s: trouble", me); return 1; } } else { if (_limnPolyDataVertexWindingProcess(pld, AIR_FALSE) || _limnPolyDataVertexWindingProcess(pld, AIR_TRUE)) { biffAddf(LIMN, "%s: trouble", me); return 1; } } return 0; } int limnPolyDataCCFind(limnPolyData *pld) { static const char me[]="limnPolyDataCCFind"; unsigned int realTriNum, *triMap, *triWithVert, vertIdx, *indxOld, *indxNew, primNumOld, *icntOld, *icntNew, *baseIndx, primIdxNew, primNumNew, passIdx, eqvNum=0; unsigned char *typeOld, *typeNew; Nrrd *nTriWithVert, *nccSize, *nTriMap; airArray *mop, *eqvArr; if (!pld) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (!(pld->xyzwNum && pld->primNum)) { /* this is empty? */ return 0; } if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) { biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me, airEnumStr(limnPrimitive, limnPrimitiveTriangles)); return 1; } mop = airMopNew(); realTriNum = limnPolyDataPolygonNumber(pld); eqvArr = airArrayNew(NULL, NULL, 2*sizeof(unsigned int), /* this is only a heuristic */ pld->xyzwNum); airMopAdd(mop, eqvArr, (airMopper)airArrayNuke, airMopAlways); nTriWithVert = nrrdNew(); airMopAdd(mop, nTriWithVert, (airMopper)nrrdNuke, airMopAlways); if (triangleWithVertex(nTriWithVert, pld)) { biffAddf(LIMN, "%s: couldn't set nTriWithVert", me); airMopError(mop); return 1; } /* simple profiling showed that stupid amount of time was spent adding the equivalences. So we go in two passes- first two see how many equivalences are needed, and then actually adding them */ /* yea, so, its like you don't really even need an airArray ... */ triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data); for (passIdx=0; passIdx<2; passIdx++) { if (0 == passIdx) { eqvNum = 0; } else { airArrayLenPreSet(eqvArr, eqvNum); } for (vertIdx=0; vertIdxaxis[1].size; vertIdx++) { unsigned int *triLine, triIdx; triLine = triWithVert + vertIdx*(nTriWithVert->axis[0].size); for (triIdx=1; triIdxdata); primNumNew = airEqvMap(eqvArr, triMap, realTriNum); if (nrrdHisto(nccSize, nTriMap, NULL, NULL, primNumNew, nrrdTypeUInt)) { biffMovef(LIMN, NRRD, "%s: couldn't histogram CC map", me); airMopError(mop); return 1; } /* indxNumOld == indxNumNew */ indxOld = pld->indx; primNumOld = pld->primNum; if (1 != primNumOld) { biffAddf(LIMN, "%s: sorry! stupid implementation can't " "do primNum %u (only 1)", me, primNumOld); airMopError(mop); return 1; } typeOld = pld->type; icntOld = pld->icnt; indxNew = AIR_CAST(unsigned int*, calloc(pld->indxNum, sizeof(unsigned int))); typeNew = AIR_CAST(unsigned char*, calloc(primNumNew, sizeof(unsigned char))); icntNew = AIR_CAST(unsigned int*, calloc(primNumNew, sizeof(unsigned int))); if (!(indxNew && typeNew && icntNew)) { biffAddf(LIMN, "%s: couldn't allocate new polydata arrays", me); airMopError(mop); return 1; } pld->indx = indxNew; pld->primNum = primNumNew; pld->type = typeNew; pld->icnt = icntNew; airMopAdd(mop, indxOld, airFree, airMopAlways); airMopAdd(mop, typeOld, airFree, airMopAlways); airMopAdd(mop, icntOld, airFree, airMopAlways); /* this multi-pass thing is really stupid (and assumes stupid primNumOld = 1) */ baseIndx = pld->indx; for (primIdxNew=0; primIdxNewprimNum; primIdxNew++) { unsigned int realTriIdx; pld->type[primIdxNew] = limnPrimitiveTriangles; pld->icnt[primIdxNew] = 0; for (realTriIdx=0; realTriIdxicnt[primIdxNew] += 3; } } } airMopOkay(mop); return 0; } int limnPolyDataPrimitiveSort(limnPolyData *pld, const Nrrd *_nval) { static const char me[]="limnPolyDataPrimitiveSort"; Nrrd *nval, *nrec; const Nrrd *ntwo[2]; airArray *mop; double *rec; unsigned int primIdx, **startIndx, *indxNew, *baseIndx, *icntNew; unsigned char *typeNew; int E; if (!(pld && _nval)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (!(1 == _nval->dim && nrrdTypeBlock != _nval->type && _nval->axis[0].size == pld->primNum)) { biffAddf(LIMN, "%s: need 1-D %u-len scalar nrrd " "(not %u-D type %s, axis[0].size %u)", me, pld->primNum, _nval->dim, airEnumStr(nrrdType, _nval->type), AIR_CAST(unsigned int, _nval->axis[0].size)); return 1; } mop = airMopNew(); nval = nrrdNew(); airMopAdd(mop, nval, (airMopper)nrrdNuke, airMopAlways); nrec = nrrdNew(); airMopAdd(mop, nrec, (airMopper)nrrdNuke, airMopAlways); E = 0; if (!E) E |= nrrdConvert(nval, _nval, nrrdTypeDouble); ntwo[0] = nval; ntwo[1] = nval; if (!E) E |= nrrdJoin(nrec, ntwo, 2, 0, AIR_TRUE); if (E) { biffMovef(LIMN, NRRD, "%s: problem creating records", me); airMopError(mop); return 1; } rec = AIR_CAST(double *, nrec->data); for (primIdx=0; primIdxprimNum; primIdx++) { rec[1 + 2*primIdx] = primIdx; } qsort(rec, pld->primNum, 2*sizeof(double), nrrdValCompareInv[nrrdTypeDouble]); startIndx = AIR_CAST(unsigned int**, calloc(pld->primNum, sizeof(unsigned int*))); indxNew = AIR_CAST(unsigned int*, calloc(pld->indxNum, sizeof(unsigned int))); icntNew = AIR_CAST(unsigned int*, calloc(pld->primNum, sizeof(unsigned int))); typeNew = AIR_CAST(unsigned char*, calloc(pld->primNum, sizeof(unsigned char))); if (!(startIndx && indxNew && icntNew && typeNew)) { biffAddf(LIMN, "%s: couldn't allocated temp buffers", me); airMopError(mop); return 1; } airMopAdd(mop, startIndx, airFree, airMopAlways); baseIndx = pld->indx; for (primIdx=0; primIdxprimNum; primIdx++) { startIndx[primIdx] = baseIndx; baseIndx += pld->icnt[primIdx]; } baseIndx = indxNew; for (primIdx=0; primIdxprimNum; primIdx++) { unsigned int sortIdx; sortIdx = AIR_CAST(unsigned int, rec[1 + 2*primIdx]); memcpy(baseIndx, startIndx[sortIdx], pld->icnt[sortIdx]*sizeof(unsigned int)); icntNew[primIdx] = pld->icnt[sortIdx]; typeNew[primIdx] = pld->type[sortIdx]; baseIndx += pld->icnt[sortIdx]; } airFree(pld->indx); pld->indx = indxNew; airFree(pld->type); pld->type = typeNew; airFree(pld->icnt); pld->icnt = icntNew; airMopOkay(mop); return 0; } int limnPolyDataVertexWindingFlip(limnPolyData *pld) { static const char me[]="limnPolyDataVertexWindingFlip"; unsigned int baseVertIdx, primIdx; if (!pld) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) { biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me, airEnumStr(limnPrimitive, limnPrimitiveTriangles)); return 1; } baseVertIdx = 0; for (primIdx=0; primIdxprimNum; primIdx++) { unsigned int triNum, triIdx, *indxLine, tmpIdx; triNum = pld->icnt[primIdx]/3; for (triIdx=0; triIdxindx + baseVertIdx + 3*triIdx; ELL_SWAP2(indxLine[0], indxLine[2], tmpIdx); } baseVertIdx += pld->icnt[primIdx]; } return 0; } int limnPolyDataPrimitiveSelect(limnPolyData *pldOut, const limnPolyData *pldIn, const Nrrd *_nmask) { static const char me[]="limnPolyDataPrimitiveSelect"; Nrrd *nmask; double *mask; unsigned int oldBaseVertIdx, oldPrimIdx, oldVertIdx, bitflag, *old2newMap, *new2oldMap, newPrimNum, newBaseVertIdx, newPrimIdx, newIndxNum, newVertIdx, newVertNum; unsigned char *vertUsed; airArray *mop; if (!(pldOut && pldIn && _nmask)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (!(1 == _nmask->dim && nrrdTypeBlock != _nmask->type && _nmask->axis[0].size == pldIn->primNum)) { biffAddf(LIMN, "%s: need 1-D %u-len scalar nrrd " "(not %u-D type %s, axis[0].size %u)", me, pldIn->primNum, _nmask->dim, airEnumStr(nrrdType, _nmask->type), AIR_CAST(unsigned int, _nmask->axis[0].size)); return 1; } mop = airMopNew(); nmask = nrrdNew(); airMopAdd(mop, nmask, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nmask, _nmask, nrrdTypeDouble)) { biffMovef(LIMN, NRRD, "%s: trouble converting mask to %s", me, airEnumStr(nrrdType, nrrdTypeDouble)); return 1; } mask = AIR_CAST(double *, nmask->data); old2newMap = AIR_CAST(unsigned int *, calloc(pldIn->xyzwNum, sizeof(unsigned int))); airMopAdd(mop, old2newMap, airFree, airMopAlways); vertUsed = AIR_CAST(unsigned char *, calloc(pldIn->xyzwNum, sizeof(unsigned char))); airMopAdd(mop, vertUsed, airFree, airMopAlways); /* initialize all verts as unused */ for (oldVertIdx=0; oldVertIdxxyzwNum; oldVertIdx++) { vertUsed[oldVertIdx] = AIR_FALSE; } /* mark the used verts, and count # new indices and primitives */ oldBaseVertIdx = 0; newPrimNum = 0; newIndxNum = 0; for (oldPrimIdx=0; oldPrimIdxprimNum; oldPrimIdx++) { unsigned indxIdx; if (mask[oldPrimIdx]) { for (indxIdx=0; indxIdxicnt[oldPrimIdx]; indxIdx++) { vertUsed[(pldIn->indx + oldBaseVertIdx)[indxIdx]] = AIR_TRUE; } newIndxNum += pldIn->icnt[oldPrimIdx]; newPrimNum++; } oldBaseVertIdx += pldIn->icnt[oldPrimIdx]; } /* count the used verts, and set up map from old to new indices */ newVertNum = 0; for (oldVertIdx=0; oldVertIdxxyzwNum; oldVertIdx++) { if (vertUsed[oldVertIdx]) { old2newMap[oldVertIdx] = newVertNum++; } } /* allocate and fill reverse map */ new2oldMap = AIR_CAST(unsigned int *, calloc(newVertNum, sizeof(unsigned int))); airMopAdd(mop, new2oldMap, airFree, airMopAlways); newVertIdx = 0; for (oldVertIdx=0; oldVertIdxxyzwNum; oldVertIdx++) { if (vertUsed[oldVertIdx]) { new2oldMap[newVertIdx++] = oldVertIdx; } } /* allocate output polydata */ bitflag = limnPolyDataInfoBitFlag(pldIn); if (limnPolyDataAlloc(pldOut, bitflag, newVertNum, newIndxNum, newPrimNum)) { biffAddf(LIMN, "%s: trouble allocating output", me); return 1; } /* transfer per-primitive information from old to new */ oldBaseVertIdx = 0; newBaseVertIdx = 0; newPrimIdx = 0; for (oldPrimIdx=0; oldPrimIdxprimNum; oldPrimIdx++) { if (mask[oldPrimIdx]) { unsigned indxIdx; pldOut->icnt[newPrimIdx] = pldIn->icnt[oldPrimIdx]; pldOut->type[newPrimIdx] = pldIn->type[oldPrimIdx]; for (indxIdx=0; indxIdxicnt[oldPrimIdx]; indxIdx++) { oldVertIdx = (pldIn->indx + oldBaseVertIdx)[indxIdx]; (pldOut->indx + newBaseVertIdx)[indxIdx] = old2newMap[oldVertIdx]; } newBaseVertIdx += pldIn->icnt[oldPrimIdx]; newPrimIdx++; } oldBaseVertIdx += pldIn->icnt[oldPrimIdx]; } /* transfer per-vertex info */ for (newVertIdx=0; newVertIdxxyzw + 4*newVertIdx, pldIn->xyzw + 4*oldVertIdx); if ((1 << limnPolyDataInfoRGBA) & bitflag) { ELL_4V_COPY(pldOut->rgba + 4*newVertIdx, pldIn->rgba + 4*oldVertIdx); } if ((1 << limnPolyDataInfoNorm) & bitflag) { ELL_3V_COPY(pldOut->norm + 3*newVertIdx, pldIn->norm + 3*oldVertIdx); } if ((1 << limnPolyDataInfoTex2) & bitflag) { ELL_3V_COPY(pldOut->tex2 + 2*newVertIdx, pldIn->tex2 + 2*oldVertIdx); } if ((1 << limnPolyDataInfoTang) & bitflag) { ELL_3V_COPY(pldOut->tang + 3*newVertIdx, pldIn->tang + 3*oldVertIdx); } } airMopOkay(mop); return 0; } /* Helper function for limnPolyDataClipMulti - clips the edge between * disc and kept that partially fulfills the thresholds and maintains * a data structure that keeps track of edges we have clipped already, * to avoid creating duplicate vertices. */ static int clipEdge(int disc, int kept, Nrrd *nval, double *thresh, int *newIdx, airArray *llistArr, limnPolyData *pld, unsigned int bitflag, limnPolyData *newpld, airArray *xyzwArr, airArray *rgbaArr, airArray *normArr, airArray *tex2Arr, airArray *tangArr) { int ref=-1, *llist=(int*)llistArr->data; int next=newIdx[disc]; double alpha=0; unsigned int i,q,p,nk; double (*lup)(const void *v, size_t I)=nrrdDLookup[nval->type]; /* check if we clipped the edge previously */ while (next!=-1) { if (llist[next]==kept) /* found the desired vertex */ return llist[next+1]; ref=next+2; next=llist[next+2]; } /* we need to interpolate - find the weight */ nk=(nval->dim==1)?1:nval->axis[0].size; for (i=0; idata, nk*disc+i); double keptval = lup(nval->data, nk*kept+i); double thisalpha = AIR_AFFINE(discval,thresh[i],keptval,0.0,1.0); if (thisalpha<1.0 && thisalpha>alpha) alpha=thisalpha; } /* add interpolated vertex */ q=airArrayLenIncr(xyzwArr, 1); ELL_4V_LERP_TT(newpld->xyzw+4*q, float, alpha, pld->xyzw+4*disc, pld->xyzw+4*kept); if ((1 << limnPolyDataInfoRGBA) & bitflag) { airArrayLenIncr(rgbaArr, 1); ELL_4V_LERP_TT(newpld->rgba+4*q, unsigned char, alpha, pld->rgba+4*disc, pld->rgba+4*kept); } if ((1 << limnPolyDataInfoNorm) & bitflag) { float fnorm[3]; double len; /* take special care to treat non-orientable surface normals correctly */ if (ELL_3V_DOT(pld->norm+3*disc, pld->norm+3*kept)<0) { ELL_3V_SCALE_TT(fnorm, float, -1.0, pld->norm+3*kept); } else { ELL_3V_COPY(fnorm, pld->norm+3*kept); } airArrayLenIncr(normArr, 1); ELL_3V_LERP_TT(newpld->norm+3*q, float, alpha, pld->norm+3*disc, fnorm); /* re-normalize */ len=ELL_3V_LEN(newpld->norm+3*q); if (len>1e-20) { ELL_3V_SCALE_TT(newpld->norm+3*q, float, 1.0/len, newpld->norm+3*q); } } if ((1 << limnPolyDataInfoTex2) & bitflag) { airArrayLenIncr(tex2Arr, 1); ELL_2V_LERP_TT(newpld->tex2+2*q, float, alpha, pld->tex2+2*disc, pld->tex2+2*kept); } if ((1 << limnPolyDataInfoTang) & bitflag) { airArrayLenIncr(tangArr, 1); ELL_3V_LERP_TT(newpld->tang+3*q, float, alpha, pld->tang+3*disc, pld->tang+3*kept); } /* add new vertex to linked list */ p=airArrayLenIncr(llistArr, 1); llist=(int*)llistArr->data; /* update in case of re-allocation */ llist[3*p]=kept; llist[3*p+1]=q; llist[3*p+2]=-1; if (ref==-1) newIdx[disc]=3*p; else llist[ref]=3*p; return q; } /* * Clips the given triangles (limnPrimitiveTriangles) according to the * input matrix nval and the threshold array thresh. First axis of * nval are different clipping criteria, second axis are vertex * indices. The length of thresh has to equal the size of the first * axis, the vertex count in pld has to equal the size of the second * axis. If nval is 1D, it is assumed to have a single criterion. * * A vertex is preserved if all values are >= the respective * threshold; triangles with partially discarded vertices are clipped, * potentially generating a quad that is then triangulated arbitrarily. */ int limnPolyDataClipMulti(limnPolyData *pld, Nrrd *nval, double *thresh) { static const char me[]="limnPolyDataClipMulti"; unsigned char *keepVert=NULL; airArray *mop; unsigned int E, i, idx=0; double (*lup)(const void *v, size_t I); airArray *xyzwArr, *rgbaArr=NULL, *normArr=NULL, *tex2Arr=NULL, *tangArr=NULL, *indxArr, *typeArr, *icntArr, *llistArr=NULL; limnPolyData *newpld=NULL; int *newIdx=NULL, *llist=NULL; unsigned int bitflag, nk, nvert; airPtrPtrUnion appu; if (!(pld && nval)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (nrrdTypeBlock == nval->type) { biffAddf(LIMN, "%s: need scalar type (not %s)", me, airEnumStr(nrrdType, nval->type)); return 1; } if (nval->dim==1) { nk=1; nvert=nval->axis[0].size; } else if (nval->dim==2) { nk=nval->axis[0].size; nvert=nval->axis[1].size; } else { biffAddf(LIMN, "%s: need 1D or 2D input array, got %uD", me, nval->dim); return 1; } if (nvert!=pld->xyzwNum) { biffAddf(LIMN, "%s: # verts %u != # values %u", me, pld->xyzwNum, nvert); return 1; } if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) { biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me, airEnumStr(limnPrimitive, limnPrimitiveTriangles)); return 1; } /* Memory allocation in C IS a headache */ mop=airMopNew(); E = AIR_FALSE; if (!E) { E|=!(keepVert = AIR_CAST(unsigned char *, calloc(pld->xyzwNum, sizeof(char)))); } if (!E) { airMopAdd(mop, keepVert, airFree, airMopAlways); E|=!(newIdx = AIR_CAST(int *, malloc(pld->xyzwNum*sizeof(int)))); } if (!E) { unsigned int incr; airMopAdd(mop, newIdx, airFree, airMopAlways); memset(newIdx, -1, sizeof(int)*pld->xyzwNum); /* This setting of incr is arbitrary and was not optimized in any way: */ incr = pld->xyzwNum/10; /* 10% of previous vertex count... */ if (incr<50) incr=50; /* ...but at least 50. */ appu.i = &llist; E|=!(llistArr=airArrayNew(appu.v, NULL, 3*sizeof(int), incr)); } if (!E) { airMopAdd(mop, llistArr, (airMopper)airArrayNuke, airMopAlways); E|=!(newpld = limnPolyDataNew()); } bitflag = limnPolyDataInfoBitFlag(pld); if (!E) { unsigned int incr; airMopAdd(mop, newpld, airFree, airMopAlways); /* "shallow" free */ incr = pld->xyzwNum/20; /* 5% of previous vertex count... */ if (incr<10) incr=10; /* ...but at least 10. */ appu.f = &(newpld->xyzw); E|=!(xyzwArr=airArrayNew(appu.v, &(newpld->xyzwNum), 4*sizeof(float), incr)); if (!E) { airMopAdd(mop, xyzwArr, (airMopper)airArrayNuke, airMopOnError); airMopAdd(mop, xyzwArr, (airMopper)airArrayNix, airMopOnOkay); } if (!E && (1 << limnPolyDataInfoRGBA) & bitflag) { appu.uc = &(newpld->rgba); E|=!(rgbaArr=airArrayNew(appu.v, &(newpld->rgbaNum), 4*sizeof(unsigned char), incr)); if (!E) { airMopAdd(mop, rgbaArr, (airMopper)airArrayNuke, airMopOnError); airMopAdd(mop, rgbaArr, (airMopper)airArrayNix, airMopOnOkay); } } if (!E && (1 << limnPolyDataInfoNorm) & bitflag) { appu.f = &(newpld->norm); E|=!(normArr=airArrayNew(appu.v, &(newpld->normNum), 3*sizeof(float), incr)); if (!E) { airMopAdd(mop, normArr, (airMopper)airArrayNuke, airMopOnError); airMopAdd(mop, normArr, (airMopper)airArrayNix, airMopOnOkay); } } if (!E && (1 << limnPolyDataInfoTex2) & bitflag) { appu.f = &(newpld->tex2); E|=!(tex2Arr=airArrayNew(appu.v, &(newpld->tex2Num), 2*sizeof(float), incr)); if (!E) { airMopAdd(mop, tex2Arr, (airMopper)airArrayNuke, airMopOnError); airMopAdd(mop, tex2Arr, (airMopper)airArrayNix, airMopOnOkay); } } if (!E && (1 << limnPolyDataInfoTang) & bitflag) { appu.f = &(newpld->tang); E|=!(tangArr=airArrayNew(appu.v, &(newpld->tangNum), 3*sizeof(float), incr)); if (!E) { airMopAdd(mop, tangArr, (airMopper)airArrayNuke, airMopOnError); airMopAdd(mop, tangArr, (airMopper)airArrayNix, airMopOnOkay); } } if (!E) { incr = pld->indxNum/20; /* 5% of previous index count... */ if (incr<10) incr=10; /* ...but at least 10. */ appu.ui = &(newpld->indx); E|=!(indxArr=airArrayNew(appu.v, &(newpld->indxNum), sizeof(unsigned int), incr)); if (!E) { airMopAdd(mop, indxArr, (airMopper)airArrayNuke, airMopOnError); airMopAdd(mop, indxArr, (airMopper)airArrayNix, airMopOnOkay); } } if (!E) { incr = pld->primNum/10; /* 10% of previous primNum... */ if (incr<1) incr=1; /* ...but at least 1. */ appu.uc = &(newpld->type); E|=!(typeArr=airArrayNew(appu.v, &(newpld->primNum), sizeof(unsigned char), incr)); if (!E) { airMopAdd(mop, typeArr, (airMopper)airArrayNuke, airMopOnError); airMopAdd(mop, typeArr, (airMopper)airArrayNix, airMopOnOkay); } appu.ui = &(newpld->icnt); E|=!(icntArr=airArrayNew(appu.v, NULL, sizeof(unsigned int), incr)); if (!E) { airMopAdd(mop, icntArr, (airMopper)airArrayNuke, airMopOnError); airMopAdd(mop, icntArr, (airMopper)airArrayNix, airMopOnOkay); } } } if (E) { biffAddf(LIMN, "%s: couldn't allocate buffers", me); airMopError(mop); return 1; } /* mark vertices, leaving at 0 means "discard" */ lup = nrrdDLookup[nval->type]; for (i=0; ixyzwNum; i++) { unsigned int j, keep = AIR_TRUE; for (j=0; jdata, idx) < thresh[j]) keep = AIR_FALSE; } if (keep) { keepVert[i]=AIR_TRUE; } } /* now, iterate over all primitives and triangles */ /* Note: If keepVert[i]==AIR_TRUE, newIdx[i] is its new index; else, it is * an index j into llist, which is a linked list: * llist[j] == other (kept) end of the edge * llist[j+1] == index of new vertex for that edge * llist[j+2] == next index into llist */ /* TODO: All the airArray stuff should have allocation error checking */ idx=0; for (i=0; iprimNum; i++) { int j, oldTriNum=pld->icnt[i]/3, newTriNum=0; unsigned int kept=0, disck=0; /* index of last kept / discarded vertex */ for (j=0; jindx[idx+k]; if (keepVert[oldidx]) { keepN++; kept=oldidx; /* make sure the vertex is copied over */ if (newIdx[oldidx]==-1) { unsigned int q=newIdx[oldidx]=airArrayLenIncr(xyzwArr, 1); ELL_4V_COPY(newpld->xyzw+4*q, pld->xyzw+4*oldidx); if ((1 << limnPolyDataInfoRGBA) & bitflag) { airArrayLenIncr(rgbaArr, 1); ELL_4V_COPY(newpld->rgba+4*q, pld->rgba+4*oldidx); } if ((1 << limnPolyDataInfoNorm) & bitflag) { airArrayLenIncr(normArr, 1); ELL_3V_COPY(newpld->norm+3*q, pld->norm+3*oldidx); } if ((1 << limnPolyDataInfoTex2) & bitflag) { airArrayLenIncr(tex2Arr, 1); ELL_2V_COPY(newpld->tex2+2*q, pld->tex2+2*oldidx); } if ((1 << limnPolyDataInfoTang) & bitflag) { airArrayLenIncr(tangArr, 1); ELL_3V_COPY(newpld->tang+3*q, pld->tang+3*oldidx); } } } else { disck=k; } } switch (keepN) { case 0: /* nothing to be done; discard this triangle */ break; case 1: /* result of clipping is a single triangle */ newTriNum++; p=airArrayLenIncr(indxArr, 3); for (k=0; k<3; k++) { if (keepVert[pld->indx[idx+k]]) newpld->indx[p+k]=newIdx[pld->indx[idx+k]]; else newpld->indx[p+k]=clipEdge(pld->indx[idx+k], kept, nval, thresh, newIdx, llistArr, pld, bitflag, newpld, xyzwArr, rgbaArr, normArr, tex2Arr, tangArr); } break; case 2: /* result of clipping is a quad, triangulate */ newTriNum+=2; p=0; for (k=0; k<3; k++) { if (keepVert[pld->indx[idx+k]]) quad[p++]=newIdx[pld->indx[idx+k]]; else { quad[p++]=clipEdge(pld->indx[idx+k], pld->indx[idx+(disck+2)%3], nval, thresh, newIdx, llistArr, pld, bitflag, newpld, xyzwArr, rgbaArr, normArr, tex2Arr, tangArr); quad[p++]=clipEdge(pld->indx[idx+k], pld->indx[idx+(disck+1)%3], nval, thresh, newIdx, llistArr, pld, bitflag, newpld, xyzwArr, rgbaArr, normArr, tex2Arr, tangArr); } } p=airArrayLenIncr(indxArr, 6); ELL_3V_SET(newpld->indx+p, quad[0], quad[1], quad[3]); ELL_3V_SET(newpld->indx+p+3, quad[1], quad[2], quad[3]); break; case 3: /* simply copy the existing triangle */ newTriNum++; p=airArrayLenIncr(indxArr, 3); for (k=0; k<3; k++) { newpld->indx[p+k]=newIdx[pld->indx[idx+k]]; } break; } } if (newTriNum>0) { unsigned int p=airArrayLenIncr(typeArr, 1); airArrayLenIncr(icntArr, 1); newpld->type[p]=limnPrimitiveTriangles; newpld->icnt[p]=newTriNum*3; } } /* finally, replace contents of pld with new data */ airFree(pld->xyzw); airFree(pld->rgba); airFree(pld->norm); airFree(pld->tex2); airFree(pld->tang); airFree(pld->indx); airFree(pld->type); airFree(pld->icnt); memcpy(pld, newpld, sizeof(limnPolyData)); airMopOkay(mop); return 0; } /* Simple wrapper around limnPolyDataClipMulti, in case of only one * clipping criterion. */ int limnPolyDataClip(limnPolyData *pld, Nrrd *nval, double thresh) { return limnPolyDataClipMulti(pld, nval, &thresh); } /* limnPolyDataCompress: * returns a "compressed" copy of the given limnPolyData pld that only * contains vertices that are referenced by some primitive * returns NULL and adds a message to biff upon error */ limnPolyData * limnPolyDataCompress(const limnPolyData *pld) { static const char me[]="limnPolyDataCompress"; limnPolyData *ret = NULL; unsigned int infoBitFlag=0, vertNum=0, i, used_indxNum=0; int *vertMap; if (pld==NULL) { biffAddf(LIMN, "%s: got NULL pointer", me); return NULL; } infoBitFlag=limnPolyDataInfoBitFlag(pld); vertMap=(int*) calloc(pld->xyzwNum,sizeof(int)); if (vertMap==NULL) { biffAddf(LIMN, "%s: could not allocate memory", me); return NULL; } /* how many indices are actually used? */ for (i=0; iprimNum; i++) { used_indxNum+=pld->icnt[i]; } /* loop over all indices and mark referenced vertices in vertMap */ for (i=0; iindx[i]]=1; } /* turn vertMap into an index map */ for (i=0; ixyzwNum; i++) { if (vertMap[i]==0) vertMap[i]=-1; else vertMap[i]=vertNum++; } /* allocate new limnPolyData */ if (NULL == (ret=limnPolyDataNew()) || 0!=limnPolyDataAlloc(ret, infoBitFlag, vertNum, used_indxNum, pld->primNum)) { biffAddf(LIMN, "%s: Could not allocate result", me); free(vertMap); return NULL; } /* fill the newly allocated structure */ for (i=0; ixyzwNum; i++) { if (vertMap[i]>=0) { ELL_4V_COPY(ret->xyzw+4*vertMap[i], pld->xyzw+4*i); } } if (ret->rgba!=NULL) { for (i=0; ixyzwNum; i++) { if (vertMap[i]>=0) { ELL_4V_COPY(ret->rgba+4*vertMap[i], pld->rgba+4*i); } } } if (ret->norm!=NULL) { for (i=0; ixyzwNum; i++) { if (vertMap[i]>=0) { ELL_3V_COPY(ret->norm+3*vertMap[i], pld->norm+3*i); } } } if (ret->tex2!=NULL) { for (i=0; ixyzwNum; i++) { if (vertMap[i]>=0) { ELL_2V_COPY(ret->tex2+2*vertMap[i], pld->tex2+2*i); } } } if (ret->tang!=NULL) { for (i=0; ixyzwNum; i++) { if (vertMap[i]>=0) { ELL_3V_COPY(ret->tang+3*vertMap[i], pld->tang+3*i); } } } for (i=0; iindx[i]=vertMap[pld->indx[i]]; } memcpy(ret->type, pld->type, sizeof(char)*pld->primNum); memcpy(ret->icnt, pld->icnt, sizeof(int)*pld->primNum); free(vertMap); return ret; } /* limnPolyDataJoin: * concatenates the primitives in all num limnPolyDatas given in plds * and returns the result as a newly allocated limnPolyData * the new limnPolyData will only have color/normals/texture coordinates * if _all_ input limnPolyDatas had the respective attribute * returns NULL and adds a message to biff upon error */ limnPolyData *limnPolyDataJoin(const limnPolyData **plds, unsigned int num) { static const char me[]="limnPolyDataJoin"; limnPolyData *ret = NULL; unsigned int infoBitFlag=(1 << limnPolyDataInfoRGBA) | (1 << limnPolyDataInfoNorm) | (1 << limnPolyDataInfoTex2) | (1 << limnPolyDataInfoTang); /* by default, assume we have all these */ unsigned int vertNum=0, indxNum=0, primNum=0; unsigned int i; if (plds==NULL) { biffAddf(LIMN, "%s: got NULL pointer", me); return NULL; } /* loop over all input plds to find infoBitFlag and the total number of * vertices / indices / primitives */ for (i=0; ixyzwNum; indxNum += plds[i]->indxNum; primNum += plds[i]->primNum; } if (NULL == (ret=limnPolyDataNew()) || 0!=limnPolyDataAlloc(ret, infoBitFlag, vertNum, indxNum, primNum)) { biffAddf(LIMN, "%s: Could not allocate result", me); return NULL; } /* loop again over all input plds and fill the newly allocated structure */ vertNum=indxNum=primNum=0; for (i=0; ixyzw+4*vertNum, plds[i]->xyzw, sizeof(float)*4*plds[i]->xyzwNum); if (ret->rgba!=NULL) { memcpy(ret->rgba+4*vertNum, plds[i]->rgba, sizeof(unsigned char)*4*plds[i]->xyzwNum); } if (ret->norm!=NULL) { memcpy(ret->norm+3*vertNum, plds[i]->norm, sizeof(float)*3*plds[i]->xyzwNum); } if (ret->tex2!=NULL) { memcpy(ret->tex2+2*vertNum, plds[i]->tex2, sizeof(float)*2*plds[i]->xyzwNum); } if (ret->tang!=NULL) { memcpy(ret->tang+3*vertNum, plds[i]->tang, sizeof(float)*3*plds[i]->xyzwNum); } for (j=0; jindxNum; j++) { ret->indx[indxNum+j]=vertNum+plds[i]->indx[j]; } for (j=0; jprimNum; j++) { ret->type[primNum+j]=plds[i]->type[j]; ret->icnt[primNum+j]=plds[i]->icnt[j]; /* need to keep track of how many indices are actually used */ used_indxNum+=plds[i]->icnt[j]; } vertNum+=plds[i]->xyzwNum; indxNum+=used_indxNum; primNum+=plds[i]->primNum; } return ret; } int limnPolyDataEdgeHalve(limnPolyData *pldOut, const limnPolyData *pldIn) { static const char me[]="limnPolyDataEdgeHalve"; Nrrd *nnewvert; unsigned int *newvert, nvold, nvidx, triidx, trinum, vlo, vhi, bitflag; airArray *mop; if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pldIn)) { biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me, airEnumStr(limnPrimitive, limnPrimitiveTriangles)); return 1; } if (1 != pldIn->primNum) { biffAddf(LIMN, "%s: sorry, can only handle a single primitive", me); return 1; } mop = airMopNew(); nnewvert = nrrdNew(); airMopAdd(mop, nnewvert, AIR_CAST(airMopper, nrrdNuke), airMopAlways); nvold = pldIn->xyzwNum; if (nrrdMaybeAlloc_va(nnewvert, nrrdTypeUInt, 2, nvold, nvold)) { biffMovef(LIMN, NRRD, "%s: couldn't allocate buffer", me); airMopError(mop); return 1; } newvert = AIR_CAST(unsigned int*, nnewvert->data); /* run through triangles, recording edges with the new vertex index */ nvidx = nvold; trinum = pldIn->indxNum/3; for (triidx=0; triidxindx[0 + 3*triidx]; vhi = pldIn->indx[1 + 3*triidx]; if (!newvert[vlo + nvold*vhi]) { newvert[vlo + nvold*vhi] = newvert[vhi + nvold*vlo] = nvidx++; } vlo = pldIn->indx[1 + 3*triidx]; vhi = pldIn->indx[2 + 3*triidx]; if (!newvert[vlo + nvold*vhi]) { newvert[vlo + nvold*vhi] = newvert[vhi + nvold*vlo] = nvidx++; } vlo = pldIn->indx[2 + 3*triidx]; vhi = pldIn->indx[0 + 3*triidx]; if (!newvert[vlo + nvold*vhi]) { newvert[vlo + nvold*vhi] = newvert[vhi + nvold*vlo] = nvidx++; } } /* allocate output */ bitflag = limnPolyDataInfoBitFlag(pldIn); if (limnPolyDataAlloc(pldOut, bitflag, nvidx, 3*4*trinum, 1)) { biffAddf(LIMN, "%s: trouble allocating output", me); airMopError(mop); return 1; } pldOut->type[0] = limnPrimitiveTriangles; pldOut->icnt[0] = 3*4*trinum; /* set output indx */ for (triidx=0; triidxindx[0 + 3*triidx]; bb = pldIn->indx[1 + 3*triidx]; cc = pldIn->indx[2 + 3*triidx]; ab = newvert[aa + nvold*bb]; bc = newvert[bb + nvold*cc]; ac = newvert[aa + nvold*cc]; ELL_3V_SET(pldOut->indx + 3*(0 + 4*triidx), aa, ab, ac); ELL_3V_SET(pldOut->indx + 3*(1 + 4*triidx), ab, bc, ac); ELL_3V_SET(pldOut->indx + 3*(2 + 4*triidx), ab, bb, bc); ELL_3V_SET(pldOut->indx + 3*(3 + 4*triidx), ac, bc, cc); } /* set output vertex info */ for (vlo=0; vloxyzw + 4*vlo, pldIn->xyzw + 4*vlo); if ((1 << limnPolyDataInfoRGBA) & bitflag) { ELL_4V_COPY(pldOut->rgba + 4*vlo, pldIn->rgba + 4*vlo); } if ((1 << limnPolyDataInfoNorm) & bitflag) { ELL_3V_COPY(pldOut->norm + 3*vlo, pldIn->norm + 3*vlo); } if ((1 << limnPolyDataInfoTex2) & bitflag) { ELL_2V_COPY(pldOut->tex2 + 2*vlo, pldIn->tex2 + 2*vlo); } if ((1 << limnPolyDataInfoTang) & bitflag) { ELL_3V_COPY(pldOut->tang + 3*vlo, pldIn->tang + 3*vlo); } for (vhi=vlo+1; vhixyzw + 4*mid, 0.5f, pldIn->xyzw + 4*vlo, pldIn->xyzw + 4*vhi); if ((1 << limnPolyDataInfoRGBA) & bitflag) { ELL_4V_LERP_TT(pldOut->rgba + 4*mid, unsigned char, 0.5f, pldIn->rgba + 4*vlo, pldIn->rgba + 4*vhi); } if ((1 << limnPolyDataInfoNorm) & bitflag) { float tmp; ELL_3V_LERP(pldOut->norm + 3*mid, 0.5f, pldIn->norm + 3*vlo, pldIn->norm + 3*vhi); ELL_3V_NORM_TT(pldOut->norm + 3*mid, float, pldOut->norm + 3*mid, tmp); } if ((1 << limnPolyDataInfoTex2) & bitflag) { ELL_2V_LERP(pldOut->tex2 + 2*mid, 0.5f, pldIn->tex2 + 2*vlo, pldIn->tex2 + 2*vhi); } if ((1 << limnPolyDataInfoTang) & bitflag) { float tmp; ELL_3V_LERP(pldOut->tang + 3*mid, 0.5f, pldIn->tang + 3*vlo, pldIn->tang + 3*vhi); ELL_3V_NORM_TT(pldOut->tang + 3*mid, float, pldOut->tang + 3*mid, tmp); } } } airMopOkay(mop); return 0; } /* helper function for the limnPolyDataNeighborList below */ static void registerNeighbor(unsigned int *nblist, size_t *len, unsigned int *maxnb, unsigned int u, unsigned int v) { unsigned int idx=nblist[u], pointer=u, depth=1; while (idx!=0) { if (nblist[idx]==v) return; /* has already been registered */ pointer=idx+1; idx=nblist[pointer]; depth++; } if (depth>*maxnb) *maxnb=depth; /* do the registration */ nblist[pointer]=*len; nblist[*len]=v; nblist[*len+1]=0; (*len)+=2; /* now the other way around */ idx=nblist[v]; pointer=v; while (idx!=0) { /* do not have to check nblist[idx]==u due to symmetry */ pointer=idx+1; idx=nblist[pointer]; } nblist[pointer]=*len; nblist[*len]=u; nblist[*len+1]=0; (*len)+=2; } /* Here's the thing with all these limnPolyDataNeighbor* functions: * * *List is used for building up the information and called by all others * *Array is a great representation if all vertices have a similar number * of neighbors (in particular, no gross outliers) * The output of this is used by glyph coloring in elf. * *ArrayComp is a compressed representation that is best if vertices have * a more variable number of neighbors * The output of this is used by surface smoothing in limn. */ /* Mallocs *nblist and fills it with a linked list that contains all neighbors * of all n vertices in the given limnPolyData. The format is as follows: * For v=n) into *nblist, or 0 if vertex v * does not have any neighbors. * For an index i obtained this way, (*nblist)[i] is a neighbor of v, * (*nblist)[i+1] is the index of the next list element (or 0). * If non-NULL, *len is set to the required size of *nblist - the initial malloc * makes a conservative guess and you may want to realloc the result to *len * bytes in order to free memory that ended up unused * If non-NULL, *m is set to the maximum number of neighbors (over all vertices) * Return value is 0 upon success, -1 upon error. Biff is used for errors. */ int limnPolyDataNeighborList(unsigned int **nblist, size_t *len, unsigned int *maxnb, const limnPolyData *pld) { static const char me[]="limnPolyDataNeighborList"; unsigned int i, j, m, estimate=0, *indx; size_t last; /* estimate the maximum number of neighborhood relations */ for (i=0; iprimNum; i++) { switch (pld->type[i]) { case limnPrimitiveTriangles: case limnPrimitiveQuads: estimate+=pld->icnt[i]*2; break; case limnPrimitiveTriangleStrip: case limnPrimitiveTriangleFan: estimate+=4*(pld->icnt[i]-2)+2; break; case limnPrimitiveLineStrip: estimate+=2*(pld->icnt[i]-1); break; case limnPrimitiveLines: estimate+=pld->icnt[i]; break; default: /* should be a noop; silently ignore it */ break; } } /* allocate *nblist */ *nblist = (unsigned int*) malloc(sizeof(unsigned int)* (pld->xyzwNum+2*estimate)); if (*nblist==NULL) { biffAddf(LIMN, "%s: couldn't allocate nblist buffer", me); return -1; } /* populate the list */ memset(*nblist, 0, sizeof(unsigned int)*pld->xyzwNum); last=pld->xyzwNum; m=0; indx=pld->indx; for (i=0; iprimNum; i++) { switch (pld->type[i]) { case limnPrimitiveTriangles: for (j=0; jicnt[i]; j+=3) { /* go through all triangles */ registerNeighbor(*nblist, &last, &m, indx[j], indx[j+1]); registerNeighbor(*nblist, &last, &m, indx[j+1], indx[j+2]); registerNeighbor(*nblist, &last, &m, indx[j+2], indx[j]); } break; case limnPrimitiveTriangleStrip: if (pld->icnt[i]>0) registerNeighbor(*nblist, &last, &m, indx[0], indx[1]); for (j=0; jicnt[i]-2; j++) { registerNeighbor(*nblist, &last, &m, indx[j], indx[j+2]); registerNeighbor(*nblist, &last, &m, indx[j+1], indx[j+2]); } break; case limnPrimitiveTriangleFan: if (pld->icnt[i]>0) registerNeighbor(*nblist, &last, &m, indx[0], indx[1]); for (j=0; jicnt[i]-2; j++) { registerNeighbor(*nblist, &last, &m, indx[0], indx[j+2]); registerNeighbor(*nblist, &last, &m, indx[j+1], indx[j+2]); } break; case limnPrimitiveQuads: for (j=0; jicnt[i]; j+=4) { /* go through all quads */ registerNeighbor(*nblist, &last, &m, indx[j], indx[j+1]); registerNeighbor(*nblist, &last, &m, indx[j+1], indx[j+2]); registerNeighbor(*nblist, &last, &m, indx[j+2], indx[j+3]); registerNeighbor(*nblist, &last, &m, indx[j+3], indx[j]); } break; case limnPrimitiveLineStrip: for (j=0; jicnt[i]-1; j++) { registerNeighbor(*nblist, &last, &m, indx[j], indx[j+1]); } break; case limnPrimitiveLines: for (j=0; jicnt[i]; j+=2) { registerNeighbor(*nblist, &last, &m, indx[j], indx[j+1]); } break; default: /* should be a noop; silently ignore it */ break; } indx+=pld->icnt[i]; } if (len!=NULL) *len=last*sizeof(unsigned int); if (maxnb!=NULL) *maxnb=m; return 0; } /* Over the set of all n vertices in a given limnPolyData, finds the * maximum number m of neighbors. Sets *neighbors to a malloc'ed block * of (n*m) indices and lists the neighbors of vertex v at position * (v*m) of the list, padded with -1s. * *maxnb will be set to m (and is assumed to be non-NULL!) * Returns -1 and adds a biff error if there was a problem allocating memory. */ int limnPolyDataNeighborArray(int **neighbors, unsigned int *maxnb, const limnPolyData *pld) { static const char me[]="limnPolyDataNeighborArray"; unsigned int i, *nblist, m; /* get the neighbors as a linked list */ if (-1==limnPolyDataNeighborList(&nblist, NULL, &m, pld)) { return -1; } /* convert the result into an array */ *neighbors = (int *) malloc(sizeof(int)*m*pld->xyzwNum); if (NULL==*neighbors) { biffAddf(LIMN, "%s: couldn't allocate neighbors buffer", me); free(nblist); return -1; } for (i=0; ixyzwNum; i++) { unsigned int aidx=0, lidx=nblist[i]; while (lidx!=0) { (*neighbors)[m*i+aidx]=nblist[lidx]; lidx=nblist[lidx+1]; aidx++; } while (aidxxyzwNum+1; * (*idx)[i] will give you the position in *neighbors at which the neighbors of * vertex i start * Returns -1 and adds a biff error if there was a problem allocating memory. */ int limnPolyDataNeighborArrayComp(int **neighbors, int **idx, const limnPolyData *pld) { static const char me[]="limnPolyDataNeighborArrayComp"; unsigned int i, ct, *nblist; size_t len; airArray *mop; mop = airMopNew(); /* get the neighbors as a linked list */ if (-1==limnPolyDataNeighborList(&nblist, &len, NULL, pld)) { return -1; } airMopAdd(mop, nblist, airFree, airMopAlways); /* convert the result into compressed form */ *neighbors = (int *) malloc(sizeof(int)*(len-pld->xyzwNum)/2); if (NULL==*neighbors) { biffAddf(LIMN, "%s: couldn't allocate neighbors buffer", me); airMopError(mop); return -1; } airMopAdd(mop, neighbors, airFree, airMopOnError); *idx = (int *) malloc(sizeof(int)*(pld->xyzwNum+1)); if (NULL==*idx) { biffAddf(LIMN, "%s: couldn't allocate index buffer", me); airMopError(mop); return -1; } airMopAdd(mop, idx, airFree, airMopOnError); for (ct=i=0; ixyzwNum; i++) { unsigned int lidx=nblist[i]; (*idx)[i]=ct; while (lidx!=0) { (*neighbors)[ct]=nblist[lidx]; lidx=nblist[lidx+1]; ct++; } } (*idx)[pld->xyzwNum]=ct; airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/limn/io.c0000664000175000017500000011332712165631065016122 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" int limnObjectDescribe(FILE *file, const limnObject *obj) { limnFace *face; unsigned int si, fii; limnEdge *edge; unsigned int eii; limnVertex *vert; unsigned int vii; limnPart *part; unsigned int partIdx; limnLook *look; fprintf(file, "parts: %d\n", obj->partNum); for (partIdx=0; partIdxpartNum; partIdx++) { part = obj->part[partIdx]; fprintf(file, "part %d | verts: %d ========\n", partIdx, part->vertIdxNum); for (vii=0; viivertIdxNum; vii++) { vert = obj->vert + part->vertIdx[vii]; fprintf(file, "part %d | %d(%d): w=(%g,%g,%g)\n", partIdx, vii, part->vertIdx[vii], vert->world[0], vert->world[1], vert->world[2]); /* vert->view[0], vert->view[1], vert->view[2]); */ /* vert->screen[0], vert->screen[1], vert->screen[2]); */ } fprintf(file, "part %d | edges: %d ========\n", partIdx, part->edgeIdxNum); for (eii=0; eiiedgeIdxNum; eii++) { edge = obj->edge + part->edgeIdx[eii]; fprintf(file, "part %d==%d | %d(%d): " "vert(%d,%d), face(%d,%d)\n", partIdx, edge->partIdx, eii, part->edgeIdx[eii], edge->vertIdx[0], edge->vertIdx[1], edge->faceIdx[0], edge->faceIdx[1]); } fprintf(file, "part %d | faces: %d ========\n", partIdx, part->faceIdxNum); for (fii=0; fiifaceIdxNum; fii++) { face = obj->face + part->faceIdx[fii]; fprintf(file, "part %d==%d | %d(%d): [", partIdx, face->partIdx, fii, part->faceIdx[fii]); for (si=0; sisideNum; si++) { fprintf(file, "%d", face->vertIdx[si]); if (si < face->sideNum-1) { fprintf(file, ","); } } fprintf(file, "]; wn = (%g,%g,%g)", face->worldNormal[0], face->worldNormal[1], face->worldNormal[2]); look = obj->look + face->lookIdx; fprintf(file, "; RGB=(%g,%g,%g)", look->rgba[0], look->rgba[1], look->rgba[2]); fprintf(file, "\n"); } } return 0; } int limnObjectWriteOFF(FILE *file, const limnObject *obj) { static const char me[]="limnObjectWriteOFF"; unsigned int si; limnVertex *vert; unsigned int vii; limnFace *face; unsigned int fii; limnPart *part; unsigned int partIdx; if (!( obj && file )) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } fprintf(file, "OFF # created by Teem/limn\n"); fprintf(file, "%d %d %d\n", obj->vertNum, obj->faceNum, obj->edgeNum); /* write vertices */ for (partIdx=0; partIdxpartNum; partIdx++) { fprintf(file, "### LIMN BEGIN PART %d\n", partIdx); part = obj->part[partIdx]; for (vii=0; viivertIdxNum; vii++) { vert = obj->vert + part->vertIdx[vii]; fprintf(file, "%g %g %g", vert->world[0]/vert->world[3], vert->world[1]/vert->world[3], vert->world[2]/vert->world[3]); /* verts no longer have a lookIdx if (vert->lookIdx) { fprintf(file, " %g %g %g", obj->look[vert->lookIdx].rgba[0], obj->look[vert->lookIdx].rgba[1], obj->look[vert->lookIdx].rgba[2]); } */ fprintf(file, "\n"); } } /* write faces */ for (partIdx=0; partIdxpartNum; partIdx++) { fprintf(file, "### LIMN BEGIN PART %d\n", partIdx); part = obj->part[partIdx]; for (fii=0; fiifaceIdxNum; fii++) { face = obj->face + part->faceIdx[fii]; fprintf(file, "%d", face->sideNum); for (si=0; sisideNum; si++) { fprintf(file, " %d", face->vertIdx[si]); } if (face->lookIdx) { fprintf(file, " %g %g %g", obj->look[face->lookIdx].rgba[0], obj->look[face->lookIdx].rgba[1], obj->look[face->lookIdx].rgba[2]); } fprintf(file, "\n"); } } return 0; } int limnPolyDataWriteIV(FILE *file, const limnPolyData *pld) { static const char me[]="limnPolyDataWriteIV"; unsigned int primIdx, xyzwIdx, rgbaIdx, normIdx, bitFlag, baseVertIdx; int haveStrips, haveTris, haveElse; double xyz[3], norm[3], len; if (!(file && pld)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } haveStrips = haveTris = haveElse = AIR_FALSE; for (primIdx=0; primIdxprimNum; primIdx++) { int isTri, isStrip, isElse; isTri = limnPrimitiveTriangles == pld->type[primIdx]; isStrip = limnPrimitiveTriangleStrip == pld->type[primIdx]; isElse = !(isTri || isStrip); haveTris |= isTri; haveStrips |= isStrip; haveElse |= isElse; if (isElse) { biffAddf(LIMN, "%s: sorry, can only have %s or %s prims (prim[%u] is %s)", me, airEnumStr(limnPrimitive, limnPrimitiveTriangles), airEnumStr(limnPrimitive, limnPrimitiveTriangleStrip), primIdx, airEnumStr(limnPrimitive, pld->type[primIdx])); return 1; } } if (haveStrips && 1 != pld->primNum) { biffAddf(LIMN, "%s: sorry, can only have a single triangle strip", me); return 1; } fprintf(file, "#Inventor V2.0 ascii\n"); fprintf(file, "# written by Teem/limn\n\n"); fprintf(file, "Separator {\n"); fprintf(file, " Coordinate3 {\n"); fprintf(file, " point [\n"); if (haveStrips) { unsigned int vii; for (vii=0; viiicnt[0]; vii++) { xyzwIdx = (pld->indx)[vii]; ELL_34V_HOMOG(xyz, pld->xyzw + 4*xyzwIdx); fprintf(file, " %g %g %g%s\n", xyz[0], xyz[1], xyz[2], vii < pld->icnt[0]-1 ? "," : ""); } } else { for (xyzwIdx=0; xyzwIdxxyzwNum; xyzwIdx++) { ELL_34V_HOMOG(xyz, pld->xyzw + 4*xyzwIdx); fprintf(file, " %g %g %g%s\n", xyz[0], xyz[1], xyz[2], xyzwIdx < pld->xyzwNum-1 ? "," : ""); } } fprintf(file, " ]\n"); fprintf(file, " }\n"); bitFlag = limnPolyDataInfoBitFlag(pld); if (bitFlag & (1 << limnPolyDataInfoNorm)) { fprintf(file, " NormalBinding { value PER_VERTEX_INDEXED }\n"); fprintf(file, " Normal {\n"); fprintf(file, " vector [\n"); if (haveStrips) { unsigned int vii; for (vii=0; viiicnt[0]; vii++) { normIdx = (pld->indx)[vii]; ELL_3V_SET(norm, pld->norm[0 + 3*normIdx], pld->norm[1 + 3*normIdx], pld->norm[2 + 3*normIdx]); ELL_3V_NORM(norm, norm, len); fprintf(file, " %g %g %g%s\n", norm[0], norm[1], norm[2], vii < pld->icnt[0]-1 ? "," : ""); } } else { for (normIdx=0; normIdxnormNum; normIdx++) { fprintf(file, " %g %g %g%s\n", pld->norm[0 + 3*normIdx], pld->norm[1 + 3*normIdx], pld->norm[2 + 3*normIdx], normIdx < pld->normNum-1 ? "," : ""); } } fprintf(file, " ]\n"); fprintf(file, " }\n"); } if (!haveStrips) { if (bitFlag & (1 << limnPolyDataInfoRGBA)) { fprintf(file, " MaterialBinding { value PER_VERTEX_INDEXED }\n"); fprintf(file, " Material {\n"); fprintf(file, " diffuseColor [\n"); for (rgbaIdx=0; rgbaIdxrgbaNum; rgbaIdx++) { fprintf(file, " %g %g %g%s\n", pld->rgba[0 + 4*rgbaIdx]/255.0, pld->rgba[1 + 4*rgbaIdx]/255.0, pld->rgba[2 + 4*rgbaIdx]/255.0, rgbaIdx < pld->rgbaNum-1 ? "," : ""); } fprintf(file, " ]\n"); fprintf(file, " }\n"); } } if (haveStrips) { fprintf(file, " TriangleStripSet {\n"); fprintf(file, " numVertices %u\n", pld->icnt[0]); fprintf(file, " }\n"); } else { fprintf(file, " IndexedFaceSet {\n"); fprintf(file, " coordIndex [\n"); baseVertIdx = 0; for (primIdx=0; primIdxprimNum; primIdx++) { unsigned int triIdx, triNum, *indx3; triNum = pld->icnt[primIdx]/3; for (triIdx=0; triIdxindx + baseVertIdx + 3*triIdx; fprintf(file, " %u, %u, %u, -1%s\n", indx3[0], indx3[1], indx3[2], triIdx < triNum-1 ? "," : ""); } baseVertIdx += 3*triNum; } fprintf(file, " ]\n"); fprintf(file, " }\n"); } fprintf(file, "}\n"); return 0; } int limnObjectReadOFF(limnObject *obj, FILE *file) { static const char me[]="limnObjectReadOFF"; double vert[6]; char line[AIR_STRLEN_LARGE]; /* HEY: bad Gordon */ int lineCount, lookIdx, partIdx, idxTmp, faceNum, faceGot, got; unsigned int vertGot,vertNum; unsigned int ibuff[1024]; /* HEY: bad Gordon */ float fbuff[1024]; /* HEY: bad bad Gordon */ float lastRGB[3]={-1,-1,-1}; int lastLook; unsigned int lret; int *vertBase; airArray *vertBaseArr, *mop; airPtrPtrUnion appu; if (!( obj && file )) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } vertBase = NULL; appu.i = &vertBase; vertBaseArr = airArrayNew(appu.v, NULL, sizeof(int), 128); mop = airMopNew(); airMopAdd(mop, vertBaseArr, (airMopper)airArrayNuke, airMopAlways); got = 0; lineCount = 0; do { if (!airOneLine(file, line, AIR_STRLEN_LARGE)) { biffAddf(LIMN, "%s: hit EOF before getting #vert #face #edge line", me); airMopError(mop); return 1; } lineCount++; got = airParseStrUI(ibuff, line, AIR_WHITESPACE, 3); } while (3 != got); vertNum = ibuff[0]; faceNum = ibuff[1]; /* read all vertex information */ lastLook = -1; partIdx = limnObjectPartAdd(obj); vertGot = 0; airArrayLenIncr(vertBaseArr, 1); vertBase[partIdx] = vertGot; while (vertGot < vertNum) { do { lret = airOneLine(file, line, AIR_STRLEN_LARGE); lineCount++; } while (1 == lret); if (!lret) { biffAddf(LIMN, "%s: (near line %d) hit EOF trying to read vert %d (of %d)", me, lineCount, vertGot, vertNum); airMopError(mop); return 1; } if (1 == sscanf(line, "### LIMN BEGIN PART %d", &idxTmp)) { if (idxTmp != 0) { partIdx = limnObjectPartAdd(obj); if (idxTmp != partIdx) { biffAddf(LIMN, "%s: got signal to start part %d, not %d", me, idxTmp, partIdx); airMopError(mop); return 1; } airArrayLenIncr(vertBaseArr, 1); vertBase[partIdx] = vertGot; } continue; } if (3 != airParseStrD(vert, line, AIR_WHITESPACE, 3)) { biffAddf(LIMN, "%s: couldn't parse 3 doubles from \"%s\" " "for vert %d (of %d)", me, line, vertGot, vertNum); airMopError(mop); return 1; } if (6 == airParseStrD(vert, line, AIR_WHITESPACE, 6)) { /* we could also parse an RGB color */ if (-1 == lastLook || !ELL_3V_EQUAL(lastRGB, vert+3)) { lookIdx = limnObjectLookAdd(obj); ELL_4V_SET(obj->look[lookIdx].rgba, AIR_CAST(float, vert[3]), AIR_CAST(float, vert[4]), AIR_CAST(float, vert[5]), 1); lastLook = lookIdx; ELL_3V_COPY_TT(lastRGB, float, vert+3); } else { lookIdx = lastLook; } } else { lookIdx = 0; } /* fprintf(stderr, "line %d: vertGot = %d; lookIdx = %d; partIdx = %d\n", lineCount, vertGot, lookIdx, partIdx); */ limnObjectVertexAdd(obj, partIdx, AIR_CAST(float, vert[0]), AIR_CAST(float, vert[1]), AIR_CAST(float, vert[2])); vertGot++; } /* read face information */ partIdx = 0; faceGot = 0; while (faceGot < faceNum) { do { lret = airOneLine(file, line, AIR_STRLEN_LARGE); lineCount++; } while (1 == lret); if (!lret) { biffAddf(LIMN, "%s: (near line %d) hit EOF trying to read face %d (of %d)", me, lineCount, faceGot, faceNum); airMopError(mop); return 1; } if (1 == sscanf(line, "### LIMN BEGIN PART %d", &idxTmp)) { if (idxTmp != 0) { partIdx += 1; if (idxTmp != partIdx) { biffAddf(LIMN, "%s: (near line %d) got signal to start " "part %d, not %d", me, lineCount, idxTmp, partIdx); airMopError(mop); return 1; } } continue; } if ('#' == line[0]) { /* its some other kind of comment line */ continue; } if (1 != sscanf(line, "%u", &vertNum)) { biffAddf(LIMN, "%s: (near line %d) can't get first int " "(#verts) from \"%s\" for face %d (of %d)", me, lineCount, line, faceGot, faceNum); airMopError(mop); return 1; } if (vertNum+1 != airParseStrUI(ibuff, line, AIR_WHITESPACE, vertNum+1)) { biffAddf(LIMN, "%s: (near line %d) couldn't parse %d ints from \"%s\" " "for face %d (of %d)", me, lineCount, vertNum+1, line, faceGot, faceNum); airMopError(mop); return 1; } if (vertNum+1+3 == airParseStrF(fbuff, line, AIR_WHITESPACE, vertNum+1+3)) { /* could also parse color */ if (-1 == lastLook || !ELL_3V_EQUAL(lastRGB, fbuff+vertNum+1)) { lookIdx = limnObjectLookAdd(obj); ELL_4V_SET(obj->look[lookIdx].rgba, fbuff[vertNum+1+0], fbuff[vertNum+1+1], fbuff[vertNum+1+2], 1); lastLook = lookIdx; ELL_3V_COPY(lastRGB, fbuff+vertNum+1); } else { lookIdx = lastLook; } } else { lookIdx = 0; } /* fprintf(stderr, "line %d: faceGot = %d; lookIdx = %d; partIdx = %d\n", lineCount, faceGot, lookIdx, partIdx); */ limnObjectFaceAdd(obj, partIdx, lookIdx, vertNum, ibuff+1); faceGot++; } airMopOkay(mop); return 0; } /* http://www.npr.org/templates/story/story.php?storyId=4531695 */ #define LMPD_MAGIC "LIMN0001" #define DEMARK_STR "====== " #define DEMARK_CHAR '=' #define NUM_STR "num:" #define INFO_STR "info:" #define TYPE_STR "type:" #define ICNT_STR "icnt:" #define INDX_STR "indx:" #define XYZW_STR "xyzw:" int limnPolyDataWriteLMPD(FILE *file, const limnPolyData *pld) { static const char me[]="limnPolyDataWriteLMPD"; char infoS[AIR_STRLEN_MED]; unsigned int primIdx, infoNum, flag, bit; Nrrd *nrrd; airArray *mop; if (!(file && pld)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } for (primIdx=0; primIdxprimNum; primIdx++) { if (limnPrimitiveNoop == pld->type[primIdx]) { biffAddf(LIMN, "%s: sorry, can't save with prim[%u] type %s", me, primIdx, airEnumStr(limnPrimitive, pld->type[primIdx])); return 1; } } mop = airMopNew(); fprintf(file, "%s\n", LMPD_MAGIC); fprintf(file, "%s%s %u %u %u\n", DEMARK_STR, NUM_STR, pld->xyzwNum, pld->indxNum, pld->primNum); flag = limnPolyDataInfoBitFlag(pld); infoNum = 0; bit = 0; strcpy(infoS, ""); while (flag) { if (flag & 1) { infoNum += 1; strcat(infoS, airEnumStr(limnPolyDataInfo, bit)); strcat(infoS, "\n"); } flag /= 2; bit += 1; } fprintf(file, "%s%s %u\n%s", DEMARK_STR, INFO_STR, infoNum, infoS); fprintf(file, "%s%s\n", DEMARK_STR, TYPE_STR); for (primIdx=0; primIdxprimNum; primIdx++) { fprintf(file, "%s\n", airEnumStr(limnPrimitive, pld->type[primIdx])); } fprintf(file, "%s%s\n", DEMARK_STR, ICNT_STR); for (primIdx=0; primIdxprimNum; primIdx++) { fprintf(file, "%u\n", pld->icnt[primIdx]); } nrrd = nrrdNew(); airMopAdd(mop, nrrd, (airMopper)nrrdNix, airMopAlways); /* nix, not nuke */ fprintf(file, "%s%s\n", DEMARK_STR, INDX_STR); if (nrrdWrap_va(nrrd, pld->indx, nrrdTypeUInt, 1, pld->indxNum) || nrrdWrite(file, nrrd, NULL)) { biffMovef(LIMN, NRRD, "%s: problem saving indx array", me); airMopError(mop); return 1; } fflush(file); fprintf(file, "\n"); fprintf(file, "%s%s\n", DEMARK_STR, XYZW_STR); if (nrrdWrap_va(nrrd, pld->xyzw, nrrdTypeFloat, 2, 4, pld->xyzwNum) || nrrdWrite(file, nrrd, NULL)) { biffMovef(LIMN, NRRD, "%s: problem saving xyzw array", me); airMopError(mop); return 1; } fflush(file); fprintf(file, "\n"); if (infoNum) { flag = limnPolyDataInfoBitFlag(pld); bit = 0; while (flag) { if (flag & 1) { int E; fprintf(file, "%s%s %s\n", DEMARK_STR, INFO_STR, airEnumStr(limnPolyDataInfo, bit)); switch (bit) { case limnPolyDataInfoRGBA: E = nrrdWrap_va(nrrd, pld->rgba, nrrdTypeUChar, 2, 4, pld->rgbaNum); break; case limnPolyDataInfoNorm: E = nrrdWrap_va(nrrd, pld->norm, nrrdTypeFloat, 2, 3, pld->normNum); break; case limnPolyDataInfoTex2: E = nrrdWrap_va(nrrd, pld->tex2, nrrdTypeFloat, 2, 2, pld->tex2Num); break; case limnPolyDataInfoTang: E = nrrdWrap_va(nrrd, pld->tang, nrrdTypeFloat, 2, 3, pld->tangNum); break; default: biffAddf(LIMN, "%s: info %d (%s) not handled", me, bit, airEnumStr(limnPolyDataInfo, bit)); airMopError(mop); return 1; break; } if (E || nrrdWrite(file, nrrd, NULL)) { biffMovef(LIMN, NRRD, "%s: problem saving %s info", me, airEnumStr(limnPolyDataInfo, bit)); airMopError(mop); return 1; } fflush(file); fprintf(file, "\n"); } flag /= 2; bit += 1; } } airMopOkay(mop); return 0; } /* ******** limnPolyDataReadLMPD ** ** reads a limnPolyData from an LMPD file ** ** HEY: this was written in a hurry, is pretty hacky, and so it ** needs some serious clean-up */ int limnPolyDataReadLMPD(limnPolyData *pld, FILE *file) { static const char me[]="limnPolyDatReadLMPD"; char line[AIR_STRLEN_MED], name[AIR_STRLEN_MED], *tmp; unsigned int vertNum, indxNum, primNum, primIdx, lineLen, infoNum, infoIdx, info, flag; Nrrd *nrrd; airArray *mop; int hackhack, tmpChar; if (!(pld && file)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } sprintf(name, "magic"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(LIMN, "%s: didn't get %s line", me, name); return 1; } if (strcmp(line, LMPD_MAGIC)) { biffAddf(LIMN, "%s: %s line \"%s\" not expected \"%s\"", me, name, line, LMPD_MAGIC); return 1; } sprintf(name, "nums"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(LIMN, "%s: didn't get %s line", me, name); return 1; } if (strncmp(line, DEMARK_STR NUM_STR, strlen(DEMARK_STR NUM_STR))) { biffAddf(LIMN, "%s: %s line \"%s\" didn't start w/ expected \"%s\"", me, name, line, NUM_STR); return 1; } tmp = line + strlen(DEMARK_STR NUM_STR); if (3 != sscanf(tmp, " %u %u %u", &vertNum, &indxNum, &primNum)) { biffAddf(LIMN, "%s: couldn't parse \"%s\" as 3 uints on %s line", me, tmp, name); return 1; } sprintf(name, "info"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(LIMN, "%s: didn't get %s line", me, name); return 1; } if (strncmp(line, DEMARK_STR INFO_STR, strlen(DEMARK_STR INFO_STR))) { biffAddf(LIMN, "%s: %s line \"%s\" didn't start w/ expected \"%s\"", me, name, line, DEMARK_STR INFO_STR); return 1; } tmp = line + strlen(DEMARK_STR INFO_STR); if (1 != sscanf(tmp, " %u", &infoNum)) { biffAddf(LIMN, "%s: couldn't parse \"%s\" as 1 uints on %s line", me, tmp, name); return 1; } flag = 0; for (infoIdx=0; infoIdxtype[primIdx] = airEnumVal(limnPrimitive, line); if (!(pld->type[primIdx])) { biffAddf(LIMN, "%s: couldn't parse \"%s\" %s line %u/%u", me, line, name, primIdx, primNum); return 1; } } sprintf(name, "icnt"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(LIMN, "%s: didn't get %s line", me, name); return 1; } if (strcmp(line, DEMARK_STR ICNT_STR)) { biffAddf(LIMN, "%s: %s line \"%s\" not expected \"%s\"", me, name, line, DEMARK_STR ICNT_STR); return 1; } for (primIdx=0; primIdxicnt[primIdx]))) { biffAddf(LIMN, "%s: couldn't parse \"%s\" %s line %u/%u", me, line, name, primIdx, primNum); return 1; } } sprintf(name, "indx"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(LIMN, "%s: didn't get %s line", me, name); return 1; } if (strcmp(line, DEMARK_STR INDX_STR)) { biffAddf(LIMN, "%s: %s line \"%s\" not expected \"%s\"", me, name, line, DEMARK_STR ICNT_STR); return 1; } /* NOW its finally time to create the mop */ mop = airMopNew(); nrrd = nrrdNew(); airMopAdd(mop, nrrd, (airMopper)nrrdNuke, airMopAlways); /* HEY HEY HEY HOLY CRAP! ** why the hell isn't the verbosity level a field in NrrdIoState ?!?! ** THIS NEES TO BE FIXED (in nrrd) ASAP! */ hackhack = nrrdStateVerboseIO; nrrdStateVerboseIO = 0; if (nrrdRead(nrrd, file, NULL)) { biffMovef(LIMN, NRRD, "%s: trouble reading %s data", me, name); airMopError(mop); return 1; } if (!(nrrdTypeUInt == nrrd->type && 1 == nrrd->dim && indxNum == nrrd->axis[0].size)) { biffAddf(LIMN, "%s: didn't get 1-D %s-type %u-sample array " "(got %u-D %s-type %u-by-? array)", me, airEnumStr(nrrdType, nrrdTypeUInt), AIR_CAST(unsigned int, indxNum), nrrd->dim, airEnumStr(nrrdType, nrrd->type), AIR_CAST(unsigned int, nrrd->axis[0].size)); airMopError(mop); return 1; } /* now copy the data */ memcpy(pld->indx, nrrd->data, nrrdElementSize(nrrd)*nrrdElementNumber(nrrd)); do { tmpChar = getc(file); if (EOF == tmpChar) { biffAddf(LIMN, "%s: hit EOF seeking to begin next line", me); airMopError(mop); return 1; } } while (DEMARK_CHAR != tmpChar); ungetc(tmpChar, file); sprintf(name, "xyzw"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(LIMN, "%s: didn't get %s line", me, name); return 1; } if (strcmp(line, DEMARK_STR XYZW_STR)) { biffAddf(LIMN, "%s: %s line \"%s\" not expected \"%s\"", me, name, line, DEMARK_STR XYZW_STR); return 1; } if (nrrdRead(nrrd, file, NULL)) { biffMovef(LIMN, NRRD, "%s: trouble reading %s data", me, name); airMopError(mop); return 1; } if (!(nrrdTypeFloat == nrrd->type && 2 == nrrd->dim && 4 == nrrd->axis[0].size && vertNum == nrrd->axis[1].size)) { biffAddf(LIMN, "%s: didn't get 2-D %s-type 4-by-%u array " "(got %u-D %s-type %u-by-%u array)", me, airEnumStr(nrrdType, nrrdTypeFloat), AIR_CAST(unsigned int, vertNum), nrrd->dim, airEnumStr(nrrdType, nrrd->type), AIR_CAST(unsigned int, nrrd->axis[0].size), AIR_CAST(unsigned int, nrrd->axis[1].size)); airMopError(mop); return 1; } /* now copy the data */ memcpy(pld->xyzw, nrrd->data, nrrdElementSize(nrrd)*nrrdElementNumber(nrrd)); if (infoNum) { int wantType; unsigned int wantSize; void *data; for (infoIdx=0; infoIdxname, INFO_STR, line); return 1; } if (nrrdRead(nrrd, file, NULL)) { biffMovef(LIMN, NRRD, "%s: trouble reading %s %s data", me, INFO_STR, tmp); airMopError(mop); return 1; } switch (info) { case limnPolyDataInfoRGBA: wantType = nrrdTypeUChar; wantSize = 4; data = pld->rgba; break; case limnPolyDataInfoNorm: wantType = nrrdTypeFloat; wantSize = 3; data = pld->norm; break; case limnPolyDataInfoTex2: wantType = nrrdTypeFloat; wantSize = 2; data = pld->tex2; break; case limnPolyDataInfoTang: wantType = nrrdTypeFloat; wantSize = 3; data = pld->tang; break; default: biffAddf(LIMN, "%s: info %d (%s) not handled", me, info, airEnumStr(limnPolyDataInfo, info)); airMopError(mop); return 1; break; } if (!(wantType == nrrd->type && 2 == nrrd->dim && wantSize == nrrd->axis[0].size && vertNum == nrrd->axis[1].size)) { biffAddf(LIMN, "%s: didn't get 2-D %s-type %u-by-%u array " "(got %u-D %s-type %u-by-%u-by-? array)", me, airEnumStr(nrrdType, wantType), wantSize, AIR_CAST(unsigned int, vertNum), nrrd->dim, airEnumStr(nrrdType, nrrd->type), AIR_CAST(unsigned int, nrrd->axis[0].size), AIR_CAST(unsigned int, nrrd->axis[1].size)); airMopError(mop); return 1; } /* now copy the data */ memcpy(data, nrrd->data, nrrdElementSize(nrrd)*nrrdElementNumber(nrrd)); } } airMopOkay(mop); nrrdStateVerboseIO = hackhack; return 0; } int _limnHestPolyDataLMPDParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { static const char me[] = "_limnHestPolyDataLMPDParse"; char *nerr; limnPolyData **lpldP; airArray *mop; FILE *file; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } lpldP = (limnPolyData **)ptr; if (!strlen(str)) { /* got empty filename; user didn't really want data, that's okay*/ *lpldP = NULL; return 0; } mop = airMopNew(); if (!( file = airFopen(str, stdin, "rb") )) { sprintf(err, "%s: couldn't fopen(\"%s\",\"rb\"): %s", me, str, strerror(errno)); biffAdd(LIMN, err); airMopError(mop); return 1; } airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); *lpldP = limnPolyDataNew(); airMopAdd(mop, *lpldP, (airMopper)limnPolyDataNix, airMopOnError); if (limnPolyDataReadLMPD(*lpldP, file)) { airMopAdd(mop, nerr = biffGetDone(LIMN), airFree, airMopOnError); airStrcpy(err, AIR_STRLEN_HUGE, nerr); airMopError(mop); return 1; } airMopOkay(mop); return 0; } hestCB _limnHestPolyDataLMPD = { sizeof(limnPolyData *), "polydata", _limnHestPolyDataLMPDParse, (airMopper)limnPolyDataNix }; hestCB * limnHestPolyDataLMPD = &_limnHestPolyDataLMPD; int _limnHestPolyDataOFFParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { static const char me[] = "_limnHestPolyDataOFFParse"; char *nerr; limnPolyData **lpldP; airArray *mop; FILE *file; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } lpldP = (limnPolyData **)ptr; if (!strlen(str)) { /* got empty filename; user didn't really want data, that's okay*/ *lpldP = NULL; return 0; } mop = airMopNew(); if (!( file = airFopen(str, stdin, "rb") )) { sprintf(err, "%s: couldn't fopen(\"%s\",\"rb\"): %s", me, str, strerror(errno)); airMopError(mop); return 1; } airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); *lpldP = limnPolyDataNew(); airMopAdd(mop, *lpldP, (airMopper)limnPolyDataNix, airMopOnError); if (limnPolyDataReadOFF(*lpldP, file)) { airMopAdd(mop, nerr = biffGetDone(LIMN), airFree, airMopOnError); strncpy(err, nerr, AIR_STRLEN_HUGE-1); airMopError(mop); return 1; } airMopOkay(mop); return 0; } hestCB _limnHestPolyDataOFF = { sizeof(limnPolyData *), "polydata", _limnHestPolyDataOFFParse, (airMopper)limnPolyDataNix }; hestCB * limnHestPolyDataOFF = &_limnHestPolyDataOFF; int limnPolyDataWriteVTK(FILE *file, const limnPolyData *pld) { static const char me[]="limnPolyDataWriteVTK"; unsigned int pntIdx, prmIdx, *indx, idxNum; int linesOnly; if (!(file && pld)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } fprintf(file, "# vtk DataFile Version 2.0\n"); fprintf(file, "limnPolyData\n"); fprintf(file, "ASCII\n"); fprintf(file, "DATASET POLYDATA\n"); fprintf(file, "POINTS %u float\n", pld->xyzwNum); for (pntIdx=0; pntIdxxyzwNum; pntIdx++) { float xyz[3]; ELL_34V_HOMOG(xyz, pld->xyzw + 4*pntIdx); fprintf(file, "%f %f %f\n", xyz[0], xyz[1], xyz[2]); } fprintf(file, "\n"); /* first check if its only lines... */ linesOnly = AIR_TRUE; for (prmIdx=0; prmIdxprimNum; prmIdx++) { linesOnly &= (limnPrimitiveLineStrip == pld->type[prmIdx]); } if (linesOnly) { fprintf(file, "LINES %u %u\n", pld->primNum, pld->primNum + pld->indxNum); indx = pld->indx; for (prmIdx=0; prmIdxprimNum; prmIdx++) { unsigned int ii; idxNum = pld->icnt[prmIdx]; fprintf(file, "%u", idxNum); for (ii=0; iiindx; for (prmIdx=0; prmIdxprimNum; prmIdx++) { unsigned int triNum, triIdx; idxNum = pld->icnt[prmIdx]; switch (pld->type[prmIdx]) { case limnPrimitiveTriangleFan: biffAddf(LIMN, "%s: %s prims (prim[%u]) not supported in VTK?", me, airEnumStr(limnPrimitive, pld->type[prmIdx]), prmIdx); return 1; break; case limnPrimitiveQuads: case limnPrimitiveTriangleStrip: biffAddf(LIMN, "%s: sorry, saving %s prims (prim[%u]) not implemented", me, airEnumStr(limnPrimitive, pld->type[prmIdx]), prmIdx); return 1; break; case limnPrimitiveLineStrip: biffAddf(LIMN, "%s: confusion", me); return 1; break; case limnPrimitiveTriangles: triNum = idxNum/3; fprintf(file, "POLYGONS %u %u\n", triNum, triNum + idxNum); for (triIdx=0; triIdxtype[prmIdx]), prmIdx); return 1; break; } fprintf(file, "\n"); indx += idxNum; } } return 0; } /* ******** limnPolyDataReadOFF ** ** HEY: this has to be re-written with different allocation strategies ** if it is to support anything other than a single limnPrimitiveTriangles */ int limnPolyDataReadOFF(limnPolyData *pld, FILE *file) { static const char me[]="limnPolyDataReadOFF"; char line[AIR_STRLEN_LARGE]; /* HEY: bad Gordon */ unsigned int num[3], xyzwNum, xyzwGot, faceNum, faceGot, lineCount, got, lret; if (!( pld && file )) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } /* HEY: this just snarfs lines until it parses 3 uints, disregarding whether the "OFF" magic was actually there! */ got = 0; lineCount = 0; do { if (!airOneLine(file, line, AIR_STRLEN_LARGE)) { biffAddf(LIMN, "%s: hit EOF before getting #vert #face #edge line", me); return 1; } lineCount++; got = airParseStrUI(num, line, AIR_WHITESPACE, 3); } while (3 != got); xyzwNum = num[0]; faceNum = num[1]; /* allocate */ if (limnPolyDataAlloc(pld, 0 /* no extra info */, xyzwNum, 3*faceNum, 1)) { biffAddf(LIMN, "%s: couldn't allocate", me); return 1; } /* read all vertex information */ xyzwGot = 0; while (xyzwGot < xyzwNum) { float *xyzw; do { lret = airOneLine(file, line, AIR_STRLEN_LARGE); lineCount++; } while (1 == lret); if (!lret) { biffAddf(LIMN, "%s: (near line %d) hit EOF trying to read vert %d (of %d)", me, lineCount, xyzwGot, xyzwNum); return 1; } xyzw = pld->xyzw + 4*xyzwGot; if (3 != airParseStrF(xyzw, line, AIR_WHITESPACE, 3)) { biffAddf(LIMN, "%s: couldn't parse 3 floats from \"%s\" " "for vert %d (of %d)", me, line, xyzwGot, xyzwNum); return 1; } xyzw[3] = 1.0; xyzwGot++; } /* read face information */ faceGot = 0; while (faceGot < faceNum) { unsigned int *indx, indxSingle[4], indxNum; do { lret = airOneLine(file, line, AIR_STRLEN_LARGE); lineCount++; } while (1 == lret); if (!lret) { biffAddf(LIMN, "%s: (near line %d) hit EOF trying to read face %d (of %d)", me, lineCount, faceGot, faceNum); return 1; } if ('#' == line[0]) { /* its some kind of comment, either LIMN BEGIN PART or otherwise */ continue; } if (1 != sscanf(line, "%u", &indxNum)) { biffAddf(LIMN, "%s: (near line %d) can't get first uint " "(#verts) from \"%s\" for face %d (of %d)", me, lineCount, line, faceGot, faceNum); return 1; } if (3 != indxNum) { biffAddf(LIMN, "%s: sorry, can only handle triangles (not %u verts)", me, indxNum); return 1; } if (indxNum+1 != airParseStrUI(indxSingle, line, AIR_WHITESPACE, indxNum+1)) { biffAddf(LIMN, "%s: (near line %d) couldn't parse %d uints from \"%s\" " "for face %d (of %d)", me, lineCount, indxNum+1, line, faceGot, faceNum); return 1; } indx = pld->indx + 3*faceGot; ELL_3V_SET(indx, indxSingle[1], indxSingle[2], indxSingle[3]); /* for now ignoring the color information */ faceGot++; } /* set remaining info */ pld->type[0] = limnPrimitiveTriangles; pld->icnt[0] = 3*faceNum; return 0; } int limnPolyDataSave(const char *_fname, const limnPolyData *lpld) { static const char me[]="limnPolyDataSave"; char *fname; FILE *file; airArray *mop; int ret; if (!(_fname && lpld)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); if (!( file = airFopen(_fname, stdout, "wb") )) { biffAddf(LIMN, "%s: couldn't fopen(\"%s\",\"wb\"): %s", me, _fname, strerror(errno)); airMopError(mop); return 1; } airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); fname = airToLower(airStrdup(_fname)); airMopAdd(mop, fname, (airMopper)airFree, airMopAlways); if (airEndsWith(fname, ".vtk")) { ret = limnPolyDataWriteVTK(file, lpld); } else if (airEndsWith(fname, ".iv")) { ret = limnPolyDataWriteIV(file, lpld); } else { if (strcmp(_fname, "-") && !airEndsWith(fname, ".lmpd")) { fprintf(stderr, "%s: WARNING: unknown or no suffix on \"%s\"; " "using LMPD format", me, _fname); } ret = limnPolyDataWriteLMPD(file, lpld); } if (ret) { biffAddf(LIMN, "%s: trouble", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/limn/sources.cmake0000664000175000017500000000066511113047450020023 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(LIMN_SOURCES cam.c defaultsLimn.c enumsLimn.c envmap.c hestLimn.c io.c light.c limn.h methodsLimn.c obj.c polydata.c polyfilter.c polymod.c polyshapes.c privateLimn.h qn.c renderLimn.c shapes.c splineEval.c splineMethods.c splineMisc.c transform.c ) ADD_TEEM_LIBRARY(limn ${LIMN_SOURCES}) teem-1.11.0~svn6057/src/limn/qn.c0000664000175000017500000005264512165631065016136 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" /* ** "16simple": 16 bit representation based on ** ** ** info: [z sign] [y sign] [x sign] [y component] [x component] ** bits: 1 1 1 7 6 ** ** despite appearances, this is isotropic in X and Y */ void _limnQN16simple_QNtoV_f(float *vec, unsigned int qn) { int xi, yi; float x, y, z, n; xi = qn & 0x3F; yi = (qn >> 6) & 0x7F; if (xi + yi >= 127) { xi = 127 - xi; yi = 127 - yi; } x = xi/126.0f; y = yi/126.0f; z = 1.0f - x - y; x = (qn & 0x2000) ? -x : x; y = (qn & 0x4000) ? -y : y; z = (qn & 0x8000) ? -z : z; n = AIR_CAST(float, 1.0/sqrt(x*x + y*y + z*z)); vec[0] = x*n; vec[1] = y*n; vec[2] = z*n; } unsigned int _limnQN16simple_VtoQN_f(const float *vec) { float x, y, z, L, w; unsigned int sgn = 0, xi, yi; x = vec[0]; y = vec[1]; z = vec[2]; if (x < 0) { sgn |= 0x2000; x = -x; } if (y < 0) { sgn |= 0x4000; y = -y; } if (z < 0) { sgn |= 0x8000; z = -z; } L = x + y + z; if (!L) { return 0; } w = 126.0f/L; xi = AIR_CAST(unsigned int, x*w); yi = AIR_CAST(unsigned int, y*w); if (xi >= 64) { xi = 127 - xi; yi = 127 - yi; } return sgn | (yi << 6) | xi; } /* ---------------------------------------------------------------- */ void _limnQN16border1_QNtoV_f(float *vec, unsigned int qn) { unsigned int ui, vi; float u, v, x, y, z, n; ui = qn & 0xFF; vi = qn >> 8; u = AIR_CAST(float, AIR_AFFINE(0.5, ui, 254.5, -0.5, 0.5)); v = AIR_CAST(float, AIR_AFFINE(0.5, vi, 254.5, -0.5, 0.5)); x = u + v; y = u - v; z = 1 - AIR_ABS(x) - AIR_ABS(y); z *= AIR_CAST(int, ((ui&1) ^ (vi&1)) << 1) - 1; n = AIR_CAST(float, 1.0/sqrt(x*x + y*y + z*z)); vec[0] = x*n; vec[1] = y*n; vec[2] = z*n; } unsigned int _limnQN16border1_VtoQN_f(const float *vec) { float L, u, v, x, y, z; unsigned int ui, vi, zi; char me[]="limnQNVto16PB1"; x = vec[0]; y = vec[1]; z = vec[2]; L = AIR_ABS(x) + AIR_ABS(y) + AIR_ABS(z); if (!L) { return 0; } x /= L; y /= L; u = x + y; v = x - y; ui = airIndex(-1, u, 1, 254); ui++; vi = airIndex(-1, v, 1, 254); vi++; zi = (ui ^ vi) & 0x01; if (!zi && z > 1.0/128.0) { ui -= (((ui >> 7) & 0x01) << 1) - 1; } else if (zi && z < -1.0/128.0) { vi -= (((vi >> 7) & 0x01) << 1) - 1; } zi = (ui ^ vi) & 0x01; if (!zi && z > 1.0/127.0) { printf("%s: panic01\n", me); } else if (zi && z < -1.0/127.0) { printf("%s: panic02\n", me); } return (vi << 8) | ui; } /* ---------------------------------------------------------------- */ /* x = [ 1.0 1.0 ] u y [ 1.0 -1.0 ] v */ /* u = [ 0.5 0.5 ] x v [ 0.5 -0.5 ] y */ /* xor of low bits == 0 --> z<0 ; xor == 1 --> z>0 */ /* May 11 2003 GK (visually) verified that except at equator seam, the error due to quantization is unbiased */ #define _EVEN_CHECK_QtoV(HNB, TT, vec, qn) \ ui = (qn) & ((1<> HNB) & ((1< 0) { \ xi = airIndex(-1.0, x, 1.0, ((1<> HNB) & ((1< 0) { \ x += z; \ } else { \ x -= z; \ } \ if (y > 0) { \ y += z; \ } else { \ y -= z; \ } \ } \ n = 1.0/sqrt(x*x + y*y + z*z); \ (vec)[0] = AIR_CAST(TT, x*n); \ (vec)[1] = AIR_CAST(TT, y*n); \ (vec)[2] = AIR_CAST(TT, z*n) #define _EVEN_OCTA_VtoQ(HNB, vec) \ x = (vec)[0]; \ y = (vec)[1]; \ z = (vec)[2]; \ L = AIR_ABS(x) + AIR_ABS(y) + AIR_ABS(z); \ if (!L) { \ return 0; \ } \ x /= L; \ y /= L; \ z /= L; \ if (z < 0) { \ if (x > 0) { \ x -= z; \ } else { \ x += z; \ } \ if (y > 0) { \ y -= z; \ } else { \ y += z; \ } \ } \ xi = airIndex(-1, x, 1, (1<> 8) & 0xFF; \ u = AIR_AFFINE(0, ui, 255, -0.5, 0.5); \ v = AIR_AFFINE(0, vi, 255, -0.5, 0.5); \ x = u + v; \ y = u - v; \ z = 1 - AIR_ABS(x) - AIR_ABS(y); \ /* would this be better served by a branch? */ \ z *= (((ui ^ vi) & 0x01) << 1) - 1; \ n = 1.0/sqrt(x*x + y*y + z*z); \ (vec)[0] = x*n; \ (vec)[1] = y*n; \ (vec)[2] = z*n #define _16_CHECK_VtoQ(vec) \ x = (vec)[0]; \ y = (vec)[1]; \ z = (vec)[2]; \ L = AIR_ABS(x) + AIR_ABS(y) + AIR_ABS(z); \ if (!L) { \ return 0; \ } \ x /= L; \ y /= L; \ if (z > 0) { \ xi = airIndex(-1.0, x, 1.0, 255); \ yi = airIndex(-1.0 - 1.0/255, y, 1.0 + 1.0/255, 256); \ ui = xi + yi - 127; \ vi = xi - yi + 128; \ } \ else { \ xi = airIndex(-1.0 - 1.0/255, x, 1.0 + 1.0/255, 256); \ yi = airIndex(-1, y, 1, 255); \ ui = xi + yi - 127; \ vi = xi - yi + 127; \ } void _limnQN16checker_QNtoV_f(float *vec, unsigned int qn) { unsigned int ui, vi; double u, v, x, y, z, n; _EVEN_CHECK_QtoV(8, float, vec, qn); } void _limnQN16checker_QNtoV_d(double *vec, unsigned int qn) { unsigned int ui, vi; double u, v, x, y, z, n; _EVEN_CHECK_QtoV(8, double, vec, qn); } unsigned int _limnQN16checker_VtoQN_f(const float *vec) { double L, x, y, z; unsigned int xi, yi, ui, vi; _EVEN_CHECK_VtoQ(8, vec); } unsigned int _limnQN16checker_VtoQN_d(const double *vec) { double L, x, y, z; unsigned int xi, yi, ui, vi; _EVEN_CHECK_VtoQ(8, vec); } /* ---------------------------------------------------------------- */ void _limnQN16octa_QNtoV_f(float *vec, unsigned int qn) { unsigned int xi, yi; double x, y, z, n; _EVEN_OCTA_QtoV(8, float, vec, qn); } void _limnQN16octa_QNtoV_d(double *vec, unsigned int qn) { unsigned int xi, yi; double x, y, z, n; _EVEN_OCTA_QtoV(8, double, vec, qn); } unsigned int _limnQN16octa_VtoQN_f(const float *vec) { double L, x, y, z; unsigned int xi, yi; _EVEN_OCTA_VtoQ(8, vec); } unsigned int _limnQN16octa_VtoQN_d(const double *vec) { double L, x, y, z; unsigned int xi, yi; _EVEN_OCTA_VtoQ(8, vec); } /* ---------------------------------------------------------------- */ /* 15 bit --> HNB == 7 */ #define _ODD_OCTA_QtoV(HNB, TT, vec, qn) \ ui = qn & ((1<> HNB) & ((1<> (2*HNB)) & 0x01; \ u = AIR_CAST(TT, AIR_AFFINE(-0.5, ui, ((1< 0); \ return (zi << (2*HNB)) | (vi << HNB) | ui void _limnQN15octa_QNtoV_f(float *vec, unsigned int qn) { unsigned int ui, vi, zi; float u, v, x, y, z, n; _ODD_OCTA_QtoV(7, float, vec, qn); } void _limnQN15octa_QNtoV_d(double *vec, unsigned int qn) { unsigned int ui, vi, zi; double u, v, x, y, z, n; _ODD_OCTA_QtoV(7, double, vec, qn); } unsigned int _limnQN15octa_VtoQN_f(const float *vec) { float L, u, v, x, y, z; unsigned int ui, vi, zi; _ODD_OCTA_VtoQ(7, vec); } unsigned int _limnQN15octa_VtoQN_d(const double *vec) { double L, u, v, x, y, z; unsigned int ui, vi, zi; _ODD_OCTA_VtoQ(7, vec); } /* ----------------------------------------------------------- */ void _limnQN14checker_QNtoV_f(float *vec, unsigned int qn) { unsigned int ui, vi; double u, v, x, y, z, n; _EVEN_CHECK_QtoV(7, float, vec, qn); } void _limnQN14checker_QNtoV_d(double *vec, unsigned int qn) { unsigned int ui, vi; double u, v, x, y, z, n; _EVEN_CHECK_QtoV(7, double, vec, qn); } unsigned int _limnQN14checker_VtoQN_f(const float *vec) { double L, x, y, z; unsigned int xi, yi, ui, vi; _EVEN_CHECK_VtoQ(7, vec); } unsigned int _limnQN14checker_VtoQN_d(const double *vec) { double L, x, y, z; unsigned int xi, yi, ui, vi; _EVEN_CHECK_VtoQ(7, vec); } /* ---------------------------------------------------------------- */ void _limnQN14octa_QNtoV_f(float *vec, unsigned int qn) { unsigned int xi, yi; double x, y, z, n; _EVEN_OCTA_QtoV(7, float, vec, qn); } void _limnQN14octa_QNtoV_d(double *vec, unsigned int qn) { unsigned int xi, yi; double x, y, z, n; _EVEN_OCTA_QtoV(7, double, vec, qn); } unsigned int _limnQN14octa_VtoQN_f(const float *vec) { double L, x, y, z; unsigned int xi, yi; _EVEN_OCTA_VtoQ(7, vec); } unsigned int _limnQN14octa_VtoQN_d(const double *vec) { double L, x, y, z; unsigned int xi, yi; _EVEN_OCTA_VtoQ(7, vec); } /* ----------------------------------------------------------- */ void _limnQN13octa_QNtoV_f(float *vec, unsigned int qn) { unsigned int ui, vi, zi; float u, v, x, y, z, n; _ODD_OCTA_QtoV(6, float, vec, qn); } void _limnQN13octa_QNtoV_d(double *vec, unsigned int qn) { unsigned int ui, vi, zi; double u, v, x, y, z, n; _ODD_OCTA_QtoV(6, double, vec, qn); } unsigned int _limnQN13octa_VtoQN_f(const float *vec) { float L, u, v, x, y, z; unsigned int ui, vi, zi; _ODD_OCTA_VtoQ(6, vec); } unsigned int _limnQN13octa_VtoQN_d(const double *vec) { double L, u, v, x, y, z; unsigned int ui, vi, zi; _ODD_OCTA_VtoQ(6, vec); } /* ----------------------------------------------------------- */ void _limnQN12checker_QNtoV_f(float *vec, unsigned int qn) { unsigned int ui, vi; double u, v, x, y, z, n; _EVEN_CHECK_QtoV(6, float, vec, qn); } void _limnQN12checker_QNtoV_d(double *vec, unsigned int qn) { unsigned int ui, vi; double u, v, x, y, z, n; _EVEN_CHECK_QtoV(6, double, vec, qn); } unsigned int _limnQN12checker_VtoQN_f(const float *vec) { double L, x, y, z; unsigned int xi, yi, ui, vi; _EVEN_CHECK_VtoQ(6, vec); } unsigned int _limnQN12checker_VtoQN_d(const double *vec) { double L, x, y, z; unsigned int xi, yi, ui, vi; _EVEN_CHECK_VtoQ(6, vec); } /* ---------------------------------------------------------------- */ void _limnQN12octa_QNtoV_f(float *vec, unsigned int qn) { unsigned int xi, yi; double x, y, z, n; _EVEN_OCTA_QtoV(6, float, vec, qn); } void _limnQN12octa_QNtoV_d(double *vec, unsigned int qn) { unsigned int xi, yi; double x, y, z, n; _EVEN_OCTA_QtoV(6, double, vec, qn); } unsigned int _limnQN12octa_VtoQN_f(const float *vec) { double L, x, y, z; unsigned int xi, yi; _EVEN_OCTA_VtoQ(6, vec); } unsigned int _limnQN12octa_VtoQN_d(const double *vec) { double L, x, y, z; unsigned int xi, yi; _EVEN_OCTA_VtoQ(6, vec); } /* ----------------------------------------------------------- */ void _limnQN11octa_QNtoV_f(float *vec, unsigned int qn) { unsigned int ui, vi, zi; float u, v, x, y, z, n; _ODD_OCTA_QtoV(5, float, vec, qn); } void _limnQN11octa_QNtoV_d(double *vec, unsigned int qn) { unsigned int ui, vi, zi; double u, v, x, y, z, n; _ODD_OCTA_QtoV(5, double, vec, qn); } unsigned int _limnQN11octa_VtoQN_f(const float *vec) { float L, u, v, x, y, z; unsigned int ui, vi, zi; _ODD_OCTA_VtoQ(5, vec); } unsigned int _limnQN11octa_VtoQN_d(const double *vec) { double L, u, v, x, y, z; unsigned int ui, vi, zi; _ODD_OCTA_VtoQ(5, vec); } /* ----------------------------------------------------------- */ void _limnQN10checker_QNtoV_f(float *vec, unsigned int qn) { unsigned int ui, vi; double u, v, x, y, z, n; _EVEN_CHECK_QtoV(5, float, vec, qn); } void _limnQN10checker_QNtoV_d(double *vec, unsigned int qn) { unsigned int ui, vi; double u, v, x, y, z, n; _EVEN_CHECK_QtoV(5, double, vec, qn); } unsigned int _limnQN10checker_VtoQN_f(const float *vec) { double L, x, y, z; unsigned int xi, yi, ui, vi; _EVEN_CHECK_VtoQ(5, vec); } unsigned int _limnQN10checker_VtoQN_d(const double *vec) { double L, x, y, z; unsigned int xi, yi, ui, vi; _EVEN_CHECK_VtoQ(5, vec); } /* ---------------------------------------------------------------- */ void _limnQN10octa_QNtoV_f(float *vec, unsigned int qn) { unsigned int xi, yi; double x, y, z, n; _EVEN_OCTA_QtoV(5, float, vec, qn); } void _limnQN10octa_QNtoV_d(double *vec, unsigned int qn) { unsigned int xi, yi; double x, y, z, n; _EVEN_OCTA_QtoV(5, double, vec, qn); } unsigned int _limnQN10octa_VtoQN_f(const float *vec) { double L, x, y, z; unsigned int xi, yi; _EVEN_OCTA_VtoQ(5, vec); } unsigned int _limnQN10octa_VtoQN_d(const double *vec) { double L, x, y, z; unsigned int xi, yi; _EVEN_OCTA_VtoQ(5, vec); } /* ----------------------------------------------------------- */ void _limnQN9octa_QNtoV_f(float *vec, unsigned int qn) { unsigned int ui, vi, zi; float u, v, x, y, z, n; _ODD_OCTA_QtoV(4, float, vec, qn); } void _limnQN9octa_QNtoV_d(double *vec, unsigned int qn) { unsigned int ui, vi, zi; double u, v, x, y, z, n; _ODD_OCTA_QtoV(4, double, vec, qn); } unsigned int _limnQN9octa_VtoQN_f(const float *vec) { float L, u, v, x, y, z; unsigned int ui, vi, zi; _ODD_OCTA_VtoQ(4, vec); } unsigned int _limnQN9octa_VtoQN_d(const double *vec) { double L, u, v, x, y, z; unsigned int ui, vi, zi; _ODD_OCTA_VtoQ(4, vec); } /* ----------------------------------------------------------- */ void _limnQN8checker_QNtoV_f(float *vec, unsigned int qn) { unsigned int ui, vi; double u, v, x, y, z, n; _EVEN_CHECK_QtoV(4, float, vec, qn); } void _limnQN8checker_QNtoV_d(double *vec, unsigned int qn) { unsigned int ui, vi; double u, v, x, y, z, n; _EVEN_CHECK_QtoV(4, double, vec, qn); } unsigned int _limnQN8checker_VtoQN_f(const float *vec) { double L, x, y, z; unsigned int xi, yi, ui, vi; _EVEN_CHECK_VtoQ(4, vec); } unsigned int _limnQN8checker_VtoQN_d(const double *vec) { double L, x, y, z; unsigned int xi, yi, ui, vi; _EVEN_CHECK_VtoQ(4, vec); } /* ---------------------------------------------------------------- */ void _limnQN8octa_QNtoV_f(float *vec, unsigned int qn) { unsigned int xi, yi; double x, y, z, n; _EVEN_OCTA_QtoV(4, float, vec, qn); } void _limnQN8octa_QNtoV_d(double *vec, unsigned int qn) { unsigned int xi, yi; double x, y, z, n; _EVEN_OCTA_QtoV(4, double, vec, qn); } unsigned int _limnQN8octa_VtoQN_f(const float *vec) { double L, x, y, z; unsigned int xi, yi; _EVEN_OCTA_VtoQ(4, vec); } unsigned int _limnQN8octa_VtoQN_d(const double *vec) { double L, x, y, z; unsigned int xi, yi; _EVEN_OCTA_VtoQ(4, vec); } /* ----------------------------------------------------------- */ void (* limnQNtoV_f[LIMN_QN_MAX+1])(float *, unsigned int) = { NULL, _limnQN16simple_QNtoV_f, _limnQN16border1_QNtoV_f, _limnQN16checker_QNtoV_f, _limnQN16octa_QNtoV_f, _limnQN15octa_QNtoV_f, _limnQN14checker_QNtoV_f, _limnQN14octa_QNtoV_f, _limnQN13octa_QNtoV_f, _limnQN12checker_QNtoV_f, _limnQN12octa_QNtoV_f, _limnQN11octa_QNtoV_f, _limnQN10checker_QNtoV_f, _limnQN10octa_QNtoV_f, _limnQN9octa_QNtoV_f, _limnQN8checker_QNtoV_f, _limnQN8octa_QNtoV_f }; void (* limnQNtoV_d[LIMN_QN_MAX+1])(double *, unsigned int) = { NULL, NULL, NULL, _limnQN16checker_QNtoV_d, _limnQN16octa_QNtoV_d, _limnQN15octa_QNtoV_d, _limnQN14checker_QNtoV_d, _limnQN14octa_QNtoV_d, _limnQN13octa_QNtoV_d, _limnQN12checker_QNtoV_d, _limnQN12octa_QNtoV_d, _limnQN11octa_QNtoV_d, _limnQN10checker_QNtoV_d, _limnQN10octa_QNtoV_d, _limnQN9octa_QNtoV_d, _limnQN8checker_QNtoV_d, _limnQN8octa_QNtoV_d }; unsigned int (* limnVtoQN_f[LIMN_QN_MAX+1])(const float *vec) = { NULL, _limnQN16simple_VtoQN_f, _limnQN16border1_VtoQN_f, _limnQN16checker_VtoQN_f, _limnQN16octa_VtoQN_f, _limnQN15octa_VtoQN_f, _limnQN14checker_VtoQN_f, _limnQN14octa_VtoQN_f, _limnQN13octa_VtoQN_f, _limnQN12checker_VtoQN_f, _limnQN12octa_VtoQN_f, _limnQN11octa_VtoQN_f, _limnQN10checker_VtoQN_f, _limnQN10octa_VtoQN_f, _limnQN9octa_VtoQN_f, _limnQN8checker_VtoQN_f, _limnQN8octa_VtoQN_f }; unsigned int (* limnVtoQN_d[LIMN_QN_MAX+1])(const double *vec) = { NULL, NULL, NULL, _limnQN16checker_VtoQN_d, _limnQN16octa_VtoQN_d, _limnQN15octa_VtoQN_d, _limnQN14checker_VtoQN_d, _limnQN14octa_VtoQN_d, _limnQN13octa_VtoQN_d, _limnQN12checker_VtoQN_d, _limnQN12octa_VtoQN_d, _limnQN11octa_VtoQN_d, _limnQN10checker_VtoQN_d, _limnQN10octa_VtoQN_d, _limnQN9octa_VtoQN_d, _limnQN8checker_VtoQN_d, _limnQN8octa_VtoQN_d }; unsigned int limnQNBins[LIMN_QN_MAX+1] = { 0, (1 << 16), (1 << 16), (1 << 16), (1 << 16), (1 << 15), (1 << 14), (1 << 14), (1 << 13), (1 << 12), (1 << 12), (1 << 11), (1 << 10), (1 << 10), (1 << 9), (1 << 8), (1 << 8) }; /* ** can use via test/tqn: foreach Q ( 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 ) echo $Q test/tqn -s 400 -q $Q \ | unu tile -a 2 0 1 -s 2 3 \ | unu tile -a 2 0 1 -s 2 1 | unu quantize -b 8 -o q${Q}.png end */ int limnQNDemo(Nrrd *nqn, unsigned int reso, int qni) { static const char me[]="limnQNDemo"; unsigned int ui, vi, oi; double *qdata, ll, uu, vv, ww, vecd[3], unqd[3]; float vecf[3], unqf[3]; if (!nqn) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (nrrdMaybeAlloc_va(nqn, nrrdTypeDouble, 4, AIR_CAST(size_t, reso), AIR_CAST(size_t, reso), AIR_CAST(size_t, 6), AIR_CAST(size_t, 2))){ biffMovef(LIMN, NRRD, "%s: couldn't alloc output", me); return 1; } if (!(limnQNUnknown < qni && qni < limnQNLast)) { biffAddf(LIMN, "%s: qni %d not in valid range [%d,%d]", me, qni, limnQNUnknown+1, limnQNLast-1); return 1; } qdata = AIR_CAST(double *, nqn->data); for (oi=0; oi<6; oi++) { for (vi=0; viprimNum; primIdx++) { unsigned int tvni, tini; if (endFacet) { tvni = tubeFacet*(2*endFacet + pldIn->icnt[primIdx]) + 1; tini = 2*tubeFacet*(2*endFacet + pldIn->icnt[primIdx] + 1) -2; } else { tvni = tubeFacet*(2 + pldIn->icnt[primIdx]); tini = 2*tubeFacet*(1 + pldIn->icnt[primIdx]); } tubeVertNum += tvni; tubeIndxNum += tini; } if (limnPolyDataAlloc(pldOut, /* sorry have to have normals, even if they weren't asked for, because currently they're used as part of vertex position calc */ (infoBitFlag | (1 << limnPolyDataInfoNorm)), tubeVertNum, tubeIndxNum, pldIn->primNum)) { biffAddf(LIMN, "%s: trouble allocating output", me); return 1; } if (nvertmap) { if (nrrdMaybeAlloc_va(nvertmap, nrrdTypeUInt, 1, AIR_CAST(size_t, tubeVertNum))) { biffMovef(LIMN, NRRD, "%s: trouble allocating vert map", me); return 1; } vertmap = AIR_CAST(unsigned int *, nvertmap->data); } else { vertmap = NULL; } color = ((infoBitFlag & (1 << limnPolyDataInfoRGBA)) && (limnPolyDataInfoBitFlag(pldIn) & (1 << limnPolyDataInfoRGBA))); mop = airMopNew(); cost = AIR_CAST(double *, calloc(tubeFacet, sizeof(double))); sint = AIR_CAST(double *, calloc(tubeFacet, sizeof(double))); airMopAdd(mop, cost, airFree, airMopAlways); airMopAdd(mop, sint, airFree, airMopAlways); if (!(cost && sint)) { biffAddf(LIMN, "%s: couldn't allocate lookup tables", me); airMopError(mop); return 1; } for (pi=0; piprimNum; primIdx++) { unsigned int inVertIdx; pldOut->type[primIdx] = limnPrimitiveTriangleStrip; if (endFacet) { pldOut->icnt[primIdx] = 2*tubeFacet*(2*endFacet + pldIn->icnt[primIdx] + 1) - 2; } else { pldOut->icnt[primIdx] = 2*tubeFacet*(1 + pldIn->icnt[primIdx]); } for (inVertIdx=0; inVertIdxicnt[primIdx]; inVertIdx++) { unsigned int forwIdx, backIdx, tubeEndIdx; double tang[3], tmp, scl, step, perp[3], pimp[3]; /* inVrt = pldIn->vert + pldIn->indx[inVertTotalIdx]; */ if (0 == inVertIdx) { forwIdx = inVertTotalIdx+1; backIdx = inVertTotalIdx; scl = 1; } else if (pldIn->icnt[primIdx]-1 == inVertIdx) { forwIdx = inVertTotalIdx; backIdx = inVertTotalIdx-1; scl = 1; } else { forwIdx = inVertTotalIdx+1; backIdx = inVertTotalIdx-1; scl = 0.5; } if (1 == pldIn->icnt[primIdx]) { ELL_3V_SET(tang, 0, 0, 1); /* completely arbitrary, as it must be */ step = 0; } else { ELL_3V_SUB(tang, pldIn->xyzw + 4*forwIdx, pldIn->xyzw + 4*backIdx); ELL_3V_NORM(tang, tang, step); step *= scl; } if (0 == inVertIdx || 1 == pldIn->icnt[primIdx]) { ell_3v_perp_d(perp, tang); } else { /* transport last perp forwards */ double dot; dot = ELL_3V_DOT(perp, tang); ELL_3V_SCALE_ADD2(perp, 1.0, perp, -dot, tang); } ELL_3V_NORM(perp, perp, tmp); ELL_3V_CROSS(pimp, perp, tang); /* (perp, pimp, tang) is a left-handed frame, on purpose */ /* limnVrt *outVrt; */ /* -------------------------------------- BEGIN initial endcap */ if (0 == inVertIdx) { unsigned int startIdx, ei; startIdx = outVertTotalIdx; if (endFacet) { for (ei=0; einorm + 3*outVertTotalIdx, float, -cosph, tang, costh*sinph, perp, sinth*sinph, pimp); ELL_3V_SCALE_ADD3_TT(pldOut->xyzw + 4*outVertTotalIdx, float, 1, pldIn->xyzw + 4*inVertTotalIdx, -step/2, tang, radius, pldOut->norm + 3*outVertTotalIdx); (pldOut->xyzw + 4*outVertTotalIdx)[3] = 1.0; if (vertmap) { vertmap[outVertTotalIdx] = inVertTotalIdx; } if (color) { ELL_4V_COPY(pldOut->rgba + 4*outVertTotalIdx, pldIn->rgba + 4*inVertTotalIdx); } outVertTotalIdx++; } } for (pi=1; piindx[outIndxIdx++] = startIdx; pldOut->indx[outIndxIdx++] = startIdx + pi; } for (ei=0; eiindx[outIndxIdx++] = (startIdx + pi + (ei + 0)*tubeFacet); pldOut->indx[outIndxIdx++] = (startIdx + pi + (ei + 1)*tubeFacet); } } } else { /* no endcap, open tube */ for (pi=0; pinorm + 3*outVertTotalIdx, float, costh, perp, sinth, pimp); ELL_3V_SCALE_ADD3_TT(pldOut->xyzw + 4*outVertTotalIdx, float, 1, pldIn->xyzw + 4*inVertTotalIdx, -step/2 + step/(2*tubeFacet), tang, radius, pldOut->norm + 3*outVertTotalIdx); (pldOut->xyzw + 4*outVertTotalIdx)[3] = 1.0; if (vertmap) { vertmap[outVertTotalIdx] = inVertTotalIdx; } if (color) { ELL_4V_COPY(pldOut->rgba + 4*outVertTotalIdx, pldIn->rgba + 4*inVertTotalIdx); } outVertTotalIdx++; } for (pi=0; piindx[outIndxIdx++] = (startIdx + pi + 0*tubeFacet); pldOut->indx[outIndxIdx++] = (startIdx + pi + 1*tubeFacet); } } } /* if (0 == inVertIdx) */ /* -------------------------------------- END initial endcap */ for (pi=0; pivert + outVertTotalIdx; */ ELL_3V_SCALE_ADD2_TT(pldOut->norm + 3*outVertTotalIdx, float, cosa, perp, sina, pimp); ELL_3V_SCALE_ADD3_TT(pldOut->xyzw + 4*outVertTotalIdx, float, 1, pldIn->xyzw + 4*inVertTotalIdx, radius, pldOut->norm + 3*outVertTotalIdx, shift, tang); (pldOut->xyzw + 4*outVertTotalIdx)[3] = 1.0; pldOut->indx[outIndxIdx++] = outVertTotalIdx; pldOut->indx[outIndxIdx++] = outVertTotalIdx + tubeFacet; if (vertmap) { vertmap[outVertTotalIdx] = inVertTotalIdx; } if (color) { ELL_4V_COPY(pldOut->rgba + 4*outVertTotalIdx, pldIn->rgba + 4*inVertTotalIdx); } outVertTotalIdx++; } tubeEndIdx = outVertTotalIdx; /* -------------------------------------- BEGIN final endcap */ if (inVertIdx == pldIn->icnt[primIdx]-1) { unsigned int ei; if (endFacet) { for (ei=0; eivert + outVertTotalIdx; */ ELL_3V_SCALE_ADD3_TT(pldOut->norm + 3*outVertTotalIdx, float, -cosph, tang, costh*sinph, perp, sinth*sinph, pimp); ELL_3V_SCALE_ADD3_TT(pldOut->xyzw + 4*outVertTotalIdx, float, 1, pldIn->xyzw + 4*inVertTotalIdx, step/2, tang, radius, pldOut->norm + 3*outVertTotalIdx); (pldOut->xyzw + 4*outVertTotalIdx)[3] = 1.0; if (vertmap) { vertmap[outVertTotalIdx] = inVertTotalIdx; } if (color) { ELL_4V_COPY(pldOut->rgba + 4*outVertTotalIdx, pldIn->rgba + 4*inVertTotalIdx); } outVertTotalIdx++; } } /* outVrt = pldOut->vert + outVertTotalIdx; */ ELL_3V_COPY_TT(pldOut->norm + 3*outVertTotalIdx, float, tang); ELL_3V_SCALE_ADD3_TT(pldOut->xyzw + 4*outVertTotalIdx, float, 1, pldIn->xyzw + 4*inVertTotalIdx, step/2, tang, radius, pldOut->norm + 3*outVertTotalIdx); (pldOut->xyzw + 4*outVertTotalIdx)[3] = 1.0; if (vertmap) { vertmap[outVertTotalIdx] = inVertTotalIdx; } outVertTotalIdx++; for (ei=0; eiindx[outIndxIdx++] = (tubeEndIdx + pi + (ei + 0)*tubeFacet); pldOut->indx[outIndxIdx++] = (tubeEndIdx + pi + (ei + 1)*tubeFacet); } } for (pi=0; piindx[outIndxIdx++] = (tubeEndIdx + pi + (endFacet - 1)*tubeFacet); pldOut->indx[outIndxIdx++] = (tubeEndIdx + (endFacet - 0)*tubeFacet); } } else { /* no endcap, open tube */ for (pi=0; pinorm + 3*outVertTotalIdx, float, costh, perp, sinth, pimp); ELL_3V_SCALE_ADD3_TT(pldOut->xyzw + 4*outVertTotalIdx, float, 1, pldIn->xyzw + 4*inVertTotalIdx, step/2 - step/(2*tubeFacet), tang, radius, pldOut->norm + 3*outVertTotalIdx); (pldOut->xyzw + 4*outVertTotalIdx)[3] = 1.0; if (vertmap) { vertmap[outVertTotalIdx] = inVertTotalIdx; } if (color) { ELL_4V_COPY(pldOut->rgba + 4*outVertTotalIdx, pldIn->rgba + 4*inVertTotalIdx); } outVertTotalIdx++; } } } /* if (inVertIdx == pldIn->icnt[primIdx]-1) */ /* -------------------------------------- END final endcap */ inVertTotalIdx++; } } airMopOkay(mop); return 0; } /* Straightforward implementation of Laplacian+HC mesh smoothing, as * described by Vollmer et al., Improved Laplacian Smoothing of Noisy * Surface Meshes, Eurographics/CGF 18(3), 1999 * * pld is the polydata you want smoothed * neighbors / idx is from limnPolyDataNeighborArrayComp * alpha / beta are parameters of the smoothing (try a=0,b=0.5) * iter is the number of iterations you want to run (try iter=10) * * Returns -1 and leaves a message on biff upon error. */ int limnPolyDataSmoothHC(limnPolyData *pld, int *neighbors, int *idx, double alpha, double beta, int iter) { static const char me[]="limnPolyDataSmoothHC"; float *orig, *in, *out, *b; unsigned int v; int i, nb; airArray *mop; mop=airMopNew(); if (pld==NULL || neighbors==NULL || idx==NULL) { biffAddf(LIMN, "%s: got NULL pointer", me); airMopError(mop); return -1; } if (alpha<0 || alpha>1 || beta<0 || beta>1) { biffAddf(LIMN, "%s: alpha/beta outside parameter range [0,1]", me); airMopError(mop); return -1; } orig = in = pld->xyzw; out = (float*) malloc(sizeof(float)*4*pld->xyzwNum); if (out==NULL) { biffAddf(LIMN, "%s: couldn't allocate output buffer", me); airMopError(mop); return -1; } airMopAdd(mop, out, airFree, airMopOnError); b = (float*) malloc(sizeof(float)*4*pld->xyzwNum); if (b==NULL) { biffAddf(LIMN, "%s: couldn't allocate buffer b", me); airMopError(mop); return -1; } airMopAdd(mop, b, airFree, airMopAlways); for (i=0; ixyzwNum; v++) { int p=4*v; if (idx[v]==idx[v+1]) { ELL_4V_COPY(out+p, in+p); } else { ELL_4V_SET(out+p,0,0,0,0); for (nb=idx[v]; nbxyzwNum; v++) { int p=4*v; if (idx[v]1) { in = out; out = (float*) malloc(sizeof(float)*4*pld->xyzwNum); } else { /* swap */ float *tmp = in; in = out; out = tmp; } } if (iter>1) airFree(out); airFree(pld->xyzw); pld->xyzw = in; airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/limn/lpu_meas.c0000664000175000017500000000503612173673666017331 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" #include "privateLimn.h" #define INFO "Measures something about each primitive" static const char *myinfo = (INFO ". Actually all it can measure is area at this point..."); int limnpu_measMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *hopt = NULL; char *err, *perr; airArray *mop; int pret; limnPolyData *pld; Nrrd *nout; char *out; hestOptAdd(&hopt, NULL, "input", airTypeOther, 1, 1, &pld, NULL, "input polydata filename", NULL, NULL, limnHestPolyDataLMPD); hestOptAdd(&hopt, NULL, "output", airTypeString, 1, 1, &out, NULL, "output nrrd filename"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(myinfo); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (limnPolyDataPrimitiveArea(nout, pld)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:%s", me, err); airMopError(mop); return 1; } if (nrrdSave(out, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } unrrduCmd limnpu_measCmd = { "meas", INFO, limnpu_measMain, AIR_FALSE }; teem-1.11.0~svn6057/src/limn/polydata.c0000664000175000017500000005103112165631065017321 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah Copyright (C) 2011 Thomas Schultz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" limnPolyData * limnPolyDataNew(void) { limnPolyData *pld; pld = AIR_CALLOC(1, limnPolyData); if (pld) { pld->xyzw = NULL; pld->xyzwNum = 0; pld->rgba = NULL; pld->rgbaNum = 0; pld->norm = NULL; pld->normNum = 0; pld->tex2 = NULL; pld->tex2Num = 0; pld->tang = NULL; pld->tangNum = 0; pld->indx = NULL; pld->indxNum = 0; pld->primNum = 0; pld->type = NULL; pld->icnt = NULL; } return pld; } limnPolyData * limnPolyDataNix(limnPolyData *pld) { if (pld) { airFree(pld->xyzw); airFree(pld->rgba); airFree(pld->norm); airFree(pld->tex2); airFree(pld->tang); airFree(pld->indx); airFree(pld->type); airFree(pld->icnt); } airFree(pld); return NULL; } /* ** doesn't set pld->xyzwNum, only the per-attribute xxxNum variables */ int _limnPolyDataInfoAlloc(limnPolyData *pld, unsigned int infoBitFlag, unsigned int vertNum) { static const char me[]="_limnPolyDataInfoAlloc"; if (vertNum != pld->rgbaNum && ((1 << limnPolyDataInfoRGBA) & infoBitFlag)) { pld->rgba = (unsigned char *)airFree(pld->rgba); if (vertNum) { pld->rgba = AIR_CALLOC(vertNum*4, unsigned char); if (!pld->rgba) { biffAddf(LIMN, "%s: couldn't allocate %u rgba", me, vertNum); return 1; } } pld->rgbaNum = vertNum; } if (vertNum != pld->normNum && ((1 << limnPolyDataInfoNorm) & infoBitFlag)) { pld->norm = (float *)airFree(pld->norm); if (vertNum) { pld->norm = AIR_CALLOC(vertNum*4, float); if (!pld->norm) { biffAddf(LIMN, "%s: couldn't allocate %u norm", me, vertNum); return 1; } } pld->normNum = vertNum; } if (vertNum != pld->tex2Num && ((1 << limnPolyDataInfoTex2) & infoBitFlag)) { pld->tex2 = (float *)airFree(pld->tex2); if (vertNum) { pld->tex2 = AIR_CALLOC(vertNum*2, float); if (!pld->tex2) { biffAddf(LIMN, "%s: couldn't allocate %u tex2", me, vertNum); return 1; } } pld->tex2Num = vertNum; } if (vertNum != pld->tangNum && ((1 << limnPolyDataInfoTang) & infoBitFlag)) { pld->tang = (float *)airFree(pld->tang); if (vertNum) { pld->tang = AIR_CALLOC(vertNum*3, float); if (!pld->tang) { biffAddf(LIMN, "%s: couldn't allocate %u tang", me, vertNum); return 1; } } pld->tangNum = vertNum; } return 0; } unsigned int limnPolyDataInfoBitFlag(const limnPolyData *pld) { unsigned int ret; ret = 0; if (pld) { if (pld->rgba && pld->rgbaNum == pld->xyzwNum) { ret |= (1 << limnPolyDataInfoRGBA); } if (pld->norm && pld->normNum == pld->xyzwNum) { ret |= (1 << limnPolyDataInfoNorm); } if (pld->tex2 && pld->tex2Num == pld->xyzwNum) { ret |= (1 << limnPolyDataInfoTex2); } if (pld->tang && pld->tangNum == pld->xyzwNum) { ret |= (1 << limnPolyDataInfoTang); } } return ret; } int limnPolyDataAlloc(limnPolyData *pld, unsigned int infoBitFlag, unsigned int vertNum, unsigned int indxNum, unsigned int primNum) { static const char me[]="limnPolyDataAlloc"; if (!pld) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (vertNum != pld->xyzwNum) { pld->xyzw = (float *)airFree(pld->xyzw); if (vertNum) { pld->xyzw = AIR_CALLOC(vertNum*4, float); if (!pld->xyzw) { biffAddf(LIMN, "%s: couldn't allocate %u xyzw", me, vertNum); return 1; } } pld->xyzwNum = vertNum; } if (_limnPolyDataInfoAlloc(pld, infoBitFlag, vertNum)) { biffAddf(LIMN, "%s: couldn't allocate info", me); return 1; } if (indxNum != pld->indxNum) { pld->indx = (unsigned int *)airFree(pld->indx); if (indxNum) { pld->indx = AIR_CALLOC(indxNum, unsigned int); if (!pld->indx) { biffAddf(LIMN, "%s: couldn't allocate %u indices", me, indxNum); return 1; } } pld->indxNum = indxNum; } if (primNum != pld->primNum) { pld->type = (unsigned char *)airFree(pld->type); pld->icnt = (unsigned int *)airFree(pld->icnt); if (primNum) { pld->type = AIR_CALLOC(primNum, unsigned char); pld->icnt = AIR_CALLOC(primNum, unsigned int); if (!(pld->type && pld->icnt)) { biffAddf(LIMN, "%s: couldn't allocate %u primitives", me, primNum); return 1; } } pld->primNum = primNum; } return 0; } size_t limnPolyDataSize(const limnPolyData *pld) { size_t ret = 0; if (pld) { ret += pld->xyzwNum*sizeof(float)*4; if (pld->rgba) { ret += pld->rgbaNum*sizeof(unsigned char)*4; } if (pld->norm) { ret += pld->normNum*sizeof(float)*3; } if (pld->tex2) { ret += pld->tex2Num*sizeof(float)*2; } if (pld->tang) { ret += pld->tangNum*sizeof(float)*3; } ret += pld->indxNum*sizeof(unsigned int); ret += pld->primNum*sizeof(signed char); ret += pld->primNum*sizeof(unsigned int); } return ret; } int limnPolyDataCopy(limnPolyData *pldB, const limnPolyData *pldA) { static const char me[]="limnPolyDataCopy"; if (!( pldB && pldA )) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (limnPolyDataAlloc(pldB, limnPolyDataInfoBitFlag(pldA), pldA->xyzwNum, pldA->indxNum, pldA->primNum)) { biffAddf(LIMN, "%s: couldn't allocate output", me); return 1; } memcpy(pldB->xyzw, pldA->xyzw, pldA->xyzwNum*sizeof(float)*4); if (pldA->rgba) { memcpy(pldB->rgba, pldA->rgba, pldA->rgbaNum*sizeof(unsigned char)*4); } if (pldA->norm) { memcpy(pldB->norm, pldA->norm, pldA->normNum*sizeof(float)*3); } if (pldA->tex2) { memcpy(pldB->tex2, pldA->tex2, pldA->tex2Num*sizeof(float)*2); } if (pldA->tang) { memcpy(pldB->tang, pldA->tang, pldA->tangNum*sizeof(float)*3); } memcpy(pldB->indx, pldA->indx, pldA->indxNum*sizeof(unsigned int)); memcpy(pldB->type, pldA->type, pldA->primNum*sizeof(signed char)); memcpy(pldB->icnt, pldA->icnt, pldA->primNum*sizeof(unsigned int)); return 0; } int limnPolyDataCopyN(limnPolyData *pldB, const limnPolyData *pldA, unsigned int num) { static const char me[]="limnPolyDataCopyN"; unsigned int ii, jj, size; if (!( pldB && pldA )) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (limnPolyDataAlloc(pldB, limnPolyDataInfoBitFlag(pldA), num*pldA->xyzwNum, num*pldA->indxNum, num*pldA->primNum)) { biffAddf(LIMN, "%s: couldn't allocate output", me); return 1; } for (ii=0; iixyzwNum*4; /* char *_beg = (char *)(pldB->xyzw + ii*size); char *_end = _beg + size - 1; fprintf(stderr, "!%s: memcpy(%p+%u=%p,%u) --> [%p,%p] inside: %d %d\n", me, pldB->xyzw, ii*size, pldB->xyzw + ii*size, size, _beg, _end, AIR_IN_CL(_xyzwBeg, _beg, _xyzwEnd), AIR_IN_CL(_xyzwBeg, _end, _xyzwEnd)); */ memcpy(pldB->xyzw + ii*size, pldA->xyzw, size*sizeof(float)); for (jj=0; jjindxNum; jj++) { (pldB->indx + ii*pldA->indxNum)[jj] = pldA->indx[jj] + ii*pldA->xyzwNum; } size = pldA->primNum; memcpy(pldB->type + ii*size, pldA->type, size*sizeof(unsigned char)); memcpy(pldB->icnt + ii*size, pldA->icnt, size*sizeof(unsigned int)); if (pldA->rgba) { size = pldA->rgbaNum*4; memcpy(pldB->rgba + ii*size, pldA->rgba, size*sizeof(unsigned char)); } if (pldA->norm) { size = pldA->normNum*3; memcpy(pldB->norm + ii*size, pldA->norm, size*sizeof(float)); } if (pldA->tex2) { size = pldA->tex2Num*2; memcpy(pldB->tex2 + ii*size, pldA->tex2, size*sizeof(float)); } if (pldA->tang) { size = pldA->tangNum*3; memcpy(pldB->tang + ii*size, pldA->tang, size*sizeof(float)); } } return 0; } /* ******** limnPolyDataTransform_f, limnPolyDataTransform_d ** ** transforms a surface (both positions, and normals (if set)) ** by given homogenous transform */ void limnPolyDataTransform_f(limnPolyData *pld, const float homat[16]) { double hovec[4], mat[9], inv[9], norm[3], tang[3], nmat[9], tlen; unsigned int vertIdx; if (pld && homat) { ELL_34M_EXTRACT(mat, homat); if (pld->norm) { ell_3m_inv_d(inv, mat); ELL_3M_TRANSPOSE(nmat, inv); } else { ELL_3M_IDENTITY_SET(nmat); /* hush unused value compiler warnings */ } for (vertIdx=0; vertIdxxyzwNum; vertIdx++) { ELL_4MV_MUL(hovec, homat, pld->xyzw + 4*vertIdx); ELL_4V_COPY_TT(pld->xyzw + 4*vertIdx, float, hovec); if (pld->norm) { ELL_3MV_MUL(norm, nmat, pld->norm + 3*vertIdx); ELL_3V_NORM(norm, norm, tlen); ELL_3V_COPY_TT(pld->norm + 3*vertIdx, float, norm); } if (pld->tang) { /* HEY: not tested */ ELL_3MV_MUL(tang, mat, pld->tang + 3*vertIdx); ELL_3V_NORM(tang, tang, tlen); ELL_3V_COPY_TT(pld->tang + 3*vertIdx, float, tang); } } } return; } /* !!! COPY AND PASTE !!! (except for double homat[16]) */ void limnPolyDataTransform_d(limnPolyData *pld, const double homat[16]) { double hovec[4], mat[9], inv[9], norm[3], nmat[9]; unsigned int vertIdx; if (pld && homat) { if (pld->norm) { ELL_34M_EXTRACT(mat, homat); ell_3m_inv_d(inv, mat); ELL_3M_TRANSPOSE(nmat, inv); } else { ELL_3M_IDENTITY_SET(nmat); /* hush unused value compiler warnings */ } for (vertIdx=0; vertIdxxyzwNum; vertIdx++) { ELL_4MV_MUL(hovec, homat, pld->xyzw + 4*vertIdx); ELL_4V_COPY_TT(pld->xyzw + 4*vertIdx, float, hovec); if (pld->norm) { ELL_3MV_MUL(norm, nmat, pld->norm + 3*vertIdx); ELL_3V_COPY_TT(pld->norm + 3*vertIdx, float, norm); } } } return; } unsigned int limnPolyDataPolygonNumber(const limnPolyData *pld) { unsigned int ret, primIdx; ret = 0; if (pld) { for (primIdx=0; primIdxprimNum; primIdx++) { switch(pld->type[primIdx]) { case limnPrimitiveNoop: /* no increment */ break; case limnPrimitiveTriangles: ret += pld->icnt[primIdx]/3; break; case limnPrimitiveTriangleStrip: case limnPrimitiveTriangleFan: ret += pld->icnt[primIdx] - 2; break; case limnPrimitiveQuads: ret += pld->icnt[primIdx]/4; break; } } } return ret; } static int limnPolyDataVertexNormals_(limnPolyData *pld, int nonorient) { static const char me[]="limnPolyDataVertexNormals_"; unsigned int infoBitFlag, primIdx, triIdx, normIdx, baseVertIdx; double len, *matrix=NULL; airArray *mop; infoBitFlag = limnPolyDataInfoBitFlag(pld); if (limnPolyDataAlloc(pld, infoBitFlag | (1 << limnPolyDataInfoNorm), pld->xyzwNum, pld->indxNum, pld->primNum)) { biffAddf(LIMN, "%s: couldn't alloc polydata normals", me); return 1; } mop = airMopNew(); if (nonorient) { /* we will accumulate outer products */ matrix = (double *) malloc(sizeof(double)*9*pld->xyzwNum); if (matrix==NULL) { biffAddf(LIMN, "%s: couldn't allocate matrix buffer", me); return 1; } airMopAdd(mop, matrix, airFree, airMopAlways); for (normIdx=0; normIdxnormNum; normIdx++) { ELL_3M_ZERO_SET(matrix + 9*normIdx); } } else { for (normIdx=0; normIdxnormNum; normIdx++) { ELL_3V_SET(pld->norm + 3*normIdx, 0, 0, 0); } } baseVertIdx = 0; for (primIdx=0; primIdxprimNum; primIdx++) { unsigned int triNum, indxLine[3]={0,0,0}, ii; float pos[3][3], edgeA[3], edgeB[3], norm[3]; switch (pld->type[primIdx]) { case limnPrimitiveTriangles: triNum = pld->icnt[primIdx]/3; break; case limnPrimitiveTriangleStrip: case limnPrimitiveTriangleFan: triNum = pld->icnt[primIdx]-2; break; case limnPrimitiveNoop: triNum = 0; break; default: biffAddf(LIMN, "%s: came across unsupported limnPrimitive \"%s\"", me, airEnumStr(limnPrimitive, pld->type[primIdx])); airMopError(mop); return 1; } if (limnPrimitiveNoop != pld->type[primIdx]) { for (triIdx=0; triIdxtype[primIdx]) { case limnPrimitiveTriangles: ELL_3V_COPY(indxLine, pld->indx + baseVertIdx + 3*triIdx); break; case limnPrimitiveTriangleStrip: if (triIdx%2==0) { ELL_3V_COPY(indxLine, pld->indx+baseVertIdx+triIdx); } else { ELL_3V_SET(indxLine, pld->indx[baseVertIdx+triIdx+1], pld->indx[baseVertIdx+triIdx], pld->indx[baseVertIdx+triIdx+2]); } break; case limnPrimitiveTriangleFan: ELL_3V_SET(indxLine, pld->indx[baseVertIdx], pld->indx[baseVertIdx+triIdx+1], pld->indx[baseVertIdx+triIdx+2]); break; } for (ii=0; ii<3; ii++) { ELL_34V_HOMOG(pos[ii], pld->xyzw + 4*indxLine[ii]); } ELL_3V_SUB(edgeA, pos[1], pos[0]); ELL_3V_SUB(edgeB, pos[2], pos[0]); ELL_3V_CROSS(norm, edgeA, edgeB); /* Adding cross products without any normalization is * equivalent to weighting by triangle area, as proposed * (among others) by G. Taubin ("Estimating the tensor of * curvature of a surface from a polyhedral approximation", * ICCV 1995). This is efficient, avoids trouble with * degenerate triangles and gives reasonable results in * practice. */ if (nonorient) { double outer[9]; ELL_3MV_OUTER(outer, norm, norm); len = ELL_3V_LEN(norm); /* re-scale so that we don't weight by square of area */ for (ii=0; ii<3; ii++) { ELL_3M_SCALE_INCR(matrix+9*indxLine[ii], 1.0/len, outer); } } else { for (ii=0; ii<3; ii++) { ELL_3V_INCR(pld->norm + 3*indxLine[ii], norm); } } } } baseVertIdx += pld->icnt[primIdx]; } if (nonorient) { double eval[3], evec[9]; for (normIdx=0; normIdxnormNum; normIdx++) { ell_3m_eigensolve_d(eval, evec, matrix+9*normIdx, AIR_TRUE); ELL_3V_COPY_TT(pld->norm + 3*normIdx, float, evec); } } else { for (normIdx=0; normIdxnormNum; normIdx++) { ELL_3V_NORM_TT(pld->norm + 3*normIdx, float, pld->norm + 3*normIdx, len); } } airMopOkay(mop); return 0; } int limnPolyDataVertexNormals(limnPolyData *pld) { return limnPolyDataVertexNormals_(pld, 0); } /* counterpart for non-orientable surfaces */ int limnPolyDataVertexNormalsNO(limnPolyData *pld) { return limnPolyDataVertexNormals_(pld, 1); } unsigned int limnPolyDataPrimitiveTypes(const limnPolyData *pld) { unsigned int ret, primIdx; ret = 0; if (pld) { for (primIdx=0; primIdxprimNum; primIdx++) { ret |= (1 << pld->type[primIdx]); } } return ret; } int limnPolyDataPrimitiveVertexNumber(Nrrd *nout, limnPolyData *pld) { static const char me[]="limnPolyDataPrimitiveVertexNumber"; unsigned int *vnum, pidx; if (!(nout && pld)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (nrrdMaybeAlloc_va(nout, nrrdTypeUInt, 1, AIR_CAST(size_t, pld->primNum))) { biffMovef(LIMN, NRRD, "%s: couldn't allocate output", me); return 1; } vnum = AIR_CAST(unsigned int *, nout->data); for (pidx=0; pidxprimNum; pidx++) { vnum[pidx] = pld->icnt[pidx]; } return 0; } int limnPolyDataPrimitiveArea(Nrrd *nout, limnPolyData *pld) { static const char me[]="limnPolyDataPrimitiveArea"; unsigned int primIdx, baseVertIdx; unsigned int triNum, triIdx, *indx, ii; float vert[3][3], edgeA[3], edgeB[3], cross[3]; double *area; if (!(nout && pld)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (nrrdMaybeAlloc_va(nout, nrrdTypeDouble, 1, AIR_CAST(size_t, pld->primNum))) { biffMovef(LIMN, NRRD, "%s: couldn't allocate output", me); return 1; } area = AIR_CAST(double *, nout->data); baseVertIdx = 0; for (primIdx=0; primIdxprimNum; primIdx++) { area[primIdx] = 0; switch (pld->type[primIdx]) { case limnPrimitiveNoop: area[primIdx] = 0.0; break; case limnPrimitiveTriangles: triNum = pld->icnt[primIdx]/3; for (triIdx=0; triIdxindx + baseVertIdx + 3*triIdx; for (ii=0; ii<3; ii++) { ELL_34V_HOMOG(vert[ii], pld->xyzw + 4*indx[ii]); } ELL_3V_SUB(edgeA, vert[1], vert[0]); ELL_3V_SUB(edgeB, vert[2], vert[0]); ELL_3V_CROSS(cross, edgeA, edgeB); area[primIdx] += ELL_3V_LEN(cross)/2; } break; case limnPrimitiveTriangleStrip: case limnPrimitiveTriangleFan: case limnPrimitiveQuads: biffAddf(LIMN, "%s: sorry, haven't implemented area(prim[%u]=%s) yet", me, primIdx, airEnumStr(limnPrimitive, pld->type[primIdx])); return 1; break; case limnPrimitiveLineStrip: /* lines have no area */ break; } baseVertIdx += pld->icnt[primIdx]; } return 0; } /* ** I may regret making this only be axis-aligned ... */ int limnPolyDataRasterize(Nrrd *nout, limnPolyData *pld, double min[3], double max[3], size_t size[3], int type) { static const char me[]="limnPolyDataRasterize"; size_t xi, yi, zi; unsigned int vertIdx; double (*ins)(void *, size_t, double); if (!(nout && pld && min && max && size)) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(nrrdType, type)) { biffAddf(LIMN, "%s: got invalid %s %d", me, nrrdType->name, type); return 1; } if (nrrdTypeBlock == type) { biffAddf(LIMN, "%s: can't use output type %s", me, airEnumStr(nrrdType, type)); return 1; } if (!( min[0] < max[0] && min[1] < max[1] && min[2] < max[2] )) { biffAddf(LIMN, "%s min (%g,%g,%g) not < max (%g,%g,%g)", me, min[0], min[1], min[2], max[0], max[1], max[2]); return 1; } if (nrrdMaybeAlloc_nva(nout, type, 3, size)) { biffMovef(LIMN, NRRD, "%s: trouble allocating output", me); return 1; } ins = nrrdDInsert[type]; for (vertIdx=0; vertIdxxyzwNum; vertIdx++) { double xyz[3]; ELL_34V_HOMOG(xyz, pld->xyzw + 4*vertIdx); if (!( AIR_IN_OP(min[0], xyz[0], max[0]) && AIR_IN_OP(min[1], xyz[1], max[1]) && AIR_IN_OP(min[2], xyz[2], max[2]) )) { continue; } xi = airIndex(min[0], xyz[0], max[0], size[0]); yi = airIndex(min[1], xyz[1], max[1], size[1]); zi = airIndex(min[2], xyz[2], max[2], size[2]); ins(nout->data, xi + size[0]*(yi + size[1]*zi), 1.0); } nrrdAxisInfoSet_nva(nout, nrrdAxisInfoMin, min); nrrdAxisInfoSet_nva(nout, nrrdAxisInfoMax, max); return 0; } void limnPolyDataColorSet(limnPolyData *pld, unsigned char RR, unsigned char GG, unsigned char BB, unsigned char AA) { unsigned int vertIdx; if (pld && ((1 << limnPolyDataInfoRGBA) & limnPolyDataInfoBitFlag(pld))) { for (vertIdx=0; vertIdxrgbaNum; vertIdx++) { ELL_4V_SET(pld->rgba + 4*vertIdx, RR, GG, BB, AA); } } return; } teem-1.11.0~svn6057/src/limn/defaultsLimn.c0000664000175000017500000000251712165631065020140 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" const int limnPresent = 42; const char * limnBiffKey = "limn"; int limnDefCameraAtRelative = AIR_FALSE; int limnDefCameraOrthographic = AIR_FALSE; int limnDefCameraRightHanded = AIR_TRUE; teem-1.11.0~svn6057/src/limn/methodsLimn.c0000664000175000017500000000574012165631065017775 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" limnLight * limnLightNew(void) { limnLight *lit; lit = (limnLight *)calloc(1, sizeof(limnLight)); return lit; } limnLight * limnLightNix(limnLight *lit) { if (lit) { free(lit); } return NULL; } void limnCameraInit(limnCamera *cam) { if (cam) { cam->atRelative = limnDefCameraAtRelative; cam->orthographic = limnDefCameraOrthographic; cam->rightHanded = limnDefCameraRightHanded; cam->uRange[0] = cam->uRange[1] = AIR_NAN; cam->vRange[0] = cam->vRange[1] = AIR_NAN; cam->fov = AIR_NAN; cam->aspect = AIR_NAN; } return; } limnCamera * limnCameraNew(void) { limnCamera *cam; cam = (limnCamera *)calloc(1, sizeof(limnCamera)); if (cam) { limnCameraInit(cam); } return cam; } limnCamera * limnCameraNix(limnCamera *cam) { if (cam) { free(cam); } return NULL; } void _limnOptsPSDefaults(limnOptsPS *ps) { ps->lineWidth[limnEdgeTypeUnknown] = AIR_NAN; ps->lineWidth[limnEdgeTypeBackFacet] = 0.0; ps->lineWidth[limnEdgeTypeBackCrease] = 0.0; ps->lineWidth[limnEdgeTypeContour] = 2.0; ps->lineWidth[limnEdgeTypeFrontCrease] = 1.0; ps->lineWidth[limnEdgeTypeFrontFacet] = 0.0; ps->lineWidth[limnEdgeTypeBorder] = 1.0; ps->lineWidth[limnEdgeTypeLone] = 1.0; ps->creaseAngle = 46; ps->showpage = AIR_FALSE; ps->wireFrame = AIR_FALSE; ps->noBackground = AIR_FALSE; ELL_3V_SET(ps->bg, 1, 1, 1); ELL_3V_SET(ps->edgeColor, 0, 0, 0); } limnWindow * limnWindowNew(int device) { limnWindow *win; win = (limnWindow *)calloc(1, sizeof(limnWindow)); if (win) { win->device = device; switch(device) { case limnDevicePS: win->yFlip = 1; _limnOptsPSDefaults(&(win->ps)); break; } win->scale = 72; win->file = NULL; } return win; } limnWindow * limnWindowNix(limnWindow *win) { if (win) { free(win); } return NULL; } teem-1.11.0~svn6057/src/limn/test/0000775000175000017500000000000012203513753016313 5ustar domibeldomibelteem-1.11.0~svn6057/src/limn/test/tps.c0000664000175000017500000001566712042367142017304 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../limn.h" char *info = ("Render something in postscript."); int main(int argc, const char *argv[]) { const char *me; char *err, *outS; limnCamera *cam; float matA[16], matB[16], winscale, edgeWidth[5]; hestOpt *hopt=NULL; airArray *mop; limnObject *obj; limnLook *look; int lookIdx; limnWindow *win; int partIdx, wire, concave; Nrrd *nmap; mop = airMopNew(); cam = limnCameraNew(); airMopAdd(mop, cam, (airMopper)limnCameraNix, airMopAlways); me = argv[0]; hestOptAdd(&hopt, "fr", "from point", airTypeDouble, 3, 3, cam->from,"4 4 4", "position of camera, used to determine view vector"); hestOptAdd(&hopt, "at", "at point", airTypeDouble, 3, 3, cam->at, "0 0 0", "camera look-at point, used to determine view vector"); hestOptAdd(&hopt, "up", "up vector", airTypeDouble, 3, 3, cam->up, "0 0 1", "camera pseudo-up vector, used to determine view coordinates"); hestOptAdd(&hopt, "rh", NULL, airTypeInt, 0, 0, &(cam->rightHanded), NULL, "use a right-handed UVN frame (V points down)"); hestOptAdd(&hopt, "or", NULL, airTypeInt, 0, 0, &(cam->orthographic), NULL, "use orthogonal projection"); hestOptAdd(&hopt, "ur", "uMin uMax", airTypeDouble, 2, 2, cam->uRange, "-1 1", "range in U direction of image plane"); hestOptAdd(&hopt, "vr", "vMin vMax", airTypeDouble, 2, 2, cam->vRange, "-1 1", "range in V direction of image plane"); hestOptAdd(&hopt, "e", "envmap", airTypeOther, 1, 1, &nmap, NULL, "16checker-based environment map", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "ws", "winscale", airTypeFloat, 1, 1, &winscale, "200", "world to points (PostScript) scaling"); hestOptAdd(&hopt, "wire", NULL, airTypeInt, 0, 0, &wire, NULL, "just do wire-frame rendering"); hestOptAdd(&hopt, "concave", NULL, airTypeInt, 0, 0, &concave, NULL, "use slightly buggy rendering method suitable for " "concave or self-occluding objects"); hestOptAdd(&hopt, "wd", "5 widths", airTypeFloat, 5, 5, edgeWidth, "0.0 0.0 3.0 2.0 0.0", "width of edges drawn for five kinds of " "edges: back non-crease, back crease, " "silohuette, front crease, front non-crease"); hestOptAdd(&hopt, "o", "output PS", airTypeString, 1, 1, &outS, "out.ps", "output file to render postscript into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); cam->neer = -0.000000001; cam->dist = 0; cam->faar = 0.0000000001; cam->atRelative = AIR_TRUE; if (limnCameraUpdate(cam)) { fprintf(stderr, "%s: trouble:\n%s\n", me, err = biffGet(LIMN)); free(err); return 1; } obj = limnObjectNew(10, AIR_TRUE); airMopAdd(mop, obj, (airMopper)limnObjectNix, airMopAlways); /* create limnLooks for diffuse (#0) and flat (#1) shading */ lookIdx = airArrayLenIncr(obj->lookArr, 2); look = obj->look + lookIdx + 0; ELL_4V_SET(look->rgba, 1, 1, 1, 1); ELL_3V_SET(look->kads, 0, 1, 0); look->spow = 0; look = obj->look + lookIdx + 1; ELL_4V_SET(look->rgba, 1, 1, 1, 1); ELL_3V_SET(look->kads, 1, 0, 0); look->spow = 0; /* X axis: rod */ partIdx = limnObjectCylinderAdd(obj, 0, 0, 16); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, 1, 0.2, 0.2); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 1.3, 0.0, 0.0); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); /* Y axis: rod + ball */ partIdx = limnObjectCylinderAdd(obj, 0, 1, 16); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, 0.2, 1, 0.2); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, 1.3, 0.0); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectPolarSphereAdd(obj, 0, 0, 32, 16); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, 0.28, 0.28, 0.28); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, 2.6, 0.0); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); /* Z axis: rod + ball + ball */ partIdx = limnObjectCylinderAdd(obj, 0, 2, 16); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, 0.2, 0.2, 1); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, 0.0, 1.3); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectPolarSphereAdd(obj, 0, 1, 32, 16); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, 0.28, 0.28, 0.28); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, 0.0, 2.6); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectPolarSphereAdd(obj, 0, 2, 32, 16); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, 0.28, 0.28, 0.28); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, 0.0, 3.2); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); win = limnWindowNew(limnDevicePS); win->scale = winscale; win->ps.wireFrame = wire; win->ps.lineWidth[limnEdgeTypeBackFacet] = edgeWidth[0]; win->ps.lineWidth[limnEdgeTypeBackCrease] = edgeWidth[1]; win->ps.lineWidth[limnEdgeTypeContour] = edgeWidth[2]; win->ps.lineWidth[limnEdgeTypeFrontCrease] = edgeWidth[3]; win->ps.lineWidth[limnEdgeTypeFrontFacet] = edgeWidth[4]; win->file = fopen(outS, "w"); airMopAdd(mop, win, (airMopper)limnWindowNix, airMopAlways); if (limnObjectRender(obj, cam, win) || (concave ? limnObjectPSDrawConcave(obj, cam, nmap, win) : limnObjectPSDraw(obj, cam, nmap, win))) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } fclose(win->file); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/limn/test/light.c0000664000175000017500000000436512042367142017576 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ char *me; #include "../limn.h" int main(int argc, char *argv[]) { limnLight *lit; limnCamera *cam; Nrrd *map, *ppm; NrrdRange *range; AIR_UNUSED(argc); me = argv[0]; cam = limnCameraNew(); ELL_3V_SET(cam->from, 10, 0, 0); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 0, 1); cam->uRange[0] = -(cam->uRange[1] = 4); cam->vRange[0] = -(cam->vRange[1] = 3); cam->neer = -5; cam->dist = 0; cam->faar = 5; cam->atRelative = AIR_TRUE; lit = limnLightNew(); limnLightSet(lit, 0, AIR_TRUE, 1, 0, 0, 1, 0, 0); limnLightSet(lit, 1, AIR_TRUE, 0, 1, 0, 0, 1, 0); limnLightSet(lit, 2, AIR_TRUE, 0, 0, 1, 0, 0, 1); limnLightUpdate(lit, cam); if (limnEnvMapFill(map=nrrdNew(), limnLightDiffuseCB, limnQN16checker, lit)) { fprintf(stderr, "%s: trouble:\n%s", me, biffGet(LIMN)); exit(1); } range = nrrdRangeNew(0, 1); if (nrrdQuantize(ppm=nrrdNew(), map, range, 8)) { fprintf(stderr, "%s: trouble:\n%s", me, biffGet(NRRD)); exit(1); } if (nrrdSave("map.ppm", ppm, NULL)) { fprintf(stderr, "%s: trouble:\n%s", me, biffGet(NRRD)); exit(1); } nrrdNuke(map); nrrdNuke(ppm); nrrdRangeNix(range); exit(0); } teem-1.11.0~svn6057/src/limn/test/tbc.c0000664000175000017500000001112412042367142017226 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* test/tbc -s 0.95 -i s.txt -loop -n 13 -m 800 -t 55 170 > ! out.ps */ /* s.txt: -10 -20 -20 -10 -10 0 -20 10 -10 20 0 10 10 20 20 10 10 0 20 -10 10 -20 0 -10 10 0 0 10 -10 0 0 -10 */ #include "../limn.h" char *info = ("Visualize the space of BC cubics with a spline."); int main(int argc, const char *argv[]) { const char *me; char *err; limnSpline *spline; hestOpt *hopt=NULL; airArray *mop; int bi, ci, i, N, M, loop; Nrrd *ncptA, *ncptB, *nout; double *out, minT, maxT, scale, tran[2], B, C; limnSplineTypeSpec *spec; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "i", "spline data", airTypeOther, 1, 1, &ncptA, NULL, "data points for the spline, must be 2-vectors", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "loop", NULL, airTypeInt, 0, 0, &loop, NULL, "the last control point is in fact the first"); hestOptAdd(&hopt, "n", "N", airTypeInt, 1, 1, &N, "10", "how many samples along each edge of BC space"); hestOptAdd(&hopt, "m", "M", airTypeInt, 1, 1, &M, "512", "the number of sample points at which to evalute the spline"); hestOptAdd(&hopt, "t", "tx ty", airTypeDouble, 2, 2, tran, "0.0 0.0", "translation for drawing"); hestOptAdd(&hopt, "s", "scale", airTypeDouble, 1, 1, &scale, "1.0", "scaling for drawing"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( 2 == ncptA->dim && 2 == ncptA->axis[0].size )) { fprintf(stderr, "%s: didn't get a 2-D 2xN nrrd)\n", me); airMopError(mop); return 1; } spec = limnSplineTypeSpecNew(limnSplineTypeBC, 0, 0); airMopAdd(mop, ncptB=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (limnSplineNrrdCleverFix(ncptB, ncptA, limnSplineInfo2Vector, limnSplineTypeBC) || !(spline = limnSplineNew(ncptB, limnSplineInfo2Vector, spec))) { airMopAdd(mop, err=biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } spline->loop = loop; airMopAdd(mop, spline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, nout=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); minT = limnSplineMinT(spline); maxT = limnSplineMaxT(spline); /* try one for error checking */ if (limnSplineSample(nout, spline, minT, M, maxT)) { airMopAdd(mop, err=biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } printf("%%!\n"); printf("1 setlinewidth\n"); printf("%g %g translate\n", tran[0], tran[1]); for (ci=0; cidata); /* shouldn't actually change */ printf("gsave\n"); printf("%g %g translate\n", bi*500.0/(N-1), ci*500.0/(N-1)); printf("%g %g scale\n", scale, scale); printf("%g %g moveto\n", out[0 + 2*0], out[1 + 2*0]); for (i=1; iloop) { printf("closepath\n"); } printf("stroke\n"); printf("grestore\n"); } } printf("showpage\n"); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/limn/test/tqn.c0000664000175000017500000000462012165631065017267 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../limn.h" const char *info = ("inspect QN schemes."); int main(int argc, const char *argv[]) { const char *me; char *err; hestOpt *hopt=NULL; airArray *mop; char *outS; unsigned int reso; int qni; Nrrd *nqn; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "s", "size", airTypeUInt, 1, 1, &reso, "256", "resolution"); hestOptAdd(&hopt, "q", "which", airTypeInt, 1, 1, &qni, NULL, "which quantization scheme"); hestOptAdd(&hopt, "o", "out", airTypeString, 1, 1, &outS, "-", "output image"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nqn = nrrdNew(); airMopAdd(mop, nqn, (airMopper)nrrdNuke, airMopAlways); if (limnQNDemo(nqn, reso, qni)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nqn, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/limn/test/tcamanim.c0000664000175000017500000001743012042367142020255 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../limn.h" char *info = ("Works with camanim.tcl to test camera path splines."); #define _LIMNMAGIC "LIMN0000" int _limnReadCamanim(int imgSize[2], limnCamera **keycamP, double **timeP, unsigned int *numKeysP, FILE *fin) { char me[]="_limnReadCamanim", err[AIR_STRLEN_MED]; char line[AIR_STRLEN_HUGE]; unsigned int ki; double *tmp, *dwell, di, dn, df, fr[3], at[3], up[3], va; airArray *mop, *camA, *dwellA; if (!( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE) && !strcmp(_LIMNMAGIC, line) )) { sprintf(err, "%s: couldn't read first line or it wasn't \"%s\"", me, _LIMNMAGIC); biffAdd(LIMN, err); return 1; } if (!( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE) && 2 == (airStrtrans(airStrtrans(line, '{', ' '), '}', ' '), sscanf(line, "imgSize %d %d", imgSize+0, imgSize+1)) )) { sprintf(err, "%s: couldn't read second line or it wasn't " "\"imgSize \"", me); biffAdd(LIMN, err); return 1; } mop = airMopNew(); camA = airArrayNew((void **)keycamP, numKeysP, sizeof(limnCamera), 1); dwellA = airArrayNew((void **)&dwell, NULL, sizeof(double), 1); airMopAdd(mop, camA, (airMopper)airArrayNix, airMopAlways); airMopAdd(mop, dwellA, (airMopper)airArrayNuke, airMopAlways); while ( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE) ) { airStrtrans(airStrtrans(line, '{', ' '), '}', ' '); ki = airArrayLenIncr(camA, 1); airArrayLenIncr(dwellA, 1); if (14 != sscanf(line, "cam.di %lg cam.at %lg %lg %lg " "cam.up %lg %lg %lg cam.dn %lg cam.df %lg cam.va %lg " "relDwell %lg cam.fr %lg %lg %lg", &di, at+0, at+1, at+2, up+0, up+1, up+2, &dn, &df, &va, dwell+ki, fr+0, fr+1, fr+2)) { sprintf(err, "%s: trouble parsing line %d: \"%s\"", me, ki, line); biffAdd(LIMN, err); airMopError(mop); return 1; } (*keycamP)[ki].neer = dn; (*keycamP)[ki].faar = df; (*keycamP)[ki].dist = di; ELL_3V_COPY((*keycamP)[ki].from, fr); ELL_3V_COPY((*keycamP)[ki].at, at); ELL_3V_COPY((*keycamP)[ki].up, up); (*keycamP)[ki].fov = va; (*keycamP)[ki].aspect = (double)imgSize[0]/imgSize[1]; (*keycamP)[ki].atRelative = AIR_FALSE; (*keycamP)[ki].orthographic = AIR_FALSE; (*keycamP)[ki].rightHanded = AIR_TRUE; } tmp = (double*)calloc(*numKeysP, sizeof(double)); airMopAdd(mop, tmp, airFree, airMopAlways); *timeP = (double*)calloc(*numKeysP, sizeof(double)); for (ki=0; ki<*numKeysP; ki++) { dwell[ki] = AIR_CLAMP(0, dwell[ki], 2); tmp[ki] = tan(AIR_AFFINE(-0.01, dwell[ki], 2.01, 0.0, AIR_PI/2)); } (*timeP)[0] = 0; for (ki=1; ki<*numKeysP; ki++) { (*timeP)[ki] = (*timeP)[ki-1] + (tmp[ki-1] + tmp[ki])/2; } for (ki=0; ki<*numKeysP; ki++) { (*timeP)[ki] /= (*timeP)[*numKeysP-1]; } airMopOkay(mop); return 0; } int _limnWriteCamanim(FILE *fout, int imgSize[2], limnCamera *cam, int numFrames) { /* char me[]="_limnWriteCamanim", err[AIR_STRLEN_MED]; */ int fi; fprintf(fout, "%s\n", _LIMNMAGIC); fprintf(fout, "imgSize {%d %d}\n", imgSize[0], imgSize[1]); for (fi=0; fidim || 3 == nin->dim )) { fprintf(stderr, "%s: input nrrd must be 2-D or 3-D (not %d-D)\n", me, nin->dim); airMopError(mop); return 1; } if (!( nrrdTypeUChar == nin->type )) { fprintf(stderr, "%s: input nrrd must be type %s (not %s)\n", me, airEnumStr(nrrdType, nrrdTypeUChar), airEnumStr(nrrdType, nin->type)); airMopError(mop); return 1; } sx = (2 == nin->dim ? nin->axis[0].size : nin->axis[1].size); sy = (2 == nin->dim ? nin->axis[1].size : nin->axis[2].size); gray = 2 == nin->dim || 1 == nin->axis[0].size; if (!( sx == sy )) { fprintf(stderr, "%s: image must be square (not %d x %d)\n", me, sx, sy); airMopError(mop); return 1; } if (!( file = airFopen(outS, stdout, "wb") )) { fprintf(stderr, "%s: couldn't open \"%s\" for writing", me, outS); airMopError(mop); return 1; } airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); hack = sqrt(3)/2; /* sorry, copied from nrrd/formatEPS.c */ minX = 0.5; maxX = 8.0; minY = 5.50 - 7.5*hack*sy/sx/2; maxY = 5.50 + 7.5*hack*sy/sx/2; scale = 7.5/sx; minX *= 72; minY *= 72; maxX *= 72; maxY *= 72; scale *= 72; fprintf(file, "%%!PS-Adobe-3.0 EPSF-3.0\n"); fprintf(file, "%%%%Creator: hairy pathetic monster\n"); fprintf(file, "%%%%Title: raving lunatic\n"); fprintf(file, "%%%%Pages: 1\n"); fprintf(file, "%%%%BoundingBox: %d %d %d %d\n", (int)floor(minX), (int)floor(minY), (int)ceil(maxX), (int)ceil(maxY)); fprintf(file, "%%%%HiResBoundingBox: %g %g %g %g\n", minX, minY, maxX, maxY); fprintf(file, "%%%%EndComments\n"); fprintf(file, "%%%%BeginProlog\n"); fprintf(file, "%% linestr creates an empty string to hold " "one scanline\n"); fprintf(file, "/linestr %d string def\n", sx*(gray ? 1 : 3)); fprintf(file, "%%%%EndProlog\n"); fprintf(file, "%%%%Page: 1 1\n"); fprintf(file, "gsave\n"); fprintf(file, "%g %g moveto\n", minX, minY); fprintf(file, "%g %g lineto\n", maxX, minY); fprintf(file, "%g %g lineto\n", (minX + maxX)/2, minY + hack*(maxX - minX)); fprintf(file, "closepath\n"); fprintf(file, "clip\n"); fprintf(file, "gsave newpath\n"); fprintf(file, "%g %g translate\n", minX, minY); fprintf(file, "%g %g scale\n", sx*scale, sy*scale); fprintf(file, "%d %d 8\n", sx, sy); fprintf(file, "[%d 0 0 -%d 0 %d]\n", sx, sy, sy); fprintf(file, "{currentfile linestr readhexstring pop} %s\n", gray ? "image" : "false 3 colorimage"); nrrdEncodingHex->write(file, nin->data, nrrdElementNumber(nin), nin, nio); nio->dataFile = NULL; fprintf(file, "\n"); fprintf(file, "grestore\n"); if (width) { fprintf(file, "%g %g moveto\n", minX, minY); fprintf(file, "%g %g lineto\n", maxX, minY); fprintf(file, "%g %g lineto\n", (minX + maxX)/2, minY + hack*(maxX - minX)); fprintf(file, "closepath\n"); fprintf(file, "%g setlinewidth 0 setgray stroke\n", 2*width*scale); } if (labels) { /* happily, psfrag doesn't respect the clipping path */ fprintf(file, "/Helvetica findfont 20 scalefont setfont\n"); fprintf(file, "%g %g moveto (A) show\n", maxX, minY); fprintf(file, "%g %g moveto (B) show\n", (minX + maxX)/2, minY + hack*(maxX - minX)); fprintf(file, "%g %g moveto (C) show\n", minX, minY); } fprintf(file, "grestore\n"); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/limn/test/tio.c0000664000175000017500000000323112042367142017251 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../limn.h" char *info = ("throw-away."); int main(int argc, char *argv[]) { char *me, *err; FILE *file; limnPolyData *lmpd; AIR_UNUSED(argc); me = argv[0]; lmpd = limnPolyDataNew(); file = fopen("in.lmpd", "r"); if (limnPolyDataReadLMPD(lmpd, file)) { err = biffGetDone(LIMN), fprintf(stderr, "%s: trouble:\n%s\n", me, err); return 1; } fclose(file); file = fopen("out.vtk", "w"); if (limnPolyDataWriteVTK(file, lmpd)) { err = biffGetDone(LIMN); fprintf(stderr, "%s: trouble:\n%s\n", me, err); return 1; } fclose(file); return 0; } teem-1.11.0~svn6057/src/limn/test/tcam.c0000664000175000017500000000524512042367142017411 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../limn.h" const char *info = ("dumb little demonstration of calculating the " "world-to-view and view-to-world transforms given " "from, at, and up locations."); int main(int argc, const char *argv[]) { const char *me; char *err; limnCamera *cam; float mat[16]; hestOpt *hopt=NULL; airArray *mop; mop = airMopNew(); cam = limnCameraNew(); airMopAdd(mop, cam, (airMopper)limnCameraNix, airMopAlways); me = argv[0]; hestOptAdd(&hopt, "fr", "eye pos", airTypeDouble, 3, 3, cam->from, NULL, "camera eye point"); hestOptAdd(&hopt, "at", "at pos", airTypeDouble, 3, 3, cam->at, "0 0 0", "camera look-at point"); hestOptAdd(&hopt, "up", "up dir", airTypeDouble, 3, 3, cam->up, "0 0 1", "camera pseudo up vector"); hestOptAdd(&hopt, "rh", NULL, airTypeInt, 0, 0, &(cam->rightHanded), NULL, "use a right-handed UVN frame (V points down)"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); cam->neer = -1; cam->dist = 0; cam->faar = 1; cam->atRelative = AIR_TRUE; if (limnCameraUpdate(cam)) { fprintf(stderr, "%s: trouble:\n%s\n", me, err = biffGet(LIMN)); free(err); return 1; } printf("%s: W2V:\n", me); ELL_4M_COPY(mat, cam->W2V); ell_4m_print_f(stdout, mat); printf("\n"); printf("%s: V2W:\n", me); ELL_4M_COPY(mat, cam->V2W); ell_4m_print_f(stdout, mat); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/limn/test/off2eps.c0000664000175000017500000001651212042367142020030 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../limn.h" char *info = ("Renders an OFF file to an EPS file."); int main(int argc, const char *argv[]) { const char *me; char *err, *inS, *outS; limnCamera *cam; float bg[3], winscale, edgeWidth[5], creaseAngle; hestOpt *hopt=NULL; airArray *mop; limnObject *obj; limnLook *look; unsigned int lookIdx; limnWindow *win; Nrrd *nmap; FILE *file; int wire, concave, describe, reverse, nobg; mop = airMopNew(); cam = limnCameraNew(); airMopAdd(mop, cam, (airMopper)limnCameraNix, airMopAlways); me = argv[0]; hestOptAdd(&hopt, "i", "input OFF", airTypeString, 1, 1, &inS, NULL, "input OFF file"); hestOptAdd(&hopt, "fr", "from point", airTypeDouble, 3, 3, cam->from,"4 4 4", "position of camera, used to determine view vector"); hestOptAdd(&hopt, "at", "at point", airTypeDouble, 3, 3, cam->at, "0 0 0", "camera look-at point, used to determine view vector"); hestOptAdd(&hopt, "up", "up vector", airTypeDouble, 3, 3, cam->up, "0 0 1", "camera pseudo-up vector, used to determine view coordinates"); hestOptAdd(&hopt, "rh", NULL, airTypeInt, 0, 0, &(cam->rightHanded), NULL, "use a right-handed UVN frame (V points down)"); hestOptAdd(&hopt, "or", NULL, airTypeInt, 0, 0, &(cam->orthographic), NULL, "use orthogonal projection"); hestOptAdd(&hopt, "ur", "uMin uMax", airTypeDouble, 2, 2, cam->uRange, "-1 1", "range in U direction of image plane"); hestOptAdd(&hopt, "vr", "vMin vMax", airTypeDouble, 2, 2, cam->vRange, "-1 1", "range in V direction of image plane"); hestOptAdd(&hopt, "e", "envmap", airTypeOther, 1, 1, &nmap, "", "16octa-based environment map", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "ws", "winscale", airTypeFloat, 1, 1, &winscale, "200", "world to points (PostScript) scaling"); hestOptAdd(&hopt, "wire", NULL, airTypeInt, 0, 0, &wire, NULL, "just do wire-frame rendering"); hestOptAdd(&hopt, "concave", NULL, airTypeInt, 0, 0, &concave, NULL, "use slightly buggy rendering method suitable for " "concave or self-occluding objects"); hestOptAdd(&hopt, "reverse", NULL, airTypeInt, 0, 0, &reverse, NULL, "reverse ordering of vertices per face (needed if they " "specified in clockwise order)"); hestOptAdd(&hopt, "describe", NULL, airTypeInt, 0, 0, &describe, NULL, "for debugging: list object definition of OFF read"); hestOptAdd(&hopt, "bg", "background", airTypeFloat, 3, 3, bg, "1 1 1", "background RGB color; each component in range [0.0,1.0]"); hestOptAdd(&hopt, "nobg", NULL, airTypeInt, 0, 0, &nobg, NULL, "don't initially fill with background color"); hestOptAdd(&hopt, "wd", "5 widths", airTypeFloat, 5, 5, edgeWidth, "0.0 0.0 3.0 2.0 0.0", "width of edges drawn for five kinds of " "edges: back non-crease, back crease, " "silohuette, front crease, front non-crease"); hestOptAdd(&hopt, "ca", "angle", airTypeFloat, 1, 1, &creaseAngle, "30", "dihedral angles greater than this are creases"); hestOptAdd(&hopt, "o", "output PS", airTypeString, 1, 1, &outS, "out.ps", "output file to render postscript into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); cam->neer = -0.000000001; cam->dist = 0; cam->faar = 0.0000000001; cam->atRelative = AIR_TRUE; if (limnCameraUpdate(cam)) { fprintf(stderr, "%s: trouble:\n%s\n", me, err = biffGet(LIMN)); free(err); return 1; } obj = limnObjectNew(10, AIR_TRUE); airMopAdd(mop, obj, (airMopper)limnObjectNix, airMopAlways); if (!(file = airFopen(inS, stdin, "r"))) { fprintf(stderr, "%s: couldn't open \"%s\" for reading\n", me, inS); airMopError(mop); return 1; } airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); if (limnObjectReadOFF(obj, file)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (describe) { fprintf(stdout, "----------------- POST-READ -----------------\n"); limnObjectDescribe(stdout, obj); fprintf(stdout, "----------------- POST-READ -----------------\n"); } if (reverse) { if (limnObjectFaceReverse(obj)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } } if (describe) { fprintf(stdout, "----------------- POST-REVERSE -----------------\n"); limnObjectDescribe(stdout, obj); fprintf(stdout, "----------------- POST-REVERSE -----------------\n"); } win = limnWindowNew(limnDevicePS); win->ps.lineWidth[limnEdgeTypeBackFacet] = edgeWidth[0]; win->ps.lineWidth[limnEdgeTypeBackCrease] = edgeWidth[1]; win->ps.lineWidth[limnEdgeTypeContour] = edgeWidth[2]; win->ps.lineWidth[limnEdgeTypeFrontCrease] = edgeWidth[3]; win->ps.lineWidth[limnEdgeTypeFrontFacet] = edgeWidth[4]; win->ps.wireFrame = wire; win->ps.creaseAngle = creaseAngle; win->ps.noBackground = nobg; ELL_3V_COPY(win->ps.bg, bg); win->file = airFopen(outS, stdout, "w"); airMopAdd(mop, win, (airMopper)limnWindowNix, airMopAlways); win->scale = winscale; for (lookIdx=0; lookIdxlookNum; lookIdx++) { look = obj->look + lookIdx; /* earlier version of limn/test/soid used (0.2,0.8,0.0), I think. Now we assume that any useful shading is happening in the emap */ ELL_3V_SET(look->kads, 0.2, 0.8, 0); } if (limnObjectRender(obj, cam, win) || (concave ? limnObjectPSDrawConcave(obj, cam, nmap, win) : limnObjectPSDraw(obj, cam, nmap, win))) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } fclose(win->file); if (describe) { fprintf(stdout, "----------------- POST-RENDER -----------------\n"); limnObjectDescribe(stdout, obj); fprintf(stdout, "----------------- POST-RENDER -----------------\n"); } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/limn/test/plot.c0000664000175000017500000004001612042367142017436 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../limn.h" char *info = ("Plot!"); typedef struct { FILE *file; double psc; double bbox[4]; int nobg; double bgColor[3]; double maxX, maxY; /* set by plotPreamble */ } plotPS; typedef struct { /* these are allocated per-graph BY HEST */ double *graphThick, *graphGray, *dotDiameter, *dotGray; /* also hest-allocated */ char *axisHorzLabel, *axisVertLabel; double *horzTick, *vertTick; int numHorzTick, numVertTick; int labelHorzTick, labelVertTick; double axisThick, tickThick, tickLabelSize, tickLength; double horzTickLabelOffset, vertTickLabelOffset; double dotInnerDiameterFraction; double axisOrig[2]; double dbox[4]; } plotParm; #define PPS_X(x) AIR_AFFINE(pps->bbox[0], (x), pps->bbox[2], 0, pps->maxX) #define PPS_Y(y) AIR_AFFINE(pps->bbox[1], (y), pps->bbox[3], 0, pps->maxY) #define PPS_S(s) AIR_DELTA(pps->bbox[1], (s), pps->bbox[3], 0, pps->maxY) void plotPreamble(plotPS *pps, plotParm *pparm) { AIR_UNUSED(pparm); pps->maxX = pps->psc*(pps->bbox[2] - pps->bbox[0]); pps->maxY = pps->psc*(pps->bbox[3] - pps->bbox[1]); fprintf(pps->file, "%%!PS-Adobe-2.0 EPSF-2.0\n"); fprintf(pps->file, "%%%%Creator: plot\n"); fprintf(pps->file, "%%%%Pages: 1\n"); fprintf(pps->file, "%%%%BoundingBox: 0 0 %d %d\n", (int)(pps->maxX), (int)(pps->maxY)); fprintf(pps->file, "%%%%EndComments\n"); fprintf(pps->file, "%%%%EndProlog\n"); fprintf(pps->file, "%%%%Page: 1 1\n"); fprintf(pps->file, "gsave\n"); fprintf(pps->file, "0 0 moveto\n"); fprintf(pps->file, "%g 0 lineto\n", pps->maxX); fprintf(pps->file, "%g %g lineto\n", pps->maxX, pps->maxY); fprintf(pps->file, "0 %g lineto\n", pps->maxY); fprintf(pps->file, "closepath\n"); if (!pps->nobg) { fprintf(pps->file, "gsave %g %g %g setrgbcolor fill grestore\n", pps->bgColor[0], pps->bgColor[1], pps->bgColor[2]); } fprintf(pps->file, "clip\n"); fprintf(pps->file, "gsave newpath\n"); fprintf(pps->file, "1 setlinejoin\n"); fprintf(pps->file, "1 setlinecap\n"); fprintf(pps->file, "/M {moveto} bind def\n"); fprintf(pps->file, "/L {lineto} bind def\n"); fprintf(pps->file, "/W {setlinewidth} bind def\n"); fprintf(pps->file, "/F {fill} bind def\n"); fprintf(pps->file, "/S {stroke} bind def\n"); fprintf(pps->file, "/CP {closepath} bind def\n"); fprintf(pps->file, "/RGB {setrgbcolor} bind def\n"); fprintf(pps->file, "/Gr {setgray} bind def\n"); fprintf(pps->file, "\n"); fprintf(pps->file, "0 setlinecap\n"); return; } void plotWidth(plotPS *pps, plotParm *pparm, double width) { AIR_UNUSED(pparm); fprintf(pps->file, "%g W\n", pps->psc*width); return; } void plotGray(plotPS *pps, plotParm *pparm, double gray) { AIR_UNUSED(pparm); fprintf(pps->file, "%g Gr\n", gray); return; } void plotLine(plotPS *pps, plotParm *pparm, double x0, double y0, double x1, double y1) { AIR_UNUSED(pparm); fprintf(pps->file, "%g %g M\n", PPS_X(x0), PPS_Y(y0)); fprintf(pps->file, "%g %g L S\n", PPS_X(x1), PPS_Y(y1)); return; } void plotLabel(plotPS *pps, plotParm *pparm, int centered, double x, double y, char *str) { fprintf(pps->file, "gsave\n"); plotWidth(pps, pparm, 0); if (centered) { fprintf(pps->file, "0 0 M (%s) false charpath pathbbox\n" "exch 4 1 roll sub 2 div 3 1 roll sub -2 div\n" /* don't ask */ "newpath %g add exch %g add M (%s) show\n", str, PPS_X(x), PPS_Y(y), str); } else { fprintf(pps->file, "%g %g M (%s) show\n", PPS_X(x), PPS_Y(y), str); } fprintf(pps->file, "grestore\n"); return; } void plotAxes(plotPS *pps, plotParm *pparm, Nrrd *ndata) { double axX, axY, xx, yy, toff; char buff[AIR_STRLEN_SMALL]; int ti; AIR_UNUSED(ndata); axX = AIR_AFFINE(pparm->dbox[0], pparm->axisOrig[0], pparm->dbox[2], pps->bbox[0], pps->bbox[2]); axY = AIR_AFFINE(pparm->dbox[1], pparm->axisOrig[1], pparm->dbox[3], pps->bbox[1], pps->bbox[3]); plotGray(pps, pparm, 0); plotWidth(pps, pparm, pparm->axisThick); plotLine(pps, pparm, axX, pps->bbox[1], axX, pps->bbox[3]); plotLine(pps, pparm, pps->bbox[0], axY, pps->bbox[2], axY); if (strlen(pparm->axisHorzLabel) || strlen(pparm->axisVertLabel)) { fprintf(pps->file, "/Helvetica findfont 20 scalefont setfont\n"); if (strlen(pparm->axisHorzLabel)) { plotLabel(pps, pparm, AIR_FALSE, pps->bbox[2], axY, pparm->axisHorzLabel); } if (strlen(pparm->axisVertLabel)) { plotLabel(pps, pparm, AIR_FALSE, axX, pps->bbox[3], pparm->axisVertLabel); } } if (pparm->numHorzTick) { if (pparm->tickThick > 0) { toff = pparm->tickLength/2; plotGray(pps, pparm, 0); plotWidth(pps, pparm, pparm->tickThick); for (ti=0; tinumHorzTick; ti++) { xx = AIR_AFFINE(pparm->dbox[0], pparm->horzTick[ti], pparm->dbox[2], pps->bbox[0], pps->bbox[2]); plotLine(pps, pparm, xx, axY - toff, xx, axY + toff); } } if (pparm->tickLabelSize) { fprintf(pps->file, "/Helvetica findfont %g scalefont setfont\n", pparm->tickLabelSize); for (ti=0; tinumHorzTick; ti++) { xx = AIR_AFFINE(pparm->dbox[0], pparm->horzTick[ti], pparm->dbox[2], pps->bbox[0], pps->bbox[2]); yy = axY + pparm->horzTickLabelOffset; sprintf(buff, "%g", pparm->horzTick[ti]); plotLabel(pps, pparm, AIR_TRUE, xx, yy, buff); } } } if (pparm->numVertTick) { if (pparm->tickThick > 0) { toff = pparm->tickLength/2; plotGray(pps, pparm, 0); plotWidth(pps, pparm, pparm->tickThick); for (ti=0; tinumVertTick; ti++) { yy = AIR_AFFINE(pparm->dbox[1], pparm->vertTick[ti], pparm->dbox[3], pps->bbox[1], pps->bbox[3]); plotLine(pps, pparm, axX - toff, yy, axX + toff, yy); } } if (pparm->tickLabelSize) { fprintf(pps->file, "/Helvetica findfont %g scalefont setfont\n", pparm->tickLabelSize); for (ti=0; tinumVertTick; ti++) { yy = AIR_AFFINE(pparm->dbox[1], pparm->vertTick[ti], pparm->dbox[3], pps->bbox[1], pps->bbox[3]); xx = axX + pparm->vertTickLabelOffset; sprintf(buff, "%g", pparm->vertTick[ti]); plotLabel(pps, pparm, AIR_TRUE, xx, yy, buff); } } } } void plotGraph(plotPS *pps, plotParm *pparm, Nrrd **ndata, int nidx) { int ii, npts; double xx, yy, *data, val; if (!( pparm->graphThick[nidx] > 0 )) { return; } data = (double *)(ndata[nidx]->data); npts = ndata[nidx]->axis[1].size; plotGray(pps, pparm, pparm->graphGray[nidx]); fprintf(pps->file, "%g W\n", pps->psc*pparm->graphThick[nidx]); for (ii=0; iiaxis[1].min, ndata[nidx]->axis[1].max); xx = AIR_AFFINE(pparm->dbox[0], xx, pparm->dbox[2], pps->bbox[0], pps->bbox[2]); yy = AIR_AFFINE(pparm->dbox[1], val, pparm->dbox[3], pps->bbox[1], pps->bbox[3]); fprintf(pps->file, "%g %g %s\n", PPS_X(xx), PPS_Y(yy), ii ? "L" : "M"); } fprintf(pps->file, "S\n"); } void plotDots(plotPS *pps, plotParm *pparm, Nrrd **ndata, int nidx) { int ii, npts; double xx, yy, orad, irad, *data, val; if (!( pparm->dotDiameter[nidx] > 0 )) { return; } fprintf(pps->file, "gsave\n"); fprintf(pps->file, "newpath\n"); plotWidth(pps, pparm, 0); data = (double *)(ndata[nidx]->data); npts = ndata[nidx]->axis[1].size; orad = pparm->dotDiameter[nidx]/2; irad = pparm->dotInnerDiameterFraction*orad; for (ii=0; iiaxis[1].min, ndata[nidx]->axis[1].max); xx = AIR_AFFINE(pparm->dbox[0], xx, pparm->dbox[2], pps->bbox[0], pps->bbox[2]); yy = AIR_AFFINE(pparm->dbox[1], val, pparm->dbox[3], pps->bbox[1], pps->bbox[3]); plotGray(pps, pparm, pparm->dotGray[nidx]); fprintf(pps->file, "%g %g %g 0 360 arc closepath fill\n", PPS_X(xx), PPS_Y(yy), PPS_S(orad)); if (irad) { plotGray(pps, pparm, 1.0); fprintf(pps->file, "%g %g %g 0 360 arc closepath fill\n", PPS_X(xx), PPS_Y(yy), PPS_S(irad)); } } fprintf(pps->file, "grestore\n"); } void plotEpilog(plotPS *pps, plotParm *pparm) { AIR_UNUSED(pparm); fprintf(pps->file, "grestore\n"); fprintf(pps->file, "grestore\n"); fprintf(pps->file, "%%%%Trailer\n"); return; } int main(int argc, const char *argv[]) { const char *me; char *err, *outS; hestOpt *hopt=NULL; airArray *mop; int numGrth, numDtdi, numGrgr, numDtgr, numNrrd, ni; plotPS pps; plotParm pparm; Nrrd **_ndata, **ndata; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "i", "data", airTypeOther, 1, -1, &_ndata, NULL, "input nrrd containing data to plot", &numNrrd, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "dbox", "minX minY maxX maxY", airTypeDouble, 4, 4, pparm.dbox, NULL, "bounding box, in data space"); hestOptAdd(&hopt, "bbox", "minX minY maxX maxY", airTypeDouble, 4, 4, pps.bbox, NULL, "bounding box, in graph space"); hestOptAdd(&hopt, "psc", "PS scale", airTypeDouble, 1, 1, &(pps.psc), "300", "scaling from graph space to PostScript points"); hestOptAdd(&hopt, "nobg", NULL, airTypeInt, 0, 0, &(pps.nobg), NULL, "don't fill with background color"); hestOptAdd(&hopt, "bg", "background", airTypeDouble, 3, 3, &(pps.bgColor), "1 1 1", "background RGB color; each component in range [0.0,1.0]"); hestOptAdd(&hopt, "grth", "graph thickness", airTypeDouble, 1, -1, &(pparm.graphThick), "0.01", "thickness of line for graph, or \"0\" for no graph line", &numGrth); hestOptAdd(&hopt, "grgr", "graph gray", airTypeDouble, 1, -1, &(pparm.graphGray), "0", "grayscale to use for graph", &numGrgr); hestOptAdd(&hopt, "dtdi", "dot diameter", airTypeDouble, 1, -1, &(pparm.dotDiameter), "0.1", "radius of dot drawn at data points, or \"0\" for no dots", &numDtdi); hestOptAdd(&hopt, "dtgr", "dot gray", airTypeDouble, 1, -1, &(pparm.dotGray), "0", "grayscale to use for dots", &numDtgr); hestOptAdd(&hopt, "dtid", "dot inner diam frac", airTypeDouble, 1, 1, &(pparm.dotInnerDiameterFraction), "0.0", "fractional radius of white dot drawn within dot"); hestOptAdd(&hopt, "tihz", "pos", airTypeDouble, 0, -1, &(pparm.horzTick), "", "locations for tickmarks on horizontal axis", &(pparm.numHorzTick)); hestOptAdd(&hopt, "tivt", "pos", airTypeDouble, 0, -1, &(pparm.vertTick), "", "locations for tickmarks on vertical axis", &(pparm.numVertTick)); hestOptAdd(&hopt, "tiho", "offset", airTypeDouble, 1, 1, &(pparm.horzTickLabelOffset), "0", "horizontal tick label offset"); hestOptAdd(&hopt, "tivo", "offset", airTypeDouble, 1, 1, &(pparm.vertTickLabelOffset), "0", "vertical tick label offset"); hestOptAdd(&hopt, "tils", "size", airTypeDouble, 1, 1, &(pparm.tickLabelSize), "0", "font size for labels on tick marks, or \"0\" for no labels"); hestOptAdd(&hopt, "tith", "tick thickness", airTypeDouble, 1, 1, &(pparm.tickThick), "0.01", "thickness of lines for tick marks"); hestOptAdd(&hopt, "tiln", "tick length", airTypeDouble, 1, 1, &(pparm.tickLength), "0.08", "length of lines for tick marks"); hestOptAdd(&hopt, "axth", "axis thickness", airTypeDouble, 1, 1, &(pparm.axisThick), "0.01", "thickness of lines for axes"); hestOptAdd(&hopt, "axor", "axis origin", airTypeDouble, 2, 2, &(pparm.axisOrig), "0 0", "origin of lines for axes, in data space"); hestOptAdd(&hopt, "axhl", "horiz axis label", airTypeString, 1, 1, &(pparm.axisHorzLabel), "", "label on horizontal axis"); hestOptAdd(&hopt, "axvl", "vert axis label", airTypeString, 1, 1, &(pparm.axisVertLabel), "", "label on vertical axis"); hestOptAdd(&hopt, "o", "output PS", airTypeString, 1, 1, &outS, "out.ps", "output file to render postscript into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( numGrth == numDtdi && numDtdi == numGrgr && numGrgr == numDtgr )) { fprintf(stderr, "%s: number of arguments given to grth (%d), dtdi (%d), " "grgr (%d), dtgr (%d) not all equal\n", me, numGrth, numDtdi, numGrgr, numDtgr); airMopError(mop); return 1; } if (!( numNrrd == numGrth )) { fprintf(stderr, "%s: number of nrrds (%d) != number graph options (%d)\n", me, numNrrd, numGrth); airMopError(mop); return 1; } /* check nrrds */ for (ni=0; nidim || 2 == _ndata[ni]->dim) && nrrdTypeBlock != _ndata[ni]->type )) { fprintf(stderr, "%s: input nrrd must be 1-D or 2-D array of scalars", me); airMopError(mop); return 1; } } ndata = (Nrrd**)calloc(numNrrd, sizeof(Nrrd *)); airMopAdd(mop, ndata, airFree, airMopAlways); for (ni=0; nidim) { if (nrrdAxesInsert(ndata[ni], ndata[ni], 0)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't insert axis 0 on nrrd %d:\n%s\n", me, ni, err); airMopError(mop); return 1; } } /* currently assuming node centering */ if (!AIR_EXISTS(ndata[ni]->axis[1].min)) { ndata[ni]->axis[1].min = 0; } if (!AIR_EXISTS(ndata[ni]->axis[1].max)) { ndata[ni]->axis[1].max = ndata[ni]->axis[1].size-1; } } if (!(pps.file = airFopen(outS, stdout, "wb"))) { fprintf(stderr, "%s: couldn't open output file\n", me); airMopError(mop); return 1; } airMopAdd(mop, pps.file, (airMopper)airFclose, airMopAlways); plotPreamble(&pps, &pparm); plotAxes(&pps, &pparm, ndata[0]); for (ni=0; nilook + lookSoid; ELL_4V_SET(look->rgba, 1, 1, 1, 1); ELL_3V_SET(look->kads, 0.2, 0.8, 0); look->spow = 0; lookRod = limnObjectLookAdd(obj); look = obj->look + lookRod; ELL_4V_SET(look->rgba, 0, 0, 0, 1); ELL_3V_SET(look->kads, 1, 0, 0); look->spow = 0; ELL_4V_SET(q, 1, p[0], p[1], p[2]); ELL_4V_NORM(q, q, len); ell_q_to_3m_f(mR, q); if (AIR_EXISTS(AB[0]) && AIR_EXISTS(AB[1])) { qA = AB[0]; qB = AB[1]; axis = 2; } else { ELL_3V_SCALE(scale, os, scale); ELL_3V_COPY(eval, scale); ELL_SORT3(eval[0], eval[1], eval[2], cl); cl = (eval[0] - eval[1])/(eval[0] + eval[1] + eval[2]); cp = 2*(eval[1] - eval[2])/(eval[0] + eval[1] + eval[2]); if (cl > cp) { axis = ELL_MAX3_IDX(scale[0], scale[1], scale[2]); qA = pow(1-cp, sh); qB = pow(1-cl, sh); } else { axis = ELL_MIN3_IDX(scale[0], scale[1], scale[2]); qA = pow(1-cl, sh); qB = pow(1-cp, sh); } /* fprintf(stderr, "eval = %g %g %g -> cl=%g %s cp=%g -> axis = %d\n", eval[0], eval[1], eval[2], cl, cl > cp ? ">" : "<", cp, axis); */ } if (sphere) { partIdx = limnObjectPolarSphereAdd(obj, lookSoid, 0, 2*res, res); } else { partIdx = limnObjectPolarSuperquadAdd(obj, lookSoid, axis, qA, qB, 2*res, res); } ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, scale[0], scale[1], scale[2]); ell_4m_post_mul_f(matA, matB); ELL_43M_INSET(matB, mR); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); if (rad) { partIdx = limnObjectCylinderAdd(obj, lookRod, 0, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, (1-eval[0])/2, rad, rad); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, (1+eval[0])/2, 0.0, 0.0); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectCylinderAdd(obj, lookRod, 0, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, (1-eval[0])/2, rad, rad); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, -(1+eval[0])/2, 0.0, 0.0); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectCylinderAdd(obj, lookRod, 1, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, rad, (1-eval[1])/2, rad); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, (1+eval[1])/2, 0.0); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectCylinderAdd(obj, lookRod, 1, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, rad, (1-eval[1])/2, rad); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, -(1+eval[1])/2, 0.0); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectCylinderAdd(obj, lookRod, 2, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, rad, rad, (1-eval[2])/2); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, 0.0, (1+eval[2])/2); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectCylinderAdd(obj, lookRod, 2, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, rad, rad, (1-eval[2])/2); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, 0.0, -(1+eval[2])/2); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); } file = airFopen(outS, stdout, "w"); airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); if (limnObjectWriteOFF(file, obj)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/limn/test/tspline.c0000664000175000017500000001244712042367142020145 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* test/tspline -loop -t 300 300 -s 10 -i s.txt:2v:bc:1,0 -m 1000 > ! out.ps */ /* s.txt: -10 -20 -20 -10 -10 0 -20 10 -10 20 0 10 10 20 20 10 10 0 20 -10 10 -20 0 -10 10 0 0 10 -10 0 0 -10 */ #include "../limn.h" char *info = ("Test limnSplines by drawing postscript curves. " "As such, the only limnSpline allowed is 2vector. " "The output is written to standard out."); int main(int argc, const char *argv[]) { const char *me; char *err; limnSpline *spline, *warp; hestOpt *hopt=NULL; airArray *mop; int i, M, ret, pause, loop; Nrrd *nout, *ntmp; double *out, minT, maxT, scale, tran[2]; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "i", "spline", airTypeOther, 1, 1, &spline, NULL, "the spline that we want to sample", NULL, NULL, limnHestSpline); hestOptAdd(&hopt, "w", "timewarp", airTypeOther, 1, 1, &warp, "", "how to (optionally) warp the spline domain", NULL, NULL, limnHestSpline); hestOptAdd(&hopt, "loop", NULL, airTypeInt, 0, 0, &loop, NULL, "the last control point is in fact the first"); hestOptAdd(&hopt, "m", "M", airTypeInt, 1, 1, &M, "512", "the number of sample points at which to evalute the spline"); hestOptAdd(&hopt, "t", "tx ty", airTypeDouble, 2, 2, tran, "0.0 0.0", "translation for drawing"); hestOptAdd(&hopt, "s", "scale", airTypeDouble, 1, 1, &scale, "1.0", "scaling for drawing"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); spline->loop = loop; if (!( limnSplineInfo2Vector == spline->info )) { fprintf(stderr, "%s: sorry, can only have %s info for PostScript\n", me, airEnumStr(limnSplineInfo, limnSplineInfo2Vector)); airMopError(mop); return 1; } if (warp) { warp->loop = loop; if (!( limnSplineTypeTimeWarp == warp->type )) { fprintf(stderr, "%s: %s spline isn't; its %s\n", me, airEnumStr(limnSplineType, limnSplineTypeTimeWarp), airEnumStr(limnSplineType, warp->type)); airMopError(mop); return 1; } if (loop) { if (!( limnSplineNumPoints(warp) == 1 + limnSplineNumPoints(spline) )) { fprintf(stderr, "%s: # warp points (%d) needs to be 1 more than " "# spline points (%d) for looping\n", me, limnSplineNumPoints(warp), limnSplineNumPoints(spline)); airMopError(mop); return 1; } } else { if (!( limnSplineNumPoints(warp) == limnSplineNumPoints(spline) )) { fprintf(stderr, "%s: # warp points (%d) != # spline points (%d)\n", me, limnSplineNumPoints(warp), limnSplineNumPoints(spline)); airMopError(mop); return 1; } } } airMopAdd(mop, nout=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (warp) { minT = limnSplineMinT(warp); maxT = limnSplineMaxT(warp); ret = (limnSplineSample(ntmp, warp, minT, M, maxT) || limnSplineNrrdEvaluate(nout, spline, ntmp)); } else { minT = limnSplineMinT(spline); maxT = limnSplineMaxT(spline); ret = limnSplineSample(nout, spline, minT, M, maxT); } if (ret) { airMopAdd(mop, err=biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } out = (double*)(nout->data); pause = M/150; printf("%%!\n"); printf("1 setlinewidth\n"); printf("%g %g moveto\n", scale*out[0 + 2*0] + tran[0], scale*out[1 + 2*0] + tran[1]); printf("gsave\n"); printf("0.2 setlinewidth\n"); printf("currentpoint newpath 3 0 360 arc stroke\n"); printf("grestore\n"); for (i=1; ielideSingleEnumType = AIR_TRUE; hparm->elideSingleOtherType = AIR_TRUE; hparm->elideSingleOtherDefault = AIR_FALSE; hparm->elideSingleNonExistFloatDefault = AIR_TRUE; hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hparm->elideSingleEmptyStringDefault = AIR_TRUE; hparm->elideMultipleEmptyStringDefault = AIR_TRUE; hparm->cleverPluralizeOtherY = AIR_TRUE; hparm->columns = 78; /* if there are no arguments, then we give general usage information */ if (1 >= argc) { limnpuUsage(LPU, hparm); airMopError(mop); exit(1); } /* else, we should see if they're asking for a command we know about */ for (i=0; limnpuCmdList[i]; i++) { if (!strcmp(argv[1], limnpuCmdList[i]->name)) { break; } } if (limnpuCmdList[i]) { /* yes, we have that command */ /* initialize variables used by the various commands */ argv0 = (char *)calloc(strlen(LPU) + strlen(argv[1]) + 2, sizeof(char)); airMopMem(mop, &argv0, airMopAlways); sprintf(argv0, "%s %s", LPU, argv[1]); /* run the individual unu program, saving its exit status */ ret = limnpuCmdList[i]->main(argc-2, argv+2, argv0, hparm); } else { fprintf(stderr, "%s: unrecognized command: \"%s\"; type \"%s\" for " "complete list\n", me, argv[1], me); ret = 1; } airMopDone(mop, ret); return ret; } teem-1.11.0~svn6057/src/limn/test/intx.c0000664000175000017500000000641512042367142017447 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../limn.h" char *info = ("test intersection of two lists."); static unsigned int flipListIntx(unsigned int *dstC, const unsigned int *_srcA, const unsigned int *_srcB) { const unsigned int *srcA, *srcB; unsigned int numA, numB, numC, idxA, idxB; numA = _srcA[0]; srcA = _srcA + 1; numB = _srcB[0]; srcB = _srcB + 1; numC = 0; for (idxA=0; idxAV[0], -cam->V[1], -cam->V[2]); fprintf(stderr, "%s: true right = %g %g %g\n", me, cam->U[0], cam->U[1], cam->U[2]); */ if (!E) E |= limnObjectWorldHomog(obj); if (!E) E |= limnObjectFaceNormals(obj, limnSpaceWorld); if (!E) E |= limnObjectSpaceTransform(obj, cam, win, limnSpaceView); if (!E) E |= limnObjectSpaceTransform(obj, cam, win, limnSpaceScreen); if (!E) E |= limnObjectFaceNormals(obj, limnSpaceScreen); if (!E) E |= limnObjectSpaceTransform(obj, cam, win, limnSpaceDevice); if (E) { biffAddf(LIMN, "%s: trouble", me); return 1; } return 0; } void _limnPSPreamble(limnObject *obj, limnCamera *cam, limnWindow *win) { AIR_UNUSED(obj); AIR_UNUSED(cam); fprintf(win->file, "%%!PS-Adobe-2.0 EPSF-2.0\n"); fprintf(win->file, "%%%%Creator: limn\n"); fprintf(win->file, "%%%%Pages: 1\n"); fprintf(win->file, "%%%%BoundingBox: %d %d %d %d\n", (int)(win->bbox[0]), (int)(win->bbox[1]), (int)(win->bbox[2]), (int)(win->bbox[3])); fprintf(win->file, "%%%%EndComments\n"); fprintf(win->file, "%%%%EndProlog\n"); fprintf(win->file, "%%%%Page: 1 1\n"); fprintf(win->file, "gsave\n"); fprintf(win->file, "%g %g moveto\n", win->bbox[0], win->bbox[1]); fprintf(win->file, "%g %g lineto\n", win->bbox[2], win->bbox[1]); fprintf(win->file, "%g %g lineto\n", win->bbox[2], win->bbox[3]); fprintf(win->file, "%g %g lineto\n", win->bbox[0], win->bbox[3]); fprintf(win->file, "closepath\n"); if (!win->ps.noBackground) { fprintf(win->file, "gsave %g %g %g setrgbcolor fill grestore\n", win->ps.bg[0], win->ps.bg[1], win->ps.bg[2]); } fprintf(win->file, "clip\n"); fprintf(win->file, "gsave newpath\n"); fprintf(win->file, "1 setlinejoin\n"); fprintf(win->file, "1 setlinecap\n"); fprintf(win->file, "/M {moveto} bind def\n"); fprintf(win->file, "/L {lineto} bind def\n"); fprintf(win->file, "/W {setlinewidth} bind def\n"); fprintf(win->file, "/F {fill} bind def\n"); fprintf(win->file, "/S {stroke} bind def\n"); fprintf(win->file, "/CP {closepath} bind def\n"); fprintf(win->file, "/RGB {setrgbcolor} bind def\n"); fprintf(win->file, "/Gr {setgray} bind def\n"); fprintf(win->file, "\n"); } void _limnPSEpilogue(limnObject *obj, limnCamera *cam, limnWindow *win) { AIR_UNUSED(obj); AIR_UNUSED(cam); fprintf(win->file, "grestore\n"); fprintf(win->file, "grestore\n"); if (win->ps.showpage) { fprintf(win->file, "showpage\n"); } fprintf(win->file, "%%%%Trailer\n"); } void _limnPSDrawFace(limnObject *obj, limnFace *face, limnCamera *cam, Nrrd *nmap, limnWindow *win) { unsigned int vii; limnVertex *vert; limnLook *look; int qn; float *map, R, G, B; AIR_UNUSED(cam); look = obj->look + face->lookIdx; for (vii=0; viisideNum; vii++) { vert = obj->vert + face->vertIdx[vii]; fprintf(win->file, "%g %g %s\n", vert->coord[0], vert->coord[1], vii ? "L" : "M"); } R = look->kads[0]*look->rgba[0]; G = look->kads[0]*look->rgba[1]; B = look->kads[0]*look->rgba[2]; if (nmap) { qn = limnVtoQN_f[limnQN16octa](face->worldNormal); map = (float *)nmap->data; R += look->kads[1]*look->rgba[0]*map[0 + 3*qn]; G += look->kads[1]*look->rgba[1]*map[1 + 3*qn]; B += look->kads[1]*look->rgba[2]*map[2 + 3*qn]; } else { R += look->kads[1]*look->rgba[0]; G += look->kads[1]*look->rgba[1]; B += look->kads[1]*look->rgba[2]; } /* HEY: not evaluating phong specular for now */ R = AIR_CLAMP(0, R, 1); G = AIR_CLAMP(0, G, 1); B = AIR_CLAMP(0, B, 1); if (R == G && G == B) { fprintf(win->file, "CP %g Gr F\n", R); } else { fprintf(win->file, "CP %g %g %g RGB F\n", R, G, B); } } void _limnPSDrawEdge(limnObject *obj, limnEdge *edge, limnCamera *cam, limnWindow *win) { limnVertex *vert0, *vert1; float R, G, B; AIR_UNUSED(cam); if (win->ps.lineWidth[edge->type]) { vert0 = obj->vert + edge->vertIdx[0]; vert1 = obj->vert + edge->vertIdx[1]; fprintf(win->file, "%g %g M ", vert0->coord[0], vert0->coord[1]); fprintf(win->file, "%g %g L ", vert1->coord[0], vert1->coord[1]); fprintf(win->file, "%g W ", win->ps.lineWidth[edge->type]); R = win->ps.edgeColor[0]; G = win->ps.edgeColor[1]; B = win->ps.edgeColor[2]; if (R == G && G == B) { fprintf(win->file, "%g Gr S\n", R); } else { fprintf(win->file, "%g %g %g RGB S\n", R, G, B); } } } /* ******** limnObjectPSDraw ** ** draws a "rendered" limn object to postscript. ** limnObjectRender MUST be called first. ** ** The current (feeble) justification for using an environment map is ** that its an expressive way of shading things based on surface ** normal, in a context where, if flat shading is all you have, ** correct specular lighting is not possible */ int limnObjectPSDraw(limnObject *obj, limnCamera *cam, Nrrd *nmap, limnWindow *win) { static const char me[]="limnObjectPSDraw"; int inside; float angle; limnFace *face, *face0, *face1; unsigned int fii; limnEdge *edge; unsigned int eii; limnPart *part; unsigned int partIdx; limnVertex *vert; unsigned int vii; if (limnSpaceDevice != obj->vertSpace) { biffAddf(LIMN, "%s: object's verts in %s (not %s) space", me, airEnumStr(limnSpace, obj->vertSpace), airEnumStr(limnSpace, limnSpaceDevice)); return 1; } if (nmap) { if (limnEnvMapCheck(nmap)) { biffAddf(LIMN, "%s: trouble", me); return 1; } } limnObjectDepthSortParts(obj); _limnPSPreamble(obj, cam, win); for (partIdx=0; partIdxpartNum; partIdx++) { part = obj->part[partIdx]; /* only draw the parts that are inside the field of view */ inside = 0; for (vii=0; viivertIdxNum; vii++) { vert = obj->vert + part->vertIdx[vii]; inside |= (AIR_IN_CL(win->bbox[0], vert->coord[0], win->bbox[2]) && AIR_IN_CL(win->bbox[1], vert->coord[1], win->bbox[3])); if (inside) { /* at least vertex is in, we know we can't skip this part */ break; } } if (!inside) { /* none of the vertices were in, we can skip this part */ continue; } /* draw the part */ if (1 == part->edgeIdxNum) { /* this part is just one lone edge */ /* HEY: this is a mess */ /* e = &(obj->e[r->eBase]); widthTmp = win->ps.lineWidth[e->type]; fprintf(win->file, "%g setgray\n", 1 - win->ps.bg[0]); win->ps.edgeWidth[e->type] = 8; _limnPSDrawEdge(obj, r, e, cam, win); fprintf(win->file, "%g %g %g RGB\n", r->rgba[0], r->rgba[1], r->rgba[2]); win->ps.edgeWidth[e->visible] = 4; _limnPSDrawEdge(obj, r, e, cam, win); win->ps.edgeWidth[e->visible] = widthTmp; */ } else { /* this part is either a lone face or a solid: draw the front-facing, shaded faces */ for (fii=0; fiifaceIdxNum; fii++) { face = obj->face + part->faceIdx[fii]; /* The consequence of having a left-handed frame is that world-space CC-wise vertex traversal becomes C-wise screen-space traversal, so all the normals are backwards of what we want */ face->visible = (cam->rightHanded ? face->screenNormal[2] < 0 : face->screenNormal[2] > 0); if (face->sideNum == part->vertIdxNum && !face->visible) { /* lone faces are always visible */ face->visible = AIR_TRUE; ELL_3V_SCALE(face->worldNormal, -1, face->worldNormal); } if (!win->ps.wireFrame && face->visible) { _limnPSDrawFace(obj, face, cam, nmap, win); } } /* draw ALL edges */ for (eii=0; eiiedgeIdxNum; eii++) { /* hack to change contour of particular object/glyph if (24 == partIdx) { win->ps.lineWidth[limnEdgeTypeContour] = 1.2; } else { win->ps.lineWidth[limnEdgeTypeContour] = 0.4; } */ edge = obj->edge + part->edgeIdx[eii]; face0 = obj->face + edge->faceIdx[0]; face1 = (-1 == edge->faceIdx[1] ? NULL : obj->face + edge->faceIdx[1]); if (!face1) { edge->type = limnEdgeTypeBorder; } else { angle = AIR_CAST(float, 180/AIR_PI*acos(ELL_3V_DOT(face0->worldNormal, face1->worldNormal))); if (face0->visible && face1->visible) { edge->type = (angle > win->ps.creaseAngle ? limnEdgeTypeFrontCrease : limnEdgeTypeFrontFacet); } else if (face0->visible ^ face1->visible) { edge->type = limnEdgeTypeContour; } else { edge->type = (angle > win->ps.creaseAngle ? limnEdgeTypeBackCrease : limnEdgeTypeBackFacet); } } _limnPSDrawEdge(obj, edge, cam, win); } } } _limnPSEpilogue(obj, cam, win); return 0; } /* ******** limnObjectPSDrawConcave ** ** new version of the above, which works per-face instead of per-part, ** thus better handling self-occlusions, but at the cost of not getting ** contours near oblique faces correct... */ int limnObjectPSDrawConcave(limnObject *obj, limnCamera *cam, Nrrd *nmap, limnWindow *win) { static const char me[]="limnObjectPSDrawConcave"; float angle; limnPart *part; limnFace *face, *face0, *face1; unsigned int faceIdx; limnEdge *edge; unsigned int edgeIdx, eii; if (limnSpaceDevice != obj->vertSpace) { biffAddf(LIMN, "%s: object's verts in %s (not %s) space", me, airEnumStr(limnSpace, obj->vertSpace), airEnumStr(limnSpace, limnSpaceDevice)); return 1; } if (nmap) { if (limnEnvMapCheck(nmap)) { biffAddf(LIMN, "%s: trouble", me); return 1; } } limnObjectDepthSortFaces(obj); _limnPSPreamble(obj, cam, win); /* set every face's visibility */ for (faceIdx=0; faceIdxfaceNum; faceIdx++) { face = obj->face + faceIdx; part = obj->part[face->partIdx]; face->visible = (cam->rightHanded ? face->screenNormal[2] < 0 : face->screenNormal[2] > 0); if (face->sideNum == part->vertIdxNum && !face->visible) { /* lone faces are always visible */ face->visible = AIR_TRUE; ELL_3V_SCALE(face->worldNormal, -1, face->worldNormal); } } /* categorize all edges by traversing edge array, and looking at each of their two faces */ for (edgeIdx=0; edgeIdxedgeNum; edgeIdx++) { edge = obj->edge + edgeIdx; part = obj->part[edge->partIdx]; face0 = obj->face + edge->faceIdx[0]; face1 = (-1 == edge->faceIdx[1] ? NULL : obj->face + edge->faceIdx[1]); if (!face1) { edge->type = limnEdgeTypeBorder; } else { angle = AIR_CAST(float, 180/AIR_PI*acos(ELL_3V_DOT(face0->worldNormal, face1->worldNormal))); if (face0->visible && face1->visible) { edge->type = (angle > win->ps.creaseAngle ? limnEdgeTypeFrontCrease : limnEdgeTypeFrontFacet); } else if (face0->visible ^ face1->visible) { edge->type = limnEdgeTypeContour; } else { edge->type = (angle > win->ps.creaseAngle ? limnEdgeTypeBackCrease : limnEdgeTypeBackFacet); } } } /* draw front-faces and their edges (contours, front crease, front non-crease) */ for (faceIdx=0; faceIdxfaceNum; faceIdx++) { face = obj->faceSort[faceIdx]; part = obj->part[face->partIdx]; if (!face->visible) { continue; } if (!win->ps.wireFrame) { _limnPSDrawFace(obj, face, cam, nmap, win); } /* draw those edges around the face that won't be seen again by future faces in the depth-first traversal */ for (eii=0; eiisideNum; eii++) { edge = obj->edge + face->edgeIdx[eii]; if (limnEdgeTypeContour == edge->type) { _limnPSDrawEdge(obj, edge, cam, win); } else if (limnEdgeTypeFrontCrease == edge->type || limnEdgeTypeFrontFacet == edge->type) { if (edge->once) { /* its been seen once already, okay to draw */ _limnPSDrawEdge(obj, edge, cam, win); edge->once = AIR_FALSE; } else { /* we're the first to see it, and we're not the last, don't draw */ edge->once = AIR_TRUE; } } } } _limnPSEpilogue(obj, cam, win); return 0; } teem-1.11.0~svn6057/src/limn/TODO.txt0000664000175000017500000000004710055147551016645 0ustar domibeldomibelpostscript generation of 1D data plots teem-1.11.0~svn6057/src/limn/lpu_rast.c0000664000175000017500000000574212173673666017361 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "limn.h" #include "privateLimn.h" #define INFO "Rasterize polydata" static const char *myinfo = (INFO ". "); int limnpu_rastMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *hopt = NULL; char *err, *perr; airArray *mop; int pret; limnPolyData *pld; double min[3], max[3]; Nrrd *nout; char *out; int type; size_t size[NRRD_DIM_MAX]; hestOptAdd(&hopt, "min", "min", airTypeDouble, 3, 3, min, NULL, "bottom corner"); hestOptAdd(&hopt, "max", "max", airTypeDouble, 3, 3, max, NULL, "top corner"); hestOptAdd(&hopt, "s", "size", airTypeSize_t, 3, 3, size, NULL, "number of samples along each axis"); hestOptAdd(&hopt, "t", "type", airTypeEnum, 1, 1, &type, "uchar", "type of output nrrd", NULL, nrrdType); hestOptAdd(&hopt, NULL, "input", airTypeOther, 1, 1, &pld, NULL, "input polydata filename", NULL, NULL, limnHestPolyDataLMPD); hestOptAdd(&hopt, NULL, "output", airTypeString, 1, 1, &out, NULL, "output nrrd filename"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(myinfo); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (limnPolyDataRasterize(nout, pld, min, max, size, type)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:%s", me, err); airMopError(mop); return 1; } if (nrrdSave(out, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } unrrduCmd limnpu_rastCmd = { "rast", INFO, limnpu_rastMain, AIR_FALSE }; teem-1.11.0~svn6057/src/preamble.mk0000664000175000017500000000217612165631065016527 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images . # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # teem-1.11.0~svn6057/src/hoover/0000775000175000017500000000000012203513753015677 5ustar domibeldomibelteem-1.11.0~svn6057/src/hoover/methodsHoover.c0000664000175000017500000001362012165631065020677 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hoover.h" hooverContext * hooverContextNew() { hooverContext *ctx; ctx = (hooverContext *)calloc(1, sizeof(hooverContext)); if (ctx) { ctx->cam = limnCameraNew(); ELL_3V_SET(ctx->volSize, 0, 0, 0); ELL_3V_SET(ctx->volSpacing, AIR_NAN, AIR_NAN, AIR_NAN); ctx->volCentering = hooverDefVolCentering; ctx->shape = NULL; ctx->imgSize[0] = ctx->imgSize[1] = 0; ctx->imgCentering = hooverDefImgCentering; ctx->user = NULL; ctx->numThreads = 1; ctx->workIdx = 0; ctx->workMutex = NULL; ctx->renderBegin = hooverStubRenderBegin; ctx->threadBegin = hooverStubThreadBegin; ctx->rayBegin = hooverStubRayBegin; ctx->sample = hooverStubSample; ctx->rayEnd = hooverStubRayEnd; ctx->threadEnd = hooverStubThreadEnd; ctx->renderEnd = hooverStubRenderEnd; } return(ctx); } int hooverContextCheck(hooverContext *ctx) { static const char me[]="hooverContextCheck"; int sxe, sye, sze, minSize, centr; if (!ctx) { biffAddf(HOOVER, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(nrrdCenter, ctx->imgCentering)) { biffAddf(HOOVER, "%s: pixel centering (%d) invalid", me, ctx->imgCentering); return 1; } centr = (ctx->shape ? ctx->shape->center : ctx->volCentering); if (airEnumValCheck(nrrdCenter, centr)) { biffAddf(HOOVER, "%s: voxel centering (%d) invalid", me, centr); return 1; } if (limnCameraAspectSet(ctx->cam, ctx->imgSize[0], ctx->imgSize[1], ctx->imgCentering) || limnCameraUpdate(ctx->cam)) { biffMovef(HOOVER, LIMN, "%s: trouble setting up camera", me); return 1; } if (ctx->shape) { if (!ELL_4M_EXISTS(ctx->shape->ItoW)) { biffAddf(HOOVER, "%s: given shape doesn't seem to be set", me); return 1; } } else { minSize = (nrrdCenterCell == centr ? 1 : 2); if (!(ctx->volSize[0] >= minSize && ctx->volSize[1] >= minSize && ctx->volSize[2] >= minSize)) { biffAddf(HOOVER, "%s: volume dimensions (%dx%dx%d) too small", me, ctx->volSize[0], ctx->volSize[1], ctx->volSize[2]); return 1; } sxe = AIR_EXISTS(ctx->volSpacing[0]); sye = AIR_EXISTS(ctx->volSpacing[1]); sze = AIR_EXISTS(ctx->volSpacing[2]); if (!sxe && !sye && !sze) { /* none of the incoming spacings existed, we'll go out on a limb and assume unit spacing */ ctx->volSpacing[0] = nrrdDefaultSpacing; ctx->volSpacing[1] = ctx->volSpacing[2] = ctx->volSpacing[0]; fprintf(stderr, "%s: WARNING: assuming spacing %g for all axes\n", me, ctx->volSpacing[0]); /* HEY : nrrdDefaultSpacing need not be the same as gageParm's defaultSpacing, but we don't know anything about gage here, so what else can we do? */ } else if (sxe && sye && sze) { /* all existed */ if (!(ctx->volSpacing[0] > 0.0 && ctx->volSpacing[1] > 0.0 && ctx->volSpacing[2] > 0.0)) { biffAddf(HOOVER, "%s: volume spacing (%gx%gx%g) invalid", me, ctx->volSpacing[0], ctx->volSpacing[1], ctx->volSpacing[2]); return 1; } } else { /* some existed, some didn't */ biffAddf(HOOVER, "%s: spacings %g, %g, %g don't all exist or not", me, ctx->volSpacing[0], ctx->volSpacing[1], ctx->volSpacing[2]); return 1; } } if (!(ctx->imgSize[0] > 0 && ctx->imgSize[1] > 0)) { biffAddf(HOOVER, "%s: image dimensions (%dx%d) invalid", me, ctx->imgSize[0], ctx->imgSize[1]); return 1; } if (!(ctx->numThreads >= 1)) { biffAddf(HOOVER, "%s: number threads (%d) invalid", me, ctx->numThreads); return 1; } if (!(ctx->numThreads <= HOOVER_THREAD_MAX)) { biffAddf(HOOVER, "%s: sorry, number threads (%d) > max (%d)", me, ctx->numThreads, HOOVER_THREAD_MAX); return 1; } if (!ctx->renderBegin) { biffAddf(HOOVER, "%s: need a non-NULL begin rendering callback", me); return 1; } if (!ctx->rayBegin) { biffAddf(HOOVER, "%s: need a non-NULL begin ray callback", me); return 1; } if (!ctx->threadBegin) { biffAddf(HOOVER, "%s: need a non-NULL begin thread callback", me); return 1; } if (!ctx->sample) { biffAddf(HOOVER, "%s: need a non-NULL sampler callback function", me); return 1; } if (!ctx->rayEnd) { biffAddf(HOOVER, "%s: need a non-NULL end ray callback", me); return 1; } if (!ctx->threadEnd) { biffAddf(HOOVER, "%s: need a non-NULL end thread callback", me); return 1; } if (!ctx->renderEnd) { biffAddf(HOOVER, "%s: need a non-NULL end render callback", me); return 1; } return 0; } void hooverContextNix(hooverContext *ctx) { if (ctx) { limnCameraNix(ctx->cam); /* workMutex is cleaned up at end of render */ free(ctx); } } teem-1.11.0~svn6057/src/hoover/stub.c0000664000175000017500000000677112165631065017037 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hoover.h" int hooverStubRenderBegin(void **rendInfoP, void *userInfo) { AIR_UNUSED(userInfo); *rendInfoP = NULL; return 0; } int hooverStubThreadBegin(void **threadInfoP, void *rendInfo, void *userInfo, int whichThread) { AIR_UNUSED(rendInfo); AIR_UNUSED(userInfo); AIR_UNUSED(whichThread); *threadInfoP = NULL; return 0; } int hooverStubRayBegin(void *threadInfo, void *renderInfo, void *userInfo, int uIndex, int vIndex, double rayLen, double rayStartWorld[3], double rayStartIndex[3], double rayDirWorld[3], double rayDirIndex[3]) { AIR_UNUSED(threadInfo); AIR_UNUSED(renderInfo); AIR_UNUSED(userInfo); AIR_UNUSED(uIndex); AIR_UNUSED(vIndex); AIR_UNUSED(rayLen); AIR_UNUSED(rayStartWorld); AIR_UNUSED(rayStartIndex); AIR_UNUSED(rayDirWorld); AIR_UNUSED(rayDirIndex); /* char me[]="hooverStubRayBegin"; fprintf(stderr, "%s: ray(%d,%d), len=%g\n" " start=(%g,%g,%g)\n" " dir=(%g,%g,%g)\n", me, uIndex, vIndex, rayLen, rayStartWorld[0], rayStartWorld[1], rayStartWorld[2], rayDirWorld[0], rayDirWorld[1], rayDirWorld[2]); */ return 0; } double hooverStubSample(void *threadInfo, void *renderInfo, void *userInfo, int num, double rayT, int inside, double samplePosWorld[3], double samplePosIndex[3]) { AIR_UNUSED(threadInfo); AIR_UNUSED(renderInfo); AIR_UNUSED(userInfo); AIR_UNUSED(num); AIR_UNUSED(rayT); AIR_UNUSED(inside); AIR_UNUSED(samplePosWorld); AIR_UNUSED(samplePosIndex); /* char me[]="hooverStubSample"; fprintf(stderr, "%s: sample(%g,%g,%g)\n", me, samplePosWorld[0], samplePosWorld[1], samplePosWorld[2]); */ /* we want the stub renderer to actually finish */ return 1.0; } int hooverStubRayEnd(void *threadInfo, void *rendInfo, void *userInfo) { AIR_UNUSED(threadInfo); AIR_UNUSED(rendInfo); AIR_UNUSED(userInfo); return 0; } int hooverStubThreadEnd(void *threadInfo, void *rendInfo, void *userInfo) { AIR_UNUSED(threadInfo); AIR_UNUSED(rendInfo); AIR_UNUSED(userInfo); return 0; } int hooverStubRenderEnd(void *rendInfo, void *userInfo) { AIR_UNUSED(rendInfo); AIR_UNUSED(userInfo); return 0; } teem-1.11.0~svn6057/src/hoover/GNUmakefile0000664000175000017500000000341612165631065017761 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := hoover #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### $(L).NEED = limn ell nrrd biff air $(L).PUBLIC_HEADERS = hoover.h $(L).OBJS = defaultsHoover.o stub.o methodsHoover.o rays.o #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/hoover/sources.cmake0000664000175000017500000000034611113047450020362 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(HOOVER_SOURCES defaultsHoover.c hoover.h methodsHoover.c rays.c stub.c ) ADD_TEEM_LIBRARY(hoover ${HOOVER_SOURCES}) teem-1.11.0~svn6057/src/hoover/hoover.h0000664000175000017500000002457412165631065017372 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HOOVER_HAS_BEEN_INCLUDED #define HOOVER_HAS_BEEN_INCLUDED #include #include #include #include #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(hoover_EXPORTS) || defined(teem_EXPORTS) # define HOOVER_EXPORT extern __declspec(dllexport) # else # define HOOVER_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define HOOVER_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define HOOVER hooverBiffKey #define HOOVER_THREAD_MAX 512 /* ******** the mess of typedefs for callbacks used below */ typedef int (hooverRenderBegin_t)(void **renderP, void *user); typedef int (hooverThreadBegin_t)(void **threadP, void *render, void *user, int whichThread); typedef int (hooverRayBegin_t)(void *thread, void *render, void *user, int uIndex, /* img coords of current ray */ int vIndex, double rayLen, /* length of ray segment between near and far planes, */ double rayStartWorld[3], double rayStartIndex[3], double rayDirWorld[3], double rayDirIndex[3]); typedef double (hooverSample_t)(void *thread, void *render, void *user, int num, /* which sample this is, 0-based */ double rayT,/* position along ray */ int inside, /* sample is inside the volume */ double samplePosWorld[3], double samplePosIndex[3]); typedef int (hooverRayEnd_t)(void *thread, void *render, void *user); typedef int (hooverThreadEnd_t)(void *thread, void *render, void *user); typedef int (hooverRenderEnd_t)(void *rend, void *user); /* ******** hooverContext struct ** ** Everything that hooverRender() needs to do its thing, and no more. ** This is all read-only information. ** 1) camera information ** 3) volume information ** 4) image information ** 5) opaque "user information" pointer ** 6) stuff about multi-threading ** 7) the callbacks */ typedef struct { /******** 1) camera information */ limnCamera *cam; /* camera info */ /******** 2) volume information: size and spacing, centering, or a gageShape that sets everything */ int volSize[3]; /* X,Y,Z resolution of volume */ double volSpacing[3]; /* distance between samples in X,Y,Z direction */ int volCentering; /* either nrrdCenterNode or nrrdCenterCell */ const gageShape *shape; /* if non-NULL, use this gageShape (which we do NOT own), which over-rides volSize, volSpacing, volCentering */ /******** 3) image information: dimensions + centering */ int imgSize[2], /* # samples of image along U and V axes */ imgCentering; /* either nrrdCenterNode or nrrdCenterCell */ /******** 4) opaque "user information" pointer */ void *user; /* passed to all callbacks */ /******** 5) stuff about multi-threading */ unsigned int numThreads; /* number of threads to spawn per rendering */ int workIdx; /* next work assignment (such as a scanline) */ airThreadMutex *workMutex; /* mutex around work assignment */ /* ******* 6) the callbacks ** ** The conceptual ordering of these callbacks is as they are listed ** below. For example, rayBegin and rayEnd are called multiple ** times between threadBegin and threadEnd, and so on. All of these ** are initialized to one of the stub functions provided by hoover. ** ** A non-zero return of any of these indicates error. Which callback ** failed is represented by the return value of hooverRender(), the ** return value from the callback is stored in *errCodeP by ** hooverRender(), and the problem thread number is stored in ** *errThreadP. */ /* ** renderBegin() ** ** called once at beginning of whole rendering, and ** *renderP is passed to all following calls as "render". ** Any mechanisms for inter-thread communication go nicely in ** the render. ** ** int (*renderBegin)(void **renderP, void *user); */ hooverRenderBegin_t *renderBegin; /* ** threadBegin() ** ** called once per thread, and *threadP is passed to all ** following calls as "thread". ** ** int (*threadBegin)(void **threadP, void *render, void *user, ** int whichThread); */ hooverThreadBegin_t *threadBegin; /* ** rayBegin() ** ** called once at the beginning of each ray. This function will be ** called regardless of whether the ray actually intersects the ** volume, but this will change in the future. ** ** int (*rayBegin)(void *thread, void *render, void *user, ** int uIndex, int vIndex, ** double rayLen, ** double rayStartWorld[3], double rayStartIndex[3], ** double rayDirWorld[3], double rayDirIndex[3]); */ hooverRayBegin_t *rayBegin; /* ** sample() ** ** called once per sample along the ray, and the return value is ** used to indicate how far to increment the ray position for the ** next sample. Negative values back you up. A return of 0.0 is ** taken to mean a non-erroneous ray termination, a return of NaN is ** taken to mean an error condition. It is the user's ** responsibility to store an error code or whatever they want ** somewhere accessible. ** ** This is not a terribly flexible scheme (don't forget, this is ** hoover) in that it imposes some constraints on how multi-threading ** can work: one thread can not render multiple rays ** simulatenously. If there were more args to sample() (like a ** ray, or an integral rayIndex), then this would be possible, ** but it would mean that _hooverThreadBody() would have to ** implement all the smarts about which samples belong on which rays, ** and which rays belong with which threads. ** ** At some point now or in the future, an effort will be made to ** never call this function if the ray does not in fact intersect ** the volume at all. ** ** double (*sample)(void *thread, void *render, void *user, ** int num, double rayT, int inside, ** double samplePosWorld[3], ** double samplePosIndex[3]); */ hooverSample_t *sample; /* ** rayEnd() ** ** called at the end of the ray. The end of a ray is when: ** 1) sample returns 0.0, or, ** 2) the sample location goes behind far plane ** ** int (*rayEnd)(void *thread, void *render, void *user); */ hooverRayEnd_t *rayEnd; /* ** threadEnd() ** ** called at end of thread ** ** int (*threadEnd)(void *thread, void *render, void *user); */ hooverThreadEnd_t *threadEnd; /* ** renderEnd() ** ** called once at end of whole rendering ** ** int (*renderEnd)(void *rend, void *user); */ hooverRenderEnd_t *renderEnd; } hooverContext; /* ******** hooverErr* enum ** ** possible returns from hooverRender. ** hooverErrNone: no error, all is well: ** hooverErrInit: error detected in hoover, call biffGet(HOOVER) ** otherwise, return indicates which call-back had trouble */ enum { hooverErrNone, /* 0 */ hooverErrInit, /* 1: call biffGet(HOOVER) */ hooverErrRenderBegin, /* 2 */ hooverErrThreadCreate, /* 3 */ hooverErrThreadBegin, /* 4 */ hooverErrRayBegin, /* 5 */ hooverErrSample, /* 6 */ hooverErrRayEnd, /* 7 */ hooverErrThreadEnd, /* 8 */ hooverErrThreadJoin, /* 9 */ hooverErrRenderEnd, /* 10 */ hooverErrLast }; #define HOOVER_ERR_MAX 10 /* defaultsHoover.c */ HOOVER_EXPORT const int hooverPresent; HOOVER_EXPORT const char *hooverBiffKey; HOOVER_EXPORT int hooverDefVolCentering; HOOVER_EXPORT int hooverDefImgCentering; HOOVER_EXPORT const airEnum *const hooverErr; /* methodsHoover.c */ HOOVER_EXPORT hooverContext *hooverContextNew(void); HOOVER_EXPORT int hooverContextCheck(hooverContext *ctx); HOOVER_EXPORT void hooverContextNix(hooverContext *ctx); /* rays.c */ HOOVER_EXPORT int hooverRender(hooverContext *ctx, int *errCodeP, int *errThreadP); /* stub.c */ HOOVER_EXPORT hooverRenderBegin_t hooverStubRenderBegin; HOOVER_EXPORT hooverThreadBegin_t hooverStubThreadBegin; HOOVER_EXPORT hooverRayBegin_t hooverStubRayBegin; HOOVER_EXPORT hooverSample_t hooverStubSample; HOOVER_EXPORT hooverRayEnd_t hooverStubRayEnd; HOOVER_EXPORT hooverThreadEnd_t hooverStubThreadEnd; HOOVER_EXPORT hooverRenderEnd_t hooverStubRenderEnd; #ifdef __cplusplus } #endif #endif /* HOOVER_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/hoover/defaultsHoover.c0000664000175000017500000000367212165631065021051 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hoover.h" const int hooverPresent = 42; const char * hooverBiffKey = "hoover"; int hooverDefVolCentering = nrrdCenterNode; int hooverDefImgCentering = nrrdCenterCell; const char * _hooverErrStr[HOOVER_ERR_MAX+1] = { "(unknown_err)", "Initialization", "RenderBegin", "ThreadCreate", "ThreadBegin", "RayBegin", "Sample", "RayEnd", "ThreadEnd", "ThreadJoin", "RenderEnd" }; const airEnum _hooverErr = { "error", HOOVER_ERR_MAX, _hooverErrStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const hooverErr = &_hooverErr; /* hooverErrNone, hooverErrInit, hooverErrRenderBegin, hooverErrThreadCreate, hooverErrThreadBegin, hooverErrRayBegin, hooverErrSample, hooverErrRayEnd, hooverErrThreadEnd, hooverErrThreadJoin, hooverErrRenderEnd, hooverErrLast */ teem-1.11.0~svn6057/src/hoover/rays.c0000664000175000017500000004146712165631065017041 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hoover.h" /* ** learned: if you're going to "simplify" code which computes some ** floating point value within a loop using AFFINE() on the loop ** control variable, by simply incrementing that value with the ** correct amount iteration, BE SURE THAT THE INCREMENTING IS DONE in ** every possible control path of the loop (wasn't incrementing ray ** sample position if first sample wasn't inside the volume) */ /* ** _hooverLearnLengths() ** ** This is where we enforce the constraint that the volume always fit ** inside a cube with edge length 2, centered at the origin. ** ** volHLen[i] is the HALF the length of the volume along axis i ** ** NOTE: none of this comes into play if we have ctx->shape */ void _hooverLearnLengths(double volHLen[3], double voxLen[3], hooverContext *ctx) { double maxLen; int numSamples[3], numElements[3]; ELL_3V_COPY(numSamples, ctx->volSize); if (nrrdCenterNode == ctx->volCentering) { numElements[0] = numSamples[0]-1; numElements[1] = numSamples[1]-1; numElements[2] = numSamples[2]-1; } else { numElements[0] = numSamples[0]; numElements[1] = numSamples[1]; numElements[2] = numSamples[2]; } volHLen[0] = numElements[0]*ctx->volSpacing[0]; volHLen[1] = numElements[1]*ctx->volSpacing[1]; volHLen[2] = numElements[2]*ctx->volSpacing[2]; maxLen = AIR_MAX(volHLen[0], volHLen[1]); maxLen = AIR_MAX(volHLen[2], maxLen); volHLen[0] /= maxLen; volHLen[1] /= maxLen; volHLen[2] /= maxLen; voxLen[0] = 2*volHLen[0]/numElements[0]; voxLen[1] = 2*volHLen[1]/numElements[1]; voxLen[2] = 2*volHLen[2]/numElements[2]; } /* ** _hooverExtraContext struct ** ** Like hooverContext, this is READ-ONLY information which is not specific ** to any thread. ** Unlike hooverContext, it is solely for the benefit of the calculations ** done in _hooverThreadBody. ** ** No one outside hoover should need to know about this. */ typedef struct { double volHLen[3], /* length of x,y,z edges of volume bounding box */ voxLen[3], /* length of x,y,z edges of voxels */ uBase, uCap, /* uMin and uMax as seen on the near cutting plane */ vBase, vCap, /* analogous to uBase and uCap */ rayZero[3]; /* location of near plane, line of sight interxion */ } _hooverExtraContext; _hooverExtraContext * _hooverExtraContextNew(hooverContext *ctx) { _hooverExtraContext *ec; ec = (_hooverExtraContext *)calloc(1, sizeof(_hooverExtraContext)); if (ec) { if (ctx->shape) { ELL_3V_NAN_SET(ec->volHLen); ELL_3V_NAN_SET(ec->voxLen); } else { _hooverLearnLengths(ec->volHLen, ec->voxLen, ctx); } ELL_3V_SCALE_ADD2(ec->rayZero, 1.0, ctx->cam->from, ctx->cam->vspNeer, ctx->cam->N); } return ec; } _hooverExtraContext * _hooverExtraContextNix(_hooverExtraContext *ec) { if (ec) { free(ec); } return NULL; } /* ** _hooverThreadArg struct ** ** A pointer to this is passed to _hooverThreadBody. It contains all the ** information which is not thread-specific, and all the thread-specific ** information known at the level of hooverRender. ** ** For simplicity sake, a pointer to a struct of this type is also ** returned from _hooverThreadBody, so this is where we store an ** error-signaling return value (errCode), and what function had ** trouble (whichErr). */ typedef struct { /* ----------------------- input */ hooverContext *ctx; _hooverExtraContext *ec; void *render; int whichThread; /* ----------------------- output */ int whichErr; int errCode; } _hooverThreadArg; void * _hooverThreadBody(void *_arg) { _hooverThreadArg *arg; void *thread; int ret, /* to catch return values from callbacks */ sampleI, /* which sample we're on */ inside, /* we're inside the volume */ vI, uI; /* integral coords in image */ double tmp, mm, /* lowest position in index space, for all axes */ Mx, My, Mz, /* highest position in index space on each axis */ u, v, /* floating-point coords in image */ uvScale, /* how to scale (u,v) to go from image to near plane, according to ortho or perspective */ lx, ly, lz, /* half edge-lengths of volume */ rayLen=0, /* length of segment formed by ray line intersecting the near and far clipping planes */ rayT, /* current position along ray (world-space) */ rayDirW[3], /* unit-length ray direction (world-space) */ rayDirI[3], /* rayDirW transformed into index space; not unit length, but a unit change in world space along rayDirW translates to this change in index space along rayDirI */ rayPosW[3], /* current ray location (world-space) */ rayPosI[3], /* current ray location (index-space) */ rayStartW[3], /* ray start on near plane (world-space) */ rayStartI[3], /* ray start on near plane (index-space) */ rayStep, /* distance between samples (world-space) */ vOff[3], uOff[3]; /* offsets in arg->ec->wU and arg->ec->wV directions towards start of ray */ arg = (_hooverThreadArg *)_arg; if ( (ret = (arg->ctx->threadBegin)(&thread, arg->render, arg->ctx->user, arg->whichThread)) ) { arg->errCode = ret; arg->whichErr = hooverErrThreadBegin; return arg; } if (arg->ctx->shape) { lx = ly = lz = AIR_NAN; if (nrrdCenterNode == arg->ctx->shape->center) { mm = 0; Mx = arg->ctx->shape->size[0]-1; My = arg->ctx->shape->size[1]-1; Mz = arg->ctx->shape->size[2]-1; } else { mm = -0.5; Mx = arg->ctx->shape->size[0]-0.5; My = arg->ctx->shape->size[1]-0.5; Mz = arg->ctx->shape->size[2]-0.5; } } else { lx = arg->ec->volHLen[0]; ly = arg->ec->volHLen[1]; lz = arg->ec->volHLen[2]; if (nrrdCenterNode == arg->ctx->volCentering) { mm = 0; Mx = arg->ctx->volSize[0]-1; My = arg->ctx->volSize[1]-1; Mz = arg->ctx->volSize[2]-1; } else { mm = -0.5; Mx = arg->ctx->volSize[0]-0.5; My = arg->ctx->volSize[1]-0.5; Mz = arg->ctx->volSize[2]-0.5; } } if (arg->ctx->cam->orthographic) { ELL_3V_COPY(rayDirW, arg->ctx->cam->N); if (arg->ctx->shape) { double zeroW[3], zeroI[3]; ELL_3V_SET(zeroW, 0, 0, 0); gageShapeWtoI(arg->ctx->shape, zeroI, zeroW); gageShapeWtoI(arg->ctx->shape, rayDirI, rayDirW); ELL_3V_SUB(rayDirI, rayDirI, zeroI); } else { rayDirI[0] = AIR_DELTA(-lx, rayDirW[0], lx, mm, Mx); rayDirI[1] = AIR_DELTA(-ly, rayDirW[1], ly, mm, My); rayDirI[2] = AIR_DELTA(-lz, rayDirW[2], lz, mm, Mz); } rayLen = arg->ctx->cam->vspFaar - arg->ctx->cam->vspNeer; uvScale = 1.0; } else { uvScale = arg->ctx->cam->vspNeer/arg->ctx->cam->vspDist; } while (1) { /* the work assignment is simply the next scanline to be rendered: the result of all this is setting vI */ if (arg->ctx->workMutex) { airThreadMutexLock(arg->ctx->workMutex); } vI = arg->ctx->workIdx; if (arg->ctx->workIdx < arg->ctx->imgSize[1]) { arg->ctx->workIdx += 1; } if (arg->ctx->workMutex) { airThreadMutexUnlock(arg->ctx->workMutex); } if (vI == arg->ctx->imgSize[1]) { /* we're done! */ break; } if (nrrdCenterCell == arg->ctx->imgCentering) { v = uvScale*AIR_AFFINE(-0.5, vI, arg->ctx->imgSize[1]-0.5, arg->ctx->cam->vRange[0], arg->ctx->cam->vRange[1]); } else { v = uvScale*AIR_AFFINE(0.0, vI, arg->ctx->imgSize[1]-1.0, arg->ctx->cam->vRange[0], arg->ctx->cam->vRange[1]); } ELL_3V_SCALE(vOff, v, arg->ctx->cam->V); for (uI=0; uIctx->imgSize[0]; uI++) { if (nrrdCenterCell == arg->ctx->imgCentering) { u = uvScale*AIR_AFFINE(-0.5, uI, arg->ctx->imgSize[0]-0.5, arg->ctx->cam->uRange[0], arg->ctx->cam->uRange[1]); } else { u = uvScale*AIR_AFFINE(0.0, uI, arg->ctx->imgSize[0]-1.0, arg->ctx->cam->uRange[0], arg->ctx->cam->uRange[1]); } ELL_3V_SCALE(uOff, u, arg->ctx->cam->U); ELL_3V_ADD3(rayStartW, uOff, vOff, arg->ec->rayZero); if (arg->ctx->shape) { gageShapeWtoI(arg->ctx->shape, rayStartI, rayStartW); } else { rayStartI[0] = AIR_AFFINE(-lx, rayStartW[0], lx, mm, Mx); rayStartI[1] = AIR_AFFINE(-ly, rayStartW[1], ly, mm, My); rayStartI[2] = AIR_AFFINE(-lz, rayStartW[2], lz, mm, Mz); } if (!arg->ctx->cam->orthographic) { ELL_3V_SUB(rayDirW, rayStartW, arg->ctx->cam->from); ELL_3V_NORM(rayDirW, rayDirW, tmp); if (arg->ctx->shape) { double zeroW[3], zeroI[3]; ELL_3V_SET(zeroW, 0, 0, 0); gageShapeWtoI(arg->ctx->shape, zeroI, zeroW); gageShapeWtoI(arg->ctx->shape, rayDirI, rayDirW); ELL_3V_SUB(rayDirI, rayDirI, zeroI); } else { rayDirI[0] = AIR_DELTA(-lx, rayDirW[0], lx, mm, Mx); rayDirI[1] = AIR_DELTA(-ly, rayDirW[1], ly, mm, My); rayDirI[2] = AIR_DELTA(-lz, rayDirW[2], lz, mm, Mz); } rayLen = ((arg->ctx->cam->vspFaar - arg->ctx->cam->vspNeer)/ ELL_3V_DOT(rayDirW, arg->ctx->cam->N)); } if ( (ret = (arg->ctx->rayBegin)(thread, arg->render, arg->ctx->user, uI, vI, rayLen, rayStartW, rayStartI, rayDirW, rayDirI)) ) { arg->errCode = ret; arg->whichErr = hooverErrRayBegin; return arg; } sampleI = 0; rayT = 0; while (1) { ELL_3V_SCALE_ADD2(rayPosW, 1.0, rayStartW, rayT, rayDirW); if (arg->ctx->shape) { gageShapeWtoI(arg->ctx->shape, rayPosI, rayPosW); } else { ELL_3V_SCALE_ADD2(rayPosI, 1.0, rayStartI, rayT, rayDirI); } inside = (AIR_IN_CL(mm, rayPosI[0], Mx) && AIR_IN_CL(mm, rayPosI[1], My) && AIR_IN_CL(mm, rayPosI[2], Mz)); rayStep = (arg->ctx->sample)(thread, arg->render, arg->ctx->user, sampleI, rayT, inside, rayPosW, rayPosI); if (!AIR_EXISTS(rayStep)) { /* sampling failed */ arg->errCode = 0; arg->whichErr = hooverErrSample; return arg; } if (!rayStep) { /* ray decided to finish itself */ break; } /* else we moved to a new location along the ray */ rayT += rayStep; if (!AIR_IN_CL(0, rayT, rayLen)) { /* ray stepped outside near-far clipping region, its done. */ break; } sampleI++; } if ( (ret = (arg->ctx->rayEnd)(thread, arg->render, arg->ctx->user)) ) { arg->errCode = ret; arg->whichErr = hooverErrRayEnd; return arg; } } /* end this scanline */ } /* end while(1) assignment of scanlines */ if ( (ret = (arg->ctx->threadEnd)(thread, arg->render, arg->ctx->user)) ) { arg->errCode = ret; arg->whichErr = hooverErrThreadEnd; return arg; } /* returning NULL actually indicates that there was NOT an error */ return NULL; } typedef union { _hooverThreadArg **h; void **v; } _htpu; /* ******** hooverRender() ** ** because of the biff usage(), only one thread can call hooverRender(), ** and no promises if the threads themselves call biff... */ int hooverRender(hooverContext *ctx, int *errCodeP, int *errThreadP) { static const char me[]="hooverRender"; _hooverExtraContext *ec; _hooverThreadArg args[HOOVER_THREAD_MAX]; _hooverThreadArg *errArg; airThread *thread[HOOVER_THREAD_MAX]; _htpu u; void *render; int ret; airArray *mop; unsigned int threadIdx; if (!( errCodeP && errThreadP )) { biffAddf(HOOVER, "%s: got NULL int return pointer", me); return hooverErrInit; } /* this calls limnCameraUpdate() */ if (hooverContextCheck(ctx)) { biffAddf(HOOVER, "%s: problem detected in given context", me); *errCodeP = 0; *errThreadP = 0; return hooverErrInit; } if (!(ec = _hooverExtraContextNew(ctx))) { biffAddf(HOOVER, "%s: problem creating thread context", me); *errCodeP = 0; *errThreadP = 0; return hooverErrInit; } mop = airMopNew(); airMopAdd(mop, ec, (airMopper)_hooverExtraContextNix, airMopAlways); if ( (ret = (ctx->renderBegin)(&render, ctx->user)) ) { *errCodeP = ret; *errCodeP = 0; *errThreadP = 0; airMopError(mop); return hooverErrRenderBegin; } for (threadIdx=0; threadIdxnumThreads; threadIdx++) { args[threadIdx].ctx = ctx; args[threadIdx].ec = ec; args[threadIdx].render = render; args[threadIdx].whichThread = threadIdx; args[threadIdx].whichErr = hooverErrNone; args[threadIdx].errCode = 0; thread[threadIdx] = airThreadNew(); } ctx->workIdx = 0; if (1 < ctx->numThreads) { ctx->workMutex = airThreadMutexNew(); } else { ctx->workMutex = NULL; } /* (done): call airThreadStart() once per thread, passing the address of a distinct (and appropriately intialized) _hooverThreadArg to each. If return of airThreadStart() is non-zero, put its return in *errCodeP, the number of the problematic in *errThreadP, and return hooverErrThreadCreate. Then call airThreadJoin() on all the threads, passing &errArg as "retval". On non-zero return, set *errCodeP and *errThreadP, and return hooverErrThreadJoin. If return of airThreadJoin() is zero, but the errArg is non-NULL, then assume that this errArg is actually just the passed _hooverThreadArg returned to us, and from this copy errArg->errCode into *errCodeP, and return errArg->whichErr */ if (1 < ctx->numThreads && !airThreadCapable) { fprintf(stderr, "%s: WARNING: not multi-threaded; will do %d " "\"threads\" serially !!!\n", me, ctx->numThreads); } for (threadIdx=0; threadIdxnumThreads; threadIdx++) { if ((ret = airThreadStart(thread[threadIdx], _hooverThreadBody, (void *) &args[threadIdx]))) { *errCodeP = ret; *errThreadP = threadIdx; airMopError(mop); return hooverErrThreadCreate; } } for (threadIdx=0; threadIdxnumThreads; threadIdx++) { u.h = &errArg; if ((ret = airThreadJoin(thread[threadIdx], u.v))) { *errCodeP = ret; *errThreadP = threadIdx; airMopError(mop); return hooverErrThreadJoin; } if (errArg != NULL) { *errCodeP = errArg->errCode; *errThreadP = threadIdx; return errArg->whichErr; } thread[threadIdx] = airThreadNix(thread[threadIdx]); } if (1 < ctx->numThreads) { ctx->workMutex = airThreadMutexNix(ctx->workMutex); } if ( (ret = (ctx->renderEnd)(render, ctx->user)) ) { *errCodeP = ret; *errThreadP = -1; return hooverErrRenderEnd; } render = NULL; airMopOkay(mop); *errCodeP = 0; *errThreadP = 0; return hooverErrNone; } teem-1.11.0~svn6057/src/coil/0000775000175000017500000000000012203513755015325 5ustar domibeldomibelteem-1.11.0~svn6057/src/coil/realmethods.c0000664000175000017500000000566312165631065020014 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "coil.h" /* ------------------------------------------ */ const coilMethod _coilMethodTesting = { "testing", coilMethodTypeTesting, 0 }; const coilMethod* coilMethodTesting = &_coilMethodTesting; /* ------------------------------------------ */ const coilMethod _coilMethodHomogeneous = { "homogeneous", coilMethodTypeHomogeneous, 1 }; const coilMethod* coilMethodHomogeneous = &_coilMethodHomogeneous; /* ------------------------------------------ */ const coilMethod _coilMethodPeronaMalik = { "perona-malik", coilMethodTypePeronaMalik, 2 }; const coilMethod* coilMethodPeronaMalik = &_coilMethodPeronaMalik; /* ------------------------------------------ */ const coilMethod _coilMethodModifiedCurvature = { "modified-curvature", coilMethodTypeModifiedCurvature, 3 }; const coilMethod* coilMethodModifiedCurvature = &_coilMethodModifiedCurvature; /* ------------------------------------------ */ const coilMethod _coilMethodModifiedCurvatureRings = { "modified-curvature-rings", coilMethodTypeModifiedCurvatureRings, 6 }; const coilMethod* coilMethodModifiedCurvatureRings = &_coilMethodModifiedCurvatureRings; /* ------------------------------------------ */ const coilMethod _coilMethodSelf = { "self", coilMethodTypeSelf, 1 }; const coilMethod* coilMethodSelf = &_coilMethodSelf; /* ------------------------------------------ */ const coilMethod _coilMethodFinish = { "finish", coilMethodTypeFinish, 4 }; const coilMethod* coilMethodFinish = &_coilMethodFinish; /* ------------------------------------------ */ const coilMethod* coilMethodArray[COIL_METHOD_TYPE_MAX+1] = { NULL, &_coilMethodTesting, &_coilMethodHomogeneous, &_coilMethodPeronaMalik, &_coilMethodModifiedCurvature, &_coilMethodModifiedCurvatureRings, NULL, &_coilMethodSelf, &_coilMethodFinish }; teem-1.11.0~svn6057/src/coil/coil.h0000664000175000017500000002404312165631065016431 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COIL_HAS_BEEN_INCLUDED #define COIL_HAS_BEEN_INCLUDED /* NOTE: there are various types that should be unsigned instead of ** signed; it may be a priority to fix this at a later date */ #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(coil_EXPORTS) || defined(teem_EXPORTS) # define COIL_EXPORT extern __declspec(dllexport) # else # define COIL_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define COIL_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define COIL coilBiffKey /* ******** coil_t ** ** this is the very crude means by which you can control the type ** of values that coil works with: "float" or "double". It is an ** unfortunate but greatly simplifying restriction that this type ** is used for all kinds of volumes, and all methods of filtering ** ** 0: double ** 1: float */ #if 0 typedef double coil_t; # define coil_nrrdType nrrdTypeDouble # define COIL_TYPE_FLOAT 0 #else typedef float coil_t; # define coil_nrrdType nrrdTypeFloat # define COIL_TYPE_FLOAT 1 #endif /* ******** #define COIL_PARMS_NUM ** ** maximum number of parameters that may be needed by any coil-driven ** filtering method */ #define COIL_PARMS_NUM 6 /* ******** coilMethodType* enum ** ** enumerated possibilities for different filtering methods */ enum { coilMethodTypeUnknown, /* 0 */ coilMethodTypeTesting, /* 1: basically a no-op */ coilMethodTypeHomogeneous, /* 2 */ coilMethodTypePeronaMalik, /* 3 */ coilMethodTypeModifiedCurvature, /* 4 */ coilMethodTypeModifiedCurvatureRings, /* 5 */ coilMethodTypeCurvatureFlow, /* 6 */ coilMethodTypeSelf, /* 7 */ coilMethodTypeFinish, /* 8 */ coilMethodTypeLast }; #define COIL_METHOD_TYPE_MAX 8 /* ******** coilMethod struct ** ** someday, there will be total orthogonality between kind and method. ** until then, this will have only a portion of the things relating to ** running one method, regardless of kind */ typedef struct { char name[AIR_STRLEN_SMALL]; int type; /* from coilMethodType* enum */ int numParm; /* number of parameters we need */ } coilMethod; /* ******** coilKindType* enum ** ** enumerated possibilities for different kinds */ enum { coilKindTypeUnknown, /* 0 */ coilKindTypeScalar, /* 1 */ coilKindType3Color, /* 2 */ coilKindType7Tensor, /* 3 */ coilKindTypeLast }; #define COIL_KIND_TYPE_MAX 3 /* ******** coilKind struct ** ** yes, there is some redunancy with the gageKind, but the the main ** reason is that its significantly easier to implement meaningful ** per-sample filtering of various quantities, than it is to do ** gage-style convolution-based measurements at arbitrary locations. ** So, there will probably be significantly more coilKinds than ** there are gageKinds. The two kind systems may play closer together ** at some point in the future where appropriate. */ typedef struct { char name[AIR_STRLEN_SMALL]; /* short identifying string for kind */ unsigned int valLen; /* number of scalars per data point 1 for plain scalars (baseDim=0), or something else (baseDim=1) */ /* all the available methods */ void (*filter[COIL_METHOD_TYPE_MAX+1])(coil_t *delta, int xi, int yi, int zi, coil_t **iv3, double spacing[3], double parm[COIL_PARMS_NUM]); void (*update)(coil_t *val, coil_t *delta); /* how to apply update */ } coilKind; struct coilContext_t; /* ******** coilTask ** ** passed to all worker threads */ typedef struct { struct coilContext_t *cctx; /* parent's context */ airThread *thread; /* my thread */ unsigned int threadIdx; /* which thread am I */ coil_t *_iv3, /* underlying value cache */ **iv3; /* short array of pointers into 2-D value caches, in which the order is based on the volume order: values, then Y, then Z */ /* how to fill iv3 */ void (*iv3Fill)(coil_t **iv3, coil_t *here, unsigned int radius, int valLen, int x0, int y0, int z0, int sizeX, int sizeY, int sizeZ); void *returnPtr; /* for airThreadJoin */ } coilTask; /* ******** coilContext struct ** ** bag of stuff relating to filtering one volume */ typedef struct coilContext_t { /* ---------- input */ const Nrrd *nin; /* input volume (converted to type coil_t in nvol, below) */ const coilKind *kind; /* what kind of volume is nin */ const coilMethod *method; /* what method of filtering to use */ unsigned int radius, /* how big a neighborhood to look at when doing filtering (use 1 for 3x3x3 size) */ numThreads; /* number of threads to enlist */ int verbose; /* blah blah blah */ double parm[COIL_PARMS_NUM]; /* all the parameters used to control the action of the filtering. The timestep is probably the first value. */ /* ---------- internal */ unsigned int iter; /* what iteration we're on */ size_t size[3], /* size of volume */ nextSlice; /* global indicator of next slice needing to be processed, either in filter or in update stage. Stage is done when nextSlice == size[2] */ double spacing[3]; /* sample spacings we'll use- we perhaps should be using a gageShape, but this is actually all we really need . . . */ Nrrd *nvol; /* an interleaved volume of (1st) the last filtering result, and (2nd) the update values from the current iteration */ int finished, /* used to signal all threads to return */ todoFilter, todoUpdate; /* flags to signal which is scheduled to come next, used as part of doling out slices to workers */ airThreadMutex *nextSliceMutex; /* mutex around nextSlice (and effectively, also the "todo" flags above) */ coilTask **task; /* dynamically allocated array of tasks */ airThreadBarrier *filterBarrier, /* so that thread 0 can see if filtering should go onward, and set "finished" */ *updateBarrier; /* after the update values have been applied to current values */ } coilContext; /* defaultsCoil.c */ COIL_EXPORT const int coilPresent; COIL_EXPORT const char *coilBiffKey; COIL_EXPORT int coilDefaultRadius; COIL_EXPORT int coilVerbose; /* enumsCoil.c */ COIL_EXPORT const airEnum *const coilMethodType; COIL_EXPORT const airEnum *const coilKindType; /* scalarCoil.c */ COIL_EXPORT const coilKind *coilKindScalar; COIL_EXPORT const coilKind *coilKindArray[COIL_KIND_TYPE_MAX+1]; /* tensorCoil.c */ COIL_EXPORT const coilKind _coilKind7Tensor; /* no privateCoil.h */ COIL_EXPORT const coilKind *coilKind7Tensor; /* realmethods.c */ COIL_EXPORT const coilMethod *coilMethodTesting; COIL_EXPORT const coilMethod *coilMethodIsotropic; COIL_EXPORT const coilMethod *coilMethodArray[COIL_METHOD_TYPE_MAX+1]; /* methodsCoil.c (sorry, confusing name!) */ COIL_EXPORT coilContext *coilContextNew(void); COIL_EXPORT int coilVolumeCheck(const Nrrd *nin, const coilKind *kind); COIL_EXPORT int coilContextAllSet(coilContext *cctx, const Nrrd *nin, const coilKind *kind, const coilMethod *method, unsigned int radius, unsigned int numThreads, int verbose, double parm[COIL_PARMS_NUM]); COIL_EXPORT int coilOutputGet(Nrrd *nout, coilContext *cctx); COIL_EXPORT coilContext *coilContextNix(coilContext *cctx); /* coreCoil.c */ COIL_EXPORT int coilStart(coilContext *cctx); COIL_EXPORT int coilIterate(coilContext *cctx, int numIterations); COIL_EXPORT int coilFinish(coilContext *cctx); #ifdef __cplusplus } #endif #endif /* COIL_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/coil/coreCoil.c0000664000175000017500000003140712165631065017237 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "coil.h" #define _COIL_IV3_FILL(radius, diam, valLen) \ if (0 && xx0) { \ /* cycle through slices */ \ tmp = iv3[0]; \ for (xni=0; xnicctx->todoFilter); thatFlag = &(task->cctx->todoUpdate); } else { thisFlag = &(task->cctx->todoUpdate); thatFlag = &(task->cctx->todoFilter); } if (task->cctx->numThreads > 1) { airThreadMutexLock(task->cctx->nextSliceMutex); } if (task->cctx->nextSlice == task->cctx->size[2] && *thisFlag) { /* we're the first thread to start this phase */ task->cctx->nextSlice = 0; *thisFlag = AIR_FALSE; } thisZ = task->cctx->nextSlice; if (task->cctx->nextSlice < task->cctx->size[2]) { task->cctx->nextSlice++; if (task->cctx->nextSlice == task->cctx->size[2]) { /* we just grabbed the last slice of this phase */ *thatFlag = AIR_TRUE; } } if (task->cctx->numThreads > 1) { airThreadMutexUnlock(task->cctx->nextSliceMutex); } return thisZ; } void _coilProcess(coilTask *task, int doFilter) { static const char me[]="_coilProcess"; int xi, yi, sizeX, sizeY, thisZ, sizeZ, valLen, radius; coil_t *here; void (*filter)(coil_t *delta, int xi, int yi, int zi, coil_t **iv3, double spacing[3], double parm[COIL_PARMS_NUM]); sizeX = task->cctx->size[0]; sizeY = task->cctx->size[1]; sizeZ = task->cctx->size[2]; valLen = task->cctx->kind->valLen; radius = task->cctx->radius; filter = task->cctx->kind->filter[task->cctx->method->type]; if (doFilter) { while (1) { thisZ = _coilThisZGet(task, doFilter); if (thisZ == sizeZ) { break; } if (task->cctx->verbose > 2) { fprintf(stderr, "%s(%u),f: iter=%u, z=%d\n", me, task->threadIdx, task->cctx->iter, thisZ); } here = (coil_t*)(task->cctx->nvol->data) + 2*valLen*sizeX*sizeY*thisZ; for (yi=0; yiiv3Fill(task->iv3, here + 0*valLen, radius, valLen, xi, yi, thisZ, sizeX, sizeY, sizeZ); filter(here + 1*valLen, xi, yi, thisZ, task->iv3, task->cctx->spacing, task->cctx->parm); here += 2*valLen; } } } } else { while (1) { thisZ = _coilThisZGet(task, doFilter); if (thisZ == sizeZ) { break; } if (task->cctx->verbose > 3) { fprintf(stderr, "%s(%u),u: iter=%u, z=%d\n", me, task->threadIdx, task->cctx->iter, thisZ); } here = (coil_t*)(task->cctx->nvol->data) + 2*valLen*sizeX*sizeY*thisZ; for (yi=0; yicctx->kind->update(here + 0*valLen, here + 1*valLen); here += 2*valLen; } } } } return; } coilTask * _coilTaskNew(coilContext *cctx, int threadIdx) { coilTask *task; int len, diam, xi; len = cctx->kind->valLen; diam = 1 + 2*cctx->radius; task = (coilTask *)calloc(1, sizeof(coilTask)); if (task) { task->cctx = cctx; task->thread = airThreadNew(); task->threadIdx = threadIdx; task->_iv3 = (coil_t*)calloc(len*diam*diam*diam, sizeof(coil_t)); task->iv3 = (coil_t**)calloc(diam, sizeof(coil_t*)); for (xi=0; xiiv3[xi] = task->_iv3 + xi*len*diam*diam; } if (1 == cctx->radius && 1 == cctx->kind->valLen) { task->iv3Fill = _coilIv3Fill_1_1; } else if (1 == cctx->radius && 7 == cctx->kind->valLen) { task->iv3Fill = _coilIv3Fill_1_7; } else { task->iv3Fill = _coilIv3Fill_R_L; } task->returnPtr = NULL; } return task; } coilTask * _coilTaskNix(coilTask *task) { if (task) { task->thread = airThreadNix(task->thread); task->_iv3 = (coil_t *)airFree(task->_iv3); task->iv3 = (coil_t **)airFree(task->iv3); free(task); } return NULL; } void * _coilWorker(void *_task) { static const char me[]="_coilWorker"; coilTask *task; task = (coilTask *)_task; while (1) { /* wait until parent has set cctx->finished */ if (task->cctx->verbose > 1) { fprintf(stderr, "%s(%d): waiting to check finished\n", me, task->threadIdx); } if (task->cctx->numThreads > 1) { airThreadBarrierWait(task->cctx->filterBarrier); } if (task->cctx->finished) { if (task->cctx->verbose > 1) { fprintf(stderr, "%s(%d): done!\n", me, task->threadIdx); } break; } /* else there's work to do ... */ /* first: filter */ if (task->cctx->verbose > 1) { fprintf(stderr, "%s(%d): filtering ... \n", me, task->threadIdx); } _coilProcess(task, AIR_TRUE); /* second: update */ if (task->cctx->numThreads > 1) { airThreadBarrierWait(task->cctx->updateBarrier); } if (task->cctx->verbose > 1) { fprintf(stderr, "%s(%d): updating ... \n", me, task->threadIdx); } _coilProcess(task, AIR_FALSE); } return _task; } int coilStart(coilContext *cctx) { static const char me[]="coilStart"; int valIdx, valLen; coil_t (*lup)(const void*, size_t), *val; unsigned tidx, elIdx; if (!cctx) { biffAddf(COIL, "%s: got NULL pointer", me); return 1; } cctx->task = (coilTask **)calloc(cctx->numThreads, sizeof(coilTask *)); if (!(cctx->task)) { biffAddf(COIL, "%s: couldn't allocate array of tasks", me); return 1; } /* we create tasks for ALL threads, including me, thread 0 */ cctx->task[0] = NULL; for (tidx=0; tidxnumThreads; tidx++) { cctx->task[tidx] = _coilTaskNew(cctx, tidx); if (!(cctx->task[tidx])) { biffAddf(COIL, "%s: couldn't allocate task %d", me, tidx); return 1; } } cctx->finished = AIR_FALSE; if (cctx->numThreads > 1) { cctx->nextSliceMutex = airThreadMutexNew(); cctx->filterBarrier = airThreadBarrierNew(cctx->numThreads); cctx->updateBarrier = airThreadBarrierNew(cctx->numThreads); } /* initialize the values in cctx->nvol */ val = (coil_t*)(cctx->nvol->data); valLen = cctx->kind->valLen; #if COIL_TYPE_FLOAT lup = nrrdFLookup[cctx->nin->type]; #else lup = nrrdDLookup[cctx->nin->type]; #endif for (elIdx=0; elIdxsize[0]*cctx->size[1]*cctx->size[2]; elIdx++) { for (valIdx=0; valIdxnin->data, valIdx + valLen*elIdx); val[valIdx + 1*valLen] = 0; } val += 2*valLen; } /* start threads 1 and up running; they'll all hit filterBarrier */ if (cctx->numThreads > 1) { for (tidx=1; tidxnumThreads; tidx++) { if (cctx->verbose > 1) { fprintf(stderr, "%s: spawning thread %d\n", me, tidx); } airThreadStart(cctx->task[tidx]->thread, _coilWorker, (void *)(cctx->task[tidx])); } } /* set things as though we've just finished an update phase */ cctx->nextSlice = cctx->size[2]; cctx->todoFilter = AIR_TRUE; cctx->todoUpdate = AIR_FALSE; return 0; } /* ******** coilIterate ** ** (documentation) ** ** NB: this implements the body of thread 0 */ int coilIterate(coilContext *cctx, int numIterations) { static const char me[]="coilIterate"; int iter; double time0, time1; if (!cctx) { biffAddf(COIL, "%s: got NULL pointer", me); return 1; } time0 = airTime(); for (iter=0; iteriter = iter; if (cctx->verbose) { fprintf(stderr, "%s: starting iter %d (of %d)\n", me, iter, numIterations); } cctx->finished = AIR_FALSE; if (cctx->numThreads > 1) { airThreadBarrierWait(cctx->filterBarrier); } /* first: filter */ if (cctx->verbose > 1) { fprintf(stderr, "%s: filtering ... \n", me); } _coilProcess(cctx->task[0], AIR_TRUE); /* second: update */ if (cctx->verbose > 1) { fprintf(stderr, "%s: updating ... \n", me); } if (cctx->numThreads > 1) { airThreadBarrierWait(cctx->updateBarrier); } _coilProcess(cctx->task[0], AIR_FALSE); } time1 = airTime(); if (cctx->verbose) { fprintf(stderr, "%s: elapsed time = %g (%g/iter)\n", me, time1 - time0, (time1 - time0)/numIterations); } return 0; } int coilFinish(coilContext *cctx) { static const char me[]="coilFinish"; unsigned int tidx; if (!cctx) { biffAddf(COIL, "%s: got NULL pointer", me); return 1; } if (cctx->verbose > 1) { fprintf(stderr, "%s: finishing workers\n", me); } cctx->finished = AIR_TRUE; if (cctx->numThreads > 1) { airThreadBarrierWait(cctx->filterBarrier); for (tidx=1; tidxnumThreads; tidx++) { airThreadJoin(cctx->task[tidx]->thread, &(cctx->task[tidx]->returnPtr)); cctx->task[tidx]->thread = airThreadNix(cctx->task[tidx]->thread); cctx->task[tidx] = _coilTaskNix(cctx->task[tidx]); } } cctx->task[0]->thread = airThreadNix(cctx->task[0]->thread); cctx->task[0] = _coilTaskNix(cctx->task[0]); cctx->task = (coilTask **)airFree(cctx->task); if (cctx->numThreads > 1) { cctx->nextSliceMutex = airThreadMutexNix(cctx->nextSliceMutex); cctx->filterBarrier = airThreadBarrierNix(cctx->filterBarrier); cctx->updateBarrier = airThreadBarrierNix(cctx->updateBarrier); } return 0; } teem-1.11.0~svn6057/src/coil/scalarCoil.c0000664000175000017500000002732212165631065017555 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "coil.h" /* ** x ----> X ** \ [0][0] [1][0] [2][0] ** | \ [0][1] [1][1] [2][1] ** | Y [0][2] [1][2] [2][2] ** | ** | [0][3] [1][3] [2][3] ** Z [0][4] [1][4] [2][4] ** [0][5] [1][5] [2][5] ** ** [0][6] [1][6] [2][6] ** [0][7] [1][7] [2][7] ** [0][8] [1][8] [2][8] */ coil_t _coilLaplacian3(coil_t **iv3, double spacing[3]) { double ret; ret = ( (iv3[0][4] - 2*iv3[1][4] + iv3[2][4])/(spacing[0]*spacing[0]) + (iv3[1][3] - 2*iv3[1][4] + iv3[1][5])/(spacing[1]*spacing[1]) + (iv3[1][1] - 2*iv3[1][4] + iv3[1][7])/(spacing[2]*spacing[2])); return AIR_CAST(coil_t, ret); } void _coilKindScalarFilterTesting(coil_t *delta, int xi, int yi, int zi, coil_t **iv3, double spacing[3], double parm[COIL_PARMS_NUM]) { AIR_UNUSED(xi); AIR_UNUSED(yi); AIR_UNUSED(zi); AIR_UNUSED(iv3); AIR_UNUSED(spacing); AIR_UNUSED(parm); delta[0] = 0; } void _coilKindScalarFilterHomogeneous(coil_t *delta, int xi, int yi, int zi, coil_t **iv3, double spacing[3], double parm[COIL_PARMS_NUM]) { AIR_UNUSED(xi); AIR_UNUSED(yi); AIR_UNUSED(zi); delta[0] = AIR_CAST(coil_t, parm[0])*_coilLaplacian3(iv3, spacing); } void _coilKindScalar3x3x3Gradients(coil_t *forwX, coil_t *backX, coil_t *forwY, coil_t *backY, coil_t *forwZ, coil_t *backZ, coil_t **i, coil_t rspX, coil_t rspY, coil_t rspZ) { /* gradients at forward and backward X */ forwX[0] = rspX*(i[2][4] - i[1][4]); forwX[1] = rspY*(i[1][5] + i[2][5] - i[1][3] - i[2][3])/2; forwX[2] = rspZ*(i[1][7] + i[2][7] - i[1][1] - i[2][1])/2; backX[0] = rspX*(i[1][4] - i[0][4]); backX[1] = rspY*(i[0][5] + i[1][5] - i[0][3] - i[1][3])/2; backX[2] = rspZ*(i[0][7] + i[1][7] - i[0][1] - i[1][1])/2; /* gradients at forward and backward Y */ forwY[0] = rspX*(i[2][4] + i[2][5] - i[0][4] - i[0][5])/2; forwY[1] = rspY*(i[1][5] - i[1][4]); forwY[2] = rspZ*(i[1][7] + i[1][8] - i[1][1] - i[1][2])/2; backY[0] = rspX*(i[2][3] + i[2][4] - i[0][3] - i[0][4])/2; backY[1] = rspY*(i[1][4] - i[1][3]); backY[2] = rspZ*(i[1][6] + i[1][7] - i[1][0] - i[1][1])/2; /* gradients at forward and backward Z */ forwZ[0] = rspX*(i[2][4] + i[2][7] - i[0][4] - i[0][7])/2; forwZ[1] = rspY*(i[1][5] + i[1][8] - i[1][3] - i[1][6])/2; forwZ[2] = rspZ*(i[1][7] - i[1][4]); backZ[0] = rspX*(i[2][1] + i[2][4] - i[0][1] - i[0][4])/2; backZ[1] = rspY*(i[1][2] + i[1][5] - i[1][0] - i[1][3])/2; backZ[2] = rspZ*(i[1][4] - i[1][1]); return; } #define _COIL_CONDUCT(LL, KK) \ AIR_CAST(coil_t, exp(-0.5*(LL)/(KK))) /* #define _COIL_CONDUCT(vec, KK) \ AIR_CAST(coil_t, 1.0/(1.0 + (LL)/(KK))) */ void _coilKindScalarFilterPeronaMalik(coil_t *delta, int xi, int yi, int zi, coil_t **iv3, double spacing[3], double parm[COIL_PARMS_NUM]) { coil_t forwX[3], backX[3], forwY[3], backY[3], forwZ[3], backZ[3], KK, rspX, rspY, rspZ; AIR_UNUSED(xi); AIR_UNUSED(yi); AIR_UNUSED(zi); /* reciprocals of spacings in X, Y, and Z */ rspX = AIR_CAST(coil_t, 1.0/spacing[0]); rspY = AIR_CAST(coil_t, 1.0/spacing[1]); rspZ = AIR_CAST(coil_t, 1.0/spacing[2]); _coilKindScalar3x3x3Gradients(forwX, backX, forwY, backY, forwZ, backZ, iv3, rspX, rspY, rspZ); /* compute fluxes */ KK = AIR_CAST(coil_t, parm[1]*parm[1]); forwX[0] *= _COIL_CONDUCT(ELL_3V_DOT(forwX, forwX), KK); forwY[1] *= _COIL_CONDUCT(ELL_3V_DOT(forwY, forwY), KK); forwZ[2] *= _COIL_CONDUCT(ELL_3V_DOT(forwZ, forwZ), KK); backX[0] *= _COIL_CONDUCT(ELL_3V_DOT(backX, backX), KK); backY[1] *= _COIL_CONDUCT(ELL_3V_DOT(backY, backY), KK); backZ[2] *= _COIL_CONDUCT(ELL_3V_DOT(backZ, backZ), KK); delta[0] = AIR_CAST(coil_t, parm[0])*(rspX*(forwX[0] - backX[0]) + rspY*(forwY[1] - backY[1]) + rspZ*(forwZ[2] - backZ[2])); } /* ** (mcde) ** parm vector: ** 0 1 2 (3) ** step K lerp (lerp=1: all laplacian) */ void _coilKindScalarFilterModifiedCurvature(coil_t *delta, int xi, int yi, int zi, coil_t **iv3, double spacing[3], double parm[COIL_PARMS_NUM]) { /* char me[]="_coilKindScalarFilterModifiedCurvature"; */ coil_t forwX[3], backX[3], forwY[3], backY[3], forwZ[3], backZ[3], grad[3], gm, eps, KK, LL, denom, rspX, rspY, rspZ, lerp; AIR_UNUSED(xi); AIR_UNUSED(yi); AIR_UNUSED(zi); /* if (coilVerbose) { fprintf(stderr, "!%s: --------- hello --------\n", me); } */ /* reciprocals of spacings in X, Y, and Z */ rspX = AIR_CAST(coil_t, 1.0/spacing[0]); rspY = AIR_CAST(coil_t, 1.0/spacing[1]); rspZ = AIR_CAST(coil_t, 1.0/spacing[2]); _coilKindScalar3x3x3Gradients(forwX, backX, forwY, backY, forwZ, backZ, iv3, rspX, rspY, rspZ); grad[0] = rspX*(iv3[2][4] - iv3[0][4]); grad[1] = rspY*(iv3[1][5] - iv3[1][3]); grad[2] = rspZ*(iv3[1][7] - iv3[1][1]); gm = AIR_CAST(coil_t, ELL_3V_LEN(grad)); /* if (coilVerbose) { fprintf(stderr, "forwX = %g %g %g backX = %g %g %g\n", forwX[0], forwX[1], forwX[2], backX[0], backX[1], backX[2]); fprintf(stderr, "forwY = %g %g %g backY = %g %g %g\n", forwY[0], forwY[1], forwY[2], backY[0], backY[1], backY[2]); fprintf(stderr, "forwZ = %g %g %g backZ = %g %g %g\n", forwZ[0], forwZ[1], forwZ[2], backZ[0], backZ[1], backZ[2]); fprintf(stderr, "grad = %g %g %g --> gm = %g\n", grad[0], grad[1], grad[2], gm); } */ /* compute fluxes */ eps = 0.0000000001f; KK = AIR_CAST(coil_t, parm[1]*parm[1]); LL = ELL_3V_DOT(forwX, forwX); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); forwX[0] *= _COIL_CONDUCT(LL, KK)*denom; LL = ELL_3V_DOT(forwY, forwY); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); forwY[1] *= _COIL_CONDUCT(LL, KK)*denom; LL = ELL_3V_DOT(forwZ, forwZ); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); forwZ[2] *= _COIL_CONDUCT(LL, KK)*denom; LL = ELL_3V_DOT(backX, backX); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); backX[0] *= _COIL_CONDUCT(LL, KK)*denom; LL = ELL_3V_DOT(backY, backY); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); backY[1] *= _COIL_CONDUCT(LL, KK)*denom; LL = ELL_3V_DOT(backZ, backZ); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); backZ[2] *= _COIL_CONDUCT(LL, KK)*denom; lerp = AIR_CAST(coil_t, parm[2]); delta[0] = (lerp*_coilLaplacian3(iv3, spacing) + (1-lerp)*gm*(rspX*(forwX[0] - backX[0]) + rspY*(forwY[1] - backY[1]) + rspZ*(forwZ[2] - backZ[2]))); delta[0] *= AIR_CAST(coil_t, parm[0]); /* if (coilVerbose) { fprintf(stderr, "!%s: delta = %g\n", me, delta[0]); } */ } /* ** parm vector: ** 0 1 2 3 4 5 (6) ** step K_perp K_tan lerp X_ring Y_ring */ void _coilKindScalarFilterModifiedCurvatureRings(coil_t *delta, int xi, int yi, int zi, coil_t **iv3, double spacing[3], double parm[COIL_PARMS_NUM]) { coil_t forwX[3], backX[3], forwY[3], backY[3], forwZ[3], backZ[3], grad[3], gm, eps, KK, LL, denom, rspX, rspY, rspZ, lerp; double bas0[3], bas1[3], bas2[3], len, norm[3], sk; AIR_UNUSED(zi); ELL_3V_SET(bas0, 0, 0, 1); ELL_3V_SET(bas1, xi - parm[4], yi - parm[5], 0); ELL_3V_NORM(bas1, bas1, len); ELL_3V_CROSS(bas2, bas0, bas1); rspX = AIR_CAST(coil_t, 1.0/spacing[0]); rspY = AIR_CAST(coil_t, 1.0/spacing[1]); rspZ = AIR_CAST(coil_t, 1.0/spacing[2]); _coilKindScalar3x3x3Gradients(forwX, backX, forwY, backY, forwZ, backZ, iv3, rspX, rspY, rspZ); grad[0] = rspX*(iv3[2][4] - iv3[0][4]); grad[1] = rspY*(iv3[1][5] - iv3[1][3]); grad[2] = rspZ*(iv3[1][7] - iv3[1][1]); gm = AIR_CAST(coil_t, ELL_3V_LEN(grad)); if (gm) { double tc, rcsq; ELL_3V_SCALE(norm, 1.0/gm, grad); tc = ELL_3V_DOT(norm, bas2); rcsq = 1 - tc*tc; sk = AIR_LERP(rcsq, parm[1], parm[2]); } else { sk = parm[1]; } /* compute fluxes */ eps = 0.0000000001f; KK = AIR_CAST(coil_t, sk*sk); LL = ELL_3V_DOT(forwX, forwX); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); forwX[0] *= _COIL_CONDUCT(LL, KK)*denom; LL = ELL_3V_DOT(forwY, forwY); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); forwY[1] *= _COIL_CONDUCT(LL, KK)*denom; LL = ELL_3V_DOT(forwZ, forwZ); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); forwZ[2] *= _COIL_CONDUCT(LL, KK)*denom; LL = ELL_3V_DOT(backX, backX); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); backX[0] *= _COIL_CONDUCT(LL, KK)*denom; LL = ELL_3V_DOT(backY, backY); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); backY[1] *= _COIL_CONDUCT(LL, KK)*denom; LL = ELL_3V_DOT(backZ, backZ); denom = AIR_CAST(coil_t, 1.0/(eps + sqrt(LL))); backZ[2] *= _COIL_CONDUCT(LL, KK)*denom; lerp = AIR_CAST(coil_t, parm[2]); delta[0] = (lerp*_coilLaplacian3(iv3, spacing) + (1-lerp)*gm*(rspX*(forwX[0] - backX[0]) + rspY*(forwY[1] - backY[1]) + rspZ*(forwZ[2] - backZ[2]))); delta[0] *= AIR_CAST(coil_t, parm[0]); } void _coilKindScalarUpdate(coil_t *val, coil_t *delta) { val[0] += delta[0]; } const coilKind _coilKindScalar = { "scalar", 1, {NULL, _coilKindScalarFilterTesting, _coilKindScalarFilterHomogeneous, _coilKindScalarFilterPeronaMalik, _coilKindScalarFilterModifiedCurvature, _coilKindScalarFilterModifiedCurvatureRings, NULL, NULL, NULL}, _coilKindScalarUpdate }; const coilKind * coilKindScalar = &_coilKindScalar; /* ------------------------------------------ */ #ifdef __cplusplus extern "C" { #endif extern const coilKind _coilKind7Tensor; #ifdef __cplusplus } #endif const coilKind* coilKindArray[COIL_KIND_TYPE_MAX+1] = { NULL, &_coilKindScalar, NULL, &_coilKind7Tensor }; teem-1.11.0~svn6057/src/coil/enumsCoil.c0000664000175000017500000000667712165631065017451 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "coil.h" const char * _coilMethodTypeStr[COIL_METHOD_TYPE_MAX+1] = { "(unknown_method)", "testing", "homogeneous", "perona-malik", "modified curvature", "modified curvature rings", "curvature flow", "self", "finish" }; const char * _coilMethodTypeDesc[COIL_METHOD_TYPE_MAX+1] = { "unknown_method", "nothing, actually, just here for testing", "homogenous isotropic diffusion (Gaussian blurring)", "Perona-Malik", "modified curvature diffusion", "modified curvature diffusion rings", "curvature flow", "self-diffusion of diffusion tensors", "finish a phd already" }; const char * _coilMethodTypeStrEqv[] = { "test", "testing", "iso", "homog", "homogeneous", "pm", "perona-malik", "mcde", "modified curvature", "mcder", "modified curvature rings", "flow", "curvature flow", "self", "finish", "" }; const int _coilMethodTypeValEqv[] = { coilMethodTypeTesting, coilMethodTypeTesting, coilMethodTypeHomogeneous, coilMethodTypeHomogeneous, coilMethodTypeHomogeneous, coilMethodTypePeronaMalik, coilMethodTypePeronaMalik, coilMethodTypeModifiedCurvature, coilMethodTypeModifiedCurvature, coilMethodTypeModifiedCurvatureRings, coilMethodTypeModifiedCurvatureRings, coilMethodTypeCurvatureFlow, coilMethodTypeCurvatureFlow, coilMethodTypeSelf, coilMethodTypeFinish, }; const airEnum _coilMethodType = { "method", COIL_METHOD_TYPE_MAX, _coilMethodTypeStr, NULL, _coilMethodTypeDesc, _coilMethodTypeStrEqv, _coilMethodTypeValEqv, AIR_FALSE }; const airEnum *const coilMethodType = &_coilMethodType; /* -------------------------------------------------- */ const char * _coilKindTypeStr[COIL_KIND_TYPE_MAX+1] = { "(unknown_kind)", "scalar", "3color", "7tensor" }; const char * _coilKindTypeDesc[COIL_KIND_TYPE_MAX+1] = { "unknown_kind", "plain old scalar quantities", "3-component color", "ten-style 7-valued tensor" }; const char * _coilKindTypeStrEqv[] = { "scalar", "3color", "7tensor", "tensor", "" }; const int _coilKindTypeValEqv[] = { coilKindTypeScalar, coilKindType3Color, coilKindType7Tensor, coilKindType7Tensor }; const airEnum _coilKindType = { "kind", COIL_KIND_TYPE_MAX, _coilKindTypeStr, NULL, _coilKindTypeDesc, _coilKindTypeStrEqv, _coilKindTypeValEqv, AIR_FALSE }; const airEnum *const coilKindType = &_coilKindType; teem-1.11.0~svn6057/src/coil/defaultsCoil.c0000664000175000017500000000240312165631065020110 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "coil.h" const int coilPresent = 42; const char * coilBiffKey = "coil"; int coilDefaultRadius = 1; int coilVerbose = 0; teem-1.11.0~svn6057/src/coil/GNUmakefile0000664000175000017500000000360512165631065017405 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := coil #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### (ell for macros) #### $(L).NEED = ten ell nrrd biff air $(L).PUBLIC_HEADERS = coil.h $(L).PRIVATE_HEADERS = $(L).OBJS = defaultsCoil.o enumsCoil.o scalarCoil.o tensorCoil.o \ realmethods.o methodsCoil.o coreCoil.o $(L).TESTS = test/coiler #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/coil/sources.cmake0000664000175000017500000000042111113047450020000 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(COIL_SOURCES coil.h coreCoil.c defaultsCoil.c enumsCoil.c methodsCoil.c realmethods.c scalarCoil.c tensorCoil.c ) ADD_TEEM_LIBRARY(coil ${COIL_SOURCES}) teem-1.11.0~svn6057/src/coil/tensorCoil.c0000664000175000017500000002526012165631065017621 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "coil.h" void _coilKind7TensorTangents(coil_t traceGrad[6], coil_t varianceGrad[6], coil_t skewGrad[6], coil_t rot0Grad[6], coil_t rot1Grad[6], coil_t rot2Grad[6], coil_t tensor[7]) { AIR_UNUSED(traceGrad); AIR_UNUSED(varianceGrad); AIR_UNUSED(skewGrad); AIR_UNUSED(rot0Grad); AIR_UNUSED(rot1Grad); AIR_UNUSED(rot2Grad); AIR_UNUSED(tensor); /* coil_t a, b, c, d, e, f; a = tensor[1]; b = tensor[2]; c = tensor[3]; d = tensor[4]; e = tensor[5]; f = tensor[6]; ELL_6V_SET(traceGrad, 1, 0, 0, 1, 0, 1); */ } void _coilKind7TensorFilterTesting(coil_t *delta, int xi, int yi, int zi, coil_t **iv3, double spacing[3], double parm[COIL_PARMS_NUM]) { AIR_UNUSED(xi); AIR_UNUSED(yi); AIR_UNUSED(zi); AIR_UNUSED(iv3); AIR_UNUSED(spacing); AIR_UNUSED(parm); delta[0] = 0; delta[1] = 0; delta[2] = 0; delta[3] = 0; delta[4] = 0; delta[5] = 0; delta[6] = 0; } /* ** o ----> X ** \ [0][ 0]-[06] [1][ 0] [2][ 0] ** | \ [0][ 7]-[13] [1][ 7] [2][ 7] ** | Y [0][14]-[20] [1][14] [2][14] ** | ** | [0][21]-[27] [1][21] [2][21] ** Z [0][28]-[34] [1][28] [2][28] ** [0][35]-[41] [1][35] [2][35] ** ** [0][42]-[48] [1][42] [2][42] ** [0][49]-[55] [1][49] [2][49] ** [0][56]-[62] [1][56] [2][56] */ #define IND(iv3, vi, X, Y, Z) ((iv3)[(X)][(vi) + 7*((Y) + 3*(Z))]) #define TENS(ten, iv3) \ TEN_T_SET(ten, \ IND(iv3,0,1,1,1), \ IND(iv3,1,1,1,1), IND(iv3,2,1,1,1), IND(iv3,3,1,1,1), \ IND(iv3,4,1,1,1), IND(iv3,5,1,1,1), \ IND(iv3,6,1,1,1)) #define TENGRAD(tengrad, iv3, rspX, rspY, rspZ) ( \ TEN_T_SET(tengrad + 0*7, \ 1.0, \ rspX*(IND(iv3,1,2,1,1) - IND(iv3,1,0,1,1))/2, \ rspX*(IND(iv3,2,2,1,1) - IND(iv3,2,0,1,1))/2, \ rspX*(IND(iv3,3,2,1,1) - IND(iv3,3,0,1,1))/2, \ rspX*(IND(iv3,4,2,1,1) - IND(iv3,4,0,1,1))/2, \ rspX*(IND(iv3,5,2,1,1) - IND(iv3,5,0,1,1))/2, \ rspX*(IND(iv3,6,2,1,1) - IND(iv3,6,0,1,1))/2), \ TEN_T_SET(tengrad + 1*7, \ 1.0, \ rspY*(IND(iv3,1,1,2,1) - IND(iv3,1,1,0,1))/2, \ rspY*(IND(iv3,2,1,2,1) - IND(iv3,2,1,0,1))/2, \ rspY*(IND(iv3,3,1,2,1) - IND(iv3,3,1,0,1))/2, \ rspY*(IND(iv3,4,1,2,1) - IND(iv3,4,1,0,1))/2, \ rspY*(IND(iv3,5,1,2,1) - IND(iv3,5,1,0,1))/2, \ rspY*(IND(iv3,6,1,2,1) - IND(iv3,6,1,0,1))/2), \ TEN_T_SET(tengrad + 2*7, \ 1.0, \ rspZ*(IND(iv3,1,1,1,2) - IND(iv3,1,1,1,0))/2, \ rspZ*(IND(iv3,2,1,1,2) - IND(iv3,2,1,1,0))/2, \ rspZ*(IND(iv3,3,1,1,2) - IND(iv3,3,1,1,0))/2, \ rspZ*(IND(iv3,4,1,1,2) - IND(iv3,4,1,1,0))/2, \ rspZ*(IND(iv3,5,1,1,2) - IND(iv3,5,1,1,0))/2, \ rspZ*(IND(iv3,6,1,1,2) - IND(iv3,6,1,1,0))/2)) #define LAPL(iv3, vi, rspsqX, rspsqY, rspsqZ) \ ( rspsqX*(IND(iv3, vi, 0, 1, 1) - 2*IND(iv3, vi, 1, 1, 1) + IND(iv3, vi, 2, 1, 1)) \ + rspsqY*(IND(iv3, vi, 1, 0, 1) - 2*IND(iv3, vi, 1, 1, 1) + IND(iv3, vi, 1, 2, 1)) \ + rspsqZ*(IND(iv3, vi, 1, 1, 0) - 2*IND(iv3, vi, 1, 1, 1) + IND(iv3, vi, 1, 1, 2))) void _coilKind7TensorFilterHomogeneous(coil_t *delta, int xi, int yi, int zi, coil_t **iv3, double spacing[3], double parm[COIL_PARMS_NUM]) { coil_t rspsqX, rspsqY, rspsqZ, parm0; AIR_UNUSED(xi); AIR_UNUSED(yi); AIR_UNUSED(zi); rspsqX = AIR_CAST(coil_t, 1.0/(spacing[0]*spacing[0])); rspsqY = AIR_CAST(coil_t, 1.0/(spacing[1]*spacing[1])); rspsqZ = AIR_CAST(coil_t, 1.0/(spacing[2]*spacing[2])); parm0 = AIR_CAST(coil_t, parm[0]); delta[0] = 0; delta[1] = parm0*LAPL(iv3, 1, rspsqX, rspsqY, rspsqZ); delta[2] = parm0*LAPL(iv3, 2, rspsqX, rspsqY, rspsqZ); delta[3] = parm0*LAPL(iv3, 3, rspsqX, rspsqY, rspsqZ); delta[4] = parm0*LAPL(iv3, 4, rspsqX, rspsqY, rspsqZ); delta[5] = parm0*LAPL(iv3, 5, rspsqX, rspsqY, rspsqZ); delta[6] = parm0*LAPL(iv3, 6, rspsqX, rspsqY, rspsqZ); } #define HESS(hess, iv3, vi, rspX, rspY, rspZ) \ (hess)[1] = rspX*rspX*(IND(iv3,vi,0,1,1) - 2*IND(iv3,vi,1,1,1) + IND(iv3,vi,2,1,1)); \ (hess)[4] = rspY*rspY*(IND(iv3,vi,1,0,1) - 2*IND(iv3,vi,1,1,1) + IND(iv3,vi,1,2,1)); \ (hess)[6] = rspZ*rspZ*(IND(iv3,vi,1,1,0) - 2*IND(iv3,vi,1,1,1) + IND(iv3,vi,1,1,2)); \ (hess)[2] = rspX*rspY*(IND(iv3,vi,2,2,1) - IND(iv3,vi,0,2,1) - IND(iv3,vi,2,0,1) + IND(iv3,vi,0,0,1))/4.0f; \ (hess)[3] = rspX*rspZ*(IND(iv3,vi,2,1,2) - IND(iv3,vi,0,1,2) - IND(iv3,vi,2,1,0) + IND(iv3,vi,0,1,0))/4.0f; \ (hess)[5] = rspY*rspZ*(IND(iv3,vi,1,2,2) - IND(iv3,vi,1,0,2) - IND(iv3,vi,1,2,0) + IND(iv3,vi,1,0,0))/4.0f #define _COIL_CONDUCT(LL, KK) \ (exp(-0.5*(LL)/(KK))) /* #define _COIL_CONDUCT(vec, KK) \ (1.0/(1.0 + (LL)/(KK))) */ /* ** watch out for false advertising! */ void _coilKind7TensorFilterSelf(coil_t *delta, int xi, int yi, int zi, coil_t **iv3, double spacing[3], double parm[COIL_PARMS_NUM]) { coil_t hess[7], rspX, rspY, rspZ, parm0; float eval[3], evec[9], tens[7], lin; AIR_UNUSED(xi); AIR_UNUSED(yi); AIR_UNUSED(zi); rspX = AIR_CAST(coil_t, 1.0/spacing[0]); rspY = AIR_CAST(coil_t, 1.0/spacing[1]); rspZ = AIR_CAST(coil_t, 1.0/spacing[2]); TENS(tens, iv3); tenEigensolve_f(eval, evec, tens); lin = (eval[0] - eval[1])/(eval[0] - eval[2] + 0.000001f); TEN_T3V_OUTER(tens, evec + 3*0); delta[0] = 0; parm0 = AIR_CAST(coil_t, parm[0]); HESS(hess, iv3, 1, rspX, rspY, rspZ); delta[1] = lin*parm0*tens[0]*TEN_T_DOT(hess, tens); HESS(hess, iv3, 2, rspX, rspY, rspZ); delta[2] = lin*parm0*tens[0]*TEN_T_DOT(hess, tens); HESS(hess, iv3, 3, rspX, rspY, rspZ); delta[3] = lin*parm0*tens[0]*TEN_T_DOT(hess, tens); HESS(hess, iv3, 4, rspX, rspY, rspZ); delta[4] = lin*parm0*tens[0]*TEN_T_DOT(hess, tens); HESS(hess, iv3, 5, rspX, rspY, rspZ); delta[5] = lin*parm0*tens[0]*TEN_T_DOT(hess, tens); HESS(hess, iv3, 6, rspX, rspY, rspZ); delta[6] = lin*parm0*tens[0]*TEN_T_DOT(hess, tens); } void _coilKind7TensorFilterFinish(coil_t *delta, int xi, int yi, int zi, coil_t **iv3, double spacing[3], double parm[COIL_PARMS_NUM]) { coil_t rspX, rspY, rspZ, rspsqX, rspsqY, rspsqZ; double eval[3], evec[9], tens[7], tengrad[21], grad[3], LL, KK, cnd, dmu1[7], dmu2[7], dskw[7], phi3[7]; AIR_UNUSED(xi); AIR_UNUSED(yi); AIR_UNUSED(zi); rspX = AIR_CAST(coil_t, 1.0/spacing[0]); rspsqX = rspX*rspX; rspY = AIR_CAST(coil_t, 1.0/spacing[1]); rspsqY = rspY*rspY; rspZ = AIR_CAST(coil_t, 1.0/spacing[2]); rspsqZ = rspZ*rspZ; TENS(tens, iv3); TENGRAD(tengrad, iv3, rspX, rspY, rspZ); tenEigensolve_d(eval, evec, tens); tenInvariantGradientsK_d(dmu1, dmu2, dskw, tens, 0.000001); tenRotationTangents_d(NULL, NULL, phi3, evec); /* \midhat{\nabla} \mu_1 ----------------- */ ELL_3V_SET(grad, TEN_T_DOT(dmu1, tengrad + 0*7), TEN_T_DOT(dmu1, tengrad + 1*7), TEN_T_DOT(dmu1, tengrad + 2*7)); LL = ELL_3V_DOT(grad,grad); KK = parm[1]*parm[1]; cnd = _COIL_CONDUCT(LL, KK); /* \midhat{\nabla} \mu_2 ----------------- */ ELL_3V_SET(grad, TEN_T_DOT(dmu2, tengrad + 0*7), TEN_T_DOT(dmu2, tengrad + 1*7), TEN_T_DOT(dmu2, tengrad + 2*7)); LL = ELL_3V_DOT(grad,grad); KK = parm[2]*parm[2]; cnd *= _COIL_CONDUCT(LL, KK); /* \midhat{\nabla} \skw and twist! ----------------- */ ELL_3V_SET(grad, TEN_T_DOT(dskw, tengrad + 0*7), TEN_T_DOT(dskw, tengrad + 1*7), TEN_T_DOT(dskw, tengrad + 2*7)); LL = ELL_3V_DOT(grad,grad); ELL_3V_SET(grad, TEN_T_DOT(phi3, tengrad + 0*7), TEN_T_DOT(phi3, tengrad + 1*7), TEN_T_DOT(phi3, tengrad + 2*7)); LL += ELL_3V_DOT(grad,grad); KK = AIR_CAST(coil_t, parm[3]*parm[3]); cnd *= _COIL_CONDUCT(LL, KK); delta[0]= 0.0f; delta[1]= AIR_CAST(coil_t, parm[0]*cnd*LAPL(iv3, 1, rspsqX, rspsqY, rspsqZ)); delta[2]= AIR_CAST(coil_t, parm[0]*cnd*LAPL(iv3, 2, rspsqX, rspsqY, rspsqZ)); delta[3]= AIR_CAST(coil_t, parm[0]*cnd*LAPL(iv3, 3, rspsqX, rspsqY, rspsqZ)); delta[4]= AIR_CAST(coil_t, parm[0]*cnd*LAPL(iv3, 4, rspsqX, rspsqY, rspsqZ)); delta[5]= AIR_CAST(coil_t, parm[0]*cnd*LAPL(iv3, 5, rspsqX, rspsqY, rspsqZ)); delta[6]= AIR_CAST(coil_t, parm[0]*cnd*LAPL(iv3, 6, rspsqX, rspsqY, rspsqZ)); } void _coilKind7TensorUpdate(coil_t *val, coil_t *delta) { val[0] += delta[0]; /* WARNING: this could change confidence! */ val[1] += delta[1]; val[2] += delta[2]; val[3] += delta[3]; val[4] += delta[4]; val[5] += delta[5]; val[6] += delta[6]; } const coilKind _coilKind7Tensor = { "tensor", 7, {NULL, _coilKind7TensorFilterTesting, _coilKind7TensorFilterHomogeneous, NULL, NULL, NULL, NULL, _coilKind7TensorFilterSelf, _coilKind7TensorFilterFinish}, _coilKind7TensorUpdate }; const coilKind * coilKind7Tensor = &_coilKind7Tensor; teem-1.11.0~svn6057/src/coil/methodsCoil.c0000664000175000017500000001662712165631065017761 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "coil.h" int coilVolumeCheck(const Nrrd *nin, const coilKind *kind) { static const char me[]="coilVolumeCheck"; unsigned int baseDim; if (!(nin && kind)) { biffAddf(COIL, "%s: got NULL pointer", me); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(COIL, "%s: can only operate on scalar types, not %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } baseDim = (1 == kind->valLen ? 0 : 1); if (3 + baseDim != nin->dim) { biffAddf(COIL, "%s: dim of input must be 3+%d (3 + baseDim), not %d", me, baseDim, nin->dim); return 1; } return 0; } coilContext * coilContextNew() { coilContext *cctx; cctx = (coilContext *)calloc(1, sizeof(coilContext)); if (cctx) { cctx->nin = NULL; cctx->radius = coilDefaultRadius; cctx->numThreads = 1; ELL_3V_SET(cctx->spacing, AIR_NAN, AIR_NAN, AIR_NAN); cctx->nvol = NULL; cctx->finished = AIR_FALSE; cctx->task = NULL; cctx->nextSliceMutex = NULL; cctx->filterBarrier = NULL; cctx->updateBarrier = NULL; } return cctx; } int coilContextAllSet(coilContext *cctx, const Nrrd *nin, const coilKind *kind, const coilMethod *method, unsigned int radius, unsigned int numThreads, int verbose, double parm[COIL_PARMS_NUM]) { static const char me[]="coilContextAllSet"; int someExist, allExist, baseDim, pi; size_t size[NRRD_DIM_MAX], sx, sy, sz; double xsp, ysp, zsp; airArray *mop; cctx->verbose = verbose; if (!( cctx && nin && kind && method )) { biffAddf(COIL, "%s: got NULL pointer", me); return 1; } if (coilVolumeCheck(nin, kind)) { biffAddf(COIL, "%s: input volume not usable as %s", me, kind->name); return 1; } if (!( radius >= 1 && numThreads >= 1 )) { biffAddf(COIL, "%s: radius (%d) not >= 1 or numThreads (%d) not >= 1", me, radius, numThreads); return 1; } if (!( AIR_IN_OP(coilMethodTypeUnknown, method->type, coilMethodTypeLast) )) { biffAddf(COIL, "%s: method->type %d not valid", me, method->type); return 1; } if (!kind->filter[method->type]) { biffAddf(COIL, "%s: sorry, %s filtering not available on %s kind", me, method->name, kind->name); return 1; } /* warn if we can't do the multiple threads user wants */ if (numThreads > 1 && !airThreadCapable && airThreadNoopWarning) { fprintf(stderr, "%s: WARNING: this Teem not thread capable: using 1 " "thread, not %d\n", me, numThreads); numThreads = 1; } mop = airMopNew(); /* set parms */ for (pi=0; pinumParm; pi++) { if (!AIR_EXISTS(parm[pi])) { biffAddf(COIL, "%s: parm[%d] (need %d) doesn't exist", me, pi, method->numParm); airMopError(mop); return 1; } cctx->parm[pi] = parm[pi]; } /* set sizes and spacings */ baseDim = (1 == kind->valLen ? 0 : 1); sx = nin->axis[0 + baseDim].size; sy = nin->axis[1 + baseDim].size; sz = nin->axis[2 + baseDim].size; if (sz < numThreads) { char stmp[AIR_STRLEN_SMALL]; airSprintSize_t(stmp, sz); fprintf(stderr, "%s: wanted %d threads but volume only has %s slices, " "using %s threads instead\n", me, numThreads, stmp, stmp); numThreads = AIR_UINT(sz); } ELL_3V_SET(cctx->size, sx, sy, sz); xsp = nin->axis[0 + baseDim].spacing; ysp = nin->axis[1 + baseDim].spacing; zsp = nin->axis[2 + baseDim].spacing; someExist = AIR_EXISTS(xsp) || AIR_EXISTS(ysp) || AIR_EXISTS(zsp); allExist = AIR_EXISTS(xsp) && AIR_EXISTS(ysp) && AIR_EXISTS(zsp); if (!( someExist )) { fprintf(stderr, "%s: WARNING: assuming unit spacing for all axes\n", me); xsp = 1; ysp = 1; zsp = 1; } else { if ( !allExist ) { biffAddf(COIL, "%s: spacings (%g,%g,%g) not uniformly existent", me, xsp, ysp, zsp); airMopError(mop); return 1; } } ELL_3V_SET(cctx->spacing, xsp, ysp, zsp); if (cctx->verbose) { fprintf(stderr, "%s: spacings: %g %g %g\n", me, cctx->spacing[0], cctx->spacing[1], cctx->spacing[2]); } /* allocate nvol */ if (0 == baseDim) { ELL_4V_SET(size, 2, sx, sy, sz); } else { ELL_5V_SET(size, kind->valLen, 2, sx, sy, sz); } cctx->nvol = nrrdNew(); if (nrrdMaybeAlloc_nva(cctx->nvol, coil_nrrdType, 4 + baseDim, size)) { biffMovef(COIL, NRRD, "%s: couldn't allocate internal processing volume", me); airMopError(mop); return 1; } airMopAdd(mop, cctx->nvol, (airMopper)nrrdNuke, airMopOnError); cctx->nin = nin; cctx->kind = kind; cctx->method = method; cctx->radius = radius; cctx->numThreads = numThreads; airMopOkay(mop); return 0; } /* ******** coilOutputGet ** ** slice the present intermediate volume to get an output. ** ** No, this does not do quantization or rounding to match the input ** type (of cctx->nin). The reason is that after filtering, it is often ** the case that subtle differences in values emerge, and it may be ** reckless to dump them back into the limited type or value range ** that they started with. That sort of operation should be under ** explicit user control. */ int coilOutputGet(Nrrd *nout, coilContext *cctx) { static const char me[]="coilOutputGet"; int baseDim; if (!(nout && cctx)) { biffAddf(COIL, "%s: got NULL pointer", me); return 1; } baseDim = (1 == cctx->kind->valLen ? 0 : 1); if (nrrdSlice(nout, cctx->nvol, baseDim, 0) || nrrdAxisInfoCopy(nout, cctx->nin, NULL, NRRD_AXIS_INFO_NONE) || nrrdBasicInfoCopy(nout, cctx->nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffMovef(COIL, NRRD, "%s: trouble getting output", me); return 1; } return 0; } coilContext * coilContextNix(coilContext *cctx) { if (cctx) { /* thread machinery destroyed with coilFinish() */ cctx->nvol = nrrdNuke(cctx->nvol); airFree(cctx); } return NULL; } teem-1.11.0~svn6057/src/coil/test/0000775000175000017500000000000012203513755016304 5ustar domibeldomibelteem-1.11.0~svn6057/src/coil/test/coiler.c0000664000175000017500000001001312042367142017716 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../coil.h" char *info = ("Test program for coil library."); int main(int argc, const char *argv[]) { const char *me; char *err, *outS; hestOpt *hopt=NULL; airArray *mop; int numIters, numThreads, methodType, kindType, _parmLen, pi, radius, verbose; Nrrd *nin, *nout; coilContext *cctx; double *_parm, parm[COIL_PARMS_NUM]; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "iter", "# iters", airTypeInt, 1, 1, &numIters, "5", "number of iterations to do processing for"); hestOptAdd(&hopt, "nt", "# threads", airTypeInt, 1, 1, &numThreads, "5", "number of threads to run"); hestOptAdd(&hopt, "k", "kind", airTypeEnum, 1, 1, &kindType, NULL, "what kind of volume is input", NULL, coilKindType); hestOptAdd(&hopt, "m", "method", airTypeEnum, 1, 1, &methodType, "test", "what kind of filtering to perform", NULL, coilMethodType); hestOptAdd(&hopt, "p", "parms", airTypeDouble, 1, -1, &_parm, NULL, "all the parameters required for filtering method", &_parmLen); hestOptAdd(&hopt, "r", "radius", airTypeInt, 1, 1, &radius, "1", "radius of filtering neighborhood"); hestOptAdd(&hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "1", "verbosity level"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &(nin), "", "input volume to filter", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output file to save filtering result into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); cctx = coilContextNew(); airMopAdd(mop, cctx, (airMopper)coilContextNix, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (_parmLen != coilMethodArray[methodType]->numParm) { fprintf(stderr, "%s: %s method wants %d parms, but got %d\n", me, coilMethodArray[methodType]->name, coilMethodArray[methodType]->numParm, _parmLen); airMopError(mop); return 1; } for (pi=0; pi<_parmLen; pi++) { parm[pi] = _parm[pi]; } if (coilContextAllSet(cctx, nin, coilKindArray[kindType], coilMethodArray[methodType], radius, numThreads, verbose, parm) || coilStart(cctx) || coilIterate(cctx, numIters) || coilFinish(cctx) || coilOutputGet(nout, cctx)) { airMopAdd(mop, err = biffGetDone(COIL), airFree, airMopAlways); fprintf(stderr, "%s: trouble with coil:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/air/0000775000175000017500000000000012203513755015152 5ustar domibeldomibelteem-1.11.0~svn6057/src/air/miscAir.c0000664000175000017500000004362012173674745016727 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" #include "privateAir.h" /* timer functions */ #ifdef _WIN32 #include #include #include #else #include #endif /* ******** airTeemVersion ******** airTeemReleaseDate ** ** updated with each release to contain a string representation of ** the Teem version number and release date. Originated in version 1.5; ** use of TEEM_VERSION #defines started in 1.9 */ const char * airTeemVersion = TEEM_VERSION_STRING; const char * airTeemReleaseDate = "8 August 2013"; double _airSanityHelper(double val) { return val*val*val; } /* ******** airNull() ** ** returns NULL */ void * airNull(void) { return NULL; } /* ******** airSetNull ** ** dereferences and sets to NULL, returns NULL */ void * airSetNull(void **ptrP) { if (ptrP) { *ptrP = NULL; } return NULL; } /* ******** airFree() ** ** to facilitate setting a newly free()'d pointer; always returns NULL. ** also makes sure that NULL is not passed to free(). */ void * airFree(void *ptr) { if (ptr) { free(ptr); } return NULL; } /* ******** airFopen() ** ** encapsulates that idea that "-" is either standard in or stardard ** out, and does McRosopht stuff required to make piping work ** ** Does not error checking. If fopen fails, then C' errno and strerror are ** left untouched for the caller to access. */ FILE * airFopen(const char *name, FILE *std, const char *mode) { FILE *ret; if (!strcmp(name, "-")) { ret = std; #ifdef _WIN32 if (strchr(mode, 'b')) { _setmode(_fileno(ret), _O_BINARY); } #endif } else { ret = fopen(name, mode); } return ret; } /* ******** airFclose() ** ** just to facilitate setting a newly fclose()'d file pointer to NULL ** also makes sure that NULL is not passed to fclose(), and won't close ** stdin, stdout, or stderr (its up to the user to open these correctly) */ FILE * airFclose(FILE *file) { if (file) { if (!( stdin == file || stdout == file || stderr == file )) { fclose(file); } } return NULL; } /* ******** airSinglePrintf ** ** a complete stand-in for {f|s}printf(), as long as the given format ** string contains exactly one conversion sequence. The utility of ** this is to standardize the printing of IEEE 754 special values: ** QNAN, SNAN -> "NaN" ** POS_INF -> "+inf" ** NEG_INF -> "-inf" ** The format string can contain other things besides just the ** conversion sequence: airSingleFprintf(f, " (%f)\n", AIR_NAN) ** will be the same as fprintf(f, " (%s)\n", "NaN"); ** ** To get fprintf behavior, pass "str" as NULL ** to get sprintf bahavior, pass "file" as NULL ** ** Finding a complete {f|s|}printf replacement is a priority for Teem 2.0 */ int airSinglePrintf(FILE *file, char *str, const char *_fmt, ...) { char *fmt, buff[AIR_STRLEN_LARGE]; double val=0, gVal, fVal; int ret, isF, isD, cls; char *conv=NULL, *p0, *p1, *p2, *p3, *p4, *p5; va_list ap; va_start(ap, _fmt); fmt = airStrdup(_fmt); /* this is needlessly complicated; the "l" modifier is a no-op */ p0 = strstr(fmt, "%e"); p1 = strstr(fmt, "%f"); p2 = strstr(fmt, "%g"); p3 = strstr(fmt, "%le"); p4 = strstr(fmt, "%lf"); p5 = strstr(fmt, "%lg"); isF = p0 || p1 || p2; isD = p3 || p4 || p5; /* the code here says "isF" and "isD" as if it means "is float" or "is double". It really should be "is2" or "is3", as in, "is 2-character conv. seq., or "is 3-character conv. seq." */ if (isF) { conv = p0 ? p0 : (p1 ? p1 : p2); } if (isD) { conv = p3 ? p3 : (p4 ? p4 : p5); } if (isF || isD) { /* use "double" instead of "float" because var args are _always_ subject to old-style C type promotions: float promotes to double */ val = va_arg(ap, double); cls = airFPClass_d(val); switch (cls) { case airFP_SNAN: case airFP_QNAN: case airFP_POS_INF: case airFP_NEG_INF: if (isF) { memcpy(conv, "%s", 2); } else { /* this sneakiness allows us to replace a 3-character conversion sequence for a double (such as %lg) with a 3-character conversion for a string, which we know has at most 4 characters */ memcpy(conv, "%4s", 3); } break; } #define PRINT(F, S, C, V) ((F) ? fprintf((F),(C),(V)) : sprintf((S),(C),(V))) switch (cls) { case airFP_SNAN: case airFP_QNAN: ret = PRINT(file, str, fmt, "NaN"); break; case airFP_POS_INF: ret = PRINT(file, str, fmt, "+inf"); break; case airFP_NEG_INF: ret = PRINT(file, str, fmt, "-inf"); break; default: if (p2 || p5) { /* got "%g" or "%lg", see if it would be better to use "%f" */ sprintf(buff, "%f", val); sscanf(buff, "%lf", &fVal); sprintf(buff, "%g", val); sscanf(buff, "%lf", &gVal); if (fVal != gVal) { /* using %g (or %lg) lost precision!! Use %f (or %lf) instead */ if (p2) { memcpy(conv, "%f", 2); } else { memcpy(conv, "%lf", 3); } } } ret = PRINT(file, str, fmt, val); break; } } else { /* conversion sequence is neither for float nor double */ ret = file ? vfprintf(file, fmt, ap) : vsprintf(str, fmt, ap); } va_end(ap); free(fmt); return ret; } /* ******** airSprintSize_t ** ** sprints a single size_t to a given string, side-stepping ** non-standardized format specifier confusion with printf */ char * airSprintSize_t(char _str[AIR_STRLEN_SMALL], size_t val) { char str[AIR_STRLEN_SMALL]; unsigned int si; if (!_str) { return NULL; } si = AIR_STRLEN_SMALL-1; str[si] = '\0'; do { str[--si] = AIR_CAST(char, (val % 10) + '0'); val /= 10; } while (val); strcpy(_str, str + si); return _str; } /* ******** airSprintPtrdiff_t ** ** sprints a single ptrdiff_t to a given string, side-stepping ** non-standardized format specifier confusion with printf */ char * airSprintPtrdiff_t(char _str[AIR_STRLEN_SMALL], ptrdiff_t val) { char str[AIR_STRLEN_SMALL]; unsigned int si; int sign; if (!_str) { return NULL; } si = AIR_STRLEN_SMALL-1; str[si] = '\0'; sign = (val < 0 ? -1 : 1); do { ptrdiff_t dig; dig = val % 10; str[--si] = AIR_CAST(char, dig > 0 ? dig + '0' : -dig + '0'); val /= 10; } while (val); if (-1 == sign) { str[--si] = '-'; } strcpy(_str, str + si); return _str; } /* ---- BEGIN non-NrrdIO */ const int airPresent = 42; /* ** sprints a length-"len" vector "vec" of size_t values into "dst", which is ** simply assumed to be big enough to hold this. Vector enclosed in "[]" and ** values separated by "," */ char * airSprintVecSize_t(char *dst, const size_t *vec, unsigned int len) { char stmp[AIR_STRLEN_SMALL]; unsigned int axi; /* if we got NULL to sprint into, there's nothing to do but return it */ if (!dst) { return dst; } strcpy(dst, "["); for (axi=0; axi 1024) { dval /= 1024; suffIdx++; } else { break; } } sprintf(str, "%g %s", dval, suff[suffIdx]); return str; } /* ******** airStderr, airStdout, airStdin ** ** these exist only to give uniform access to FILE *s that might be ** annoying to access in higher-level language wrappings around Teem. */ FILE * airStderr(void) { return stderr; } FILE * airStdout(void) { return stdout; } FILE * airStdin(void) { return stdin; } /* ******** AIR_INDEX(i,x,I,L,t) ** ** READ CAREFULLY!! ** ** Utility for mapping a floating point x in given range [i,I] to the ** index of an array with L elements, AND SAVES THE INDEX INTO GIVEN ** VARIABLE t, WHICH MUST BE OF SOME INTEGER TYPE because this relies ** on the implicit cast of an assignment to truncate away the ** fractional part. ALSO, t must be of a type large enough to hold ** ONE GREATER than L. So you can't pass a variable of type unsigned ** char if L is 256 ** ** DOES NOT DO BOUNDS CHECKING: given an x which is not inside [i,I], ** this may produce an index not inside [0,L-1] (but it won't always ** do so: the output being outside range [0,L-1] is not a reliable ** test of the input being outside range [i, I]). The mapping is ** accomplished by dividing the range from i to I into L intervals, ** all but the last of which is half-open; the last one is closed. ** For example, the number line from 0 to 3 would be divided as ** follows for a call with i = 0, I = 4, L = 4: ** ** index: 0 1 2 3 = L-1 ** intervals: [ )[ )[ )[ ] ** |----|----|----|----| ** value: 0 1 2 3 4 ** ** The main point of the diagram above is to show how I made the ** arbitrary decision to orient the half-open interval, and which ** end has the closed interval. ** ** Note that AIR_INDEX(0,3,4,4,t) and AIR_INDEX(0,4,4,4,t) both set t = 3 ** ** The reason that this macro requires a argument for saving the ** result is that this is the easiest way to avoid extra conditionals. ** Otherwise, we'd have to do some check to see if x is close enough ** to I so that the generated index would be L and not L-1. "Close ** enough" because due to precision problems you can have an x < I ** such that (x-i)/(I-i) == 1, which was a bug with the previous version ** of this macro. It is far simpler to just do the index generation ** and then do the sneaky check to see if the index is too large by 1. ** We are relying on the fact that C _defines_ boolean true to be exactly 1. ** ** Note also that we are never explicity casting to one kind of int or ** another-- the given t can be any integral type, including long long. */ /* #define AIR_INDEX(i,x,I,L,t) ( \ (t) = (L) * ((double)(x)-(i)) / ((double)(I)-(i)), \ (t) -= ((t) == (L)) ) */ /* ******* airIndex ** ** replaces AIR_INDEX macro; see above */ unsigned int airIndex(double min, double val, double max, unsigned int N) { unsigned int idx; double mnm; mnm = max - min; if (mnm) { idx = AIR_UINT(N*(val - min)/mnm); idx -= (idx == N); } else { idx = 0; } return idx; } unsigned int airIndexClamp(double min, double val, double max, unsigned int N) { unsigned int idx; double mnm; /* NOTE: now that unsigned types are used more widely in Teem, the clamping that used to happen after index generation now must happen prior to index generation */ mnm = max - min; if (mnm) { val = AIR_MAX(min, val); idx = AIR_UINT(N*(val - min)/mnm); idx = AIR_MIN(idx, N-1); } else { idx = 0; } return idx; } airULLong airIndexULL(double min, double val, double max, airULLong N) { airULLong idx; #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) /* compile error on Win32-vs60: "error C2520: conversion from unsigned __int64 to double not implemented, use signed __int64 */ airLLong sidx; sidx = AIR_CAST(airLLong, AIR_CAST(double, N)*(val - min)/(max - min)); idx = AIR_CAST(airULLong, sidx); #else idx = AIR_CAST(airULLong, AIR_CAST(double, N)*(val - min)/(max - min)); #endif idx -= (idx == N); return idx; } airULLong airIndexClampULL(double min, double val, double max, airULLong N) { airULLong idx; #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) airLLong sidx; val = AIR_MAX(min, val); /* see note in airIndexClamp */ sidx = AIR_CAST(airLLong, AIR_CAST(double, N)*(val - min)/(max - min)); idx = AIR_CAST(airULLong, sidx); #else val = AIR_MAX(min, val); /* see note in airIndexClamp */ idx = AIR_CAST(airULLong, AIR_CAST(double, N)*(val - min)/(max - min)); #endif idx = AIR_MIN(idx, N-1); return idx; } /* ******* airDoneStr() ** ** dinky little utility for generating progress messages of the form ** " 1.9%" or " 35.3%" or "100.0%" ** ** The message will ALWAYS be six characters, and will ALWAYS be ** preceeded by six backspaces. Thus, you pass in a string to print ** into, and it had better be allocated for at least 6+6+1 = 13 chars. */ char * airDoneStr(double start, double here, double end, char *str) { int perc=0; if (str) { if (end != start) perc = (int)(1000*(here-start)/(end-start) + 0.5); else perc = 1000; if (perc < 0) { sprintf(str, "\b\b\b\b\b\b ---- "); } else if (perc < 1000) { sprintf(str, "\b\b\b\b\b\b% 3d.%d%%", perc/10, perc%10); } else if (perc == 1000) { /* the "% 3d" formatting sequence should have taken care of this, but whatever */ sprintf(str, "\b\b\b\b\b\b100.0%%"); } else { sprintf(str, "\b\b\b\b\b\b done."); } } return str; } /* ******** airTime() ** ** returns current time in seconds (with millisecond resolution only ** when not on Windows) as a double. From "man gettimeofday": The ** time is expressed in seconds and microseconds since midnight (0 ** hour), January 1, 1970. */ double airTime() { #ifdef _WIN32 /* HEY: this has crummy precision */ return (double)clock()/CLOCKS_PER_SEC; #else double sec, usec; struct timeval tv; gettimeofday(&tv, NULL); sec = AIR_CAST(double, tv.tv_sec); usec = AIR_CAST(double, tv.tv_usec); return sec + usec*1.0e-6; #endif } const char airTypeStr[AIR_TYPE_MAX+1][AIR_STRLEN_SMALL] = { "(unknown)", "bool", "int", "unsigned int", "long int", "unsigned long int", "size_t", "float", "double", "char", "string", "enum", "other", }; const size_t airTypeSize[AIR_TYPE_MAX+1] = { 0, sizeof(int), sizeof(int), sizeof(unsigned int), sizeof(long int), sizeof(unsigned long int), sizeof(size_t), sizeof(float), sizeof(double), sizeof(char), sizeof(char*), sizeof(int), 0 /* we don't know anything about type "other" */ }; /* ******** airEqvSettle() ** ** takes a mapping map[i], i in [0..len-1], and shifts the range of the ** mapping downward so that the range is a contiguous set of uints ** starting at 0. ** ** returns the number of different uints; previous version returned one ** less than this (the highest mapping output value, after the settling) ** ** honestly this doesn't necessarily have anything to do with processing ** equivalence classes, but its an operation that is nice to have in ** those cases */ unsigned int airEqvSettle(unsigned int *map, unsigned int len) { unsigned int i, j, count, max, *hit; max = 0; for (i=0; idata); for (eqi=0; eqilen; eqi++) { j = eqv[0 + 2*eqi]; k = eqv[1 + 2*eqi]; while (map[j] != j) { j = map[j]; } while (map[k] != k) { k = map[k]; } if (j != k) { if (j < k) { t = j; j = k; k = t; } map[j] = k; } } for (j=0; jlen) { /* we have some equivalences, but we're only going to check against the last one in an effort to reduce duplicate entries */ eqv = AIR_CAST(unsigned int*, eqvArr->data); eqi = eqvArr->len-1; if ( (eqv[0 + 2*eqi] == j && eqv[1 + 2*eqi] == k) || (eqv[0 + 2*eqi] == k && eqv[1 + 2*eqi] == j) ) { /* don't add a duplicate */ return; } } eqi = airArrayLenIncr(eqvArr, 1); eqv = AIR_CAST(unsigned int*, eqvArr->data); eqv[0 + 2*eqi] = j; eqv[1 + 2*eqi] = k; return; } /* ---- END non-NrrdIO */ teem-1.11.0~svn6057/src/air/threadAir.c0000664000175000017500000003274212165631065017233 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" /* HEY: the whole matter of function returns has to be standardized. */ int airThreadNoopWarning = AIR_TRUE; /* ------------------------------------------------------------------ */ #if TEEM_PTHREAD /* ----------------------------------------- PTHREAD */ /* ------------------------------------------------------------------ */ #include const int airThreadCapable = AIR_TRUE; struct _airThread { pthread_t id; }; struct _airThreadMutex { pthread_mutex_t id; }; struct _airThreadCond { pthread_cond_t id; }; airThread * airThreadNew(void) { airThread *thread; thread = AIR_CALLOC(1, airThread); /* HEY: not sure if this can be usefully initialized */ return thread; } int airThreadStart(airThread *thread, void *(*threadBody)(void *), void *arg) { pthread_attr_t attr; pthread_attr_init(&attr); #ifdef __sgi pthread_attr_setscope(&attr, PTHREAD_SCOPE_BOUND_NP); #endif return pthread_create(&(thread->id), &attr, threadBody, arg); } int airThreadJoin(airThread *thread, void **retP) { return pthread_join(thread->id, retP); } airThread * airThreadNix(airThread *thread) { airFree(thread); return NULL; } airThreadMutex * airThreadMutexNew(void) { airThreadMutex *mutex; mutex = AIR_CALLOC(1, airThreadMutex); if (mutex) { if (pthread_mutex_init(&(mutex->id), NULL)) { mutex = (airThreadMutex *)airFree(mutex); } } return mutex; } int airThreadMutexLock(airThreadMutex *mutex) { return pthread_mutex_lock(&(mutex->id)); } int airThreadMutexUnlock(airThreadMutex *mutex) { return pthread_mutex_unlock(&(mutex->id)); } airThreadMutex * airThreadMutexNix(airThreadMutex *mutex) { if (mutex) { if (!pthread_mutex_destroy(&(mutex->id))) { /* there was no error */ mutex = (airThreadMutex *)airFree(mutex); } } return mutex; } airThreadCond * airThreadCondNew(void) { airThreadCond *cond; cond = AIR_CALLOC(1, airThreadCond); if (cond) { if (pthread_cond_init(&(cond->id), NULL)) { /* there was an error */ cond = (airThreadCond *)airFree(cond); } } return cond; } int airThreadCondWait(airThreadCond *cond, airThreadMutex *mutex) { return pthread_cond_wait(&(cond->id), &(mutex->id)); } int airThreadCondSignal(airThreadCond *cond) { return pthread_cond_signal(&(cond->id)); } int airThreadCondBroadcast(airThreadCond *cond) { return pthread_cond_broadcast(&(cond->id)); } airThreadCond * airThreadCondNix(airThreadCond *cond) { if (cond) { if (!pthread_cond_destroy(&(cond->id))) { /* there was no error */ cond = (airThreadCond *)airFree(cond); } } return cond; } /* ------------------------------------------------------------------ */ #elif defined(_WIN32) /* ------------------------------------- WIN 32 */ /* ------------------------------------------------------------------ */ #if defined(_WIN32) /* SignalObjectAndWait supported by NT4.0 and greater only */ # define _WIN32_WINNT 0x400 # include #endif const int airThreadCapable = AIR_TRUE; struct _airThread { HANDLE handle; void *(*body)(void *); void *arg; void *ret; }; struct _airThreadMutex { HANDLE handle; }; struct _airThreadCond { int count; CRITICAL_SECTION lock; HANDLE sema; HANDLE done; size_t broadcast; }; airThread * airThreadNew(void) { airThread *thread; thread = AIR_CALLOC(1, airThread); /* HEY: any useful way to initialized a HANDLE? */ thread->handle = NULL; thread->body = NULL; thread->arg = thread->ret = NULL; return thread; } #if defined(__BORLANDC__) unsigned long #else int #endif /* defined(__BORLANDC__) */ WINAPI _airThreadWin32Body(void *_thread) { airThread *thread; thread = (airThread *)_thread; thread->ret = thread->body(thread->arg); return 0; } int airThreadStart(airThread *thread, void *(*threadBody)(void *), void *arg) { thread->body = threadBody; thread->arg = arg; thread->handle = CreateThread(0, 0, _airThreadWin32Body, (void *)thread, 0, 0); return NULL == thread->handle; } int airThreadJoin(airThread *thread, void **retP) { int err; err = (WAIT_FAILED == WaitForSingleObject(thread->handle, INFINITE)); *retP = thread->ret; return err; } airThread * airThreadNix(airThread *_thread) { char me[] = "airThreadNix"; if (0 == CloseHandle(_thread->handle)) { fprintf(stderr, "%s: CloseHandle failed, something is wrong\n", me); } return airFree(_thread); } airThreadMutex * airThreadMutexNew() { airThreadMutex *mutex; mutex = AIR_CALLOC(1, airThreadMutex); if (mutex) { if (!(mutex->handle = CreateMutex(NULL, FALSE, NULL))) { return airFree(mutex); } } return mutex; } int airThreadMutexLock(airThreadMutex *mutex) { return WAIT_FAILED == WaitForSingleObject(mutex->handle, INFINITE); } int airThreadMutexUnlock(airThreadMutex *mutex) { return 0 == ReleaseMutex(mutex->handle); } airThreadMutex * airThreadMutexNix(airThreadMutex *mutex) { CloseHandle(mutex->handle); mutex = airFree(mutex); return mutex; } airThreadCond * airThreadCondNew(void) { airThreadCond *cond; cond = AIR_CALLOC(1, airThreadCond); if (cond) { cond->count = 0; cond->broadcast = 0; cond->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); if (NULL == cond->sema) { return airFree(cond); } InitializeCriticalSection(&(cond->lock)); cond->done = CreateEvent(NULL, FALSE, FALSE, NULL); if (NULL == cond->done) { CloseHandle(cond->sema); return airFree(cond); } } return cond; } int airThreadCondWait(airThreadCond *cond, airThreadMutex *mutex) { int last; /* increment count */ EnterCriticalSection(&(cond->lock)); /* avoid race conditions */ cond->count++; LeaveCriticalSection(&(cond->lock)); /* atomically release the mutex and wait on the semaphore until airThreadCondSignal or airThreadCondBroadcast are called by another thread */ if (WAIT_FAILED == SignalObjectAndWait(mutex->handle, cond->sema, INFINITE, FALSE)) { return 1; } /* reacquire lock to avoid race conditions */ EnterCriticalSection(&(cond->lock)); /* we're no longer waiting... */ cond->count--; /* check to see if we're the last waiter after airThreadCondBroadcast */ last = (cond->broadcast && 0 == cond->count); LeaveCriticalSection(&(cond->lock)); /* if we're the last waiter thread during this particular broadcast then let all the other threads proceed */ if (last) { /* atomically signal the done event and waits until we can acquire the mutex (this is required to ensure fairness) */ if (WAIT_FAILED == SignalObjectAndWait(cond->done, mutex->handle, INFINITE, FALSE)) { return 1; } } else { /* regain the external mutex since that's the guarantee to our callers */ if (WAIT_FAILED == WaitForSingleObject(mutex->handle, INFINITE)) { return 1; } } return 0; } int airThreadCondSignal(airThreadCond *cond) { int waiters; EnterCriticalSection(&(cond->lock)); waiters = cond->count > 0; LeaveCriticalSection(&(cond->lock)); /* if there aren't any waiters, then this is a no-op */ if (waiters) { if (0 == ReleaseSemaphore(cond->sema, 1, NULL)) { return 1; } } return 0; } int airThreadCondBroadcast(airThreadCond *cond) { int waiters; /* need to ensure that cond->count and cond->broadcast are consistent */ EnterCriticalSection(&(cond->lock)); waiters = 0; if (cond->count > 0) { /* we are broadcasting, even if there is just one waiter... record that we are broadcasting, which helps optimize airThreadCondWait for the non-broadcast case */ cond->broadcast = 1; waiters = 1; } if (waiters) { /* wake up all the waiters atomically */ if (0 == ReleaseSemaphore(cond->sema, cond->count, 0)) { return 1; } LeaveCriticalSection(&(cond->lock)); /* wait for all the awakened threads to acquire the counting semaphore */ if (WAIT_FAILED == WaitForSingleObject(cond->done, INFINITE)) { return 1; } /* this assignment is okay, even without the lock held because no other waiter threads can wake up to access it */ cond->broadcast = 0; } else { LeaveCriticalSection(&(cond->lock)); } return 0; } airThreadCond * airThreadCondNix(airThreadCond *cond) { airThreadCond *ret=NULL; if (cond) { cond->count = 0; cond->broadcast = 0; if (0 == CloseHandle(cond->done)) { ret = cond; } DeleteCriticalSection(&(cond->lock)); if (0 == CloseHandle(cond->sema)) { ret = cond; } ret = airFree(cond); } return ret; } /* ------------------------------------------------------------------ */ #else /* --------------------------------------- (no multi-threading) */ /* ------------------------------------------------------------------ */ const int airThreadCapable = AIR_FALSE; struct _airThread { void *ret; }; struct _airThreadMutex { int dummy; }; struct _airThreadCond { int dummy; }; airThread * airThreadNew(void) { airThread *thread; thread = AIR_CALLOC(1, airThread); thread->ret = NULL; return thread; } int airThreadStart(airThread *thread, void *(*threadBody)(void *), void *arg) { /* run the threadBody callback, which will return only when the task has decided to call it quits */ thread->ret = (*threadBody)(arg); return 0; } int airThreadJoin(airThread *thread, void **retP) { *retP = thread->ret; return 0; } airThread * airThreadNix(airThread *thread) { airFree(thread); return NULL; } airThreadMutex * airThreadMutexNew(void) { airThreadMutex *mutex; mutex = AIR_CALLOC(1, airThreadMutex); return mutex; } int airThreadMutexLock(airThreadMutex *mutex) { char me[]="airThreadMutexLock"; AIR_UNUSED(mutex); if (airThreadNoopWarning) { fprintf(stderr, "%s: WARNING: all mutex usage is a no-op!\n", me); } return 0; } int airThreadMutexUnlock(airThreadMutex *mutex) { char me[]="airThreadMutexUnlock"; AIR_UNUSED(mutex); if (airThreadNoopWarning) { fprintf(stderr, "%s: WARNING: all mutex usage is a no-op!\n", me); } return 0; } airThreadMutex * airThreadMutexNix(airThreadMutex *mutex) { airFree(mutex); return NULL; } airThreadCond * airThreadCondNew(void) { airThreadCond *cond; cond = AIR_CALLOC(1, airThreadCond); return cond; } int airThreadCondWait(airThreadCond *cond, airThreadMutex *mutex) { char me[]="airThreadCondWait"; AIR_UNUSED(cond); AIR_UNUSED(mutex); if (airThreadNoopWarning) { fprintf(stderr, "%s: WARNING: all cond usage is a no-op!\n", me); } return 0; } int airThreadCondSignal(airThreadCond *cond) { char me[]="airThreadCondSignal"; AIR_UNUSED(cond); if (airThreadNoopWarning) { fprintf(stderr, "%s: WARNING: all cond usage is a no-op!\n", me); } return 0; } int airThreadCondBroadcast(airThreadCond *cond) { char me[]="airThreadCondBroadcast"; AIR_UNUSED(cond); if (airThreadNoopWarning) { fprintf(stderr, "%s: WARNING: all cond usage is a no-op!\n", me); } return 0; } airThreadCond * airThreadCondNix(airThreadCond *cond) { airFree(cond); return NULL; } /* ------------------------------------------------------------------ */ #endif /* ----------------------------------------------------------- */ /* ------------------------------------------------------------------ */ airThreadBarrier * airThreadBarrierNew(unsigned int numUsers) { airThreadBarrier *barrier; barrier = AIR_CALLOC(1, airThreadBarrier); if (barrier) { barrier->numUsers = numUsers; barrier->numDone = 0; if (!(barrier->doneMutex = airThreadMutexNew())) { airFree(barrier); return NULL; } if (!(barrier->doneCond = airThreadCondNew())) { barrier->doneMutex = airThreadMutexNix(barrier->doneMutex); airFree(barrier); return NULL; } } return barrier; } int airThreadBarrierWait(airThreadBarrier *barrier) { airThreadMutexLock(barrier->doneMutex); barrier->numDone += 1; if (barrier->numDone < barrier->numUsers) { airThreadCondWait(barrier->doneCond, barrier->doneMutex); } else { barrier->numDone = 0; airThreadCondBroadcast(barrier->doneCond); } airThreadMutexUnlock(barrier->doneMutex); return 0; } airThreadBarrier * airThreadBarrierNix(airThreadBarrier *barrier) { barrier->doneMutex = airThreadMutexNix(barrier->doneMutex); barrier->doneCond = airThreadCondNix(barrier->doneCond); airFree(barrier); return NULL; } teem-1.11.0~svn6057/src/air/randMT.c0000664000175000017500000002332412165631065016511 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* This file is a modified version of the MersenneTwister.h source file written by Richard J. Wagner. The original copyright notice follows. Mersenne Twister random number generator -- a C++ class MTRand Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com The Mersenne Twister is an algorithm for generating random numbers. It was designed with consideration of the flaws in various other generators. The period, 2^19937-1, and the order of equidistribution, 623 dimensions, are far greater. The generator is also fast; it avoids multiplication and division, and it benefits from caches and pipelines. For more information see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html Reference M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, Copyright (C) 2000 - 2003, Richard J. Wagner All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The original code included the following notice: When you use this, send an email to: matumoto@math.keio.ac.jp with an appropriate reference to your work. It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu when you write. */ #include "air.h" #include "privateAir.h" #define AIR_RANDMT_M 397 #define AIR_RANDMT_DEFAULT_SEED 42 /* Inlined class member functions that I made macros */ #define HIBIT( u ) ((u) & 0x80000000UL) #define LOBIT( u ) ((u) & 0x00000001UL) #define LOBITS( u ) ((u) & 0x7fffffffUL) #define MIXBITS( u, v ) (HIBIT(u) | LOBITS(v)) #define TWOSCOMP( u ) (~(u)+1) #define TWIST( m, s0, s1 ) \ ((m) ^ (MIXBITS(s0,s1)>>1) ^ (TWOSCOMP(LOBIT(s1)) & 0x9908b0dfUL)) /* airRandMTStateGlobal is not allocated at compile-time because of weirdness of where non-initialized global objects go in shared libraries. Its allocation and initialization are controlled by _airRandMTStateGlobal_{allocated,initialized}. Users who want to ensure thread safety should be using airRandMTStateNew and airDrandMT_r, not the global state. */ airRandMTState *airRandMTStateGlobal = NULL; static int _airRandMTStateGlobal_allocated = AIR_FALSE; static int _airRandMTStateGlobal_initialized = AIR_FALSE; static void _airRandMTInitialize(airRandMTState *rng, unsigned int seed) { /* Initialize generator state with seed See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. In previous versions, most significant bits (MSBs) of the seed affect only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */ register unsigned int *s = rng->state; register unsigned int *r = rng->state; register int i = 1; *s++ = seed & 0xffffffffUL; for( ; i < AIR_RANDMT_N; ++i ) { *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; r++; } } static void _airRandMTReload(airRandMTState *rng) { /* Generate N new values in state. Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */ register int i; register unsigned int *p = rng->state; for (i=AIR_RANDMT_N - AIR_RANDMT_M; i--; ++p) { *p = TWIST( p[AIR_RANDMT_M], p[0], p[1] ); } for (i=AIR_RANDMT_M; --i; ++p) { *p = TWIST( p[AIR_RANDMT_M-AIR_RANDMT_N], p[0], p[1] ); } *p = TWIST( p[AIR_RANDMT_M-AIR_RANDMT_N], p[0], rng->state[0] ); rng->left = AIR_RANDMT_N; rng->pNext = rng->state; } airRandMTState * airRandMTStateNew(unsigned int seed) { airRandMTState* ret; ret = AIR_CAST(airRandMTState*, malloc(sizeof(airRandMTState))); airSrandMT_r(ret, seed); return ret; } airRandMTState * airRandMTStateNix(airRandMTState *state) { airFree(state); return NULL; } void airSrandMT_r(airRandMTState *rng, unsigned int seed) { /* Seed the generator with a simple uint32 */ _airRandMTInitialize(rng, seed); _airRandMTReload(rng); } /* Pull a 32-bit integer from the generator state. Every other access ** function simply transforms the numbers extracted here. */ unsigned int airUIrandMT_r(airRandMTState *rng) { register unsigned int s1; if (rng->left == 0) { _airRandMTReload(rng); } --rng->left; s1 = *rng->pNext++; s1 ^= (s1 >> 11); s1 ^= (s1 << 7) & 0x9d2c5680UL; s1 ^= (s1 << 15) & 0xefc60000UL; return ( s1 ^ (s1 >> 18) ); } /* This generates the closed interval [0,1] */ double airDrandMT_r(airRandMTState *rng) { double result = airUIrandMT_r(rng); return result * (1.0/4294967295.0); } /* [0,1) w/ 53 bit precision (capacity of IEEE double precision) */ double airDrandMT53_r(airRandMTState *rng) { unsigned int a, b; a = airUIrandMT_r(rng) >> 5; b = airUIrandMT_r(rng) >> 6; return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); /* by Isaku Wada */ } #define _GLOBAL_ALLOC \ if (!_airRandMTStateGlobal_allocated) { \ airRandMTStateGlobal = airRandMTStateNew(0); \ _airRandMTStateGlobal_allocated = AIR_TRUE; \ } #define _GLOBAL_INIT \ if (!_airRandMTStateGlobal_initialized) { \ airSrandMT(AIR_RANDMT_DEFAULT_SEED); \ } \ /* ******** airRandMTStateGlobalInit ** ** Allocates and initializes airRandMTStateGlobal if it was not already ** allocated and initialized. However, this does not need to be called ** except if the user wants to use the airRandMTStateGlobal pointer. ** The allocation and initialization is done as necessary inside the ** the airSrandMT(), airDrandMT(), etc functions (all the functions ** that use airRandMTStateGlobal */ void airRandMTStateGlobalInit() { _GLOBAL_ALLOC; _GLOBAL_INIT; } void airSrandMT(unsigned int seed) { _GLOBAL_ALLOC; airSrandMT_r(airRandMTStateGlobal, seed); _airRandMTStateGlobal_initialized = AIR_TRUE; } double airDrandMT() { _GLOBAL_ALLOC; _GLOBAL_INIT; return airDrandMT_r(airRandMTStateGlobal); } /* ******** airRandInt ** ** returns a random integer in range [0, N-1] */ unsigned int airRandInt(unsigned int N) { _GLOBAL_ALLOC; _GLOBAL_INIT; return airUIrandMT_r(airRandMTStateGlobal)%N; } unsigned int airRandInt_r(airRandMTState *state, unsigned int N) { return airUIrandMT_r(state)%N; } /* This checks to see if the sequence of numbers we get is what we expect. It should return 1 if all is well, 0 if not. One thing to check for if it fails is the presence of twos complement interger representation. The code here relies on it. */ int airRandMTSanity(void) { int result = 0; /* Create a new generator with our seed */ airRandMTState* rng = airRandMTStateNew(AIR_RANDMT_DEFAULT_SEED); if (!rng) { /* Couldn't allocate memory */ return 0; } /* Now check against a predetermined list of values; any inequality will set result to be non-zero */ result |= airUIrandMT_r(rng) != 1608637542U; result |= airUIrandMT_r(rng) != 3421126067U; result |= airUIrandMT_r(rng) != 4083286876U; result |= airUIrandMT_r(rng) != 787846414U; result |= airUIrandMT_r(rng) != 3143890026U; result |= airUIrandMT_r(rng) != 3348747335U; result |= airUIrandMT_r(rng) != 2571218620U; result |= airUIrandMT_r(rng) != 2563451924U; result |= airUIrandMT_r(rng) != 670094950U; result |= airUIrandMT_r(rng) != 1914837113U; airRandMTStateNix(rng); return !result; } teem-1.11.0~svn6057/src/air/heap.c0000664000175000017500000002615612042242175016241 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2010 Thomas Schultz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" #include /* internal helper routines */ /* restore heap property by pulling a single node up the tree */ static void upheap(airHeap *h, unsigned int i) { while (i) { unsigned int parent = (i-1)/2; if (h->key[h->idx[parent]] > h->key[h->idx[i]]) { /* swap */ unsigned int tmp = h->idx[parent]; h->idx[parent] = h->idx[i]; h->idx[i] = tmp; h->invidx[h->idx[i]] = i; h->invidx[h->idx[parent]] = parent; i = parent; } else i = 0; /* break loop */ } } /* restore heap property by pushing a single node down the tree */ static void downheap (airHeap *h, unsigned int i) { unsigned int len = h->key_a->len; while (1) { unsigned int left = 2*i+1; unsigned int right = 2*i+2; unsigned int minidx = 0; double minval = 0; if (left>=len) return; if ((right>=len) || (h->key[h->idx[left]] < h->key[h->idx[right]])) { minidx = left; minval = h->key[h->idx[left]]; } else { minidx = right; minval = h->key[h->idx[right]]; } if (h->key[h->idx[i]] > minval) { /* swap */ unsigned int tmp = h->idx[i]; h->idx[i] = h->idx[minidx]; h->idx[minidx] = tmp; h->invidx[h->idx[i]] = i; h->invidx[h->idx[minidx]] = minidx; i = minidx; } else return; } } /* Restore heap property "from scratch" (without any prior assumptions). * Works in a bottom-up manner, which is more efficient than top-down */ static void heapify (airHeap *h) { unsigned int len = h->key_a->len; int maxi = len/2-1, i; /* above maxi, downheap has no effect */ for (i=maxi; i>=0; i--) { downheap(h, i); } } /* Tries to increase or decrease the length of h. returns 0 upon success. */ static int heapLenIncr (airHeap *h, int delta) { unsigned int oldlen=h->key_a->len; unsigned int newlen=oldlen+delta; if (delta==0) return 0; airArrayLenIncr(h->key_a, delta); if (h->data_a!=NULL) airArrayLenIncr(h->data_a, delta); airArrayLenIncr(h->idx_a, delta); airArrayLenIncr(h->invidx_a, delta); if (h->key_a->lendata_a!=NULL && h->data_a->lenidx_a->leninvidx_a->lenkey_a->len>oldlen) airArrayLenSet(h->key_a, oldlen); if (h->data_a!=NULL && h->data_a->len>oldlen) airArrayLenSet(h->data_a, oldlen); if (h->idx_a->len>oldlen) airArrayLenSet(h->idx_a, oldlen); if (h->invidx_a->len>oldlen) airArrayLenSet(h->invidx_a, oldlen); return 1; } return 0; } /* Creates a new heap, returning NULL upon error. If additional data * is to be stored with each node, dataUnit needs to be set to the * number of data bytes needed per element. incr is used for dynamic * memory allocation (an additional number of incr elements are * allocated each time the heap grows past its current capacity). */ airHeap *airHeapNew(size_t dataUnit, unsigned int incr) { airHeap *h; airPtrPtrUnion appu; h = AIR_CALLOC(1, airHeap); if (h==NULL) { return NULL; } appu.d = &h->key; h->key_a = airArrayNew(appu.v, NULL, sizeof(double), incr); if (dataUnit>0) { /* data is optional */ h->data_a = airArrayNew(&h->data, NULL, dataUnit, incr); } appu.ui = &h->idx; h->idx_a = airArrayNew(appu.v, NULL, sizeof(unsigned int), incr); appu.ui = &h->invidx; h->invidx_a = airArrayNew(appu.v, NULL, sizeof(unsigned int), incr); if (h->key_a==NULL || (dataUnit>0 && h->data_a==NULL) || h->idx_a==NULL || h->invidx_a==NULL) { /* allocation failed (partly) */ airHeapNix(h); return NULL; } return h; } /* Same as airHeapNew, but initializes the new heap with the keys from key * and the optional data from data. If data is non-NULL, it needs to * have the same length as key, or this function will fail. The incr * field of the heap is copied from key (rather than data). */ airHeap *airHeapFromArray(const airArray *key, const airArray *data) { airHeap *h; unsigned int i; if (key==NULL || (data!=NULL && data->len!=key->len)) return NULL; /* unusable input */ h = airHeapNew((data==NULL)?0:data->unit, key->incr); if (heapLenIncr (h, key->len)) { /* could not allocate memory */ airHeapNix(h); return NULL; } memcpy(h->key, key->data, key->len*sizeof(double)); if (h->data_a!=NULL) memcpy(h->data, data->data, data->len*data->unit); for (i=0; ilen; i++) { /* initialize indices to identity */ h->idx[i]=i; h->invidx[i]=i; } heapify(h); return h; } /* Frees all memory associated with the heap and its data */ airHeap *airHeapNix(airHeap *h) { if (h!=NULL) { airArrayNuke(h->key_a); if (h->data_a!=NULL) airArrayNuke(h->data_a); airArrayNuke(h->idx_a); airArrayNuke(h->invidx_a); free(h); } return NULL; } /* Returns the number of elements that are currently in heap h * (or 0 if h==NULL) */ unsigned int airHeapLength(const airHeap *h) { if (h!=NULL) { return h->key_a->len; } else return 0; } /* Inserts a key into the heap. data is copied over (deep copy) when the * heap was initialized to hold additional data. Otherwise, it is ignored. * * Returns the new number of elements in h. * Upon error, this can be the same as the old length. */ unsigned int airHeapInsert(airHeap *h, double key, const void *data) { unsigned int oldlen; if (h==NULL) return 0; oldlen = h->key_a->len; /* make space for the new element */ if (heapLenIncr(h, 1)) return oldlen; h->key[oldlen]=key; if (h->data_a!=NULL && data!=NULL) { memcpy((char*)h->data_a->data+oldlen*h->data_a->unit, data, h->data_a->unit); } h->idx[oldlen]=oldlen; h->invidx[oldlen]=oldlen; upheap(h,oldlen); /* restore the heap property */ return oldlen+1; } /* Merges the second heap into the first. Returns the new length of first, * or zero upon error. */ unsigned int airHeapMerge(airHeap *first, const airHeap *second) { unsigned int first_len, second_len, i; if (first==NULL || second==NULL) return 0; if ((first->data_a==NULL) ^ (second->data_a==NULL)) return 0; /* incompatible data */ if (first->data_a!=NULL && (first->data_a->unit!=second->data_a->unit)) return 0; /* incompatible data */ /* make sufficient space in first */ first_len = first->key_a->len; second_len = second->key_a->len; if (heapLenIncr(first, second_len)) return 0; /* concatenate and heapify */ memcpy(first->key+first_len, second->key, second_len*sizeof(double)); if (first->data_a!=NULL) memcpy((char*)first->data_a->data+first_len*first->data_a->unit, second->data_a->data,second_len*second->data_a->unit); for (i=0; iidx[first_len+i] = first_len+second->idx[i]; first->invidx[first->idx[first_len+i]]=first_len+i; } heapify(first); return first_len+second_len; } /* Returns the key of the front element in the heap and optionally copies the * associated data to *data. Does not modify the heap. */ double airHeapFrontPeek(const airHeap *h, void *data) { if (h==NULL || h->key_a->len==0) return 0.0; if (data!=NULL && h->data_a!=NULL) memcpy(data, (char*)h->data_a->data+h->idx[0]*h->data_a->unit, h->data_a->unit); return h->key[h->idx[0]]; } /* Same as airHeapFrontPeek, but additionally removes the front element. */ double airHeapFrontPop(airHeap *h, void *data) { double retval = airHeapFrontPeek(h, data); if (h!=NULL && h->key_a->len>0) { airHeapRemove(h, h->idx[0]); } return retval; } /* Assigns a new key (and optionally, new data) to the front element and * re-sorts the heap if necessary. */ int airHeapFrontUpdate(airHeap *h, double newKey, const void *newData) { if (h==NULL || h->key_a->len==0) return 1; if (newData!=NULL && h->data_a!=NULL) memcpy((char*)h->data_a->data+h->idx[0]*h->data_a->unit, newData, h->data_a->unit); h->key[h->idx[0]]=newKey; downheap(h, 0); return 0; } /* The following functions provide advanced functionality and should * not be required in most applications. */ /* airHeapFind returns 0 if an element is found whose data is bitwise * equal to the given data, and sets *ai to the index of this * element. If more than one element matches the data, an arbitrary * one of them is returned. The index can be used with airHeapRemove * or airHeapUpdate, but will become invalid as soon as any element is * removed from the heap. */ int airHeapFind(const airHeap *h, unsigned int *ai, const void *data) { unsigned int i; if (h==NULL || ai==NULL || data==NULL || h->data_a==NULL) return 1; for (i=0; ikey_a->len; i++) { if (!memcmp((char*)h->data_a->data+i*h->data_a->unit, data, h->data_a->unit)) { *ai = i; return 0; } } return 1; } /* Removes element ai from the heap, returns 0 upon success. */ int airHeapRemove(airHeap *h, unsigned int ai) { unsigned int old_invidx_ai; if (h==NULL || h->key_a->len<=ai) return 1; /* in the tree, replace ai with last element */ old_invidx_ai=h->invidx[ai]; h->idx[h->invidx[ai]]=h->idx[h->key_a->len-1]; h->invidx[h->idx[h->key_a->len-1]]=h->invidx[ai]; /* remove ai - copy last element over, then drop it */ if (ai!=h->key_a->len-1) { h->key[ai]=h->key[h->key_a->len-1]; if (h->data_a!=NULL) { memcpy((char*)h->data_a->data+ai*h->data_a->unit, (char*)h->data_a->data+(h->key_a->len-1)*h->data_a->unit, h->data_a->unit); } h->idx[h->invidx[h->key_a->len-1]]=ai; h->invidx[ai]=h->invidx[h->key_a->len-1]; } heapLenIncr(h, -1); /* push moved element downheap */ if (old_invidx_aikey_a->len) downheap(h, old_invidx_ai); return 0; } /* Changes the key (and optional data) of the element ai, re-sorting * the heap if necessary. Returns 0 upon success. */ int airHeapUpdate(airHeap *h, unsigned int ai, double newKey, const void *newData) { double oldkey; if (h==NULL || h->key_a->len<=ai) return 1; oldkey = h->key[ai]; /* replace key and data */ h->key[ai] = newKey; if (h->data_a!=NULL && newData!=NULL) { memcpy((char*)h->data_a->data+ai*h->data_a->unit, newData, h->data_a->unit); } if (oldkeyinvidx[ai]); else upheap(h, h->invidx[ai]); return 0; } teem-1.11.0~svn6057/src/air/dio.c0000664000175000017500000002240212165631065016073 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" #include #if TEEM_DIO == 0 #else /* HEY: these may be SGI-specific */ #include #include #include #endif #if TEEM_DIO == 0 const int airMyDio = 0; #else const int airMyDio = 1; #endif int airDisableDio = AIR_FALSE; static const char _airNoDioErr[AIR_NODIO_MAX+2][AIR_STRLEN_SMALL] = { "(invalid noDio value)", "CAN TOO do direct I/O!", "direct I/O apparently not available on this architecture", "direct I/O apparently not suitable for given file format", "won't do direct I/O on std{in|out|err}", "got -1 as file descriptor", "fcntl(F_DIOINFO) to learn direct I/O specifics failed", "requested transfer size is too small", "requested transfer size not a multiple of d_miniosz", "data memory address not multiple of d_mem", "current file position not multiple of d_miniosz", "fcntl(F_SETFL, FDIRECT) to turn on direct I/O failed", "memalign() test (on a small chuck of memory) failed", "direct I/O (in air library) has been disabled with airDisableDio" }; const char * airNoDioErr(int noDio) { if (AIR_IN_CL(0, noDio, AIR_NODIO_MAX)) { return _airNoDioErr[noDio+1]; } else { return _airNoDioErr[0]; } } /* ******** airDioTest ** ** does everything necessary to assess whether direct IO can be used ** to read a data segment of a given size, from a given file ** descriptor, into a given pointer. The given pointer ptr can be ** NULL, and/or the size can be 0, in order to test the other aspects ** of direct IO. The return value of this is from the airNoDio_* enum. ** Note that airNoDio_okay means, "actually, direct IO *does* seem to ** be possible here". */ #if TEEM_DIO == 0 int airDioTest(int fd, const void *ptr, size_t size) { AIR_UNUSED(fd); AIR_UNUSED(ptr); AIR_UNUSED(size); /* Teem makefiles think no direct IO is possible on this architecture */ return airNoDio_arch; } #else int airDioTest(int fd, const void *ptr, size_t size) { struct dioattr dioinfo; void *tmp; int flags; if (airDisableDio) { /* user turned direct I/O off */ return airNoDio_disable; } if (0 == fd || 1 == fd || 2 == fd) { /* This was added because I was noticing a problem with piping between unrrdu programs- sometimes the fread() of the receiving data through a unix pipe ("|") failed to read all the data. If the body of this function was bypassed (with "return airNoDio_disable;", for instance), then the problem went away. The problematic call seemed to be the fflush() below (Tue Feb 1 06:47:33 EST 2005: which has since been removed with the change of this function's argument from a FILE * to an integral file descriptor). I don't think direct I/O is possible on stdin, stdout, or stdout, since the fcntl() call below fails on stdin and stdout. However, something about making that fcntl() call changes something which means that about half the time, the read() on a piped stdin fails (on an irix6.n32 O2, at least). So, seems to be safest to just explicitly say that direct I/O is unavailable, based solely on the file descriptor number (0, 1, 2). */ return airNoDio_std; } if (-1 == fd) { /* caller probably couldn't get the underlying file descriptor */ return airNoDio_fd; } if (0 != fcntl(fd, F_DIOINFO, &dioinfo)) { /* couldn't learn direct I/O specifics */ return airNoDio_dioinfo; } if (size) { /* ** direct I/O requirements: ** 1) xfer size between d_miniosz and d_maxiosz ** 2) xfer size a multiple of d_miniosz ** 3) memory buffer on d_mem-byte boundary ** 4) file position on d_miniosz-byte boundary ** ** As long as xfer size is >= d_miniosz and meets req. #2, then ** we can break the xfer into d_maxiosz-size pieces of need be. ** We can test #3 here if we're given non-NULL ptr ** We can always test #4 */ if (size < dioinfo.d_miniosz) { /* fails req. #1 above */ return airNoDio_small; } /* we don't actually check for being too large, since we can always do IO on d_maxiosz-sized pieces */ if (size % dioinfo.d_miniosz) { /* fails req. #2 above */ return airNoDio_size; } } if (ptr) { if ((unsigned long)(ptr) % dioinfo.d_mem) { /* fails req. #3 above */ return airNoDio_ptr; } } else { tmp = memalign(dioinfo.d_mem, dioinfo.d_miniosz); if (!tmp) { /* couldn't even alloc (via memalign) the minimum size */ return airNoDio_test; } free(tmp); } if (lseek(fd, 0, SEEK_CUR) % dioinfo.d_miniosz) { /* fails req. #4 above */ return airNoDio_fpos; } flags = fcntl(fd, F_GETFL); if (-1 == fcntl(fd, F_SETFL, flags | FDIRECT)) { /* couln't turn on direct I/O */ return airNoDio_setfl; } /* put things back the way they were */ fcntl(fd, F_SETFL, flags); /* as far as we know, direct I/O seems workable */ return airNoDio_okay; } #endif /* ******** airDioInfo ** ** does the fcntl stuff to learn the direct IO parameters: ** align: required alignment of memory (pointer must be multiple of this) ** min: minimum size of dio transfer ** max: maximum size of dio transfer ** ** NOTE: this does not try to do any error checking, because it assumes ** that you've already called airDioTest without incident. */ #if TEEM_DIO == 0 void airDioInfo(int *align, int *min, int *max, int fd) { AIR_UNUSED(align); AIR_UNUSED(min); AIR_UNUSED(max); AIR_UNUSED(fd); return; } #else void airDioInfo(int *align, int *min, int *max, int fd) { struct dioattr dioinfo; if (align && min && max && !fcntl(fd, F_DIOINFO, &dioinfo)) { *align = dioinfo.d_mem; *min = dioinfo.d_miniosz; *max = dioinfo.d_maxiosz; } return; } #endif /* ******** airDioMalloc ** ** does direct IO compatible memory allocation. ** ** NOTE: like airDioInfo, this assumes that you've called airDioTest ** without incident */ #if TEEM_DIO == 0 void * airDioMalloc(size_t size, int fd) { AIR_UNUSED(size); AIR_UNUSED(fd); return NULL; } #else void * airDioMalloc(size_t size, int fd) { int align, min, max; airDioInfo(&align, &min, &max, fd); return memalign(align, size); } #endif /* ******** airDioRead ** ** like read(), but for direct IO. The idea is that you call this on as ** big a chunk of memory as possible. ** ** NOTE: like airDioInfo, this assumes that you've called airDioTest ** without incident */ #if TEEM_DIO == 0 size_t airDioRead(int fd, void *_ptr, size_t size) { AIR_UNUSED(fd); AIR_UNUSED(_ptr); AIR_UNUSED(size); return 0; } #else size_t airDioRead(int fd, void *_ptr, size_t size) { size_t red, totalred; int align, min, max, flags; size_t remain, part; char *ptr; if (!( _ptr && airNoDio_okay == airDioTest(fd, _ptr, size) )) { return 0; } flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | FDIRECT); airDioInfo(&align, &min, &max, fd); remain = size; totalred = 0; ptr = (char*)_ptr; do { part = AIR_MIN(remain, max); red = read(fd, ptr, part); totalred += red; if (red != part) { break; } ptr += red; remain -= red; } while (remain); fcntl(fd, F_SETFL, flags); return totalred; } #endif /* ******** airDioWrite ** ** like write(), but for direct IO. The idea is that you call this on as ** big a chunk of memory as possible. ** ** NOTE: like airDioInfo, this assumes that you've called airDioTest ** without incident */ #if TEEM_DIO == 0 size_t airDioWrite(int fd, const void *_ptr, size_t size) { AIR_UNUSED(fd); AIR_UNUSED(_ptr); AIR_UNUSED(size); return 0; } #else size_t airDioWrite(int fd, const void *_ptr, size_t size) { size_t rit, totalrit; int align, min, max, flags; size_t remain, part; char *ptr; if (!( _ptr && (airNoDio_okay == airDioTest(fd, _ptr, size)) )) { return 0; } flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | FDIRECT); airDioInfo(&align, &min, &max, fd); remain = size; totalrit = 0; ptr = (char*)_ptr; do { part = AIR_MIN(remain, max); rit = write(fd, ptr, part); totalrit += rit; if (rit != part) { break; } ptr += rit; remain -= rit; } while (remain); fcntl(fd, F_SETFL, flags); return totalrit; } #endif teem-1.11.0~svn6057/src/air/GNUmakefile0000664000175000017500000000407412165631065017233 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := air #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### $(L).NEED = $(L).PUBLIC_HEADERS = air.h $(L).PRIVATE_HEADERS = privateAir.h $(L).OBJS = 754.o randMT.o array.o miscAir.o parseAir.o math.o \ endianAir.o dio.o mop.o enum.o sane.o string.o threadAir.o heap.o $(L).TESTS = test/floatprint test/doubleprint test/tok \ test/tmop test/tline test/fp test/trand test/tdio \ test/bessy test/tarr test/texp test/logrice test/tprint $(L).NEED_QNANHIBIT = true $(L).NEED_DIO = true #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/air/sane.c0000664000175000017500000001327712165631065016260 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" #include "privateAir.h" /* ******** airSanity() ** ** Does run-time checks to see if the compile-time constants are correct. ** Returns a value from the airInsane* enum; airInsane_not means all ** the checks came back without detecting any problems. */ int airSanity(void) { double nanValue, pinf, ninf; float nanF, pinfF, ninfF; unsigned int sign, expvalue, mant; int tmpI; char endian; unsigned char uc0, uc1; static int _airSanity=0; if (_airSanity) { return airInsane_not; } /* now that there is no more compile-time endian info, this is merely double checking that airMyEndian() works, and returns the constants (either 1234, pronounced "little endian", or 4321, "big endian") that are defined in air.h */ tmpI = 1; endian = !(*((char*)(&tmpI))); if (endian) { /* big endian */ if (4321 != airMyEndian()) { return airInsane_endian; } } else { if (1234 != airMyEndian()) { return airInsane_endian; } } /* checks on sizes of uchar, float, int, double, airLLong */ uc0 = 255; uc1 = AIR_CAST(unsigned char, AIR_INT(uc0) + 1); /* want to overflow */ if (!( 255 == uc0 && 0 == uc1 )) { return airInsane_UCSize; } /* these justify the AIR_EXISTS_F and AIR_EXISTS_D macros */ if (!( (sizeof(float) == sizeof(int)) && (4 == sizeof(int)) )) { return airInsane_FISize; } if (!( (sizeof(double) == sizeof(airLLong)) && (8 == sizeof(airLLong)) )) { return airInsane_DLSize; } /* run-time NaN checks */ pinf = DBL_MAX; pinf = _airSanityHelper(pinf); pinf = _airSanityHelper(pinf); pinf = _airSanityHelper(pinf); if (AIR_EXISTS(pinf)) { return airInsane_pInfExists; } ninf = -pinf; if (AIR_EXISTS(ninf)) { return airInsane_nInfExists; } nanValue = pinf / pinf; if (AIR_EXISTS(nanValue)) { return airInsane_NaNExists; } nanF = (float)nanValue; pinfF = (float)pinf; ninfF = (float)ninf; airFPValToParts_f(&sign, &expvalue, &mant, nanF); mant >>= 22; if (AIR_QNANHIBIT != (int)mant) { return airInsane_QNaNHiBit; } if (!( airFP_QNAN == airFPClass_f(AIR_NAN) && airFP_QNAN == airFPClass_f(AIR_QNAN) /* As of July 4 2012 GLK decides that the signalling NaN tests are more trouble than they're worth: the signal-ness of the NaN is not preserved in double-float conversion for some platforms (so airFP_SNAN == airFPClass_d(AIR_SNAN) has never been enforced), and there are more platforms for which (apparently) passing AIR_SNAN to airFPClass_d changes it to a quiet NaN, which defeats the purpose of the test. To summarize, given that: ** AIR_NAN and AIR_QNAN are checked here to be quiet NaN, after casting to both float and double, ** quiet NaN "hi bit" is tested above, and that ** quiet and signalling NaN are mutually exclusive, skipping the signalling NaN tests is unlikely to undermine knowing the correctness of the compile-time representation of NaNs. So the following line is now commented out for all platforms. */ /* && airFP_SNAN == airFPClass_f(AIR_SNAN) */ && airFP_QNAN == airFPClass_d(AIR_NAN) && airFP_QNAN == airFPClass_d(AIR_QNAN) )) { return airInsane_AIR_NAN; } if (!(airFP_QNAN == airFPClass_f(nanF) && airFP_POS_INF == airFPClass_f(pinfF) && airFP_NEG_INF == airFPClass_f(ninfF))) { /* really, this is verifying that assigning from a double to a float maintains the FPClass for non-existent values */ return airInsane_FltDblFPClass; } /* just make sure AIR_DIO is reasonably set (actually, this should be done by include/teemDio.h) */ switch (AIR_DIO) { case 0: break; case 1: break; default: return airInsane_dio; } _airSanity = 1; return airInsane_not; } static const char _airInsaneErr[AIR_INSANE_MAX+1][AIR_STRLEN_MED] = { "sanity checked PASSED!", "airMyEndian() is wrong", "AIR_EXISTS(+inf) was true", "AIR_EXISTS(-inf) was true", "AIR_EXISTS(NaN) was true", "air_FPClass_f() wrong after double->float assignment", "TEEM_QNANHIBIT is wrong", "airFPClass(AIR_QNAN) wrong", "TEEM_DIO has invalid value", "unsigned char isn't 8 bits", "sizeof(float), sizeof(int) not both == 4", "sizeof(double), sizeof(airLLong) not both == 8", }; static const char _airBadInsane[] = "(invalid insane value)"; const char * airInsaneErr(int insane) { if (AIR_IN_CL(0, insane, AIR_INSANE_MAX)) { return _airInsaneErr[insane]; } else { return _airBadInsane; } } teem-1.11.0~svn6057/src/air/sources.cmake0000664000175000017500000000050311433343453017635 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(AIR_SOURCES 754.c air.h array.c dio.c endianAir.c enum.c heap.c math.c miscAir.c mop.c parseAir.c privateAir.h randMT.c sane.c string.c threadAir.c ) ADD_TEEM_LIBRARY(air ${AIR_SOURCES}) teem-1.11.0~svn6057/src/air/754.c0000664000175000017500000004332712165631065015650 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" #include "privateAir.h" #include /* ** all this is based on a reading of ** Hennessy + Patterson "Computer Architecture, A Quantitative Approach" ** pages A-13 - A-17 ** ** and some assorted web pages, such as: ** http://en.wikipedia.org/wiki/NaN#Encoding ** which explains what Teem calls qnanhibit, and ** http://grouper.ieee.org/groups/754/email/msg04192.html ** which includes some discussion on signal-vs-quiet nan */ /* ** _airFloatEndian{Little,Big}, _airDoubleEndian{Little,Big} ** ** these unions facilitate converting amongst ** i: unsigned integral type ** c: (sign,exp,frac) triples of unsigned integral components ** v: the floating point numbers these bit-patterns represent */ typedef union { unsigned int i; struct { unsigned int mant : 23; unsigned int expo : 8; unsigned int sign : 1; } c; float v; } _airFloatEndianLittle; /* HEY COPY AND PASTE */ typedef union { unsigned int i; struct { unsigned int sign : 1; unsigned int expo : 8; unsigned int mant : 23; } c; float v; } _airFloatEndianBig; typedef union { airULLong i; /* these next two members are used for printing in airFPFprintf_d */ struct { /* access to whole double as two unsigned ints */ unsigned int half0 : 32; unsigned int half1 : 32; } h; struct { /* access to fraction with two unsigned ints */ unsigned int mant1 : 32; unsigned int mant0 : 20; unsigned int expo : 11; unsigned int sign : 1; } c; double v; } _airDoubleEndianLittle; /* HEY COPY AND PASTE */ typedef union { airULLong i; /* these next two members are used for printing in airFPFprintf_d */ struct { /* access to whole double as two unsigned ints */ unsigned int half1 : 32; unsigned int half0 : 32; } h; struct { /* access to fraction with two unsigned ints */ unsigned int sign : 1; unsigned int expo : 11; unsigned int mant0 : 20; unsigned int mant1 : 32; } c; double v; } _airDoubleEndianBig; /* ** The hex numbers in braces are examples of C's "initial member of a union" ** aggregate initialization. */ #if TEEM_QNANHIBIT == 1 const int airMyQNaNHiBit = 1; const airFloat airFloatQNaN = {0x7fffffff}; const airFloat airFloatSNaN = {0x7fbfffff}; #else const int airMyQNaNHiBit = 0; const airFloat airFloatQNaN = {0x7fbfffff}; const airFloat airFloatSNaN = {0x7fffffff}; #endif const airFloat airFloatPosInf = {0x7f800000}; const airFloat airFloatNegInf = {0xff800000}; /* why does solaris whine? */ /* ** these shouldn't be needed, but here they are if need be: in this file: const airFloat airFloatMax = {0x7f7fffff}; const airFloat airFloatMin = {0x00800000}; const airDouble airDoubleMax = {AIR_ULLONG(0x7fefffffffffffff)}; const airDouble airDoubleMin = {AIR_ULLONG(0x0010000000000000)}; in air.h: extern air_export const airFloat airFloatMax; extern air_export const airFloat airFloatMin; extern air_export const airDouble airDoubleMax; extern air_export const airDouble airDoubleMin; #define AIR_FLT_MIN (airFloatMin.f) #define AIR_FLT_MAX (airFloatMax.f) #define AIR_DBL_MIN (airDoubleMin.d) #define AIR_DBL_MAX (airDoubleMax.d) */ /* the bit-masking done here quiets gcc -Wconversion warnings */ #define FP_SET_F(flit, fbig, s, e, m) \ flit.c.sign = 1u & (s); \ flit.c.expo = ((1u<8)-1) & (e); \ flit.c.mant = ((1u<23)-1) & (m); \ fbig.c.sign = 1u & (s); \ fbig.c.expo = ((1u<8)-1) & (e); \ fbig.c.mant = ((1u<23)-1) & (m) #define FP_GET_F(s, e, m, flit, fbig) \ if (airEndianLittle == airMyEndian()) { \ (s) = flit.c.sign; \ (e) = flit.c.expo; \ (m) = flit.c.mant; \ } else { \ (s) = fbig.c.sign; \ (e) = fbig.c.expo; \ (m) = fbig.c.mant; \ } #define FP_SET_D(dlit, dbig, s, e, m0, m1) \ dlit.c.sign = 1u & (s); \ dlit.c.expo = ((1u<<11)-1) & (e); \ dlit.c.mant0 = ((1u<<20)-1) & (m0); \ dlit.c.mant1 = (m1); \ dbig.c.sign = 1u & (s); \ dbig.c.expo = ((1u<<11)-1) & (e); \ dbig.c.mant0 = ((1u<<20)-1) & (m0); \ dbig.c.mant1 = (m1) #define FP_GET_D(s, e, m0, m1, dlit, dbig) \ if (airEndianLittle == airMyEndian()) { \ (s) = dlit.c.sign; \ (e) = dlit.c.expo; \ (m0) = dlit.c.mant0; \ (m1) = dlit.c.mant1; \ } else { \ (s) = dbig.c.sign; \ (e) = dbig.c.expo; \ (m0) = dbig.c.mant0; \ (m1) = dbig.c.mant1; \ } float airFPPartsToVal_f(unsigned int sign, unsigned int expo, unsigned int mant) { _airFloatEndianLittle flit; _airFloatEndianBig fbig; FP_SET_F(flit, fbig, sign, expo, mant); return (airEndianLittle == airMyEndian() ? flit.v : fbig.v); } void airFPValToParts_f(unsigned int *signP, unsigned int *expoP, unsigned int *mantP, float v) { _airFloatEndianLittle flit; _airFloatEndianBig fbig; flit.v = fbig.v = v; FP_GET_F(*signP, *expoP, *mantP, flit, fbig); } double airFPPartsToVal_d(unsigned int sign, unsigned int expo, unsigned int mant0, unsigned int mant1) { _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; FP_SET_D(dlit, dbig, sign, expo, mant0, mant1); return (airEndianLittle == airMyEndian() ? dlit.v : dbig.v); } /* ** Disable the 'local variable used without having been initialized' ** warning produced by the MSVC compiler */ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4700) #endif void airFPValToParts_d(unsigned int *signP, unsigned int *expoP, unsigned int *mant0P, unsigned int *mant1P, double v) { _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; dlit.v = dbig.v = v; FP_GET_D(*signP, *expoP, *mant0P, *mant1P, dlit, dbig); } #ifdef _MSC_VER #pragma warning(pop) #endif /* ******** airFPGen_f() ** ** generates a floating point value which is a member of the given class */ float airFPGen_f(int cls) { _airFloatEndianLittle flit; _airFloatEndianBig fbig; switch(cls) { case airFP_SNAN: /* sgn: anything, mant: anything non-zero with high bit !TEEM_QNANHIBIT */ FP_SET_F(flit, fbig, 0, 0xff, (!TEEM_QNANHIBIT << 22) | 0x3fffff); break; case airFP_QNAN: /* sgn: anything, mant: anything non-zero with high bit TEEM_QNANHIBIT */ FP_SET_F(flit, fbig, 0, 0xff, (TEEM_QNANHIBIT << 22) | 0x3fffff); break; case airFP_POS_INF: FP_SET_F(flit, fbig, 0, 0xff, 0); break; case airFP_NEG_INF: FP_SET_F(flit, fbig, 1, 0xff, 0); break; case airFP_POS_NORM: /* exp: anything non-zero but < 0xff, mant: anything */ FP_SET_F(flit, fbig, 0, 0x80, 0x7ff000); break; case airFP_NEG_NORM: /* exp: anything non-zero but < 0xff, mant: anything */ FP_SET_F(flit, fbig, 1, 0x80, 0x7ff000); break; case airFP_POS_DENORM: /* mant: anything non-zero */ FP_SET_F(flit, fbig, 0, 0, 0xff); break; case airFP_NEG_DENORM: /* mant: anything non-zero */ FP_SET_F(flit, fbig, 1, 0, 0xff); break; case airFP_NEG_ZERO: FP_SET_F(flit, fbig, 1, 0, 0); break; case airFP_POS_ZERO: default: FP_SET_F(flit, fbig, 0, 0, 0); break; } return (airEndianLittle == airMyEndian() ? flit.v : fbig.v); } /* ******** airFPGen_d() ** ** generates a floating point value which is a member of the given class */ double airFPGen_d(int cls) { _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; switch(cls) { case airFP_SNAN: /* sgn: anything, mant: anything non-zero with high bit !TEEM_QNANHIBIT */ FP_SET_D(dlit, dbig, 0, 0x7ff, (!TEEM_QNANHIBIT << 19) | 0x7ffff, 0xffffffff); break; case airFP_QNAN: /* sgn: anything, mant anything non-zero with high bit TEEM_QNANHIBIT */ FP_SET_D(dlit, dbig, 0, 0x7ff, (TEEM_QNANHIBIT << 19) | 0x7ffff, 0xffffffff); break; case airFP_POS_INF: FP_SET_D(dlit, dbig, 0, 0x7ff, 0, 0); break; case airFP_NEG_INF: FP_SET_D(dlit, dbig, 1, 0x7ff, 0, 0); break; case airFP_POS_NORM: /* exp: anything non-zero but < 0xff, mant: anything */ FP_SET_D(dlit, dbig, 0, 0x400, 0x0ff00, 0); break; case airFP_NEG_NORM: /* exp: anything non-zero but < 0xff, mant: anything */ FP_SET_D(dlit, dbig, 1, 0x400, 0x0ff00, 0); break; case airFP_POS_DENORM: /* mant: anything non-zero */ FP_SET_D(dlit, dbig, 0, 0, 0xff, 0); break; case airFP_NEG_DENORM: /* mant: anything non-zero */ FP_SET_D(dlit, dbig, 1, 0, 0xff, 0); break; case airFP_NEG_ZERO: FP_SET_D(dlit, dbig, 1, 0, 0, 0); break; case airFP_POS_ZERO: default: FP_SET_D(dlit, dbig, 0, 0, 0, 0); break; } return (airEndianLittle == airMyEndian() ? dlit.v : dbig.v); } /* ******** airFPClass_f() ** ** given a floating point number, tells which class its in */ int airFPClass_f(float val) { _airFloatEndianLittle flit; _airFloatEndianBig fbig; unsigned int sign, expv, mant; int indexv, ret = 0; flit.v = fbig.v = val; FP_GET_F(sign, expv, mant, flit, fbig); indexv = ((!!sign) << 2) | ((!!expv) << 1) | (!!mant); switch(indexv) { case 0: /* all fields are zero */ ret = airFP_POS_ZERO; break; case 1: /* only mantissa is non-zero */ ret = airFP_POS_DENORM; break; case 2: /* only exponent field is non-zero */ if (0xff == expv) { ret = airFP_POS_INF; } else { ret = airFP_POS_NORM; } break; case 3: /* exponent and mantissa fields are non-zero */ if (0xff == expv) { if (TEEM_QNANHIBIT == mant >> 22) { ret = airFP_QNAN; } else { ret = airFP_SNAN; } } else { ret = airFP_POS_NORM; } break; case 4: /* only sign field is non-zero */ ret = airFP_NEG_ZERO; break; case 5: /* sign and mantissa fields are non-zero */ ret = airFP_NEG_DENORM; break; case 6: /* sign and exponent fields are non-zero */ if (0xff > expv) { ret = airFP_NEG_NORM; } else { ret = airFP_NEG_INF; } break; case 7: /* all fields are non-zero */ if (0xff > expv) { ret = airFP_NEG_NORM; } else { if (TEEM_QNANHIBIT == mant >> 22) { ret = airFP_QNAN; } else { ret = airFP_SNAN; } } break; } return ret; } /* ** Disable the 'local variable used without having been initialized' ** warning produced by the MSVC compiler */ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4700) #endif /* ******** airFPClass_d() ** ** given a double, tells which class its in */ int airFPClass_d(double val) { _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; unsigned int sign, expo, mant0, mant1; int indexv, ret=0; unsigned char hibit; dlit.v = dbig.v = val; /* "expo = d.c.expo" had been annotated with: "this seems to be a WIN32 bug: on a quiet-NaN, d.c.exp should be non-zero, but it was completely zero, so that this function returned airFP_NEG_DENORM instead of airFP_QNAN" */ FP_GET_D(sign, expo, mant0, mant1, dlit, dbig); hibit = AIR_CAST(unsigned char, mant0 >> 19); /* mant0 20 bits wide: ok */ indexv = ((!!sign) << 2) | ((!!expo) << 1) | (!!mant0 || !!mant1); switch(indexv) { case 0: /* all fields are zero */ ret = airFP_POS_ZERO; break; case 1: /* only fractional field is non-zero */ ret = airFP_POS_DENORM; break; case 2: /* only exponent field is non-zero */ if (0x7ff > expo) { ret = airFP_POS_NORM; } else { ret = airFP_POS_INF; } break; case 3: /* exponent and fractional fields are non-zero */ if (0x7ff > expo) { ret = airFP_POS_NORM; } else { if (TEEM_QNANHIBIT == hibit) { ret = airFP_QNAN; } else { ret = airFP_SNAN; } } break; case 4: /* only sign field is non-zero */ ret = airFP_NEG_ZERO; break; case 5: /* sign and fractional fields are non-zero */ ret = airFP_NEG_DENORM; break; case 6: /* sign and exponent fields are non-zero */ if (0x7ff > expo) { ret = airFP_NEG_NORM; } else { ret = airFP_NEG_INF; } break; case 7: /* all fields are non-zero */ if (0x7ff > expo) ret = airFP_NEG_NORM; else { if (TEEM_QNANHIBIT == hibit) { ret = airFP_QNAN; } else { ret = airFP_SNAN; } } break; } return ret; } #ifdef _MSC_VER #pragma warning(pop) #endif /* ******** airIsNaN() ** ** returns 1 if input is either kind of NaN, 0 otherwise. It is okay ** to only have a double version of this function, as opposed to ** having one for float and one for double, because Section 6.2 of the ** 754 spec tells us that that NaN is to be preserved across precision ** changes (and airSanity() explicitly checks for this). */ int airIsNaN(double g) { _airFloatEndianLittle flit; _airFloatEndianBig fbig; unsigned int sign, expo, mant; flit.v = fbig.v = AIR_CAST(float, g); FP_GET_F(sign, expo, mant, flit, fbig); AIR_UNUSED(sign); return (0xff == expo && mant); } /* ******** airIsInf_f(), airIsInf_d() ** ** returns 1 if input is positive infinity, ** -1 if negative infinity, ** or 0 otherwise (including NaN) ** ** thus the non-zero-ness of the return is an easy way to do a ** boolean check of whether the value is infinite */ int airIsInf_f(float f) { int c, ret; c = airFPClass_f(f); if (airFP_POS_INF == c) { ret = 1; } else if (airFP_NEG_INF == c) { ret = -1; } else { ret = 0; } return ret; } int airIsInf_d(double d) { int c, ret; c = airFPClass_d(d); if (airFP_POS_INF == c) { ret = 1; } else if (airFP_NEG_INF == c) { ret = -1; } else { ret = 0; } return ret; } /* airExists_f() airExists_d() were nixed because they weren't used- you can just use AIR_EXISTS_F and AIR_EXISTS_D directly */ /* ******** airExists() ** ** an optimization-proof alternative to AIR_EXISTS */ int airExists(double val) { _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; dbig.v = dlit.v = val; return (airEndianLittle == airMyEndian() ? 0x7ff != dlit.c.expo : 0x7ff != dbig.c.expo); } /* ******** airNaN() ** ** returns a float quiet NaN */ float airNaN(void) { return airFPGen_f(airFP_QNAN); } /* ******** airFPFprintf_f() ** ** prints out the bits of a "float", indicating the three different fields */ void airFPFprintf_f(FILE *file, float val) { int i; unsigned int sign, expo, mant; _airFloatEndianLittle flit; _airFloatEndianBig fbig; if (file) { flit.v = fbig.v = val; FP_GET_F(sign, expo, mant, flit, fbig); fprintf(file, "%f: class %d; 0x%08x = ",val, airFPClass_f(val), airEndianLittle == airMyEndian() ? flit.i : fbig.i); fprintf(file, "sign:0x%x, expo:0x%02x, mant:0x%06x = \n", sign, expo, mant); fprintf(file, " S [ . . Exp . . ] " "[ . . . . . . . . . Mant. . . . . . . . . . ]\n"); fprintf(file, " %d ", sign); for (i=7; i>=0; i--) { fprintf(file, "%d ", (expo >> i) & 1); } for (i=22; i>=0; i--) { fprintf(file, "%d ", (mant >> i) & 1); } fprintf(file, "\n"); } } /* ******** airFPFprintf_d() ** ** prints out the bits of a "double", indicating the three different fields */ void airFPFprintf_d(FILE *file, double val) { int i; _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; unsigned int sign, expo, mant0, mant1; if (file) { dlit.v = dbig.v = val; fprintf(file, "%f: class %d; 0x%08x %08x = \n", val, airFPClass_d(val), (airEndianLittle == airMyEndian() ? dlit.h.half1 : dbig.h.half1), (airEndianLittle == airMyEndian() ? dlit.h.half0 : dbig.h.half0)); FP_GET_D(sign, expo, mant0, mant1, dlit, dbig); fprintf(file, "sign:0x%x, expo:0x%03x, mant:0x%05x %08x = \n", sign, expo, mant0, mant1); fprintf(file, "S[...Exp...][.......................Mant.......................]\n"); fprintf(file, "%d", sign); for (i=10; i>=0; i--) { fprintf(file, "%d", (expo >> i) & 1); } for (i=19; i>=0; i--) { fprintf(file, "%d", (mant0 >> i) & 1); } for (i=31; i>=0; i--) { fprintf(file, "%d", (mant1 >> i) & 1); } fprintf(file, "\n"); } } teem-1.11.0~svn6057/src/air/enum.c0000664000175000017500000003316112165631065016270 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" /* ** Until Teem has its own printf implementation, this will have to do; ** it is imperfect because these are not functionally identical. */ #if defined(WIN32) || defined(_WIN32) # define snprintf _snprintf #endif /* ******** airEnumUnknown ** ** return the value representing "unknown" in an enum */ int airEnumUnknown(const airEnum *enm) { if (enm && enm->val) { return enm->val[0]; } else { return 0; } } /* ** _airEnumIndex() ** ** given an enum "enm" and value "val", return the index into enm->str[] ** and enm->desc[] which correspond to that value. To be safe, when ** given an invalid enum value, we return zero. */ static unsigned int _airEnumIndex(const airEnum *enm, int val) { unsigned int ii, ret; ret = 0; if (enm->val) { for (ii=1; ii<=enm->M; ii++) { if (val == enm->val[ii]) { ret = ii; break; } } } else { unsigned int uval; uval = AIR_UINT(val); ret = (0 <= val && uval <= enm->M) ? uval : 0; } return ret; } /* ** returns non-zero if there is an error: given "val" is *not* ** a valid value of the airEnum "enm" */ int airEnumValCheck(const airEnum *enm, int val) { return (0 == _airEnumIndex(enm, val)); } const char * airEnumStr(const airEnum *enm, int val) { unsigned int idx; idx = _airEnumIndex(enm, val); return enm->str[idx]; } const char * airEnumDesc(const airEnum *enm, int val) { unsigned int idx; idx = _airEnumIndex(enm, val); return enm->desc[idx]; } int airEnumVal(const airEnum *enm, const char *str) { char *strCpy, test[AIR_STRLEN_SMALL]; unsigned int ii; if (!str) { return airEnumUnknown(enm); } strCpy = airStrdup(str); if (!enm->sense) { airToLower(strCpy); } if (enm->strEqv) { /* want strlen and not airStrlen here because the strEqv array should be terminated by a non-null empty string */ for (ii=0; strlen(enm->strEqv[ii]); ii++) { airStrcpy(test, AIR_STRLEN_SMALL, enm->strEqv[ii]); if (!enm->sense) { airToLower(test); } if (!strcmp(test, strCpy)) { free(strCpy); return enm->valEqv[ii]; } } } else { /* enm->strEqv NULL */ for (ii=1; ii<=enm->M; ii++) { airStrcpy(test, AIR_STRLEN_SMALL, enm->str[ii]); if (!enm->sense) { airToLower(test); } if (!strcmp(test, strCpy)) { free(strCpy); return enm->val ? enm->val[ii] : (int)ii; /* HEY scrutinize cast */ } } } /* else we never matched a string */ free(strCpy); return airEnumUnknown(enm); } /* ******** airEnumFmtDesc() ** ** Formats a description line for one element "val" of airEnum "enm", ** and puts the result in a NEWLY ALLOCATED string which is the return ** of this function. The formatting is done via sprintf(), as governed ** by "fmt", which should contain to "%s" conversion sequences, the ** first for the string version "val", and the second for the ** description If "canon", then the canonical string representation ** will be used (the one in enm->str[]), otherwise the shortest string ** representation will be used (which differs from the canonical one ** when there is a strEqv[]/valEqv[] pair defining a shorter string) */ char * airEnumFmtDesc(const airEnum *enm, int val, int canon, const char *fmt) { const char *desc; char *buff, ident[AIR_STRLEN_SMALL]; const char *_ident; int i; size_t len; if (!(enm && enm->desc && fmt)) { return airStrdup("(airEnumDesc: invalid args)"); } if (airEnumValCheck(enm, val)) { val = airEnumUnknown(enm); } _ident = airEnumStr(enm, val); if (!canon && enm->strEqv) { len = airStrlen(_ident); for (i=0; airStrlen(enm->strEqv[i]); i++) { if (val != enm->valEqv[i]) { /* this isn't a string representing the value we care about */ continue; } if (airStrlen(enm->strEqv[i]) < len) { /* this one is shorter */ len = airStrlen(enm->strEqv[i]); _ident = enm->strEqv[i]; } } } airStrcpy(ident, AIR_STRLEN_SMALL, _ident); if (!enm->sense) { airToLower(ident); } desc = enm->desc[_airEnumIndex(enm, val)]; buff = AIR_CALLOC(airStrlen(fmt) + airStrlen(ident) + airStrlen(desc) + 1, char); if (buff) { sprintf(buff, fmt, ident, desc); } return buff; } static void _enumPrintVal(FILE *file, const airEnum *enm, int ii) { if (enm->desc) { fprintf(file, "desc: %s\n", enm->desc[ii]); } if (enm->strEqv) { unsigned int jj; fprintf(file, "eqv:"); fflush(file); jj = 0; while (airStrlen(enm->strEqv[jj])) { if (enm->valEqv[jj] == (enm->val ? enm->val[ii] : ii)) { fprintf(file, " \"%s\"", enm->strEqv[jj]); } jj++; } fprintf(file, "\n"); } } void airEnumPrint(FILE *file, const airEnum *enm) { int ii; /* this should arguable be unsigned int, but airEnum values were kept as "int", even after the great unsigned conversion */ if (!(file && enm)) { return; } if (airStrlen(enm->name)) { fprintf(file, "airEnum \"%s\":\n", enm->name); } else { fprintf(file, "airEnum (NO NAME!):\n"); } fprintf(file, "(%s case sensitive)\n", (enm->sense ? "yes, is" : "is not")); if (enm->val) { fprintf(file, "Values (%u valid) given explicitly\n", enm->M); fprintf(file, "--- (0) %d: \"%s\"\n", enm->val[0], enm->str[0]); for (ii=1; ii<=AIR_CAST(int, enm->M); ii++) { fprintf(file, "--- (%d) %d: \"%s\" == \"%s\"\n", ii, enm->val[ii], enm->str[ii], airEnumStr(enm, enm->val[ii])); _enumPrintVal(file, enm, ii); } } else { /* enm->val NULL */ fprintf(file, "Values implicit; [1,%u] valid\n", enm->M); fprintf(file, "--- 0: \"%s\"\n", enm->str[0]); for (ii=1; ii<=AIR_CAST(int, enm->M); ii++) { fprintf(file, "--- %d: %s == %s\n", ii, enm->str[ii], airEnumStr(enm, ii)); _enumPrintVal(file, enm, ii); } } return; } /* ---- BEGIN non-NrrdIO */ /* ******** airEnumCheck ** ** tries various things to check on the construction and internal ** consistency of an airEnum; returns 1 if there is a problem, and 0 ** if all is well. we're in air, so there's no biff, but we sprintf a ** description of the error into "err", if given ** ** The requirement that the strings have strlen <= AIR_STRLEN_SMALL-1 ** is a reflection of the cheap implementation of the airEnum ** functions in this file, rather than an actual restriction on what an ** airEnum could be. */ int airEnumCheck(char err[AIR_STRLEN_LARGE], const airEnum *enm) { static const char me[]="airEnumCheck"; unsigned int ii, jj; size_t slen, ASL; ASL = AIR_STRLEN_LARGE; if (!enm) { if (err) { snprintf(err, ASL, "%s: got NULL enm", me); } return 1; } if (!enm->name) { if (err) { snprintf(err, ASL, "%s: enm->name NULL", me); } return 1; } if (0 == enm->M) { if (err) { snprintf(err, ASL, "%s(%s): enm->M zero; no values in enum", me, enm->name); } return 1; } for (ii=0; ii<=enm->M; ii++) { if (!enm->str[ii]) { if (err) { snprintf(err, ASL, "%s(%s): enm->str[%u] NULL", me, enm->name, ii); } return 1; } slen = airStrlen(enm->str[ii]); if (!( slen >= 1 && slen <= AIR_STRLEN_SMALL-1 )) { if (err) { char stmp[AIR_STRLEN_SMALL]; snprintf(err, ASL, "%s(%s): strlen(enm->str[%u] \"%s\") " "%s not in range [1,%u]", me, enm->name, ii, enm->str[ii], airSprintSize_t(stmp, slen), AIR_STRLEN_SMALL-1); } return 1; } /* make sure there are no duplicates among the strings, including remapping the case in case of case insensitivity */ for (jj=ii+1; jj<=enm->M; jj++) { if (!strcmp(enm->str[ii], enm->str[jj])) { if (err) { snprintf(err, ASL, "%s(%s): str[%d] and [%u] both \"%s\"", me, enm->name, ii, jj, enm->str[jj]); } return 1; } if (!enm->sense) { char bb1[AIR_STRLEN_SMALL], bb2[AIR_STRLEN_SMALL]; strcpy(bb1, enm->str[ii]); airToLower(bb1); strcpy(bb2, enm->str[jj]); airToLower(bb2); if (!strcmp(bb1, bb2)) { if (err) { snprintf(err, ASL, "%s(%s): after case-lowering, " "str[%d] and [%u] both \"%s\"", me, enm->name, ii, jj, bb1); } return 1; } } } } if (enm->val) { for (ii=1; ii<=enm->M; ii++) { if (enm->val[0] == enm->val[ii]) { if (err) { snprintf(err, ASL, "%s(%s): val[%u] %u same as " "unknown/invalid val[0] %u", me, enm->name, ii, enm->val[ii], enm->val[0]); } return 1; } for (jj=ii+1; jj<=enm->M; jj++) { if (enm->val[jj] == enm->val[ii]) { if (err) { snprintf(err, ASL, "%s(%s): val[%u] %u same as val[%u] %u", me, enm->name, ii, enm->val[ii], jj, enm->val[jj]); } return 1; } } } } if (enm->desc) { for (ii=0; ii<=enm->M; ii++) { if (!enm->desc[ii]) { if (err) { snprintf(err, ASL, "%s(%s): enm->desc[%u] NULL", me, enm->name, ii); } return 1; } /* we don't really care about slen, but learning it will hopefully produce some memory error if the array is not valid */ slen = airStrlen(enm->desc[ii]); if (!( slen > 0 )) { if (err) { snprintf(err, ASL, "%s(%s): enm->desc[%u] empty", me, enm->name, ii); } return 1; } } } if (enm->strEqv) { /* the strEqv array is one of the easiest ways to mess up an airEnum definition; it deserves these tests and maybe more */ if (!enm->valEqv) { if (err) { snprintf(err, ASL, "%s(%s): non-NULL strEqv but NULL valEqv", me, enm->name); } return 1; } if (!( airStrlen(enm->strEqv[0]) )) { if (err) { snprintf(err, ASL, "%s(%s): strEqv[0] is NULL or empty", me, enm->name); } return 1; } /* check length and see if any string maps to an invalid value */ for (ii=0; (slen = strlen(enm->strEqv[ii])); ii++) { if (!( slen <= AIR_STRLEN_SMALL-1 )) { if (err) { char stmp[AIR_STRLEN_SMALL]; snprintf(err, ASL, "%s(%s): strlen(enm->strEqv[%u] \"%s\") " "%s not <= %u", me, enm->name, ii, enm->strEqv[ii], airSprintSize_t(stmp, slen), AIR_STRLEN_SMALL-1); } return 1; } /* see if a string maps to an invalid value */ if (airEnumValCheck(enm, enm->valEqv[ii])) { if (err) { snprintf(err, ASL, "%s(%s): valEqv[%u] %u (with strEqv[%u] \"%s\")" " not valid", me, enm->name, ii, enm->valEqv[ii], ii, enm->strEqv[ii]); } return 1; } } /* make sure eqv strings contain the canonical string */ for (ii=1; ii<=enm->M; ii++) { int eval, rval; eval = (enm->val ? enm->val[ii] : AIR_CAST(int, ii)); rval = airEnumVal(enm, enm->str[ii]); if (eval != rval) { if (err) { snprintf(err, ASL, "%s(%s): ii=%u->eval=%d->str=\"%s\"->%d != %d " "(i.e. canonical string didn't map to its own value)", me, enm->name, ii, eval, enm->str[ii], rval, eval); } return 1; } } /* make sure there are no duplicates among the strEqv, including remapping the case in case of case insensitivity */ for (ii=0; strlen(enm->strEqv[ii]); ii++) { for (jj=ii+1; strlen(enm->strEqv[jj]); jj++) { if (!strcmp(enm->strEqv[ii], enm->strEqv[jj])) { if (err) { snprintf(err, ASL, "%s(%s): strEqv[%d] and [%u] both \"%s\"", me, enm->name, ii, jj, enm->strEqv[jj]); } return 1; } if (!enm->sense) { char bb1[AIR_STRLEN_SMALL], bb2[AIR_STRLEN_SMALL]; strcpy(bb1, enm->strEqv[ii]); airToLower(bb1); strcpy(bb2, enm->strEqv[jj]); airToLower(bb2); if (!strcmp(bb1, bb2)) { if (err) { snprintf(err, ASL, "%s(%s): after case-lowering, " "strEqv[%d] and [%u] both \"%s\"", me, enm->name, ii, jj, bb1); } return 1; } } } } } return 0; } /* ---- END non-NrrdIO */ /* this is the end */ teem-1.11.0~svn6057/src/air/parseAir.c0000664000175000017500000002500112165631065017064 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" static const char * _airBoolStr[] = { "(unknown bool)", "false", "true" }; static const char * _airBoolDesc[] = { "unknown boolean", "false", "true" }; static const int _airBoolVal[] = { -1, AIR_FALSE, AIR_TRUE }; static const char * _airBoolStrEqv[] = { "0", "no", "n", "false", "f", "off", "nope", "1", "yes", "y", "true", "t", "on", "yea", "" }; static const int _airBoolValEqv[] = { AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE }; static const airEnum _airBool = { "boolean", 2, _airBoolStr, _airBoolVal, _airBoolDesc, _airBoolStrEqv, _airBoolValEqv, AIR_FALSE }; const airEnum *const airBool = &_airBool; double airAtod(const char *str) { double val = 0.0; airSingleSscanf(str, "%lf", &val); return val; } int airSingleSscanf(const char *str, const char *fmt, void *ptr) { char *tmp; double val; int ret; if (!strcmp(fmt, "%e") || !strcmp(fmt, "%f") || !strcmp(fmt, "%g") || !strcmp(fmt, "%le") || !strcmp(fmt, "%lf") || !strcmp(fmt, "%lg")) { tmp = airStrdup(str); if (!tmp) { return 0; } airToLower(tmp); if (strstr(tmp, "nan")) { val = AIR_NAN; } /* ---- BEGIN non-NrrdIO */ else if (strstr(tmp, "pi")) { val = AIR_PI; } /* ---- END non-NrrdIO */ else if (strstr(tmp, "-inf")) { val = AIR_NEG_INF; } else if (strstr(tmp, "inf")) { val = AIR_POS_INF; } else { /* nothing special matched; pass it off to sscanf() */ ret = sscanf(str, fmt, ptr); free(tmp); return ret; } /* else we matched "nan", "-inf", or "inf", and set val accordingly */ if (!strncmp(fmt, "%l", 2)) { /* we were given a double pointer */ *((double *)(ptr)) = val; } else { /* we were given a float pointer */ *((float *)(ptr)) = AIR_CAST(float, val); } free(tmp); return 1; } else if (!strcmp(fmt, "%z")) { /* its a size_t */ size_t tsz = 0; /* tmp size_t */ const char *chh = str; /* char here */ while (chh) { int dig; dig = AIR_CAST(int, *chh - '0'); if (AIR_IN_CL(0, dig, 9)) { tsz = 10*tsz + AIR_CAST(size_t, dig); } else { break; } chh++; } *((size_t *)(ptr)) = tsz; return 1; } else { /* not a float, double, or size_t, let sscanf handle it */ return sscanf(str, fmt, ptr); } } #define _PARSE_STR_ARGS(type) type *out, const char *_s, \ const char *ct, unsigned int n, ... #define _PARSE_STR_BODY(format) \ unsigned int i; \ char *tmp, *s, *last; \ \ /* if we got NULL, there's nothing to do */ \ if (!(out && _s && ct)) \ return 0; \ \ /* copy the input so that we don't change it */ \ s = airStrdup(_s); \ \ /* keep calling airStrtok() until we have everything */ \ for (i=0; i 1 || !greedy) { tmp = airStrtok(i ? NULL : s, ct, &last); } else { tmp = s; } if (!tmp) { airMopError(mop); return i; } out[i] = airStrdup(tmp); if (!out[i]) { airMopError(mop); return i; } airMopMem(mop, out+i, airMopOnError); } airMopOkay(mop); return n; } unsigned int airParseStrE(int *out, const char *_s, const char *ct, unsigned int n, ...) { unsigned int i; char *tmp, *s, *last; airArray *mop; va_list ap; airEnum *enm; /* grab the enum every time, prior to error checking */ va_start(ap, n); enm = va_arg(ap, airEnum *); va_end(ap); /* if we got NULL, there's nothing to do */ if (!(out && _s && ct)) { return 0; } mop = airMopNew(); /* copy the input so that we don't change it */ s = airStrdup(_s); airMopMem(mop, &s, airMopAlways); if (1 == n) { /* Because it should be permissible to have spaces in what is intended to be only a single string from an airEnum, we treat 1==n differently, and do NOT use airStrtok to tokenize the input string s into spaces. We check the whole s string */ out[0] = airEnumVal(enm, s); if (airEnumUnknown(enm) == out[0]) { airMopError(mop); return 0; } } else { /* keep calling airStrtok() until we have everything */ for (i=0; istr[0])) { airMopError(mop); return i; } } } airMopOkay(mop); return n; } unsigned int (*airParseStr[AIR_TYPE_MAX+1])(void *, const char *, const char *, unsigned int, ...) = { NULL, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrB, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrI, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrUI, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrLI, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrULI, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrZ, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrF, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrD, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrC, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrS, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrE, NULL /* no standard way of parsing type "other" */ }; teem-1.11.0~svn6057/src/air/math.c0000664000175000017500000007246412202221765016260 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" /* ** by the way, the organization of functions into files is a little ** arbitrary around here */ /* ** from: Gavin C Cawley, "On a Fast, Compact Approximation of the ** Exponential Function" Neural Computation, 2000, 12(9), 2009-2012. ** ** which in turn is based on: N N Schraudolph, "A fast, compact approximation ** of the exponential function." Neural Computation, 1999, 11(4), 853-862. ** http://cnl.salk.edu/~schraudo/pubs/Schraudolph99.pdf ** ** some possible code which does not depend on endian ** http://www.machinedlearnings.com/2011/06/fast-approximate-logarithm-exponential.html */ typedef union { double dd; int nn[2]; } eco_t; #define EXPA (1048576/0.69314718055994530942) #define EXPC 60801 double airFastExp(double val) { eco_t eco; double ret; int tmpI, EXPI; /* HEY: COPY AND PASTE from airMyEndian */ tmpI = 1; EXPI = *(AIR_CAST(char*, &tmpI)); eco.nn[EXPI] = AIR_CAST(int, (EXPA*(val)) + (1072693248 - EXPC)); eco.nn[1-EXPI] = 0; ret = (eco.dd > 0.0 ? eco.dd /* seems that only times this happens is when the real exp() returns either 0 or +inf */ : (val < 0 ? 0 : AIR_POS_INF)); return ret; } #undef EXPA #undef EXPC /* ** based on MiniMaxApproximation, but this has failed in its goal ** of being faster than libc's exp(), so should be considered ** a work-in-progress */ double airExp(double x) { double num, den, ee; unsigned int pp; if (AIR_IN_CL(-1, x, 1)) { num = 1 + x*(0.500241 + x*(0.107193 + (0.0118938 + 0.000591457*x)*x)); den = 1 + x*(-0.499759 + x*(0.106952 + (-0.0118456 + 0.000587495*x)*x)); ee = num/den; } else if (x > 1) { pp = 0; while (x > 2) { x /= 2; pp += 1; } num = 1 + x*(0.552853 + x*(0.135772 + (0.0183685 + 0.00130944*x)*x)); den = 1 + x*(-0.44714 + x*(0.0828937 + (-0.00759541 + 0.000291662*x)*x)); ee = num/den; while (pp) { ee *= ee; pp -= 1; } } else if (x < -1) { pp = 0; while (x < -2) { x /= 2; pp += 1; } num = 0.999999+x*(0.44726+x*(0.0829439 + (0.00760326 + 0.000292122*x)*x)); den = 1 + x*(-0.552732 + x*(0.135702 + (-0.0183511 + 0.00130689*x)*x)); ee = num/den; while (pp) { ee *= ee; pp -= 1; } } else { /* x is probably not finite */ ee = exp(x); } return ee; } /* ******** airNormalRand ** ** generates two numbers with normal distribution (mean 0, stdv 1) ** using the Box-Muller transform. ** ** on (seemingly sound) advice of ** , ** I'm using the polar form of the Box-Muller transform, instead of the ** Cartesian one described at ** ** ** this is careful to not write into given NULL pointers */ void airNormalRand(double *z1, double *z2) { double w, x1, x2; do { x1 = 2*airDrandMT() - 1; x2 = 2*airDrandMT() - 1; w = x1*x1 + x2*x2; } while ( w >= 1 ); w = sqrt((-2*log(w))/w); if (z1) { *z1 = x1*w; } if (z2) { *z2 = x2*w; } return; } void airNormalRand_r(double *z1, double *z2, airRandMTState *state) { double w, x1, x2; do { x1 = 2*airDrandMT_r(state) - 1; x2 = 2*airDrandMT_r(state) - 1; w = x1*x1 + x2*x2; } while ( w >= 1 ); w = sqrt((-2*log(w))/w); if (z1) { *z1 = x1*w; } if (z2) { *z2 = x2*w; } return; } /* ******** airShuffle() ** ** generates a random permutation of integers [0..N-1] if perm is non-zero, ** otherwise, just fills buff with [0..N-1] in order ** ** see http://en.wikipedia.org/wiki/Knuth_shuffle */ void airShuffle(unsigned int *buff, unsigned int N, int perm) { unsigned int i; if (!(buff && N > 0)) { return; } for (i=0; i 1) { swp = airRandInt(N); N--; tmp = buff[N]; buff[N] = buff[swp]; buff[swp] = tmp; } } } void airShuffle_r(airRandMTState *state, unsigned int *buff, unsigned int N, int perm) { unsigned int i; /* HEY !!! COPY AND PASTE !!!! */ if (!(buff && N > 0)) { return; } for (i=0; i 1) { swp = airRandInt_r(state, N); N--; tmp = buff[N]; buff[N] = buff[swp]; buff[swp] = tmp; } } /* HEY !!! COPY AND PASTE !!!! */ } double airSgnPow(double v, double p) { return (p == 1 ? v : (v >= 0 ? pow(v, p) : -pow(-v, p))); } double airFlippedSgnPow(double vv, double pp) { double sn=1.0; if (1.0 == pp) { return vv; } if (vv < 0) { sn = -1.0; vv = -vv; } return sn*(1.0 - pow(1.0 - AIR_MIN(1.0, vv), pp)); } double airIntPow(double v, int p) { double sq, ret; if (p > 0) { sq = v; while (!(p & 1)) { /* while the low bit is zero */ p >>= 1; sq *= sq; } /* must terminate because we know p != 0, and when it terminates we know that the low bit is 1 */ ret = sq; while (p >>= 1) { /* while there are any non-zero bits in p left */ sq *= sq; if (p & 1) { ret *= sq; } } } else if (p < 0) { ret = airIntPow(1.0/v, -p); } else { ret = 1.0; } return ret; } /* ******** airLog2() ** ** silly little function which returns log_2(n) if n is exactly a power of 2, ** and -1 otherwise */ int airLog2(size_t _nn) { size_t nn; int ret; nn = _nn; if (0 == nn) { /* 0 is not a power of 2 */ ret = -1; } else { int alog = 0; /* divide by 2 while its non-zero and the low bit is off */ while (!!nn && !(nn & 1)) { alog += 1; nn /= 2; } if (1 == nn) { ret = alog; } else { ret = -1; } } return ret; } int airSgn(double v) { return (v > 0 ? 1 : (v < 0 ? -1 : 0)); } /* ******** airCbrt ** ** cbrt() isn't in C89, so any hacks to create a stand-in for cbrt() ** are done here. */ double airCbrt(double v) { #if defined(_WIN32) || defined(__STRICT_ANSI__) return (v < 0.0 ? -pow(-v,1.0/3.0) : pow(v,1.0/3.0)); #else return cbrt(v); #endif } /* ** skewness of three numbers, scaled to fit in [-1,+1] ** -1: small, big, big ** +1: small, small, big */ double airMode3_d(const double _v[3]) { double num, den, mean, v[3]; mean = (_v[0] + _v[1] + _v[2])/3; v[0] = _v[0] - mean; v[1] = _v[1] - mean; v[2] = _v[2] - mean; num = (v[0] + v[1] - 2*v[2])*(2*v[0] - v[1] - v[2])*(v[0] - 2*v[1] + v[2]); den = v[0]*v[0] + v[1]*v[1] + v[2]*v[2] - v[1]*v[2] - v[0]*v[1] - v[0]*v[2]; den = sqrt(den); return (den ? num/(2*den*den*den) : 0); } double airMode3(double v0, double v1, double v2) { double v[3]; v[0] = v0; v[1] = v1; v[2] = v2; return airMode3_d(v); } double airGaussian(double x, double mean, double stdv) { x = x - mean; return exp(-(x*x)/(2*stdv*stdv))/(stdv*sqrt(2*AIR_PI)); } /* ** The function approximations below were done by GLK in Mathematica, ** using its MiniMaxApproximation[] function. The functional forms ** used for the Bessel functions were copied from Numerical Recipes ** (which were in turn copied from the "Handbook of Mathematical ** Functions with Formulas, Graphs, and Mathematical Tables" by ** Abramowitz and Stegun), but the coefficients here represent ** an increase in accuracy. ** ** The rational functions (crudely copy/paste from Mathematica into ** this file) upon which the approximations are based have a relative ** error of less than 10^-9, at least on the intervals on which they ** were created (in the case of the second branch of the ** approximation, the lower end of the interval was chosen as close to ** zero as possible). The relative error of the total approximation ** may be greater. */ double airErfc(double x) { double ax, y, b; ax = AIR_ABS(x); if (ax < 0.9820789566638689) { b = (0.9999999999995380997 + ax*(-1.0198241793287349401 + ax*(0.37030717279808919457 + ax*(-0.15987839763633308864 + ax*(0.12416682580357861661 + (-0.04829622197742573233 + 0.0066094852952188890901*ax)*ax)))))/ (1 + ax*(0.1085549876246959456 + ax*(0.49279836663925410323 + ax*(0.020058474597886988472 + ax*(0.10597158000597863862 + (-0.0012466514192679810876 + 0.0099475501252703646772*ax)*ax))))); } else if (ax < 2.020104167011169) { y = ax - 1; b = (0.15729920705029613528 + y*(-0.37677358667097191131 + y*(0.3881956571123873123 + y*(-0.22055886537359936478 + y*(0.073002666454740425451 + (-0.013369369366972563804 + 0.0010602024397541548717*y)*y)))))/ (1 + y*(0.243700597525225235 + y*(0.47203101881562848941 + y*(0.080051054975943863026 + y*(0.083879049958465759886 + (0.0076905426306038205308 + 0.0058528196473365970129*y)*y))))); } else { y = 2/ax; b = (-2.7460876468061399519e-14 + y*(0.28209479188874503125 + y*(0.54260398586720212019 + y*(0.68145162781305697748 + (0.44324741856237800393 + 0.13869182273440856508*y)*y))))/ (1 + y*(1.9234811027995435174 + y*(2.5406810534399069812 + y*(1.8117409273494093139 + (0.76205066033991530997 + 0.13794679143736608167*y)*y)))); b *= exp(-x*x); } if (x < 0) { b = 2-b; } return b; } double airErf(double x) { return 1.0 - airErfc(x); } /* ******** airBesselI0 ** ** modified Bessel function of the first kind, order 0 */ double airBesselI0(double x) { double b, ax, y; ax = AIR_ABS(x); if (ax < 5.664804810929075) { y = x/5.7; y *= y; b = (0.9999999996966272 + y*(7.7095783675529646 + y*(13.211021909077445 + y*(8.648398832703904 + (2.5427099920536578 + 0.3103650754941674*y)*y))))/ (1 + y*(-0.41292170755003793 + (0.07122966874756179 - 0.005182728492608365*y)*y)); } else { y = 5.7/ax; b = (0.398942280546057 + y*(-0.749709626164583 + y*(0.507462772839054 + y*(-0.0918770649691261 + (-0.00135238228377743 - 0.0000897561853670307*y)*y))))/ (1 + y*(-1.90117313211089 + (1.31154807540649 - 0.255339661975509*y)*y)); b *= (exp(ax)/sqrt(ax)); } return b; } /* ******** airBesselI0ExpScaled ** ** modified Bessel function of the first kind, order 0, ** scaled by exp(-abs(x)). */ double airBesselI0ExpScaled(double x) { double b, ax, y; ax = AIR_ABS(x); if (ax < 5.664804810929075) { y = x/5.7; y *= y; b = (0.9999999996966272 + y*(7.7095783675529646 + y*(13.211021909077445 + y*(8.648398832703904 + (2.5427099920536578 + 0.3103650754941674*y)*y))))/ (1 + y*(-0.41292170755003793 + (0.07122966874756179 - 0.005182728492608365*y)*y)); b *= exp(-ax); } else { y = 5.7/ax; b = (0.398942280546057 + y*(-0.749709626164583 + y*(0.507462772839054 + y*(-0.0918770649691261 + (-0.00135238228377743 - 0.0000897561853670307*y)*y))))/ (1 + y*(-1.90117313211089 + (1.31154807540649 - 0.255339661975509*y)*y)); b *= (1/sqrt(ax)); } return b; } /* ******** airBesselI1 ** ** modified Bessel function of the first kind, order 1 */ double airBesselI1(double x) { double b, ax, y; ax = AIR_ABS(x); if (ax < 6.449305566387246) { y = x/6.45; y *= y; b = ax*(0.4999999998235554 + y*(2.370331499358438 + y*(3.3554331305863787 + y*(2.0569974969268707 + (0.6092719473097832 + 0.0792323006694466*y)*y))))/ (1 + y*(-0.4596495788370524 + (0.08677361454866868 \ - 0.006777712190188699*y)*y)); } else { y = 6.45/ax; b = (0.398942280267484 + y*(-0.669339325353065 + y*(0.40311772245257 + y*(-0.0766281832045885 + (0.00248933264397244 + 0.0000703849046144657*y)*y))))/ (1 + y*(-1.61964537617937 + (0.919118239717915 - 0.142824922601647*y)*y)); b *= exp(ax)/sqrt(ax); } return x < 0.0 ? -b : b; } /* ******** airBesselI1ExpScaled ** ** modified Bessel function of the first kind, order 1, ** scaled by exp(-abs(x)) */ double airBesselI1ExpScaled(double x) { double b, ax, y; ax = AIR_ABS(x); if (ax < 6.449305566387246) { y = x/6.45; y *= y; b = ax*(0.4999999998235554 + y*(2.370331499358438 + y*(3.3554331305863787 + y*(2.0569974969268707 + (0.6092719473097832 + 0.0792323006694466*y)*y))))/ (1 + y*(-0.4596495788370524 + (0.08677361454866868 \ - 0.006777712190188699*y)*y)); b *= exp(-ax); } else { y = 6.45/ax; b = (0.398942280267484 + y*(-0.669339325353065 + y*(0.40311772245257 + y*(-0.0766281832045885 + (0.00248933264397244 + 0.0000703849046144657*y)*y))))/ (1 + y*(-1.61964537617937 + (0.919118239717915 - 0.142824922601647*y)*y)); b *= 1/sqrt(ax); } return x < 0.0 ? -b : b; } /* ******** airLogBesselI0 ** ** natural logarithm of airBesselI0 */ double airLogBesselI0(double x) { double b, ax, y; ax = AIR_ABS(x); if (ax < 4.985769687853781) { y = x/5.0; y *= y; b = (5.86105592521167098e-27 + y*(6.2499999970669017 + y*(41.1327842713925212 + y*(80.9030404787605539 + y*(50.7576267390706893 + 6.88231907401413133*y)))))/ (1 + y*(8.14374525361325784 + y*(21.3288286560361152 + y*(20.0880670952325953 + (5.5138982784800139 + 0.186784275148079847*y)*y)))); } else { y = 5.0/ax; b = (-0.91893853280169872884 + y*(2.7513907055333657679 + y*(-3.369024122613176551 + y*(1.9164545708124343838 + (-0.46136261965797010108 + 0.029092365715948197066*y)*y))))/ (1 + y*(-2.9668913151685312745 + y*(3.5882191453626541066 + y*(-1.9954040017063882247 + (0.45606687718126481603 - 0.0231678041994100784*y)*y)))); b += ax - log(ax)/2; } return b; } /* ******** airLogRician ** ** natural logarithm of Rician distribution ** mes is measured value ** tru is "true" underlying value ** sig is sigma of 2-D Gaussian */ double airLogRician(double mes, double tru, double sig) { double lb, ss; ss = sig*sig; lb = airLogBesselI0(mes*tru/ss); return lb + log(mes/ss) - (mes*mes + tru*tru)/(2*ss); } double airRician(double mes, double tru, double sig) { return exp(airLogRician(mes, tru, sig)); } /* ******** airBesselI1By0 ** ** the quotient airBesselI1(x)/airBesselI0(x) */ double airBesselI1By0(double x) { double q, ax, y; ax = AIR_ABS(x); if (ax < 2.2000207427754046) { y = ax/2.2; q = (1.109010375603908e-29 + y*(1.0999999994454934 + y*(0.05256560007682146 + y*(0.3835178789165919 + (0.011328636410807382 + 0.009066934622942833*y)*y))))/ (1 + y*(0.047786822784523904 + y*(0.9536550439261017 + (0.03918380275938573 + 0.09730715527121027*y)*y))); } else if (ax < 5.888258985638512) { y = (ax-2.2)/3.68; q = (0.7280299135046744 + y*(2.5697382341657002 + y*(3.69819451510548 + y*(3.131374238190916 + (1.2811958061688737 + 0.003601218043466571*y)*y))))/ (1 + y*(2.8268553393021527 + y*(4.164742157157812 + y*(3.2377768820326756 + 1.3051900460060342*y)))); } else { y = 5.88/ax; q = (1.000000000646262020372530870790956088593 + y*(-2.012513842496824157039372120680781513697 + y*(1.511644590219033259220408231325838531123 + (-0.3966391319921114140077576390415605232003 + 0.02651815520696779849352690755529178056941*y)*y)))/ (1 + y*(-1.927479858946526082413004924812844224781 + y*(1.351359456116228102988125069310621733956 + (-0.288087717540546638165144937495654019162 + 0.005906535730887518966127383058238522133819*y)*y))); } return x < 0.0 ? -q : q; } /* ******** airBesselIn ** ** modified Bessel function of the first kind, order n. ** */ double airBesselIn(int nn, double xx) { double tax, bb, bi, bim, bip; int ii, an, top; an = AIR_ABS(nn); if (0 == an) { return airBesselI0(xx); } else if (1 == an) { return airBesselI1(xx); } if (0.0 == xx) { return 0.0; } tax = 2.0/AIR_ABS(xx); bip = bb = 0.0; bi = 1.0; top = 2*(an + AIR_CAST(int, sqrt(40.0*an))); for (ii=top; ii > 0; ii--) { bim = bip + ii*tax*bi; bip = bi; bi = bim; if (AIR_ABS(bi) > 1.0e10) { bb *= 1.0e-10; bi *= 1.0e-10; bip*= 1.0e-10; } if (ii == an) { bb = bip; } } bb *= airBesselI0(xx)/bi; return (xx < 0.0 ? -bb : bb); } /* ******** airBesselInExpScaled ** ** modified Bessel function of the first kind, order n, ** scaled by exp(-abs(x)) ** */ double airBesselInExpScaled(int nn, double xx) { double tax, bb, bi, bim, bip, eps; int top, ii, an; an = AIR_ABS(nn); if (0 == an) { return airBesselI0ExpScaled(xx); } else if (1 == an) { return airBesselI1ExpScaled(xx); } if (0 == xx) { return 0.0; } tax = 2.0/AIR_ABS(xx); bip = bb = 0.0; bi = 1.0; /* HEY: GLK tried to increase sqrt(40.0*an) to sqrt(100.0*an) to avoid jagged discontinuities in (e.g.) airBesselInExpScaled(n, 17*17); the problem was detected because of glitches in the highest blurring levels for scale-space feature detection; but that didn't quite work either; this will have to be debugged further! */ top = 2*(an + AIR_CAST(int, sqrt(40.0*an))); eps = 1.0e-10; for (ii=top; ii > 0; ii--) { bim = bip + ii*tax*bi; bip = bi; bi = bim; if (AIR_ABS(bi) > 1.0/eps) { bb *= eps; bi *= eps; bip*= eps; } if (ii == an) { bb = bip; } } bb *= airBesselI0ExpScaled(xx)/bi; return (xx < 0.0 ? -bb : bb); } /* http://en.wikipedia.org/wiki/Halton_sequences */ double airVanDerCorput(unsigned int indx, unsigned int base) { double result=0.0, ff; unsigned int ii; ff = 1.0/base; ii = indx; while (ii) { result += ff*(ii % base); ii /= base; ff /= base; } return result; } void airHalton(double *out, unsigned int indx, const unsigned int *base, unsigned int num) { unsigned int nn, bb; for (nn=0; nn> 24) ^ (c)]) unsigned int airCRC32(const unsigned char *cdata, size_t len, size_t unit, int swap) { unsigned int crc=0; size_t ii, jj, nn, mm; const unsigned char *crev; if (!(cdata && len)) { return 0; } if (swap) { /* if doing swapping, we need make sure we have a unit size, and that it divides into len */ if (!(unit && !(len % unit))) { return 0; } } nn = len; if (!swap) { /* simple case: feed "len" bytes from "cdata" into CRC32 */ for (ii=0; ii>= 8) { CRC32(crc, (nn & 0xff)); } return ~crc; } teem-1.11.0~svn6057/src/air/air.h0000664000175000017500000011611212165631065016102 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2010 Thomas Schultz Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AIR_HAS_BEEN_INCLUDED #define AIR_HAS_BEEN_INCLUDED #include #include #include #include #include #include #include /* ---- BEGIN non-NrrdIO */ #include /* for ptrdiff_t */ /* ---- END non-NrrdIO */ /* ******** TEEM_VERSION ** ** TEEM_VERSION is a single (decimal) number that will always increase ** monotically, and the _MAJOR, _MINOR, _PATCH are also numbers that ** can be used to implement pre-processor logic about specifc ** versions. The TEEM_VERSION_STRING is used in the (existing) char ** *airTeemVersion (added in version 1.9.0). Yes, keeping these in ** sync is currently a manual operation. ** ** NOTE: Significant API changes (aside from API additions) should NOT ** occur with changes in patch level, only with major or minor version ** changes. ** ** NOTE: ../../CMakeLists.txt's Teem_VERSION variables must be in sync */ #define TEEM_VERSION_MAJOR 1 /* must be 1 digit */ #define TEEM_VERSION_MINOR 11 /* 1 or 2 digits */ #define TEEM_VERSION_PATCH 01 /* 1 or 2 digits */ #define TEEM_VERSION 11101 /* must be 5 digits, to facilitate easy numerical comparison */ #define TEEM_VERSION_STRING "1.11.1" /* cannot be so easily compared */ /* NrrdIO-hack-000 */ #ifdef __cplusplus extern "C" { #endif /* NrrdIO-hack-001 */ #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(air_EXPORTS) || defined(teem_EXPORTS) # define AIR_EXPORT extern __declspec(dllexport) # else # define AIR_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define AIR_EXPORT extern #endif #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) typedef signed __int64 airLLong; typedef unsigned __int64 airULLong; # define AIR_LLONG_FMT "%I64d" # define AIR_ULLONG_FMT "%I64u" # define AIR_LLONG(x) x##i64 # define AIR_ULLONG(x) x##ui64 #else typedef signed long long airLLong; typedef unsigned long long airULLong; # define AIR_LLONG_FMT "%lld" # define AIR_ULLONG_FMT "%llu" # define AIR_LLONG(x) x##ll # define AIR_ULLONG(x) x##ull #endif /* ---- BEGIN non-NrrdIO */ /* This is annoying, thanks to windows */ #define AIR_PI 3.14159265358979323846 #define AIR_E 2.71828182845904523536 /* ---- END non-NrrdIO */ /* ** These serve as conservative estimates on how large various strings ** might end up being. It would be theoretically better to completely ** avoid the use of fixed-size buffers, but in many contexts the ** implementation complexity of handling them reliably is distracts ** from more urgent implementation goals. In the mean time, these can ** be used safely as long as the lengths are used consistently. ** ** The possibly unfortunate convention that has become established in ** Teem is code using these tends to NOT add the "+1", to explicitly ** indicate the space for 0-termination, and instead assumes it is ** part of the numbers below, even though this is at the cost of ** confusion about how the maximal strlen() will be less than each of ** these numbers. This will be addressed in Teem 2.0. */ #define AIR_STRLEN_SMALL (128+1) /* has to be big enough to hold: - printed value of size_t and ptrdiff_t, - line of text that should contain file format "magic" */ #define AIR_STRLEN_MED (256+1) #define AIR_STRLEN_LARGE (512+1) #define AIR_STRLEN_HUGE (1024+1) /* has to be big enough to hold a biff error message (one line of it) */ /* ******** airPtrPtrUnion ** ** union of addresses of pointers to various types, to deal with strict ** aliasing warnings, especially with the first argument to airArrayNew(). ** Unfortunately this can't meet the needs of all such cases because some ** libraries need to manage addresses of arrays of other kinds of ** library-specific objects (about which air is ignorant). */ typedef union { unsigned char **uc; signed char **sc; char **c; char ***cp; unsigned short **us; short **s; unsigned int **ui; int **i; float **f; double **d; void **v; } airPtrPtrUnion; /* ******** airEnum struct ** ** The airEnum provides the basic mechanism of mapping from a ** string to an int enum value, and back. */ typedef struct { const char *name; /* what are these things? */ unsigned int M; /* str[0]: string for the unknown/invalid value; * str[1] .. str[M]: canonical strings for the enum values; * "val" NULL: unknown/invalid = 0; * valid values are 1 .. M * "val" non-NULL: unknown/invalid = val[0]; * valid are val[1].. val[M] */ const char **str; /* see above */ const int *val; /* see above */ const char **desc; /* desc[i] is a short description of the enum values represented by str[i] (thereby starting with the unknown value), to be used to by things like hest */ const char **strEqv; /* If non-NULL, all the variations in strings recognized in mapping from string to value (the values in valEqv). This **MUST** be terminated by a zero-length string ("") so as to signify the end of the list. This should *not* contain the string for unknown/invalid. If "strEqv" is NULL, then mapping from string to value is done only by traversing "str", and "valEqv" is ignored. */ const int *valEqv; /* If strEqv non-NULL, valEqv holds the values corresponding to the strings in strEqv, with one integer for each non-zero-length string in strEqv: strEqv[i] is a valid string representation for value valEqv[i]. This should *not* contain the value for unknown/invalid. This "valEqv" is ignored if "strEqv" is NULL. */ int sense; /* require case matching on strings */ } airEnum; AIR_EXPORT int airEnumUnknown(const airEnum *enm); AIR_EXPORT int airEnumValCheck(const airEnum *enm, int val); AIR_EXPORT const char *airEnumStr(const airEnum *enm, int val); AIR_EXPORT const char *airEnumDesc(const airEnum *enm, int val); AIR_EXPORT int airEnumVal(const airEnum *enm, const char *str); AIR_EXPORT char *airEnumFmtDesc(const airEnum *enm, int val, int canon, const char *fmt); AIR_EXPORT void airEnumPrint(FILE *file, const airEnum *enm); /* ---- BEGIN non-NrrdIO */ AIR_EXPORT int airEnumCheck(char err[AIR_STRLEN_LARGE], const airEnum *enm); /* ---- END non-NrrdIO */ /* ******** airEndian enum ** ** for identifying how a file was written to disk, for those encodings ** where the raw data on disk is dependent on the endianness of the ** architecture. */ enum { airEndianUnknown, /* 0: nobody knows */ airEndianLittle = 1234, /* 1234: Intel and friends */ airEndianBig = 4321, /* 4321: the rest */ airEndianLast }; /* endianAir.c */ AIR_EXPORT const airEnum *const airEndian; AIR_EXPORT int airMyEndian(void); /* array.c: poor-man's dynamically resizable arrays */ typedef struct { void *data, /* where the data is */ **dataP; /* (possibly NULL) address of user's data variable, kept in sync with internal "data" variable */ unsigned int len, /* length of array: # units for which there is considered to be data (which is <= total # units allocated). The # bytes which contain data is len*unit. Always updated (unlike "*lenP") */ *lenP, /* (possibly NULL) address of user's length variable, kept in sync with internal "len" variable */ incr, /* the granularity of the changes in amount of space allocated: when the length reaches a multiple of "incr", then the array is resized */ size; /* array is allocated to have "size" increments, or, size*incr elements, or, size*incr*unit bytes */ size_t unit; /* the size in bytes of one element in the array */ int noReallocWhenSmaller; /* as it says */ /* the following are all callbacks useful for maintaining either an array of pointers (allocCB and freeCB) or array of structs (initCB and doneCB). allocCB or initCB is called when the array length increases, and freeCB or doneCB when it decreases. Any of them can be NULL if no such activity is desired. allocCB sets values in the array (as in storing the return from malloc(); freeCB is called on values in the array (as in calling free()), and the values are cast to void*. allocCB and freeCB don't care about the value of "unit" (though perhaps they should). initCB and doneCB are called on the _addresses_ of elements in the array. allocCB and initCB are called for the elements in ascending order in the array, and freeCB and doneCB are called in descending order. allocCB and initCB are mutually exclusive- they can't both be non-NULL. Same goes for freeCB and doneCB */ void *(*allocCB)(void); /* values of new elements set to return of this */ void *(*freeCB)(void *); /* called on the values of invalidated elements */ void (*initCB)(void *); /* called on addresses of new elements */ void (*doneCB)(void *); /* called on addresses of invalidated elements */ } airArray; AIR_EXPORT airArray *airArrayNew(void **dataP, unsigned int *lenP, size_t unit, unsigned int incr); AIR_EXPORT void airArrayStructCB(airArray *a, void (*initCB)(void *), void (*doneCB)(void *)); AIR_EXPORT void airArrayPointerCB(airArray *a, void *(*allocCB)(void), void *(*freeCB)(void *)); AIR_EXPORT void airArrayLenSet(airArray *a, unsigned int newlen); /* ---- BEGIN non-NrrdIO */ AIR_EXPORT void airArrayLenPreSet(airArray *a, unsigned int newlen); /* ---- END non-NrrdIO */ AIR_EXPORT unsigned int airArrayLenIncr(airArray *a, int delta); AIR_EXPORT airArray *airArrayNix(airArray *a); AIR_EXPORT airArray *airArrayNuke(airArray *a); /* ---- BEGIN non-NrrdIO */ /* heap.c: a (mostly) standard binary min-heap, built on top of airArray. * Additional non-standard functionality includes storing additional * data and addressing elements that are not at the top of the heap */ typedef struct { airArray *key_a; /* where the keys are */ airArray *data_a; /* where the data is */ airArray *idx_a; /* indices */ airArray *invidx_a; /* inverse indices, to access arbitrary elements */ double *key; void *data; unsigned int *idx; unsigned int *invidx; } airHeap; AIR_EXPORT airHeap *airHeapNew(size_t dataUnit, unsigned int incr); AIR_EXPORT airHeap *airHeapFromArray(const airArray *key, const airArray *data); AIR_EXPORT airHeap *airHeapNix(airHeap *h); AIR_EXPORT unsigned int airHeapLength(const airHeap *h); AIR_EXPORT unsigned int airHeapInsert(airHeap *h, double key, const void *data); AIR_EXPORT unsigned int airHeapMerge(airHeap *first, const airHeap *second); AIR_EXPORT double airHeapFrontPeek(const airHeap *h, void *data); AIR_EXPORT double airHeapFrontPop(airHeap *h, void *data); AIR_EXPORT int airHeapFrontUpdate(airHeap *h, double newKey, const void *newData); AIR_EXPORT int airHeapFind(const airHeap *h, unsigned int *ai, const void *data); AIR_EXPORT int airHeapRemove(airHeap *h, unsigned int ai); AIR_EXPORT int airHeapUpdate(airHeap *h, unsigned int ai, double newKey, const void *newData); /* threadAir.c: simplistic wrapper functions for multi-threading */ /* ******** airThreadCapable ** ** if non-zero: we have some kind of multi-threading available, either ** via pthreads, or via Windows stuff */ AIR_EXPORT const int airThreadCapable; /* ******** airThreadNoopWarning ** ** When multi-threading is not available, and hence constructs like ** mutexes are not available, the operations on them will be ** no-ops. When this variable is non-zero, we fprintf(stderr) a ** warning to this effect when those constructs are used */ AIR_EXPORT int airThreadNoopWarning; /* opaque typedefs for OS-specific stuff */ typedef struct _airThread airThread; typedef struct _airThreadMutex airThreadMutex; typedef struct _airThreadCond airThreadCond; typedef struct { unsigned int numUsers, numDone; airThreadMutex *doneMutex; airThreadCond *doneCond; } airThreadBarrier; AIR_EXPORT airThread *airThreadNew(void); AIR_EXPORT int airThreadStart(airThread *thread, void *(*threadBody)(void *), void *arg); AIR_EXPORT int airThreadJoin(airThread *thread, void **retP); AIR_EXPORT airThread *airThreadNix(airThread *thread); AIR_EXPORT airThreadMutex *airThreadMutexNew(void); AIR_EXPORT int airThreadMutexLock(airThreadMutex *mutex); AIR_EXPORT int airThreadMutexUnlock(airThreadMutex *mutex); AIR_EXPORT airThreadMutex *airThreadMutexNix(airThreadMutex *mutex); AIR_EXPORT airThreadCond *airThreadCondNew(void); AIR_EXPORT int airThreadCondWait(airThreadCond *cond, airThreadMutex *mutex); AIR_EXPORT int airThreadCondSignal(airThreadCond *cond); AIR_EXPORT int airThreadCondBroadcast(airThreadCond *cond); AIR_EXPORT airThreadCond *airThreadCondNix(airThreadCond *cond); AIR_EXPORT airThreadBarrier *airThreadBarrierNew(unsigned numUsers); AIR_EXPORT int airThreadBarrierWait(airThreadBarrier *barrier); AIR_EXPORT airThreadBarrier *airThreadBarrierNix(airThreadBarrier *barrier); /* ---- END non-NrrdIO */ /* ******** airFP enum ** ** the different kinds of floating point number afforded by IEEE 754, ** and the values returned by airFPClass_f(). ** ** The values probably won't agree with those in #include's like ** ieee.h, ieeefp.h, fp_class.h. This is because IEEE 754 hasn't ** defined standard values for these, so everyone does it differently. ** ** This enum uses underscores (against Teem convention) to help ** legibility while also conforming to the spirit of the somewhat ** standard naming conventions */ enum { airFP_Unknown, /* 0: nobody knows */ airFP_SNAN, /* 1: signalling NaN */ airFP_QNAN, /* 2: quiet NaN */ airFP_POS_INF, /* 3: positive infinity */ airFP_NEG_INF, /* 4: negative infinity */ airFP_POS_NORM, /* 5: positive normalized non-zero */ airFP_NEG_NORM, /* 6: negative normalized non-zero */ airFP_POS_DENORM, /* 7: positive denormalized non-zero */ airFP_NEG_DENORM, /* 8: negative denormalized non-zero */ airFP_POS_ZERO, /* 9: +0.0, positive zero */ airFP_NEG_ZERO, /* 10: -0.0, negative zero */ airFP_Last /* after the last valid one */ }; /* 754.c: IEEE-754 related stuff values */ typedef union { unsigned int i; float f; } airFloat; typedef union { airULLong i; double d; } airDouble; AIR_EXPORT const int airMyQNaNHiBit; AIR_EXPORT float airFPPartsToVal_f(unsigned int sign, unsigned int expo, unsigned int mant); AIR_EXPORT void airFPValToParts_f(unsigned int *signP, unsigned int *expoP, unsigned int *mantP, float v); AIR_EXPORT double airFPPartsToVal_d(unsigned int sign, unsigned int expo, unsigned int mant0, unsigned int mant1); AIR_EXPORT void airFPValToParts_d(unsigned int *signP, unsigned int *expoP, unsigned int *mant0P, unsigned int *mant1P, double v); AIR_EXPORT float airFPGen_f(int cls); AIR_EXPORT double airFPGen_d(int cls); AIR_EXPORT int airFPClass_f(float val); AIR_EXPORT int airFPClass_d(double val); AIR_EXPORT void airFPFprintf_f(FILE *file, float val); AIR_EXPORT void airFPFprintf_d(FILE *file, double val); AIR_EXPORT const airFloat airFloatQNaN; AIR_EXPORT const airFloat airFloatSNaN; AIR_EXPORT const airFloat airFloatPosInf; AIR_EXPORT const airFloat airFloatNegInf; AIR_EXPORT float airNaN(void); AIR_EXPORT int airIsNaN(double d); AIR_EXPORT int airIsInf_f(float f); AIR_EXPORT int airIsInf_d(double d); AIR_EXPORT int airExists(double d); /* ---- BEGIN non-NrrdIO */ #define AIR_RANDMT_N 624 typedef struct { /* These need to be at least 32 bits */ unsigned int state[AIR_RANDMT_N], /* internal state */ *pNext, /* next value to get from state */ left; /* number of values left before reload needed */ } airRandMTState; /* randMT.c */ AIR_EXPORT airRandMTState *airRandMTStateGlobal; AIR_EXPORT void airRandMTStateGlobalInit(void); AIR_EXPORT airRandMTState *airRandMTStateNew(unsigned int seed); AIR_EXPORT airRandMTState *airRandMTStateNix(airRandMTState *state); AIR_EXPORT void airSrandMT_r(airRandMTState *state, unsigned int seed); AIR_EXPORT double airDrandMT_r(airRandMTState *state); /* [0,1] */ AIR_EXPORT unsigned int airUIrandMT_r(airRandMTState *state); AIR_EXPORT double airDrandMT53_r(airRandMTState *state); /* [0,1) */ AIR_EXPORT unsigned int airRandInt(unsigned int N); AIR_EXPORT unsigned int airRandInt_r(airRandMTState *state, unsigned int N); AIR_EXPORT void airSrandMT(unsigned int seed); AIR_EXPORT double airDrandMT(void); AIR_EXPORT int airRandMTSanity(void); /* ---- END non-NrrdIO */ /* ******** airType ** ** Different types which air cares about. ** Currently only used in the command-line parsing, but perhaps will ** be used elsewhere in air later */ enum { airTypeUnknown, /* 0 */ airTypeBool, /* 1 */ airTypeInt, /* 2 */ airTypeUInt, /* 3 */ airTypeLongInt, /* 4 */ airTypeULongInt, /* 5 */ airTypeSize_t, /* 6 */ airTypeFloat, /* 7 */ airTypeDouble, /* 8 */ airTypeChar, /* 9 */ airTypeString, /* 10 */ airTypeEnum, /* 11 */ airTypeOther, /* 12 */ airTypeLast }; #define AIR_TYPE_MAX 12 /* parseAir.c */ AIR_EXPORT double airAtod(const char *str); AIR_EXPORT int airSingleSscanf(const char *str, const char *fmt, void *ptr); AIR_EXPORT const airEnum *const airBool; AIR_EXPORT unsigned int airParseStrB(int *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); AIR_EXPORT unsigned int airParseStrI(int *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); AIR_EXPORT unsigned int airParseStrUI(unsigned int *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); AIR_EXPORT unsigned int airParseStrZ(size_t *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); AIR_EXPORT unsigned int airParseStrF(float *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); AIR_EXPORT unsigned int airParseStrD(double *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); AIR_EXPORT unsigned int airParseStrC(char *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); AIR_EXPORT unsigned int airParseStrS(char **out, const char *s, const char *ct, unsigned int n, ... /* REQ'D even if n>1: int greedy */); AIR_EXPORT unsigned int airParseStrE(int *out, const char *s, const char *ct, unsigned int n, ... /* REQUIRED: airEnum *e */); AIR_EXPORT unsigned int (*airParseStr[AIR_TYPE_MAX+1])(void *, const char *, const char *, unsigned int, ...); /* string.c */ AIR_EXPORT char *airStrdup(const char *s); AIR_EXPORT size_t airStrlen(const char *s); /* ---- BEGIN non-NrrdIO */ AIR_EXPORT int airStrcmp(const char *s1, const char *s2); /* ---- END non-NrrdIO */ AIR_EXPORT int airStrtokQuoting; AIR_EXPORT char *airStrtok(char *s, const char *ct, char **last); AIR_EXPORT unsigned int airStrntok(const char *s, const char *ct); AIR_EXPORT char *airStrtrans(char *s, char from, char to); AIR_EXPORT char *airStrcpy(char *dst, size_t dstSize, const char *src); AIR_EXPORT int airEndsWith(const char *s, const char *suff); AIR_EXPORT char *airUnescape(char *s); AIR_EXPORT char *airOneLinify(char *s); AIR_EXPORT char *airToLower(char *str); AIR_EXPORT char *airToUpper(char *str); AIR_EXPORT unsigned int airOneLine(FILE *file, char *line, unsigned int size); /* sane.c */ /* ******** airInsane enum ** ** reasons for why airSanity() failed (specifically, the possible ** return values for airSanity() */ enum { airInsane_not, /* 0: actually, all sanity checks passed */ airInsane_endian, /* 1: airMyEndian is wrong */ airInsane_pInfExists, /* 2: AIR_EXISTS(positive infinity) was true */ airInsane_nInfExists, /* 3: AIR_EXISTS(negative infinity) was true */ airInsane_NaNExists, /* 4: AIR_EXISTS(NaN) was true */ airInsane_FltDblFPClass, /* 5: double -> float assignment messed up the airFPClass_f() of the value */ airInsane_QNaNHiBit, /* 6: airMyQNaNHiBit is wrong */ airInsane_AIR_NAN, /* 7: airFPClass_f(AIR_QNAN) wrong (no longer checking on problematic SNAN) */ airInsane_dio, /* 8: airMyDio set to something invalid */ airInsane_UCSize, /* 9: unsigned char isn't 8 bits */ airInsane_FISize, /* 10: sizeof(float), sizeof(int) not 4 */ airInsane_DLSize /* 11: sizeof(double), sizeof(airLLong) not 8 */ }; #define AIR_INSANE_MAX 11 AIR_EXPORT const char *airInsaneErr(int insane); AIR_EXPORT int airSanity(void); /* miscAir.c */ AIR_EXPORT const char *airTeemVersion; AIR_EXPORT const char *airTeemReleaseDate; AIR_EXPORT void *airNull(void); AIR_EXPORT void *airSetNull(void **ptrP); AIR_EXPORT void *airFree(void *ptr); AIR_EXPORT FILE *airFopen(const char *name, FILE *std, const char *mode); AIR_EXPORT FILE *airFclose(FILE *file); AIR_EXPORT int airSinglePrintf(FILE *file, char *str, const char *fmt, ...); AIR_EXPORT char *airSprintSize_t(char str[AIR_STRLEN_SMALL], size_t val); /* ---- BEGIN non-NrrdIO */ AIR_EXPORT char *airSprintVecSize_t(char *str, const size_t *vec, unsigned int len); AIR_EXPORT char *airPrettySprintSize_t(char str[AIR_STRLEN_SMALL], size_t v); AIR_EXPORT char *airSprintPtrdiff_t(char str[AIR_STRLEN_SMALL], ptrdiff_t v); AIR_EXPORT const int airPresent; AIR_EXPORT FILE *airStderr(void); AIR_EXPORT FILE *airStdout(void); AIR_EXPORT FILE *airStdin(void); AIR_EXPORT unsigned int airIndex(double min, double val, double max, unsigned int N); AIR_EXPORT unsigned int airIndexClamp(double min, double val, double max, unsigned int N); AIR_EXPORT airULLong airIndexULL(double min, double val, double max, airULLong N); AIR_EXPORT airULLong airIndexClampULL(double min, double val, double max, airULLong N); AIR_EXPORT char *airDoneStr(double start, double here, double end, char *str); AIR_EXPORT double airTime(void); AIR_EXPORT const char airTypeStr[AIR_TYPE_MAX+1][AIR_STRLEN_SMALL]; AIR_EXPORT const size_t airTypeSize[AIR_TYPE_MAX+1]; AIR_EXPORT void airEqvAdd(airArray *eqvArr, unsigned int j, unsigned int k); AIR_EXPORT unsigned int airEqvMap(airArray *eqvArr, unsigned int *map, unsigned int len); AIR_EXPORT unsigned int airEqvSettle(unsigned int *map, unsigned int len); /* math.c */ AIR_EXPORT double airFastExp(double val); AIR_EXPORT double airExp(double val); AIR_EXPORT void airNormalRand(double *z1, double *z2); AIR_EXPORT void airNormalRand_r(double *z1, double *z2, airRandMTState *state); AIR_EXPORT void airShuffle(unsigned int *buff, unsigned int N, int perm); AIR_EXPORT void airShuffle_r(airRandMTState *state, unsigned int *buff, unsigned int N, int perm); AIR_EXPORT double airCbrt(double); AIR_EXPORT double airMode3(double a, double b, double c); AIR_EXPORT double airMode3_d(const double v[3]); AIR_EXPORT double airSgnPow(double, double); AIR_EXPORT double airFlippedSgnPow(double, double); AIR_EXPORT double airIntPow(double v, int p); AIR_EXPORT int airSgn(double); AIR_EXPORT int airLog2(size_t n); AIR_EXPORT double airErfc(double x); AIR_EXPORT double airErf(double x); AIR_EXPORT double airGaussian(double x, double mean, double stdv); AIR_EXPORT double airBesselI0(double x); AIR_EXPORT double airBesselI1(double x); AIR_EXPORT double airBesselI0ExpScaled(double x); AIR_EXPORT double airBesselI1ExpScaled(double x); AIR_EXPORT double airLogBesselI0(double x); AIR_EXPORT double airLogRician(double mes, double tru, double sig); AIR_EXPORT double airRician(double mes, double tru, double sig); AIR_EXPORT double airBesselI1By0(double x); AIR_EXPORT double airBesselIn(int n, double x); AIR_EXPORT double airBesselInExpScaled(int n, double x); AIR_EXPORT double airVanDerCorput(unsigned int indx, unsigned int base); AIR_EXPORT void airHalton(double *out, unsigned int indx, const unsigned int *base, unsigned int num); #define AIR_PRIME_NUM 1000 AIR_EXPORT const unsigned int airPrimeList[AIR_PRIME_NUM]; AIR_EXPORT unsigned int airCRC32(const unsigned char *data, size_t len, size_t unit, int swap); /* ---- END non-NrrdIO */ /* dio.c */ /* ******** airNoDio enum ** ** reasons for why direct I/O won't be used with a particular ** file/pointer combination */ enum { airNoDio_okay, /* 0: actually, you CAN do direct I/O */ airNoDio_arch, /* 1: Teem thinks this architecture can't do it */ airNoDio_format, /* 2: Teem thinks given data file format can't use it */ airNoDio_std, /* 3: DIO isn't possible for std{in|out|err} */ airNoDio_fd, /* 4: couldn't get underlying file descriptor */ airNoDio_dioinfo, /* 5: calling fcntl() to get direct I/O info failed */ airNoDio_small, /* 6: requested size is too small */ airNoDio_size, /* 7: requested size not a multiple of d_miniosz */ airNoDio_ptr, /* 8: pointer not multiple of d_mem */ airNoDio_fpos, /* 9: current file position not multiple of d_miniosz */ airNoDio_setfl, /* 10: fcntl(fd, SETFL, FDIRECT) failed */ airNoDio_test, /* 11: couldn't memalign() even a small bit of memory */ airNoDio_disable /* 12: someone disabled it with airDisableDio */ }; #define AIR_NODIO_MAX 12 AIR_EXPORT const char *airNoDioErr(int noDio); AIR_EXPORT const int airMyDio; AIR_EXPORT int airDisableDio; AIR_EXPORT void airDioInfo(int *align, int *min, int *max, int fd); AIR_EXPORT int airDioTest(int fd, const void *ptr, size_t size); AIR_EXPORT void *airDioMalloc(size_t size, int fd); AIR_EXPORT size_t airDioRead(int fd, void *ptr, size_t size); AIR_EXPORT size_t airDioWrite(int fd, const void *ptr, size_t size); /* mop.c: clean-up utilities */ enum { airMopNever, airMopOnError, airMopOnOkay, airMopAlways }; typedef void *(*airMopper)(void *); typedef struct { void *ptr; /* the thing to be processed */ airMopper mop; /* the function to which does the processing */ int when; /* from the airMopWhen enum */ } airMop; AIR_EXPORT airArray *airMopNew(void); AIR_EXPORT int airMopAdd(airArray *arr, void *ptr, airMopper mop, int when); AIR_EXPORT void airMopSub(airArray *arr, void *ptr, airMopper mop); AIR_EXPORT void airMopMem(airArray *arr, void *_ptrP, int when); AIR_EXPORT void airMopUnMem(airArray *arr, void *_ptrP); AIR_EXPORT void airMopPrint(airArray *arr, const void *_str, int when); AIR_EXPORT void airMopDone(airArray *arr, int error); AIR_EXPORT void airMopError(airArray *arr); AIR_EXPORT void airMopOkay(airArray *arr); AIR_EXPORT void airMopDebug(airArray *arr); /* ---- BEGIN non-NrrdIO */ AIR_EXPORT void airMopSingleDone(airArray *arr, void *ptr, int error); AIR_EXPORT void airMopSingleError(airArray *arr, void *ptr); AIR_EXPORT void airMopSingleOkay(airArray *arr, void *ptr); /* ---- END non-NrrdIO */ /******* the interminable sea of defines and macros *******/ #define AIR_TRUE 1 #define AIR_FALSE 0 #define AIR_WHITESPACE " \t\n\r\v\f" /* K+R pg. 157 */ /* ******** AIR_UNUSED ** ** one way of reconciling "warning: unused parameter" with ** C's "error: parameter name omitted" */ #define AIR_UNUSED(x) (void)(x) /* ******** AIR_CAST, AIR_UINT, AIR_INT ** ** just casts, but with the added ability to grep for them more easily, ** since casts should probably always be revisited and reconsidered. */ #define AIR_CAST(t, v) ((t)(v)) #define AIR_UINT(x) AIR_CAST(unsigned int, x) #define AIR_INT(x) AIR_CAST(int, x) /* ******** AIR_VOIDP, AIR_CVOIDP ** ** explicit casting to "void *" (and "const void *") from non-void* pointers ** is strictly speaking needed for the %p format specifier in printf-like ** functions; this is a slightly more convenient form */ #define AIR_VOIDP(x) AIR_CAST(void *, x) #define AIR_CVOIDP(x) AIR_CAST(const void *, x) /* ******** AIR_MALLOC, AIR_CALLOC ** ** slightly simpler wrapper around cast and malloc/calloc ** ** HEY note that "T" is not guarded by parentheses in its first usage, ** as arguments in Teem macros normally are */ #define AIR_MALLOC(N, T) (T*)(malloc((N)*sizeof(T))) #define AIR_CALLOC(N, T) (T*)(calloc((N), sizeof(T))) /* ******** AIR_ENDIAN, AIR_QNANHIBIT, AIR_DIO ** ** These reflect particulars of hardware which we're running on. The ** difference from the things starting with TEEM_ is that the TEEM_ ** values are for passing architecture-specific to compilation of source ** files, and thes AIR_ variables are for advertising that information ** to anyone linking against air (or Teem) and including air.h. */ #define AIR_ENDIAN (airMyEndian()) #define AIR_QNANHIBIT (airMyQNaNHiBit) #define AIR_DIO (airMyDio) /* ******** AIR_NAN, AIR_QNAN, AIR_SNAN, AIR_POS_INF, AIR_NEG_INF ** ** its nice to have these values available without the cost of a ** function call. ** ** NOTE: AIR_POS_INF and AIR_NEG_INF correspond to the _unique_ ** bit-patterns which signify positive and negative infinity. With ** the NaNs, however, they are only one of many possible ** representations. */ #define AIR_NAN (airFloatQNaN.f) #define AIR_QNAN (airFloatQNaN.f) #define AIR_SNAN (airFloatSNaN.f) #define AIR_POS_INF (airFloatPosInf.f) #define AIR_NEG_INF (airFloatNegInf.f) /* ******** AIR_EXISTS ** ** is non-zero (true) only for values which are not NaN or +/-infinity ** ** You'd think that (x == x) might work, but no no no, some optimizing ** compilers (e.g. SGI's cc) say "well of course they're equal, for all ** possible values". Bastards! ** ** One of the benefits of IEEE 754 floating point numbers is that ** gradual underflow means that x = y <==> x - y = 0 for any (positive ** or negative) normalized or denormalized float. Otherwise this ** macro could not be valid; some floating point conventions say that ** a zero-valued exponent means zero, regardless of the mantissa. ** ** However, there MAY be problems on machines which use extended ** (80-bit) floating point registers, such as Intel chips- where the ** same initial value 1) directly read from the register, versus 2) ** saved to memory and loaded back, may end up being different. I ** have yet to produce this behavior, or convince myself it can't happen. ** ** The reason to #define AIR_EXISTS as airExists is that on some ** optimizing compilers, the !((x) - (x)) doesn't work. This has been ** the case on Windows and 64-bit irix6 (64 bit) with -Ofast. If ** airSanity fails because a special value "exists", then use the ** first version of AIR_EXISTS. ** ** There is a performance consequence of using airExists(x), in that it ** is a function call, although (HEY) we should facilitate inline'ing it ** for compilers that know how to. ** ** gcc 4.5.3 -std=c89, at least on cygwin, has problems with ** the type of "!((x) - (x))" when used with bit-wise xor ^, saying ** "invalid operands to binary ^ (have ‘int’ and ‘int’)" but these ** problems oddly went away with the explicit cast to int. */ #if defined(_WIN32) || defined(__ECC) /* NrrdIO-hack-002 */ #define AIR_EXISTS(x) (airExists(x)) #else #define AIR_EXISTS(x) (AIR_CAST(int, !((x) - (x)))) #endif /* ---- BEGIN non-NrrdIO */ /* ******** AIR_EXISTS_F(x) ** ** This is another way to check for non-specialness (not NaN, not ** +inf, not -inf) of a _float_, by making sure the exponent field ** isn't all ones. ** ** Unlike !((x) - (x)) or airExists(x), the argument to this macro ** MUST MUST MUST be a float, and the float must be of the standard ** 32-bit size, which must also be the size of an int. The reason for ** this constraint is that macros are not functions, so there is no ** implicit cast or conversion to a single type. Casting the address ** of the macro arg to an int* only works when the arg has the same ** size as an int. ** ** No cross-platform comparative timings have been done to compare the ** speed of !((x) - (x)) versus airExists() versus AIR_EXISTS_F() ** ** This macro is endian-safe. */ #define AIR_EXISTS_F(x) ((*(unsigned int*)&(x) & 0x7f800000) != 0x7f800000) /* ******** AIR_EXISTS_D(x) ** ** like AIR_EXISTS_F(), but the argument here MUST be a double */ #define AIR_EXISTS_D(x) ( \ (*(airULLong*)&(x) & AIR_ULLONG(0x7ff0000000000000)) \ != AIR_ULLONG(0x7ff0000000000000)) /* ******** AIR_ISNAN_F(x) ** ** detects if a float is NaN by looking at the bits, without relying on ** any of its arithmetic properties. As with AIR_EXISTS_F(), this only ** works when the argument really is a float, and when floats are 4-bytes */ #define AIR_ISNAN_F(x) (((*(unsigned int*)&(x) & 0x7f800000)==0x7f800000) && \ (*(unsigned int*)&(x) & 0x007fffff)) /* ---- END non-NrrdIO */ /* ******** AIR_MAX(a,b), AIR_MIN(a,b), AIR_ABS(a) ** ** the usual */ #define AIR_MAX(a,b) ((a) > (b) ? (a) : (b)) #define AIR_MIN(a,b) ((a) < (b) ? (a) : (b)) #define AIR_ABS(a) ((a) > 0.0f ? (a) : -(a)) /* ******** AIR_COMPARE(a,b) ** ** the sort of compare that qsort() wants for ascending sort */ #define AIR_COMPARE(a,b) ((a) < (b) \ ? -1 \ : ((a) > (b) \ ? 1 \ : 0)) /* ******** AIR_IN_OP(a,b,c), AIR_IN_CL(a,b,c) ** ** is true if the middle argument is in the open/closed interval ** defined by the first and third arguments ** ** AIR_IN_OP is new name for old AIR_BETWEEN ** AIR_IN_CL is new name for old AIR_INSIDE */ #define AIR_IN_OP(a,b,c) ((a) < (b) && (b) < (c)) /* closed interval */ #define AIR_IN_CL(a,b,c) ((a) <= (b) && (b) <= (c)) /* open interval */ /* ******** AIR_CLAMP(a,b,c) ** ** returns the middle argument, after being clamped to the closed ** interval defined by the first and third arguments */ #define AIR_CLAMP(a,b,c) ((b) < (a) \ ? (a) \ : ((b) > (c) \ ? (c) \ : (b))) /* ******** AIR_MOD(i, N) ** ** returns that integer in [0, N-1] which is i plus a multiple of N. It ** may be unfortunate that the expression (i)%(N) appears three times; ** this should be inlined. Or perhaps the compiler's optimizations ** (common sub-expression elimination) will save us. ** ** Note: integer divisions are not very fast on some modern chips; ** don't go silly using this one. */ #define AIR_MOD(i, N) ((i)%(N) >= 0 ? (i)%(N) : N + (i)%(N)) /* ******** AIR_LERP(w, a, b) ** ** returns a when w=0, and b when w=1, and linearly varies in between */ #define AIR_LERP(w, a, b) ((w)*((b) - (a)) + (a)) /* ******** AIR_AFFINE(i,x,I,o,O) ** ** given intervals [i,I], [o,O] and a value x which may or may not be ** inside [i,I], return the value y such that y stands in the same ** relationship to [o,O] that x does with [i,I]. Or: ** ** y - o x - i ** ------- = ------- ** O - o I - i ** ** It is the callers responsibility to make sure I-i and O-o are ** both non-zero. Strictly speaking, real problems arise only when ** when I-i is zero: division by zero generates either NaN or infinity ** ** NOTE that "x" is evaluated only once (which makes this more useful), ** as is "I" and "O" (usually not so important); "i" and "o" are each ** evaluated twice */ #define AIR_AFFINE(i,x,I,o,O) ( \ ((double)(O)-(o))*((double)(x)-(i)) / ((double)(I)-(i)) + (o)) /* ******** AIR_DELTA(i,x,I,o,O) ** ** given intervals [i,I] and [o,O], calculates the number y such that ** a change of x within [i,I] is proportional to a change of y within ** [o,O]. Or: ** ** y x ** ------- = ------- ** O - o I - i ** ** It is the callers responsibility to make sure I-i and O-o are ** both non-zero ** ** NOTE that all arguments are evaluated only once */ #define AIR_DELTA(i,x,I,o,O) ( \ ((double)(O)-(o))*((double)(x)) / ((double)(I)-(i)) ) /* ******** AIR_ROUNDUP, AIR_ROUNDDOWN ** ** rounds integers up or down; just wrappers around floor and ceil */ #define AIR_ROUNDUP(x) ((int)(floor((x)+0.5))) #define AIR_ROUNDDOWN(x) ((int)(ceil((x)-0.5))) #define AIR_ROUNDUP_UI(x) ((unsigned int)(floor((x)+0.5))) #define AIR_ROUNDDOWN_UI(x) ((unsigned int)(ceil((x)-0.5))) #ifdef __cplusplus } #endif #endif /* AIR_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/air/endianAir.c0000664000175000017500000000451112165631065017213 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" /* ******** airMyEndian() ** ** determine at run-time if we are little (1234) or big (4321) endian */ int airMyEndian(void) { int tmpI, ret; char leastbyte; /* set int to 1: least signficant byte will be 1, most signficant byte will be 0 */ tmpI = 1; /* cast address of (4-byte) int to char*, and dereference, which retrieves the byte at the low-address-end of int (the "first" byte in memory ordering). On big endian, we're getting the most significant byte (0); on little endian, we're getting least significant byte (1) */ leastbyte = *(AIR_CAST(char*, &tmpI)); if (leastbyte) { ret = airEndianLittle; } else { ret = airEndianBig; } return ret; } static const char * _airEndianStr[] = { "(unknown endian)", "little", "big" }; static const char * _airEndianDesc[] = { "unknown endianness", "Intel and compatible", "Everyone besides Intel and compatible" }; static const int _airEndianVal[] = { airEndianUnknown, airEndianLittle, airEndianBig, }; static const airEnum _airEndian = { "endian", 2, _airEndianStr, _airEndianVal, _airEndianDesc, NULL, NULL, AIR_FALSE }; const airEnum *const airEndian = &_airEndian; teem-1.11.0~svn6057/src/air/array.c0000664000175000017500000002242212165631065016440 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" static void _airLenSet(airArray *a, unsigned int len) { a->len = len; /* printf(" HEY: len = %d\n", a->len); */ if (a->lenP) { *(a->lenP) = len; /* printf(" HEY: *(a->lenP) = *(%lu) = %d\n", (unsigned long)a->lenP, *(a->lenP)); */ } } static void _airSetData(airArray *a, void *data) { a->data = data; if (a->dataP) { *(a->dataP) = data; } } /* ******** airArrayNew() ** ** creates a new airArray struct and returns a pointer to it. ** dataP is a pointer to the user's data pointer ** lenP is a pointer to the user's array length variable (optional) ** unit is the size (in bytes) of one element in the array ** incr is the number of units by which the array will grow or shrink ** ** returns NULL on error, or the new airArray pointer if okay ** errors: bogus arguments, or couldn't alloc airArray struct ** ** --> The user CAN NOT change the pointer variable (of which *dataP ** is the address) after this is called, or else everything will ** get all bolloxed up. The same goes for the array length ** variable, if its address is passed- though in that case the ** correct value will over-write any other. */ airArray * airArrayNew(void **dataP, unsigned int *lenP, size_t unit, unsigned int incr) { airArray *a; if (unit<=0 || incr<=0) { return NULL; } a = AIR_CALLOC(1, airArray); if (!a) { return NULL; } a->dataP = dataP; _airSetData(a, NULL); a->lenP = lenP; _airLenSet(a, 0); a->incr = incr; a->unit = unit; a->noReallocWhenSmaller = AIR_FALSE; a->allocCB = NULL; a->freeCB = NULL; a->initCB = NULL; a->doneCB = NULL; return a; } /* ******** airArrayStructCB() ** ** set callbacks to maintain array of structs */ void airArrayStructCB(airArray *a, void (*initCB)(void *), void (*doneCB)(void *)) { if (a) { a->initCB = initCB; a->doneCB = doneCB; a->allocCB = NULL; a->freeCB = NULL; } } /* ******** airArrayPointerCB() ** ** set callbacks to maintain array of pointers */ void airArrayPointerCB(airArray *a, void *(*allocCB)(void), void *(*freeCB)(void *)) { if (a) { a->initCB = NULL; a->doneCB = NULL; a->allocCB = allocCB; a->freeCB = freeCB; } } /* ---- BEGIN non-NrrdIO */ /* ******** airArrayLenPreSet() ** ** allocates the array to hold up to given length, without ** actually changing the length. In order for this to be ** useful, this also turns on noReallocWhenSmaller ** ** NB: this used to have a "boolean" return to indicate allocation ** error, but nothing in Teem actually did the error checking. Now ** conscientious users can look at NULL-ity of a->data to detect such ** an error. */ void airArrayLenPreSet(airArray *a, unsigned int newlen) { /* char me[]="airArrayLenPreSet"; */ unsigned int newsize; void *newdata; if (!a) { return; } if (newlen == 0) { /* there is no pre-set length, turn off noReallocWhenSmaller */ a->noReallocWhenSmaller = AIR_FALSE; } else { newsize = (newlen-1)/a->incr + 1; /* fprintf(stderr, "!%s: newlen = %u, incr = %u -> newsize = %u\n", me, newlen, a->incr, newsize); fprintf(stderr, "!%s: a->size = %u, a->len = %u, a->unit = %u\n", me, a->size, a->len, a->unit); */ if (newsize > a->size) { newdata = calloc(newsize*a->incr, a->unit); /* fprintf(stderr, "!%s: a->data = %p, newdata = %p\n", me, a->data, newdata); */ if (!newdata) { free(a->data); _airSetData(a, NULL); return; } if (a->data) { memcpy(newdata, a->data, AIR_MIN(a->len*a->unit, newsize*a->incr*a->unit)); free(a->data); } _airSetData(a, newdata); a->size = newsize; } a->noReallocWhenSmaller = AIR_TRUE; } /* fprintf(stderr, "!%s: returning data %p\n", me, a->data); */ return; } /* ---- END non-NrrdIO */ /* ******** airArrayLenSet() ** ** Set the length of the array, allocating or freeing as needed ** ** returns 1 on error, otherwise 0 if okay ** possible errors: bogus arguments, or couldn't allocate new memory segment ** ** In case we can't allocate the new space, the old space is left untouched, ** however if the new length is smaller, the free/done callbacks will ** have been called on invalidated elements ** ** NB: this used to have a "boolean" return to indicate allocation ** error, but almost nothing in Teem actually did the error checking. ** Now conscientious users can look at NULL-ity of a->data to detect ** such an error. */ void airArrayLenSet(airArray *a, unsigned int newlen) { /* char me[]="airArrayLenSet"; */ unsigned int ii, newsize; void *addr, *newdata; if (!a) { /* user is a moron, what can you do */ return; } if (newlen == a->len) { /* nothing to do */ return; } /* call freeCB/doneCB on all the elements which are going bye-bye */ /* Wed Sep 12 14:40:45 EDT 2007: the order in which these called is now ascending, instead of descending (as was the way before) */ if (newlen < a->len && (a->freeCB || a->doneCB)) { for (ii=newlen; iilen; ii++) { addr = (char*)(a->data) + ii*a->unit; if (a->freeCB) { (a->freeCB)(*((void**)addr)); } else { (a->doneCB)(addr); } } } newsize = newlen ? (newlen-1)/a->incr + 1 : 0; if (newsize != a->size) { /* we have to change the size of the array */ if (newsize) { /* array should be bigger or smaller, but not zero-length */ if (newsize > a->size || (newsize < a->size && !(a->noReallocWhenSmaller)) ) { newdata = calloc(newsize*a->incr, a->unit); if (!newdata) { free(a->data); _airSetData(a, NULL); return; } memcpy(newdata, a->data, AIR_MIN(a->len*a->unit, newsize*a->incr*a->unit)); free(a->data); _airSetData(a, newdata); a->size = newsize; } } else { /* array should be zero-length */ free(a->data); _airSetData(a, NULL); a->size = newsize; } } /* else new size is still within current allocated length, and neither "size" nor "data" need to change */ /* call allocCB/initCB on newly created elements */ if (newlen > a->len && (a->allocCB || a->initCB)) { for (ii=a->len; iidata) + ii*a->unit; if (a->allocCB) { *((void**)addr) = (a->allocCB)(); } else { (a->initCB)(addr); } } } _airLenSet(a, newlen); return; } /* ******** airArrayLenIncr() ** ** Like airArrayLenSet, but works with an increment instead of an ** absolute length. Return value is different: ** got NULL: return 0 ** allocation error: return 0, and a->data set to NULL ** no error, delta > 0: return index of 1st element in newly allocated ** segment (a->len before length was increased) ** no error, delta <= 0: return 0, and a->data unchanged ** ** HEY: it is apparently not clear how to do error checking (aside from ** looking at a->data) when there was NO data previously allocated, and the ** first index of the newly allocated data is zero. */ unsigned int airArrayLenIncr(airArray *a, int delta) { /* char me[]="airArrayLenIncr"; */ unsigned int oldlen, ret, negdel; if (!a) { return 0; } negdel = (delta < 0 ? AIR_UINT(-delta) : 0); if (delta < 0 && negdel > a->len) { /* error: asked for newlength to be negative */ airArrayLenSet(a, 0); return 0; } oldlen = a->len; airArrayLenSet(a, (delta >= 0 ? oldlen + AIR_UINT(delta) : oldlen - negdel)); if (!a->data) { /* allocation error */ ret = 0; } else { ret = (delta <= 0 ? 0 : oldlen); } return ret; } /* ******** airArrayNuke() ** ** free both the memory pointed to by the struct and the struct itself */ airArray * airArrayNuke(airArray *a) { if (a) { airArrayLenSet(a, 0); free(a); } return NULL; } /* ******** airArrayNix() ** ** frees just the struct, leaving the memory it points to untouched */ airArray * airArrayNix(airArray *a) { if (a) { free(a); } return NULL; } teem-1.11.0~svn6057/src/air/test/0000775000175000017500000000000012203513755016131 5ustar domibeldomibelteem-1.11.0~svn6057/src/air/test/tmop.c0000664000175000017500000000552312165631065017263 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" void * print(void *_iP) { int *iP; iP = (int *)_iP; printf("%d\n", *iP); return NULL; } int main(int argc, char *argv[]) { void *ptr; int i = 111, j = 222, k = 333, l = 444; airArray *mop; char *str, *me; AIR_UNUSED(argc); me = argv[0]; printf("%s: -------------------------------------\n", me); mop = airMopNew(); str = airStrdup("this is just a test"); printf("%s: str = \"%s\", str = 0x%p, &str = 0x%p\n", me, str, str, AIR_CAST(void*, &str)); airMopMem(mop, &str, airMopAlways); airMopDebug(mop); airMopOkay(mop); printf("%s: -------------------------------------\n", me); mop = airMopNew(); ptr = calloc(1024, sizeof(char)); airMopMem(mop, &ptr, airMopAlways); airMopAdd(mop, &i, print, airMopNever); airMopAdd(mop, &j, print, airMopOnError); airMopAdd(mop, &k, print, airMopOnOkay); airMopAdd(mop, &l, print, airMopAlways); airMopPrint(mop, "this is a joke", airMopOnError); airMopDebug(mop); airMopError(mop); printf("%s: -------------------------------------\n", me); mop = airMopNew(); ptr = calloc(1024, sizeof(char)); airMopMem(mop, &ptr, airMopAlways); airMopAdd(mop, &i, print, airMopNever); airMopAdd(mop, &j, print, airMopOnError); airMopAdd(mop, &k, print, airMopOnOkay); airMopPrint(mop, "this is a joke", airMopOnError); airMopAdd(mop, &l, print, airMopAlways); airMopDebug(mop); airMopOkay(mop); printf("%s: -------------------------------------\n", me); mop = airMopNew(); ptr = calloc(1024, sizeof(char)); airMopMem(mop, &ptr, airMopAlways); free(ptr); airMopUnMem(mop, &ptr); airMopDebug(mop); airMopOkay(mop); printf("%s: -------------------------------------\n", me); exit(0); } teem-1.11.0~svn6057/src/air/test/tarr.c0000664000175000017500000001165712165631065017261 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" int main(int argc, char *argv[]) { char *me, *fname, *incrS; airArray *mop, *dataArr; FILE *file; unsigned int incr, numRed; unsigned char *data; int datum; /* must be int, so it can store EOF */ airPtrPtrUnion appu; me = argv[0]; if (3 != argc) { /* 0 1 2 (3) */ fprintf(stderr, "usage: %s \n", me); return 1; } fname = argv[1]; incrS = argv[2]; /* the "mop" is for management of dynamically allocated resources cleanly in combination with error handling, its not important for understanding how airArrays work (although as you can tell from the declaration, the "mop" is built on an airArray) */ mop = airMopNew(); if (!(file = fopen(fname, "rb"))) { fprintf(stderr, "%s: couldn't open %s for reading\n", me, fname); airMopError(mop); return 1; } airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); if (1 != sscanf(incrS, "%ud", &incr)) { fprintf(stderr, "%s: couln't parse incr \"%s\" as unsigned int\n", me, incrS); airMopError(mop); return 1; } /* now "file" is the open file that we read from, and "incr is the size increment (the granularity) for re-allocating the data */ /* the arguments here are as follows: 1) &data: the address of the array (itself a pointer) into which we'll copy the data. Whenever the airArray re-allocates the array, it will update the value of the array variable to the new location. So, while it seems a little weird at first, the value of the "data" variable can change as a side-effect of calling the airArray functions. 2) NULL: we could pass the address of a variable to record the current allocated length of the array, and this might be useful, but isn't necessary 3) sizeof(unsigned char): this is the size of the individual elements that we are saving in the array. Because memory is allocated and addressed at the level of individual bytes (and files can be read one byte at-a-time), we manage the buffer as an array of unsigned chars. 4) incr: when the array length is a multiple of incr, the memory segment is re-allocated, so this determines how often the re-allocation happens (we want it to happen fairly infrequently) */ /* dataArr = airArrayNew(&data, NULL, sizeof(unsigned char), incr); */ /* but wait: to play well with type checking, we have to use a stupid union to pass in the address of the array. So, appu.v == &data, but the types are right. We don't do a cast because recent versions of gcc will complain about breaking "strict-aliasing rules". */ appu.uc = &data; dataArr = airArrayNew(appu.v, NULL, sizeof(unsigned char), incr); if (!dataArr) { fprintf(stderr, "%s: couldn't allocate airArray\n", me); airMopError(mop); return 1; } /* numRed will keep track of the number of bytes that have been successfully read from the file AND stored in data[] */ numRed = 0; /* try to get the first byte of data */ datum = fgetc(file); if (EOF == datum) { fprintf(stderr, "%s: hit EOF trying to get first byte\n", me); airMopError(mop); return 1; } while (EOF != datum) { airArrayLenSet(dataArr, numRed+1); if (!data) { fprintf(stderr, "%s: couldn't re-allocated data buffer\n", me); airMopError(mop); return 1; } /* now "data" is the address of a sufficiently large array */ data[numRed++] = datum; datum = fgetc(file); } /* loop finishes when there's nothing more to read from file */ printf("%s: read %u bytes into memory\n", me, numRed); /* destroy the airArray, but keep the data allocated */ airArrayNix(dataArr); printf("%s: first value was %u\n", me, data[0]); /* free up the data array itself */ free(data); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/air/test/fp.c0000664000175000017500000001060412165631065016705 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" #include char *me; int main(int argc, char *argv[]) { int c, hibit, ret; float f, g, parsed_f; double d, parsed_d; char str[128]; AIR_UNUSED(argc); me = argv[0]; g = 0.0; g = g/g; printf("0.0/0.0 = %f\n", g); airFPFprintf_f(stdout, g); hibit = (*((int*)&g) >> 22) & 1; printf("hi bit of 23-bit fraction field = %d\n", hibit); if (hibit == airMyQNaNHiBit) { printf("(agrees with airMyQNaNHiBit)\n"); } else { printf("%s: !!!!\n", me); printf("%s: !!!! PROBLEM: nan's hi bit is NOT airMyQNaNHiBit (%d)\n", me, airMyQNaNHiBit); printf("%s: !!!!\n", me); } printf(" - - - - - - - - - - - - - - - -\n"); printf(" - - - - - FLOATS - - - - - - -\n"); printf(" - - - - - - - - - - - - - - - -\n"); for(c=airFP_Unknown+1; c %f(%d)) (AIR_EXISTS = %d)\n", c, f, parsed_f, ret, AIR_EXISTS(f)); airSinglePrintf(stdout, NULL, "--<%f>--\n", f); if (c != airFPClass_f(f)) { printf("\n\n%s: Silly hardware!!!\n", me); printf("%s: can't return a float of class %d %sfrom a function\n\n\n", me, c, airFP_SNAN == c ? "(signaling NaN) " : ""); } airFPFprintf_f(stdout, f); d = f; /* I think solaris turns the SNAN into a QNAN */ printf("to double and back:\n"); airFPFprintf_f(stdout, d); printf("AIR_ISNAN_F = %d\n", AIR_ISNAN_F(f)); } printf(" - - - - - - - - - - - - - - - -\n"); printf(" - - - - - DOUBLES - - - - - - -\n"); printf(" - - - - - - - - - - - - - - - -\n"); for(c=airFP_Unknown+1; c %f(%d)) (AIR_EXISTS = %d)\n", c, d, parsed_d, ret, AIR_EXISTS(d)); airSinglePrintf(stdout, NULL, "--<%f>--\n", d); if (c != airFPClass_d(d)) { printf("\n\n%s: Silly hardware!!!\n", me); printf("%s: can't return a double of class %d %sfrom a function\n\n\n", me, c, airFP_SNAN == c ? "(signaling NaN) " : ""); } airFPFprintf_d(stdout, d); } printf(" - - - - - - - - - - - - - - - -\n"); printf(" - - - - - - - - - - - - - - - -\n"); f = AIR_SNAN; printf("SNaN test: f = SNaN = float(0x%x) = %f; (QNaNHiBit = %d)\n", airFloatSNaN.i, f, airMyQNaNHiBit); airFPFprintf_f(stdout, f); g = f*f; printf("g = f*f = %f\n", g); airFPFprintf_f(stdout, g); g = sin(f); printf("g = sin(f) = %f\n", g); airFPFprintf_f(stdout, g); printf("\n"); printf("FLT_MAX:\n"); airFPFprintf_f(stdout, FLT_MAX); printf("\n"); printf("FLT_MIN:\n"); airFPFprintf_f(stdout, FLT_MIN); printf("\n"); printf("DBL_MAX:\n"); airFPFprintf_d(stdout, DBL_MAX); printf("\n"); printf("DBL_MIN:\n"); airFPFprintf_d(stdout, DBL_MIN); printf("\n"); printf("AIR_NAN = %f; AIR_EXISTS(AIR_NAN) = %d\n", AIR_NAN, AIR_EXISTS(AIR_NAN)); printf("AIR_POS_INF = %f; AIR_EXISTS(AIR_POS_INF) = %d\n", AIR_POS_INF, AIR_EXISTS(AIR_POS_INF)); printf("AIR_NEG_INF = %f; AIR_EXISTS(AIR_NEG_INF) = %d\n", AIR_NEG_INF, AIR_EXISTS(AIR_NEG_INF)); exit(0); } teem-1.11.0~svn6057/src/air/test/tline.c0000664000175000017500000000430112165631065017410 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" FILE * myopen(char *name) { if (!strcmp(name, "-")) { return stdin; } else { return fopen(name, "r"); } } void myclose(FILE *file) { if (file != stdin) { fclose(file); } return; } int main(int argc, char *argv[]) { char *me, *fileS, *line; FILE *file; int size, maxed, ret; me = argv[0]; if (2 != argc) { /* 0 1 (2) */ fprintf(stderr, "usage: %s \n", me); exit(1); } fileS = argv[1]; if (!( file = myopen(fileS) )) { fprintf(stderr, "%s: couldn't open \"%s\" for reading\n", me, fileS); exit(1); } myclose(file); size = 30000; do { maxed = 0; file = myopen(fileS); line = (char *)calloc(size, sizeof(char)); printf(" ----------------- size = %d\n", size); do { ret = airOneLine(file, line, size); if (ret) { printf("%2d |%s|\n", ret, line); maxed |= (ret == size+1); } } while(ret > 0); myclose(file); free(line); size++; } while(maxed); printf("\n\nsize = %d\n", size); exit(0); } teem-1.11.0~svn6057/src/air/test/logrice.c0000664000175000017500000000316312165631065017726 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" /* ** 0 1 2 3 (4) ** logrice tru mes sig */ int main(int argc, char *argv[]) { char *me; double tru, mes, sig; me = argv[0]; if (4 != argc || 1 != sscanf(argv[1], "%lg", &tru) || 1 != sscanf(argv[2], "%lg", &mes) || 1 != sscanf(argv[3], "%lg", &sig)) { fprintf(stderr, "%s: need three doubles\n", me); exit(1); } printf("log(Rician(%g,%g,%g)) = %g\n", tru, mes, sig, airLogRician(tru, mes, sig)); exit(0); } teem-1.11.0~svn6057/src/air/test/bessy.c0000664000175000017500000000420112165631065017421 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" /* ** 0 1 (2) ** bessy */ int main(int argc, char *argv[]) { char *me; double x; int i; me = argv[0]; if (2 != argc || 1 != sscanf(argv[1], "%lg", &x)) { fprintf(stderr, "%s: need one double as argument\n", me); exit(1); } printf("BesselI(0, %g) = %g\n", x, airBesselI0(x)); printf("log(BesselI(0, %g)) = %g ?=? %g\n", x, airLogBesselI0(x), log(airBesselI0(x))); printf("BesselI(1, %g) = %g\n", x, airBesselI1(x)); printf("BesselI1By0(%g) = %g ?=? %g\n", x, airBesselI1By0(x), airBesselI1(x)/airBesselI0(x)); printf("BesselIExpScaled(0, %g) = %f\n", x, airBesselI0ExpScaled(x)); printf("BesselIExpScaled(1, %g) = %f\n", x, airBesselI1ExpScaled(x)); printf("erfc,erf(%g) = %g %g\n", x, airErfc(x), airErf(x)); printf(" n BesselIn(n,%g) BesselInExpScaled(n,%g)\n", x, x); for (i = -10; i<= 10; i++) { printf("%d %g %g\n", i, airBesselIn(i,x), airBesselInExpScaled(i,x)); } exit(0); } teem-1.11.0~svn6057/src/air/test/doubleprint.c0000664000175000017500000000412312165631065020626 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" char *me; int main(int argc, char *argv[]) { char *fS, buff[128]; float f; double d, sd; int ret; me = argv[0]; if (2 != argc) { fprintf(stderr, "usage: %s \n", me); exit(1); } fS = argv[1]; ret = sscanf(fS, "%lf", &sd); if (!ret) { printf("%s: sscanf(%s, \"%%lf\") failed\n", me, fS); printf("\n"); } if (1 != airSingleSscanf(fS, "%lf", &d)) { fprintf(stderr, "%s: couldn't parse \"%s\" as double\n", me, fS); exit(1); } if (ret && (sd != d)) { printf("%s: sscanf result (%f) != airSingleSscanf (%f)!!!\n", me, sd, d); printf("\n"); } f = d; airSinglePrintf(NULL, buff, "%f", f); printf("%s: printf/airSinglePrintf as float:\n%f\n%s\n", me, f, buff); airSinglePrintf(NULL, buff, "%lf", d); printf("\n"); printf("%s: printf/airSinglePrintf as double:\n%f\n%s\n", me, d, buff); printf("\n"); printf("%s: airFPFprintf_d:\n", me); airFPFprintf_d(stderr, d); exit(0); } teem-1.11.0~svn6057/src/air/test/tok.c0000664000175000017500000000337012165631065017077 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" int main(int argc, char *argv[]) { char *s, *ct, *last, *ret; if (3 != argc) { /* 0 1 2 (3) */ printf("usage: %s \n", argv[0]); exit(1); } s = argv[1]; ct = argv[2]; printf("s = |%s|\n", s); printf("ct = |%s|\n", ct); ret = airStrtok(s, ct, &last); while (ret) { printf("|%s|\n", ret); ret = airStrtok(NULL, ct, &last); } printf("--------------\n"); printf("hey, why doesn't this work?!?!?\n"); printf("s = |%s|\n", s); ret = strtok(s, ct); while (ret) { printf("ret = |%s|\n", ret); ret = strtok(NULL, ct); } exit(0); } teem-1.11.0~svn6057/src/air/test/texp.c0000664000175000017500000000306612165631065017264 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" /* ** 0 1 (2) ** texp N */ int main(int argc, char *argv[]) { char *me; unsigned int ii, NN; me = argv[0]; if (2 != argc || 1 != sscanf(argv[1], "%u", &NN)) { fprintf(stderr, "%s: need one uint as argument\n", me); exit(1); } for (ii=0; ii #include #include #endif int main(int argc, char *argv[]) { #if TEEM_DIO == 0 AIR_UNUSED(argc); fprintf(stderr, "%s: no direct-io testing for you\n", argv[0]); return 1; #else char *me, *fname, *multS, *data; FILE *file; double time0, time1, time2; int fd, align, mult, min, max, ret; size_t size; airArray *mop; me = argv[0]; if (3 != argc) { /* 0 1 2 (3) */ fprintf(stderr, "usage: %s \n", me); return 1; } fname = argv[1]; multS = argv[2]; if (1 != sscanf(multS, "%d", &mult)) { fprintf(stderr, "%s: couln't parse mult %s as int\n", me, multS); return 1; } mop = airMopNew(); if (!(file = fopen(fname, "w"))) { fprintf(stderr, "%s: couldn't open %s for writing\n", me, fname); airMopError(mop); return 1; } airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); fd = fileno(file); if (-1 == fd) { fprintf(stderr, "%s: couldn't get underlying descriptor\n", me); airMopError(mop); return 1; } fprintf(stderr, "%s: fd(%s) = %d\n", me, fname, fd); ret = airDioTest(fd, NULL, 0); if (airNoDio_okay != ret) { fprintf(stderr, "%s: no good: \"%s\"\n", me, airNoDioErr(ret)); airMopError(mop); return 1; } airDioInfo(&align, &min, &max, fd); fprintf(stderr, "%s: --> align=%d, min=%d, max=%d\n", me, align, min, max); size = (size_t)max*mult; data = airDioMalloc(size, fd); if (!data) { char stmp[AIR_STRLEN_SMALL]; fprintf(stderr, "%s: airDioMalloc(%s) failed\n", me, airSprintSize_t(stmp, size)); airMopError(mop); return 1; } airMopAdd(mop, data, airFree, airMopAlways); fprintf(stderr, "\ndata size = %g MB\n", (double)size/(1024*1024)); /* -------------------------------------------------------------- */ fprintf(stderr, "(1) non-aligned memory, regular write:\n"); time0 = airTime(); if (size-1 != write(fd, data+1, size-1)) { fprintf(stderr, "%s: write failed\n", me); airMopError(mop); return 1; } time1 = airTime(); fsync(fd); time2 = airTime(); fprintf(stderr, " time = %g + %g = %g (%g MB/sec)\n", time1 - time0, time2 - time1, time2 - time0, (size/(1024*1024)) / (time2 - time0)); airMopSub(mop, file, (airMopper)airFclose); fclose(file); /* -------------------------------------------------------------- */ /* -------------------------------------------------------------- */ fprintf(stderr, "(2) aligned memory, regular write:\n"); file = fopen(fname, "w"); airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); fd = fileno(file); time0 = airTime(); if (size != write(fd, data, size)) { fprintf(stderr, "%s: write failed\n", me); airMopError(mop); return 1; } time1 = airTime(); fsync(fd); time2 = airTime(); fprintf(stderr, " time = %g + %g = %g (%g MB/sec)\n", time1 - time0, time2 - time1, time2 - time0, (size/(1024*1024)) / (time2 - time0)); airMopSub(mop, file, (airMopper)airFclose); fclose(file); /* -------------------------------------------------------------- */ /* -------------------------------------------------------------- */ fprintf(stderr, "(3) aligned memory, air's direct IO:\n"); file = fopen(fname, "w"); airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); fd = fileno(file); time0 = airTime(); if (size != airDioWrite(fd, data, size)) { fprintf(stderr, "%s: write failed\n", me); airMopError(mop); return 1; } time1 = airTime(); fsync(fd); time2 = airTime(); fprintf(stderr, " time = %g + %g = %g (%g MB/sec)\n", time1 - time0, time2 - time1, time2 - time0, (size/(1024*1024)) / (time2 - time0)); airMopSub(mop, file, (airMopper)airFclose); fclose(file); /* -------------------------------------------------------------- */ /* -------------------------------------------------------------- */ fprintf(stderr, "(4) aligned memory, direct IO by hand:\n"); { /* "input": fname, size, data */ int flags; struct dioattr dio; char *ptr; size_t remain, totalrit, rit, part; file = fopen(fname, "w"); if (-1 == (fd = fileno(file))) { fprintf(stderr, "%s: couldn't get underlying descriptor\n", me); airMopError(mop); return 1; } airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); flags = fcntl(fd, F_GETFL); if (-1 == fcntl(fd, F_SETFL, flags | FDIRECT)) { fprintf(stderr, "%s: couldn't turn on direct IO\n", me); airMopError(mop); return 1; } if (0 != fcntl(fd, F_DIOINFO, &dio)) { fprintf(stderr, "%s: couldn't learn direct IO specifics", me); airMopError(mop); return 1; } remain = size; totalrit = 0; ptr = data; time0 = airTime(); do { part = remain > dio.d_maxiosz ? dio.d_maxiosz : remain; rit = write(fd, ptr, part); if (rit != part) { fprintf(stderr, "%s: write failed\n", me); airMopError(mop); return 1; } totalrit += rit; ptr += rit; remain -= rit; } while (remain); time1 = airTime(); fsync(fd); time2 = airTime(); fprintf(stderr, " time = %g + %g = %g (%g MB/sec)\n", time1 - time0, time2 - time1, time2 - time0, (size/(1024*1024)) / (time2 - time0)); airMopSub(mop, file, (airMopper)airFclose); fclose(file); } /* -------------------------------------------------------------- */ airMopError(mop); exit(0); #endif } teem-1.11.0~svn6057/src/air/test/tprint.c0000664000175000017500000000414412165631065017622 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" int main(int argc, char *argv[]) { char *me; char str[AIR_STRLEN_SMALL]; size_t sz; ptrdiff_t pd; int change; AIR_UNUSED(argc); me = argv[0]; #define PRINT printf("%s: %lu %s\n", me, AIR_CAST(unsigned long, sz), airSprintSize_t(str, sz)) sz = 1; PRINT; do { sz = 2.4*sz; PRINT; } while (sz); *((long int *)(&sz)) = -1; PRINT; sz += 1; PRINT; sz -= 1; PRINT; sz -= 1; PRINT; sz -= 1; PRINT; #undef PRINT #define PRINT printf("%s: %ld %s\n", me, AIR_CAST(long, pd), airSprintPtrdiff_t(str, pd)) pd = 1; PRINT; do { ptrdiff_t od; od = pd; pd = 2.4*pd; PRINT; change = (od != pd); } while (change); pd = -1; PRINT; do { ptrdiff_t od; od = pd; pd = 2.4*pd; PRINT; change = (od != pd); } while (change); pd -= 5; pd += 1; PRINT; pd += 1; PRINT; pd += 1; PRINT; pd += 1; PRINT; pd += 1; PRINT; pd += 1; PRINT; pd += 1; PRINT; pd += 1; PRINT; pd += 1; PRINT; exit(0); } teem-1.11.0~svn6057/src/air/test/floatprint.c0000664000175000017500000000411612165631065020463 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" char *me; int main(int argc, char *argv[]) { char *fS, buff[128]; float f, sf; double d; int ret; me = argv[0]; if (2 != argc) { fprintf(stderr, "usage: %s \n", me); exit(1); } fS = argv[1]; ret = sscanf(fS, "%f", &sf); if (!ret) { printf("%s: sscanf(%s, \"%%f\") failed\n", me, fS); printf("\n"); } if (1 != airSingleSscanf(fS, "%f", &f)) { fprintf(stderr, "%s: couldn't parse \"%s\" as float\n", me, fS); exit(1); } if (ret && (sf != f)) { printf("%s: sscanf result (%f) != airSingleSscanf (%f) !!!\n", me, sf, f); printf("\n"); } d = f; airSinglePrintf(NULL, buff, "%f", f); printf("%s: printf/airSinglePrintf as float:\n%f\n%s\n", me, f, buff); airSinglePrintf(NULL, buff, "%f", d); printf("\n"); printf("%s: printf/airSinglePrintf as double:\n%f\n%s\n", me, d, buff); printf("\n"); printf("%s: airFPFprintf_f:\n", me); airFPFprintf_f(stderr, f); exit(0); } teem-1.11.0~svn6057/src/air/test/trand.c0000664000175000017500000000306612165631065017414 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../air.h" int main(int argc, char *argv[]) { char *me, *NS; int ii, N; me = argv[0]; if (2 != argc) { /* 0 1 (2) */ fprintf(stderr, "usage: %s \n", me); exit(1); } NS = argv[1]; if (1 != sscanf(NS, "%d", &N)) { fprintf(stderr, "%s: couldn't parse %s as int N\n", me, NS); exit(1); } for (ii=0; iis1=|%s| _s2=|%s|->s2=|%s|\n", _s1 ? _s1 : "(NULL)", s1, _s2 ? _s2 : "(NULL)", s2); */ ret = strcmp(s1, s2); return ret; } /* ---- END non-NrrdIO */ /* ******** airStrtok() ** ** thread-safe strtok() replacement. Use just like strtok(), but on ** each call to parse a given string, pass as the last argument the ** address of a char*, to be used for saving state while the string is ** traversed. Like strtok(), this will alter the "s" array passed to ** it on the first call, and like strtok(), this returns pointers into ** this string (rather than allocating new strings for each token). */ char * airStrtok(char *s, const char *ct, char **last) { char *h, *e, *q; if (!(ct && last)) { /* can't do any work, bail */ return NULL; } h = s ? s : *last; if (!airStrlen(h)) return NULL; h += strspn(h, ct); if ('\"' == *h && airStrtokQuoting) { /* something is trying to be quoted, and, we'll respect that */ /* have to find the next un-escaped '\"' */ h++; q = h; while (*q && !('\"' == *q && '\\' != q[-1])) { q++; } if (*q) { /* we found an unescaped '\"' */ e = q; } else { /* give up; pretend we never tried to do this quoting stuff */ e = h + strcspn(h, ct); } } else { e = h + strcspn(h, ct); } if ('\0' == *e) { *last = e; } else { *e = '\0'; *last = e + 1; } return h; } /* ******** airStrntok() ** ** returns the number of tokens parsable by airStrtok(), but does ** NOT alter the given string */ unsigned int airStrntok(const char *_s, const char *ct) { char *s, *t, *l=NULL; unsigned int n = 0; if (_s && ct) { s = airStrdup(_s); t = airStrtok(s, ct, &l); while (t) { n++; t = airStrtok(NULL, ct, &l); } airFree(s); /* no NULL assignment to s, else compile warnings */ } return n; } char * airStrtrans(char *s, char from, char to) { size_t i, l; if (s) { l = strlen(s); for (i=0; i 0)) { return NULL; } srcLen = airStrlen(src); if (1 == dstSize || !srcLen) { dst[0] = '\0'; return dst; } /* else dstSize > 1 AND src is a non-empy string */ copyLen = AIR_MIN(dstSize-1, srcLen); for (ii=0; ii= strlen(suff))) return 0; if (!strncmp(s + strlen(s) - strlen(suff), suff, strlen(suff))) return 1; else return 0; } /* ******** airUnescape() ** ** unescapes \\ and \n in place in a given string. ** Always returns the same pointer as given */ char * airUnescape(char *s) { size_t i, j, len; int found=0; len = airStrlen(s); if (!len) return s; for (i=1, j=0; i= 3 /* need room for a character and a Windows newline */ && line && file)) { return 0; } /* c is always set at least once, but not so for any char in line[] */ for (ii=0; (ii <= size-2 /* room for line[ii] and \0 after that */ && EOF != (cc=getc(file)) /* didn't hit EOF trying to read char */ && cc != '\n' /* char isn't newline */ && cc != '\r'); /* char isn't carriage return */ ++ii) { line[ii] = AIR_CAST(char, cc); } if (EOF == cc) { /* for-loop terminated because we hit EOF */ line[0] = '\0'; return 0; } else if ('\r' == cc || '\n' == cc) { /* for-loop terminated because we hit '\n' or '\r' */ /* if it was '\r', see if next character is '\n' */ if ('\r' == cc) { cc = getc(file); if (EOF != cc && '\n' != cc) { /* oops, we got something, and it was not a '\n'; put it back */ ungetc(cc, file); } } line[ii] = '\0'; return ii+1; } else { /* for-loop terminated because we got to end of buffer (ii == size-1) */ cc = getc(file); /* but see if we were about to get '\r', "\r\n", or '\n' */ if ('\r' == cc) { int dd; dd = getc(file); if (EOF != dd && '\n' != dd) { /* oops, put it back */ ungetc(dd, file); } line[ii] = '\0'; return ii+1; } else if ('\n' == cc) { line[ii] = '\0'; return ii+1; } else { /* weren't about to get a line termination, we really did run out of buffer */ if (EOF != cc) { ungetc(cc, file); /* we're allowed one ungetc on ANY stream */ } line[size-1] = '\0'; return size+1; } } } teem-1.11.0~svn6057/src/air/mop.c0000664000175000017500000002100212165631065016106 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "air.h" /* learned: using these functions correctly to manage even simple memory usage can be very tricky. problem 0: even trying to write airMopPrint, I foolishly thought: "print the string, then free it". But the print callback clobbered the free callback, because of the semantics of airMopAdd(). So, I had to add _airMopAdd(). problem 1: debugging hest with purify, on case of hitting error after parsing multiple variable parameter option of strings: so, I allocated an array of strings (arrays), and registered all the strings with airMopMem(), and registered the array itself also with airMopMem(). Again, got clobbered. airSetNull(&((*vP)[0])) clobbered airFree(*vP). So, I gave up on using airMopMem() for the individual elements, and am using simply airMopAdd(airFree). The alternative was to change the airMopAdd()s in airMopMem() to _airMopAdd()s, but I didn't feel confident that this would be safe. ----------- SO: as a result of all that: airMopAdd() will no longer over-write a callback based on the pointer It will only over-write the "when" of a (pointer,callback) pair, so that you can't register multiple copies of a (pointer,callback) pair (regardless of differences, if any, in "when"). Therefore, there will be AT MOST ONE instance of a (pointer,callback) pair in a mop. _airMopAdd() was nixed. airMopSub() and airMopUnMem were created */ #define AIR_MOP_INCR 10 airArray * airMopNew() { return airArrayNew(NULL, NULL, sizeof(airMop), AIR_MOP_INCR); } /* ** except for allocation error, this always returns 0, ** to facilitate this weird idiom: ** ** if (!(nmeasr = nrrdNew()) ** || airMopAdd(mop, nmeasr, (airMopper)nrrdNuke, airMopAlways) ** || !(nsize = nrrdNew()) ** || airMopAdd(mop, nsize, (airMopper)nrrdNuke, airMopAlways) ** || !(pair = AIR_CAST(ccpair *, calloc(pctx->CCNum, sizeof(ccpair)))) ** || airMopAdd(mop, pair, airFree, airMopAlways)) { ** ** GLK may regret this. */ int airMopAdd(airArray *arr, void *ptr, airMopper mop, int when) { static const char me[]="airMopAdd"; airMop *mops; unsigned int ii; if (!arr) { return 0; } mops = (airMop *)arr->data; /* first see if this is something we already set a callback for */ for (ii=0; iilen; ii++) { if (mops[ii].ptr == ptr && mops[ii].mop == mop) { mops[ii].when = when; /* we're done */ return 0; } } /* this is a new ptr */ ii = airArrayLenIncr(arr, 1); if (!arr->data) { fprintf(stderr, "%s: PANIC: can't re-allocate mop array\n", me); return 1; } mops = (airMop *)arr->data; mops[ii].ptr = ptr; mops[ii].mop = mop; mops[ii].when = when; return 0; } void airMopSub(airArray *arr, void *ptr, airMopper mop) { airMop *mops; unsigned int ii; if (!arr) { return; } mops = (airMop *)arr->data; /* first see if this is something we already set a callback for */ for (ii=0; iilen; ii++) { if (mops[ii].ptr == ptr && mops[ii].mop == mop) { mops[ii].ptr = NULL; mops[ii].mop = NULL; mops[ii].when = airMopNever; return; } } /* else we've never seen this before, user is a moron */ return; } void airMopMem(airArray *arr, void *_ptrP, int when) { void **ptrP; if (!(arr && _ptrP)) { return; } ptrP = (void **)_ptrP; airMopAdd(arr, ptrP, (airMopper)airSetNull, when); airMopAdd(arr, *ptrP, airFree, when); /* printf("airMopMem(0x%p): will free() 0x%p\n", (void*)arr, (void*)(*ptrP)); printf("airMopMem(0x%p): will set 0x%p to NULL\n", (void*)arr, (void*)ptrP); */ return; } void airMopUnMem(airArray *arr, void *_ptrP) { void **ptrP; if (!(arr && _ptrP)) { return; } ptrP = (void **)_ptrP; airMopSub(arr, ptrP, (airMopper)airSetNull); airMopSub(arr, *ptrP, airFree); return; } static void * _airMopPrint(void *_str) { char *str; str = (char *)_str; if (str) { printf("%s\n", str); } return NULL; } void airMopPrint(airArray *arr, const void *_str, int when) { char *copy; if (!(arr && _str)) return; copy = airStrdup(AIR_CAST(const char*, _str)); airMopAdd(arr, copy, airFree, airMopAlways); airMopAdd(arr, copy, _airMopPrint, when); return; } static const char _airMopWhenStr[4][128] = { " never", " error", " okay", "always", }; /* ** This is to overcome the warning about ** "ISO C forbids conversion of function pointer to object pointer type"; ** the result here is thus implementation-dependent */ typedef union { airMopper m; void *v; } mvunion; void airMopDebug(airArray *arr) { airMop *mops; unsigned int ii; mvunion mvu; if (!arr) return; mops = (airMop *)arr->data; printf("airMopDebug: _________________________ mop stack for 0x%p:\n", AIR_VOIDP(arr)); if (arr->len) { ii = arr->len; do { ii--; printf("%4u: ", ii); if (NULL == mops[ii].mop && NULL == mops[ii].ptr && airMopNever == mops[ii].when) { printf("no-op\n"); continue; } /* else */ printf("%s: ", _airMopWhenStr[mops[ii].when]); if (airFree == mops[ii].mop) { printf("airFree(0x%p)\n", AIR_VOIDP(mops[ii].ptr)); continue; } if ((airMopper)airSetNull == mops[ii].mop) { printf("airSetNull(0x%p)\n", AIR_VOIDP(mops[ii].ptr)); continue; } if (_airMopPrint == mops[ii].mop) { printf("_airMopPrint(\"%s\" == 0x%p)\n", AIR_CAST(char*, mops[ii].ptr), AIR_VOIDP(mops[ii].ptr)); continue; } if ((airMopper)airFclose == mops[ii].mop) { printf("airFclose(0x%p)\n", AIR_VOIDP(mops[ii].ptr)); continue; } /* else */ mvu.m = mops[ii].mop; printf("0x%p(0x%p)\n", AIR_VOIDP(mvu.v), AIR_VOIDP(mops[ii].ptr)); } while (ii); } printf("airMopDebug: ^^^^^^^^^^^^^^^^^^^^^^^^^\n"); } void airMopDone(airArray *arr, int error) { airMop *mops; unsigned int ii; /* printf("airMopDone(%p): hello, %s\n", (void*)arr, error ? "error" : "okay"); */ if (arr) { mops = (airMop *)arr->data; if (arr->len) { ii = arr->len; do { ii--; if (mops[ii].ptr && (airMopAlways == mops[ii].when || (airMopOnError == mops[ii].when && error) || (airMopOnOkay == mops[ii].when && !error))) { mops[ii].mop(mops[ii].ptr); } } while (ii); } airArrayNuke(arr); /* printf("airMopDone(%p): done!\n", (void*)arr); */ } return; } void airMopError(airArray *arr) { airMopDone(arr, AIR_TRUE); } void airMopOkay(airArray *arr) { airMopDone(arr, AIR_FALSE); } /* ---- BEGIN non-NrrdIO */ /* ** like airMopSub but calls the mopper first */ void airMopSingleDone(airArray *arr, void *ptr, int error) { airMop *mops; unsigned int ii; if (!arr || !(arr->len)) { return; } mops = (airMop *)arr->data; ii = arr->len; do { ii--; if (ptr == mops[ii].ptr && (airMopAlways == mops[ii].when || (airMopOnError == mops[ii].when && error) || (airMopOnOkay == mops[ii].when && !error))) { mops[ii].mop(mops[ii].ptr); mops[ii].ptr = NULL; mops[ii].mop = NULL; mops[ii].when = airMopNever; } } while (ii); return; } void airMopSingleError(airArray *arr, void *ptr) { airMopSingleDone(arr, ptr, AIR_TRUE); } void airMopSingleOkay(airArray *arr, void *ptr) { airMopSingleDone(arr, ptr, AIR_FALSE); } /* ---- END non-NrrdIO */ teem-1.11.0~svn6057/src/air/TODO.txt0000664000175000017500000000327212042322251016452 0ustar domibeldomibel Things that would be good for a future airString or airBuffer * string dynamically reallocated on write, with optional cap on allowed size, maybe allowing control of whether reaching that size is an error * when reading from a file, allow the file to be gzip'd, so that all the subsequent IO can be blind to whether the file was compressed or not * unify sprint'ing into string, or fwrite'ing to file * unify reading from string, or from file * when writing to string, should be remembering where last output is, to avoid needless repeated calls to strlen * should this be the thing that can make stdin fseek-able? * could use one of these as wrapper around stderr for normal output of unrrduCmds, but allowing it to be saved to string in case the command is being invoked from C * remember comment from nrrd/formatPNG.c: /* Reading PNGs teaches Gordon that his scheme for parsing nrrd header information is inappropriately specific to reading PNMs and NRRDs, since in this case the text from which we parse a nrrd field descriptor did NOT come from a line of text as read by _nrrdOneLine */ nio->line = (char *)airFree(nio->line); nio->line = airStrdup(txt[i].text); An OS-general means of: * generating a unique temp file name * removing a given file (for cleaning up temp file) Fix Windows implementation of airTime() fix airSinglePrintf to not put a space before double-type special values ("%4s" hack has to go) complete fprintf/sprintf/printf stand-in: use trio? complete fscanf/sscanf stand-in Decide if air will have a general-purpose thread-safe progress indication scheme, or if this will be each library's responsibility teem-1.11.0~svn6057/src/air/privateAir.h0000664000175000017500000000227312165631065017437 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* miscAir.c */ extern double _airSanityHelper(double val); teem-1.11.0~svn6057/src/release.txt0000664000175000017500000001150612165726377016600 0ustar domibeldomibel GLK's list of things to do for each release. This started with an early version of Teem and has been only partially maintained since then; consider it a work-in-progress ... -5) use names.pl to make sure all sourcefile names are unique -4) Make sure the CMake and GNUMake agree on source files per-library -3.5) make sure that teem/src/meet/enumall.c:meetAirEnumAll() is up-to-date -3) make sure that all the files have the right copyright pre-ambles -2.5) make sure that there are no DOS files find . -name \*.c -exec unu undos -n {} \; find . -name \*.h -exec unu undos -n {} \; find . -name \*.txt -exec unu undos -n {} \; -2) make sure there are no tabs: cat > tab.txt (tab) (return) ^D find . -name \*.c -exec grep -f tab.txt {} /dev/null \; | more find . -name \*.h -exec grep -f tab.txt {} /dev/null \; | more find . -name \*.txt -exec grep -f tab.txt {} /dev/null \; | more rm -f tab.txt -1) finalize TEEM_VERSION* in air/air.h and airTeemReleaseDate in air/misc.c -0.5) regenerate teem/python/ctypes/teem.py -0.25) regenerate NrrdIO 0) Bring documentation up-to-date with source tree make sure all the unrrdu pages agree with unu usage info cd ~/teem cp README.txt ~/teemdoc/html/tree/ cp src/README.txt ~/teemdoc/html/tree/src/ cp src/CHANGES.txt ~/teemdoc/html/tree/src/ cp src/TODO.txt ~/teemdoc/html/tree/src/ cp src/LICENSE.txt ~/teemdoc/html/tree/src/ (cd ~/teemdoc; cvs commit -m "pre-release text file update") sftp kindlmann,teem@web.sourceforge.net is one way of putting files onto the web server 1) Check namespace safety a) Make sure all library symbols are properly prefixed: run make/release-nm-check.csh and make/release-def-check.csh 2) Make sure it works "make teem/dev teem/install" on every supported architecture, and make sure Windows stuff also still works (if new files or libraries were added) 2.5) svn "tag": setenv VERSION <> svn copy https://teem.svn.sourceforge.net/svnroot/teem/teem/trunk \ https://teem.svn.sourceforge.net/svnroot/teem/teem/branches/Teem-${VERSION} 3) Create source-only tgz: svn co http://teem.svn.sourceforge.net/svnroot/teem/teem/trunk teem cd teem find . -name .svn -exec rm -rf {} \; cd ..; mv teem teem-${VERSION}-src tar czvf teem-${VERSION}-src.tar.gz teem-${VERSION}-src mv teem-${VERSION}-src.tar.gz ~/rel svn co http://teem.svn.sf.net:/svnroot/teem/NrrdIO/trunk NrrdIO cd NrrdIO find . -name .svn -exec rm -rf {} \; cd ..; mv NrrdIO NrrdIO-${VERSION}-src tar czvf NrrdIO-${VERSION}-src.tar.gz NrrdIO-${VERSION}-src mv NrrdIO-${VERSION}-src.tar.gz ~/rel 4) Checkout a tree: "make teem/install" on all platforms ... rapture/ray/muse: setenv TEEM_ARCH irix6.64 setenv TEEM_PNG setenv TEEM_ZLIB setenv TEEM_BZIP2 setenv TEEM_BZIP2_LPATH -L/home/sci/gk/usr/irix6.64/lib setenv TEEM_BZIP2_IPATH -I/home/sci/gk/usr/include setenv TEEM_PNG_LPATH -L/home/sci/gk/usr/irix6.64/lib setenv TEEM_PNG_IPATH -I/home/sci/gk/usr/irix6.64/include (options for being multi-threaded) setenv TEEM_PTHREAD setenv TEEM_LINK_SHARED setenv PATH ${PATH}:/home/sci/gk/usr/local/irix6.64/bin setenv LD_LIBRARY64_PATH ${LD_LIBRARY64_PATH}:/home/sci/gk/usr/local/irix6.64/lib vision: setenv TEEM_ARCH solaris setenv TEEM_PNG setenv TEEM_ZLIB setenv TEEM_BZIP2 setenv TEEM_BZIP2_LPATH -L/home/gk/lib setenv TEEM_BZIP2_IPATH -I/home/gk/include setenv TEEM_ZLIB_LPATH -L/home/gk/lib setenv TEEM_ZLIB_IPATH -I/home/gk/include setenv TEEM_PNG_LPATH -L/usr/local/lib setenv TEEM_PNG_IPATH -I/usr/local/include 5) Create Unix-ish binary builds (without src or docs): for each TEEM_ARCH in: irix6.n32 linux.ia64 linux.amd64 linux.32 cygwin solaris darwin.32 darwin.64 - ssh to some ARCH machine, copy teem-VERSION-src.tar.gz there if needed - setenv TEEM_XXX for all the externals that it makes sense to compile into the distributed statically linked binaries setenv VERSION <<>> tar xzvf ~/rel/teem-${VERSION}-src.tar.gz cd teem-${VERSION}-src; setenv TEEM_DEST `pwd` cd src; make; cd .. mv src/LICENSE.txt . yes | rm -rf README.txt aix cygwin darwin.32 darwin.64 irix6.64 \ irix6.n32 linux.32 linux.amd64 linux.ia64 solaris win32 src \ CMake CMakeLists.txt CTestConfig.cmake DartConfig.cmake cd .. mv teem-${VERSION}-src teem-${VERSION}-${TEEM_ARCH} tar czvf teem-${VERSION}-${TEEM_ARCH}.tar.gz teem-${VERSION}-${TEEM_ARCH} mv -f teem-${VERSION}-${TEEM_ARCH}.tar.gz ~/rel yes | rm -rf teem-${VERSION}-${TEEM_ARCH} 6) Create Windows binary builds. 7) put the builds on sourceforge: cd ~/rel sftp kindlmann@frs.sourceforge.net cd uploads mput *.tar.gz 9) update online documentation (which may be done more often than releases) cd ~/teemdoc cvs commit -m "pre-release doc update" ssh kindlmann@shell.sf.net cd teem/htdocs cvs update exit for Deft: cvs -d:ext:kindlmann@teem.cvs.sf.net:/cvsroot/teem init cvs -d:ext:kindlmann@teem.cvs.sf.net:/cvsroot/teem import -m "initial import" Deft Deft start teem-1.11.0~svn6057/src/names.pl0000664000175000017500000000361412165631065016045 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # GLK uses this to make sure that there are no filename clashes # between different libraries, a constraint introduced with the # Windows port, and then enforced with the practice of putting all # object files into per-architecture "obj" directories. # # Currently this is very simple! Usage is: # # (TEEM_LIB_LIST) # ls -1 {air,hest,biff,nrrd,ell,unrrdu,alan,moss,tijk,gage,dye,bane,limn,echo,hoover,seek,ten,elf,pull,coil,push,mite,meet}/*.c | perl names.pl | sort | wc -l # ls -1 {air,hest,biff,nrrd,ell,unrrdu,alan,moss,tijk,gage,dye,bane,limn,echo,hoover,seek,ten,elf,pull,coil,push,mite,meet}/*.c | perl names.pl | sort | uniq | wc -l # # and then make sure the two numbers are the same. while (<>) { chomp; ($dir, $name) = split '/', $_, 2; print "$name\n"; } teem-1.11.0~svn6057/src/preamble.c0000664000175000017500000000217512165631065016341 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ teem-1.11.0~svn6057/src/gage/0000775000175000017500000000000012203513754015301 5ustar domibeldomibelteem-1.11.0~svn6057/src/gage/scl.c0000664000175000017500000003221312174651113016226 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" /* ** _gageSclTable ** ** the static array of item information for the scalar kind. */ gageItemEntry _gageSclTable[GAGE_SCL_ITEM_MAX+1] = { /* enum value len,deriv, prereqs, parent item, parent index, needData */ {gageSclUnknown, 0, 0, {0}, 0, 0, AIR_FALSE}, {gageSclValue, 1, 0, {0}, 0, 0, AIR_FALSE}, {gageSclGradVec, 3, 1, {0}, 0, 0, AIR_FALSE}, {gageSclGradMag, 1, 1, {gageSclGradVec}, 0, 0, AIR_FALSE}, {gageSclNormal, 3, 1, {gageSclGradVec, gageSclGradMag}, 0, 0, AIR_FALSE}, {gageSclNProj, 9, 1, {gageSclNormal}, 0, 0, AIR_FALSE}, {gageSclNPerp, 9, 1, {gageSclNormal}, 0, 0, AIR_FALSE}, {gageSclHessian, 9, 2, {gageSclHessian}, 0, 0, AIR_FALSE}, {gageSclHessianTen, 7, 2, {gageSclHessian}, 0, 0, AIR_FALSE}, {gageSclLaplacian, 1, 2, {gageSclHessian}, 0, 0, AIR_FALSE}, {gageSclHessFrob, 1, 2, {gageSclHessian}, 0, 0, AIR_FALSE}, {gageSclHessEval, 3, 2, {gageSclHessian}, 0, 0, AIR_FALSE}, {gageSclHessEval0, 1, 2, {gageSclHessEval}, gageSclHessEval, 0, AIR_FALSE}, {gageSclHessEval1, 1, 2, {gageSclHessEval}, gageSclHessEval, 1, AIR_FALSE}, {gageSclHessEval2, 1, 2, {gageSclHessEval}, gageSclHessEval, 2, AIR_FALSE}, {gageSclHessEvec, 9, 2, {gageSclHessian, gageSclHessEval}, 0, 0, AIR_FALSE}, {gageSclHessEvec0, 3, 2, {gageSclHessEvec}, gageSclHessEvec, 0, AIR_FALSE}, {gageSclHessEvec1, 3, 2, {gageSclHessEvec}, gageSclHessEvec, 3, AIR_FALSE}, {gageSclHessEvec2, 3, 2, {gageSclHessEvec}, gageSclHessEvec, 6, AIR_FALSE}, {gageScl2ndDD, 1, 2, {gageSclHessian, gageSclNormal}, 0, 0, AIR_FALSE}, {gageSclGeomTens, 9, 2, {gageSclHessian, gageSclNPerp, gageSclGradMag}, 0, 0, AIR_FALSE}, {gageSclGeomTensTen, 7, 2, {gageSclGeomTens}, 0, 0, AIR_FALSE}, {gageSclK1, 1, 2, {gageSclTotalCurv, gageSclShapeTrace}, 0, 0, AIR_FALSE}, {gageSclK2, 1, 2, {gageSclTotalCurv, gageSclShapeTrace}, 0, 0, AIR_FALSE}, {gageSclTotalCurv, 1, 2, {gageSclGeomTens}, 0, 0, AIR_FALSE}, {gageSclShapeTrace, 1, 2, {gageSclGeomTens}, 0, 0, AIR_FALSE}, {gageSclShapeIndex, 1, 2, {gageSclK1, gageSclK2}, 0, 0, AIR_FALSE}, {gageSclMeanCurv, 1, 2, {gageSclK1, gageSclK2}, 0, 0, AIR_FALSE}, {gageSclGaussCurv, 1, 2, {gageSclK1, gageSclK2}, 0, 0, AIR_FALSE}, {gageSclCurvDir1, 3, 2, {gageSclGeomTens, gageSclK1, gageSclK2}, 0, 0, AIR_FALSE}, {gageSclCurvDir2, 3, 2, {gageSclGeomTens, gageSclK1, gageSclK2}, 0, 0, AIR_FALSE}, {gageSclFlowlineCurv, 1, 2, {gageSclGeomTens}, 0, 0, AIR_FALSE}, {gageSclMedian, 1, 0, {0}, 0, 0, AIR_FALSE}, {gageSclHessValleyness,1, 2, {gageSclHessEval}, 0, 0, AIR_FALSE}, {gageSclHessRidgeness, 1, 2, {gageSclHessEval}, 0, 0, AIR_FALSE}, {gageSclHessDotPeakness,1, 2, {gageSclHessEval}, 0, 0, AIR_FALSE}, {gageSclHessMode, 1, 2, {gageSclHessEval}, 0, 0, AIR_FALSE} }; const char * _gageSclStr[] = { "(unknown gageScl)", "value", "gradient vector", "gradient magnitude", "normalized gradient", "normal projector", "tangent projector", "Hessian", "HessianTen", "Laplacian", "Frob(Hessian)", "Hessian eigenvalues", "Hessian eigenvalue[0]", "Hessian eigenvalue[1]", "Hessian eigenvalue[2]", "Hessian eigenvectors", "Hessian eigenvector[0]", "Hessian eigenvector[1]", "Hessian eigenvector[2]", "2nd DD along gradient", "geometry tensor", "geometry tensor tensor", /* HEY this is really silly, should be fixed for Teem 2.0 */ "kappa1", "kappa2", "total curvature", "shape trace", "shape index", "mean curvature", "Gaussian curvature", "1st curvature direction", "2nd curvature direction", "flowline curvature", "median", "Hessian valleyness", "Hessian ridgeness", "Hessian peakness", "Hessian mode" }; const char * _gageSclDesc[] = { "unknown gageScl query", "reconstructed scalar data value", "gradient vector, un-normalized", "gradient magnitude (length of gradient vector)", "normalized gradient vector", "projection into normal", "projection into tangent (perp space of normal)", "3x3 Hessian matrix", "7-element Hessian tensor", "Laplacian", "Frobenius norm of Hessian", "Hessian's eigenvalues", "Hessian's 1st eigenvalue", "Hessian's 2nd eigenvalue", "Hessian's 3rd eigenvalue", "Hessian's eigenvectors", "Hessian's 1st eigenvector", "Hessian's 2nd eigenvector", "Hessian's 3rd eigenvector", "2nd directional derivative along gradient", "geometry tensor", "7-element geometry tensor", "1st principal curvature (K1)", "2nd principal curvature (K2)", "total curvature (L2 norm of K1, K2)", "shape trace = (K1+K2)/(total curvature)", "Koenderink's shape index", "mean curvature = (K1+K2)/2", "gaussian curvature = K1*K2", "1st principal curvature direction", "2nd principal curvature direction", "curvature of normal streamline", "median of iv3 cache (not weighted by any filter (yet))", "measure of valleyness of Hessian", "measure of ridgeness of Hessian", "measure of peakness of Hessian", "mode of Hessian eigenvalues" }; int _gageSclVal[] = { gageSclUnknown, gageSclValue, gageSclGradVec, gageSclGradMag, gageSclNormal, gageSclNProj, gageSclNPerp, gageSclHessian, gageSclHessianTen, gageSclLaplacian, gageSclHessFrob, gageSclHessEval, gageSclHessEval0, gageSclHessEval1, gageSclHessEval2, gageSclHessEvec, gageSclHessEvec0, gageSclHessEvec1, gageSclHessEvec2, gageScl2ndDD, gageSclGeomTens, gageSclGeomTensTen, gageSclK1, gageSclK2, gageSclTotalCurv, gageSclShapeTrace, gageSclShapeIndex, gageSclMeanCurv, gageSclGaussCurv, gageSclCurvDir1, gageSclCurvDir2, gageSclFlowlineCurv, gageSclMedian, gageSclHessValleyness, gageSclHessRidgeness, gageSclHessDotPeakness, gageSclHessMode }; #define GS_V gageSclValue #define GS_GV gageSclGradVec #define GS_GM gageSclGradMag #define GS_N gageSclNormal #define GS_NProj gageSclNProj #define GS_NP gageSclNPerp #define GS_H gageSclHessian #define GS_HT gageSclHessianTen #define GS_L gageSclLaplacian #define GS_HF gageSclHessFrob #define GS_HA gageSclHessEval #define GS_HA0 gageSclHessEval0 #define GS_HA1 gageSclHessEval1 #define GS_HA2 gageSclHessEval2 #define GS_HE gageSclHessEvec #define GS_HE0 gageSclHessEvec0 #define GS_HE1 gageSclHessEvec1 #define GS_HE2 gageSclHessEvec2 #define GS_2D gageScl2ndDD #define GS_GT gageSclGeomTens #define GS_GTT gageSclGeomTensTen #define GS_K1 gageSclK1 #define GS_K2 gageSclK2 #define GS_TC gageSclTotalCurv #define GS_ST gageSclShapeTrace #define GS_SI gageSclShapeIndex #define GS_MC gageSclMeanCurv #define GS_GC gageSclGaussCurv #define GS_C1 gageSclCurvDir1 #define GS_C2 gageSclCurvDir2 #define GS_FC gageSclFlowlineCurv #define GS_MD gageSclMedian #define GS_HV gageSclHessValleyness #define GS_HR gageSclHessRidgeness #define GS_PK gageSclHessDotPeakness #define GS_HM gageSclHessMode const char * _gageSclStrEqv[] = { "v", "val", "value", "gv", "gvec", "gradvec", "grad vec", "gradient vector", "gm", "gmag", "gradmag", "grad mag", "gradient magnitude", "gn", "n", "normal", "gnorm", "normg", "norm", "normgrad", "norm grad", "normalized gradient", "nproj", "normal projector", "np", "nperp", "tangent projector", "h", "hess", "hessian", "ht", "hessten", "hessianten", "l", "lapl", "laplacian", "hf", "frob(hessian)", "heval", "hesseval", "hessian eval", "hessian eigenvalues", "heval0", "hesseval0", "hessian eigenvalue[0]", "heval1", "hesseval1", "hessian eigenvalue[1]", "heval2", "hesseval2", "hessian eigenvalue[2]", "hevec", "hessevec", "hessian evec", "hessian eigenvectors", "hevec0", "hessevec0", "hessian eigenvector[0]", "hevec1", "hessevec1", "hessian eigenvector[1]", "hevec2", "hessevec2", "hessian eigenvector[2]", "2d", "2dd", "2nddd", "2nd", "2nd dd", "2nd dd along gradient", "gt", "gten", "geoten", "geomten", "geometry tensor", "gtenten", "geometry tensor tensor", "k1", "kap1", "kappa1", "k2", "kap2", "kappa2", "total curv", "totalcurv", "total curvature", "tc", "cv", "curvedness", "st", "shape trace", "si", "shape index", "mc", "mcurv", "meancurv", "mean curvature", "gc", "gcurv", "gausscurv", "gaussian curvature", "cdir1", "c dir1", "curvdir1", "curv dir1", "curvature direction 1", "1st curvature direction", "cdir2", "c dir2", "curvdir2", "curv dir2", "curvature direction 2", "2nd curvature direction", "fc", "flowlinecurv", "flowline curv", "flowline curvature", "med", "median", "hvalley", "hessvalley", "hessian valleyness", "hridge", "hessridge", "hessian ridgeness", "hdpeak", "hessian peakness", "hmode", "hessmode", "hessian mode", "" }; const int _gageSclValEqv[] = { GS_V, GS_V, GS_V, GS_GV, GS_GV, GS_GV, GS_GV, GS_GV, GS_GM, GS_GM, GS_GM, GS_GM, GS_GM, GS_N, GS_N, GS_N, GS_N, GS_N, GS_N, GS_N, GS_N, GS_N, GS_NProj, GS_NProj, GS_NP, GS_NP, GS_NP, GS_H, GS_H, GS_H, GS_HT, GS_HT, GS_HT, GS_L, GS_L, GS_L, GS_HF, GS_HF, GS_HA, GS_HA, GS_HA, GS_HA, GS_HA0, GS_HA0, GS_HA0, GS_HA1, GS_HA1, GS_HA1, GS_HA2, GS_HA2, GS_HA2, GS_HE, GS_HE, GS_HE, GS_HE, GS_HE0, GS_HE0, GS_HE0, GS_HE1, GS_HE1, GS_HE1, GS_HE2, GS_HE2, GS_HE2, GS_2D, GS_2D, GS_2D, GS_2D, GS_2D, GS_2D, GS_GT, GS_GT, GS_GT, GS_GT, GS_GT, GS_GTT, GS_GTT, GS_K1, GS_K1, GS_K1, GS_K2, GS_K2, GS_K2, GS_TC, GS_TC, GS_TC, GS_TC, GS_TC, GS_TC, GS_ST, GS_ST, GS_SI, GS_SI, GS_MC, GS_MC, GS_MC, GS_MC, GS_GC, GS_GC, GS_GC, GS_GC, GS_C1, GS_C1, GS_C1, GS_C1, GS_C1, GS_C1, GS_C2, GS_C2, GS_C2, GS_C2, GS_C2, GS_C2, GS_FC, GS_FC, GS_FC, GS_FC, GS_MD, GS_MD, GS_HV, GS_HV, GS_HV, GS_HR, GS_HR, GS_HR, GS_PK, GS_PK, GS_HM, GS_HM, GS_HM }; const airEnum _gageScl = { "gageScl", GAGE_SCL_ITEM_MAX, _gageSclStr, _gageSclVal, _gageSclDesc, _gageSclStrEqv, _gageSclValEqv, AIR_FALSE }; const airEnum *const gageScl = &_gageScl; gageKind _gageKindScl = { AIR_FALSE, /* statically allocated */ "scalar", &_gageScl, 0, /* baseDim */ 1, /* valLen */ GAGE_SCL_ITEM_MAX, _gageSclTable, _gageSclIv3Print, _gageSclFilter, _gageSclAnswer, NULL, NULL, NULL, NULL, NULL }; gageKind *const gageKindScl = &_gageKindScl; const gageItemPack _gageItemPackSclValue = { &_gageKindScl, {gageSclUnknown, gageSclValue, gageSclGradVec, gageSclGradMag, gageSclNormal, gageSclHessian, gageSclHessEval0, gageSclHessEval1, gageSclHessEval2, gageSclHessEvec0, gageSclHessEvec1, gageSclHessEvec2} }; const gageItemPack *const gageItemPackSclValue = &_gageItemPackSclValue; teem-1.11.0~svn6057/src/gage/deconvolve.c0000664000175000017500000002412512165631065017620 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" int gageDeconvolve(Nrrd *_nout, double *lastDiffP, const Nrrd *nin, const gageKind *kind, const NrrdKernelSpec *ksp, int typeOut, unsigned int maxIter, int saveAnyway, double step, double epsilon, int verbose) { static const char me[]="gageDeconvolve"; gageContext *ctx[2]; gagePerVolume *pvl[2]; double *out[2], *val[2], alpha, (*lup)(const void *, size_t), meandiff=0; const double *ans[2]; Nrrd *nout[2]; airArray *mop; unsigned int sx, sy, sz, xi, yi, zi, anslen, thiz=0, last, inIdx, iter; int E, valItem; if (!(_nout && lastDiffP && nin && kind && ksp)) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!(nrrdTypeDefault == typeOut || !airEnumValCheck(nrrdType, typeOut))) { biffAddf(GAGE, "%s: typeOut %d not valid", me, typeOut); return 1; } if (!( maxIter >= 1 )) { biffAddf(GAGE, "%s: need maxIter >= 1 (not %u)", me, maxIter); return 1; } if (!( epsilon >= 0 )) { biffAddf(GAGE, "%s: need epsilon >= 0.0 (not %g)", me, epsilon); return 1; } /* this once changed from 0 to 1, but is unlikely to change again */ valItem = 1; mop = airMopNew(); for (iter=0; iter<2; iter++) { nout[iter] = nrrdNew(); airMopAdd(mop, nout[iter], (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nout[iter], nin, nrrdTypeDouble)) { biffMovef(GAGE, NRRD, "%s: couldn't allocate working buffer %u", me, iter); airMopError(mop); return 1; } ctx[iter] = gageContextNew(); airMopAdd(mop, ctx[iter], (airMopper)gageContextNix, airMopAlways); E = 0; if (!E) E |= !(pvl[iter] = gagePerVolumeNew(ctx[iter], nout[iter], kind)); if (!E) E |= gagePerVolumeAttach(ctx[iter], pvl[iter]); if (!E) E |= gageKernelSet(ctx[iter], gageKernel00, ksp->kernel, ksp->parm); if (!E) E |= gageQueryItemOn(ctx[iter], pvl[iter], valItem); if (!E) E |= gageUpdate(ctx[iter]); if (E) { biffAddf(GAGE, "%s: trouble setting up context %u", me, iter); airMopError(mop); return 1; } out[iter] = AIR_CAST(double*, nout[iter]->data); ans[iter] = gageAnswerPointer(ctx[iter], pvl[iter], valItem); } anslen = kind->table[valItem].answerLength; lup = nrrdDLookup[nin->type]; alpha = ksp->kernel->eval1_d(0.0, ksp->parm); sx = ctx[0]->shape->size[0]; sy = ctx[0]->shape->size[1]; sz = ctx[0]->shape->size[2]; for (iter=0; iterdata, ai + anslen*inIdx); aa = ans[last][ai]; val[thiz][ai] = val[last][ai] + step*(in - aa)/alpha; meandiff += 2*(in - aa)*(in - aa)/(DBL_EPSILON + in*in + aa*aa); } val[thiz] += anslen; val[last] += anslen; ++inIdx; } } } meandiff /= sx*sy*sz; if (verbose) { fprintf(stderr, "%s: iter %u meandiff = %g\n", me, iter, meandiff); } if (meandiff < epsilon) { /* we have indeed converged while iter < maxIter */ break; } } if (iter == maxIter) { if (!saveAnyway) { biffAddf(GAGE, "%s: failed to converge in %u iterations, meandiff = %g", me, maxIter, meandiff); airMopError(mop); return 1; } else { if (verbose) { fprintf(stderr, "%s: at maxIter %u; err %g still > thresh %g\n", me, iter, meandiff, epsilon); } } } if (nrrdClampConvert(_nout, nout[thiz], (nrrdTypeDefault == typeOut ? nin->type : typeOut))) { biffAddf(GAGE, "%s: couldn't create output", me); airMopError(mop); return 1; } *lastDiffP = meandiff; airMopOkay(mop); return 0; } /* ******************************* ** all the following functionality should at some point be ** pushed down to nrrd . . . */ static void deconvLine(double *line, size_t llen, const NrrdKernelSpec *ksp) { /* Add as many other parameters to this as you want, like number and location of poles, or whatever other buffers you think you need (they should be passed, not allocated and freed on a per-line basis) */ /* comment these out when there is a real function body */ AIR_UNUSED(line); AIR_UNUSED(llen); AIR_UNUSED(ksp); return; } static int deconvTrivial(const NrrdKernelSpec *ksp) { int ret; /* HEY this will be much easier once kernels have a way of advertising whether or not they interpolate */ if (1 == ksp->parm[0] && (ksp->kernel == nrrdKernelHann || ksp->kernel == nrrdKernelBlackman || ksp->kernel == nrrdKernelBox || ksp->kernel == nrrdKernelCheap || ksp->kernel == nrrdKernelTent)) { ret = AIR_TRUE; } else { ret = AIR_FALSE; } return ret; } int gageDeconvolveSeparableKnown(const NrrdKernelSpec *ksp) { int ret; if (!ksp) { ret = 0; } else if (deconvTrivial(ksp) || nrrdKernelBSpline3 == ksp->kernel || nrrdKernelBSpline5 == ksp->kernel) { ret = 1; } else { ret = 0; } return ret; } int gageDeconvolveSeparable(Nrrd *nout, const Nrrd *nin, const gageKind *kind, const NrrdKernelSpec *ksp, int typeOut) { static const char me[]="gageDeconvolveSeparable"; double *line, (*lup)(const void *, size_t), (*ins)(void *, size_t, double); airArray *mop; size_t lineLen, sx, sy, sz, idx, ii, jj; unsigned int vi, valLen; if (!(nout && nin && kind && ksp)) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!(nrrdTypeDefault == typeOut || !airEnumValCheck(nrrdType, typeOut))) { biffAddf(GAGE, "%s: typeOut %d not valid", me, typeOut); return 1; } if (!gageDeconvolveSeparableKnown(ksp)) { biffAddf(GAGE, "%s: separable deconv not known for %s kernel", me, ksp->kernel->name); return 1; } if (gageKindVolumeCheck(kind, nin)) { biffAddf(GAGE, "%s: given volume doesn't fit %s kind", me, kind->name); return 1; } if (nrrdTypeDefault == typeOut ? nrrdCopy(nout, nin) : nrrdConvert(nout, nin, typeOut)) { biffMovef(GAGE, NRRD, "%s: problem allocating output", me); return 1; } if (deconvTrivial(ksp)) { /* if there's no real work for the deconvolution, then by copying the values we're already done; bye */ return 0; } valLen = kind->valLen; sx = nin->axis[kind->baseDim + 0].size; sy = nin->axis[kind->baseDim + 1].size; sz = nin->axis[kind->baseDim + 2].size; lineLen = sx; lineLen = AIR_MAX(lineLen, sy); lineLen = AIR_MAX(lineLen, sz); lup = nrrdDLookup[nin->type]; ins = nrrdDInsert[nout->type]; mop = airMopNew(); line = AIR_CALLOC(lineLen*valLen, double); airMopAdd(mop, line, airFree, airMopAlways); /* process along X scanlines */ for (jj=0; jj xi + sx*(yi + sy*zi) == 0 + sx*(jj%sy + sy*(jj/sy)) == 0 + sx*jj */ idx = 0 + valLen*(0 + sx*jj); for (ii=0; iidata, idx + vi + valLen*ii); } } for (vi=0; vidata, idx + vi + valLen*ii, line[ii + sx*vi]); } } } /* process along Y scanlines */ for (jj=0; jj xi + sx*(yi + sy*zi) == jj%sx + sx*(0 + sy*jj/sx) */ idx = 0 + valLen*((jj%sx) + sx*(0 + sy*(jj/sx))); for (ii=0; iidata, idx + vi + valLen*sx*ii); } } for (vi=0; vidata, idx + vi + valLen*sx*ii, line[ii + sy*vi]); } } } /* process along Z scanlines */ for (jj=0; jj xi + sx*(yi + sy*zi) == jj%sx + sx*(jj/sx + sy*0) == jj%sx + sx*(jj/sx) == jj */ idx = 0 + valLen*jj; for (ii=0; iidata, idx + vi + valLen*sx*sy*ii); } } for (vi=0; vidata, idx + vi + valLen*sx*sy*ii, line[ii + sz*vi]); } } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/gage/stack.c0000664000175000017500000004214712202222110016540 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" /* ** based on: T. Lindeberg. "Effective Scale: A Natural Unit For ** Measuring Scale-Space Lifetime" IEEE PAMI, 15:1068-1074 (1993) ** ** which includes tau(tee) as equation (29), ** here taking A'' == 0 and B'' == 1, with ** a0 and a1 as defined by eqs (22) and (23) ** ** Used MiniMaxApproximation[] from Mathematica (see ** ~gk/papers/ssp/nb/effective-scale-TauOfTee.nb) to get functions, ** but the specific functions and their domains could certainly be ** improved upon. Also, the absence of conversions directly between ** tau and sigma is quite unfortunate: going through tee loses ** precision and takes more time. ** ** ACCURATE: can set this to 0 or 1: ** 0: use a quick-and-dirty approximation to tau(tee), which ** uses a straight line segment for small scales, and then ** has a C^1-continuous transition to the large-scale approximation eq (33) ** 1: careful approximation based on the MiniMaxApproximation[]s */ #define ACCURATE 1 double gageTauOfTee(double tee) { double tau; if (tee < 0) { tau = 0; #if ACCURATE } else if (tee < 1.49807) { /* mmtau0tweaked */ tau = (tee*(0.2756644487429131 + tee*(0.10594329088466668 + tee*(0.05514331911165778 + (0.021449249669475232 + 0.004417835440932558*tee)*tee))))/ (1.0 + tee*(-0.08684532328108877 + tee*(0.1792830876099199 + tee*(0.07468999631784223 + (0.012123550696192354 + 0.0021535864222409365*tee)*tee)))); } else if (tee < 4.96757) { /* mmtau1 */ tau = (0.0076145275813930356 + tee*(0.24811886965997867 + tee*(0.048329025380584194 + tee*(0.04227260554167517 + (0.0084221516844712 + 0.0092075782656669*tee)*tee))))/ (1.0 + tee*(-0.43596678272093764 + tee*(0.38077975530585234 + tee*(-0.049133766853683175 + (0.030319379462443567 + 0.0034126333151669654*tee)*tee)))); } else if (tee < 15.4583) { /* mmtau2 */ tau = (-0.2897145176074084 + tee*(1.3527948686285203 + tee*(-0.47099157589904095 + tee*(-0.16031981786376195 + (-0.004820970155881798 - 4.149777202275125e-6*tee)*tee))))/ (1.0 + tee*(0.3662508612514773 + tee*(-0.5357849572367938 + (-0.0805122462310566 - 0.0015558889784971902*tee)*tee))); } else if (tee < 420.787) { /* mmtau3 */ tau = (-4.2037874383990445e9 + tee*(2.838805157541766e9 + tee*(4.032410315406513e8 + tee*(5.392017876788518e6 + tee*(9135.49750298428 + tee)))))/ (tee*(2.326563899563907e9 + tee*(1.6920560224321905e8 + tee*(1.613645012626063e6 + (2049.748257887103 + 0.1617034516398788*tee)*tee)))); #else /* quick and dirty approximation */ } else if (tee < 1.3741310015234351) { tau = 0.600069568241882*tee/1.3741310015234351; #endif } else { /* lindtau = eq (33) in paper */ tau = 0.53653222368715360118 + log(tee)/2.0 + log(1.0 - 1.0/(8.0*tee)); } return tau; } double gageTeeOfTau(double tau) { double tee; /* the number of branches here is not good; needs re-working */ if (tau < 0) { tee = 0; #if ACCURATE } else if (tau < 0.611262) { /* mmtee0tweaked */ tee = (tau*(3.6275987317285265 + tau*(11.774700160760132 + tau*(4.52406587856803 + tau*(-14.125688866786549 + tau*(-0.725387283317479 + 3.5113122862478865*tau))))))/ (1.0 + tau*(4.955066250765395 + tau*(4.6850073321973404 + tau*(-6.407987550661679 + tau*(-6.398430668865182 + 5.213709282093169*tau))))); } else if (tau < 1.31281) { /* mmtee1 */ tee = (1.9887378739371435e49 + tau*(-2.681749984485673e50 + tau*(-4.23360463718195e50 + tau*(2.09694591123974e51 + tau*(-2.7561518523389087e51 + (1.661629137055526e51 - 4.471073383223687e50*tau)*tau)))))/ (1.0 + tau*(-5.920734745050949e50 + tau*(1.580953446553531e51 + tau*(-1.799463907469813e51 + tau*(1.0766702953985062e51 + tau*(-3.57278667155516e50 + 5.008335824520649e49*tau)))))); } else if (tau < 1.64767) { /* mmtee2 */ tee = (7.929177830383403 + tau*(-26.12773195115971 + tau*(40.13296225515305 + tau*(-25.041659428733585 + 11.357596970027744*tau))))/ (1.0 + tau*(-2.3694595653302377 + tau*(7.324354882915464 + (-3.5335141717471314 + 0.4916661013041915*tau)*tau))); } else if (tau < 1.88714) { /* mmtee3 */ tee = (0.8334252264680793 + tau*(-0.2388940380698891 + (0.6057616935583752 - 0.01610044688317929*tau)*tau))/(1.0 + tau*(-0.7723301124908083 + (0.21283962841683607 - 0.020834957466407206*tau)*tau)); } else if (tau < 2.23845) { /* mmtee4 */ tee = (0.6376900379835665 + tau*(0.3177131886056259 + (0.1844114646774132 + 0.2001613331260136*tau)*tau))/(1.0 + tau*(-0.6685635461372561 + (0.15860524381878136 - 0.013304300252332686*tau)*tau)); } else if (tau < 2.6065) { /* mmtee5 */ tee = (1.3420027677612982 + (-0.939215712453483 + 0.9586140009249253*tau)*tau)/(1.0 + tau*(-0.6923014141351673 + (0.16834190074776287 - 0.014312833444962668*tau)*tau)); } else if (tau < 3.14419) { /* mmtee6 */ tee = (tau*(190.2181493338235 + tau*(-120.16652155353106 + 60.*tau)))/(76.13355144582292 + tau*(-42.019121363472614 + (8.023304636521623 - 0.5281725039404653*tau)*tau)); #else /* quick and dirty approximation */ } else if (tau < 0.600069568241882) { tee = 1.3741310015234351*tau/0.600069568241882; #endif } else { /* lindtee = lindtau^{-1} */ double ee; ee = exp(2.0*tau); tee = 0.0063325739776461107152*(27.0*ee + 2*AIR_PI*AIR_PI + 3.0*sqrt(81.0*ee*ee + 12*ee*AIR_PI*AIR_PI)); } return tee; } double gageSigOfTau(double tau) { return sqrt(gageTeeOfTau(tau)); } double gageTauOfSig(double sig) { return gageTauOfTee(sig*sig); } double gageStackWtoI(gageContext *ctx, double swrl, int *outside) { double si; if (ctx && ctx->parm.stackUse && outside) { unsigned int sidx; if (swrl < ctx->stackPos[0]) { /* we'll extrapolate from stackPos[0] and [1] */ sidx = 0; *outside = AIR_TRUE; } else if (swrl > ctx->stackPos[ctx->pvlNum-2]) { /* extrapolate from stackPos[ctx->pvlNum-3] and [ctx->pvlNum-2]; gageStackPerVolumeAttach ensures that we there are at least two blurrings pvls & one base pvl ==> pvlNum >= 3 ==> pvlNum-3 >= 0 */ sidx = ctx->pvlNum-3; *outside = AIR_TRUE; } else { /* HEY: stupid linear search */ for (sidx=0; sidxpvlNum-2; sidx++) { if (AIR_IN_CL(ctx->stackPos[sidx], swrl, ctx->stackPos[sidx+1])) { break; } } if (sidx == ctx->pvlNum-2) { /* search failure */ *outside = AIR_FALSE; return AIR_NAN; } *outside = AIR_FALSE; } si = AIR_AFFINE(ctx->stackPos[sidx], swrl, ctx->stackPos[sidx+1], sidx, sidx+1); } else { si = AIR_NAN; } return si; } double gageStackItoW(gageContext *ctx, double si, int *outside) { unsigned int sidx; double swrl, sfrac; if (ctx && ctx->parm.stackUse && outside) { if (si < 0) { sidx = 0; *outside = AIR_TRUE; } else if (si > ctx->pvlNum-2) { sidx = ctx->pvlNum-3; *outside = AIR_TRUE; } else { sidx = AIR_CAST(unsigned int, si); *outside = AIR_FALSE; } sfrac = si - sidx; swrl = AIR_AFFINE(0, sfrac, 1, ctx->stackPos[sidx], ctx->stackPos[sidx+1]); /* fprintf(stderr, "!%s: si %g (%u) --> %u + %g --> [%g,%g] -> %g\n", me, si, ctx->pvlNum, sidx, sfrac, ctx->stackPos[sidx], ctx->stackPos[sidx+1], swrl); */ } else { swrl = AIR_NAN; } return swrl; } /* ** this is a little messy: the given pvlStack array has to be allocated ** by the caller to hold blNum gagePerVolume pointers, BUT, the values ** of pvlStack[i] shouldn't be set to anything: as with gagePerVolumeNew(), ** gage allocates the pervolume itself. */ int gageStackPerVolumeNew(gageContext *ctx, gagePerVolume **pvlStack, const Nrrd *const *nblur, unsigned int blNum, const gageKind *kind) { static const char me[]="gageStackPerVolumeNew"; unsigned int blIdx; if (!( ctx && pvlStack && nblur && kind )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!blNum) { biffAddf(GAGE, "%s: need non-zero num", me); return 1; } for (blIdx=0; blIdxpvl[pvlNum-1] */ int gageStackPerVolumeAttach(gageContext *ctx, gagePerVolume *pvlBase, gagePerVolume **pvlStack, const double *stackPos, unsigned int blNum) { static const char me[]="gageStackPerVolumeAttach"; unsigned int blIdx; if (!(ctx && pvlBase && pvlStack && stackPos)) { biffAddf(GAGE, "%s: got NULL pointer %p %p %p %p", me, AIR_VOIDP(ctx), AIR_VOIDP(pvlBase), AIR_VOIDP(pvlStack), AIR_CVOIDP(stackPos)); return 1; } if (!( blNum >= 2 )) { /* this constraint is important for the logic of stack reconstruction: minimum number of node-centered samples is 2, and the number of pvls has to be at least 3 (two blurrings + one base pvl) */ biffAddf(GAGE, "%s: need at least two samples along stack", me); return 1; } if (ctx->pvlNum) { biffAddf(GAGE, "%s: can't have pre-existing volumes (%u) " "prior to stack attachment", me, ctx->pvlNum); return 1; } for (blIdx=0; blIdxstackPos); airFree(ctx->stackFsl); airFree(ctx->stackFw); ctx->stackPos = AIR_CALLOC(blNum, double); ctx->stackFsl = AIR_CALLOC(blNum, double); ctx->stackFw = AIR_CALLOC(blNum, double); if (!( ctx->stackPos && ctx->stackFsl && ctx->stackFw )) { biffAddf(GAGE, "%s: couldn't allocate stack buffers (%p %p %p)", me, AIR_CAST(void *, ctx->stackPos), AIR_CAST(void *, ctx->stackFsl), AIR_CAST(void *, ctx->stackFw)); return 1; } for (blIdx=0; blIdxstackPos[blIdx] = stackPos[blIdx]; } return 0; } /* ** _gageStackBaseIv3Fill ** ** after the individual iv3's in the stack have been filled, this does ** the across-stack filtering to fill the iv3 of pvl[pvlNum-1] (the ** "base" pvl) */ int _gageStackBaseIv3Fill(gageContext *ctx) { static const char me[]="_gageStackBaseIv3Fill"; unsigned int fd, pvlIdx, cacheIdx, cacheLen, baseIdx, valLen; fd = 2*ctx->radius; /* the "base" pvl is the LAST pvl */ baseIdx = ctx->pvlNum - 1; cacheLen = fd*fd*fd*ctx->pvl[0]->kind->valLen; if (ctx->verbose > 2) { fprintf(stderr, "%s: cacheLen = %u\n", me, cacheLen); } if (nrrdKernelHermiteScaleSpaceFlag == ctx->ksp[gageKernelStack]->kernel) { unsigned int xi, yi, zi, blurIdx, valIdx, fdd; double xx, *iv30, *iv31, sigma0, sigma1; fdd = fd*fd; /* initialize the output iv3 to all zeros, since we won't be usefully setting the values on the boundary (the boundary which is required in the rest of the stack's iv3s in order to do the laplacian-based spline recon), and we can't have any non-existent values creeping in. We shouldn't need to do any kind of nrrdBoundaryBleed thing here, because the kernel weights really should be zero on the boundary. */ for (cacheIdx=0; cacheIdxpvl[baseIdx]->iv3[cacheIdx] = 0; } /* find the interval in the pre-blurred volumes containing the desired scale location */ for (pvlIdx=0; pvlIdxpvlNum-1; pvlIdx++) { if (ctx->stackFw[pvlIdx]) { /* has to be non-zero somewhere, since _gageLocationSet() gives an error if there aren't non-zero stackFw[i] */ break; } } /* so no way that pvlIdx == pvlNum-1 */ if (pvlIdx == ctx->pvlNum-2) { /* pvlNum-2 is pvl index of last pre-blurred volume */ /* gageStackPerVolumeAttach() enforces getting at least two pre-blurred volumes --> pvlNum >= 3 --> blurIdx >= 0 */ blurIdx = pvlIdx-1; xx = 1; } else { blurIdx = pvlIdx; /* by design, the hermite non-kernel generates the same values as the tent kernel (with scale forced == 1), so we can use those to control the interpolation */ xx = 1 - ctx->stackFw[pvlIdx]; } iv30 = ctx->pvl[blurIdx]->iv3; iv31 = ctx->pvl[blurIdx+1]->iv3; sigma0 = ctx->stackPos[blurIdx]; sigma1 = ctx->stackPos[blurIdx+1]; valLen = ctx->pvl[baseIdx]->kind->valLen; for (valIdx=0; valIdxpvl[baseIdx]->iv3[iii] = dd + xx*(cc + xx*(bb + aa*xx)); } } } } } else { /* we're doing simple convolution-based recon on the stack */ /* NOTE we are treating the 4D fd*fd*fd*valLen iv3 as a big 1-D array */ double wght, val; for (cacheIdx=0; cacheIdxpvlNum-1; pvlIdx++) { wght = ctx->stackFw[pvlIdx]; val += (wght ? wght*ctx->pvl[pvlIdx]->iv3[cacheIdx] : 0); } ctx->pvl[baseIdx]->iv3[cacheIdx] = val; } } return 0; } /* ******** gageStackProbe() */ int gageStackProbe(gageContext *ctx, double xi, double yi, double zi, double stackIdx) { static const char me[]="gageStackProbe"; if (!ctx) { return 1; } if (!ctx->parm.stackUse) { if (ctx->parm.generateErrStr) { sprintf(ctx->errStr, "%s: can't probe stack without parm.stackUse", me); } else { strcpy(ctx->errStr, _GAGE_NON_ERR_STR); } ctx->errNum = gageErrStackUnused; return 1; } return _gageProbe(ctx, xi, yi, zi, stackIdx); } int gageStackProbeSpace(gageContext *ctx, double xx, double yy, double zz, double ss, int indexSpace, int clamp) { static const char me[]="gageStackProbeSpace"; if (!ctx) { return 1; } if (!ctx->parm.stackUse) { if (ctx->parm.generateErrStr) { sprintf(ctx->errStr, "%s: can't probe stack without parm.stackUse", me); } else { strcpy(ctx->errStr, _GAGE_NON_ERR_STR); } ctx->errNum = gageErrStackUnused; return 1; } return _gageProbeSpace(ctx, xx, yy, zz, ss, indexSpace, clamp); } teem-1.11.0~svn6057/src/gage/stackBlur.c0000664000175000017500000006224112200257507017402 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" /* ** does not use biff */ gageStackBlurParm * gageStackBlurParmNew() { gageStackBlurParm *parm; parm = AIR_CALLOC(1, gageStackBlurParm); if (parm) { parm->scale = NULL; parm->sigmaMax = gageDefStackBlurSigmaMax; parm->padValue = AIR_NAN; parm->kspec = NULL; parm->dataCheck = AIR_TRUE; parm->boundary = nrrdBoundaryUnknown; parm->renormalize = AIR_FALSE; parm->oneDim = AIR_FALSE; parm->verbose = 0; } return parm; } gageStackBlurParm * gageStackBlurParmNix(gageStackBlurParm *sbp) { if (sbp) { airFree(sbp->scale); nrrdKernelSpecNix(sbp->kspec); free(sbp); } return NULL; } int gageStackBlurParmScaleSet(gageStackBlurParm *sbp, unsigned int num, double scaleMin, double scaleMax, int uniform, int optim) { static const char me[]="gageStackBlurParmScaleSet"; unsigned int ii; if (!( sbp )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } airFree(sbp->scale); sbp->scale = NULL; if (!( scaleMin < scaleMax )) { biffAddf(GAGE, "%s: scaleMin %g not < scaleMax %g", me, scaleMin, scaleMax); return 1; } sbp->scale = AIR_CALLOC(num, double); if (!sbp->scale) { biffAddf(GAGE, "%s: couldn't alloc scale for %u", me, num); return 1; } sbp->num = num; if (uniform) { for (ii=0; iiscale[ii] = AIR_AFFINE(0, ii, num-1, scaleMin, scaleMax); } } else if (!optim) { double tau0, tau1, tau; tau0 = gageTauOfSig(scaleMin); tau1 = gageTauOfSig(scaleMax); for (ii=0; iiscale[ii] = gageSigOfTau(tau); } } else { unsigned int sigmax; sigmax = AIR_CAST(unsigned int, scaleMax); if (!( 0 == scaleMin && sigmax == scaleMax )) { biffAddf(GAGE, "%s: range [%g,%g] not [0,N] w/ integral N", me, scaleMin, scaleMax); return 1; } if (gageOptimSigSet(sbp->scale, num, sigmax)) { biffAddf(GAGE, "%s: trouble w/ optimal sigmas", me); return 1; } } if (sbp->verbose > 1) { fprintf(stderr, "%s: [%g,%g] with %u samples (uniform=%d, optim=%d):\n", me, scaleMin, scaleMax, num, !!uniform, !!optim); for (ii=0; iiscale[ii] - sbp->scale[ii-1], gageTauOfSig(sbp->scale[ii]) - gageTauOfSig(sbp->scale[ii-1])); } fprintf(stderr, "%s: scale[%02u]=%g%s\t tau=%g\n", me, ii, sbp->scale[ii], !sbp->scale[ii] ? " " : "", gageTauOfSig(sbp->scale[ii])); } } return 0; } int gageStackBlurParmKernelSet(gageStackBlurParm *sbp, const NrrdKernelSpec *kspec, int renormalize) { static const char me[]="gageStackBlurParmKernelSet"; if (!( sbp && kspec )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } nrrdKernelSpecNix(sbp->kspec); sbp->kspec = nrrdKernelSpecCopy(kspec); sbp->renormalize = renormalize; return 0; } int gageStackBlurParmSigmaMaxSet(gageStackBlurParm *sbp, double sigmaMax) { static const char me[]="gageStackBlurParmSigmaMaxSet"; if (!sbp) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!sigmaMax > 0) { biffAddf(GAGE, "%s: given sigmaMax %g not > 0", me, sigmaMax); return 1; } sbp->sigmaMax = sigmaMax; return 0; } int gageStackBlurParmBoundarySet(gageStackBlurParm *sbp, int boundary, double padValue) { static const char me[]="gageStackBlurParmBoundarySet"; if (!sbp) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(nrrdBoundary, boundary)) { biffAddf(GAGE, "%s: %d not a known %s", me, boundary, nrrdBoundary->name); return 1; } if (nrrdBoundaryPad == boundary && !AIR_EXISTS(padValue)) { biffAddf(GAGE, "%s: want boundary %s but padValue %g doesn't exist", me, airEnumStr(nrrdBoundary, boundary), padValue); return 1; } sbp->boundary = boundary; sbp->padValue = padValue; return 0; } int gageStackBlurParmVerboseSet(gageStackBlurParm *sbp, int verbose) { static const char me[]="gageStackBlurParmVerboseSet"; if (!sbp) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } sbp->verbose = verbose; return 0; } int gageStackBlurParmOneDimSet(gageStackBlurParm *sbp, int oneDim) { static const char me[]="gageStackBlurParmOneDimSet"; if (!sbp) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } sbp->oneDim = oneDim; return 0; } int gageStackBlurParmCheck(gageStackBlurParm *sbp) { static const char me[]="gageStackBlurParmCheck"; unsigned int ii; if (!sbp) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!( sbp->scale && sbp->kspec )) { biffAddf(GAGE, "%s: scale and kernel aren't set", me); return 1; } if (!( sbp->num >= 2)) { biffAddf(GAGE, "%s: need num >= 2, not %u", me, sbp->num); return 1; } for (ii=0; iinum; ii++) { if (!AIR_EXISTS(sbp->scale[ii])) { biffAddf(GAGE, "%s: scale[%u] = %g doesn't exist", me, ii, sbp->scale[ii]); return 1; } if (ii) { if (!( sbp->scale[ii-1] < sbp->scale[ii] )) { biffAddf(GAGE, "%s: scale[%u] = %g not < scale[%u] = %g", me, ii, sbp->scale[ii-1], ii+1, sbp->scale[ii]); return 1; } } } if (airEnumValCheck(nrrdBoundary, sbp->boundary)) { biffAddf(GAGE, "%s: %d not a known %s", me, sbp->boundary, nrrdBoundary->name); return 1; } return 0; } static int _checkNrrd(Nrrd *const nblur[], const Nrrd *const ncheck[], unsigned int blNum, int checking, const Nrrd *nin, const gageKind *kind) { static const char me[]="_checkNrrd"; unsigned int blIdx; for (blIdx=0; blIdxbaseDim != nin->dim) { biffAddf(GAGE, "%s: need nin->dim %u (not %u) with baseDim %u", me, 3 + kind->baseDim, nin->dim, kind->baseDim); return 1; } return 0; } #define KVP_NUM 6 static const char _blurKey[KVP_NUM][AIR_STRLEN_LARGE] = {/* 0 */ "gageStackBlur", /* 1 */ "scale", /* 2 */ "kernel", /* 3 */ "boundary", /* 4 */ "renormalize", /* 5 */ "onedim" /* (6 == KVP_NUM) */}; typedef struct { char val[KVP_NUM][AIR_STRLEN_LARGE]; } blurVal_t; static blurVal_t * _blurValAlloc(airArray *mop, gageStackBlurParm *sbp) { static const char me[]="_blurValAlloc"; blurVal_t *blurVal; unsigned int blIdx; blurVal = AIR_CAST(blurVal_t *, calloc(sbp->num, sizeof(blurVal_t))); if (!blurVal) { biffAddf(GAGE, "%s: couldn't alloc blurVal for %u", me, sbp->num); return NULL; } for (blIdx=0; blIdxnum; blIdx++) { sbp->kspec->parm[0] = sbp->scale[blIdx]; sprintf(blurVal[blIdx].val[0], "true"); sprintf(blurVal[blIdx].val[1], "%g", sbp->scale[blIdx]); nrrdKernelSpecSprint(blurVal[blIdx].val[2], sbp->kspec); sprintf(blurVal[blIdx].val[3], "%s", airEnumStr(nrrdBoundary, sbp->boundary)); sprintf(blurVal[blIdx].val[4], "%s", sbp->renormalize ? "true" : "false"); sprintf(blurVal[blIdx].val[5], "%s", sbp->oneDim ? "true" : "false"); } airMopAdd(mop, blurVal, airFree, airMopAlways); return blurVal; } /* ** little helper function to do pre-blurring of a given nrrd ** of the sort that might be useful for scale-space gage use ** ** nblur has to already be allocated for "blNum" Nrrd*s, AND, they all ** have to point to valid (possibly empty) Nrrds, so they can hold the ** results of blurring */ int gageStackBlur(Nrrd *const nblur[], gageStackBlurParm *sbp, const Nrrd *nin, const gageKind *kind) { static const char me[]="gageStackBlur"; unsigned int blIdx, axi; NrrdResampleContext *rsmc; blurVal_t *blurVal; airArray *mop; int E, iterative, rsmpType; Nrrd *niter; double timeStepMax, /* max length of diffusion time allowed per blur, as determined by sbp->sigmaMax */ timeDone, /* amount of diffusion time just applied */ timeLeft; /* amount of diffusion time left to do */ if (!(nblur && sbp && nin && kind)) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); if (gageStackBlurParmCheck(sbp) || _checkNrrd(nblur, NULL, sbp->num, AIR_FALSE, nin, kind) || (!( blurVal = _blurValAlloc(mop, sbp) )) ) { biffAddf(GAGE, "%s: problem", me); airMopError(mop); return 1; } rsmc = nrrdResampleContextNew(); airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways); if (nrrdKernelDiscreteGaussian == sbp->kspec->kernel) { iterative = AIR_TRUE; /* we don't want to lose precision when iterating */ rsmpType = nrrdResample_nt; } else { iterative = AIR_FALSE; rsmpType = nrrdTypeDefault; } /* may or may not be needed for iterative diffusion */ niter = nrrdNew(); airMopAdd(mop, niter, (airMopper)nrrdNuke, airMopAlways); E = 0; if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, nrrdDefaultCenter); /* the input for the first scale is indeed nin, regardless of iterative */ if (!E) E |= nrrdResampleInputSet(rsmc, nin); if (kind->baseDim) { unsigned int bai; for (bai=0; baibaseDim; bai++) { if (!E) E |= nrrdResampleKernelSet(rsmc, bai, NULL, NULL); } } for (axi=0; axi<3; axi++) { if (!E) E |= nrrdResampleSamplesSet(rsmc, kind->baseDim + axi, nin->axis[kind->baseDim + axi].size); if (!E) E |= nrrdResampleRangeFullSet(rsmc, kind->baseDim + axi); } if (!E) E |= nrrdResampleBoundarySet(rsmc, sbp->boundary); if (!E) E |= nrrdResampleTypeOutSet(rsmc, rsmpType); if (!E) E |= nrrdResampleClampSet(rsmc, AIR_TRUE); /* probably moot */ if (!E) E |= nrrdResampleRenormalizeSet(rsmc, sbp->renormalize); if (E) { biffAddf(GAGE, "%s: trouble setting up resampling", me); airMopError(mop); return 1; } timeDone = 0; timeStepMax = (sbp->sigmaMax)*(sbp->sigmaMax); for (blIdx=0; blIdxnum; blIdx++) { unsigned int kvpIdx; if (sbp->verbose) { fprintf(stderr, "%s: . . . blurring %u / %u (scale %g) . . . ", me, blIdx, sbp->num, sbp->scale[blIdx]); fflush(stderr); } if (iterative) { double timeNow = sbp->scale[blIdx]*sbp->scale[blIdx]; unsigned int passIdx = 0; timeLeft = timeNow - timeDone; if (sbp->verbose) { fprintf(stderr, "\n"); fprintf(stderr, "%s: scale %g == time %g;\n" " timeLeft %g = %g - %g\n", me, sbp->scale[blIdx], timeNow, timeLeft, timeNow, timeDone); if (timeLeft > timeStepMax) { fprintf(stderr, "%s: diffusing for time %g in steps of %g\n", me, timeLeft, timeStepMax); } fflush(stderr); } do { double timeDo; if (blIdx || passIdx) { /* either we're past the first scale (blIdx >= 1), or (unlikely) we're on the first scale but after the first pass of a multi-pass blurring, so we have to feed the previous result back in as input. AND: the way that niter is being used is very sneaky, and probably too clever: the resampling happens in multiple passes, among buffers internal to nrrdResample; so its okay have the output and input nrrds be the same: they're never used at the same time. */ if (!E) E |= nrrdResampleInputSet(rsmc, niter); } timeDo = (timeLeft > timeStepMax ? timeStepMax : timeLeft); sbp->kspec->parm[0] = sqrt(timeDo); for (axi=0; axi<3; axi++) { if (!sbp->oneDim || !axi) { /* we set the blurring kernel on this axis if we are NOT doing oneDim, or, we are, but this is axi == 0 */ if (!E) E |= nrrdResampleKernelSet(rsmc, kind->baseDim + axi, sbp->kspec->kernel, sbp->kspec->parm); } else { /* what to do with oneDom on axi 1, 2 */ /* you might think that we should just do no resampling at all on this axis, but that would undermine the in==out==niter trick described above; and produce the mysterious behavior that the second scale-space volume is all 0.0 */ double boxparm[NRRD_KERNEL_PARMS_NUM] = {1.0}; if (!E) E |= nrrdResampleKernelSet(rsmc, kind->baseDim + axi, nrrdKernelBox, boxparm); } } if (sbp->verbose) { fprintf(stderr, " pass %u (timeLeft=%g => time=%g, sigma=%g) ...\n", passIdx, timeLeft, timeDo, sbp->kspec->parm[0]); } if (!E) E |= nrrdResampleExecute(rsmc, niter); /* for debugging problem with getting zero output if (!E) { NrrdRange *nrange; nrange = nrrdRangeNewSet(niter, AIR_FALSE); fprintf(stderr, "%s: min/max = %g/%g\n", me, nrange->min, nrange->max); if (!nrange->min || !nrange->max) { fprintf(stderr, "%s: what? zero zero\n", me); biffAddf(GAGE, "%s: no good", me); airMopError(mop); return 1; } } */ timeLeft -= timeDo; passIdx++; } while (!E && timeLeft > 0.0); /* at this point we have to replicate the behavior of the last stage of resampling (e.g. _nrrdResampleOutputUpdate in nrrd/resampleContext.c), since we've gently hijacked the resampling to access the nrrdResample_t blurring result (for further blurring) */ if (!E) E |= nrrdCastClampRound(nblur[blIdx], niter, nin->type, AIR_TRUE, nrrdTypeIsIntegral[nin->type]); if (!E) E |= nrrdContentSet_va(nblur[blIdx], "blur", nin, ""); timeDone = timeNow; } else { /* do blurring in one shot */ sbp->kspec->parm[0] = sbp->scale[blIdx]; for (axi=0; axi<(sbp->oneDim ? 1 : 3); axi++) { if (!E) E |= nrrdResampleKernelSet(rsmc, kind->baseDim + axi, sbp->kspec->kernel, sbp->kspec->parm); } if (!E) E |= nrrdResampleExecute(rsmc, nblur[blIdx]); } if (E) { if (sbp->verbose) { fprintf(stderr, "problem!\n"); } biffAddf(GAGE, "%s: trouble w/ %u of %u (scale %g)", me, blIdx, sbp->num, sbp->scale[blIdx]); airMopError(mop); return 1; } if (sbp->verbose) { fprintf(stderr, " done.\n"); } /* add the KVPs to document how these were blurred */ for (kvpIdx=0; kvpIdxdataCheck should be non-zero: really need to check that ** the data values themselves are correct; its too dangerous to have ** this unchecked, because it means that experimental changes in ** volumes could mysteriously have no effect, because the cached ** pre-blurred volumes from the old data are being re-used */ int gageStackBlurCheck(const Nrrd *const nblur[], gageStackBlurParm *sbp, const Nrrd *nin, const gageKind *kind) { static const char me[]="gageStackBlurCheck"; gageShape *shapeOld, *shapeNew; blurVal_t *blurVal; airArray *mop; unsigned int blIdx, kvpIdx; if (!(nblur && sbp && nin && kind)) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); if (gageStackBlurParmCheck(sbp) || _checkNrrd(NULL, nblur, sbp->num, AIR_TRUE, nin, kind) || (!( blurVal = _blurValAlloc(mop, sbp) )) ) { biffAddf(GAGE, "%s: problem", me); airMopError(mop); return 1; } shapeNew = gageShapeNew(); airMopAdd(mop, shapeNew, (airMopper)gageShapeNix, airMopAlways); if (gageShapeSet(shapeNew, nin, kind->baseDim)) { biffAddf(GAGE, "%s: trouble setting up reference shape", me); airMopError(mop); return 1; } shapeOld = gageShapeNew(); airMopAdd(mop, shapeOld, (airMopper)gageShapeNix, airMopAlways); for (blIdx=0; blIdxnum; blIdx++) { if (nin->type != nblur[blIdx]->type) { biffAddf(GAGE, "%s: nblur[%u]->type %s != nin type %s\n", me, blIdx, airEnumStr(nrrdType, nblur[blIdx]->type), airEnumStr(nrrdType, nin->type)); airMopError(mop); return 1; } /* check to see if nblur[blIdx] is as expected */ if (gageShapeSet(shapeOld, nblur[blIdx], kind->baseDim) || !gageShapeEqual(shapeOld, "nblur", shapeNew, "nin")) { biffAddf(GAGE, "%s: trouble, or nblur[%u] shape != nin shape", me, blIdx); airMopError(mop); return 1; } /* see if the KVPs are already there */ for (kvpIdx=0; kvpIdxdataCheck) { double (*lup)(const void *, size_t), vin, vbl; size_t II, NN; if (!(0.0 == sbp->scale[0])) { biffAddf(GAGE, "%s: sorry, dataCheck w/ scale[0] %g " "!= 0.0 not implemented", me, sbp->scale[0]); airMopError(mop); return 1; /* so the non-zero return here will be acted upon as though there was a difference between the desired and the current stack, so things will be recomputed, which is conservative but costly */ } lup = nrrdDLookup[nin->type]; NN = nrrdElementNumber(nin); for (II=0; IIdata, II); vbl = lup(nblur[0]->data, II); if (vin != vbl) { biffAddf(GAGE, "%s: value[%u] in nin %g != in nblur[0] %g\n", me, AIR_CAST(unsigned int, II), vin, vbl); airMopError(mop); return 1; } } } airMopOkay(mop); return 0; } int gageStackBlurGet(Nrrd *const nblur[], int *recomputedP, gageStackBlurParm *sbp, const char *format, const Nrrd *nin, const gageKind *kind) { static const char me[]="gageStackBlurGet"; airArray *mop; int recompute; unsigned int ii; if (!( nblur && sbp && nin && kind )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } for (ii=0; iinum; ii++) { if (!nblur[ii]) { biffAddf(GAGE, "%s: nblur[%u] NULL", me, ii); return 1; } } if (gageStackBlurParmCheck(sbp)) { biffAddf(GAGE, "%s: trouble with blur parms", me); return 1; } mop = airMopNew(); /* set recompute flag */ if (!airStrlen(format)) { /* no info about files to load, obviously have to recompute */ if (sbp->verbose) { fprintf(stderr, "%s: no file info, must recompute blurrings\n", me); } recompute = AIR_TRUE; } else { char *fname, *suberr; int firstExists; FILE *file; /* do have info about files to load, but may fail in many ways */ fname = AIR_CALLOC(strlen(format) + AIR_STRLEN_SMALL, char); if (!fname) { biffAddf(GAGE, "%s: couldn't allocate fname", me); airMopError(mop); return 1; } airMopAdd(mop, fname, airFree, airMopAlways); /* see if we can get the first file (number 0) */ sprintf(fname, format, 0); firstExists = !!(file = fopen(fname, "r")); airFclose(file); if (!firstExists) { if (sbp->verbose) { fprintf(stderr, "%s: no file \"%s\"; will recompute blurrings\n", me, fname); } recompute = AIR_TRUE; } else if (nrrdLoadMulti(nblur, sbp->num, format, 0, NULL)) { airMopAdd(mop, suberr = biffGetDone(NRRD), airFree, airMopAlways); if (sbp->verbose) { fprintf(stderr, "%s: will recompute blurrings that couldn't be " "read:\n%s\n", me, suberr); } recompute = AIR_TRUE; } else if (gageStackBlurCheck(AIR_CAST(const Nrrd*const*, nblur), sbp, nin, kind)) { airMopAdd(mop, suberr = biffGetDone(GAGE), airFree, airMopAlways); if (sbp->verbose) { fprintf(stderr, "%s: will recompute blurrings (from \"%s\") " "that don't match:\n%s\n", me, format, suberr); } recompute = AIR_TRUE; } else { /* else precomputed blurrings could all be read, and did match */ if (sbp->verbose) { fprintf(stderr, "%s: will reuse %u %s pre-blurrings.\n", me, sbp->num, format); } recompute = AIR_FALSE; } } if (recompute) { if (gageStackBlur(nblur, sbp, nin, kind)) { biffAddf(GAGE, "%s: trouble computing blurrings", me); airMopError(mop); return 1; } } if (recomputedP) { *recomputedP = recompute; } airMopOkay(mop); return 0; } /* ******** gageStackBlurManage ** ** does the work of gageStackBlurGet and then some: ** allocates the array of Nrrds, allocates an array of doubles for scale, ** and saves output if recomputed */ int gageStackBlurManage(Nrrd ***nblurP, int *recomputedP, gageStackBlurParm *sbp, const char *format, int saveIfComputed, NrrdEncoding *enc, const Nrrd *nin, const gageKind *kind) { static const char me[]="gageStackBlurManage"; Nrrd **nblur; unsigned int ii; airArray *mop; int recomputed; if (!( nblurP && sbp && nin && kind )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } nblur = *nblurP = AIR_CALLOC(sbp->num, Nrrd *); if (!nblur) { biffAddf(GAGE, "%s: couldn't alloc %u Nrrd*s", me, sbp->num); return 1; } mop = airMopNew(); airMopAdd(mop, nblurP, (airMopper)airSetNull, airMopOnError); airMopAdd(mop, *nblurP, airFree, airMopOnError); for (ii=0; iinum; ii++) { nblur[ii] = nrrdNew(); airMopAdd(mop, nblur[ii], (airMopper)nrrdNuke, airMopOnError); } if (gageStackBlurGet(nblur, &recomputed, sbp, format, nin, kind)) { biffAddf(GAGE, "%s: trouble getting nblur", me); airMopError(mop); return 1; } if (recomputedP) { *recomputedP = recomputed; } if (recomputed && format && saveIfComputed) { NrrdIoState *nio; int E; E = 0; if (enc) { if (!enc->available()) { biffAddf(GAGE, "%s: requested %s encoding which is not " "available in this build", me, enc->name); airMopError(mop); return 1; } nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); if (!E) E |= nrrdIoStateEncodingSet(nio, nrrdEncodingGzip); } else { nio = NULL; } if (!E) E |= nrrdSaveMulti(format, AIR_CAST(const Nrrd *const *, nblur), sbp->num, 0, nio); if (E) { biffMovef(GAGE, NRRD, "%s: trouble saving blurrings", me); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/gage/vecGage.c0000664000175000017500000005654312165631065017026 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" /* * highly inefficient computation of the imaginary part of complex * conjugate eigenvalues of a 3x3 non-symmetric matrix */ double gage_imaginary_part_eigenvalues(double *M ) { double A, B, C, scale, frob, m[9], _eval[3]; double beta, gamma; int roots; frob = ELL_3M_FROB(M); scale = frob > 10 ? 10.0/frob : 1.0; ELL_3M_SCALE(m, scale, M); /* ** from gordon with mathematica; these are the coefficients of the ** cubic polynomial in x: det(x*I - M). The full cubic is ** x^3 + A*x^2 + B*x + C. */ A = -m[0] - m[4] - m[8]; B = m[0]*m[4] - m[3]*m[1] + m[0]*m[8] - m[6]*m[2] + m[4]*m[8] - m[7]*m[5]; C = (m[6]*m[4] - m[3]*m[7])*m[2] + (m[0]*m[7] - m[6]*m[1])*m[5] + (m[3]*m[1] - m[0]*m[4])*m[8]; roots = ell_cubic(_eval, A, B, C, AIR_TRUE); if ( roots != ell_cubic_root_single ) return 0.; /* 2 complex conjuguate eigenvalues */ beta = A + _eval[0]; gamma = -C/_eval[0]; return sqrt( 4.*gamma - beta*beta ); } gageItemEntry _gageVecTable[GAGE_VEC_ITEM_MAX+1] = { /* enum value len, deriv, prereqs, parent item, parent index, needData */ {gageVecUnknown, 0, 0, {0}, 0, 0, AIR_FALSE}, {gageVecVector, 3, 0, {0}, 0, 0, AIR_FALSE}, {gageVecVector0, 1, 0, {gageVecVector}, gageVecVector, 0, AIR_FALSE}, {gageVecVector1, 1, 0, {gageVecVector}, gageVecVector, 1, AIR_FALSE}, {gageVecVector2, 1, 0, {gageVecVector}, gageVecVector, 2, AIR_FALSE}, {gageVecLength, 1, 0, {gageVecVector}, 0, 0, AIR_FALSE}, {gageVecNormalized, 3, 0, {gageVecVector, gageVecLength}, 0, 0, AIR_FALSE}, {gageVecJacobian, 9, 1, {0}, 0, 0, AIR_FALSE}, {gageVecStrain, 9, 1, {gageVecJacobian}, 0, 0, AIR_FALSE}, {gageVecDivergence, 1, 1, {gageVecJacobian}, 0, 0, AIR_FALSE}, {gageVecCurl, 3, 1, {gageVecJacobian}, 0, 0, AIR_FALSE}, {gageVecCurlNorm, 1, 1, {gageVecCurl}, 0, 0, AIR_FALSE}, {gageVecHelicity, 1, 1, {gageVecVector, gageVecCurl}, 0, 0, AIR_FALSE}, {gageVecNormHelicity, 1, 1, {gageVecNormalized, gageVecCurl}, 0, 0, AIR_FALSE}, {gageVecSOmega, 9, 1, {gageVecJacobian,gageVecStrain}, 0, 0, AIR_FALSE}, {gageVecLambda2, 1, 1, {gageVecSOmega}, 0, 0, AIR_FALSE}, {gageVecImaginaryPart, 1, 1, {gageVecJacobian}, 0, 0, AIR_FALSE}, {gageVecHessian, 27, 2, {0}, 0, 0, AIR_FALSE}, {gageVecDivGradient, 3, 1, {gageVecHessian}, 0, 0, AIR_FALSE}, {gageVecCurlGradient, 9, 2, {gageVecHessian}, 0, 0, AIR_FALSE}, {gageVecCurlNormGrad, 3, 2, {gageVecHessian, gageVecCurl}, 0, 0, AIR_FALSE}, {gageVecNCurlNormGrad, 3, 2, {gageVecCurlNormGrad}, 0, 0, AIR_FALSE}, {gageVecHelGradient, 3, 2, {gageVecVector, gageVecJacobian, gageVecCurl, gageVecCurlGradient}, 0, 0, AIR_FALSE}, {gageVecDirHelDeriv, 1, 2, {gageVecNormalized, gageVecHelGradient}, 0, 0, AIR_FALSE}, {gageVecProjHelGradient, 3, 2, {gageVecNormalized, gageVecHelGradient, gageVecDirHelDeriv}, 0, 0, AIR_FALSE}, /* HEY: these should change to sub-items!!! */ {gageVecGradient0, 3, 1, {gageVecJacobian}, 0, 0, AIR_FALSE}, {gageVecGradient1, 3, 1, {gageVecJacobian}, 0, 0, AIR_FALSE}, {gageVecGradient2, 3, 1, {gageVecJacobian}, 0, 0, AIR_FALSE}, {gageVecMultiGrad, 9, 1, {gageVecGradient0, gageVecGradient1, gageVecGradient2}, 0, 0, AIR_FALSE}, {gageVecMGFrob, 1, 1, {gageVecMultiGrad}, 0, 0, AIR_FALSE}, {gageVecMGEval, 3, 1, {gageVecMultiGrad}, 0, 0, AIR_FALSE}, {gageVecMGEvec, 9, 1, {gageVecMultiGrad, gageVecMGEval}, 0, 0, AIR_FALSE} }; void _gageVecFilter(gageContext *ctx, gagePerVolume *pvl) { char me[]="_gageVecFilter"; double *fw00, *fw11, *fw22, *vec, *jac, *hes; int fd; gageScl3PFilter_t *filter[5] = {NULL, gageScl3PFilter2, gageScl3PFilter4, gageScl3PFilter6, gageScl3PFilter8}; unsigned int valIdx; fd = 2*ctx->radius; vec = pvl->directAnswer[gageVecVector]; jac = pvl->directAnswer[gageVecJacobian]; hes = pvl->directAnswer[gageVecHessian]; if (!ctx->parm.k3pack) { fprintf(stderr, "!%s: sorry, 6pack filtering not implemented\n", me); return; } fw00 = ctx->fw + fd*3*gageKernel00; fw11 = ctx->fw + fd*3*gageKernel11; fw22 = ctx->fw + fd*3*gageKernel22; /* perform the filtering */ if (fd <= 8) { for (valIdx=0; valIdx<3; valIdx++) { filter[ctx->radius](ctx->shape, pvl->iv3 + valIdx*fd*fd*fd, pvl->iv2 + valIdx*fd*fd, pvl->iv1 + valIdx*fd, fw00, fw11, fw22, vec + valIdx, jac + valIdx*3, hes + valIdx*9, pvl->needD); } } else { for (valIdx=0; valIdx<3; valIdx++) { gageScl3PFilterN(ctx->shape, fd, pvl->iv3 + valIdx*fd*fd*fd, pvl->iv2 + valIdx*fd*fd, pvl->iv1 + valIdx*fd, fw00, fw11, fw22, vec + valIdx, jac + valIdx*3, hes + valIdx*9, pvl->needD); } } return; } void _gageVecAnswer(gageContext *ctx, gagePerVolume *pvl) { char me[]="_gageVecAnswer"; double cmag, tmpMat[9], mgevec[9], mgeval[3]; double asym[9], tran[9], eval[3], tmpVec[3], norm; double *vecAns, *normAns, *jacAns, *strainAns, *somegaAns, *curlAns, *hesAns, *curlGradAns, *helGradAns, *dirHelDirAns, *curlnormgradAns; /* int asw; */ vecAns = pvl->directAnswer[gageVecVector]; normAns = pvl->directAnswer[gageVecNormalized]; jacAns = pvl->directAnswer[gageVecJacobian]; strainAns = pvl->directAnswer[gageVecStrain]; somegaAns = pvl->directAnswer[gageVecSOmega]; curlAns = pvl->directAnswer[gageVecCurl]; hesAns = pvl->directAnswer[gageVecHessian]; curlGradAns = pvl->directAnswer[gageVecCurlGradient]; curlnormgradAns = pvl->directAnswer[gageVecCurlNormGrad]; helGradAns = pvl->directAnswer[gageVecHelGradient]; dirHelDirAns = pvl->directAnswer[gageVecDirHelDeriv]; if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecVector)) { /* done if doV */ if (ctx->verbose) { fprintf(stderr, "vec = "); ell_3v_print_d(stderr, vecAns); } } /* done if doV if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecVector{0,1,2})) { } */ if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecLength)) { pvl->directAnswer[gageVecLength][0] = ELL_3V_LEN(vecAns); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecNormalized)) { if (pvl->directAnswer[gageVecLength][0]) { ELL_3V_SCALE(normAns, 1.0/pvl->directAnswer[gageVecLength][0], vecAns); } else { ELL_3V_COPY(normAns, gageZeroNormal); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecJacobian)) { /* done if doD1 */ /* 0:dv_x/dx 1:dv_x/dy 2:dv_x/dz 3:dv_y/dx 4:dv_y/dy 5:dv_y/dz 6:dv_z/dx 7:dv_z/dy 8:dv_z/dz */ if (ctx->verbose) { fprintf(stderr, "%s: jac = \n", me); ell_3m_print_d(stderr, jacAns); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecDivergence)) { pvl->directAnswer[gageVecDivergence][0] = jacAns[0] + jacAns[4] + jacAns[8]; if (ctx->verbose) { fprintf(stderr, "%s: div = %g + %g + %g = %g\n", me, jacAns[0], jacAns[4], jacAns[8], pvl->directAnswer[gageVecDivergence][0]); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurl)) { ELL_3V_SET(curlAns, jacAns[7] - jacAns[5], jacAns[2] - jacAns[6], jacAns[3] - jacAns[1]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurlNorm)) { pvl->directAnswer[gageVecCurlNorm][0] = ELL_3V_LEN(curlAns); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecHelicity)) { pvl->directAnswer[gageVecHelicity][0] = ELL_3V_DOT(vecAns, curlAns); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecNormHelicity)) { cmag = ELL_3V_LEN(curlAns); pvl->directAnswer[gageVecNormHelicity][0] = cmag ? ELL_3V_DOT(normAns, curlAns)/cmag : 0; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecStrain)) { ELL_3M_TRANSPOSE(tran, jacAns); /* symmetric part */ ELL_3M_SCALE_ADD2(strainAns, 0.5, jacAns, 0.5, tran); /* nested these ifs to make dependency explicit to the compiler */ if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecSOmega)) { /* antisymmetric part */ ELL_3M_SCALE_ADD2(asym, 0.5, jacAns, -0.5, tran); /* square symmetric part */ ELL_3M_MUL(tmpMat, strainAns, strainAns); ELL_3M_COPY(somegaAns, tmpMat); /* square antisymmetric part */ ELL_3M_MUL(tmpMat, asym, asym); /* sum of both */ ELL_3M_ADD2(somegaAns, somegaAns, tmpMat); if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecLambda2)) { /* get eigenvalues in sorted order */ /* asw = */ ell_3m_eigenvalues_d(eval, somegaAns, AIR_TRUE); pvl->directAnswer[gageVecLambda2][0] = eval[1]; } } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecImaginaryPart)) { pvl->directAnswer[gageVecImaginaryPart][0] = gage_imaginary_part_eigenvalues(jacAns); } /* 2nd order vector derivative continued */ if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecHessian)) { /* done if doD2 */ /* the ordering is induced by the scalar hessian computation : 0:d2v_x/dxdx 1:d2v_x/dxdy 2:d2v_x/dxdz 3:d2v_x/dydx 4:d2v_x/dydy 5:d2v_x/dydz 6:d2v_x/dzdx 7:d2v_x/dzdy 8:d2v_x/dzdz 9:d2v_y/dxdx [. . .] [. . .] 24:dv2_z/dzdx 25:d2v_z/dzdy 26:d2v_z/dzdz */ if (ctx->verbose) { fprintf(stderr, "%s: hes = \n", me); ell_3m_print_d(stderr, hesAns); /* ?? */ } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecDivGradient)) { pvl->directAnswer[gageVecDivGradient][0] = hesAns[0] + hesAns[12] + hesAns[24]; pvl->directAnswer[gageVecDivGradient][1] = hesAns[1] + hesAns[13] + hesAns[25]; pvl->directAnswer[gageVecDivGradient][2] = hesAns[2] + hesAns[14] + hesAns[26]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurlGradient)) { pvl->directAnswer[gageVecCurlGradient][0] = hesAns[21]-hesAns[15]; pvl->directAnswer[gageVecCurlGradient][1] = hesAns[22]-hesAns[16]; pvl->directAnswer[gageVecCurlGradient][2] = hesAns[23]-hesAns[17]; pvl->directAnswer[gageVecCurlGradient][3] = hesAns[ 6]-hesAns[18]; pvl->directAnswer[gageVecCurlGradient][4] = hesAns[ 7]-hesAns[19]; pvl->directAnswer[gageVecCurlGradient][5] = hesAns[ 8]-hesAns[20]; pvl->directAnswer[gageVecCurlGradient][6] = hesAns[ 9]-hesAns[ 1]; pvl->directAnswer[gageVecCurlGradient][7] = hesAns[10]-hesAns[ 2]; pvl->directAnswer[gageVecCurlGradient][8] = hesAns[11]-hesAns[ 3]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurlNormGrad)) { norm = 1./ELL_3V_LEN(curlAns); tmpVec[0] = hesAns[21] - hesAns[15]; tmpVec[1] = hesAns[ 6] - hesAns[18]; tmpVec[2] = hesAns[ 9] - hesAns[ 3]; pvl->directAnswer[gageVecCurlNormGrad][0] = norm*ELL_3V_DOT(tmpVec, curlAns); tmpVec[0] = hesAns[22] - hesAns[16]; tmpVec[1] = hesAns[ 7] - hesAns[19]; tmpVec[2] = hesAns[10] - hesAns[ 4]; pvl->directAnswer[gageVecCurlNormGrad][1]= norm*ELL_3V_DOT(tmpVec, curlAns); tmpVec[0] = hesAns[23] - hesAns[17]; tmpVec[1] = hesAns[ 8] - hesAns[20]; tmpVec[2] = hesAns[11] - hesAns[ 5]; pvl->directAnswer[gageVecCurlNormGrad][2]= norm*ELL_3V_DOT(tmpVec, curlAns); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecNCurlNormGrad)) { norm = 1./ELL_3V_LEN(curlnormgradAns); ELL_3V_SCALE(pvl->directAnswer[gageVecNCurlNormGrad], norm, pvl->directAnswer[gageVecCurlNormGrad]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecHelGradient)) { pvl->directAnswer[gageVecHelGradient][0] = jacAns[0]*curlAns[0]+ jacAns[3]*curlAns[1]+ jacAns[6]*curlAns[2]+ curlGradAns[0]*vecAns[0]+ curlGradAns[3]*vecAns[1]+ curlGradAns[6]*vecAns[2]; pvl->directAnswer[gageVecHelGradient][1] = jacAns[1]*curlAns[0]+ jacAns[4]*curlAns[1]+ jacAns[7]*curlAns[2]+ curlGradAns[1]*vecAns[0]+ curlGradAns[4]*vecAns[1]+ curlGradAns[7]*vecAns[2]; pvl->directAnswer[gageVecHelGradient][0] = jacAns[2]*curlAns[0]+ jacAns[5]*curlAns[1]+ jacAns[8]*curlAns[2]+ curlGradAns[2]*vecAns[0]+ curlGradAns[5]*vecAns[1]+ curlGradAns[8]*vecAns[2]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecDirHelDeriv)) { pvl->directAnswer[gageVecDirHelDeriv][0] = ELL_3V_DOT(normAns, helGradAns); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecProjHelGradient)) { pvl->directAnswer[gageVecDirHelDeriv][0] = helGradAns[0]-dirHelDirAns[0]*normAns[0]; pvl->directAnswer[gageVecDirHelDeriv][1] = helGradAns[1]-dirHelDirAns[0]*normAns[1]; pvl->directAnswer[gageVecDirHelDeriv][2] = helGradAns[2]-dirHelDirAns[0]*normAns[2]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecGradient0)) { ELL_3V_SET(pvl->directAnswer[gageVecGradient0], jacAns[0], jacAns[1], jacAns[2]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecGradient1)) { ELL_3V_SET(pvl->directAnswer[gageVecGradient1], jacAns[3], jacAns[4], jacAns[5]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecGradient2)) { ELL_3V_SET(pvl->directAnswer[gageVecGradient2], jacAns[6], jacAns[7], jacAns[8]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMultiGrad)) { ELL_3M_IDENTITY_SET(pvl->directAnswer[gageVecMultiGrad]); ELL_3MV_OUTER_INCR(pvl->directAnswer[gageVecMultiGrad], pvl->directAnswer[gageVecGradient0], pvl->directAnswer[gageVecGradient0]); ELL_3MV_OUTER_INCR(pvl->directAnswer[gageVecMultiGrad], pvl->directAnswer[gageVecGradient1], pvl->directAnswer[gageVecGradient1]); ELL_3MV_OUTER_INCR(pvl->directAnswer[gageVecMultiGrad], pvl->directAnswer[gageVecGradient2], pvl->directAnswer[gageVecGradient2]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMGFrob)) { pvl->directAnswer[gageVecMGFrob][0] = ELL_3M_FROB(pvl->directAnswer[gageVecMultiGrad]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMGEval)) { ELL_3M_COPY(tmpMat, pvl->directAnswer[gageVecMultiGrad]); /* HEY: look at the return value for root multiplicity? */ ell_3m_eigensolve_d(mgeval, mgevec, tmpMat, AIR_TRUE); ELL_3V_COPY(pvl->directAnswer[gageVecMGEval], mgeval); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMGEvec)) { ELL_3M_COPY(pvl->directAnswer[gageVecMGEvec], mgevec); } return; } const char * _gageVecStr[] = { "(unknown gageVec)", "vector", "vector0", "vector1", "vector2", "length", "normalized", "Jacobian", "strain", "divergence", "curl", "curl norm", "helicity", "normalized helicity", "SOmega", "lambda2", "imag", "vector hessian", "div gradient", "curl gradient", "curl norm gradient", "normalized curl norm gradient", "helicity gradient", "directional helicity derivative", "projected helicity gradient", "gradient0", "gradient1", "gradient2", "multigrad", "frob(multigrad)", "multigrad eigenvalues", "multigrad eigenvectors", }; const char * _gageVecDesc[] = { "unknown gageVec query", "component-wise-interpolated vector", "vector[0]", "vector[1]", "vector[2]", "length of vector", "normalized vector", "3x3 Jacobian", "rate-of-strain tensor", "divergence", "curl", "curl magnitude", "helicity: dot(vector,curl)", "normalized helicity", "S squared plus Omega squared for vortex characterization", "lambda2 value of SOmega", "imaginary part of complex-conjugate eigenvalues of Jacobian", "3x3x3 second-order vector derivative", "gradient of divergence", "3x3 derivative of curl", "gradient of curl norm", "normalized gradient of curl norm", "gradient of helicity", "directional derivative of helicity along flow", "projection of the helicity gradient onto plane orthogonal to flow", "gradient of 1st component of vector", "gradient of 2nd component of vector", "gradient of 3rd component of vector", "multi-gradient: sum of outer products of gradients", "frob norm of multi-gradient", "eigenvalues of multi-gradient", "eigenvectors of multi-gradient" }; const int _gageVecVal[] = { gageVecUnknown, gageVecVector, gageVecVector0, gageVecVector1, gageVecVector2, gageVecLength, gageVecNormalized, gageVecJacobian, gageVecStrain, gageVecDivergence, gageVecCurl, gageVecCurlNorm, gageVecHelicity, gageVecNormHelicity, gageVecSOmega, gageVecLambda2, gageVecImaginaryPart, gageVecHessian, gageVecDivGradient, gageVecCurlGradient, gageVecCurlNormGrad, gageVecNCurlNormGrad, gageVecHelGradient, gageVecDirHelDeriv, gageVecProjHelGradient, gageVecGradient0, gageVecGradient1, gageVecGradient2, gageVecMultiGrad, gageVecMGFrob, gageVecMGEval, gageVecMGEvec, }; #define GV_V gageVecVector #define GV_V0 gageVecVector0 #define GV_V1 gageVecVector1 #define GV_V2 gageVecVector2 #define GV_L gageVecLength #define GV_N gageVecNormalized #define GV_J gageVecJacobian #define GV_S gageVecStrain #define GV_D gageVecDivergence #define GV_C gageVecCurl #define GV_CM gageVecCurlNorm #define GV_H gageVecHelicity #define GV_NH gageVecNormHelicity #define GV_SO gageVecSOmega #define GV_LB gageVecLambda2 #define GV_IM gageVecImaginaryPart #define GV_VH gageVecHessian #define GV_DG gageVecDivGradient #define GV_CG gageVecCurlGradient #define GV_CNG gageVecCurlNormGrad #define GV_NCG gageVecNCurlNormGrad #define GV_HG gageVecHelGradient #define GV_DH gageVecDirHelDeriv #define GV_PH gageVecProjHelGradient #define GV_G0 gageVecGradient0 #define GV_G1 gageVecGradient1 #define GV_G2 gageVecGradient2 #define GV_MG gageVecMultiGrad #define GV_MF gageVecMGFrob #define GV_ML gageVecMGEval #define GV_MC gageVecMGEvec const char * _gageVecStrEqv[] = { "v", "vector", "vec", "v0", "vector0", "vec0", "v1", "vector1", "vec1", "v2", "vector2", "vec2", "l", "length", "len", "n", "normalized", "normalized vector", "jacobian", "jac", "j", "strain", "S", "divergence", "div", "d", "curl", "c", "curlnorm", "curl norm", "curl magnitude", "cm", "h", "hel", "hell", "helicity", "nh", "nhel", "normhel", "normhell", "normalized helicity", "SOmega", "lbda2", "lambda2", "imag", "imagpart", "vh", "vhes", "vhessian", "vector hessian", "dg", "divgrad", "div gradient", "cg", "curlgrad", "curlg", "curljac", "curl gradient", "cng", "curl norm gradient", "ncng", "norm curl norm gradient", "normalized curl norm gradient", "hg", "helg", "helgrad", "helicity gradient", "dirhelderiv", "dhd", "ddh", "directional helicity derivative", "phg", "projhel", "projhelgrad", "projected helicity gradient", "g0", "grad0", "gradient0", "g1", "grad1", "gradient1", "g2", "grad2", "gradient2", "mg", "multigrad", "mgfrob", "frob(multigrad)", "mgeval", "mg eval", "multigrad eigenvalues", "mgevec", "mg evec", "multigrad eigenvectors", "" }; const int _gageVecValEqv[] = { GV_V, GV_V, GV_V, GV_V0, GV_V0, GV_V0, GV_V1, GV_V1, GV_V1, GV_V2, GV_V2, GV_V2, GV_L, GV_L, GV_L, GV_N, GV_N, GV_N, GV_J, GV_J, GV_J, GV_S, GV_S, GV_D, GV_D, GV_D, GV_C, GV_C, GV_CM, GV_CM, GV_CM, GV_CM, GV_H, GV_H, GV_H, GV_H, GV_NH, GV_NH, GV_NH, GV_NH, GV_NH, GV_SO, GV_LB, GV_LB, GV_IM, GV_IM, GV_VH, GV_VH, GV_VH, GV_VH, GV_DG, GV_DG, GV_DG, GV_CG, GV_CG, GV_CG, GV_CG, GV_CG, GV_CNG, GV_CNG, GV_NCG, GV_NCG, GV_NCG, GV_HG, GV_HG, GV_HG, GV_HG, GV_DH, GV_DH, GV_DH, GV_DH, GV_PH, GV_PH, GV_PH, GV_PH, GV_G0, GV_G0, GV_G0, GV_G1, GV_G1, GV_G1, GV_G2, GV_G2, GV_G2, GV_MG, GV_MG, GV_MF, GV_MF, GV_ML, GV_ML, GV_ML, GV_MC, GV_MC, GV_MC }; const airEnum _gageVec = { "gageVec", GAGE_VEC_ITEM_MAX, _gageVecStr, _gageVecVal, _gageVecDesc, _gageVecStrEqv, _gageVecValEqv, AIR_FALSE }; const airEnum *const gageVec = &_gageVec; gageKind _gageKindVec = { AIR_FALSE, /* statically allocated */ "vector", &_gageVec, 1, /* baseDim */ 3, /* valLen */ GAGE_VEC_ITEM_MAX, _gageVecTable, _gageVecIv3Print, _gageVecFilter, _gageVecAnswer, NULL, NULL, NULL, NULL, NULL }; gageKind *const gageKindVec = &_gageKindVec; teem-1.11.0~svn6057/src/gage/defaultsGage.c0000664000175000017500000000536212202227307020041 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" const char * gageBiffKey = "gage"; int gageDefVerbose = 0; double gageDefGradMagCurvMin = 0.0; int gageDefRenormalize = AIR_FALSE; int gageDefCheckIntegrals = AIR_TRUE; int gageDefK3Pack = AIR_TRUE; int gageDefCurvNormalSide = 1; double gageDefKernelIntegralNearZero = 0.0001; int gageDefDefaultCenter = nrrdCenterCell; int gageDefStackUse = AIR_FALSE; /* GLK doesn't see any reasonable circumstance where this could be AIR_TRUE, since it significantly changes the behavior of gage */ int gageDefStackNormalizeRecon = AIR_FALSE; int gageDefStackNormalizeDeriv = AIR_FALSE; double gageDefStackNormalizeDerivBias = 0.0; /* NOTE: this value is really a property of nrrdKernelDiscreteGaussian and its implementation, so this information strictly speaking belongs there. The info is here in gage since it is the context of doing scale-space stacks that the info is typically needed and used */ double gageDefStackBlurSigmaMax = 6.0; int gageDefOrientationFromSpacing = AIR_FALSE; /* Before teem 1.10.0, gage behaved inconsistently: Derivatives were taken as if orientationFromSpacing were TRUE, the index space to world space mapping acted as if it were FALSE. Now, you have the choice and get consistent results in either case. */ int gageDefGenerateErrStr = AIR_TRUE; /* Before Teem 1.11, error strings were always sprintf, which can easily become a bottleneck in some situations, but this should still stay the default behavior */ int gageDefTwoDimZeroZ = AIR_FALSE; /* no way this can default to true */ teem-1.11.0~svn6057/src/gage/INFO.txt0000664000175000017500000002716310170343057016604 0ustar domibeldomibelwhat passes for documentation about gage: cheat sheet for using gage: -------------------------------------- 1) Create a context (ctx) with gageContextNew(). 2) For each of the volumes to probe, create a pervolume (pvl) with gagePerVolumeNew(), and pass this the volume that you want to probe. As the name implies, you use one gagePerVolume per volume. 3) Attach the pervolume(s) to the context with gagePerVolumeAttach(). 4) If you want verbose feedback, gageSet(ctx, gageParmVerbose, 1), which sets the verbosity in the context and all attached pervolumes 4) For each of the volumes, calculate the bitflag query according to the various measures you care about. If in a scalar volume you want value and gradient magnitude, then your query should be: (1<directAnswer[gageSclHessian]; gvec = pvl->directAnswer[gageSclGradVec]; norm = pvl->directAnswer[gageSclNormal]; nPerp = pvl->directAnswer[gageSclNPerp]; gten = pvl->directAnswer[gageSclGeomTens]; k1 = pvl->directAnswer[gageSclK1]; k2 = pvl->directAnswer[gageSclK2]; if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclValue)) { /* done if doV */ if (ctx->verbose > 2) { fprintf(stderr, "%s: val = % 15.7f\n", me, (double)(pvl->directAnswer[gageSclValue][0])); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGradVec)) { /* done if doD1 */ if (ctx->verbose > 2) { fprintf(stderr, "%s: gvec = ", me); ell_3v_print_d(stderr, gvec); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGradMag)) { /* this is the true value of gradient magnitude */ gmag = pvl->directAnswer[gageSclGradMag][0] = sqrt(ELL_3V_DOT(gvec, gvec)); } /* NB: it would seem that gageParmGradMagMin is completely ignored . . . */ if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclNormal)) { if (gmag) { ELL_3V_SCALE(norm, 1/gmag, gvec); /* polishing len = sqrt(ELL_3V_DOT(norm, norm)); ELL_3V_SCALE(norm, 1/len, norm); */ } else { ELL_3V_COPY(norm, gageZeroNormal); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclNProj)) { ELL_3MV_OUTER(pvl->directAnswer[gageSclNProj], norm, norm); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclNPerp)) { /* nPerp = I - outer(norm, norm) */ /* NB: this sets both nPerp and nProj */ ELL_3MV_OUTER(nProj, norm, norm); ELL_3M_SCALE(nPerp, -1, nProj); nPerp[0] += 1; nPerp[4] += 1; nPerp[8] += 1; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessian)) { /* done if doD2 */ if (ctx->verbose > 2) { fprintf(stderr, "%s: hess = \n", me); ell_3m_print_d(stderr, hess); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessianTen)) { pvl->directAnswer[gageSclHessianTen][0] = 1.0; TEN_M2T(pvl->directAnswer[gageSclHessianTen], pvl->directAnswer[gageSclHessian]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclLaplacian)) { pvl->directAnswer[gageSclLaplacian][0] = hess[0] + hess[4] + hess[8]; if (ctx->verbose > 2) { fprintf(stderr, "%s: lapl = %g + %g + %g = %g\n", me, hess[0], hess[4], hess[8], pvl->directAnswer[gageSclLaplacian][0]); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessFrob)) { pvl->directAnswer[gageSclHessFrob][0] = ELL_3M_FROB(hess); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessEval)) { if (ctx->parm.twoDimZeroZ) { ell_3m2sub_eigensolve_d(heval, hevec, hess); } else { /* HEY: look at the return value for root multiplicity? */ ell_3m_eigensolve_d(heval, hevec, hess, AIR_TRUE); } ELL_3V_COPY(pvl->directAnswer[gageSclHessEval], heval); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessEvec)) { ELL_3M_COPY(pvl->directAnswer[gageSclHessEvec], hevec); } #if 1 if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessRidgeness)) { double A, B, S; if (heval[1] >0 || heval[2]>0) { pvl->directAnswer[gageSclHessRidgeness][0] = 0; } else if (AIR_ABS(heval[1])<1e-10 || AIR_ABS(heval[2])<1e-10) { pvl->directAnswer[gageSclHessRidgeness][0] = 0; } else { double *ans; A = AIR_ABS(heval[1])/AIR_ABS(heval[2]); B = AIR_ABS(heval[0])/sqrt(AIR_ABS(heval[1]*heval[2])); S = sqrt(heval[0]*heval[0] + heval[1]*heval[1] + heval[2]*heval[2]); ans = pvl->directAnswer[gageSclHessRidgeness]; ans[0] = (1-exp(-A*A/(2*alpha*alpha))) * exp(-B*B/(2*beta*beta)) * (1-exp(-S*S/(2*gamma*gamma))) * exp(-2*cc*cc/(AIR_ABS(heval[1])*heval[2]*heval[2])); } } #else /* alternative implementation by GLK, based on directly following Frangi text. Only significant difference from above is a discontinuity at heval[0] = -heval[1] */ if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessRidgeness)) { double ev[4], tmp; ELL_3V_COPY(ev+1, heval); if (AIR_ABS(ev[2]) > AIR_ABS(ev[3])) { ELL_SWAP2(ev[2], ev[3], tmp); } if (AIR_ABS(ev[1]) > AIR_ABS(ev[2])) { ELL_SWAP2(ev[1], ev[2], tmp); } if (AIR_ABS(ev[2]) > AIR_ABS(ev[3])) { ELL_SWAP2(ev[2], ev[3], tmp); } if (ev[2] > 0 || ev[3] > 0) { pvl->directAnswer[gageSclHessRidgeness][0] = 0; } else { double a1, a2, a3, RB, RA, SS, fa, fb, fg, aa, bb, gg, frangi; a1 = AIR_ABS(ev[1]); a2 = AIR_ABS(ev[2]); a3 = AIR_ABS(ev[3]); RB = a1/sqrt(a2*a3); RA = a2/a3; SS = sqrt(a1*a1 + a2*a2 + a3*a3); aa = bb = 0.5; gg = 1; fa = 1 - exp(-RA*RA/(2*aa*aa)); fb = exp(-RB*RB/(2*bb*bb)); fg = 1 - exp(-SS*SS/(2*gg*gg)); frangi = fa*fb*fg; if (!AIR_EXISTS(frangi)) { frangi = 0.0; } pvl->directAnswer[gageSclHessRidgeness][0] = frangi; } } #endif if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessValleyness)) { double A, B, S; if (heval[0] <0 || heval[1]<0) { pvl->directAnswer[gageSclHessValleyness][0] = 0; } else if (AIR_ABS(heval[0])<1e-10 || AIR_ABS(heval[1])<1e-10) { pvl->directAnswer[gageSclHessValleyness][0] = 0; } else { double *ans; A = AIR_ABS(heval[1])/AIR_ABS(heval[0]); B = AIR_ABS(heval[2])/sqrt(AIR_ABS(heval[1]*heval[0])); S = sqrt(heval[0]*heval[0] + heval[1]*heval[1] + heval[2]*heval[2]); ans = pvl->directAnswer[gageSclHessValleyness]; ans[0] = (1-exp(-A*A/(2*alpha*alpha))) * exp(-B*B/(2*beta*beta)) * (1-exp(-S*S/(2*gamma*gamma))) * exp(-2*cc*cc/(AIR_ABS(heval[1])*heval[0]*heval[0])); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessDotPeakness)) { #define OSQT 0.57735026918962576451 double neval[3], hlen, pness, peak[3] = {-OSQT, -OSQT, -OSQT}; ELL_3V_NORM(neval, heval, hlen); if (!hlen) { pness = 0; } else { pness = ELL_3V_DOT(peak, neval); pness = AIR_AFFINE(-1, pness, 1, 0, 1); pvl->directAnswer[gageSclHessDotPeakness][0] = pness; } #undef OSQT } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessMode)) { pvl->directAnswer[gageSclHessMode][0] = airMode3_d(heval); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageScl2ndDD)) { ELL_3MV_MUL(tmpVec, hess, norm); pvl->directAnswer[gageScl2ndDD][0] = ELL_3V_DOT(norm, tmpVec); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGeomTens)) { if (gmag > ctx->parm.gradMagCurvMin) { /* parm.curvNormalSide applied here to determine the sense of the normal when doing all curvature calculations */ ELL_3M_SCALE(sHess, -(ctx->parm.curvNormalSide)/gmag, hess); /* gten = nPerp * sHess * nPerp */ ELL_3M_MUL(tmpMat, sHess, nPerp); ELL_3M_MUL(gten, nPerp, tmpMat); if (ctx->verbose > 2) { fprintf(stderr, "%s: gten: \n", me); ell_3m_print_d(stderr, gten); ELL_3MV_MUL(tmpVec, gten, norm); len = ELL_3V_LEN(tmpVec); fprintf(stderr, "%s: should be small: %30.15f\n", me, (double)len); ell_3v_perp_d(gp1, norm); ELL_3MV_MUL(tmpVec, gten, gp1); len = ELL_3V_LEN(tmpVec); fprintf(stderr, "%s: should be bigger: %30.15f\n", me, (double)len); ELL_3V_CROSS(gp2, gp1, norm); ELL_3MV_MUL(tmpVec, gten, gp2); len = ELL_3V_LEN(tmpVec); fprintf(stderr, "%s: should (also) be bigger: %30.15f\n", me, (double)len); } } else { ELL_3M_ZERO_SET(gten); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGeomTensTen)) { pvl->directAnswer[gageSclGeomTensTen][0] = 1.0; TEN_M2T(pvl->directAnswer[gageSclGeomTensTen], pvl->directAnswer[gageSclGeomTens]); TEN_T_SCALE(pvl->directAnswer[gageSclGeomTensTen], -1, pvl->directAnswer[gageSclGeomTensTen]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclTotalCurv)) { curv = pvl->directAnswer[gageSclTotalCurv][0] = ELL_3M_FROB(gten); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclShapeTrace)) { pvl->directAnswer[gageSclShapeTrace][0] = (curv ? ELL_3M_TRACE(gten)/curv : 0); } if ( (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclK1)) || (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclK2)) ){ double T, N, D; T = ELL_3M_TRACE(gten); N = curv; D = 2*N*N - T*T; /* if (D < -0.0000001) { fprintf(stderr, "%s: %g %g\n", me, T, N); fprintf(stderr, "%s: !!! D curv determinant % 22.10f < 0.0\n", me, D); fprintf(stderr, "%s: gten: \n", me); ell_3m_print_d(stderr, gten); } */ D = AIR_MAX(D, 0); D = sqrt(D); k1[0] = 0.5*(T + D); k2[0] = 0.5*(T - D); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclMeanCurv)) { pvl->directAnswer[gageSclMeanCurv][0] = (*k1 + *k2)/2; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGaussCurv)) { pvl->directAnswer[gageSclGaussCurv][0] = (*k1)*(*k2); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclShapeIndex)) { pvl->directAnswer[gageSclShapeIndex][0] = -(2/AIR_PI)*atan2(*k1 + *k2, *k1 - *k2); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclCurvDir1)) { /* HEY: this only works when K1, K2, 0 are all well mutually distinct, since these are the eigenvalues of the geometry tensor, and this code assumes that the eigenspaces are all one-dimensional */ ELL_3M_COPY(tmpMat, gten); ELL_3M_DIAG_SET(tmpMat, gten[0] - *k1, gten[4]- *k1, gten[8] - *k1); ell_3m_1d_nullspace_d(tmpVec, tmpMat); ELL_3V_COPY(pvl->directAnswer[gageSclCurvDir1], tmpVec); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclCurvDir2)) { /* HEY: this only works when K1, K2, 0 are all well mutually distinct, since these are the eigenvalues of the geometry tensor, and this code assumes that the eigenspaces are all one-dimensional */ ELL_3M_COPY(tmpMat, gten); ELL_3M_DIAG_SET(tmpMat, gten[0] - *k2, gten[4] - *k2, gten[8] - *k2); ell_3m_1d_nullspace_d(tmpVec, tmpMat); ELL_3V_COPY(pvl->directAnswer[gageSclCurvDir2], tmpVec); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclFlowlineCurv)) { if (gmag >= ctx->parm.gradMagCurvMin) { /* because of the gageSclGeomTens prerequisite, sHess, nPerp, and nProj are all already set */ /* ncTen = nPerp * sHess * nProj */ ELL_3M_MUL(tmpMat, sHess, nProj); ELL_3M_MUL(ncTen, nPerp, tmpMat); } else { ELL_3M_ZERO_SET(ncTen); } /* there used to be a wrong extra sqrt() here */ pvl->directAnswer[gageSclFlowlineCurv][0] = ELL_3M_FROB(ncTen); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclMedian)) { /* this item is currently a complete oddball in that it does not benefit from anything done in the "filter" stage, which is in fact a waste of time if the query consists only of this item */ fd = 2*ctx->radius; if (fd > FD_MEDIAN_MAX) { fprintf(stderr, "%s: PANIC: current filter diameter = %d " "> FD_MEDIAN_MAX = %d\n", me, fd, FD_MEDIAN_MAX); exit(1); } fw = ctx->fw + fd*3*gageKernel00; /* HEY: this needs some optimization help */ wghtSum = 0; nidx = 0; for (xi=0; xiiv3[nidx]; iv3wght[1 + 2*nidx] = fw[xi + 0*fd]*fw[yi + 1*fd]*fw[zi + 2*fd]; wghtSum += iv3wght[1 + 2*nidx]; nidx++; } } } qsort(iv3wght, fd*fd*fd, 2*sizeof(double), nrrdValCompare[nrrdTypeDouble]); wght = 0; for (nidx=0; nidx wghtSum/2) { break; } } pvl->directAnswer[gageSclMedian][0] = iv3wght[0 + 2*nidx]; } return; } #undef TEN_M2T #undef TEN_T_SCALE teem-1.11.0~svn6057/src/gage/scl3pfilterbody.c0000664000175000017500000000740012165631065020561 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*********** THIS IS A HACK !!! *********** THIS ISN'T REALLY A SOURCE FILE !!! *********** ITS JUST A MACRO (sorry) */ /* fw? + N*? | | | +- along which axis (0:x, 1:y, 2:z) | + what information (0:value, 1:1st deriv, 2:2nd deriv, ...) ivX: 3D cube cache of original volume values (its scanlines are along the "X" or fastest spatial axis) ivY: 2D square cache of intermediate filter results (its scanlines are along the "Y" or medium spatial axis) ivZ: 1D linear cache of intermediate filter results (it is a scanline along the "Z" or slowest spatial axis) */ #define DOT_N(ANS, a, b) \ for (T=0.0f,i=0; iItoWSubInvTransp, gvec); if (!doD2) return; /* x1y0z1 */ D1_N(hess[6],0,Z); /* h_xz */ hess[2] = hess[6]; /* x1y1 */ for (j=0; jItoWSubInvTransp, hess); ELL_3M_MUL(hess, matA, shape->ItoWSubInv); } #undef DOT_N #undef VL_N #undef D1_N #undef D2_N teem-1.11.0~svn6057/src/gage/sclfilter.c0000664000175000017500000002665612165631065017456 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" /* ** The functions gageScl3PFilter2, gageScl3PFilter4, and ** gageScl3PFilterN weren't really intended to be public, but they ** have become so because they implement the brains of the lowest ** level of dot-products needed to do the convolution-based ** reconstruction, and, to do the the transforms that affect gradient ** and hessian values. These functions are very useful for the ** implementation of other gageKinds, in which non-scalar values are ** filtered per-component. */ /* ** Teem trivia: "3P" stands for "3-pack", the kind of kernel set used (k00, ** k11, and k22). 6-pack filtering is still unimplemented. The name ** "3-pack" persists even with the possibility of higher derivatives, and ** what would be called 10-pack filtering (ability to specify and use all ** 10 kinds of spatial kernels) is also still unimplemented. */ #define X 0 #define Y 1 #define Z 2 void gageScl3PFilter2(gageShape *shape, double *ivX, double *ivY, double *ivZ, double *fw0, double *fw1, double *fw2, double *val, double *gvec, double *hess, const int *needD) { int doV, doD1, doD2; doV = needD[0]; doD1 = needD[1]; doD2 = needD[2]; /* fw? + 2*? | | | +- along which axis (0:x, 1:y, 2:z) | + what information (0:value, 1:1st deriv, 2:2nd deriv) ivX: 3D cube cache of original volume values (its scanlines are along the X axis) ivY: 2D square cache of intermediate filter results (its scanlines are along the Y axis) ivZ: 1D linear cache of intermediate filter results (it is a scanline along the Z axis) */ #define DOT_2(a, b) ((a)[0]*(b)[0] + (a)[1]*(b)[1]) #define VL_2(i, axis) DOT_2(fw0 + (axis)*2, iv##axis + i*2) #define D1_2(i, axis) DOT_2(fw1 + (axis)*2, iv##axis + i*2) #define D2_2(i, axis) DOT_2(fw2 + (axis)*2, iv##axis + i*2) /* x0 */ ivY[0] = VL_2(0,X); /* interpolate values of 0th scanline along X axis */ ivY[1] = VL_2(1,X); ivY[2] = VL_2(2,X); ivY[3] = VL_2(3,X); /* x0y0 */ ivZ[0] = VL_2(0,Y); ivZ[1] = VL_2(1,Y); /* x0y0z0 */ if (doV) { *val = VL_2(0,Z); /* f */ } if (!( doD1 || doD2 )) return; /* x0y0z1 */ if (doD1) { gvec[2] = D1_2(0,Z); /* g_z */ } if (doD2) { /* actually, there is no possible way in which it makes sense to try to measure a second derivative with only two samples, so all this "if (doD2)" code is basically bogus, but we'll keep it around for generality . . . */ /* x0y0z2 */ hess[8] = D2_2(0,Z); /* h_zz */ } /* x0y1 */ ivZ[0] = D1_2(0,Y); ivZ[1] = D1_2(1,Y); /* x0y1z0 */ if (doD1) { gvec[1] = VL_2(0,Z); /* g_y */ } if (doD2) { /* x0y1z1 */ hess[5] = hess[7] = D1_2(0,Z); /* h_yz */ /* x0y2 */ ivZ[0] = D2_2(0,Y); ivZ[1] = D2_2(1,Y); /* x0y2z0 */ hess[4] = VL_2(0,Z); /* h_yy */ } /* x1 */ ivY[0] = D1_2(0,X); ivY[1] = D1_2(1,X); ivY[2] = D1_2(2,X); ivY[3] = D1_2(3,X); /* x1y0 */ ivZ[0] = VL_2(0,Y); ivZ[1] = VL_2(1,Y); /* x1y0z0 */ if (doD1) { gvec[0] = VL_2(0,Z); /* g_x */ } ell_3mv_mul_d(gvec, shape->ItoWSubInvTransp, gvec); if (!doD2) return; /* x1y0z1 */ hess[2] = hess[6] = D1_2(0,Z); /* h_xz */ /* x1y1 */ ivZ[0] = D1_2(0,Y); ivZ[1] = D1_2(1,Y); /* x1y1z0 */ hess[1] = hess[3] = VL_2(0,Z); /* h_xy */ /* x2 */ ivY[0] = D2_2(0,X); ivY[1] = D2_2(1,X); ivY[2] = D2_2(2,X); ivY[3] = D2_2(3,X); /* x2y0 */ ivZ[0] = VL_2(0,Y); ivZ[1] = VL_2(1,Y); /* x2y0z0 */ hess[0] = VL_2(0,Z); /* h_xx */ if (1) { double matA[9]; ELL_3M_MUL(matA, shape->ItoWSubInvTransp, hess); ELL_3M_MUL(hess, matA, shape->ItoWSubInv); } return; } void gageScl3PFilter4(gageShape *shape, double *ivX, double *ivY, double *ivZ, double *fw0, double *fw1, double *fw2, double *val, double *gvec, double *hess, const int *needD) { int doV, doD1, doD2; doV = needD[0]; doD1 = needD[1]; doD2 = needD[2]; /* fw? + 4*? | | | +- along which axis (0:x, 1:y, 2:z) | + what information (0:value, 1:1st deriv, 2:2nd deriv) ivX: 3D cube cache of original volume values (its scanlines are along the X axis) ivY: 2D square cache of intermediate filter results (its scanlines are along the Y axis) ivZ: 1D linear cache of intermediate filter results (it is a scanline along the Z axis) */ #define DOT_4(a,b) ((a)[0]*(b)[0]+(a)[1]*(b)[1]+(a)[2]*(b)[2]+(a)[3]*(b)[3]) #define VL_4(i, axis) DOT_4(fw0 + (axis)*4, iv##axis + i*4) #define D1_4(i, axis) DOT_4(fw1 + (axis)*4, iv##axis + i*4) #define D2_4(i, axis) DOT_4(fw2 + (axis)*4, iv##axis + i*4) /* x0 */ ivY[ 0] = VL_4( 0,X); ivY[ 1] = VL_4( 1,X); ivY[ 2] = VL_4( 2,X); ivY[ 3] = VL_4( 3,X); ivY[ 4] = VL_4( 4,X); ivY[ 5] = VL_4( 5,X); ivY[ 6] = VL_4( 6,X); ivY[ 7] = VL_4( 7,X); ivY[ 8] = VL_4( 8,X); ivY[ 9] = VL_4( 9,X); ivY[10] = VL_4(10,X); ivY[11] = VL_4(11,X); ivY[12] = VL_4(12,X); ivY[13] = VL_4(13,X); ivY[14] = VL_4(14,X); ivY[15] = VL_4(15,X); /* */ /* x0y0 */ ivZ[ 0] = VL_4( 0,Y); ivZ[ 1] = VL_4( 1,Y); ivZ[ 2] = VL_4( 2,Y); ivZ[ 3] = VL_4( 3,Y); /* x0y0z0 */ if (doV) { *val = VL_4( 0,Z); /* f */ } if (!( doD1 || doD2 )) return; /* x0y0z1 */ if (doD1) { gvec[2] = D1_4( 0,Z); /* g_z */ } if (doD2) { /* x0y0z2 */ hess[8] = D2_4( 0,Z); /* h_zz */ } /* x0y1 */ ivZ[ 0] = D1_4( 0,Y); ivZ[ 1] = D1_4( 1,Y); ivZ[ 2] = D1_4( 2,Y); ivZ[ 3] = D1_4( 3,Y); /* x0y1z0 */ if (doD1) { gvec[1] = VL_4( 0,Z); /* g_y */ } if (doD2) { /* x0y1z1 */ hess[5] = hess[7] = D1_4( 0,Z); /* h_yz */ /* x0y2 */ ivZ[ 0] = D2_4( 0,Y); ivZ[ 1] = D2_4( 1,Y); ivZ[ 2] = D2_4( 2,Y); ivZ[ 3] = D2_4( 3,Y); /* x0y2z0 */ hess[4] = VL_4( 0,Z); /* h_yy */ } /* x1 */ ivY[ 0] = D1_4( 0,X); ivY[ 1] = D1_4( 1,X); ivY[ 2] = D1_4( 2,X); ivY[ 3] = D1_4( 3,X); ivY[ 4] = D1_4( 4,X); ivY[ 5] = D1_4( 5,X); ivY[ 6] = D1_4( 6,X); ivY[ 7] = D1_4( 7,X); ivY[ 8] = D1_4( 8,X); ivY[ 9] = D1_4( 9,X); ivY[10] = D1_4(10,X); ivY[11] = D1_4(11,X); ivY[12] = D1_4(12,X); ivY[13] = D1_4(13,X); ivY[14] = D1_4(14,X); ivY[15] = D1_4(15,X); /* x1y0 */ ivZ[ 0] = VL_4( 0,Y); ivZ[ 1] = VL_4( 1,Y); ivZ[ 2] = VL_4( 2,Y); ivZ[ 3] = VL_4( 3,Y); /* x1y0z0 */ if (doD1) { gvec[0] = VL_4( 0,Z); /* g_x */ } ell_3mv_mul_d(gvec, shape->ItoWSubInvTransp, gvec); if (!doD2) return; /* x1y0z1 */ hess[2] = hess[6] = D1_4( 0,Z); /* h_xz */ /* x1y1 */ ivZ[ 0] = D1_4( 0,Y); ivZ[ 1] = D1_4( 1,Y); ivZ[ 2] = D1_4( 2,Y); ivZ[ 3] = D1_4( 3,Y); /* x1y1z0 */ hess[1] = hess[3] = VL_4( 0,Z); /* h_xy */ /* x2 */ ivY[ 0] = D2_4( 0,X); ivY[ 1] = D2_4( 1,X); ivY[ 2] = D2_4( 2,X); ivY[ 3] = D2_4( 3,X); ivY[ 4] = D2_4( 4,X); ivY[ 5] = D2_4( 5,X); ivY[ 6] = D2_4( 6,X); ivY[ 7] = D2_4( 7,X); ivY[ 8] = D2_4( 8,X); ivY[ 9] = D2_4( 9,X); ivY[10] = D2_4(10,X); ivY[11] = D2_4(11,X); ivY[12] = D2_4(12,X); ivY[13] = D2_4(13,X); ivY[14] = D2_4(14,X); ivY[15] = D2_4(15,X); /* x2y0 */ ivZ[ 0] = VL_4( 0,Y); ivZ[ 1] = VL_4( 1,Y); ivZ[ 2] = VL_4( 2,Y); ivZ[ 3] = VL_4( 3,Y); /* x2y0z0 */ hess[0] = VL_4( 0,Z); /* h_xx */ if (1) { double matA[9]; ELL_3M_MUL(matA, shape->ItoWSubInvTransp, hess); ELL_3M_MUL(hess, matA, shape->ItoWSubInv); } return; } void gageScl3PFilter6(gageShape *shape, double *ivX, double *ivY, double *ivZ, double *fw0, double *fw1, double *fw2, double *val, double *gvec, double *hess, const int *needD) { int i, j; double T; int doV, doD1, doD2; doV = needD[0]; doD1 = needD[1]; doD2 = needD[2]; #define fd 6 #include "scl3pfilterbody.c" #undef fd return; } void gageScl3PFilter8(gageShape *shape, double *ivX, double *ivY, double *ivZ, double *fw0, double *fw1, double *fw2, double *val, double *gvec, double *hess, const int *needD) { int i, j; double T; int doV, doD1, doD2; doV = needD[0]; doD1 = needD[1]; doD2 = needD[2]; #define fd 8 #include "scl3pfilterbody.c" #undef fd return; } void gageScl3PFilterN(gageShape *shape, int fd, double *ivX, double *ivY, double *ivZ, double *fw0, double *fw1, double *fw2, double *val, double *gvec, double *hess, const int *needD) { int i, j; double T; int doV, doD1, doD2; doV = needD[0]; doD1 = needD[1]; doD2 = needD[2]; #include "scl3pfilterbody.c" return; } void _gageSclFilter(gageContext *ctx, gagePerVolume *pvl) { char me[]="_gageSclFilter"; int fd; double *fw00, *fw11, *fw22; gageScl3PFilter_t *filter[5] = {NULL, gageScl3PFilter2, gageScl3PFilter4, gageScl3PFilter6, gageScl3PFilter8}; fd = 2*ctx->radius; if (!ctx->parm.k3pack) { fprintf(stderr, "!%s: sorry, 6-pack filtering not implemented\n", me); return; } fw00 = ctx->fw + fd*3*gageKernel00; fw11 = ctx->fw + fd*3*gageKernel11; fw22 = ctx->fw + fd*3*gageKernel22; /* perform the filtering */ if (fd <= 8) { filter[ctx->radius](ctx->shape, pvl->iv3, pvl->iv2, pvl->iv1, fw00, fw11, fw22, pvl->directAnswer[gageSclValue], pvl->directAnswer[gageSclGradVec], pvl->directAnswer[gageSclHessian], pvl->needD); } else { gageScl3PFilterN(ctx->shape, fd, pvl->iv3, pvl->iv2, pvl->iv1, fw00, fw11, fw22, pvl->directAnswer[gageSclValue], pvl->directAnswer[gageSclGradVec], pvl->directAnswer[gageSclHessian], pvl->needD); } return; } #undef X #undef Y #undef Z teem-1.11.0~svn6057/src/gage/print.c0000664000175000017500000001512212165631065016605 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" void _gagePrint_off(FILE *file, gageContext *ctx) { int i, fd; unsigned int *off; fd = 2*ctx->radius; off = ctx->off; fprintf(file, "off[]:\n"); switch(fd) { case 2: fprintf(file, "% 6d % 6d\n", off[6], off[7]); fprintf(file, " % 6d % 6d\n\n", off[4], off[5]); fprintf(file, "% 6d % 6d\n", off[2], off[3]); fprintf(file, " % 6d % 6d\n", off[0], off[1]); break; case 4: for (i=3; i>=0; i--) { fprintf(file, "% 6d % 6d % 6d % 6d\n", off[12+16*i], off[13+16*i], off[14+16*i], off[15+16*i]); fprintf(file, " % 6d %c% 6d % 6d%c % 6d\n", off[ 8+16*i], (i==1||i==2)?'\\':' ', off[ 9+16*i], off[10+16*i], (i==1||i==2)?'\\':' ', off[11+16*i]); fprintf(file, " % 6d %c% 6d % 6d%c % 6d\n", off[ 4+16*i], (i==1||i==2)?'\\':' ', off[ 5+16*i], off[ 6+16*i], (i==1||i==2)?'\\':' ', off[ 7+16*i]); fprintf(file, " % 6d % 6d % 6d % 6d\n", off[ 0+16*i], off[ 1+16*i], off[ 2+16*i], off[ 3+16*i]); if (i) fprintf(file, "\n"); } break; default: for (i=0; i% 15.7f % 15.7f\n", \ (float)fw[0], (float)fw[1]) #define PRINT_4(NN,C) \ fw = fw##NN##C; \ fprintf(file, " --" #NN "-->% 15.7f % 15.7f % 15.7f % 15.7f\n", \ (float)fw[0], (float)fw[1], (float)fw[2], (float)fw[3]) #define PRINT_N(NN,C) \ fw = fw##NN##C; \ fprintf(file, " --" #NN "--> \n"); \ for (i=0; ineedK[gageKernel00]) { HOW(00,C); } \ if (ctx->needK[gageKernel10]) { HOW(10,C); } \ if (ctx->needK[gageKernel11]) { HOW(11,C); } \ if (ctx->needK[gageKernel20]) { HOW(20,C); } \ if (ctx->needK[gageKernel21]) { HOW(21,C); } \ if (ctx->needK[gageKernel22]) { HOW(22,C); } void _gagePrint_fslw(FILE *file, gageContext *ctx) { int i, fd; double *fslx, *fsly, *fslz, *fw, *fw000, *fw001, *fw002, *fw100, *fw101, *fw102, *fw110, *fw111, *fw112, *fw200, *fw201, *fw202, *fw210, *fw211, *fw212, *fw220, *fw221, *fw222; /* float *p; */ fd = 2*ctx->radius; fslx = ctx->fsl + fd*0; fsly = ctx->fsl + fd*1; fslz = ctx->fsl + fd*2; fw000 = ctx->fw + 0 + fd*(0 + 3*gageKernel00); fw001 = ctx->fw + 0 + fd*(1 + 3*gageKernel00); fw002 = ctx->fw + 0 + fd*(2 + 3*gageKernel00); fw100 = ctx->fw + 0 + fd*(0 + 3*gageKernel10); fw101 = ctx->fw + 0 + fd*(1 + 3*gageKernel10); fw102 = ctx->fw + 0 + fd*(2 + 3*gageKernel10); fw110 = ctx->fw + 0 + fd*(0 + 3*gageKernel11); fw111 = ctx->fw + 0 + fd*(1 + 3*gageKernel11); fw112 = ctx->fw + 0 + fd*(2 + 3*gageKernel11); fw200 = ctx->fw + 0 + fd*(0 + 3*gageKernel20); fw201 = ctx->fw + 0 + fd*(1 + 3*gageKernel20); fw202 = ctx->fw + 0 + fd*(2 + 3*gageKernel20); fw210 = ctx->fw + 0 + fd*(0 + 3*gageKernel21); fw211 = ctx->fw + 0 + fd*(1 + 3*gageKernel21); fw212 = ctx->fw + 0 + fd*(2 + 3*gageKernel21); fw220 = ctx->fw + 0 + fd*(0 + 3*gageKernel22); fw221 = ctx->fw + 0 + fd*(1 + 3*gageKernel22); fw222 = ctx->fw + 0 + fd*(2 + 3*gageKernel22); fprintf(file, "fsl -> fw: \n"); switch(fd) { case 2: fprintf(file, "x[]: % 15.7f % 15.7f\n", (float)fslx[0], (float)fslx[1]); PRINTALL(PRINT_2, 0); fprintf(file, "y[]: % 15.7f % 15.7f\n", (float)fsly[0], (float)fsly[1]); PRINTALL(PRINT_2, 1); fprintf(file, "z[]: % 15.7f % 15.7f\n", (float)fslz[0], (float)fslz[1]); PRINTALL(PRINT_2, 2); break; case 4: fprintf(file, "x[]: % 15.7f % 15.7f % 15.7f % 15.7f\n", (float)fslx[0], (float)fslx[1], (float)fslx[2], (float)fslx[3]); PRINTALL(PRINT_4, 0); fprintf(file, "y[]: % 15.7f % 15.7f % 15.7f % 15.7f\n", (float)fsly[0], (float)fsly[1], (float)fsly[2], (float)fsly[3]); PRINTALL(PRINT_4, 1); fprintf(file, "z[]: % 15.7f % 15.7f % 15.7f % 15.7f\n", (float)fslz[0], (float)fslz[1], (float)fslz[2], (float)fslz[3]); PRINTALL(PRINT_4, 2); break; default: fprintf(file, "x[]:\n"); for (i=0; iname); ii = kind->itemMax+1; do { ii--; if (GAGE_QUERY_ITEM_TEST(query, ii)) { fprintf(file, " %3d: %s\n", ii, airEnumStr(kind->enm, ii)); } } while (ii); } teem-1.11.0~svn6057/src/gage/privateGage.h0000664000175000017500000000560412165631065017720 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GAGE_PRIVATE_HAS_BEEN_INCLUDED #define GAGE_PRIVATE_HAS_BEEN_INCLUDED #ifdef __cplusplus extern "C" { #endif /* define ell_3v_PRINT ell_3v_print_d define ell_3m_PRINT ell_3m_print_d define ell_3v_PERP ell_3v_perp_d define nrrdLOOKUP nrrdDLookup define EVALN evalN_d / * NrrdKernel method * / */ /* the what to put in gctx->errStr when !generateErrStr */ #define _GAGE_NON_ERR_STR "(error)" /* shape.c */ extern int _gageShapeSet(const gageContext *ctx, gageShape *shape, const Nrrd *nin, unsigned int baseDim); /* ctx.c */ extern int _gageProbe(gageContext *ctx, double xi, double yi, double zi, double stackIdx); extern int _gageProbeSpace(gageContext *ctx, double xx, double yy, double zz, double ss, int indexSpace, int clamp); /* pvl.c */ extern gagePerVolume *_gagePerVolumeCopy(gagePerVolume *pvl, unsigned int fd); extern double *_gageAnswerPointer(const gageContext *ctx, gagePerVolume *pvl, int item); /* print.c */ extern void _gagePrint_off(FILE *, gageContext *ctx); extern void _gagePrint_fslw(FILE *, gageContext *ctx); /* filter.c */ extern int _gageLocationSet(gageContext *ctx, double x, double y, double z, double s); /* stack.c */ extern int _gageStackBaseIv3Fill(gageContext *ctx); /* sclprint.c */ extern void _gageSclIv3Print(FILE *, gageContext *ctx, gagePerVolume *pvl); /* sclfilter.c */ extern void _gageSclFilter(gageContext *ctx, gagePerVolume *pvl); /* sclanswer.c */ extern void _gageSclAnswer(gageContext *ctx, gagePerVolume *pvl); /* vecprint.c */ extern void _gageVecIv3Print(FILE *, gageContext *ctx, gagePerVolume *pvl); #ifdef __cplusplus } #endif #endif /* GAGE_PRIVATE_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/gage/st.c0000664000175000017500000002234312165631065016102 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" #define GAGE_CACHE_LEN 1013 unsigned int _gageHash(int x, int y, int z) { unsigned int h, g; unsigned char s[6]; int i; s[0] = x & 0xff; s[1] = (x >> 8) & 0xff; s[2] = y & 0xff; s[3] = (y >> 8) & 0xff; s[4] = z & 0xff; s[5] = (z >> 8) & 0xff; h = 0; for (i=0; i<=5; i++) { h = (h << 4) + s[i]; if ((g = h & 0xf0000000)) { h = h ^ (g >> 24); h = h ^ g; } } return h % GAGE_CACHE_LEN; } void _gageCacheProbe(gageContext *ctx, double *grad, int *cc, double *gc, int x, int y, int z) { int hi; hi = _gageHash(x, y, z); if ( (cc[3*hi + 0] == x) && (cc[3*hi + 1] == y) && (cc[3*hi + 2] == z) ) { /* cache hit */ ELL_3V_COPY(grad, gc + 3*hi); } else { /* cache miss */ cc[3*hi + 0] = x; cc[3*hi + 1] = y; cc[3*hi + 2] = z; gageProbe(ctx, x, y, z); ELL_3V_COPY(gc + 3*hi, grad); } return ; } /* ******** gageStructureTensor() ** ** Computes volume of structure tensors. Currently, only works on a scalar ** fields (for multi-scalar fields, just add structure tensors from each ** component scalar), and only uses the B-spline kernel for differentiation ** and derivative blurring. ** ** Note, if you want to use dsmp > 1, its your responsibility to give ** an appropriate iScale > 1, so that you don't undersample. */ int gageStructureTensor(Nrrd *nout, const Nrrd *nin, int dScale, int iScale, int dsmp) { static const char me[]="gageStructureTensor"; NrrdKernelSpec *gk0, *gk1, *ik0; int E, rad, diam, osx, osy, osz, oxi, oyi, ozi, _ixi, _iyi, _izi, ixi, iyi, izi, wi, *coordCache; gageContext *ctx; gageQuery query; gagePerVolume *pvl; airArray *mop; double *grad, *ixw, *iyw, *izw, wght, sten[6], *gradCache, *out; double xs, ys, zs, ms; if (!(nout && nin)) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!( 3 == nin->dim && nrrdTypeBlock != nin->type )) { biffAddf(GAGE, "%s: nin isn't a 3D non-block type nrrd", me); return 1; } /* if (!( AIR_EXISTS(nin->axis[0].spacing) && AIR_EXISTS(nin->axis[1].spacing) && AIR_EXISTS(nin->axis[2].spacing) )) { biffAddf(GAGE, "%s: nin's axis 0,1,2 spacings don't all exist", me); return 1; } */ if (!( dScale >= 1 && iScale >= 1 && dsmp >= 1 )) { biffAddf(GAGE, "%s: dScale (%d), iScale (%d), dsmp (%d) not all >= 1", me, dScale, iScale, dsmp); return 1; } mop = airMopNew(); gk0 = nrrdKernelSpecNew(); gk1 = nrrdKernelSpecNew(); ik0 = nrrdKernelSpecNew(); airMopAdd(mop, gk0, (airMopper)nrrdKernelSpecNix, airMopAlways); airMopAdd(mop, gk1, (airMopper)nrrdKernelSpecNix, airMopAlways); airMopAdd(mop, ik0, (airMopper)nrrdKernelSpecNix, airMopAlways); if ( nrrdKernelSpecParse(gk0, "cubic:1,0") || nrrdKernelSpecParse(gk1, "cubicd:1,0") || nrrdKernelSpecParse(ik0, "cubic:1,0")) { biffMovef(GAGE, NRRD, "%s: couldn't set up kernels", me); airMopError(mop); return 1; } /* manually set scale parameters */ gk0->parm[0] = dScale; gk1->parm[0] = dScale; ik0->parm[0] = 1.0; /* this is more complicated . . . */ ctx = gageContextNew(); airMopAdd(mop, ctx, (airMopper)gageContextNix, airMopAlways); gageParmSet(ctx, gageParmRenormalize, AIR_TRUE); E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(ctx, nin, gageKindScl)); if (!E) E |= gagePerVolumeAttach(ctx, pvl); if (!E) E |= gageKernelSet(ctx, gageKernel00, gk0->kernel, gk0->parm); if (!E) E |= gageKernelSet(ctx, gageKernel11, gk1->kernel, gk1->parm); if (!E) GAGE_QUERY_RESET(query); if (!E) GAGE_QUERY_ITEM_ON(query, gageSclGradVec); if (!E) E |= gageQuerySet(ctx, pvl, query); if (!E) E |= gageUpdate(ctx); if (E) { biffAddf(GAGE, "%s: ", me); airMopError(mop); return 1; } grad = _gageAnswerPointer(ctx, pvl, gageSclGradVec); xs = nin->axis[0].spacing; ys = nin->axis[1].spacing; zs = nin->axis[2].spacing; xs = AIR_EXISTS(xs) ? AIR_ABS(xs) : 1.0; ys = AIR_EXISTS(ys) ? AIR_ABS(ys) : 1.0; zs = AIR_EXISTS(zs) ? AIR_ABS(zs) : 1.0; ms = airCbrt(xs*ys*zs); /* ms is geometric mean of {xs,ys,zs} */ xs /= ms; ys /= ms; zs /= ms; fprintf(stderr, "iScale = %d, xs, ys, zs = %g, %g, %g\n", iScale, xs, ys, xs); rad = 0; ik0->parm[0] = iScale/xs; rad = AIR_MAX(rad, AIR_ROUNDUP(ik0->kernel->support(ik0->parm))); ik0->parm[0] = iScale/ys; rad = AIR_MAX(rad, AIR_ROUNDUP(ik0->kernel->support(ik0->parm))); ik0->parm[0] = iScale/zs; rad = AIR_MAX(rad, AIR_ROUNDUP(ik0->kernel->support(ik0->parm))); diam = 2*rad + 1; ixw = AIR_CALLOC(diam, double); iyw = AIR_CALLOC(diam, double); izw = AIR_CALLOC(diam, double); airMopAdd(mop, ixw, airFree, airMopAlways); airMopAdd(mop, iyw, airFree, airMopAlways); airMopAdd(mop, izw, airFree, airMopAlways); if (!(ixw && iyw && izw)) { biffAddf(GAGE, "%s: couldn't allocate grad vector or weight buffers", me); airMopError(mop); return 1; } /* the only reason that it is thread-safe to cache gageProbe results, without having the cache hang directly off the gageContext, is that we're doing all the probing for one context in one shot- producing an entirely volume of structure tensors with one function call */ gradCache = AIR_CALLOC(3*GAGE_CACHE_LEN, double); coordCache = AIR_CALLOC(3*GAGE_CACHE_LEN, int); airMopAdd(mop, gradCache, airFree, airMopAlways); airMopAdd(mop, coordCache, airFree, airMopAlways); if (!(gradCache && coordCache)) { biffAddf(GAGE, "%s: couldn't allocate caches", me); airMopError(mop); return 1; } for (ixi=0; ixiparm[0] = iScale/xs; ixw[wi+rad] = ik0->kernel->eval1_d(wi, ik0->parm); ik0->parm[0] = iScale/ys; iyw[wi+rad] = ik0->kernel->eval1_d(wi, ik0->parm); ik0->parm[0] = iScale/zs; izw[wi+rad] = ik0->kernel->eval1_d(wi, ik0->parm); fprintf(stderr, "%d --> (%g,%g,%g) -> (%g,%g,%g)\n", wi, wi/xs, wi/ys, wi/zs, ixw[wi+rad], iyw[wi+rad], izw[wi+rad]); } osx = (nin->axis[0].size)/dsmp; osy = (nin->axis[1].size)/dsmp; osz = (nin->axis[2].size)/dsmp; if (nrrdMaybeAlloc_va(nout, nrrdTypeDouble, 4, AIR_CAST(size_t, 7), AIR_CAST(size_t, osx), AIR_CAST(size_t, osy), AIR_CAST(size_t, osz))) { biffAddf(GAGE, NRRD, "%s: couldn't allocate output", me); airMopError(mop); return 1; } airMopAdd(mop, nout, (airMopper)nrrdEmpty, airMopOnError); out = AIR_CAST(double *, nout->data); for (ozi=0; oziaxis[2].size-1); if (!izw[_izi]) continue; for (_iyi=0; _iyiaxis[1].size-1); if (!iyw[_iyi]) continue; for (_ixi=0; _ixiaxis[0].size-1); if (!ixw[_ixi]) continue; wght = ixw[_ixi]*iyw[_iyi]*izw[_izi]; _gageCacheProbe(ctx, grad, coordCache, gradCache, ixi, iyi, izi); sten[0] += wght*grad[0]*grad[0]; sten[1] += wght*grad[0]*grad[1]; sten[2] += wght*grad[0]*grad[2]; sten[3] += wght*grad[1]*grad[1]; sten[4] += wght*grad[1]*grad[2]; sten[5] += wght*grad[2]*grad[2]; } } } out[0] = 1.0; out[1] = sten[0]; out[2] = sten[1]; out[3] = sten[2]; out[4] = sten[3]; out[5] = sten[4]; out[6] = sten[5]; out += 7; } } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/gage/filter.c0000664000175000017500000003634012165631065016743 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" /* ** sets the filter sample location (fsl) array based on fractional ** probe location ctx->point->frac ** ** One possible rare surpise: if a filter is not continuous with 0 ** at the end of its support, and if the sample location is at the ** highest possible point (xi == N-2, xf = 1.0), then the filter ** weights may not be the desired ones. Forward differencing (via ** nrrdKernelForwDiff) is a good example of this. */ void _gageFslSet(gageContext *ctx) { int fr, i; double *fslx, *fsly, *fslz; double xf, yf, zf; fr = ctx->radius; fslx = ctx->fsl + 0*2*fr; fsly = ctx->fsl + 1*2*fr; fslz = ctx->fsl + 2*2*fr; xf = ctx->point.frac[0]; yf = ctx->point.frac[1]; zf = ctx->point.frac[2]; switch (fr) { case 1: fslx[0] = xf; fslx[1] = xf-1; fsly[0] = yf; fsly[1] = yf-1; fslz[0] = zf; fslz[1] = zf-1; break; case 2: fslx[0] = xf+1; fslx[1] = xf; fslx[2] = xf-1; fslx[3] = xf-2; fsly[0] = yf+1; fsly[1] = yf; fsly[2] = yf-1; fsly[3] = yf-2; fslz[0] = zf+1; fslz[1] = zf; fslz[2] = zf-1; fslz[3] = zf-2; break; default: /* filter radius bigger than 2 */ for (i=-fr+1; i<=fr; i++) { fslx[i+fr-1] = xf-i; fsly[i+fr-1] = yf-i; fslz[i+fr-1] = zf-i; } break; } return; } /* ** renormalize weights of a reconstruction kernel with ** constraint: the sum of the weights must equal the continuous ** integral of the kernel */ void _gageFwValueRenormalize(gageContext *ctx, int wch) { double integral, sumX, sumY, sumZ, *fwX, *fwY, *fwZ; int i, fd; fd = 2*ctx->radius; fwX = ctx->fw + 0 + fd*(0 + 3*wch); fwY = ctx->fw + 0 + fd*(1 + 3*wch); fwZ = ctx->fw + 0 + fd*(2 + 3*wch); integral = ctx->ksp[wch]->kernel->integral(ctx->ksp[wch]->parm); sumX = sumY = sumZ = 0; for (i=0; iradius; fwX = ctx->fw + 0 + fd*(0 + 3*wch); fwY = ctx->fw + 0 + fd*(1 + 3*wch); fwZ = ctx->fw + 0 + fd*(2 + 3*wch); negX = negY = negZ = 0; posX = posY = posZ = 0; for (i=0; iverbose > 2) { fprintf(stderr, "%s: fixX = % 10.4f, fixY = % 10.4f, fixX = % 10.4f\n", me, (float)fixX, (float)fixY, (float)fixZ); } for (i=0; iradius; for (kidx=gageKernelUnknown+1; kidxneedK[kidx] || kidx==gageKernelStack) { continue; } /* we evaluate weights for all three axes with one call */ ctx->ksp[kidx]->kernel->evalN_d(ctx->fw + fd*3*kidx, ctx->fsl, fd*3, ctx->ksp[kidx]->parm); } if (ctx->verbose > 2) { fprintf(stderr, "%s: filter weights after kernel evaluation:\n", me); _gagePrint_fslw(stderr, ctx); } if (ctx->parm.renormalize) { for (kidx=gageKernelUnknown+1; kidxneedK[kidx] || kidx==gageKernelStack) { continue; } switch (kidx) { case gageKernel00: case gageKernel10: case gageKernel20: _gageFwValueRenormalize(ctx, kidx); break; default: _gageFwDerivRenormalize(ctx, kidx); break; } } if (ctx->verbose > 2) { fprintf(stderr, "%s: filter weights after renormalization:\n", me); _gagePrint_fslw(stderr, ctx); } } if (ctx->parm.stackUse && ctx->parm.stackNormalizeDeriv) { unsigned int jj; double scl, norm, *fwX, *fwY, *fwZ; scl = AIR_AFFINE(0.0, sfrac, 1.0, ctx->stackPos[sidx], ctx->stackPos[sidx+1]); #if 0 double (*dgeval)(double x, const double *parm), dgparm[2] = {0, 3}; dgeval = nrrdKernelDiscreteGaussian->eval1_d; dgparm[0] = scl; /* from Eq. (120) in T. Lindeberg. "Feature Detection with Automatic Scale Selection." International Journal of Computer Vision, 1998, 30, 77-116 */ /* 0.7978845608 ~= sqrt(2)/sqrt(pi) */ norm = 0.7978845608/(dgeval(0.0, dgparm) + dgeval(1.0, dgparm)); #endif /* really simple; no lindeberg normalization, possible bias */ norm = scl + ctx->parm.stackNormalizeDerivBias; fd = 2*ctx->radius; kidx = gageKernel11; fwX = ctx->fw + 0 + fd*(0 + 3*kidx); fwY = ctx->fw + 0 + fd*(1 + 3*kidx); fwZ = ctx->fw + 0 + fd*(2 + 3*kidx); for (jj=0; jjfw + 0 + fd*(0 + 3*kidx); fwY = ctx->fw + 0 + fd*(1 + 3*kidx); fwZ = ctx->fw + 0 + fd*(2 + 3*kidx); for (jj=0; jjerrNum and sprints message into ctx->errStr. */ int _gageLocationSet(gageContext *ctx, double xif, double yif, double zif, double sif) { char me[]="_gageProbeLocationSet"; unsigned int top[3], /* "top" x, y, z: highest valid index in volume */ idx[4]; int sdiff; /* computed integral positions in volume */ double frac[4], min, max[3]; /* **** bounds checking **** */ top[0] = ctx->shape->size[0] - 1; top[1] = ctx->shape->size[1] - 1; top[2] = ctx->shape->size[2] - 1; if (nrrdCenterNode == ctx->shape->center) { min = 0; max[0] = top[0]; max[1] = top[1]; max[2] = top[2]; } else { min = -0.5; max[0] = AIR_CAST(double, top[0]) + 0.5; max[1] = AIR_CAST(double, top[1]) + 0.5; max[2] = AIR_CAST(double, top[2]) + 0.5; } if (!( AIR_IN_CL(min, xif, max[0]) && AIR_IN_CL(min, yif, max[1]) && AIR_IN_CL(min, zif, max[2]) )) { if (ctx->parm.generateErrStr) { sprintf(ctx->errStr, "%s: position (%g,%g,%g) outside (%s-centered) " "bounds [%g,%g]x[%g,%g]x[%g,%g]", me, xif, yif, zif, airEnumStr(nrrdCenter, ctx->shape->center), min, max[0], min, max[1], min, max[2]); } else { strcpy(ctx->errStr, _GAGE_NON_ERR_STR); } ctx->errNum = gageErrBoundsSpace; return 1; } if (ctx->parm.stackUse) { if (!( AIR_IN_CL(0, sif, ctx->pvlNum-2) )) { if (ctx->parm.generateErrStr) { sprintf(ctx->errStr, "%s: stack position %g outside (%s-centered) " "bounds [0,%u]", me, sif, airEnumStr(nrrdCenter, nrrdCenterNode), ctx->pvlNum-2); } else { strcpy(ctx->errStr, _GAGE_NON_ERR_STR); } ctx->errNum = gageErrBoundsStack; return 1; } } /* **** computing integral and fractional sample locations **** */ /* Thu Jan 14 19:46:53 CST 2010: detected that along the low edge (next to sample 0) in cell centered, the rounding behavior of AIR_CAST(unsigned int, xif), namely [-0.5,0] --> 0, meant that the low edge was not treated symmetrically with the high edge. This motivated the change from using idx to store the lower corner of the containing voxel, to the upper corner. So, the new "idx" is always +1 of what the old idx was. Code here and in ctx.c (since idx is saved into ctx->point.idx) has been changed accordingly */ ELL_3V_SET(idx, AIR_CAST(unsigned int, xif+1), /* +1: see above */ AIR_CAST(unsigned int, yif+1), AIR_CAST(unsigned int, zif+1)); if (ctx->verbose > 5) { fprintf(stderr, "%s: (%g,%g,%g,%g) -%s-> mm [%g, %g/%g/%g]\n" " --> idx %u %u %u\n", me, xif, yif, zif, sif, airEnumStr(nrrdCenter, ctx->shape->center), min, max[0], max[1], max[2], idx[0], idx[1], idx[2]); } /* these can only can kick in for node-centered, because that's when max[] has an integral value */ idx[0] -= (idx[0]-1 == max[0]); idx[1] -= (idx[1]-1 == max[1]); idx[2] -= (idx[2]-1 == max[2]); if (ctx->verbose > 5) { fprintf(stderr, "%s: ----> idx %u %u %u\n", me, idx[0], idx[1], idx[2]); } ELL_3V_SET(frac, xif - (AIR_CAST(float, idx[0])-1), yif - (AIR_CAST(float, idx[1])-1), zif - (AIR_CAST(float, idx[2])-1)); ELL_3V_COPY(ctx->point.idx, idx); /* not idx[3], yet */ if (ctx->parm.stackUse) { idx[3] = AIR_CAST(unsigned int, sif); idx[3] -= (idx[3] == ctx->pvlNum-2); frac[3] = sif - idx[3]; sdiff = (ctx->point.idx[3] + ctx->point.frac[3] != sif); } else { idx[3] = 0; frac[3] = 0; sdiff = AIR_FALSE; } if (ctx->verbose > 2) { fprintf(stderr, "%s: \n" " pos (% 15.7f,% 15.7f,% 15.7f,% 15.7f) \n" " -> i(%5d,%5d,%5d,%5d) \n" " + f(% 15.7f,% 15.7f,% 15.7f,% 15.7f) \n", me, xif, yif, zif, sif, idx[0], idx[1], idx[2], idx[3], frac[0], frac[1], frac[2], frac[3]); } /* **** compute *spatial* fsl and fw **** these have to be reconsidered if anything changes about the fractional spatial position, or (if no fractional spatial change), movement along scale AND using normalization based on scale */ if ( ctx->point.frac[0] != frac[0] || ctx->point.frac[1] != frac[1] || ctx->point.frac[2] != frac[2] || (ctx->parm.stackUse && sdiff && ctx->parm.stackNormalizeDeriv)) { /* We don't yet record the scale position in ctx->point because that's done below while setting stackFsl and stackFw. So, have to pass stack pos info to _gageFwSet() */ ELL_3V_COPY(ctx->point.frac, frac); /* these may take some time (especially if using renormalization), hence the conditional above */ _gageFslSet(ctx); _gageFwSet(ctx, idx[3], frac[3]); } /* **** compute *stack* fsl and fw **** */ if (ctx->verbose > 2 && ctx->parm.stackUse) { fprintf(stderr, "%s: point.frac[3] %f + idx[3] %u = %f %s sif %f\n", me, ctx->point.frac[3], ctx->point.idx[3], ctx->point.frac[3] + ctx->point.idx[3], (sdiff ? "*NOT ==*" : "=="), sif); } if (!ctx->parm.stackUse) { ctx->point.idx[3] = idx[3]; ctx->point.frac[3] = frac[3]; ctx->point.stackFwNonZeroNum = 0; } else if (sdiff) { double sum; unsigned int ii, nnz; NrrdKernelSpec *sksp; /* node-centered sampling of stack indices from 0 to ctx->pvlNum-2 */ /* HEY: we really are quite far from implementing arbitrary nrrdBoundary behaviors here, and centering is stuck on node! */ /* HEY: honestly, the whole idea that it still makes sense to do low-level operations in index space, when the world-space locations of the samples can be non-uniform, is a little suspect. This is all legit for nrrdKernelTent and nrrdKernelHermiteScaleSpaceFlag, but is pretty fishy otherwise */ for (ii=0; iipvlNum-1; ii++) { ctx->stackFsl[ii] = sif - ii; if (ctx->verbose > 2) { fprintf(stderr, "%s: ctx->stackFsl[%u] = %g\n", me, ii, ctx->stackFsl[ii]); } } sksp = ctx->ksp[gageKernelStack]; sksp->kernel->evalN_d(ctx->stackFw, ctx->stackFsl, ctx->pvlNum-1, sksp->parm); if (ctx->verbose > 2) { for (ii=0; iipvlNum-1; ii++) { fprintf(stderr, "%s: ctx->stackFw[%u] = %g\n", me, ii, ctx->stackFw[ii]); } } /* compute stackFwNonZeroNum whether or not parm.stackNormalizeRecon! */ nnz = 0; if (ctx->parm.stackNormalizeRecon) { sum = 0; for (ii=0; iipvlNum-1; ii++) { nnz += !!ctx->stackFw[ii]; sum += ctx->stackFw[ii]; } if (!sum) { if (ctx->parm.generateErrStr) { sprintf(ctx->errStr, "%s: integral of stackFw[] is zero; " "can't do stack reconstruction", me); } else { strcpy(ctx->errStr, _GAGE_NON_ERR_STR); } ctx->errNum = gageErrStackIntegral; return 1; } for (ii=0; iipvlNum-1; ii++) { ctx->stackFw[ii] /= sum; } if (ctx->verbose > 2) { for (ii=0; iipvlNum-1; ii++) { fprintf(stderr, "%s: ctx->stackFw[%u] = %g\n", me, ii, ctx->stackFw[ii]); } } } else { for (ii=0; iipvlNum-1; ii++) { nnz += !!ctx->stackFw[ii]; } if (!nnz) { if (ctx->parm.generateErrStr) { sprintf(ctx->errStr, "%s: all stackFw[] weights are zero; " "can't do stack reconstruction", me); } else { strcpy(ctx->errStr, _GAGE_NON_ERR_STR); } ctx->errNum = gageErrStackIntegral; return 1; } } ctx->point.idx[3] = idx[3]; ctx->point.frac[3] = frac[3]; ctx->point.stackFwNonZeroNum = nnz; } return 0; } teem-1.11.0~svn6057/src/gage/sclprint.c0000664000175000017500000000541312165631065017311 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" void _gageSclIv3Print (FILE *file, gageContext *ctx, gagePerVolume *pvl) { double *iv3; int i, fd; iv3 = pvl->iv3; fd = 2*ctx->radius; fprintf(file, "iv3[]:\n"); switch(fd) { case 2: fprintf(file, "% 10.4f % 10.4f\n", (float)iv3[6], (float)iv3[7]); fprintf(file, " % 10.4f % 10.4f\n\n", (float)iv3[4], (float)iv3[5]); fprintf(file, "% 10.4f % 10.4f\n", (float)iv3[2], (float)iv3[3]); fprintf(file, " % 10.4f % 10.4f\n", (float)iv3[0], (float)iv3[1]); break; case 4: for (i=3; i>=0; i--) { fprintf(file, "% 10.4f % 10.4f % 10.4f % 10.4f\n", (float)iv3[12+16*i], (float)iv3[13+16*i], (float)iv3[14+16*i], (float)iv3[15+16*i]); fprintf(file, " % 10.4f %c% 10.4f % 10.4f%c % 10.4f\n", (float)iv3[ 8+16*i], (i==1||i==2)?'\\':' ', (float)iv3[ 9+16*i], (float)iv3[10+16*i], (i==1||i==2)?'\\':' ', (float)iv3[11+16*i]); fprintf(file, " % 10.4f %c% 10.4f % 10.4f%c % 10.4f\n", (float)iv3[ 4+16*i], (i==1||i==2)?'\\':' ', (float)iv3[ 5+16*i], (float)iv3[ 6+16*i], (i==1||i==2)?'\\':' ', (float)iv3[ 7+16*i]); fprintf(file, " % 10.4f % 10.4f % 10.4f % 10.4f\n", (float)iv3[ 0+16*i], (float)iv3[ 1+16*i], (float)iv3[ 2+16*i], (float)iv3[ 3+16*i]); if (i) fprintf(file, "\n"); } break; default: for (i=0; ipvlNum; pvlIdx++) { ret |= ctx->pvl[pvlIdx]->flag[pvlFlag]; } return ret; } void _gagePvlFlagDown(gageContext *ctx, int pvlFlag) { unsigned int pvlIdx; for (pvlIdx=0; pvlIdxpvlNum; pvlIdx++) { ctx->pvl[pvlIdx]->flag[pvlFlag] = AIR_FALSE; } } /* ** One could go from all the pvls' queries to the context's needD in ** one shot, but doing it in two steps (as below) seems a little clearer, ** and it means that pvl->needD isn't needlessly re-computed for ** pvl's whose query hasn't changed. */ /* ** for each pvl: pvl's query --> pvl's needD */ void _gagePvlNeedDUpdate(gageContext *ctx) { static const char me[]="_gagePvlNeedDUpdate"; gagePerVolume *pvl; int que, needD[GAGE_DERIV_MAX+1]; unsigned int pvlIdx, di; if (ctx->verbose) fprintf(stderr, "%s: hello\n", me); for (pvlIdx=0; pvlIdxpvlNum; pvlIdx++) { pvl = ctx->pvl[pvlIdx]; if (pvl->flag[gagePvlFlagQuery]) { GAGE_DV_SET(needD, 0, 0, 0); que = pvl->kind->itemMax+1; do { que--; if (GAGE_QUERY_ITEM_TEST(pvl->query, que)) { needD[pvl->kind->table[que].needDeriv] = 1; } } while (que); if (!GAGE_DV_EQUAL(needD, pvl->needD)) { if (ctx->verbose) { fprintf(stderr, "%s: updating pvl[%d]'s needD to (", me, pvlIdx); for (di=0; di<=GAGE_DERIV_MAX; di++) { fprintf(stderr, "%s%d", di ? "," : "", needD[di]); } fprintf(stderr, "\n"); } GAGE_DV_COPY(pvl->needD, needD); pvl->flag[gagePvlFlagNeedD] = AIR_TRUE; } } } if (ctx->verbose) fprintf(stderr, "%s: bye\n", me); return; } /* ** all pvls' needD --> ctx's needD */ void _gageNeedDUpdate(gageContext *ctx) { static const char me[]="_gageNeedDUpdate"; gagePerVolume *pvl; int needD[GAGE_DERIV_MAX+1]; unsigned int pvlIdx, di; if (ctx->verbose) fprintf(stderr, "%s: hello\n", me); GAGE_DV_SET(needD, 0, 0, 0); for (pvlIdx=0; pvlIdxpvlNum; pvlIdx++) { pvl = ctx->pvl[pvlIdx]; for (di=0; di<=GAGE_DERIV_MAX; di++) { needD[di] |= pvl->needD[di]; } } if (!GAGE_DV_EQUAL(needD, ctx->needD)) { if (ctx->verbose) { fprintf(stderr, "%s: updating ctx's needD to (", me); for (di=0; di<=GAGE_DERIV_MAX; di++) { fprintf(stderr, "%s%d", di ? "," : "", needD[di]); } fprintf(stderr, "\n"); } GAGE_DV_COPY(ctx->needD, needD); ctx->flag[gageCtxFlagNeedD] = AIR_TRUE; } if (ctx->verbose) fprintf(stderr, "%s: bye\n", me); return; } /* ** ctx's needD & k3pack --> needK */ static int _gageNeedKUpdate(gageContext *ctx) { static const char me[]="_gageNeedKUpdate"; int kernIdx, needK[GAGE_KERNEL_MAX+1], change; unsigned int di; if (ctx->verbose) fprintf(stderr, "%s: hello\n", me); for (kernIdx=gageKernelUnknown+1; kernIdxparm.k3pack) { biffAddf(GAGE, "%s: sorry, only 3-pack filtering implemented now", me); return 1; } di = 0; if (ctx->needD[di]) { needK[gageKernel00] = AIR_TRUE; } di += 1; if (ctx->needD[di]) { needK[gageKernel00] = needK[gageKernel11] = AIR_TRUE; } di += 1; if (ctx->needD[di]) { needK[gageKernel00] = needK[gageKernel11] = needK[gageKernel22] = AIR_TRUE; } if (GAGE_DERIV_MAX != di) { biffAddf(GAGE, "%s: sorry, code needs updating for GAGE_DERIV_MAX %u", me, GAGE_DERIV_MAX); return 1; } change = AIR_FALSE; for (kernIdx=gageKernelUnknown+1; kernIdxneedK[kernIdx]); } if (change) { if (ctx->verbose) { fprintf(stderr, "%s: changing needK to (", me); for (kernIdx=gageKernelUnknown+1; kernIdx gageKernelUnknown+1 ? "," : "", needK[kernIdx]); } fprintf(stderr, ")\n"); } for (kernIdx=gageKernelUnknown+1; kernIdxneedK[kernIdx] = needK[kernIdx]; } ctx->flag[gageCtxFlagNeedK] = AIR_TRUE; } if (ctx->verbose) fprintf(stderr, "%s: bye\n", me); return 0; } /* ** ctx's ksp[] & needK --> radius ** */ int _gageRadiusUpdate(gageContext *ctx) { static const char me[]="_gageRadiusUpdate"; unsigned int kernIdx, radius; double maxRad, rad; NrrdKernelSpec *ksp; if (ctx->verbose) fprintf(stderr, "%s: hello\n", me); maxRad = 0; for (kernIdx=gageKernelUnknown+1; kernIdxneedK[kernIdx]) { ksp = ctx->ksp[kernIdx]; if (!ksp) { biffAddf(GAGE, "%s: need kernel %s but it hasn't been set", me, airEnumStr(gageKernel, kernIdx)); return 1; } rad = ksp->kernel->support(ksp->parm); maxRad = AIR_MAX(maxRad, rad); if (ctx->verbose) { fprintf(stderr, "%s: k[%s]=%s -> rad = %g -> maxRad = %g\n", me, airEnumStr(gageKernel, kernIdx), ksp->kernel->name, rad, maxRad); } } } radius = AIR_ROUNDUP(maxRad); /* In case either kernels have tiny supports (less than 0.5), or if we in fact don't need any kernels, then we need to do this to ensure that we generate a valid radius */ radius = AIR_MAX(radius, 1); if (ctx->parm.stackUse && (nrrdKernelHermiteScaleSpaceFlag == ctx->ksp[gageKernelStack]->kernel)) { if (ctx->verbose) { fprintf(stderr, "%s: hermite on stack: bumping radius %d --> %d\n", me, radius, radius+1); } radius += 1; } if (radius != ctx->radius) { if (ctx->verbose) { fprintf(stderr, "%s: changing radius from %d to %d\n", me, ctx->radius, radius); } ctx->radius = radius; ctx->flag[gageCtxFlagRadius] = AIR_TRUE; } if (ctx->verbose) fprintf(stderr, "%s: bye\n", me); return 0; } int _gageCacheSizeUpdate(gageContext *ctx) { static const char me[]="_gageCacheSizeUpdate"; int fd; gagePerVolume *pvl; unsigned int pvlIdx; if (ctx->verbose) fprintf(stderr, "%s: hello (radius = %d)\n", me, ctx->radius); if (!( ctx->radius > 0 )) { biffAddf(GAGE, "%s: have bad radius %d", me, ctx->radius); return 1; } fd = 2*ctx->radius; ctx->fsl = (double *)airFree(ctx->fsl); ctx->fw = (double *)airFree(ctx->fw); ctx->off = (unsigned int *)airFree(ctx->off); ctx->fsl = (double *)calloc(fd*3, sizeof(double)); ctx->fw = (double *)calloc(fd*3*(GAGE_KERNEL_MAX+1), sizeof(double)); ctx->off = (unsigned int *)calloc(fd*fd*fd, sizeof(unsigned int)); if (!(ctx->fsl && ctx->fw && ctx->off)) { biffAddf(GAGE, "%s: couldn't allocate filter caches for fd=%d", me, fd); return 1; } /* note that all pvls get caches allocated for the same size, even if their queries don't involve the largest-size kernels. This is actually the feature that enabled the stack functionality to be easily added. */ for (pvlIdx=0; pvlIdxpvlNum; pvlIdx++) { pvl = ctx->pvl[pvlIdx]; pvl->iv3 = (double *)airFree(pvl->iv3); pvl->iv2 = (double *)airFree(pvl->iv2); pvl->iv1 = (double *)airFree(pvl->iv1); pvl->iv3 = (double *)calloc(fd*fd*fd*pvl->kind->valLen, sizeof(double)); pvl->iv2 = (double *)calloc(fd*fd*pvl->kind->valLen, sizeof(double)); pvl->iv1 = (double *)calloc(fd*pvl->kind->valLen, sizeof(double)); if (!(pvl->iv3 && pvl->iv2 && pvl->iv1)) { biffAddf(GAGE, "%s: couldn't allocate pvl[%d]'s value caches for fd=%d", me, pvlIdx, fd); return 1; } } if (ctx->verbose) fprintf(stderr, "%s: bye\n", me); return 0; } void _gageOffValueUpdate(gageContext *ctx) { static const char me[]="_gageOffValueUpdate"; int fd, i, j, k; unsigned int sx, sy; if (ctx->verbose) fprintf(stderr, "%s: hello\n", me); sx = ctx->shape->size[0]; sy = ctx->shape->size[1]; fd = 2*ctx->radius; /* HEY: look into special casing this for small fd */ for (k=0; koff[i + fd*(j + fd*k)] = i + sx*(j + sy*k); } } } /* no flags to set for further action */ if (ctx->verbose) fprintf(stderr, "%s: bye\n", me); return; } /* ******** gageUpdate() ** ** call just before probing begins. */ int gageUpdate(gageContext *ctx) { static const char me[]="gageUpdate"; unsigned int pi; int i, haveQuery; if (!( ctx )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (0 == ctx->pvlNum) { biffAddf(GAGE, "%s: context has no attached pervolumes", me); return 1; } haveQuery = AIR_FALSE; for (pi=0; pipvlNum; pi++) { haveQuery |= GAGE_QUERY_NONZERO(ctx->pvl[pi]->query); } if (!haveQuery) { biffAddf(GAGE, "%s: no query item set in %s", me, (ctx->pvlNum == 1 ? "the pervolume" : "any of the pervolumes")); return 1; } /* HEY: shouldn't there be some more logic/state for this? */ if (ctx->parm.stackUse) { if (!ctx->ksp[gageKernelStack]) { biffAddf(GAGE, "%s: can't do stack without ksp[%s]", me, airEnumStr(gageKernel, gageKernelStack)); return 1; } if (!( 2 <= ctx->pvlNum )) { biffAddf(GAGE, "%s: need at least 2 pervolumes for stack", me); return 1; } for (pi=1; pipvlNum; pi++) { if (!( ctx->pvl[0]->kind == ctx->pvl[pi]->kind )) { biffAddf(GAGE, "%s: pvl[%u] kind (%s) != pvl[0] kind (%s)", me, pi, ctx->pvl[pi]->kind->name, ctx->pvl[0]->kind->name); return 1; } } } /* start traversing the whole update graph . . . */ if (ctx->verbose) { fprintf(stderr, "%s: hello ____________________ \n", me); fprintf(stderr, " context flags:"); for (i=gageCtxFlagUnknown+1; iflag[i]); } fprintf(stderr, "\n"); fprintf(stderr, " pvl flags:"); for (i=gagePvlFlagUnknown+1; iflag[gageCtxFlagNeedD] || ctx->flag[gageCtxFlagK3Pack]) { if (_gageNeedKUpdate(ctx)) { biffAddf(GAGE, "%s: trouble", me); return 1; } ctx->flag[gageCtxFlagNeedD] = AIR_FALSE; ctx->flag[gageCtxFlagK3Pack] = AIR_FALSE; } if (ctx->flag[gageCtxFlagKernel] || ctx->flag[gageCtxFlagNeedK]) { if (_gageRadiusUpdate(ctx)) { biffAddf(GAGE, "%s: trouble", me); return 1; } ctx->flag[gageCtxFlagKernel] = AIR_FALSE; ctx->flag[gageCtxFlagNeedK] = AIR_FALSE; } if (ctx->flag[gageCtxFlagRadius] /* HEY HEY HEY: this is a total hack: right now its possible for a new pvl to have unallocated iv3,iv2,iv1, if it was attached to a context which had already been probing, as was the case with _tenRegisterDoit. So, with this hack we reallocate ALL caches just because a new pervolume was attached . . . */ || _gagePvlFlagCheck(ctx, gagePvlFlagVolume)) { if (_gageCacheSizeUpdate(ctx)) { biffAddf(GAGE, "%s: trouble", me); return 1; } } if (ctx->flag[gageCtxFlagRadius] || ctx->flag[gageCtxFlagShape] /* see above; following flags that triggered _gageCacheSizeUpdate(ctx) */ || _gagePvlFlagCheck(ctx, gagePvlFlagVolume)) { _gageOffValueUpdate(ctx); ctx->flag[gageCtxFlagShape] = AIR_FALSE; } ctx->flag[gageCtxFlagRadius] = AIR_FALSE; /* chances are, something above has invalidated the state maintained during successive calls to gageProbe() */ gagePointReset(&ctx->point); for (pi=0; pipvlNum; pi++) { if (ctx->pvl[pi]->kind->pvlDataUpdate) { if (ctx->pvl[pi]->kind->pvlDataUpdate(ctx->pvl[pi]->kind, ctx, ctx->pvl[pi], ctx->pvl[pi]->data)) { biffAddf(GAGE, "%s: pvlDataUpdate(pvl[%u]) failed", me, pi); return 1; } } } if (ctx->verbose > 3 && ctx->stackPos) { fprintf(stderr, "%s: pvlNum = %u -> stack of %u [0,%u]\n", me, ctx->pvlNum, ctx->pvlNum-1, ctx->pvlNum-2); for (pi=0; pipvlNum-1; pi++) { fprintf(stderr, "%s: stackPos[%u] = %g\n", me, pi, ctx->stackPos[pi]); } } if (ctx->verbose) fprintf(stderr, "%s: bye ^^^^^^^^^^^^^^^^^^^ \n", me); return 0; } teem-1.11.0~svn6057/src/gage/miscGage.c0000664000175000017500000001555012174665402017177 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" const int gagePresent = 42; /* ******** gageZeroNormal[] ** ** this is the vector to supply when someone wants the normalized ** version of a vector with zero length. We could be nasty and ** set this to {AIR_NAN, AIR_NAN, AIR_NAN}, but simply passing ** NANs around can make things fantastically slow . . . */ double gageZeroNormal[3] = {0,0,0}; const char * _gageKernelStr[] = { "(unknown_kernel)", "00", "10", "11", "20", "21", "22", /* "33", */ /* "44", */ "stack" }; const char * _gageKernelDesc[] = { "unknown kernel", "kernel for reconstructing values", "kernel for reconstruction values when doing 1st derivatives", "kernel for measuring 1st derivatives when doing 1st derivatives", "kernel for reconstruction values when doing 2nd derivatives", "kernel for measuring 1st derivatives when doing 2nd derivatives", "kernel for measuring 2nd derivatives when doing 2nd derivatives", /* "kernel for measuring 3rd derivatives when doing 3rd derivatives", */ /* "kernel for measuring 4th derivatives when doing 4th derivatives", */ "kernel for reconstruction across a stack" }; const char * _gageKernelStrEqv[] = { "00", "k00", "10", "k10", "11", "k11", "20", "k20", "21", "k21", "22", "k22", /* "33", "k33", */ /* "44", "k44", */ "stack", "ss", "kss", "" }; const int _gageKernelValEqv[] = { gageKernel00, gageKernel00, gageKernel10, gageKernel10, gageKernel11, gageKernel11, gageKernel20, gageKernel20, gageKernel21, gageKernel21, gageKernel22, gageKernel22, /* gageKernel33, gageKernel33, */ /* gageKernel44, gageKernel44, */ gageKernelStack, gageKernelStack, gageKernelStack }; const airEnum _gageKernel_enum = { "kernel", GAGE_KERNEL_MAX, _gageKernelStr, NULL, _gageKernelDesc, _gageKernelStrEqv, _gageKernelValEqv, AIR_FALSE }; const airEnum *const gageKernel = &_gageKernel_enum; void gageParmReset(gageParm *parm) { if (parm) { parm->renormalize = gageDefRenormalize; parm->checkIntegrals = gageDefCheckIntegrals; parm->k3pack = gageDefK3Pack; parm->gradMagCurvMin = gageDefGradMagCurvMin; parm->curvNormalSide = gageDefCurvNormalSide; parm->kernelIntegralNearZero = gageDefKernelIntegralNearZero; parm->defaultCenter = gageDefDefaultCenter; parm->stackUse = gageDefStackUse; parm->stackNormalizeRecon = gageDefStackNormalizeRecon; parm->stackNormalizeDeriv = gageDefStackNormalizeDeriv; parm->stackNormalizeDerivBias = gageDefStackNormalizeDerivBias; parm->orientationFromSpacing = gageDefOrientationFromSpacing; parm->generateErrStr = gageDefGenerateErrStr; parm->twoDimZeroZ = gageDefTwoDimZeroZ; } return; } void gagePointReset(gagePoint *point) { if (point) { unsigned int big; /* learned: can't initialize the floating point to AIR_NAN, non-dot-net windows compilers proclaim that QNAN == x for any existent x!!! For some reason though, infinity is handled correctly */ ELL_4V_SET(point->frac, AIR_POS_INF, AIR_POS_INF, AIR_POS_INF, AIR_POS_INF); big = AIR_CAST(unsigned int, -1); ELL_4V_SET(point->idx, big, big, big, big); point->stackFwNonZeroNum = 0; } return; } void gageItemSpecInit(gageItemSpec *isp) { if (isp) { isp->item = -1; isp->kind = NULL; } return; } gageItemSpec * gageItemSpecNew(void) { gageItemSpec *isp; isp = (gageItemSpec *)calloc(1, sizeof(gageItemSpec)); gageItemSpecInit(isp); return isp; } gageItemSpec * gageItemSpecNix(gageItemSpec *isp) { if (isp) { airFree(isp); } return NULL; } const char * _gageErrStr[GAGE_ERR_MAX+1] = { "(unknown gageErr)", "none", "space bounds", "stack bounds", "stack integral", "stack search", "stack unused" }; const airEnum _gageErr = { "gageErr", GAGE_ERR_MAX, _gageErrStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const gageErr = &_gageErr; const char * _gageItemPackPartStr[] = { "(unknown_pack_part)", "scalar", "gradvec", "gradmag", "normal", "hessian", "hesseval0", "hesseval1", "hesseval2", "hessevec0", "hessevec1", "hessevec2" }; const char * _gageItemPackPartDesc[] = { "unknown pack part", "the base scalar F", "gradient (vector) of F", "magnitude of the gradient of F", "normalized gradient (vector) of F", "Hessian of F", "1st eigenvalue of Hessian of F", "2nd eigenvalue of Hessian of F", "3rd eigenvalue of Hessian of F", "1st eigenvector of Hessian of F", "2nd eigenvector of Hessian of F", "3rd eigenvector of Hessian of F" }; const char * _gageItemPackPartStrEqv[] = { "scalar", "scl", "gradvec", "gvec", "gradmag", "gmag", "normal", "norm", "hessian", "hess", "hesseval0", "heval0", "hesseval1", "heval1", "hesseval2", "heval2", "hessevec0", "hevec0", "hessevec1", "hevec1", "hessevec2", "hevec2", "" }; const int _gageItemPackPartValEqv[] = { gageItemPackPartScalar, gageItemPackPartScalar, gageItemPackPartGradVec, gageItemPackPartGradVec, gageItemPackPartGradMag, gageItemPackPartGradMag, gageItemPackPartNormal, gageItemPackPartNormal, gageItemPackPartHessian, gageItemPackPartHessian, gageItemPackPartHessEval0, gageItemPackPartHessEval0, gageItemPackPartHessEval1, gageItemPackPartHessEval1, gageItemPackPartHessEval2, gageItemPackPartHessEval2, gageItemPackPartHessEvec0, gageItemPackPartHessEvec0, gageItemPackPartHessEvec1, gageItemPackPartHessEvec1, gageItemPackPartHessEvec2, gageItemPackPartHessEvec2, }; const airEnum _gageItemPackPart_enum = { "pack part", GAGE_ITEM_PACK_PART_MAX, _gageItemPackPartStr, NULL, _gageItemPackPartDesc, _gageItemPackPartStrEqv, _gageItemPackPartValEqv, AIR_FALSE }; const airEnum *const gageItemPackPart = &_gageItemPackPart_enum; teem-1.11.0~svn6057/src/gage/vecprint.c0000664000175000017500000000252712165631065017310 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" void _gageVecIv3Print (FILE *file, gageContext *ctx, gagePerVolume *pvl) { AIR_UNUSED(ctx); AIR_UNUSED(pvl); fprintf(file, "_gageVecIv3Print() not implemented\n"); } teem-1.11.0~svn6057/src/gage/sources.cmake0000664000175000017500000000063512064573246020001 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(GAGE_SOURCES ctx.c deconvolve.c defaultsGage.c filter.c gage.h kind.c miscGage.c print.c privateGage.h pvl.c scl.c sclanswer.c sclfilter.c sclprint.c shape.c st.c stack.c stackBlur.c update.c vecGage.c vecprint.c optimsig.c ) ADD_TEEM_LIBRARY(gage ${GAGE_SOURCES}) teem-1.11.0~svn6057/src/gage/shape.c0000664000175000017500000004721612165631065016562 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" void gageShapeReset(gageShape *shape) { /* NOTE this guards against NULL */ if (shape) { /* input */ shape->defaultCenter = gageDefDefaultCenter; shape->orientationFromSpacing = gageDefOrientationFromSpacing; /* output; setting this in ways that can be used to test whether the shape has been set */ shape->center = nrrdCenterUnknown; shape->fromOrientation = AIR_FALSE; ELL_3V_SET(shape->size, 0, 0, 0); ELL_3V_SET(shape->spacing, AIR_NAN, AIR_NAN, AIR_NAN); ELL_4M_NAN_SET(shape->ItoW); ELL_4M_NAN_SET(shape->WtoI); ELL_3M_NAN_SET(shape->ItoWSubInvTransp); ELL_3M_NAN_SET(shape->ItoWSubInv); } return; } static void * _mopShapeReset(void *_shape) { gageShape *shape; shape = AIR_CAST(gageShape *, _shape); gageShapeReset(shape); return NULL; } gageShape * gageShapeNew() { gageShape *shape; shape = (gageShape *)calloc(1, sizeof(gageShape)); if (shape) { gageShapeReset(shape); } return shape; } gageShape * gageShapeCopy(const gageShape *shp) { gageShape *nhp; if ((nhp = gageShapeNew())) { /* no pointers, so this is easy */ memcpy(nhp, shp, sizeof(gageShape)); } return nhp; } gageShape * gageShapeNix(gageShape *shape) { airFree(shape); return NULL; } static void shapeUnitItoW(const gageShape *shape, double world[3], const double indx[3], const double volHalfLen[3]) { unsigned int i; if (nrrdCenterNode == shape->center) { for (i=0; i<=2; i++) { world[i] = NRRD_NODE_POS(-volHalfLen[i], volHalfLen[i], shape->size[i], indx[i]); } } else { for (i=0; i<=2; i++) { world[i] = NRRD_CELL_POS(-volHalfLen[i], volHalfLen[i], shape->size[i], indx[i]); } } } /* ** _gageShapeSet ** ** we are serving two masters here. If ctx is non-NULL, we are being called ** from within gage, and we are to be lax or strict according to the settings ** of ctx->parm.requireAllSpacings and ctx->parm.requireEqualCenters. If ** ctx is NULL, gageShapeSet was called, in which case we go with lax ** behavior (nothing "required") ** ** This function has subsumed the contents of the old gageVolumeCheck, ** and hence has become this weird beast- part error checker and part ** (gageShape) initializer. Oh well... */ int _gageShapeSet(const gageContext *ctx, gageShape *shape, const Nrrd *nin, unsigned int baseDim) { static const char me[]="_gageShapeSet"; int ai, cx, cy, cz, statCalc[3], status, ofspc; unsigned int minsize; const NrrdAxisInfo *ax[3]; double vecA[4], vecB[3], vecC[3], vecD[4], matA[9], spcCalc[3], vecCalc[3][NRRD_SPACE_DIM_MAX], orig[NRRD_SPACE_DIM_MAX]; airArray *mop; /* fprintf(stderr, "!%s: ctx = %p (%s, %s)\n", me, ctx, (ctx ? (ctx->shape->fromOrientation ? "YES from orient" : "not from orient") : "???"), (ctx ? (ctx->parm.orientationFromSpacing ? "YES ofs" : "not ofs") : "???")); */ /* ------ basic error checking */ mop = airMopNew(); airMopAdd(mop, shape, _mopShapeReset, airMopOnError); if (!( shape && nin )) { biffAddf(GAGE, "%s: got NULL pointer", me); airMopError(mop); return 1; } if (nrrdCheck(nin)) { biffMovef(GAGE, NRRD, "%s: basic nrrd validity check failed", me); airMopError(mop); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(GAGE, "%s: need a non-block type nrrd", me); airMopError(mop); return 1; } if (!(nin->dim == 3 + baseDim)) { biffAddf(GAGE, "%s: nrrd should be %u-D, not %u-D", me, 3 + baseDim, nin->dim); airMopError(mop); return 1; } ax[0] = &(nin->axis[baseDim+0]); ax[1] = &(nin->axis[baseDim+1]); ax[2] = &(nin->axis[baseDim+2]); statCalc[0] = nrrdSpacingCalculate(nin, baseDim + 0, spcCalc + 0, vecCalc[0]); statCalc[1] = nrrdSpacingCalculate(nin, baseDim + 1, spcCalc + 1, vecCalc[1]); statCalc[2] = nrrdSpacingCalculate(nin, baseDim + 2, spcCalc + 2, vecCalc[2]); /* see if nrrdSpacingCalculate ever *failed* */ if (nrrdSpacingStatusUnknown == statCalc[0] || nrrdSpacingStatusUnknown == statCalc[1] || nrrdSpacingStatusUnknown == statCalc[2]) { biffAddf(GAGE, "%s: nrrdSpacingCalculate trouble on axis %d, %d, or %d", me, baseDim + 0, baseDim + 1, baseDim + 2); airMopError(mop); return 1; } if (!( statCalc[0] == statCalc[1] && statCalc[1] == statCalc[2] )) { biffAddf(GAGE, "%s: inconsistent spacing information on axes " "%u (%s), %u (%s), and %u (%s)", me, baseDim + 0, airEnumDesc(nrrdSpacingStatus, statCalc[0]), baseDim + 1, airEnumDesc(nrrdSpacingStatus, statCalc[1]), baseDim + 2, airEnumDesc(nrrdSpacingStatus, statCalc[2])); airMopError(mop); return 1; } /* this simplifies reasoning in the code that follows */ status = statCalc[0]; /* zero spacing would be problematic */ if (0 == spcCalc[0] && 0 == spcCalc[1] && 0 == spcCalc[2]) { biffAddf(GAGE, "%s: spacings (%g,%g,%g) for axes %d,%d,%d not all " "non-zero", me, spcCalc[1], spcCalc[1], spcCalc[2], baseDim+0, baseDim+1, baseDim+2); airMopError(mop); return 1; } /* error checking based on status */ if (nrrdSpacingStatusScalarWithSpace == status) { biffAddf(GAGE, "%s: sorry, can't handle per-axis spacing that isn't part " "of a surrounding world space (%s)", me, airEnumStr(nrrdSpacingStatus, status)); airMopError(mop); return 1; } /* we no longer allow a nrrd to come in with no spacing info at all */ if (nrrdSpacingStatusNone == status) { biffAddf(GAGE, "%s: sorry, need some spacing info for spatial axes " "%u, %u, %u", me, baseDim+0, baseDim+1, baseDim+2); airMopError(mop); return 1; } /* actually, there shouldn't be any other options for spacing status besides these too; this is just being careful */ if (!( nrrdSpacingStatusDirection == status || nrrdSpacingStatusScalarNoSpace == status )) { biffAddf(GAGE, "%s: sorry, can only handle spacing status %d (%s) " "or %d (%s), not %d (%s)", me, nrrdSpacingStatusDirection, airEnumStr(nrrdSpacingStatus, nrrdSpacingStatusDirection), nrrdSpacingStatusScalarNoSpace, airEnumStr(nrrdSpacingStatus, nrrdSpacingStatusScalarNoSpace), status, airEnumStr(nrrdSpacingStatus, status)); airMopError(mop); return 1; } if (nrrdSpacingStatusDirection == status) { shape->fromOrientation = AIR_TRUE; if (3 != nin->spaceDim) { biffAddf(GAGE, "%s: orientation space dimension %d != 3", me, nin->spaceDim); airMopError(mop); return 1; } } else { shape->fromOrientation = AIR_FALSE; } /* ------ find centering (set shape->center) */ /* NOTE: when the volume is being crammed in a bi-unit cube, the centering will actually affect the positions of the samples. Otherwise, (having full orientation, or using orientationFromSpacing), the centering will only affect the probe-able bounds of the volume, but the sample positions in space don't depend on centering */ cx = ax[0]->center; cy = ax[1]->center; cz = ax[2]->center; if (!( cx == cy && cy == cz )) { biffAddf(GAGE, "%s: axes %d,%d,%d centerings (%s,%s,%s) not all equal", me, baseDim+0, baseDim+1, baseDim+2, airEnumStr(nrrdCenter, cx), airEnumStr(nrrdCenter, cy), airEnumStr(nrrdCenter, cz)); airMopError(mop); return 1; } /* Hopefully, ctx->parm.defaultCenter == shape->defaultCenter; and this worry will be moot if ctx->parm.defaultCenter goes away */ shape->center = (nrrdCenterUnknown != cx ? cx /* cx == cy == cz, by above */ : (ctx ? ctx->parm.defaultCenter : shape->defaultCenter)); /* ------ find sizes (set shape->size[0,1,2]) */ shape->size[0] = ax[0]->size; shape->size[1] = ax[1]->size; shape->size[2] = ax[2]->size; minsize = (nrrdCenterCell == shape->center ? 1 : 2); /* this can't be relaxed in the face of having full orientation info, because even then, you can't have a non-zero probe-able volume if there's only one sample along a node-centered axis */ if (!(shape->size[0] >= minsize && shape->size[1] >= minsize && shape->size[2] >= minsize )) { biffAddf(GAGE, "%s: sizes (%u,%u,%u) must all be >= %u " "(min number of %s-centered samples)", me, shape->size[0], shape->size[1], shape->size[2], minsize, airEnumStr(nrrdCenter, shape->center)); airMopError(mop); return 1; } /* ------ find spacings[0,1,2] and ItoW matrix */ /* Hopefully, ctx->parm.orientationFromSpacing and shape->orientationFromSpacing don't represent competing interests; this worry will be moot if ctx->parm.orientationFromSpacing goes away */ ofspc = ((ctx && ctx->parm.orientationFromSpacing) || shape->orientationFromSpacing); if (shape->fromOrientation || ofspc) { if (ofspc) { /* need abs() in case an axis had negative spacing */ ELL_3V_ABS(shape->spacing, spcCalc); ELL_3V_SET(vecCalc[0], airSgn(spcCalc[0]), 0.0, 0.0); ELL_3V_SET(vecCalc[1], 0.0, airSgn(spcCalc[1]), 0.0); ELL_3V_SET(vecCalc[2], 0.0, 0.0, airSgn(spcCalc[2])); } else { ELL_3V_COPY(shape->spacing, spcCalc); /* vecCalc set by nrrdSpacingCalculate */ } if (shape->fromOrientation) { /* if the spaceOrigin isn't set, this will be all NaNs */ nrrdSpaceOriginGet(nin, orig); } else { /* sorry, if you want to specify an image origin that over-rides the behavior of centering the volume at (0,0,0), then it has to be done through the full orientation info. That is, we don't want to use nrrdOriginCalculate() because otherwise the logic gets too complicated */ ELL_3V_SET(orig, AIR_NAN, AIR_NAN, AIR_NAN); } if (!ELL_3V_EXISTS(orig)) { /* don't have origin, for whatever reason; center volume on (0,0,0) */ ELL_3V_SET(orig, 0.0, 0.0, 0.0); ELL_3V_SCALE_INCR(orig, -(shape->size[0] - 1.0)*shape->spacing[0]/2.0, vecCalc[0]); ELL_3V_SCALE_INCR(orig, -(shape->size[1] - 1.0)*shape->spacing[1]/2.0, vecCalc[1]); ELL_3V_SCALE_INCR(orig, -(shape->size[2] - 1.0)*shape->spacing[2]/2.0, vecCalc[2]); } vecD[3] = 0; ELL_3V_SCALE(vecD, spcCalc[0], vecCalc[0]); ELL_4MV_COL0_SET(shape->ItoW, vecD); ELL_3V_SCALE(vecD, spcCalc[1], vecCalc[1]); ELL_4MV_COL1_SET(shape->ItoW, vecD); ELL_3V_SCALE(vecD, spcCalc[2], vecCalc[2]); ELL_4MV_COL2_SET(shape->ItoW, vecD); vecD[3] = 1; ELL_3V_COPY(vecD, orig); ELL_4MV_COL3_SET(shape->ItoW, vecD); /* fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[0], vecCalc[0][0], vecCalc[0][1], vecCalc[0][2]); fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[1], vecCalc[1][0], vecCalc[1][1], vecCalc[1][2]); fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[2], vecCalc[2][0], vecCalc[2][1], vecCalc[2][2]); */ /* fprintf(stderr, "%s: ItoW = %g %g %g %g\n", me, shape->ItoW[ 0], shape->ItoW[ 1], shape->ItoW[ 2], shape->ItoW[ 3]); fprintf(stderr, "%s: %g %g %g %g\n", me, shape->ItoW[ 4], shape->ItoW[ 5], shape->ItoW[ 6], shape->ItoW[ 7]); fprintf(stderr, "%s: %g %g %g %g\n", me, shape->ItoW[ 8], shape->ItoW[ 9], shape->ItoW[10], shape->ItoW[11]); fprintf(stderr, "%s: %g %g %g %g\n", me, shape->ItoW[12], shape->ItoW[13], shape->ItoW[14], shape->ItoW[15]); */ } else { /* not (shape->fromOrientation || ofspc) */ double maxLen, volHalfLen[3]; size_t num[3]; /* ------ learn lengths for bounding nrrd in bi-unit cube */ ELL_3V_ABS(shape->spacing, spcCalc); maxLen = 0.0; for (ai=0; ai<=2; ai++) { num[ai] = (nrrdCenterNode == shape->center ? shape->size[ai]-1 : shape->size[ai]); volHalfLen[ai] = num[ai]*shape->spacing[ai]; maxLen = AIR_MAX(maxLen, volHalfLen[ai]); } /* Thu Dec 13 02:45:01 EST 2007 fixed long-standing bug in handling vols without full orientation info: spacing[ai] was never scaled to account for being crammed into the bi-unit cube!! */ for (ai=0; ai<=2; ai++) { volHalfLen[ai] /= maxLen; shape->spacing[ai] = 2*volHalfLen[ai]/num[ai]; } ELL_3V_SET(vecC, 0, 0, 0); shapeUnitItoW(shape, vecA, vecC, volHalfLen); ELL_3V_SET(vecC, 1, 0, 0); shapeUnitItoW(shape, vecB, vecC, volHalfLen); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL0_SET(shape->ItoW, vecD); ELL_3V_SET(vecC, 0, 1, 0); shapeUnitItoW(shape, vecB, vecC, volHalfLen); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL1_SET(shape->ItoW, vecD); ELL_3V_SET(vecC, 0, 0, 1); shapeUnitItoW(shape, vecB, vecC, volHalfLen); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL2_SET(shape->ItoW, vecD); vecA[3] = 1; ELL_4MV_COL3_SET(shape->ItoW, vecA); } /* ------ set the rest of the matrices */ ell_4m_inv_d(shape->WtoI, shape->ItoW); ELL_34M_EXTRACT(matA, shape->ItoW); ell_3m_inv_d(shape->ItoWSubInv, matA); ELL_3M_TRANSPOSE(shape->ItoWSubInvTransp, shape->ItoWSubInv); airMopOkay(mop); return 0; } int gageShapeSet(gageShape *shape, const Nrrd *nin, int baseDim) { static const char me[]="gageShapeSet"; if (_gageShapeSet(NULL, shape, nin, baseDim)) { biffAddf(GAGE, "%s: trouble", me); return 1; } return 0; } /* ** this wasn't being used at all void gageShapeUnitWtoI(gageShape *shape, double indx[3], double world[3]) { int i; if (nrrdCenterNode == shape->center) { for (i=0; i<=2; i++) { indx[i] = NRRD_NODE_IDX(-shape->volHalfLen[i], shape->volHalfLen[i], shape->size[i], world[i]); } } else { for (i=0; i<=2; i++) { indx[i] = NRRD_CELL_IDX(-shape->volHalfLen[i], shape->volHalfLen[i], shape->size[i], world[i]); } } } */ void gageShapeWtoI(const gageShape *shape, double _indx[3], const double _world[3]) { /* static const char me[]="gageShapeWtoI"; */ double indx[4], world[4]; /* fprintf(stderr, "!%s: hello %p %p %p; %p\n", me, shape, _indx, _world, shape->WtoI); */ ELL_3V_COPY(world, _world); world[3] = 1.0; ELL_4MV_MUL(indx, shape->WtoI, world); ELL_3V_SCALE(_indx, 1.0/indx[3], indx); } void gageShapeItoW(const gageShape *shape, double _world[3], const double _indx[3]) { double world[4], indx[4]; ELL_3V_COPY(indx, _indx); indx[3] = 1.0; ELL_4MV_MUL(world, shape->ItoW, indx); ELL_3V_SCALE(_world, 1.0/world[3], world); } /* ******** gageShapeEqual ** ** shapes not being equal is a biffable error, ** returning 0 signifies this "error" ** returning 1 means no error, they ARE equal */ int gageShapeEqual(const gageShape *shape1, const char *_name1, const gageShape *shape2, const char *_name2) { static const char me[]="gageShapeEqual"; const char *name1, *name2, what[] = "???"; if (!( shape1 && shape2 )) { biffAddf(GAGE, "%s: can't judge equality w/ NULL pointer", me); return 0; } name1 = _name1 ? _name1 : what; name2 = _name2 ? _name2 : what; if (!( shape1->fromOrientation == shape2->fromOrientation )) { biffAddf(GAGE, "%s: fromOrientation of %s (%s) != %s's (%s)", me, name1, shape1->fromOrientation ? "true" : "false", name2, shape2->fromOrientation ? "true" : "false"); return 0; } if (!( shape1->size[0] == shape2->size[0] && shape1->size[1] == shape2->size[1] && shape1->size[2] == shape2->size[2] )) { biffAddf(GAGE, "%s: dimensions of %s (%u,%u,%u) != %s's (%u,%u,%u)", me, name1, shape1->size[0], shape1->size[1], shape1->size[2], name2, shape2->size[0], shape2->size[1], shape2->size[2]); return 0; } if (shape1->fromOrientation) { if (!( ELL_4M_EQUAL(shape1->ItoW, shape2->ItoW) )) { biffAddf(GAGE, "%s: ItoW matrices of %s and %s not the same", me, name1, name2); return 0; } } else { if (!( shape1->spacing[0] == shape2->spacing[0] && shape1->spacing[1] == shape2->spacing[1] && shape1->spacing[2] == shape2->spacing[2] )) { biffAddf(GAGE, "%s: spacings of %s (%g,%g,%g) != %s's (%g,%g,%g)", me, name1, shape1->spacing[0], shape1->spacing[1], shape1->spacing[2], name2, shape2->spacing[0], shape2->spacing[1], shape2->spacing[2]); return 0; } if (!( shape1->center == shape2->center )) { biffAddf(GAGE, "%s: centering of %s (%s) != %s's (%s)", me, name1, airEnumStr(nrrdCenter, shape1->center), name2, airEnumStr(nrrdCenter, shape2->center)); return 0; } } return 1; } void gageShapeBoundingBox(double min[3], double max[3], const gageShape *shape) { /* static const char me[]="gageShapeBoundingBox"; */ double minIdx[3], maxIdx[3], cornerIdx[8][3], tmp[3]; unsigned int ii; if (!( min && max && shape )) { return; } if (nrrdCenterNode == shape->center) { ELL_3V_SET(minIdx, 0, 0, 0); ELL_3V_SET(maxIdx, shape->size[0]-1, shape->size[1]-1, shape->size[2]-1); } else { ELL_3V_SET(minIdx, -0.5, -0.5, -0.5); ELL_3V_SET(maxIdx, shape->size[0]-0.5, shape->size[1]-0.5, shape->size[2]-0.5); } ELL_3V_SET(cornerIdx[0], minIdx[0], minIdx[1], minIdx[2]); ELL_3V_SET(cornerIdx[1], maxIdx[0], minIdx[1], minIdx[2]); ELL_3V_SET(cornerIdx[2], minIdx[0], maxIdx[1], minIdx[2]); ELL_3V_SET(cornerIdx[3], maxIdx[0], maxIdx[1], minIdx[2]); ELL_3V_SET(cornerIdx[4], minIdx[0], minIdx[1], maxIdx[2]); ELL_3V_SET(cornerIdx[5], maxIdx[0], minIdx[1], maxIdx[2]); ELL_3V_SET(cornerIdx[6], minIdx[0], maxIdx[1], maxIdx[2]); ELL_3V_SET(cornerIdx[7], maxIdx[0], maxIdx[1], maxIdx[2]); gageShapeItoW(shape, tmp, cornerIdx[0]); ELL_3V_COPY(min, tmp); ELL_3V_COPY(max, tmp); for (ii=1; ii<8; ii++) { gageShapeItoW(shape, tmp, cornerIdx[ii]); ELL_3V_MIN(min, min, tmp); ELL_3V_MAX(max, max, tmp); } return; } teem-1.11.0~svn6057/src/gage/kind.c0000664000175000017500000002135212165631065016400 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" /* ******** gageKindCheck ** ** some some basic checking of the gageEntryItem array (the "table") for ** the sorts of mistakes that may be introduced by its hand-coding, although ** theoretically this is good for dynamically-generated gageKinds as well. */ int gageKindCheck(const gageKind *kind) { static const char me[]="gageKindCheck"; int pitem, pindex, alen; int ii, pi; gageItemEntry *item; if (!kind) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (kind->itemMax > GAGE_ITEM_MAX) { biffAddf(GAGE, "%s: kind \"%s\" item max %d > GAGE_ITEM_MAX %d", me, kind->name, kind->itemMax, GAGE_ITEM_MAX); return 1; } for (ii=1; ii<=kind->itemMax; ii++) { item = kind->table + ii; if (ii != item->enumVal) { biffAddf(GAGE, "%s: \"%s\"-kind \"%s\" (item %d) has enumVal %d (not %d)", me, kind->name, airEnumStr(kind->enm, ii), ii, item->enumVal, ii); return 1; } alen = item->answerLength; if (!(1 <= alen)) { if (kind->dynamicAlloc) { biffAddf(GAGE, "%s: (dynamic) \"%s\"-kind \"%s\" (item %d) " "answerLength (%d) not set?", me, kind->name, airEnumStr(kind->enm, ii), ii, alen); } else { biffAddf(GAGE, "%s: \"%s\"-kind \"%s\" (item %d) has invalid " "answerLength %d", me, kind->name, airEnumStr(kind->enm, ii), ii, alen); } return 1; } if (!(AIR_IN_CL(0, item->needDeriv, GAGE_DERIV_MAX))) { biffAddf(GAGE, "%s: \"%s\"-kind \"%s\" (item %d) has invalid needDeriv %d " "(not in [0,%u])\n", me, kind->name, airEnumStr(kind->enm, ii), ii, item->needDeriv, GAGE_DERIV_MAX); return 1; } for (pi=0; piprereq[pi] )) { if (kind->dynamicAlloc) { biffAddf(GAGE, "%s: (dynamic) \"%s\"-kind \"%s\" (item %d) " "prereq %d (%d) not set?", me, kind->name, airEnumStr(kind->enm, ii), ii, pi, item->prereq[pi]); } else { biffAddf(GAGE, "%s: \"%s\"-kind \"%s\" (item %d) has invalid " "prereq %d (%d)", me, kind->name, airEnumStr(kind->enm, ii), ii, pi, item->prereq[pi]); } return 1; } } pitem = item->parentItem; pindex = item->parentIndex; if (0 != pitem) { if (1 == ii) { biffAddf(GAGE, "%s: first item (index 1) of \"%s\"-kind can't " "be a sub-item (wanted parent index %d)", me, kind->name, pitem); return 1; } if (!(AIR_IN_CL(1, pitem, kind->itemMax))) { biffAddf(GAGE, "%s: item %d of \"%s\"-kind wants parent item %d " "outside valid range [0..%d]", me, ii, kind->name, pitem, kind->itemMax); return 1; } if (0 != kind->table[pitem].parentItem) { biffAddf(GAGE, "%s: item %d of \"%s\"-kind has parent %d which " "wants to have parent %d: can't have sub-sub-items", me, ii, kind->name, pitem, kind->table[pitem].parentItem); return 1; } if (!( 0 <= pindex && ((unsigned int)pindex + alen <= kind->table[pitem].answerLength) )) { biffAddf(GAGE, "%s: item %d of \"%s\"-kind wants index range [%d,%d] " "of parent %d, which isn't in valid range [0,%d]", me, ii, kind->name, pindex, pindex + alen - 1, pitem, kind->table[pitem].answerLength - 1); return 1; } } } return 0; } unsigned int gageKindTotalAnswerLength(const gageKind *kind) { static const char me[]="gageKindTotalAnswerLength"; char *err; unsigned int alen; int ii; if (gageKindCheck(kind)) { err = biffGetDone(GAGE); fprintf(stderr, "%s: PANIC:\n %s", me, err); free(err); exit(1); } alen = 0; for (ii=1; ii<=kind->itemMax; ii++) { alen += (0 == kind->table[ii].parentItem ? kind->table[ii].answerLength : 0); } return alen; } /* ** _gageKindAnswerOffset ** ** return the location of the item in the master answer array ** ** I don't think this will work if there are sub-sub-items */ int _gageKindAnswerOffset(const gageKind *kind, int item) { int parent, ii; if (1 >= item) { /* the first item always has zero offset */ return 0; } /* else we're not the first */ parent = kind->table[item].parentItem; if (0 != parent) { /* we're a sub-item */ return (kind->table[item].parentIndex + _gageKindAnswerOffset(kind, parent)); } /* else we're not a sub-item: find the first previous non-sub-item */ ii = item-1; while (0 != kind->table[ii].parentItem) { /* gageKindCheck ensures that item 1 is not a sub-item */ ii--; } return (kind->table[ii].answerLength + _gageKindAnswerOffset(kind, ii)); } unsigned int gageKindAnswerLength(const gageKind *kind, int item) { static const char me[]="gageKindAnswerLength"; char *err; if (gageKindCheck(kind)) { err = biffGetDone(GAGE); fprintf(stderr, "%s: PANIC:\n %s", me, err); free(err); exit(1); } return (!airEnumValCheck(kind->enm, item) ? kind->table[item].answerLength : 0); } int gageKindAnswerOffset(const gageKind *kind, int item) { static const char me[]="gageKindAnswerOffset"; char *err; if (gageKindCheck(kind)) { err = biffGetDone(GAGE); fprintf(stderr, "%s: PANIC:\n %s", me, err); free(err); exit(1); } return _gageKindAnswerOffset(kind, item); } /* ** so that you can see if a given volume will work as the given kind */ int gageKindVolumeCheck(const gageKind *kind, const Nrrd *nrrd) { static const char me[]="gageKindVolumeCheck"; if (!(kind && nrrd)) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (nrrdCheck(nrrd)) { biffMovef(GAGE, NRRD, "%s: problem with nrrd", me); return 1; } if (!(nrrd->dim == 3 + kind->baseDim)) { biffAddf(GAGE, "%s: nrrd should be %u-D, not %u-D", me, 3 + kind->baseDim, nrrd->dim); return 1; } if (nrrdTypeBlock == nrrd->type) { biffAddf(GAGE, "%s: can't handle %s-type volumes", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (kind->baseDim) { char stmp[AIR_STRLEN_SMALL]; if (1 == kind->baseDim) { if (kind->valLen != nrrd->axis[0].size) { biffAddf(GAGE, "%s: %s kind needs %u axis 0 values, not %s", me, kind->name, kind->valLen, airSprintSize_t(stmp, nrrd->axis[0].size)); return 1; } } else { /* actually there is yet to be a kind in Teem for which kind->baseDim > 1, but this code would work in that case */ unsigned int axi; size_t numsub; /* number of samples sub base dim */ numsub = 1; for (axi=0; axibaseDim; axi++) { numsub *= nrrd->axis[axi].size; } if (kind->valLen != numsub) { biffAddf(GAGE, "%s: %s kind needs %u values below baseDim axis %u, " "not %s", me, kind->name,kind->valLen, kind->baseDim, airSprintSize_t(stmp, numsub)); return 1; } } } /* this eventually calls _gageShapeSet(), which, for purely historical reasons, does the brunt of the error checking, some of which is almost certainly redundant with checks above . . . */ if (gageVolumeCheck(NULL, nrrd, kind)) { biffAddf(GAGE, "%s: trouble", me); return 1; } return 0; } teem-1.11.0~svn6057/src/gage/pvl.c0000664000175000017500000002646312165631065016264 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" /* ******** gageVolumeCheck() ** ** checks whether a given volume is valid for the given kind ** and the given parameter settings in the context ** ** Note that ctx is simply passed to _gageShapeSet(), with no NULL-ity ** test- which is fine- it just means that the check is not specific ** to the parameters that can be set in the gageContext. */ int gageVolumeCheck(const gageContext *ctx, const Nrrd *nin, const gageKind *kind) { static const char me[]="gageVolumeCheck"; gageShape shape; gageShapeReset(&shape); if (_gageShapeSet(ctx, &shape, nin, kind->baseDim)) { biffAddf(GAGE, "%s: trouble setting volume as %s kind", me, kind->name); return 1; } return 0; } /* ******** gagePerVolumeNew() ** ** creates a new pervolume of a known kind, but nothing besides the ** answer array is allocated ** ** uses biff primarily because of the error checking in gageVolumeCheck() */ gagePerVolume * gagePerVolumeNew(gageContext *ctx, const Nrrd *nin, const gageKind *kind) { static const char me[]="gagePerVolumeNew"; gagePerVolume *pvl; int ii; airArray *mop; if (!( nin && kind )) { biffAddf(GAGE, "%s: got NULL pointer (%p, %p, or %p)", me, AIR_VOIDP(ctx), AIR_CVOIDP(nin), AIR_CVOIDP(kind)); return NULL; } /* Craziness: since circa 2003, the test below was to call gageVolumeCheck, which is now just a wrapper around _gageShapeSet(), and which never actually does the important checks of gageKindVolumeCheck (which in turn eventually calls gageVolumeCheck). This means that basic errors were not being caught, like having the wrong number of samples along axis 0 for non-scalar kinds. These various functions need to be simplified soon */ if (gageKindVolumeCheck(kind, nin)) { biffAddf(GAGE, "%s: problem with volume as %s kind", me, kind->name); return NULL; } pvl = AIR_CALLOC(1, gagePerVolume); if (!pvl) { biffAddf(GAGE, "%s: couldn't alloc gagePerVolume", me); return NULL; } mop = airMopNew(); airMopAdd(mop, pvl, airFree, airMopOnError); pvl->verbose = gageDefVerbose; pvl->kind = kind; GAGE_QUERY_RESET(pvl->query); for (ii=0; ii<=GAGE_DERIV_MAX; ii++) { ctx->needD[ii] = AIR_FALSE; } pvl->nin = nin; for (ii=gagePvlFlagUnknown+1; iiflag[ii] = AIR_FALSE; } pvl->iv3 = pvl->iv2 = pvl->iv1 = NULL; pvl->lup = nrrdDLookup[nin->type]; pvl->answer = AIR_CALLOC(gageKindTotalAnswerLength(kind), double); airMopAdd(mop, pvl->answer, airFree, airMopOnError); pvl->directAnswer = AIR_CALLOC(kind->itemMax+1, double*); airMopAdd(mop, pvl->directAnswer, airFree, airMopOnError); if (!(pvl->answer && pvl->directAnswer)) { biffAddf(GAGE, "%s: couldn't alloc answer and directAnswer arrays", me); airMopError(mop); return NULL; } for (ii=1; ii<=kind->itemMax; ii++) { pvl->directAnswer[ii] = pvl->answer + gageKindAnswerOffset(kind, ii); } pvl->flag[gagePvlFlagVolume] = AIR_TRUE; if (kind->pvlDataNew) { if (!(pvl->data = kind->pvlDataNew(kind))) { biffAddf(GAGE, "%s: double creating gagePerVolume data", me); airMopError(mop); return NULL; } } else { pvl->data = NULL; } airMopOkay(mop); return pvl; } /* ** _gagePerVolumeCopy() ** ** copies a pervolume for use in a copied context, and probably ** should only be called by gageContextCopy() */ gagePerVolume * _gagePerVolumeCopy(gagePerVolume *pvl, unsigned int fd) { static const char me[]="gagePerVolumeCopy"; gagePerVolume *nvl; int ii; airArray *mop; nvl = AIR_CALLOC(1, gagePerVolume); if (!nvl) { biffAddf(GAGE, "%s: couldn't create new pervolume", me); return NULL; } mop = airMopNew(); airMopAdd(mop, nvl, airFree, airMopOnError); /* we should probably restrict ourselves to gage API calls, but given the constant state of gage construction, this seems much simpler. Pointers to per-pervolume-allocated arrays are fixed below */ memcpy(nvl, pvl, sizeof(gagePerVolume)); nvl->iv3 = AIR_CALLOC(fd*fd*fd*nvl->kind->valLen, double); nvl->iv2 = AIR_CALLOC(fd*fd*nvl->kind->valLen, double); nvl->iv1 = AIR_CALLOC(fd*nvl->kind->valLen, double); airMopAdd(mop, nvl->iv3, airFree, airMopOnError); airMopAdd(mop, nvl->iv2, airFree, airMopOnError); airMopAdd(mop, nvl->iv1, airFree, airMopOnError); nvl->answer = AIR_CALLOC(gageKindTotalAnswerLength(nvl->kind), double); airMopAdd(mop, nvl->answer, airFree, airMopOnError); nvl->directAnswer = AIR_CALLOC(nvl->kind->itemMax+1, double*); airMopAdd(mop, nvl->directAnswer, airFree, airMopOnError); if (!( nvl->iv3 && nvl->iv2 && nvl->iv1 && nvl->answer && nvl->directAnswer )) { biffAddf(GAGE, "%s: couldn't allocate all caches " "(fd=%u, valLen=%u, totAnsLen=%u, itemMax=%u)", me, fd, nvl->kind->valLen, gageKindTotalAnswerLength(nvl->kind), nvl->kind->itemMax); airMopError(mop); return NULL; } for (ii=1; ii<=pvl->kind->itemMax; ii++) { nvl->directAnswer[ii] = nvl->answer + gageKindAnswerOffset(pvl->kind, ii); } if (pvl->kind->pvlDataCopy) { if (!(nvl->data = pvl->kind->pvlDataCopy(pvl->kind, pvl->data))) { biffAddf(GAGE, "%s: double copying gagePerVolume data", me); airMopError(mop); return NULL; } /* HEY: pvlDataNix takes 2 arguments; so we can't mop nvl->data, so its a good thing that we created nvl->data last */ } else { nvl->data = NULL; } airMopOkay(mop); return nvl; } /* ******** gagePerVolumeNix() ** ** frees all dynamically allocated memory assocated with a pervolume ** ** does not use biff */ gagePerVolume * gagePerVolumeNix(gagePerVolume *pvl) { if (pvl) { if (pvl->kind->pvlDataNix) { pvl->data = pvl->kind->pvlDataNix(pvl->kind, pvl->data); } pvl->iv3 = (double *)airFree(pvl->iv3); pvl->iv2 = (double *)airFree(pvl->iv2); pvl->iv1 = (double *)airFree(pvl->iv1); pvl->answer = (double *)airFree(pvl->answer); pvl->directAnswer = (double **)airFree(pvl->directAnswer); airFree(pvl); } return NULL; } /* ******** gageAnswerPointer() ** ** way of getting a pointer to a specific answer in a pervolume's ans array ** ** Returns NULL if the item is invalid, but there is no other error checking. ** In particular, this does not let you know if the item is actually part ** of the current query. ** */ const double * gageAnswerPointer(const gageContext *ctx, const gagePerVolume *pvl, int item) { const double *ret; AIR_UNUSED(ctx); if (pvl && !airEnumValCheck(pvl->kind->enm, item)) { ret = pvl->answer + gageKindAnswerOffset(pvl->kind, item); } else { ret = NULL; } return ret; } /* non-const version of the above */ double * _gageAnswerPointer(const gageContext *ctx, gagePerVolume *pvl, int item) { double *ret; AIR_UNUSED(ctx); if (pvl && !airEnumValCheck(pvl->kind->enm, item)) { ret = pvl->answer + gageKindAnswerOffset(pvl->kind, item); } else { ret = NULL; } return ret; } unsigned int gageAnswerLength(const gageContext *ctx, const gagePerVolume *pvl, int item) { unsigned int ret; AIR_UNUSED(ctx); if (pvl && !airEnumValCheck(pvl->kind->enm, item)) { ret = gageKindAnswerLength(pvl->kind, item); } else { ret = 0; } return ret; } int gageQueryReset(gageContext *ctx, gagePerVolume *pvl) { static const char me[]="gageQueryReset"; AIR_UNUSED(ctx); if (!( pvl )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } GAGE_QUERY_RESET(pvl->query); return 0; } /* ******** gageQuerySet() ** ** sets a query in a pervolume. Does recursive expansion of query ** to cover all prerequisite measures. ** ** Sets: pvl->query ** ** the gageContext is not actually used here, but I'm cautiously ** including it in case its used in the future. */ int gageQuerySet(gageContext *ctx, gagePerVolume *pvl, gageQuery query) { static const char me[]="gageQuerySet"; gageQuery lastQuery; int pi, ii; AIR_UNUSED(ctx); if (!( pvl )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } GAGE_QUERY_COPY(pvl->query, query); if (pvl->verbose) { fprintf(stderr, "%s: original ", me); gageQueryPrint(stderr, pvl->kind, pvl->query); } /* recursive expansion of prerequisites */ do { GAGE_QUERY_COPY(lastQuery, pvl->query); ii = pvl->kind->itemMax+1; do { ii--; if (GAGE_QUERY_ITEM_TEST(pvl->query, ii)) { for (pi=0; pikind->table[ii].prereq[pi]) { GAGE_QUERY_ITEM_ON(pvl->query, pvl->kind->table[ii].prereq[pi]); } } } } while (ii); } while (!GAGE_QUERY_EQUAL(pvl->query, lastQuery)); if (pvl->verbose) { fprintf(stderr, "%s: expanded ", me); gageQueryPrint(stderr, pvl->kind, pvl->query); } /* doing this kind of error checking here is not really the way gage should work-- it should be done at the time of gageUpdate()-- but the novelty of pvl->data encourages putting new smarts at superficial levels instead of deeper levels */ if (!pvl->data) { for (ii=1; ii<=pvl->kind->itemMax; ii++) { if (GAGE_QUERY_ITEM_TEST(pvl->query, ii) && pvl->kind->table[ii].needData) { biffAddf(GAGE, "%s: item %d (%s) needs data, but pvl->data is NULL", me, ii, airEnumStr(pvl->kind->enm, ii)); return 1; } } } pvl->flag[gagePvlFlagQuery] = AIR_TRUE; return 0; } int gageQueryAdd(gageContext *ctx, gagePerVolume *pvl, gageQuery query) { static const char me[]="gageQueryAdd"; if (!( pvl )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } GAGE_QUERY_ADD(pvl->query, query); if (gageQuerySet(ctx, pvl, pvl->query)) { biffAddf(GAGE, "%s: trouble", me); return 1; } return 0; } int gageQueryItemOn(gageContext *ctx, gagePerVolume *pvl, int item) { static const char me[]="gageQueryItemOn"; if (!( pvl )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(pvl->kind->enm, item)) { biffAddf(GAGE, "%s: %d not a valid %s value", me, item, pvl->kind->enm->name); return 1; } GAGE_QUERY_ITEM_ON(pvl->query, item); if (gageQuerySet(ctx, pvl, pvl->query)) { biffAddf(GAGE, "%s: trouble", me); return 1; } return 0; } teem-1.11.0~svn6057/src/gage/optimsig.c0000664000175000017500000013652512202223212017276 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" /* static int debugging = 0; static int debugii; */ static airArray *debugReconErrArr = NULL; static double *debugReconErr = NULL; static char *debugReconErrName = NULL; /* ** learned: ** ** -- debug high/discontinuous errors at the low sigmas: was because ** cut-off was insufficient to prevent some discontinuous change in ** kernel values: increased minimum support in the kernel itself, and ** now using larger cut-offs. ** ** -- also, separately from this problem, you can have minima in the ** inf error (in imgMeasr) *not* at sample points, apparently simply ** because of how the hermite interpolation works (but this is ** troubling) ** ** -- do now have a different minimization scheme for allMeasr=Linf, ** but this may still be a work in progress. Recognizing that this is ** essentially seeking to find a uniform re-parameterization of ** something with a hidden non-uniform parameterization, we could ** probably implement a simple global warping of control points within ** the implied non-uniform domain. */ /* this limits how big the kernel can get with a single evaluation of nrrdKernelDiscreteGaussian; there are some numerical issues with large kernels that need ironing out */ #define GOOD_SIGMA_MAX 5 #define N -1 /* ** NOTE: The idea for this table originated with Raul San Jose Estepar; ** GLK recomputed it optimizing for 3D recon, but ** NOTE: there are probably still be bugs in this; look at the ** "HEY: bug?" notes below, the same problem occurs elsewhere ** ** Basic indexing idea: [sigma max][total # samples][which sample] ** ** "sigma max" can't be 0; smallest value is 1 ** ==> index with (sigma max)-1 ** biggest value is GAGE_OPTIMSIG_SIGMA_MAX, ** ==> biggest index is GAGE_OPTIMSIG_SIGMA_MAX-1 ** ==> allocate for GAGE_OPTIMSIG_SIGMA_MAX ** ** "total # samples" can't be 0, or 1, smallest value is 2 ** ==> index with (total # samples)-2 ** biggest value is GAGE_OPTIMSIG_SAMPLES_MAXNUM ** ==> biggest index is GAGE_OPTIMSIG_SAMPLES_MAXNUM-2 ** ==> allocate for GAGE_OPTIMSIG_SAMPLES_MAXNUM-1 ** ** "which sample" ranges from 0 to GAGE_OPTIMSIG_SAMPLES_MAXNUM-1 ** ==> allocate for GAGE_OPTIMSIG_SAMPLES_MAXNUM */ static double _optimSigTable[GAGE_OPTIMSIG_SIGMA_MAX][GAGE_OPTIMSIG_SAMPLES_MAXNUM-1][GAGE_OPTIMSIG_SAMPLES_MAXNUM] = { { {0,1,N,N,N,N,N,N,N,N,N}, {0,0.5279398,1,N,N,N,N,N,N,N,N}, {0,0.30728838,0.59967405,1,N,N,N,N,N,N,N}, {0,0.25022203,0.47050092,0.69525677,1,N,N,N,N,N,N}, {0,0.17127343,0.39234546,0.56356072,0.75660759,1,N,N,N,N,N}, {0,0.16795139,0.37100673,0.51324213,0.65655005,0.81952846,1,N,N,N,N}, {0,0.1662873,0.34969759,0.46556041,0.55324608,0.68717259,0.83465695,1,N,N,N}, {0,0.12720504,0.22565289,0.28316727,0.44209728,0.58615023,0.75034028,0.87391609,1,N,N}, {0,0.12836272 /* HEY: bug? should be < 0.12720504 */,0.22926401,0.27715567,0.43546647,0.56471503,0.69411868,0.80830419,0.89314467,1,N}, {0,0.13169055 /* HEY: bug? should be < 0.12720504 */,0.23498112,0.26570156,0.42672107,0.54272485,0.62969965,0.73375762,0.76996493,0.89293921,1} }, { {0,2,N,N,N,N,N,N,N,N,N}, {0,0.75118494,2,N,N,N,N,N,N,N,N}, {0,0.55478472,1.1535828,2,N,N,N,N,N,N,N}, {0,0.49007216,0.8412028,1.308665,2,N,N,N,N,N,N}, {0,0.29460263,0.57445061,0.93797231,1.368475,2,N,N,N,N,N}, {0,0.2506085,0.49080029,0.73882496,1.069332,1.4497081,2,N,N,N,N}, {0,0.18255657,0.42056954,0.62766695,0.87999368,1.1692151,1.5175625,2,N,N,N}, {0,0.17582123,0.40522173,0.58696139,0.79624867,1.0485514,1.2950466,1.5977446,2,N,N}, {0,0.17304537,0.39376548,0.56427032,0.75127059,0.96672511,1.187861,1.4141362,1.6921321,2,N}, {0,0.16970521,0.38116929,0.53575242,0.69498152,0.88430929,1.0844854,1.2899524,1.5211773,1.7645421,2} }, { {0,3,N,N,N,N,N,N,N,N,N}, {0,0.92324787,3,N,N,N,N,N,N,N,N}, {0,0.59671402,1.3871731,3,N,N,N,N,N,N,N}, {0,0.53303385,1.0274624,1.6725048,3,N,N,N,N,N,N}, {0,0.47298154,0.79659319,1.2379739,1.8434249,3,N,N,N,N,N}, {0,0.29337707,0.56664073,0.94871783,1.3666322,1.949043,3,N,N,N,N}, {0,0.25583801,0.52919179,0.78387552,1.1250161,1.516176,2.0632432,3,N,N,N}, {0,0.25013804,0.48255014,0.72428173,1.0308567,1.3638159,1.7629964,2.2885511,3,N,N}, {0,0.25038671,0.46448985,0.67336935,0.94502586,1.2324173,1.5780864,1.9883285,2.5002999,3,N}, {0,0.25034565,0.44725224,0.63590652,0.8669008,1.1130947,1.3942779,1.7180597,2.1408446,2.5466051,3} }, { {0,4,N,N,N,N,N,N,N,N,N}, {0,1.0342592,4,N,N,N,N,N,N,N,N}, {0,0.6341188,1.5414433,4,N,N,N,N,N,N,N}, {0,0.5523203,1.1400089,1.9595566,4,N,N,N,N,N,N}, {0,0.51082283,0.91567439,1.4275582,2.2504199,4,N,N,N,N,N}, {0,0.46390373,0.76406777,1.1620381,1.6579833,2.470386,4,N,N,N,N}, {0,0.29957226,0.58226484,0.90447241,1.318499,1.8011117,2.5972142,4,N,N,N}, {0,0.29072434,0.5657317,0.8687849,1.2413157,1.7351674,2.2752147,3.1038468,4,N,N}, {0,0.25000414,0.5027808,0.75375289,1.0744231,1.4267329,1.8665372,2.4665236,3.2203004,4,N}, {0,0.19010291,0.44269502,0.66081244,0.95829803,1.2627038,1.6005131,2.0043969,2.6440792,3.2979164,4} }, { {0,5,N,N,N,N,N,N,N,N,N}, {0,1.1176668,5,N,N,N,N,N,N,N,N}, {0,0.66791451,1.6688319,5,N,N,N,N,N,N,N}, {0,0.56513244,1.2151262,2.2046661,5,N,N,N,N,N,N}, {0,0.51955444,0.96157616,1.5293243,2.5639,5,N,N,N,N,N}, {0,0.50639188,0.83235806,1.2596023,1.8475783,2.8751452,5,N,N,N,N}, {0,0.30821687,0.60048282,1.0057166,1.4351804,2.0372179,3.0747592,5,N,N,N}, {0,0.28437388,0.560866,0.92278755,1.3049414,1.7620444,2.4607313,3.5198457,5,N,N}, {0,0.26883101,0.53947717,0.84076571,1.1986721,1.6077875,2.165575,2.9591467,3.931181,5,N}, {0,0.25029126,0.50162876,0.75587535,1.0861237,1.4452776,1.8865763,2.5002809,3.2476835,4.0337272,5} }, { {0,6,N,N,N,N,N,N,N,N,N}, {0,1.185726,6,N,N,N,N,N,N,N,N}, {0,0.69637311,1.7772807,6,N,N,N,N,N,N,N}, {0,0.57470578,1.2709187,2.4227901,6,N,N,N,N,N,N}, {0,0.52996641,1.0128419,1.632214,2.8718762,6,N,N,N,N,N}, {0,0.50426048,0.87729794,1.3428378,2.0053113,3.2981832,6,N,N,N,N}, {0,0.46658435,0.76617205,1.1726109,1.6950468,2.5514688,4.1463666,6,N,N,N}, {0,0.50030917,0.78596908,1.1486269,1.5887094,2.2150676,3.2805684,4.4828262,6,N,N}, {0,0.27919531,0.56878412,0.88591647,1.2631332,1.7201432,2.3851209,3.392889,4.6255312,6,N}, {0,0.25088972,0.50369233,0.78494686,1.1030188,1.482311,1.9812444,2.6906328,3.734978,4.7532525,6} }, { {0,7,N,N,N,N,N,N,N,N,N}, {0,1.2437892,7,N,N,N,N,N,N,N,N}, {0,0.72099203,1.8771845,7,N,N,N,N,N,N,N}, {0,0.58251196,1.3139123,2.6157444,7,N,N,N,N,N,N}, {0,0.5371021,1.0473768,1.7166929,3.1448426,7,N,N,N,N,N}, {0,0.51312029,0.92989367,1.4221185,2.2125893,3.6739931,7,N,N,N,N}, {0,0.50083971,0.84841007,1.2561073,1.8532455,2.8668625,4.7535434,7,N,N,N}, {0,0.3375614,0.63945627,1.0301709,1.4884938,2.073761,3.1614799,5.0744987,7,N,N}, {0,0.29428458,0.58668923,0.93714356,1.3736334,1.8300356,2.6405344,3.9042048,5.3097196,7,N}, {0,0.25234449,0.52068585,0.79422623,1.1273863,1.5991755,2.1453006,2.8984315,4.1899557,5.4597921,7} }, { {0,8,N,N,N,N,N,N,N,N,N}, {0,1.2942501,8,N,N,N,N,N,N,N,N}, {0,0.74332041,1.9693407,8,N,N,N,N,N,N,N}, {0,0.58823597,1.3483386,2.7880962,8,N,N,N,N,N,N}, {0,0.56661958,1.2263036,1.9593971,3.6037345,8,N,N,N,N,N}, {0,0.52106231,0.97026396,1.486012,2.3670862,4.1632919,8,N,N,N,N}, {0,0.50727636,0.86810225,1.3293955,2.0115428,3.1358411,5.3943086,8,N,N,N}, {0,0.47202346,0.77812189,1.1608884,1.6648751,2.4694417,3.9094045,5.7665443,8,N,N}, {0,0.37446901,0.66116196,1.038642,1.4625595,2.0528309,2.9814169,4.4429126,5.9815373,8,N}, {0,0.26310974,0.54373014,0.84282249,1.2090484,1.6551158,2.3275802,3.3196113,4.7216973,6.1578932,8} }, { {0,9,N,N,N,N,N,N,N,N,N}, {0,1.3413963,9,N,N,N,N,N,N,N,N}, {0,0.76222414,2.0542119,9,N,N,N,N,N,N,N}, {0,0.59559792,1.3777219,2.946173,9,N,N,N,N,N,N}, {0,0.56240517,1.1527119,1.9145473,3.6841569,9,N,N,N,N,N}, {0,0.52387071,0.98832464,1.5376476,2.5417714,4.4669261,9,N,N,N,N}, {0,0.50359035,0.87327009,1.3558764,2.0646384,3.3180211,5.9420524,9,N,N,N}, {0,0.50140077,0.83020425,1.256588,1.7709454,2.7100441,4.4434023,6.3934889,9,N,N}, {0,0.36521655,0.65757704,1.0627806,1.5081434,2.1497617,3.1920822,4.870122,6.6418982,9,N}, {0,0.31160679,0.59032226,0.94745982,1.3620865,1.8115216,2.6007423,3.8324564,5.2064519,6.8468728,9} }, { {0,10,N,N,N,N,N,N,N,N,N}, {0,1.3838946,10,N,N,N,N,N,N,N,N}, {0,0.77946955,2.1342247,10,N,N,N,N,N,N,N}, {0,0.60070014,1.4040204,3.0944126,10,N,N,N,N,N,N}, {0,0.55609542,1.1508646,1.9495349,3.9375696,10,N,N,N,N,N}, {0,0.5350194,1.031119,1.6607633,2.8520992,5.4718146,10,N,N,N,N}, {0,0.5083549,0.90783268,1.4059756,2.1796026,3.571064,6.5497985,10,N,N,N}, {0,0.50199872,0.85233968,1.2647815,1.8777326,2.8592849,4.7821364,7.0110598,10,N,N}, {0,0.46663594,0.75212663,1.1302133,1.6134665,2.3560972,3.6558499,5.3189116,7.2945781,10,N}, {0,0.3789258,0.64023608,1.0374272,1.4685256,2.0717783,3.0241971,4.2591534,5.6669927,7.5286098,10} }, { {0,11,N,N,N,N,N,N,N,N,N}, {0,1.4234025,11,N,N,N,N,N,N,N,N}, {0,0.79513794,2.2098076,11,N,N,N,N,N,N,N}, {0,0.60728961,1.4287171,3.2358651,11,N,N,N,N,N,N}, {0,0.55890071,1.165283,2.0149148,4.1530919,11,N,N,N,N,N}, {0,0.55071467,1.0660659,1.7177736,3.0094495,6.0395317,11,N,N,N,N}, {0,0.5066433,0.89661205,1.4050072,2.2117786,3.7080047,7.0954437,11,N,N,N}, {0,0.50242329,0.86727452,1.3264461,1.9118301,2.9509099,5.1184769,7.624764,11,N,N}, {0,0.47785854,0.78873962,1.1769236,1.6880652,2.4978926,4.0288033,5.7288432,7.9420485,11,N}, {0,0.50532979,0.79486167,1.1706896,1.6148115,2.2648265,3.3499777,4.5595574,6.116312,8.2049971,11} } }; /* ** this is only for retreiving part of the table above */ int gageOptimSigSet(double *scale, unsigned int num, unsigned int sigmaMax) { static const char me[]="gageOptimSigSet"; unsigned int si; if (!scale) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!AIR_IN_CL(2, num, GAGE_OPTIMSIG_SAMPLES_MAXNUM)) { biffAddf(GAGE, "%s: requested # sigma samples %u not in known range [2,%u]", me, num, GAGE_OPTIMSIG_SAMPLES_MAXNUM); return 1; } if (!AIR_IN_CL(1, sigmaMax, GAGE_OPTIMSIG_SIGMA_MAX)) { biffAddf(GAGE, "%s: requested sigma max %u not in known range [1,%u]", me, sigmaMax, GAGE_OPTIMSIG_SIGMA_MAX); return 1; } for (si=0; si= 3)) { biffAddf(GAGE, "%s: sampleNumMax %u not >= 3", me, sampleNumMax); return NULL; } if (!(trueImgNum >= 3)) { biffAddf(GAGE, "%s: trueImgNum %u not >= 3", me, trueImgNum); return NULL; } if (!(sigmaMin >= 0 && sigmaMax > sigmaMin && cutoff > 0)) { biffAddf(GAGE, "%s: sigmaMin %g, sigmaMax %g, cutoff %g not valid", me, sigmaMin, sigmaMax, cutoff); return NULL; } oscx->dim = dim; oscx->sampleNumMax = sampleNumMax; oscx->trueImgNum = trueImgNum; oscx->sigmaRange[0] = sigmaMin; oscx->sigmaRange[1] = sigmaMax; oscx->cutoff = cutoff; /* will be set later */ oscx->kssSpec = NULL; oscx->sampleNum = 0; oscx->maxIter = 0; oscx->imgMeasr = nrrdMeasureUnknown; oscx->allMeasr = nrrdMeasureUnknown; oscx->convEps = AIR_NAN; /* allocate internal buffers based on arguments */ kparm[0] = oscx->sigmaRange[1]; kparm[1] = oscx->cutoff; support = AIR_ROUNDUP(nrrdKernelDiscreteGaussian->support(kparm)); oscx->sx = 2*support - 1; oscx->sy = dim >= 2 ? 2*support - 1 : 1; oscx->sz = dim >= 3 ? 2*support - 1 : 1; /* fprintf(stderr, "%s: max sigma = %g, cutoff %g ==> support=%u, " "3D vol size=%u x %u x %u\n", me, sigmaMax, cutoff, support, oscx->sx, oscx->sy, oscx->sz); */ oscx->nerr = nrrdNew(); oscx->ninterp = nrrdNew(); oscx->ndiff = nrrdNew(); if (nrrdMaybeAlloc_va(oscx->nerr, nrrdTypeDouble, 1, AIR_CAST(size_t, oscx->trueImgNum)) || nrrdMaybeAlloc_va(oscx->ninterp, nrrdTypeDouble, 3, AIR_CAST(size_t, oscx->sx), AIR_CAST(size_t, oscx->sy), AIR_CAST(size_t, oscx->sz)) || nrrdMaybeAlloc_va(oscx->ndiff, nrrdTypeDouble, 3, AIR_CAST(size_t, oscx->sx), AIR_CAST(size_t, oscx->sy), AIR_CAST(size_t, oscx->sz))) { biffMovef(GAGE, NRRD, "%s: couldn't allocate buffers", me); return NULL; } nrrdAxisInfoSet_va(oscx->ninterp, nrrdAxisInfoSpacing, 1.0, 1.0, 1.0); nrrdAxisInfoSet_va(oscx->ndiff, nrrdAxisInfoSpacing, 1.0, 1.0, 1.0); oscx->rhoRange[0] = _RhoOfSig(oscx->sigmaRange[0]); oscx->rhoRange[1] = _RhoOfSig(oscx->sigmaRange[1]); nrrdAxisInfoSet_va(oscx->nerr, nrrdAxisInfoMin, oscx->rhoRange[0]); nrrdAxisInfoSet_va(oscx->nerr, nrrdAxisInfoMax, oscx->rhoRange[1]); fprintf(stderr, "!%s: sigma [%g,%g] -> rho [%g,%g]\n", me, oscx->sigmaRange[0], oscx->sigmaRange[1], oscx->rhoRange[0], oscx->rhoRange[1]); fprintf(stderr, "!%s: rho %g -- %g\n", me, oscx->rhoRange[0], oscx->rhoRange[1]); for (ii=0; iitrueImgNum; ii++) { double rr, ss, rc, eps=1e-13; rr = AIR_AFFINE(0, ii, oscx->trueImgNum-1, oscx->rhoRange[0], oscx->rhoRange[1]); ss = _SigOfRho(rr); rc = _RhoOfSig(ss); /* fprintf(stderr, "!%s: (%u) rho %.17g -> sig %.17g -> rho %.17g (%.17g)\n", me, ii, rr, ss, rc, AIR_ABS(rr - rc)/(rr + eps/2)); */ if ( AIR_ABS(rr - rc)/(rr + eps) > eps ) { biffAddf(GAGE, "%s: rho %g -> sig %g -> rho %g has error %g > %g; " "_SigOfRho() and _RhoOfSig() not invertible", me, rr, ss, rc, AIR_ABS(rr - rc)/(rr + eps/2), eps); return NULL; } } oscx->kloc = AIR_CALLOC(oscx->sx, double); oscx->kern = AIR_CALLOC(oscx->sx, double); oscx->ktmp1 = AIR_CALLOC(oscx->sx, double); oscx->ktmp2 = AIR_CALLOC(oscx->sx, double); if (!(oscx->kloc && oscx->kern && oscx->ktmp1 && oscx->ktmp2)) { biffAddf(GAGE, "%s: couldn't allocate kernel buffers", me); return NULL; } for (ii=0; iisx; ii++) { oscx->kloc[ii] = AIR_CAST(double, ii) - ((oscx->sx + 1)/2 - 1); } oscx->kone[0] = 1.0; oscx->gctx = NULL; oscx->pvlBase = NULL; oscx->pvlSS = AIR_CALLOC(oscx->sampleNumMax, gagePerVolume *); oscx->nsampleImg = AIR_CALLOC(oscx->sampleNumMax, Nrrd *); oscx->sampleSigma = AIR_CALLOC(oscx->sampleNumMax, double); oscx->sampleRho = AIR_CALLOC(oscx->sampleNumMax, double); oscx->sampleTmp = AIR_CALLOC(oscx->sampleNumMax, double); oscx->sampleErrMax = AIR_CALLOC(oscx->sampleNumMax, double); oscx->step = AIR_CALLOC(oscx->sampleNumMax, double); if (!(oscx->pvlSS && oscx->nsampleImg && oscx->sampleSigma && oscx->sampleRho && oscx->sampleTmp && oscx->sampleErrMax && oscx->step)) { biffAddf(GAGE, "%s: couldn't allocate per-sample arrays", me); return NULL; } for (ii=0; iisampleNumMax; ii++) { oscx->nsampleImg[ii] = nrrdNew(); if (nrrdMaybeAlloc_va(oscx->nsampleImg[ii], nrrdTypeDouble, 3, AIR_CAST(size_t, oscx->sx), AIR_CAST(size_t, oscx->sy), AIR_CAST(size_t, oscx->sz))) { biffMovef(GAGE, NRRD, "%s: couldn't allocate vol[%u]", me, ii); return NULL; } nrrdAxisInfoSet_va(oscx->nsampleImg[ii], nrrdAxisInfoSpacing, 1.0, 1.0, 1.0); } /* implementation not started oscx->nsampleHist = nrrdNew(); */ return oscx; } gageOptimSigContext * gageOptimSigContextNix(gageOptimSigContext *oscx) { if (oscx) { unsigned int si; nrrdKernelSpecNix(oscx->kssSpec); nrrdNuke(oscx->nerr); nrrdNuke(oscx->ninterp); nrrdNuke(oscx->ndiff); airFree(oscx->kloc); airFree(oscx->kern); airFree(oscx->ktmp1); airFree(oscx->ktmp2); gageContextNix(oscx->gctx); /* airFree(oscx->pvlSS); needed? */ for (si=0; sisampleNumMax; si++) { nrrdNuke(oscx->nsampleImg[si]); } airFree(oscx->nsampleImg); airFree(oscx->sampleSigma); airFree(oscx->sampleRho); airFree(oscx->sampleTmp); airFree(oscx->sampleErrMax); airFree(oscx->step); /* nrrdNuke(oscx->nsampleHist); */ airFree(oscx); } return NULL; } static int _volInterp(Nrrd *ninterp, double rho, gageOptimSigContext *oscx) { static const char me[]="_volInterp"; double *interp, scaleIdx, sigma; const double *answer; unsigned int xi, yi, zi; int outside; /* debugging = rho > 1.197; gageParmSet(oscx->gctx, gageParmVerbose, 2*debugging); */ sigma = _SigOfRho(rho); scaleIdx = gageStackWtoI(oscx->gctx, sigma, &outside); /* Because of limited numerical precision, _SigOfRho(rhoRange[1]) can end up "outside" stack, which should really be a bug. However, since the use of gage is pretty straight-forward here, we're okay with ignoring the "outside" here, and also clamping the probe below */ answer = gageAnswerPointer(oscx->gctx, oscx->pvlBase, gageSclValue); interp = AIR_CAST(double *, ninterp->data); for (zi=0; zisz; zi++) { for (yi=0; yisy; yi++) { for (xi=0; xisx; xi++) { if (gageStackProbeSpace(oscx->gctx, xi, yi, zi, scaleIdx, AIR_TRUE /* index space */, AIR_TRUE /* clamping */)) { biffAddf(GAGE, "%s: probe error at (%u,%u,%u,%.17g): %s (%d)", me, xi, yi, zi, scaleIdx, oscx->gctx->errStr, oscx->gctx->errNum); return 1; } interp[xi + oscx->sx*(yi + oscx->sy*zi)] = answer[0]; } } } /* gageParmSet(oscx->gctx, gageParmVerbose, 0); */ /* if (debugging) { char fname[AIR_STRLEN_SMALL]; sprintf(fname, "interp-%04u.nrrd", debugii); nrrdSave(fname, ninterp, NULL); } */ return 0; } static void _kernset(double **kzP, double **kyP, double **kxP, gageOptimSigContext *oscx, double rho) { NrrdKernel *dg; double sig, kparm[NRRD_KERNEL_PARMS_NUM], *kloc, *kern, *ktmp1, *ktmp2; unsigned int ki, kj, kk, sx; kern = oscx->kern; kloc = oscx->kloc; ktmp1 = oscx->ktmp1; ktmp2 = oscx->ktmp2; sx = oscx->sx; sig = _SigOfRho(rho); dg = nrrdKernelDiscreteGaussian; kparm[1] = oscx->cutoff; if (sig < GOOD_SIGMA_MAX) { /* for small sigma, can evaluate directly into kern */ kparm[0] = sig; dg->evalN_d(kern, kloc, sx, kparm); } else { double timeleft, tdelta; unsigned int rx; rx = (sx + 1)/2 - 1; /* we have to iteratively blur */ kparm[0] = GOOD_SIGMA_MAX; dg->evalN_d(kern, kloc, sx, kparm); timeleft = sig*sig - GOOD_SIGMA_MAX*GOOD_SIGMA_MAX; do { tdelta = AIR_MIN( GOOD_SIGMA_MAX*GOOD_SIGMA_MAX, timeleft ); kparm[0] = sqrt(tdelta); dg->evalN_d(ktmp1, kloc, sx, kparm); for (ki=0; kidim >= 3 ? kern : oscx->kone; *kyP = oscx->dim >= 2 ? kern : oscx->kone; *kxP = kern; return; } /* ** sets one of the sampleImg, to be used as a sample in scale-space interp */ static void _sampleSet(gageOptimSigContext *oscx, unsigned int si, double rho) { double *vol, *kz, *ky, *kx; unsigned int ii, xi, yi, zi; oscx->sampleSigma[si] = _SigOfRho(rho); oscx->sampleRho[si] = rho; vol = AIR_CAST(double *, oscx->nsampleImg[si]->data); _kernset(&kz, &ky, &kx, oscx, rho); ii = 0; for (zi=0; zisz; zi++) { for (yi=0; yisy; yi++) { for (xi=0; xisx; xi++) { vol[ii] = kz[zi]*ky[yi]*kx[xi]; ii++; } } } if (oscx->gctx) { /* the gage stack needs to know new scale pos */ oscx->gctx->stackPos[si] = oscx->sampleSigma[si]; /* HEY: GLK forgets why this is needed, but remembers it was a tricky bug to find */ gagePointReset(&(oscx->gctx->point)); } return; } static int _errSingle(double *retP, gageOptimSigContext *oscx, double rho) { static const char me[]="_errSingle"; double *diff, *kx, *ky, *kz; const double *interp; unsigned int ii, xi, yi, zi; if (_volInterp(oscx->ninterp, rho, oscx)) { biffAddf(GAGE, "%s: trouble at rho %.17g\n", me, rho); return 1; } /* if (debugging) { char fname[AIR_STRLEN_SMALL]; sprintf(fname, "interp-%04u.nrrd", debugii); nrrdSave(fname, oscx->ninterp, NULL); } */ interp = AIR_CAST(const double *, oscx->ninterp->data); diff = AIR_CAST(double *, oscx->ndiff->data); _kernset(&kz, &ky, &kx, oscx, rho); ii = 0; for (zi=0; zisz; zi++) { for (yi=0; yisy; yi++) { for (xi=0; xisx; xi++) { double tru; tru = kz[zi]*ky[yi]*kx[xi]; diff[ii] = interp[ii] - tru; if (debugReconErrArr) { unsigned int idx = airArrayLenIncr(debugReconErrArr, 2); debugReconErr[idx + 0] = tru; debugReconErr[idx + 1] = interp[ii]; } ii++; } } } nrrdMeasureLine[oscx->imgMeasr](retP, nrrdTypeDouble, diff, nrrdTypeDouble, ii /* HEY: cleverness */, AIR_NAN, AIR_NAN); return 0; } static int _errTotal(double *retP, gageOptimSigContext *oscx) { static const char me[]="_errTotal"; unsigned int ii; double *err; err = AIR_CAST(double *, oscx->nerr->data); for (ii=0; iitrueImgNum; ii++) { /* debugii = ii; */ if (_errSingle(err + ii, oscx, AIR_AFFINE(0, ii, oscx->trueImgNum-1, oscx->rhoRange[0], oscx->rhoRange[1]))) { biffAddf(GAGE, "%s: trouble at ii %u", me, ii); return 1; } } nrrdMeasureLine[oscx->allMeasr](retP, nrrdTypeDouble, err, nrrdTypeDouble, oscx->trueImgNum, AIR_NAN, AIR_NAN); /* if (debugging) { char fname[AIR_STRLEN_SMALL]; unsigned int ni; for (ni=0; nisampleNum; ni++) { sprintf(fname, "sample-%04u.nrrd", ni); nrrdSave(fname, oscx->nsampleImg[ni], NULL); } } */ if (0) { static unsigned int call=0; char fname[AIR_STRLEN_SMALL]; sprintf(fname, "err-%04u.nrrd", call); nrrdSave(fname, oscx->nerr, NULL); call++; } return 0; } static int _errTotalLinf(double *retP, gageOptimSigContext *oscx, unsigned int mmIdx[2], double mmErr[2]) { static const char me[]="_errTotalLinf"; unsigned int ii, pi; double *err, *sem, *rr, rho, sig, pid; int outside; err = AIR_CAST(double *, oscx->nerr->data); sem = oscx->sampleErrMax; rr = oscx->rhoRange; for (pi=0; pi<=oscx->sampleNum-2; pi++) { sem[pi] = 0; } /* NOTE: we don't bother with last "true image": it will always be a low error, and not meaningfully associated with a gap */ for (ii=0; iitrueImgNum-1; ii++) { /* debugii = ii; */ rho = AIR_AFFINE(0, ii, oscx->trueImgNum-1, rr[0], rr[1]); if (_errSingle(err + ii, oscx, rho)) { biffAddf(GAGE, "%s: trouble at ii %u", me, ii); return 1; } sig = _SigOfRho(rho); pid = gageStackWtoI(oscx->gctx, sig, &outside); pi = AIR_CAST(unsigned int, pid); if (outside || !(pi <= oscx->sampleNum-2)) { biffAddf(GAGE, "%s: ii %u -> rho %g -> sig %g -> pi %u-> OUTSIDE", me, ii, rho, sig, pi); return 1; } sem[pi] = AIR_MAX(sem[pi], err[ii]); } mmIdx[0] = mmIdx[1] = 0; mmErr[0] = mmErr[1] = sem[0]; for (pi=1; pi<=oscx->sampleNum-2; pi++) { if (sem[pi] < mmErr[0]) { mmIdx[0] = pi; mmErr[0] = sem[pi]; } if (sem[pi] > mmErr[1]) { mmIdx[1] = pi; mmErr[1] = sem[pi]; } } /* returned error invented, not really Linf, but minimizing this does the right thing */ *retP = 1000*oscx->sampleNum*(mmErr[1] - mmErr[0])/(rr[1] - rr[0]); if (0) { static unsigned int call=0; char fname[AIR_STRLEN_SMALL]; sprintf(fname, "err-%04u.nrrd", call); nrrdSave(fname, oscx->nerr, NULL); call++; } return 0; } /* ** this can be called repeatedly */ static int _gageSetup(gageOptimSigContext *oscx) { static const char me[]="_gageSetup"; double kparm[NRRD_KERNEL_PARMS_NUM]; int E; if (oscx->gctx) { gageContextNix(oscx->gctx); } oscx->gctx = gageContextNew(); gageParmSet(oscx->gctx, gageParmVerbose, 0); gageParmSet(oscx->gctx, gageParmRenormalize, AIR_FALSE); gageParmSet(oscx->gctx, gageParmCheckIntegrals, AIR_FALSE); gageParmSet(oscx->gctx, gageParmOrientationFromSpacing, AIR_TRUE); gageParmSet(oscx->gctx, gageParmStackUse, AIR_TRUE); E = 0; if (!E) E |= !(oscx->pvlBase = gagePerVolumeNew(oscx->gctx, oscx->nsampleImg[0], gageKindScl)); if (!E) E |= gageStackPerVolumeNew(oscx->gctx, oscx->pvlSS, AIR_CAST(const Nrrd*const*, oscx->nsampleImg), oscx->sampleNum, gageKindScl); if (!E) E |= gageStackPerVolumeAttach(oscx->gctx, oscx->pvlBase, oscx->pvlSS, oscx->sampleSigma, oscx->sampleNum); kparm[0] = 1; if (!E) E |= gageKernelSet(oscx->gctx, gageKernel00, nrrdKernelBox, kparm); if (!E) E |= gageKernelSet(oscx->gctx, gageKernelStack, oscx->kssSpec->kernel, oscx->kssSpec->parm); if (!E) E |= gageQueryItemOn(oscx->gctx, oscx->pvlBase, gageSclValue); if (!E) E |= gageUpdate(oscx->gctx); if (E) { biffAddf(GAGE, "%s: problem setting up gage", me); return 1; } return 0; } static char * _timefmt(char tstr[AIR_STRLEN_MED], double deltim) { if (deltim < 60) { sprintf(tstr, "%g secs", deltim); return tstr; } deltim /= 60; if (deltim < 60) { sprintf(tstr, "%g mins", deltim); return tstr; } deltim /= 60; if (deltim < 24) { sprintf(tstr, "%g hours", deltim); return tstr; } deltim /= 24; if (deltim < 7) { sprintf(tstr, "%g days", deltim); return tstr; } deltim /= 7; sprintf(tstr, "%g weeks", deltim); return tstr; } static int _optsigrun(gageOptimSigContext *oscx) { static const char me[]="_optsigrun"; char tstr[AIR_STRLEN_MED]; unsigned int iter, pnt; double lastErr, newErr, rhoeps, oppor, lastPos, backoff, decavg, time0; int badStep; /* used to debug failure to measure error meaningfully { unsigned int hi, hn=200; double rr; for (hi=0; hirhoRange[0], oscx->rhoRange[1]); _sampleSet(oscx, 1, rr); _errTotal(oscx); } } */ time0 = airTime(); if (_errTotal(&lastErr, oscx)) { biffAddf(GAGE, "%s: first error measurement", me); return 1; } fprintf(stderr, "%s: (%s for initial error measr)\n", me, _timefmt(tstr, airTime() - time0)); newErr = AIR_NAN; decavg = oscx->sampleNum; /* hack */ /* meaningful discrete difference for looking at error gradient is bounded by the resolution of the sampling we're doing along scale */ rhoeps = 0.1*(oscx->rhoRange[1]-oscx->rhoRange[0])/oscx->trueImgNum; oppor = 1.3333; backoff = 0.25; /* initialize step for the moving samples: 1 through oscx->sampleNum-2 */ for (pnt=1; pntsampleNum-1; pnt++) { oscx->step[pnt] = 10; } for (iter=0; itermaxIter; iter++) { double limit, err1, grad, delta; unsigned int tryi; int zerodelta, esgn; esgn = 2*AIR_CAST(int, airRandInt(2)) - 1; pnt = 1 + (iter % (oscx->sampleNum-2)); lastPos = oscx->sampleRho[pnt]; fprintf(stderr, "%s: ***** iter %u; [[ err %g ]] %s\n", me, iter, lastErr, _timefmt(tstr, airTime() - time0)); limit = AIR_MIN((oscx->sampleRho[pnt] - oscx->sampleRho[pnt-1])/3, (oscx->sampleRho[pnt+1] - oscx->sampleRho[pnt])/3); fprintf(stderr, ". pnt %u: pos %g, step %g\n", pnt, lastPos, oscx->step[pnt]); fprintf(stderr, ". limit = min((%g-%g)/3,(%g-%g)/3) = %g\n", oscx->sampleRho[pnt], oscx->sampleRho[pnt-1], oscx->sampleRho[pnt+1], oscx->sampleRho[pnt], limit); _sampleSet(oscx, pnt, lastPos + esgn*rhoeps); if (_errTotal(&err1, oscx)) { biffAddf(GAGE, "%s: for err1 (%u -> %.17g)", me, pnt, lastPos + esgn*rhoeps); return 1; } _sampleSet(oscx, pnt, lastPos); grad = (err1 - lastErr)/(esgn*rhoeps); fprintf(stderr, ". grad = %g\n", grad); delta = -grad*oscx->step[pnt]; if (!AIR_EXISTS(delta)) { biffAddf(GAGE, "%s: got non-exist delta %g on iter %u (pnt %u) err %g", me, delta, iter, pnt, lastErr); return 1; } if (AIR_ABS(delta) > limit) { oscx->step[pnt] *= limit/AIR_ABS(delta); fprintf(stderr, ". step *= %g/%g -> %g\n", limit, AIR_ABS(delta), oscx->step[pnt]); delta = -grad*oscx->step[pnt]; } fprintf(stderr, ". delta = %g\n", delta); tryi = 0; badStep = AIR_FALSE; do { if (tryi == oscx->maxIter) { biffAddf(GAGE, "%s: confusion (tryi %u) on iter %u (pnt %u) err %g", me, tryi, iter, pnt, lastErr); return 1; } if (!delta) { fprintf(stderr, "... try %u: delta = 0; nothing to do\n", tryi); newErr = lastErr; zerodelta = AIR_TRUE; } else { zerodelta = AIR_FALSE; _sampleSet(oscx, pnt, lastPos + delta); if (_errTotal(&newErr, oscx)) { biffAddf(GAGE, "%s: for newErr (%u -> %.17g)", me, pnt, lastPos + delta); return 1; } badStep = newErr > lastErr; fprintf(stderr, "... try %u: pos[%u] %g + %g = %g;\n" "%s: err %g %s %g\n", tryi, pnt, lastPos, delta, oscx->sampleRho[pnt], badStep ? "*BAD*" : "good", newErr, newErr > lastErr ? ">" : "<=", lastErr); if (badStep) { oscx->step[pnt] *= backoff; if (oscx->step[pnt] < rhoeps/1000) { /* step got so small its stupid to be moving this point */ fprintf(stderr, "... !! step %g < %g pointlessly small, " "moving on\n", oscx->step[pnt], rhoeps/1000); _sampleSet(oscx, pnt, lastPos); newErr = lastErr; badStep = AIR_FALSE; } else { delta = -grad*oscx->step[pnt]; } } } tryi++; } while (badStep); if (!zerodelta) { /* don't update decavg if we moved on because slope was EXACTLY zero */ decavg = AIR_AFFINE(0, 1, oscx->sampleNum, decavg, (lastErr - newErr)/lastErr); oscx->step[pnt] *= oppor; } if (decavg <= oscx->convEps) { fprintf(stderr, "%s: converged (%g <= %g) after %u iters\n", me, decavg, oscx->convEps, iter); break; } else { fprintf(stderr, "%s: _____ iter %u done; decavg = %g > eps %g\n", me, iter, decavg, oscx->convEps); } lastErr = newErr; } if (iter == oscx->maxIter && decavg > oscx->convEps) { biffAddf(GAGE, "%s: failed to converge (%g > %g) after %u iters\n", me, decavg, oscx->convEps, iter); return 1; } oscx->finalErr = lastErr; return 0; } static int _optsigrunLinf(gageOptimSigContext *oscx) { static const char me[]="_optsigrunLinf"; char tstr[AIR_STRLEN_MED]; double *srho, *stmp, time0, lastErr, newErr, decavg, step, oppor, backoff, ceps, mmErr[2]; unsigned int iter, si, sn, mmIdx[2]; int shrink; time0 = airTime(); if (_errTotalLinf(&lastErr, oscx, mmIdx, mmErr)) { biffAddf(GAGE, "%s: first error measurement", me); return 1; } fprintf(stderr, "%s: (init) min %u %g max %u %g\n", me, mmIdx[0], mmErr[0], mmIdx[1], mmErr[1]); fprintf(stderr, "%s: (%s for initial error measr)\n", me, _timefmt(tstr, airTime() - time0)); newErr = AIR_NAN; /* shorcuts */ sn = oscx->sampleNum; srho = oscx->sampleRho; stmp = oscx->sampleTmp; /* Linf uses a single scalar step istead of oscx->step array */ step = 0.1; oppor = 1.4; backoff = 0.8; /* more demanding for more points */ ceps = oscx->convEps/sn; decavg = 2*ceps; for (iter=0; itermaxIter; iter++) { double midp, wlo, whi, glo, ghi, gerr; unsigned int gap, tryi; int badStep; if (iter % 2) { /* we will grow gap around smallest peak */ gap = mmIdx[0]; gerr = mmErr[0]; shrink = AIR_FALSE; } else { /* we will shrink gap around tallest peak */ gap = mmIdx[1]; gerr = mmErr[1]; shrink = AIR_TRUE; } midp = (srho[gap] + srho[gap+1])/2; fprintf(stderr, "%s: ---- iter %u (step %g): gap %u/%g (%s)\n", me, iter, step, gap, gerr, shrink ? "shrinking tallest" : "growing lowest"); /* save last set of positions to restore after bad step */ for (si=1; simaxIter) { biffAddf(GAGE, "%s: confusion (tryi %u) on iter %u err %g", me, tryi, iter, lastErr); return 1; } if (shrink) { wlo = AIR_AFFINE(0, step, 1, srho[gap], midp); whi = AIR_AFFINE(0, step, 1, srho[gap+1], midp); } else { wlo = AIR_AFFINE(0, step, -2, srho[gap], midp); whi = AIR_AFFINE(0, step, -2, srho[gap+1], midp); } glo = srho[gap]; ghi = srho[gap+1]; fprintf(stderr, "%s: rho[%u] %g | %g -- rho[%u] %g | %g\n", me, gap, srho[gap], wlo, gap+1, srho[gap+1], whi); for (si=1; si lastErr; fprintf(stderr, "... try %u: step %g -> newErr %g %s lastErr %g %s\n", tryi, step, newErr, badStep ? ">" : "<=", lastErr, badStep ? "*BAD*" : "good"); if (badStep) { step *= backoff; for (si=1; si eps %g\n", me, iter, decavg, ceps); } lastErr = newErr; } /* for iter */ if (oscx->maxIter && iter == oscx->maxIter && decavg > ceps) { biffAddf(GAGE, "%s: failed to converge (%g > %g) after %u iters\n", me, decavg, ceps, iter); return 1; } oscx->finalErr = lastErr; return 0; } int gageOptimSigCalculate(gageOptimSigContext *oscx, /* output */ double *sigma, unsigned int sigmaNum, const NrrdKernelSpec *kssSpec, int imgMeasr, int allMeasr, unsigned int maxIter, double convEps) { static const char me[]="gageOptimSigCalculate"; unsigned int ii; if (!( oscx && sigma && kssSpec )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (sigmaNum > oscx->sampleNumMax) { biffAddf(GAGE, "%s: initialized for max %u samples, not %u", me, oscx->sampleNumMax, sigmaNum); return 1; } /* initialize to uniform samples in rho */ oscx->sampleNum = sigmaNum; fprintf(stderr, "%s: initializing %u samples ... ", me, oscx->sampleNum); fflush(stderr); for (ii=0; iisampleNum; ii++) { _sampleSet(oscx, ii, AIR_AFFINE(0, ii, oscx->sampleNum-1, oscx->rhoRange[0], oscx->rhoRange[1])); } fprintf(stderr, "done.\n"); /* copy remaining input parameters */ nrrdKernelSpecNix(oscx->kssSpec); oscx->kssSpec = nrrdKernelSpecCopy(kssSpec); oscx->imgMeasr = imgMeasr; oscx->allMeasr = allMeasr; oscx->convEps = convEps; oscx->maxIter = maxIter; oscx->convEps = convEps; /* set up gage */ fprintf(stderr, "%s: setting up gage ... \n", me); if (_gageSetup(oscx)) { biffAddf(GAGE, "%s: problem setting up gage", me); return 1; } fprintf(stderr, "%s: ... gage setup done.\n", me); /* run the optimization */ if (oscx->sampleNum > 2) { int EE; EE = (nrrdMeasureLinf == oscx->allMeasr ? _optsigrunLinf(oscx) : _optsigrun(oscx)); if (EE) { biffAddf(GAGE, "%s: trouble", me); return 1; } } else { fprintf(stderr, "%s: num == 2, no optimization, finding error ... ", me); fflush(stderr); if (_errTotal(&(oscx->finalErr), oscx)) { biffAddf(GAGE, "%s: for finalErr", me); return 1; } fprintf(stderr, "done.\n"); } /* save output */ for (ii=0; iisampleNum; ii++) { sigma[ii] = oscx->sampleSigma[ii]; } return 0; } int gageOptimSigErrorPlot(gageOptimSigContext *oscx, Nrrd *nout, const double *sigma, unsigned int sigmaNum, const NrrdKernelSpec *kssSpec, int imgMeasr) { static const char me[]="gageOptimSigErrorPlot"; char doneStr[AIR_STRLEN_SMALL]; double *out; unsigned int ii; if (!(oscx && nout && sigma)) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!( sigmaNum >= 2 )) { biffAddf(GAGE, "%s: need sigmaNum >= 2 (not %u)", me, sigmaNum); return 1; } if (sigmaNum > oscx->sampleNumMax) { biffAddf(GAGE, "%s: initialized for max %u samples, not %u", me, oscx->sampleNumMax, sigmaNum); return 1; } /* copy remaining input parms */ nrrdKernelSpecNix(oscx->kssSpec); oscx->kssSpec = nrrdKernelSpecCopy(kssSpec); oscx->sampleNum = sigmaNum; oscx->maxIter = 0; oscx->imgMeasr = imgMeasr; oscx->allMeasr = nrrdMeasureUnknown; oscx->convEps = AIR_NAN; if (nrrdMaybeAlloc_va(nout, nrrdTypeDouble, 2, AIR_CAST(size_t, 2), AIR_CAST(size_t, oscx->trueImgNum))) { biffMovef(GAGE, NRRD, "%s: trouble allocating output", me); return 1; } out = AIR_CAST(double *, nout->data); /* set up requested samples */ for (ii=0; iisampleNum; ii++) { _sampleSet(oscx, ii, _RhoOfSig(sigma[ii])); } if (_gageSetup(oscx)) { biffAddf(GAGE, "%s: problem setting up gage", me); return 1; } fprintf(stderr, "%s: plotting ... ", me); fflush(stderr); for (ii=0; iitrueImgNum; ii++) { double rho, sig, err; fprintf(stderr, "%s", airDoneStr(0, ii, oscx->trueImgNum, doneStr)); fflush(stderr); rho = AIR_AFFINE(0, ii, oscx->trueImgNum-1, oscx->rhoRange[0], oscx->rhoRange[1]); sig = _SigOfRho(rho); out[0 + 2*ii] = rho; /* debugii = ii; */ if (_errSingle(&err, oscx, rho)) { biffAddf(GAGE, "%s: plotting %u", me, ii); return 1; } out[1 + 2*ii] = err; /* if (AIR_IN_CL(69,ii,71)) { fprintf(stderr, "!%s: ----- %u : %g\n", me, ii, err); } */ } fprintf(stderr, "%s\n", airDoneStr(0, ii, oscx->trueImgNum, doneStr)); /* if (0) { static unsigned int call=0; char fname[AIR_STRLEN_SMALL]; unsigned int ni; if (0) { sprintf(fname, "err-%04u.nrrd", call); nrrdSave(fname, oscx->nerr, NULL); } if (debugging) { for (ni=0; nisampleNum; ni++) { sprintf(fname, "sample-%04u.nrrd", ni); nrrdSave(fname, oscx->nsampleImg[ni], NULL); } } call++; debugging = (2 == call); } */ return 0; } int gageOptimSigErrorPlotSliding(gageOptimSigContext *oscx, Nrrd *nout, double windowRho, unsigned int sampleNum, const NrrdKernelSpec *kssSpec, int imgMeasr) { static const char me[]="gageOptimSigRecondErrorPlotSliding"; char doneStr[AIR_STRLEN_SMALL]; unsigned int ii; double *out; char hackKeyStr[]="TEEM_OPTSIG_RECONERR"; if (!( oscx && nout && kssSpec )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (windowRho <= 0) { biffAddf(GAGE, "%s: need positive windowRho (not %g)", me, windowRho); return 1; } if (windowRho > oscx->rhoRange[1] - oscx->rhoRange[0]) { biffAddf(GAGE, "%s: window %g > rhorange %g-%g=%g", me, windowRho, oscx->rhoRange[1], oscx->rhoRange[0], oscx->rhoRange[1] - oscx->rhoRange[0]); return 1; } if (nrrdGetenvString(&debugReconErrName, hackKeyStr)) { fprintf(stderr, "%s: %s hack on: will save recon results to %s\n", me, hackKeyStr, debugReconErrName); debugReconErrArr = airArrayNew(AIR_CAST(void **, &(debugReconErr)), NULL, sizeof(double), 1000); } /* copy remaining input parms */ nrrdKernelSpecNix(oscx->kssSpec); oscx->kssSpec = nrrdKernelSpecCopy(kssSpec); oscx->sampleNum = 3; /* hacky */ oscx->maxIter = 0; oscx->imgMeasr = imgMeasr; oscx->allMeasr = nrrdMeasureUnknown; oscx->convEps = AIR_NAN; oscx->sampleSigma[0] = oscx->sigmaRange[0]; /* just for gage setup */ oscx->sampleSigma[1] = oscx->sigmaRange[1]; /* just for gage setup */ oscx->sampleSigma[2] = oscx->sigmaRange[1]+1; if (_gageSetup(oscx)) { biffAddf(GAGE, "%s: problem setting up gage", me); return 1; } if (nrrdMaybeAlloc_va(nout, nrrdTypeDouble, 2, AIR_CAST(size_t, 2), AIR_CAST(size_t, sampleNum))) { biffMovef(GAGE, NRRD, "%s: trouble allocating output", me); return 1; } out = AIR_CAST(double *, nout->data); fprintf(stderr, "%s: plotting ... ", me); fflush(stderr); for (ii=0; iitrueImgNum, doneStr)); fflush(stderr); rho = AIR_AFFINE(0, ii, sampleNum, oscx->rhoRange[0], oscx->rhoRange[1]); rlo = rho - windowRho/2; rhi = rho + windowRho/2; if (rlo < oscx->rhoRange[0] || rhi > oscx->rhoRange[1]) { if (debugReconErrArr) { /* we have to simulate the updates to debugReconErrArr that would happen with a call to _errSingle */ airArrayLenIncr(debugReconErrArr, 2*oscx->sz*oscx->sy*oscx->sx); } out[0 + 2*ii] = _SigOfRho(rho); out[1 + 2*ii] = AIR_NAN; continue; } /* required samples will slide along with plotting */ _sampleSet(oscx, 0, rlo); _sampleSet(oscx, 1, rhi); if (_errSingle(&err, oscx, rho)) { biffAddf(GAGE, "%s: plotting/sliding %u", me, ii); return 1; } out[0 + 2*ii] = _SigOfRho(rho); out[1 + 2*ii] = err; } fprintf(stderr, "%s\n", airDoneStr(0, ii, oscx->trueImgNum, doneStr)); if (debugReconErrArr) { Nrrd *nre = nrrdNew(); nrrdWrap_va(nre, debugReconErr, nrrdTypeDouble, 3, AIR_CAST(size_t, 2), AIR_CAST(size_t, oscx->sz*oscx->sy*oscx->sx), AIR_CAST(size_t, sampleNum)); nrrdSave(debugReconErrName, nre, NULL); nrrdNix(nre); } return 0; } teem-1.11.0~svn6057/src/gage/test/0000775000175000017500000000000012203513754016260 5ustar domibeldomibelteem-1.11.0~svn6057/src/gage/test/maxes.c0000664000175000017500000001234112175662312017545 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../gage.h" void maxes1(Nrrd *nout, const Nrrd *nin) { unsigned int sx, xi, xd; int xo, ismax; double val, (*lup)(const void *, size_t), (*ins)(void *, size_t, double); lup = nrrdDLookup[nin->type]; ins = nrrdDInsert[nin->type]; sx = AIR_CAST(unsigned int, nin->axis[0].size); for (xi=0; xidata, xi); for (xo=-1; xo<=1; xo+=2) { xd = !xi && xo < 0 ? sx-1 : AIR_MOD(xi+xo, sx); ismax &= lup(nin->data, xd) < val; if (!ismax) break; } ins(nout->data, xi, ismax); } return; } void maxes2(Nrrd *nout, const Nrrd *nin) { unsigned int sx, sy, xi, yi, xd, yd; int xo, yo, ismax; double val, (*lup)(const void *, size_t), (*ins)(void *, size_t, double); lup = nrrdDLookup[nin->type]; ins = nrrdDInsert[nin->type]; sx = AIR_CAST(unsigned int, nin->axis[0].size); sy = AIR_CAST(unsigned int, nin->axis[1].size); for (yi=0; yidata, xi + sx*yi); for (yo=-1; yo<=1; yo++) { yd = !yi && yo < 0 ? sy-1 : AIR_MOD(yi+yo, sy); for (xo=-1; xo<=1; xo++) { if (!xo && !yo) continue; xd = !xi && xo < 0 ? sx-1 : AIR_MOD(xi+xo, sx); ismax &= lup(nin->data, xd + sx*yd) < val; } } ins(nout->data, xi + sx*yi, ismax); } } return; } void maxes3(Nrrd *nout, const Nrrd *nin) { unsigned int sx, sy, sz, xi, yi, zi, xd, yd, zd; int xo, yo, zo, ismax; double val, (*lup)(const void *, size_t), (*ins)(void *, size_t, double); lup = nrrdDLookup[nin->type]; ins = nrrdDInsert[nin->type]; sx = AIR_CAST(unsigned int, nin->axis[0].size); sy = AIR_CAST(unsigned int, nin->axis[1].size); sz = AIR_CAST(unsigned int, nin->axis[2].size); for (zi=0; zidata, xi + sx*(yi + sy*zi)); for (zo=-1; zo<=1; zo++) { zd = !zi && zo < 0 ? sz-1 : AIR_MOD(zi+zo, sz); for (yo=-1; yo<=1; yo++) { yd = !yi && yo < 0 ? sy-1 : AIR_MOD(yi+yo, sy); for (xo=-1; xo<=1; xo++) { if (!xo && !yo && !zo) continue; xd = !xi && xo < 0 ? sx-1 : AIR_MOD(xi+xo, sx); ismax &= lup(nin->data, xd + sx*(yd + sy*zd)) < val; } } } ins(nout->data, xi + sx*(yi + sy*zi), ismax); } } } return; } char *maxesInfo = ("sets maxima to 1, else 0"); int main(int argc, const char *argv[]) { const char *me; char *outS; hestOpt *hopt; hestParm *hparm; airArray *mop; char *err; Nrrd *nin, *nout; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, NULL, "output volume"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, maxesInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!AIR_IN_CL(1, nin->dim, 3)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: nin->dim %u not 1, 2, or 3", me, nin->dim); airMopError(mop); return 1; } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s", me, err); airMopError(mop); return 1; } if (1 == nin->dim) { maxes1(nout, nin); } else if (2 == nin->dim) { maxes2(nout, nin); } else { maxes3(nout, nin); } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/gage/test/genoptsig.c0000664000175000017500000001631512202223212020413 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../gage.h" char *optsigInfo = ("Computes tables of optimal sigmas for hermite-spline " "reconstruction of scale space"); int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt; hestParm *hparm; airArray *mop; char *err, *outS; double sigma[2], convEps, cutoff; int measr[2], tentRecon; unsigned int sampleNum[2], dim, measrSampleNum, maxIter, num, ii; gageOptimSigContext *osctx; double *scalePos, *out, info[512]; Nrrd *nout; NrrdKernelSpec *kss; double *plotPos; unsigned int plotPosNum; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "num", "# samp", airTypeUInt, 2, 2, sampleNum, NULL, "min and max number of samples to optimize"); hestOptAdd(&hopt, "dim", "dimension", airTypeUInt, 1, 1, &dim, "3", "dimension of image to work with"); hestOptAdd(&hopt, "sig", "min max", airTypeDouble, 2, 2, sigma, NULL, "sigmas to use for bottom and top sample; normal use " "should have 0 for bottom scale"); hestOptAdd(&hopt, "cut", "cut", airTypeDouble, 1, 1, &cutoff, "4", "at how many sigmas to cut-off discrete gaussian"); hestOptAdd(&hopt, "maxi", "max", airTypeUInt, 1, 1, &maxIter, "1000", "maximum # iterations"); hestOptAdd(&hopt, "N", "# samp", airTypeUInt, 1, 1, &measrSampleNum, "300", "number of samples in the measurement of error across scales"); hestOptAdd(&hopt, "eps", "eps", airTypeDouble, 1, 1, &convEps, "0.0001", "convergence threshold for optimization"); hestOptAdd(&hopt, "m", "m1 m2", airTypeEnum, 2, 2, measr, "l2 l2", "how to measure error across image, and across scales", NULL, nrrdMeasure); hestOptAdd(&hopt, "p", "s0 s1", airTypeDouble, 1, -1, &plotPos, "nan nan", "hack: don't do optimization; just plot the recon error given " "these (two or more) samples along scale. OR, hackier hack: " "if there is only one value given here, do a different " "plot: of the recon error within a small window (with the width " "given here) in rho, as rho moves through its range", &plotPosNum); hestOptAdd(&hopt, "kss", "kern", airTypeOther, 1, 1, &kss, "hermite", "kernel for gageKernelStack", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "tent", NULL, airTypeInt, 0, 0, &tentRecon, NULL, "same hack: plot error with tent recon, not hermite"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL, "output array"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, optsigInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, AIR_CAST(airMopper, nrrdNuke), airMopAlways); osctx = gageOptimSigContextNew(dim, sampleNum[1], measrSampleNum, sigma[0], sigma[1], cutoff); if (!osctx) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s", me, err); airMopError(mop); return 1; } airMopAdd(mop, osctx, AIR_CAST(airMopper, gageOptimSigContextNix), airMopAlways); scalePos = AIR_CALLOC(sampleNum[1], double); airMopAdd(mop, scalePos, airFree, airMopAlways); if (1 == plotPosNum && AIR_EXISTS(plotPos[0])) { /* hackity hack: a different kind of plotting requested */ if (gageOptimSigErrorPlotSliding(osctx, nout, plotPos[0], measrSampleNum, kss, measr[0])) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s", me, err); airMopError(mop); return 1; } } else if (AIR_EXISTS(plotPos[0]) && AIR_EXISTS(plotPos[1])) { /* hack: plotting requested */ if (gageOptimSigErrorPlot(osctx, nout, plotPos, plotPosNum, kss, measr[0])) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s", me, err); airMopError(mop); return 1; } } else { /* do sample position optimization */ if (nrrdMaybeAlloc_va(nout, nrrdTypeDouble, 2, AIR_CAST(size_t, sampleNum[1]+1), AIR_CAST(size_t, sampleNum[1]+1))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble allocating output:\n%s", me, err); airMopError(mop); return 1; } out = AIR_CAST(double *, nout->data); /* hacky way of saving some of the computation information */ /* HEY: this is what KVPs are for */ info[0] = cutoff; info[1] = measrSampleNum; info[2] = measr[0]; info[3] = measr[1]; info[4] = convEps; info[5] = maxIter; for (ii=0; iifinalErr; } } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/gage/test/ctfix.c0000664000175000017500000001622412042325724017545 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../gage.h" char *ctfixInfo = ("removes circular streaks from CT datasets, " "assuming you know where they are. nrrd/test/histrad " "is useful for finding the radius of a streak " "in a given slice."); int main(int argc, const char *argv[]) { const char *me; char *outS; hestOpt *hopt; hestParm *hparm; airArray *mop; char *err, done[13]; Nrrd *nin, *_nsinfo, *nsinfo, *nout; NrrdKernelSpec *kern; int E; unsigned int sx, sy, sz, xi, yi, zi, si; gageContext *ctx; gagePerVolume *pvl; const double *answer; double cent[2], r, srad, *sinfo, swidth, astart, angle, astop, corerad; double (*ins)(void *v, size_t I, double d); me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "si", "streak info", airTypeOther, 1, 1, &_nsinfo, NULL, "list of streaks as 2D nrrd; per line: \n " " \n " "where start and stop are angles in [0,4] " "(instead of [-pi,pi]) delimiting arc"); hestOptAdd(&hopt, "k", "kernel", airTypeOther, 1, 1, &kern, "tent", "kernel to use for median weighting", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "c", "center x, y", airTypeDouble, 2, 2, cent, NULL, "The center point of tomography, around which streaks are " "circular arcs or fragments thereof"); hestOptAdd(&hopt, "r", "core radius", airTypeDouble, 1, 1, &corerad, "0", "in addition to the per-streak filtering, giving a non-zero " "radius here turns on filtering of *every* slice within " "this radius, to handle glitches along central tomography " "radius"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "fixed volume output"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, ctfixInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( 3 == nin->dim )) { fprintf(stderr, "%s: need 3-D input (not %d-D)\n", me, nin->dim); airMopError(mop); return 1; } if (!( 2 == _nsinfo->dim && 5 == _nsinfo->axis[0].size )) { fprintf(stderr, "%s: need a 2-D 5-by-N list of streak locations\n", me); airMopError(mop); return 1; } nsinfo = nrrdNew(); airMopAdd(mop, nsinfo, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nsinfo, _nsinfo, nrrdTypeDouble)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't convert streak info to doubles:\n%s", me, err); airMopError(mop); return 1; } sinfo = (double*)(nsinfo->data); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s", me, err); airMopError(mop); return 1; } #define DIST(x0, y0, x1, y1) (sqrt((x0-x1)*(x0-x1) + (y0-y1)*(y0-y1))) sx = nin->axis[0].size; sy = nin->axis[1].size; sz = nin->axis[2].size; ctx = gageContextNew(); airMopAdd(mop, ctx, (airMopper)gageContextNix, airMopAlways); gageParmSet(ctx, gageParmRenormalize, AIR_TRUE); gageParmSet(ctx, gageParmCheckIntegrals, AIR_TRUE); E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(ctx, nin, gageKindScl)); if (!E) E |= gagePerVolumeAttach(ctx, pvl); if (!E) E |= gageKernelSet(ctx, gageKernel00, kern->kernel, kern->parm); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclMedian); if (!E) E |= gageUpdate(ctx); if (E) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } answer = gageAnswerPointer(ctx, pvl, gageSclMedian); ins = nrrdDInsert[nout->type]; fprintf(stderr, "%s: processing streaks ... \n", me); for (si=0; siaxis[1].size; si++) { fprintf(stderr, "%s: streak %d of %d ", me, si+1, AIR_CAST(int, nsinfo->axis[1].size)); fflush(stderr); zi = (int)(sinfo[0 + 5*si]); if (!( zi < sz )) { fprintf(stderr, "%s: streak[%d] zi=%d outside valid range [0,%d]\n", me, si, zi, sz-1); airMopError(mop); return 1; } srad = sinfo[1 + 5*si]; swidth = sinfo[2 + 5*si]; astart = sinfo[3 + 5*si]; astop = sinfo[4 + 5*si]; for (yi=0; yi astop && (AIR_IN_CL(astart, angle, 4) || AIR_IN_CL(0, angle, astop))) )) { continue; } gageProbe(ctx, xi, yi, zi); ins(nout->data, xi + sx*(yi + sy*zi), *answer); } } fprintf(stderr, "\n"); } if (corerad > 0) { fprintf(stderr, "%s: processing core ... ", me); for (zi=0; zidata, xi + sx*(yi + sy*zi), *answer); } } } fprintf(stderr, "\n"); } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/gage/test/aalias.c0000664000175000017500000001741512042325724017665 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../gage.h" char *aaliasInfo = ("implements \"Reducing Aliasing Artifacts " "in Iso-Surfaces of Binary Volumes\", " "R. T. Whitaker IEEE VolVis 2000. With optimization, " "could favorably compare to " "itk::AntiAliasBinaryImageFilter."); int main(int argc, const char *argv[]) { const char *me; char *outS; hestOpt *hopt; hestParm *hparm; airArray *mop; char *err, done[13]; Nrrd *nin, *ndist, *nmask, *nupdate, *nout; NrrdKernelSpec *k00, *k11, *k22; int E; unsigned int sx, sy, sz, xi, yi, zi, si, iter, maxIter; gageContext *ctx; gagePerVolume *pvl; double dt, *dist, *mask, *update, thresh, eps, rmsMin; const double *valu, *gmag, *mcrv; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "k00", "kernel", airTypeOther, 1, 1, &k00, "tent", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kernel", airTypeOther, 1, 1, &k11, "cubicd:0,0.5", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kernel", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "dt", "step", airTypeDouble, 1, 1, &dt, "0.17", "time step size"); hestOptAdd(&hopt, "th", "val", airTypeDouble, 1, 1, &thresh, "0.0", "the value to use for thresholding the input " "volume, to create the binary constraint image."); hestOptAdd(&hopt, "eps", "val", airTypeDouble, 1, 1, &eps, "0.05", "width of value bracket around threshold, to constrain the " "the update from letting to value, originally on either " "side of the threshold, from getting too close."); hestOptAdd(&hopt, "rms", "thresh", airTypeDouble, 1, 1, &rmsMin, "0.01", "RMS change below this terminates updates."); hestOptAdd(&hopt, "mi", "max", airTypeUInt, 1, 1, &maxIter, "100", "maximum # iterations"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "fixed volume output"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, aaliasInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); ndist = nrrdNew(); nmask = nrrdNew(); nupdate = nrrdNew(); airMopAdd(mop, ndist, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nmask, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nupdate, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(ndist, nin, nrrdTypeDouble) || nrrdConvert(nmask, nin, nrrdTypeDouble) || nrrdConvert(nupdate, nin, nrrdTypeDouble)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate buffers:\n%s", me, err); airMopError(mop); return 1; } dist = AIR_CAST(double *, ndist->data); mask = AIR_CAST(double *, nmask->data); update = AIR_CAST(double *, nupdate->data); ctx = gageContextNew(); airMopAdd(mop, ctx, (airMopper)gageContextNix, airMopAlways); gageParmSet(ctx, gageParmRenormalize, AIR_TRUE); gageParmSet(ctx, gageParmCheckIntegrals, AIR_TRUE); E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(ctx, ndist, gageKindScl)); if (!E) E |= gagePerVolumeAttach(ctx, pvl); if (!E) E |= gageKernelSet(ctx, gageKernel00, k00->kernel, k00->parm); if (!E) E |= gageKernelSet(ctx, gageKernel11, k11->kernel, k11->parm); if (!E) E |= gageKernelSet(ctx, gageKernel22, k22->kernel, k22->parm); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclValue); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclGradMag); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclMeanCurv); if (!E) E |= gageUpdate(ctx); if (E) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } valu = gageAnswerPointer(ctx, pvl, gageSclValue); gmag = gageAnswerPointer(ctx, pvl, gageSclGradMag); mcrv = gageAnswerPointer(ctx, pvl, gageSclMeanCurv); sx = nin->axis[0].size; sy = nin->axis[1].size; sz = nin->axis[2].size; for (iter=0; iter thresh) { newval = AIR_MAX(newval, thresh+eps/2); } else { newval = AIR_MIN(newval, thresh-eps/2); } rms += (dist[si] - newval)*(dist[si] - newval); dist[si] = newval; count++; } } fprintf(stderr, "%s", airDoneStr(0, zi, sz-1, done)); rms /= count; rms = sqrt(rms); if (rms < rmsMin) { fprintf(stderr, "\n%s: RMS %g below threshold %g\n", me, rms, rmsMin); break; } else { fprintf(stderr, " rms = %g > %g\n", rms, rmsMin); } } if (iter == maxIter) { fprintf(stderr, "%s: hit max iter %u\n", me, maxIter); } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, ndist)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/gage/test/demo.c0000664000175000017500000000621412042325724017352 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../gage.h" /* self-contained gage usage example */ int main(int argc, char *argv[]) { char *me, *err; airArray *mop; Nrrd *nin; gageContext *ctx; gagePerVolume *pvl; const double *grad, *norm; double kparm[NRRD_KERNEL_PARMS_NUM]; int E; me = argv[0]; if (2 != argc) { fprintf(stderr, "usage: %s \n", me); return 1; } mop = airMopNew(); nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); if (nrrdLoad(nin, argv[1], NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble loading\n%s\n", me, err); airMopError(mop); return 1; } if (gageKindVolumeCheck(gageKindScl, nin)) { airMopAdd(mop, err=biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: didn't get a %s volume:\n%s\n", me, gageKindScl->name, err); airMopError(mop); return 1; } kparm[0] = 1.0; /* scale parameter, in units of samples */ kparm[1] = 0.0; /* B */ kparm[2] = 0.5; /* C */ ctx = gageContextNew(); airMopAdd(mop, ctx, (airMopper)gageContextNix, airMopAlways); E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(ctx, nin, gageKindScl)); if (!E) E |= gagePerVolumeAttach(ctx, pvl); if (!E) E |= gageKernelSet(ctx, gageKernel00, nrrdKernelBCCubic, kparm); if (!E) E |= gageKernelSet(ctx, gageKernel11, nrrdKernelBCCubicD, kparm); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclGradVec); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclNormal); if (!E) E |= gageUpdate(ctx); if (E) { airMopAdd(mop, err=biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting up Gage:\n%s\n", me, err); airMopError(mop); return 1; } grad = gageAnswerPointer(ctx, pvl, gageSclGradVec); norm = gageAnswerPointer(ctx, pvl, gageSclNormal); if (gageProbe(ctx, 1.1, 2.3, 6.8)) { fprintf(stderr, "%s: trouble:\n(%d) %s\n", me, ctx->errNum, ctx->errStr); airMopError(mop); return 1; } printf("gradient (%g,%g,%g) --> norm (%g,%g,%g)\n", grad[0], grad[1], grad[2], norm[0], norm[1], norm[2]); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/gage/test/vh.c0000664000175000017500000002307412042325724017046 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../gage.h" char *vhInfo = ("deepens valleys. a hack."); int main(int argc, const char *argv[]) { const char *me; char *outS; hestOpt *hopt; hestParm *hparm; airArray *mop; char *err, done[13]; Nrrd *nin, *nblur, *nout; NrrdKernelSpec *kb0, *kb1, *k00, *k11, *k22; NrrdResampleContext *rsmc; int E; unsigned int sx, sy, sz, xi, yi, zi, ai; gageContext *ctx; gagePerVolume *pvl; const double *gvec, *gmag, *evec0, *eval; double (*ins)(void *v, size_t I, double d); double (*lup)(const void *v, size_t I); double dotmax, dotpow, gmmax, evalshift, gmpow, _dotmax, _gmmax, scl, clamp; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "kb0", "kernel", airTypeOther, 1, 1, &kb0, "guass:3,5", "kernel to use for pre-process blurring", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "kb1", "kernel", airTypeOther, 1, 1, &kb1, "cubic:1.5,1,0", "kernel to use for pos-process blurring", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k00", "kernel", airTypeOther, 1, 1, &k00, "cubic:1,0", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kernel", airTypeOther, 1, 1, &k11, "cubicd:1,0", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kernel", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "dotmax", "dot", airTypeDouble, 1, 1, &dotmax, "5", "max effective value of dot(gvec, evec0)"); hestOptAdd(&hopt, "evs", "shift", airTypeDouble, 1, 1, &evalshift, "0", "negative shift to avoid changing mostly flat regions"); hestOptAdd(&hopt, "clamp", "clamp", airTypeDouble, 1, 1, &clamp, "nan", "if it exists, data value can't be forced below this"); hestOptAdd(&hopt, "dotpow", "pow", airTypeDouble, 1, 1, &dotpow, "1", "exponent for dot"); hestOptAdd(&hopt, "gmmax", "dot", airTypeDouble, 1, 1, &gmmax, "2", "max effective value of gmag"); hestOptAdd(&hopt, "gmpow", "pow", airTypeDouble, 1, 1, &gmpow, "1", "exponent for gmag"); hestOptAdd(&hopt, "scl", "scale", airTypeDouble, 1, 1, &scl, "0.1", "how much to scale hack to decrease input value"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "fixed volume output"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, vhInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( 3 == nin->dim && nrrdTypeBlock != nin->type )) { fprintf(stderr, "%s: need a 3-D scalar nrrd (not %u-D %s)", me, nin->dim, airEnumStr(nrrdType, nin->type)); airMopError(mop); return 1; } nblur = nrrdNew(); airMopAdd(mop, nblur, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nblur, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "%s: pre-blurring ... ", me); fflush(stderr); rsmc = nrrdResampleContextNew(); airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways); E = AIR_FALSE; if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, nrrdCenterCell); if (!E) E |= nrrdResampleInputSet(rsmc, nin); for (ai=0; ai<3; ai++) { if (!E) E |= nrrdResampleKernelSet(rsmc, ai, kb0->kernel, kb0->parm); if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, nin->axis[ai].size); if (!E) E |= nrrdResampleRangeFullSet(rsmc, ai); } if (!E) E |= nrrdResampleBoundarySet(rsmc, nrrdBoundaryBleed); if (!E) E |= nrrdResampleTypeOutSet(rsmc, nrrdTypeDefault); if (!E) E |= nrrdResampleRenormalizeSet(rsmc, AIR_TRUE); if (!E) E |= nrrdResampleExecute(rsmc, nblur); if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "done.\n"); ctx = gageContextNew(); airMopAdd(mop, ctx, (airMopper)gageContextNix, airMopAlways); gageParmSet(ctx, gageParmRenormalize, AIR_TRUE); gageParmSet(ctx, gageParmCheckIntegrals, AIR_TRUE); E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(ctx, nblur, gageKindScl)); if (!E) E |= gagePerVolumeAttach(ctx, pvl); if (!E) E |= gageKernelSet(ctx, gageKernel00, k00->kernel, k00->parm); if (!E) E |= gageKernelSet(ctx, gageKernel11, k11->kernel, k11->parm); if (!E) E |= gageKernelSet(ctx, gageKernel22, k22->kernel, k22->parm); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclGradVec); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclGradMag); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclHessEvec0); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclHessEval); if (!E) E |= gageUpdate(ctx); if (E) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } gvec = gageAnswerPointer(ctx, pvl, gageSclGradVec); gmag = gageAnswerPointer(ctx, pvl, gageSclGradMag); evec0 = gageAnswerPointer(ctx, pvl, gageSclHessEvec0); eval = gageAnswerPointer(ctx, pvl, gageSclHessEval); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s", me, err); airMopError(mop); return 1; } if (!(nout->type == nin->type && nblur->type == nin->type)) { fprintf(stderr, "%s: whoa, types (%s %s %s) not all equal\n", me, airEnumStr(nrrdType, nin->type), airEnumStr(nrrdType, nblur->type), airEnumStr(nrrdType, nout->type)); } ins = nrrdDInsert[nout->type]; lup = nrrdDLookup[nout->type]; sx = nin->axis[0].size; sy = nin->axis[1].size; sz = nin->axis[2].size; gageProbe(ctx, 0, 0, 0); _dotmax = ELL_3V_DOT(gvec, evec0); _gmmax = *gmag; fprintf(stderr, "%s: hacking ", me); fflush(stderr); for (zi=0; zidata, si); out = in - shift; out = AIR_MAX(out, clamp); shift = AIR_MAX(0, in - out); } ins(nout->data, si, shift); } } } fprintf(stderr, "\n"); fprintf(stderr, "%s: max dot seen: %g\n", me, _dotmax); fprintf(stderr, "%s: max gm seen: %g\n", me, _gmmax); fprintf(stderr, "%s: post-blurring ... ", me); fflush(stderr); E = AIR_FALSE; if (!E) E |= nrrdResampleInputSet(rsmc, nout); for (ai=0; ai<3; ai++) { if (!E) E |= nrrdResampleKernelSet(rsmc, ai, kb1->kernel, kb1->parm); if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, nout->axis[ai].size); if (!E) E |= nrrdResampleRangeFullSet(rsmc, ai); } if (!E) E |= nrrdResampleExecute(rsmc, nblur); if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "done.\n"); for (zi=0; zidata, si); shift = lup(nblur->data, si); ins(nout->data, si, in - shift); } } } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/gage/test/tplot.c0000664000175000017500000000610012200257241017554 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../gage.h" char *tplotInfo = ("for plotting tau"); int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt; hestParm *hparm; airArray *mop; double dommm[2]; unsigned int num; char *err; char *outS; Nrrd *nout; double *out, dd; unsigned int ii; int tee; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "tee", NULL, airTypeInt, 0, 0, &tee, NULL, "domain of plot is t, not sigma"); hestOptAdd(&hopt, "mm", "min max", airTypeDouble, 2, 2, dommm, NULL, "range of domain to plot"); hestOptAdd(&hopt, "n", "# samp", airTypeUInt, 1, 1, &num, NULL, "number of samples"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, NULL, "output volume"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, tplotInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(nout, nrrdTypeDouble, 1, AIR_CAST(size_t, num))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s", me, err); airMopError(mop); return 1; } out = AIR_CAST(double *, nout->data); nout->axis[0].min = dommm[0]; nout->axis[0].max = dommm[1]; nout->axis[0].center = nrrdCenterNode; for (ii=0; ii sig % 10.7g tau % 10.7g\n", me, val, gageSigOfTau(gageTauOfTee(val)), gageTauOfTee(val)); printf("%s: tau % 10.7g -> sig % 10.7g tee % 10.7g\n", me, val, gageSigOfTau(val), gageTeeOfTau(val)); printf("%s: sig % 10.7g -> tee % 10.7g tau % 10.7g\n", me, val, gageTeeOfTau(gageTauOfSig(val)), gageTauOfSig(val)); airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/gage/test/indx.c0000664000175000017500000000537012042325724017372 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../gage.h" char *indxInfo = ("for debugging part of _gageLocationSet"); int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt; hestParm *hparm; airArray *mop; double *val, top, min, max; unsigned ii, valLen, xi, size; int center; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, NULL, "val0 val1", airTypeDouble, 1, -1, &val, NULL, "input values", &valLen, NULL); hestOptAdd(&hopt, "c,center", "center", airTypeEnum, 1, 1, ¢er, NULL, "centering (cell or node)", NULL, nrrdCenter); hestOptAdd(&hopt, "s", "size", airTypeUInt, 1, 1, &size, NULL, "number of samples"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, indxInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); fprintf(stderr, "%s: using %s centering\n", me, airEnumStr(nrrdCenter, center)); top = size-1; if (nrrdCenterNode == center) { min = 0; max = top; } else { min = -0.5; max = top + 0.5; } for (ii=0; iikind = kind; gmi->item = NULL; gmi->ansDir = NULL; gmi->ansLen = NULL; gmi->nans = nrrdNew(); } return gmi; } gageMultiItem * /*Teem: no error */ gageMultiItemNix(gageMultiItem *gmi) { if (gmi) { airFree(gmi->item); airFree(gmi); } return NULL; } gageMultiItem * /*Teem: no error */ gageMultiItemNuke(gageMultiItem *gmi) { if (gmi) { nrrdNuke(gmi->nans); } return gageMultiItemNix(gmi); } int /*Teem: biff if (ret) */ gageMultiItemSet(gageMultiItem *gmi, const int *item, unsigned int itemNum) { static const char me[]="gageMultiItemSet"; unsigned int ii; if (!(gmi && item)) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!itemNum) { biffAddf(GAGE, "%s: can't set zero items", me); return 1; } gmi->item = AIR_CAST(int *, airFree(gmi->item)); if (!( gmi->item = AIR_CALLOC(itemNum, int) )) { biffAddf(GAGE, "%s: couldn't allocate %u ints for items", me, itemNum); return 1; } for (ii=0; iikind->enm, item[ii])) { biffAddf(GAGE, "%s: item[%u] %d not a valid %s value", me, ii, item[ii], gmi->kind->enm->name); return 1; } gmi->item[ii] = item[ii]; fprintf(stderr, "!%s: item[%u] = %d %s\n", me, ii, item[ii], airEnumStr(gmi->kind->enm, item[ii])); } return 0; } /* ******** gageMultiItemSet_va ** ** How to set (in one call) the multiple items in a gageMultiItem. ** These are the items for which the answers will be collected into ** a single nrrd on output (maximizing memory locality). */ int /*Teem: biff if (ret) */ gageMultiItemSet_va(gageMultiItem *gmi, unsigned int itemNum, ... /* itemNum items */) { static const char me[]="gageMultiItemSet_va"; int *item; unsigned int ii; va_list ap; airArray *mop; if (!gmi) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!itemNum) { biffAddf(GAGE, "%s: can't set zero items", me); return 1; } if (!( item = AIR_CALLOC(itemNum, int) )) { biffAddf(GAGE, "%s: couldn't allocate %u ints for items", me, itemNum); return 1; } mop = airMopNew(); airMopAdd(mop, item, airFree, airMopAlways); /* consume items from var args */ va_start(ap, itemNum); for (ii=0; iipvlNum = gctx->pvlNum; gmq->mitmNum = AIR_CALLOC(gmq->pvlNum, unsigned int); gmq->mitm = AIR_CALLOC(gmq->pvlNum, gageMultiItem **); gmq->nidx = nrrdNew(); if (!( gmq->mitmNum && gmq->mitm && gmq->nidx )) { /* bail */ airFree(gmq->mitmNum); airFree(gmq->mitm); nrrdNuke(gmq->nidx); airFree(gmq); gmq=NULL; } else { /* allocated everything ok */ unsigned int qi; for (qi=0; qipvlNum; qi++) { gmq->mitm[qi] = NULL; } } } return gmq; } /* ******** gageMultiQueryAdd_va ** ** add multi-items for one particular pvl (pvlIdx) */ int /*Teem: biff if (ret) */ gageMultiQueryAdd_va(gageContext *gctx, gageMultiQuery *gmq, unsigned int pvlIdx, unsigned int mitmNum, ... /* mitmNum gageMultiItem* */) { static const char me[]="gageMultiQueryAdd_va"; unsigned int qi; va_list ap; if (!gmq) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!( pvlIdx < gmq->pvlNum )) { biffAddf(GAGE, "%s: pvlIdx %u not in valid range [0,%u]", me, pvlIdx, gmq->pvlNum-1); return 1; } gmq->mitmNum[pvlIdx] = mitmNum; gmq->mitm[pvlIdx] = AIR_CALLOC(mitmNum, gageMultiItem*); /* consume and add items to context */ va_start(ap, mitmNum); for (qi=0; qimitm[pvlIdx][qi] = va_arg(ap, gageMultiItem*); } va_end(ap); AIR_UNUSED(gctx); return 0; } int /*Teem: biff if (ret) */ gageMultiProbe(gageContext *gctx, gageMultiQuery *gmq, const gageMultiInput *minput) { AIR_UNUSED(gmq); AIR_UNUSED(gctx); AIR_UNUSED(minput); return 0; } gageMultiQuery * /*Teem: no error */ gageMultiQueryNix(gageMultiQuery *gmq) { AIR_UNUSED(gmq); return NULL; } /* ** here the difference between nix and nuke is where the ** nans in gageMultiItem is freed? */ gageMultiQuery * /*Teem: no error */ gageMultiQueryNuke(gageMultiQuery *gmq) { AIR_UNUSED(gmq); return NULL; } teem-1.11.0~svn6057/src/gage/TODO.txt0000664000175000017500000000253612026462327016617 0ustar domibeldomibelshould really work on gage tests before more re-architecting for third derivatives: * have to update gageScl3PFilter_t and change arguments to not itemize filter weights or derivative answers, involving sclfilter.c, scl3pfilterbody.c, * add gageSclJerk (full third derivative) * considering third-deriv-abort directory: NO: it does make sense to talk about second derivatives with a 2-support kernel: the small kernel still has second derivatives, even if its constant zero. With tent, for example, the mixed partials will still be non-zero recovering the pre-ISBI12-optim work * ctx.c: removal of gageIv3Fill * iv3Gage.c: new home for new gageIv3Fill, which includes some type-specific shortcuts * stack.c: use of new stackIv3FillBody.c * filter.c: some nrrdKernelHermiteFlag-specific smarts (line 386) * gage.h: gageContext->hermiteNonZeroPvlIndex (line 649) figure out how to handle derivative renormalizating (for scale) at scale=0 add flags for explicitly specifying gmax, hmax, vrange 6pack filtering, adapt vector stuff change type of iv3Fill to take (int xi,int yi,int zi) instead of void *here; one step towards supporting bricking. test: two pervolumes with different sized volumes test: NRWS doing its job retest comparison of upsampling by probing and by unu resample make the curvature direction code (scl.c) more robust teem-1.11.0~svn6057/src/gage/ctx.c0000664000175000017500000007343712174665402016266 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gage.h" #include "privateGage.h" typedef union { void **vd; gagePerVolume ***pvl; } perVolumeUnion; /* ******** gageContextNew() ** ** doesn't use biff */ gageContext * gageContextNew() { gageContext *ctx; perVolumeUnion pvu; int ii; ctx = AIR_CALLOC(1, gageContext); if (ctx) { ctx->verbose = gageDefVerbose; gageParmReset(&ctx->parm); for (ii=gageKernelUnknown+1; iiksp[ii] = NULL; } ctx->pvl = NULL; ctx->pvlNum = 0; pvu.pvl = &(ctx->pvl); ctx->pvlArr = airArrayNew(pvu.vd, &(ctx->pvlNum), sizeof(gagePerVolume*), GAGE_PERVOLUME_ARR_INCR); gageKernelReset(ctx); /* placed here for logic of kernel flag */ ctx->shape = gageShapeNew(); for (ii=gageCtxFlagUnknown+1; iiflag[ii] = AIR_FALSE; } ctx->stackPos = NULL; ctx->stackFsl = NULL; ctx->stackFw = NULL; for (ii=0; ii<=GAGE_DERIV_MAX; ii++) { ctx->needD[ii] = AIR_FALSE; } for (ii=gageKernelUnknown+1; iineedK[ii] = AIR_FALSE; } ctx->radius = 0; ctx->fsl = ctx->fw = NULL; ctx->off = NULL; gagePointReset(&ctx->point); strcpy(ctx->errStr, ""); ctx->errNum = gageErrNone; ctx->edgeFrac = 0; } return ctx; } /* ******** gageContextCopy() ** ** gives you a new context, which behaves the same as the given context, ** with newly allocated pervolumes attached. With the avoidance of ** padding to create a private copy of the volume, the gageContext is ** light-weight enough that there is no reason that this function can't ** return an independent and fully functioning copy of the context (whereas ** before you weren't allowed to do anything but gageProbe() on the ** copied context). */ gageContext * /*Teem: biff if (!ret) */ gageContextCopy(gageContext *ctx) { static const char me[]="gageContextCopy"; gageContext *ntx; unsigned int fd, pvlIdx; perVolumeUnion pvu; int ki; ntx = AIR_CALLOC(1, gageContext); if (!ntx) { biffAddf(GAGE, "%s: couldn't make a gageContext", me); return NULL; } /* we should probably restrict ourselves to gage API calls, but given the constant state of gage construction, this seems much simpler. Pointers are fixed below */ memcpy(ntx, ctx, sizeof(gageContext)); for (ki=gageKernelUnknown+1; kiksp[ki] = nrrdKernelSpecCopy(ctx->ksp[ki]); } pvu.pvl = &(ntx->pvl); ntx->pvlArr = airArrayNew(pvu.vd, &(ntx->pvlNum), sizeof(gagePerVolume*), GAGE_PERVOLUME_ARR_INCR); airArrayLenSet(ntx->pvlArr, ctx->pvlNum); if (!ntx->pvl) { biffAddf(GAGE, "%s: couldn't allocate new pvl array", me); return NULL; } for (pvlIdx=0; pvlIdxpvlNum; pvlIdx++) { ntx->pvl[pvlIdx] = _gagePerVolumeCopy(ctx->pvl[pvlIdx], 2*ctx->radius); if (!ntx->pvl[pvlIdx]) { biffAddf(GAGE, "%s: trouble copying pervolume %u", me, pvlIdx); return NULL; } } if (ctx->stackPos && ctx->stackFsl && ctx->stackFw) { ntx->stackPos = AIR_CALLOC(ctx->pvlNum-1, double); ntx->stackFsl = AIR_CALLOC(ctx->pvlNum-1, double); ntx->stackFw = AIR_CALLOC(ctx->pvlNum-1, double); if (!( ntx->stackPos && ntx->stackFsl && ntx->stackFw )) { biffAddf(GAGE, "%s: couldn't allocate stack Pos, Fsl, Fw", me); return NULL; } for (pvlIdx=0; pvlIdxpvlNum-1; pvlIdx++) { ntx->stackPos[pvlIdx] = ctx->stackPos[pvlIdx]; ntx->stackFsl[pvlIdx] = ctx->stackFsl[pvlIdx]; ntx->stackFw[pvlIdx] = ctx->stackFw[pvlIdx]; } } else { ntx->stackPos = NULL; ntx->stackFsl = NULL; ntx->stackFw = NULL; } ntx->shape = gageShapeCopy(ctx->shape); fd = 2*ntx->radius; ntx->fsl = AIR_CALLOC(fd*3, double); ntx->fw = AIR_CALLOC(fd*3*(GAGE_KERNEL_MAX+1), double); ntx->off = AIR_CALLOC(fd*fd*fd, unsigned int); if (!( ntx->fsl && ntx->fw && ntx->off )) { biffAddf(GAGE, "%s: couldn't allocate new filter caches for fd=%d", me, fd); return NULL; } /* the content of the offset array needs to be copied because it won't be refilled simply by calls to gageProbe() */ memcpy(ntx->off, ctx->off, fd*fd*fd*sizeof(unsigned int)); /* make sure gageProbe() has to refill caches */ gagePointReset(&ntx->point); return ntx; } /* ******** gageContextNix() ** ** responsible for freeing and clearing up everything hanging off a ** context so that things can be returned to the way they were prior ** to gageContextNew(). ** ** does not use biff */ gageContext * gageContextNix(gageContext *ctx) { /* static const char me[]="gageContextNix"; */ unsigned int pvlIdx; if (ctx) { gageKernelReset(ctx); for (pvlIdx=0; pvlIdxpvlNum; pvlIdx++) { gagePerVolumeNix(ctx->pvl[pvlIdx]); /* no point in doing a detach, the whole context is going bye-bye */ } airArrayNuke(ctx->pvlArr); ctx->shape = gageShapeNix(ctx->shape); ctx->stackPos = AIR_CAST(double *, airFree(ctx->stackPos)); ctx->stackFsl = AIR_CAST(double *, airFree(ctx->stackFsl)); ctx->stackFw = AIR_CAST(double *, airFree(ctx->stackFw)); ctx->fw = AIR_CAST(double *, airFree(ctx->fw)); ctx->fsl = AIR_CAST(double *, airFree(ctx->fsl)); ctx->off = AIR_CAST(unsigned int *, airFree(ctx->off)); } airFree(ctx); return NULL; } /* ******** gageKernelSet() ** ** sets one kernel in a gageContext; but the value of this function ** is all the error checking it does. ** ** Refers to ctx->checkIntegrals and acts appropriately. ** ** Does use biff. */ int gageKernelSet(gageContext *ctx, int which, const NrrdKernel *k, const double *kparm) { static const char me[]="gageKernelSet"; int numParm; double support, integral; if (!(ctx && k && kparm)) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(gageKernel, which)) { biffAddf(GAGE, "%s: \"which\" (%d) not in range [%d,%d]", me, which, gageKernelUnknown+1, gageKernelLast-1); return 1; } if (ctx->verbose) { fprintf(stderr, "%s: which = %d -> %s\n", me, which, airEnumStr(gageKernel, which)); } numParm = k->numParm; if (!(AIR_IN_CL(0, numParm, NRRD_KERNEL_PARMS_NUM))) { biffAddf(GAGE, "%s: kernel's numParm (%d) not in range [%d,%d]", me, numParm, 0, NRRD_KERNEL_PARMS_NUM); return 1; } support = k->support(kparm); if (!( support > 0 )) { biffAddf(GAGE, "%s: kernel's support (%g) not > 0", me, support); return 1; } if (ctx->parm.checkIntegrals) { integral = k->integral(kparm); if (gageKernel00 == which || gageKernel10 == which || gageKernel20 == which || gageKernelStack == which) { if (!( integral > 0 )) { biffAddf(GAGE, "%s: reconstruction kernel's integral (%g) not > 0.0", me, integral); return 1; } } else { /* its a derivative, so integral must be near zero */ if (!( AIR_ABS(integral) <= ctx->parm.kernelIntegralNearZero )) { char str[AIR_STRLEN_LARGE]=""; nrrdKernelSprint(str, k, kparm); biffAddf(GAGE, "%s: derivative %s kernel (%s) integral %g not within " "%g of 0.0", me, airEnumStr(gageKernel, which), str, integral, ctx->parm.kernelIntegralNearZero); return 1; } } } /* okay, enough enough, go set the kernel */ if (!ctx->ksp[which]) { ctx->ksp[which] = nrrdKernelSpecNew(); } nrrdKernelSpecSet(ctx->ksp[which], k, kparm); ctx->flag[gageCtxFlagKernel] = AIR_TRUE; return 0; } /* ******** gageKernelReset() ** ** reset kernels (including the stack ksp) and parameters. */ void gageKernelReset(gageContext *ctx) { /* static const char me[]="gageKernelReset"; */ int i; if (ctx) { for(i=gageKernelUnknown+1; iksp[i] = nrrdKernelSpecNix(ctx->ksp[i]); } ctx->flag[gageCtxFlagKernel] = AIR_TRUE; } return; } /* ******** gageParmSet() ** ** for setting the boolean-ish flags in the context in a safe and ** intelligent manner, since changing some of them can have many ** consequences */ void gageParmSet(gageContext *ctx, int which, double val) { static const char me[]="gageParmSet"; unsigned int pvlIdx; switch (which) { case gageParmVerbose: ctx->verbose = AIR_CAST(int, val); if (ctx->verbose > 3) { fprintf(stderr, "%s(%p): ctx->verbose now %d\n", me, AIR_CAST(void *, ctx), ctx->verbose); } for (pvlIdx=0; pvlIdxpvlNum; pvlIdx++) { ctx->pvl[pvlIdx]->verbose = AIR_CAST(int, val); if (ctx->pvl[pvlIdx]->verbose > 3) { fprintf(stderr, "%s: ctx->pvl[%u]->verbose now %d\n", me, pvlIdx, ctx->pvl[pvlIdx]->verbose); } } break; case gageParmRenormalize: ctx->parm.renormalize = val ? AIR_TRUE : AIR_FALSE; /* we have to make sure that any existing filter weights are not re-used; because gageUpdage() is not called mid-probing, we don't use the flag machinery. Instead we just invalidate the last known fractional probe locations */ gagePointReset(&ctx->point); break; case gageParmCheckIntegrals: ctx->parm.checkIntegrals = val ? AIR_TRUE : AIR_FALSE; /* no flags to set, simply affects future calls to gageKernelSet() */ break; case gageParmK3Pack: ctx->parm.k3pack = val ? AIR_TRUE : AIR_FALSE; ctx->flag[gageCtxFlagK3Pack] = AIR_TRUE; break; case gageParmGradMagCurvMin: ctx->parm.gradMagCurvMin = val; /* no flag to set, simply affects future calls to gageProbe() */ break; case gageParmCurvNormalSide: ctx->parm.curvNormalSide = AIR_CAST(int, val); /* no flag to set, simply affects future calls to gageProbe() */ break; case gageParmKernelIntegralNearZero: ctx->parm.kernelIntegralNearZero = val; /* no flag to set, simply affects future calls to gageKernelSet() */ break; case gageParmDefaultCenter: ctx->parm.defaultCenter = AIR_CAST(int, val); /* no flag to set, I guess, although the value here effects the action of _gageShapeSet when called by gagePerVolumeAttach . . . */ break; case gageParmStackUse: ctx->parm.stackUse = AIR_CAST(int, val); /* no flag to set, right? simply affects future calls to gageProbe()? */ /* HEY: no? because if you're turning on the stack behavior, you now should be doing the error checking to make sure that all the pvls have the same kind! This should be caught by gageUpdate(), which is supposed to be called after changing anything, prior to gageProbe() */ break; case gageParmStackNormalizeRecon: ctx->parm.stackNormalizeRecon = AIR_CAST(int, val); break; case gageParmStackNormalizeDeriv: ctx->parm.stackNormalizeDeriv = AIR_CAST(int, val); break; case gageParmStackNormalizeDerivBias: ctx->parm.stackNormalizeDerivBias = val; break; case gageParmOrientationFromSpacing: ctx->parm.orientationFromSpacing = AIR_CAST(int, val); /* affects future calls to _gageShapeSet */ break; case gageParmGenerateErrStr: ctx->parm.generateErrStr = AIR_CAST(int, val); break; case gageParmTwoDimZeroZ: ctx->parm.twoDimZeroZ = AIR_CAST(int, val); break; default: fprintf(stderr, "\n%s: sorry, which = %d not valid\n\n", me, which); break; } return; } /* ******** gagePerVolumeIsAttached() ** */ int gagePerVolumeIsAttached(const gageContext *ctx, const gagePerVolume *pvl) { int ret; unsigned int pvlIdx; ret = AIR_FALSE; for (pvlIdx=0; pvlIdxpvlNum; pvlIdx++) { if (pvl == ctx->pvl[pvlIdx]) { ret = AIR_TRUE; } } return ret; } /* ******** gagePerVolumeAttach() ** ** attaches a pervolume to a context, which actually involves ** very little work */ int gagePerVolumeAttach(gageContext *ctx, gagePerVolume *pvl) { static const char me[]="gagePerVolumeAttach"; gageShape *shape; unsigned int newidx; if (!( ctx && pvl )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (gagePerVolumeIsAttached(ctx, pvl)) { biffAddf(GAGE, "%s: given pervolume already attached", me); return 1; } if (0 == ctx->pvlNum) { /* the volume "shape" is context state that we set now, because unlike everything else (handled by gageUpdate()), it does not effect the kind or amount of padding done */ if (_gageShapeSet(ctx, ctx->shape, pvl->nin, pvl->kind->baseDim)) { biffAddf(GAGE, "%s: trouble", me); return 1; } ctx->flag[gageCtxFlagShape] = AIR_TRUE; } else { /* have to check to that new pvl matches first one. Since all attached pvls were at some point the "new" one, they all should match each other */ shape = gageShapeNew(); if (_gageShapeSet(ctx, shape, pvl->nin, pvl->kind->baseDim)) { biffAddf(GAGE, "%s: trouble", me); return 1; } if (!gageShapeEqual(ctx->shape, "existing context", shape, "new volume")) { biffAddf(GAGE, "%s: trouble", me); gageShapeNix(shape); return 1; } gageShapeNix(shape); } /* here we go */ newidx = airArrayLenIncr(ctx->pvlArr, 1); if (!ctx->pvl) { biffAddf(GAGE, "%s: couldn't increase length of pvl", me); return 1; } ctx->pvl[newidx] = pvl; pvl->verbose = ctx->verbose; return 0; } /* ******** gagePerVolumeDetach() ** ** detaches a pervolume from a context, but does nothing else ** with the pervolume; caller may want to call gagePerVolumeNix ** if this pervolume will no longer be used */ int gagePerVolumeDetach(gageContext *ctx, gagePerVolume *pvl) { static const char me[]="gagePerVolumeDetach"; unsigned int pvlIdx, foundIdx=0; if (!( ctx && pvl )) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!gagePerVolumeIsAttached(ctx, pvl)) { biffAddf(GAGE, "%s: given pervolume not currently attached", me); return 1; } for (pvlIdx=0; pvlIdxpvlNum; pvlIdx++) { if (pvl == ctx->pvl[pvlIdx]) { foundIdx = pvlIdx; } } for (pvlIdx=foundIdx+1; pvlIdxpvlNum; pvlIdx++) { ctx->pvl[pvlIdx-1] = ctx->pvl[pvlIdx]; } ctx->pvl[ctx->pvlNum-1] = NULL; airArrayLenIncr(ctx->pvlArr, -1); if (0 == ctx->pvlNum) { /* leave things the way that they started */ gageShapeReset(ctx->shape); ctx->flag[gageCtxFlagShape] = AIR_TRUE; } return 0; } /* ** gageIv3Fill() ** ** based on ctx's shape and radius, and the (xi,yi,zi) determined from ** the probe location, fills the iv3 cache in the given pervolume ** ** This function is a bottleneck for so many things, so forcing off ** the verbose comments seems like one way of trying to speed it up. ** However, temporarily if-def'ing out unused branches of the ** "switch(pvl->kind->valLen)", and #define'ing fddd to 64 (for 4x4x4 ** neighborhoods) did not noticeably speed anything up. ** */ void gageIv3Fill(gageContext *ctx, gagePerVolume *pvl) { static const char me[]="gageIv3Fill"; int lx, ly, lz, hx, hy, hz, _xx, _yy, _zz; unsigned int xx, yy, zz, fr, cacheIdx, dataIdx, fddd; unsigned int sx, sy, sz; char *data, *here; unsigned int tup; sx = ctx->shape->size[0]; sy = ctx->shape->size[1]; sz = ctx->shape->size[2]; fr = ctx->radius; /* idx[0]-1: see Thu Jan 14 comment in filter.c */ lx = ctx->point.idx[0]-1 - (fr - 1); ly = ctx->point.idx[1]-1 - (fr - 1); lz = ctx->point.idx[2]-1 - (fr - 1); hx = lx + 2*fr - 1; hy = ly + 2*fr - 1; hz = lz + 2*fr - 1; fddd = 2*fr*2*fr*2*fr; if (ctx->verbose > 1) { fprintf(stderr, "%s: ___ hello; s %u %u %u; fr %u\n", me, sx, sy, sz, fr); fprintf(stderr, "%s: point.idx %u %u %u\n", me, ctx->point.idx[0], ctx->point.idx[1], ctx->point.idx[2]); fprintf(stderr, "%s: l %d %d %d; h %d %d %d; fddd %u\n", me, lx, ly, lz, hx, hy, hz, fddd); } data = (char*)pvl->nin->data; if (lx >= 0 && ly >= 0 && lz >= 0 && hx < AIR_CAST(int, sx) && hy < AIR_CAST(int, sy) && hz < AIR_CAST(int, sz)) { /* all the samples we need are inside the existing volume */ dataIdx = lx + sx*(ly + sy*(lz)); if (ctx->verbose > 1) { fprintf(stderr, "%s: hello, valLen = %d, pvl->nin = %p, data = %p\n", me, pvl->kind->valLen, AIR_CVOIDP(pvl->nin), pvl->nin->data); } here = data + dataIdx*pvl->kind->valLen*nrrdTypeSize[pvl->nin->type]; if (ctx->verbose > 1) { fprintf(stderr, "%s: size = (%u,%u,%u);\n" "%s: fd = %d; coord = (%u,%u,%u) --> dataIdx = %d\n", me, sx, sy, sz, me, 2*fr, ctx->point.idx[0], ctx->point.idx[1], ctx->point.idx[2], dataIdx); fprintf(stderr, "%s: here = %p; iv3 = %p; off[0,1,2,3,4,5,6,7] = " "%d,%d,%d,%d,%d,%d,%d,%d\n", me, here, AIR_CAST(void*, pvl->iv3), ctx->off[0], ctx->off[1], ctx->off[2], ctx->off[3], ctx->off[4], ctx->off[5], ctx->off[6], ctx->off[7]); } switch(pvl->kind->valLen) { case 1: for (cacheIdx=0; cacheIdxiv3[cacheIdx] = pvl->lup(here, ctx->off[cacheIdx]); } break; /* NOTE: the tuple axis is being shifted from the fastest to the slowest axis, to anticipate component-wise filtering operations */ case 3: for (cacheIdx=0; cacheIdxiv3[cacheIdx + fddd*0] = pvl->lup(here, 0 + 3*ctx->off[cacheIdx]); pvl->iv3[cacheIdx + fddd*1] = pvl->lup(here, 1 + 3*ctx->off[cacheIdx]); pvl->iv3[cacheIdx + fddd*2] = pvl->lup(here, 2 + 3*ctx->off[cacheIdx]); } break; case 7: /* this might come in handy for tenGage . . . */ for (cacheIdx=0; cacheIdxiv3[cacheIdx + fddd*0] = pvl->lup(here, 0 + 7*ctx->off[cacheIdx]); pvl->iv3[cacheIdx + fddd*1] = pvl->lup(here, 1 + 7*ctx->off[cacheIdx]); pvl->iv3[cacheIdx + fddd*2] = pvl->lup(here, 2 + 7*ctx->off[cacheIdx]); pvl->iv3[cacheIdx + fddd*3] = pvl->lup(here, 3 + 7*ctx->off[cacheIdx]); pvl->iv3[cacheIdx + fddd*4] = pvl->lup(here, 4 + 7*ctx->off[cacheIdx]); pvl->iv3[cacheIdx + fddd*5] = pvl->lup(here, 5 + 7*ctx->off[cacheIdx]); pvl->iv3[cacheIdx + fddd*6] = pvl->lup(here, 6 + 7*ctx->off[cacheIdx]); } break; default: for (cacheIdx=0; cacheIdxkind->valLen; tup++) { pvl->iv3[cacheIdx + fddd*tup] = pvl->lup(here, tup + pvl->kind->valLen*ctx->off[cacheIdx]); } } break; } ctx->edgeFrac = 0; } else { unsigned int edgeNum; /* the query requires samples which don't actually lie within the volume- more care has to be taken */ cacheIdx = 0; edgeNum = 0; for (_zz=lz; _zz<=hz; _zz++) { zz = AIR_CLAMP(0, _zz, AIR_CAST(int, sz-1)); for (_yy=ly; _yy<=hy; _yy++) { yy = AIR_CLAMP(0, _yy, AIR_CAST(int, sy-1)); for (_xx=lx; _xx<=hx; _xx++) { xx = AIR_CLAMP(0, _xx, AIR_CAST(int, sx-1)); edgeNum += ((AIR_CAST(int, zz) != _zz) || (AIR_CAST(int, yy) != _yy) || (AIR_CAST(int, xx) != _xx)); dataIdx = xx + sx*(yy + sy*zz); here = data + dataIdx*pvl->kind->valLen*nrrdTypeSize[pvl->nin->type]; if (ctx->verbose > 2) { fprintf(stderr, "%s: (%d,%d,%d) --clamp--> (%u,%u,%u)\n", me, _xx, _yy, _zz, xx, yy, zz); fprintf(stderr, " --> dataIdx = %d; data = %p -> here = %p\n", dataIdx, data, here); } for (tup=0; tupkind->valLen; tup++) { pvl->iv3[cacheIdx + fddd*tup] = pvl->lup(here, tup); if (ctx->verbose > 3) { fprintf(stderr, "%s: iv3[%u + %u*%u=%u] = %g\n", me, cacheIdx, fddd, tup, cacheIdx + fddd*tup, pvl->iv3[cacheIdx + fddd*tup]); } } cacheIdx++; } } } ctx->edgeFrac = AIR_CAST(double, edgeNum)/fddd; } if (ctx->verbose > 1) { fprintf(stderr, "%s: ^^^ bye\n", me); } return; } /* ** _gageProbe ** ** how to do probing. (x,y,z) position is *index space* position. ** Note, however, that derivatives (gradients and hessians) will ** effectively be computed in *world space*. ** ** doesn't actually do much more than call callbacks in the gageKind ** structs of the attached pervolumes ** ** NOTE: the stack filter weights are (like the spatial filter weights) ** computed inside _gageLocationSet() */ int _gageProbe(gageContext *ctx, double _xi, double _yi, double _zi, double _si) { static const char me[]="_gageProbe"; unsigned int oldIdx[4], oldNnz=0, pvlIdx; int idxChanged; if (!ctx) { return 1; } if (ctx->verbose > 3) { fprintf(stderr, "%s: hello(%g,%g,%g,%g) _____________ \n", me, _xi, _yi, _zi, _si); } ELL_4V_COPY(oldIdx, ctx->point.idx); oldNnz = ctx->point.stackFwNonZeroNum; if (_gageLocationSet(ctx, _xi, _yi, _zi, _si)) { /* we're outside the volume; leave ctx->errNum and ctx->errStr set; as they have just been set by _gageLocationSet() */ /* GLK had added but not checked in the following line; the logic of this has to be studied further */ /* ctx->edgeFrac = 0.666; */ return 1; } /* if necessary, refill the iv3 cache */ idxChanged = (oldIdx[0] != ctx->point.idx[0] || oldIdx[1] != ctx->point.idx[1] || oldIdx[2] != ctx->point.idx[2]); if (ctx->parm.stackUse) { idxChanged |= oldIdx[3] != ctx->point.idx[3]; /* this is subtle (and the source of a difficult bug): even if point.idx[3] has not changed, you can still have a change in which of the stackFw[] are non-zero, which in turn determines which iv3s have to be refilled. For example, changing _si from 0.0 to 0.1, using tent or hermite reconstruction, will newly require pvl[1]'s iv3 to be refilled. To catch this kind of situation, we could keep a list of which iv3s are active and look for changes in that, or, we could just look for changes in point.idx[3] AND changes in stackFwNonZeroNum */ idxChanged |= oldNnz != ctx->point.stackFwNonZeroNum; } if (ctx->verbose > 3) { fprintf(stderr, "%s: oldIdx %u %u %u %u, point.idx %u %u %u %u --> %d\n", me, oldIdx[0], oldIdx[1], oldIdx[2], oldIdx[3], ctx->point.idx[0], ctx->point.idx[1], ctx->point.idx[2], ctx->point.idx[3], idxChanged); } if (idxChanged) { if (!ctx->parm.stackUse) { for (pvlIdx=0; pvlIdxpvlNum; pvlIdx++) { if (ctx->verbose > 3) { fprintf(stderr, "%s: gageIv3Fill(pvl[%u/%u] %s): .......\n", me, pvlIdx, ctx->pvlNum, ctx->pvl[pvlIdx]->kind->name); } gageIv3Fill(ctx, ctx->pvl[pvlIdx]); } } else { for (pvlIdx=0; pvlIdxpvlNum-1; pvlIdx++) { /* note that we only fill the cache for the stack samples that have a non-zero weight. HEY, however, it would be nice to only refill the iv3 that we have to, based on the change in scale, instead of refilling all of them in the support of the stack recon */ if (ctx->stackFw[pvlIdx]) { if (ctx->verbose > 3) { fprintf(stderr, "%s: stackFw[%u] == %g -> iv3fill needed\n", me, pvlIdx, ctx->stackFw[pvlIdx]); } gageIv3Fill(ctx, ctx->pvl[pvlIdx]); } else { if (ctx->verbose > 3) { fprintf(stderr, "%s: stackFw[%u] == %g -> NO iv3fill\n", me, pvlIdx, ctx->stackFw[pvlIdx]); } } } } } if (ctx->parm.stackUse) { unsigned int baseIdx, vi; baseIdx = ctx->pvlNum - 1; if (ctx->verbose > 3) { for (vi=0; vipoint.idx[0], ctx->point.idx[1], ctx->point.idx[2]); ctx->pvl[vi]->kind->iv3Print(stderr, ctx, ctx->pvl[vi]); } } _gageStackBaseIv3Fill(ctx); if (ctx->verbose > 3) { fprintf(stderr, "%s: (stack) base pvl's value cache at " "coords = %u,%u,%u:\n", me, ctx->point.idx[0], ctx->point.idx[1], ctx->point.idx[2]); ctx->pvl[baseIdx]->kind->iv3Print(stderr, ctx, ctx->pvl[baseIdx]); } ctx->pvl[baseIdx]->kind->filter(ctx, ctx->pvl[baseIdx]); ctx->pvl[baseIdx]->kind->answer(ctx, ctx->pvl[baseIdx]); } else { for (pvlIdx=0; pvlIdxpvlNum; pvlIdx++) { if (ctx->verbose > 3) { fprintf(stderr, "%s: pvl[%u/%u %s]'s value cache at " "coords = %u,%u,%u:\n", me, pvlIdx, ctx->pvlNum, ctx->pvl[pvlIdx]->kind->name, ctx->point.idx[0], ctx->point.idx[1], ctx->point.idx[2]); ctx->pvl[pvlIdx]->kind->iv3Print(stderr, ctx, ctx->pvl[pvlIdx]); } ctx->pvl[pvlIdx]->kind->filter(ctx, ctx->pvl[pvlIdx]); ctx->pvl[pvlIdx]->kind->answer(ctx, ctx->pvl[pvlIdx]); } } if (ctx->verbose > 3) { fprintf(stderr, "%s: bye ^^^^^^^^^^^^^ \n\n", me); } return 0; } /* ******** gageProbe() ** ** bummer: the non-stack gageProbe function is now just a wrapper ** around the stack-based gageProbe */ int gageProbe(gageContext *ctx, double xi, double yi, double zi) { return _gageProbe(ctx, xi, yi, zi, 0.0); } int _gageProbeSpace(gageContext *ctx, double xx, double yy, double zz, double ss, int indexSpace, int clamp) { static const char me[]="_gageProbeSpace"; unsigned int *size; double xi, yi, zi, si; if (ctx->verbose > 3) { fprintf(stderr, "%s: hi; pos=(%g,%g,%g,%g) in %s space %s clamping\n", me, xx, yy, zz, ss, indexSpace ? "index" : "world", clamp ? "WITH" : "w/out"); } size = ctx->shape->size; if (indexSpace) { xi = xx; yi = yy; zi = zz; if (ctx->parm.stackUse) { si = ss; } else { si = 0; } if (ctx->verbose > 3) { fprintf(stderr, "%s: staying at ipos (%g,%g,%g)\n", me, xi, yi, zi); } } else { /* have to convert from world to index. NOTE: the [4]s here are for homogeneous coordinates, not for anything stack-related */ double icoord[4]; double wcoord[4]; ELL_4V_SET(wcoord, xx, yy, zz, 1); ELL_4MV_MUL(icoord, ctx->shape->WtoI, wcoord); ELL_4V_HOMOG(icoord, icoord); xi = icoord[0]; yi = icoord[1]; zi = icoord[2]; if (ctx->parm.stackUse) { unsigned int sidx; /* HEY: this is a stupid linear search! */ if (ss < ctx->stackPos[0]) { /* we'll extrapolate from stackPos[0] and [1]. um, actually the extrapolation is overkill because we're either going to clamp out-of-range scale index positions, or they'll cause a gageErrStackBounds error downstream */ sidx = 0; } else if (ss > ctx->stackPos[ctx->pvlNum-2]) { /* extrapolate from stackPos[ctx->pvlNum-3] and [ctx->pvlNum-2]; gageStackPerVolumeAttach ensures that we there are at least two blurrings pvls and one base pvl */ sidx = ctx->pvlNum-3; } else { for (sidx=0; sidxpvlNum-2; sidx++) { if (AIR_IN_CL(ctx->stackPos[sidx], ss, ctx->stackPos[sidx+1])) { break; } } if (sidx == ctx->pvlNum-2) { if (ctx->parm.generateErrStr) { sprintf(ctx->errStr, "%s: search failure for ss = %g", me, ss); } else { strcpy(ctx->errStr, _GAGE_NON_ERR_STR); } ctx->errNum = gageErrStackSearch; return 1; } } si = AIR_AFFINE(ctx->stackPos[sidx], ss, ctx->stackPos[sidx+1], sidx, sidx+1); if (ctx->verbose > 3) { fprintf(stderr, "%s: si = affine(%g, %g, %g -> %u %u) = %g\n", me, ctx->stackPos[sidx], ss, ctx->stackPos[sidx+1], sidx, sidx+1, si); } } else { si = 0; } if (ctx->verbose > 3) { fprintf(stderr, "%s: wpos (%g,%g,%g) --> ipos (%g,%g,%g)\n", me, xx, yy, zz, xi, yi, zi); } } if (clamp) { if (nrrdCenterNode == ctx->shape->center) { xi = AIR_CLAMP(0, xi, size[0]-1); yi = AIR_CLAMP(0, yi, size[1]-1); zi = AIR_CLAMP(0, zi, size[2]-1); } else { xi = AIR_CLAMP(-0.5, xi, size[0]-0.5); yi = AIR_CLAMP(-0.5, yi, size[1]-0.5); zi = AIR_CLAMP(-0.5, zi, size[2]-0.5); } if (ctx->parm.stackUse) { si = AIR_CLAMP(0, si, ctx->pvlNum-2); } if (ctx->verbose > 3) { fprintf(stderr, "%s: with clamping --> ipos (%g,%g,%g)\n", me, xi, yi, zi); } } return _gageProbe(ctx, xi, yi, zi, si); } int gageProbeSpace(gageContext *ctx, double xx, double yy, double zz, int indexSpace, int clamp) { return _gageProbeSpace(ctx, xx, yy, zz, AIR_NAN, indexSpace, clamp); } teem-1.11.0~svn6057/src/gage/gage.h0000664000175000017500000016706312202223212016354 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GAGE_HAS_BEEN_INCLUDED #define GAGE_HAS_BEEN_INCLUDED #include #include #include #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(gage_EXPORTS) || defined(teem_EXPORTS) # define GAGE_EXPORT extern __declspec(dllexport) # else # define GAGE_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define GAGE_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define GAGE gageBiffKey /* ** GAGE_DERIV_MAX is the maximum derivative that gage knows how to ** work with. The 0th derivative is the reonstructed value (no ** derivative). Many short arrays are allocated to 1 + this value ** ** MUST KEEP IN SYNC with GAGE_DV_* macros below */ #define GAGE_DERIV_MAX 2 /* ** Convenience macros for arrays declared like blah[GAGE_DERIV_MAX+1] ** (so these need to be kept in sync with GAGE_DERIV_MAX above). Note ** that "DV" is short for "derivative vector". With consistent use of ** these, changes to GAGE_DERIV_MAX will either obviously break code ** (in an easy to fix way) or there will be no change needed at all. ** */ #define GAGE_DV_SET(v, d0, d1, d2) \ ((v)[0] = (d0), \ (v)[1] = (d1), \ (v)[2] = (d2)) #define GAGE_DV_EQUAL(a, b) \ ((a)[0] == (b)[0] && \ (a)[1] == (b)[1] && \ (a)[2] == (b)[2]) #define GAGE_DV_COPY(a, b) \ ((a)[0] = (b)[0], \ (a)[1] = (b)[1], \ (a)[2] = (b)[2]) /* ** the only extent to which gage treats different axes differently is ** the spacing between samples along the axis. To have different ** filters for the same function, but along different axes, would be ** too messy. [Sun Mar 9 13:32:22 EDT 2008: Actually, doing per-axis ** kernels is looking more and more sensible all the time. . .] Thus, ** gage is not very useful as the engine for downsampling: it can't ** tell that along one axis samples should be blurred while they ** should be interpolated along another. Rather, it assumes that the ** main task of probing is *reconstruction*: of values, of ** derivatives, or lots of different quantities */ /* ** terminology: gage is intended to measure multiple things at one ** point in a volume. The SET of ALL the things that you want ** gage to measure is called the "QUERY". Each of the many quantities ** comprising the query are called "ITEM"s. */ /* ******** gageParm.. enum ** ** these are passed to gageSet. Look for like-wise named field of ** gageParm for documentation on what these mean. ** ** The following things have to agree: ** - gageParm* enum ** - fields of gageParm struct ** - analogous gageDef* defaults (their declaration and setting) ** - action of gageParmSet (ctx.c) ** - action of gageParmReset (miscGage.c) */ enum { gageParmUnknown, gageParmVerbose, /* non-boolean int */ gageParmRenormalize, /* int */ gageParmCheckIntegrals, /* int */ gageParmK3Pack, /* int */ gageParmGradMagCurvMin, /* double */ gageParmCurvNormalSide, /* int */ gageParmKernelIntegralNearZero, /* double */ gageParmDefaultCenter, /* int */ gageParmStackUse, /* int */ gageParmStackNormalizeDeriv, /* int; does NOT imply gageParmStackUse */ gageParmStackNormalizeDerivBias, /* double; does NOT imply " */ gageParmStackNormalizeRecon, /* int; does NOT imply " */ gageParmOrientationFromSpacing, /* int */ gageParmGenerateErrStr, /* int */ gageParmTwoDimZeroZ, /* int */ gageParmLast }; /* keep in synch with gageErr airEnum */ enum { gageErrUnknown, /* 0: nobody knows */ gageErrNone, /* 1: no error, actually, all's well */ gageErrBoundsSpace, /* 2: out of 3-D (index-space) bounds */ gageErrBoundsStack, /* 3: out of 1-D bounds of stack */ gageErrStackIntegral, /* 4: stack recon coeff's sum to 0, so can't normalize them */ gageErrStackSearch, /* 5: for some reason couldn't find the index position of the probed stack location */ gageErrStackUnused, /* 6: can't probe stack without parm.stackUse */ gageErrLast }; #define GAGE_ERR_MAX 6 /* ******** gage{Ctx,Pvl}Flag.. enum ** ** organizes all the dependendies within a context and between a ** context and pervolumes. This logic is to determine the support ** required for a query: different queries need different kernels, ** different kernels have different supports. The user should not ** have to be concerned about any of this; it should be useful only ** to gageUpdate(). */ enum { gageCtxFlagUnknown, gageCtxFlagNeedD, /* 1: derivatives required for query */ gageCtxFlagK3Pack, /* 2: whether to use 3 or 6 kernels */ gageCtxFlagNeedK, /* 3: which of the kernels will actually be used */ gageCtxFlagKernel, /* 4: any one of the kernels or its parameters */ gageCtxFlagRadius, /* 5: radius of support for kernels with query */ gageCtxFlagShape, /* 6: a new pervolume shape was set */ gageCtxFlagLast }; #define GAGE_CTX_FLAG_MAX 6 enum { gagePvlFlagUnknown, gagePvlFlagVolume, /* 1: got a new volume */ gagePvlFlagQuery, /* 2: what do you really care about */ gagePvlFlagNeedD, /* 3: derivatives required for query */ gagePvlFlagLast }; #define GAGE_PVL_FLAG_MAX 3 /* ******** gageKernel.. enum ** ** these are the different kernels that might be used in gage, regardless ** of what kind of volume is being probed. ** (synch with miscGage.c:gageKernel airEnum) */ enum { gageKernelUnknown, /* 0: nobody knows */ gageKernel00, /* 1: measuring values */ gageKernel10, /* 2: reconstructing 1st derivatives */ gageKernel11, /* 3: measuring 1st derivatives */ gageKernel20, /* 4: reconstructing 1st partials and 2nd deriv.s */ gageKernel21, /* 5: measuring 1st partials for a 2nd derivative */ gageKernel22, /* 6: measuring 2nd derivatives */ gageKernelStack, /* 7: for reconstructing across a stack */ gageKernelLast }; #define GAGE_KERNEL_MAX 7 /* ******** GAGE_ITEM_PREREQ_MAXNUM ** ** Max number of prerequisites for any item in *any* kind. ** ** This value has gotten bumped periodically, which used to mean ** that *all* item tables had to be updated, when "-1" was used ** represent the unknown item. But now that 0 represents the ** unknown item, and because struct elements are implicitly ** initialized to zero, this is no longer the case. ** ** Wed Nov 8 14:12:44 EST 2006 pre-emptively upping this from 6 */ #define GAGE_ITEM_PREREQ_MAXNUM 8 /* ******** gageItemEntry struct ** ** necessary information about each item supported by the kind, which ** is defined at compile-time in a per-kind table (at least it is for ** the scalar, vector, and tensor kinds) ** ** NOTE!!! YOU CAN NOT re-arrange these variables, because of all the ** compile-time definitions that are done to define the gageKind->table ** for all current kinds. */ typedef struct { int enumVal; /* the item's enum value */ unsigned int answerLength; /* how many double's are needed to store the answer, or (for non-zero items), 0 to represent "the length will be learned later at runtime" */ int needDeriv, /* what kind of derivative info is immediately needed for this item (not recursively expanded). This is NO LONGER a bitvector: values are 0, 1, 2, .. */ prereq[GAGE_ITEM_PREREQ_MAXNUM], /* what are the other items this item depends on (not recusively expanded), you can list up to GAGE_ITEM_PREREQ_MAXNUM of them, and use 0 (the unknown item) to fill out the list. _OR_ -1 if this is a dynamic kind and the prereqs will not be known until later in runtime */ parentItem, /* the enum value of an item (answerLength > 1) containing the smaller value for which we are merely an alias _OR_ 0 if there's no parent */ parentIndex, /* where our value starts in parents value _OR_ 0 if there's no parent */ needData; /* whether non-NULL gagePerVolume->data is needed for answering this item */ } gageItemEntry; /* ** modifying the enums below (scalar, vector, etc query quantities) ** necesitates modifying: ** - the central item table in scl.c, vecGage.c ** - the associated arrays in scl.c, vecGage.c ** - the "answer" method itself in sclanswer.c, vecGage.c */ /* ******** gageScl* enum ** ** all the "items" that gage can measure in a scalar volume. ** ** NOTE: although it is currently listed that way, it is not necessary ** that prerequisite measurements are listed before the other measurements ** which need them (that is represented by _gageSclPrereq) ** ** The description for each enum value starts with the numerical value ** followed by a string which identifies the value in the gageScl airEnum. ** The "[N]" indicates how many doubles are used for storing the quantity. */ enum { gageSclUnknown, /* 0: nobody knows */ gageSclValue, /* 1: "v", data value: [1] */ gageSclGradVec, /* 2: "grad", gradient vector, un-normalized: [3] */ gageSclGradMag, /* 3: "gm", gradient magnitude: [1] */ gageSclNormal, /* 4: "n", gradient vector, normalized: [3] */ gageSclNProj, /* 5: "nproj", projection onto normal: [9] */ gageSclNPerp, /* 6: "np", projection onto tangent plane: [9] */ gageSclHessian, /* 7: "h", Hessian: [9] (column-order) */ gageSclHessianTen, /* 8: "ht", Hessian as 7-component tensor: [7] In principle this is a dependency inversion since gage doesn't know about the ten library, where the 7-element tensor is based. */ gageSclLaplacian, /* 9: "l", Laplacian: Dxx + Dyy + Dzz: [1] */ gageSclHessFrob, /* 10: "hf", Frobenius norm of Hessian: [1] */ gageSclHessEval, /* 11: "heval", Hessian's eigenvalues: [3] */ gageSclHessEval0, /* 12: "heval0", Hessian's 1st eigenvalue: [1] */ gageSclHessEval1, /* 13: "heval1", Hessian's 2nd eigenvalue: [1] */ gageSclHessEval2, /* 14: "heval2", Hessian's 3rd eigenvalue: [1] */ gageSclHessEvec, /* 15: "hevec", Hessian's eigenvectors: [9] */ gageSclHessEvec0, /* 16: "hevec0", Hessian's 1st eigenvector: [3] */ gageSclHessEvec1, /* 17: "hevec1", Hessian's 2nd eigenvector: [3] */ gageSclHessEvec2, /* 18: "hevec2", Hessian's 3rd eigenvector: [3] */ gageScl2ndDD, /* 19: "2d", 2nd dir.deriv. along gradient: [1] */ gageSclGeomTens, /* 20: "gten", sym. matx w/ evals {0, K1, K2} and evecs {grad, cdir0, cdir1}: [9] */ gageSclGeomTensTen, /* 21: "gtenten", 7-element geometry tensor [7] */ gageSclK1, /* 22: "k1", 1st principle curvature: [1] */ gageSclK2, /* 23: "k2", 2nd principle curvature (k2 <= k1): [1] */ gageSclTotalCurv, /* 24: "tc", L2 norm(K1,K2) (not Koen.'s "C"): [1] */ gageSclShapeTrace, /* 25, "st", (K1+K2)/Curvedness: [1] */ gageSclShapeIndex, /* 26: "si", Koen.'s shape index, ("S"): [1] */ gageSclMeanCurv, /* 27: "mc", mean curvature (K1 + K2)/2: [1] */ gageSclGaussCurv, /* 28: "gc", gaussian curvature K1*K2: [1] */ gageSclCurvDir1, /* 29: "cdir1", 1st principle curv direction: [3] */ gageSclCurvDir2, /* 30: "cdir2", 2nd principle curv direction: [3] */ gageSclFlowlineCurv, /* 31: "fc", curvature of normal streamline: [1] */ gageSclMedian, /* 32: "med", median filter */ gageSclHessValleyness, /* 33: "hvalley", vallyness measure: [1] */ gageSclHessRidgeness, /* 34: "hridge", ridgeness measure: [1] */ gageSclHessDotPeakness, /* 35: "hdpeak", positive blobness measure; based on simple dot-product w/ evals : [1] */ gageSclHessMode, /* 36: "hmode", Hessian's mode: [1] */ gageSclLast }; #define GAGE_SCL_ITEM_MAX 36 /* ******** gageVec* enum ** ** all the "items" that gage knows how to measure in a 3-vector volume ** ** The strings gives one of the gageVec airEnum identifiers, and [x] ** says how many scalars are associated with this value. */ enum { gageVecUnknown, /* 0: nobody knows */ gageVecVector, /* 1: "v", component-wise-interpolated (CWI) vec: [3] */ gageVecVector0, /* 2: "v0", vector[0]: [1] */ gageVecVector1, /* 3: "v1", vector[0]: [1] */ gageVecVector2, /* 4: "v2", vector[0]: [1] */ gageVecLength, /* 5: "l", length of CWI vector: [1] */ gageVecNormalized, /* 6: "n", normalized CWI vector: [3] */ gageVecJacobian, /* 7: "j", component-wise Jacobian: [9] 0:dv_x/dx 1:dv_x/dy 2:dv_x/dz 3:dv_y/dx 4:dv_y/dy 5:dv_y/dz 6:dv_z/dx 7:dv_z/dy 8:dv_z/dz */ gageVecStrain, /* 8: "S", rate-of-strain tensor: [9] */ gageVecDivergence, /* 9: "d", divergence (based on Jacobian): [1] */ gageVecCurl, /* 10: "c", curl (based on Jacobian): [3] */ gageVecCurlNorm, /* 11: "cm", curl magnitude: [1] */ gageVecHelicity, /* 12: "h", helicity: vec . curl: [1] */ gageVecNormHelicity, /* 13: "nh", normalized helicity: [1] */ gageVecSOmega, /* 14: "somega", S squared + Omega squared: [9] */ gageVecLambda2, /* 15: "lambda2", lambda2 criterion: [1] */ gageVecImaginaryPart, /* 16: "imag", imag. part of jacobian's complex-conjugate eigenvalues: [1] */ gageVecHessian, /* 17: "vh", second-order derivative: [27] HEY: indices here need to be double checked 0:d2v_x/dxdx 1:d2v_x/dxdy 2:d2v_x/dxdz 3:d2v_x/dydx 4:d2v_x/dydy 5:d2v_x/dydz 6:d2v_x/dzdx 7:d2v_x/dzdy 8:d2v_x/dzdz 9:d2v_y/dxdx [..] [..] 24:dv2_z/dzdx 25:d2v_z/dzdy 26:d2v_z/dzdz */ gageVecDivGradient, /* 18: "dg", divergence gradient: [3] */ gageVecCurlGradient, /* 19: "cg", curl gradient: [9] */ gageVecCurlNormGrad, /* 20: "cng", curl norm gradient: [3] */ gageVecNCurlNormGrad, /* 21: "ncng", normalized curl norm gradient: [3] */ gageVecHelGradient, /* 22: "hg", helicity gradient: [3] */ gageVecDirHelDeriv, /* 23: "dhd", directional derivative of helicity: [1] */ gageVecProjHelGradient, /* 24: "phg", projected helicity gradient: [3] */ gageVecGradient0, /* 25: "g0", gradient of 1st coeff of vector: [3] */ gageVecGradient1, /* 26: "g1", gradient of 2nd coeff of vector: [3] */ gageVecGradient2, /* 27: "g2", gradient of 3rd coeff of vector: [3] */ gageVecMultiGrad, /* 28: "mg", sum of outer products of grads: [9] */ gageVecMGFrob, /* 29: "mgfrob", frob norm of multi-gradient: [1] */ gageVecMGEval, /* 30: "mgeval", evals of multi-gradient: [3] */ gageVecMGEvec, /* 31: "mgevec", evecs of multi-gradient: [9] */ gageVecLast }; #define GAGE_VEC_ITEM_MAX 31 struct gageKind_t; /* dumb forward declaraction, ignore */ struct gagePerVolume_t; /* dumb forward declaraction, ignore */ /* ******** gageItemPackPart* enum ** ** the different ways that some items can be related for a gagePack */ enum { gageItemPackPartUnknown, /* 0 */ gageItemPackPartScalar, /* 1 */ gageItemPackPartGradVec, /* 2 */ gageItemPackPartGradMag, /* 3 */ gageItemPackPartNormal, /* 4 */ gageItemPackPartHessian, /* 5 */ gageItemPackPartHessEval0, /* 6 */ gageItemPackPartHessEval1, /* 7 */ gageItemPackPartHessEval2, /* 8 */ gageItemPackPartHessEvec0, /* 9 */ gageItemPackPartHessEvec1, /* 10 */ gageItemPackPartHessEvec2, /* 11 */ gageItemPackPartLast }; #define GAGE_ITEM_PACK_PART_MAX 11 /* ******** gageShape struct ** ** just a container for all the information related to the "shape" ** of all the volumes associated with a context ** ** Note that the utility of gageShape has extended well beyond doing ** convolution-based measurements in volumes- it has become the ** one-stop place for all of Teem to figure out a reasonable way of ** locating a logically a volume in 3-D space, including using a ** nrrd's full orientation information if it is known. */ typedef struct gageShape_t { /* ========= INPUT ========= (controls for _gageShapeSet) */ int defaultCenter, /* default centering to use when given volume has no centering set. */ orientationFromSpacing; /* only meaningful if nrrd has per-axis spacing, but not full orientation info. If zero, the volume is crammed into the bi-unit cube. If non-zero, gage treats the volume as if it had axis-aligned spaceDirection vectors, with the non-zero values determined by the given per-axis spacing. */ /* ======== OUTPUT ========= (set by _gageShapeSet) */ int center, /* the sample centering of the volume(s)- this determines the extent of the locations that may be probed */ fromOrientation; /* non-zero iff the spaceDirections and spaceOrigin information was used */ unsigned int size[3]; /* raster dimensions of volume HEY: shouldn't this be size_t? */ double spacing[3]; /* spacings for each axis */ /* fwScale[GAGE_KERNEL_MAX+1][3] has been superceded by the cleaner and more general ItoWSubInvTransp and ItoWSubInv matrices below */ double ItoW[16], /* homogeneous coord transform from index to world space (defined either by bi-unit cube or from full orientation info ) */ WtoI[16], /* inverse of above */ ItoWSubInvTransp[9], /* inverse transpose of 3x3 sub-matrix of ItoW, to transform (covariant) gradients */ ItoWSubInv[9]; /* tranpose of above, to transform hessians */ } gageShape; /* ******** gageParm struct ** ** a container for the various switches and knobs which control ** gage, aside from the obvious inputs (kernels, queries, volumes) */ typedef struct gageParm_t { int renormalize; /* hack to make sure that sum of discrete value reconstruction weights is same as kernel's continuous integral, and that the 1nd and 2nd deriv weights really sum to 0.0 */ int checkIntegrals; /* call the "integral" method of the kernel to verify that it is appropriate for the task for which the kernel is being set: reconstruction: 1.0, derivatives: 0.0 */ int k3pack; /* non-zero (true) iff we do not use kernels gageKernelIJ with I != J. So, we use the value reconstruction kernel (gageKernel00) for 1st and 2nd derivative reconstruction, and so on. This is faster because we can re-use results from low-order convolutions. The name "3pack" will likely persist even with 3rd dervatives, for which "4pack" would make more sense (for 00, 11, 22, 33) */ double gradMagCurvMin, /* punt on computing curvature information if gradient magnitude is less than this. Yes, this is scalar-kind-specific, but there's no other good place for it */ kernelIntegralNearZero, /* tolerance with checkIntegrals on derivative kernels */ stackNormalizeDerivBias; /* when using stackNormalizeDeriv, a bias on the effective scale, used for the normalization */ int curvNormalSide, /* determines direction of gradient that is used as normal in curvature calculations, exactly the same as miteUser's normalSide: 1 for normal pointing to lower values (higher values are more "inside"); -1 for normal pointing to higher values (low values more "inside") */ defaultCenter, /* what centering to use when you have to invent one, because its not set on any axis */ stackUse, /* if non-zero: treat the pvl's (all same kind) as multiple values of a single logical volume (e.g. for approximating scale space). The first pvl is effectively just a buffer; the N-1 pvls used are at index 1 through N-2. The query in pvl[0] will determine the computations done, and answers for the whole stack will be stored in pvl[0]. */ stackNormalizeRecon, /* if non-zero (and if stackUse is non-zero): the weights used to reconstruct across stack samples are normalized to unit sum; not needed if the kernel is accurate enough */ stackNormalizeDeriv, /* if non-zero (and if stackUse is non-zero): derivatives at filter stage (before answer stage) are renormalized based on the stack position */ orientationFromSpacing, /* only meaningful if nrrd has per-axis spacing, but not full orientation info. If zero, the volume is crammed into the bi-unit cube. If non-zero, gage treats the volume as if it had axis-aligned spaceDirection vectors, with the non-zero values determined by the given per-axis spacing. */ generateErrStr, /* when errors happen, biff is never used, but a descriptive error is sprintf into gctx->errStr as long as this is non-zero. */ twoDimZeroZ; /* a limited way of supporting queries on two-dimensional images: if this is non-zero then *some* answers will only involve the 1st ("X") and 2nd ("Y") coordinates of world space. Eigensystems should have only two two elements, with the 3rd being NaN'd out. Because gage has always been only about 3D images; the implementation of this is likely incomplete, since the responsibility for correctly handling it ultimately falls to the "answer" functions of the various gageKinds */ } gageParm; /* ******** gagePoint struct ** ** stores *Index Space* location of last query location, which is used ** to determine whether the ctx->fsl, ctx->fw values can be re-used ** (based on the "Frac" values), and, whether all the pvl->iv3 have to ** be refilled (based on the "Idx" values). The last index (frac[3] ** and idx[3])is for the stack, and can safely stay 0 if the stack ** isn't being used. ** ** with stack usage, stackFwNonZeroNum records how many pvls had ** non-zero stack filter weights, which is used to detect when ** iv3s have to be refilled. Looking at idx[3] alone is not always ** sufficient for this. */ typedef struct gagePoint_t { double frac[4]; /* last fractional voxel location */ unsigned int idx[4], /* last integral voxel location; actually the *upper* corner of the containing voxel */ stackFwNonZeroNum; /* last number of non-zero values of stack filter weights (ctx->stackFw) */ } gagePoint; /* ******** gageQuery typedef ** ** this short, fixed-length array is used as a bit-vector to store ** all the items that comprise a query. Its length sets an upper ** bound on the maximum item value that a gageKind may use. ** ** The important thing to keep in mind is that a variable of type ** gageKind ends up being passed by reference, even though the ** syntax of its usage doesn't immediately announce that. ** ** gageKindCheck currently has the role of verifying that a gageKind's ** maximum item value is within the bounds set here. Using ** GAGE_QUERY_BYTES_NUM == 8 gives a max item value of 63, which is ** far above anything being used now. ** ** Sat Jan 21 18:12:01 EST 2006: ha! second derivatives of tensors blew ** past old GAGE_QUERY_BYTES_NUM, now GAGE_QUERY_BYTES_NUM == 16 ** ** Tue Nov 7 19:51:05 EST 2006; tenGage items now pushing 127, ** guardedly changing GAGE_QUERY_BYTES_NUM to 24 --> max item 191 ** ** Wed Nov 18 17:53:23 CST 2009; yikes, tenGage items now at 190, ** changing GAGE_QUERY_BYTES_NUM to 32 --> max item 255 ** ** NOTE: increasing GAGE_QUERY_BYTES_NUM means that the macros below ** have to be redefined as well! */ #define GAGE_QUERY_BYTES_NUM 32 #define GAGE_ITEM_MAX ((8*GAGE_QUERY_BYTES_NUM)-1) typedef unsigned char gageQuery[GAGE_QUERY_BYTES_NUM]; /* ******** GAGE_QUERY_RESET, GAGE_QUERY_TEST ******** GAGE_QUERY_ON, GAGE_QUERY_OFF ** ** Macros for manipulating a gageQuery. ** ** airSanity ensures that an unsigned char is in fact 8 bits */ #define GAGE_QUERY_RESET(q) \ q[ 0] = q[ 1] = q[ 2] = q[ 3] = \ q[ 4] = q[ 5] = q[ 6] = q[ 7] = \ q[ 8] = q[ 9] = q[10] = q[11] = \ q[12] = q[13] = q[14] = q[15] = \ q[16] = q[17] = q[18] = q[19] = \ q[20] = q[21] = q[22] = q[23] = \ q[24] = q[25] = q[26] = q[27] = \ q[28] = q[29] = q[30] = q[31] = 0 #define GAGE_QUERY_COPY(p, q) \ p[ 0] = q[ 0]; p[ 1] = q[ 1]; p[ 2] = q[ 2]; p[ 3] = q[ 3]; \ p[ 4] = q[ 4]; p[ 5] = q[ 5]; p[ 6] = q[ 6]; p[ 7] = q[ 7]; \ p[ 8] = q[ 8]; p[ 9] = q[ 9]; p[10] = q[10]; p[11] = q[11]; \ p[12] = q[12]; p[13] = q[13]; p[14] = q[14]; p[15] = q[15]; \ p[16] = q[16]; p[17] = q[17]; p[18] = q[18]; p[19] = q[19]; \ p[20] = q[20]; p[21] = q[21]; p[22] = q[22]; p[23] = q[23]; \ p[24] = q[24]; p[25] = q[25]; p[26] = q[26]; p[27] = q[27]; \ p[28] = q[28]; p[29] = q[29]; p[30] = q[30]; p[31] = q[31] #define GAGE_QUERY_ADD(p, q) \ p[ 0] |= q[ 0]; p[ 1] |= q[ 1]; p[ 2] |= q[ 2]; p[ 3] |= q[ 3]; \ p[ 4] |= q[ 4]; p[ 5] |= q[ 5]; p[ 6] |= q[ 6]; p[ 7] |= q[ 7]; \ p[ 8] |= q[ 8]; p[ 9] |= q[ 9]; p[10] |= q[10]; p[11] |= q[11]; \ p[12] |= q[12]; p[13] |= q[13]; p[14] |= q[14]; p[15] |= q[15]; \ p[16] |= q[16]; p[17] |= q[17]; p[18] |= q[18]; p[19] |= q[19]; \ p[20] |= q[20]; p[21] |= q[21]; p[22] |= q[22]; p[23] |= q[23]; \ p[24] |= q[24]; p[25] |= q[25]; p[26] |= q[26]; p[27] |= q[27]; \ p[28] |= q[28]; p[29] |= q[29]; p[30] |= q[30]; p[31] |= q[31] #define GAGE_QUERY_EQUAL(p, q) ( \ p[ 0] == q[ 0] && p[ 1] == q[ 1] && p[ 2] == q[ 2] && p[ 3] == q[ 3] && \ p[ 4] == q[ 4] && p[ 5] == q[ 5] && p[ 6] == q[ 6] && p[ 7] == q[ 7] && \ p[ 8] == q[ 8] && p[ 9] == q[ 9] && p[10] == q[10] && p[11] == q[11] && \ p[12] == q[12] && p[13] == q[13] && p[14] == q[14] && p[15] == q[15] && \ p[16] == q[16] && p[17] == q[17] && p[18] == q[18] && p[19] == q[19] && \ p[20] == q[20] && p[21] == q[21] && p[22] == q[22] && p[23] == q[23] && \ p[24] == q[24] && p[25] == q[25] && p[26] == q[26] && p[27] == q[27] && \ p[28] == q[28] && p[29] == q[29] && p[30] == q[30] && p[31] == q[31] ) #define GAGE_QUERY_NONZERO(q) ( \ q[ 0] | q[ 1] | q[ 2] | q[ 3] | \ q[ 4] | q[ 5] | q[ 6] | q[ 7] | \ q[ 8] | q[ 9] | q[10] | q[11] | \ q[12] | q[13] | q[14] | q[15] | \ q[16] | q[17] | q[18] | q[19] | \ q[20] | q[21] | q[22] | q[23] | \ q[24] | q[25] | q[26] | q[27] | \ q[28] | q[29] | q[30] | q[31] ) #define GAGE_QUERY_ITEM_TEST(q, i) (q[i/8] & (1 << (i % 8))) #define GAGE_QUERY_ITEM_ON(q, i) (q[i/8] |= (1 << (i % 8))) #define GAGE_QUERY_ITEM_OFF(q, i) (q[i/8] &= ~(1 << (i % 8))) /* increment for ctx->pvlArr airArray */ #define GAGE_PERVOLUME_ARR_INCR 32 /* extents of known information about optimal sigma samples for Hermite-spline-based scale-space reconstruction */ #define GAGE_OPTIMSIG_SIGMA_MAX 11 #define GAGE_OPTIMSIG_SAMPLES_MAXNUM 11 /* ******** gageContext struct ** ** The information here is specific to the dimensions, scalings, and ** radius of kernel support, but not to kind of volume (all kind-specific ** information is in the gagePerVolume). One context can be used in ** conjuction with probing multiple volumes. */ typedef struct gageContext_t { /* INPUT ------------------------- */ int verbose; /* verbosity */ gageParm parm; /* all parameters */ /* all the kernels we'll ever need, including the stack kernel */ NrrdKernelSpec *ksp[GAGE_KERNEL_MAX+1]; /* all the pervolumes attached to this context. If using stack, the base pvl is the LAST, pvl[pvlNum-1], and the stack samples are pvl[0] through pvl[pvlNum-2] */ struct gagePerVolume_t **pvl; /* number of pervolumes currently attached. If using stack, this is one more than number of stack samples (because of the base volume at the end) */ unsigned int pvlNum; /* airArray for managing pvl and pvlNum */ airArray *pvlArr; /* sizes, spacings, centering, and other geometric aspects of the volume */ gageShape *shape; /* if stack is being used, allocated for length pvlNum-1, and stackPos[0] through stackPos[pvlNum-2] MUST exist and be monotonically increasing stack positions for each volume. Otherwise NULL */ double *stackPos; /* INTERNAL ------------------------- */ /* if using stack: allocated for length pvlNum-1, and filter sample locations and weights for reconstruction along the stack. Otherwise NULL. */ double *stackFsl; double *stackFw; /* all the flags used by gageUpdate() used to describe what changed in this context */ int flag[GAGE_CTX_FLAG_MAX+1]; /* which value/derivatives need to be calculated for all pervolumes (doV, doD1, doD2) */ int needD[GAGE_DERIV_MAX+1]; /* which kernels are needed for all pvls. needK[gageKernelStack] is currently not set by the update function that sets needK[] */ int needK[GAGE_KERNEL_MAX+1]; /* radius of support of samples needed to satisfy query, given the set of kernels. The "filter diameter" fd == 2*radius. This is incremented by one when filtering across the stack with nrrdKernelHermiteScaleSpaceFlag */ unsigned int radius; /* filter sample locations (all axes): logically a fd x 3 array (and its 3 because gage works on 3D fields, not because of the number of derivatives supported) */ double *fsl; /* filter weights (all axes, all kernels): logically a fd x 3 x GAGE_KERNEL_MAX+1 (fast-to-slow) array */ double *fw; /* offsets to other fd^3 samples needed to fill 3D intermediate value cache. Allocated size is dependent on kernels, values inside are dependent on the dimensions of the volume. It may be more correct to be using size_t instead of uint, but the X and Y dimensions of the volume would have to be super-outrageous for that to be a problem */ unsigned int *off; /* last probe location */ gagePoint point; /* errStr and errNum are for describing errors that happen in gageProbe(): using biff is too heavy-weight for this, and the idea is that no ill should occur if the error is repeatedly ignored. Whether or not something is actually sprintf'ed into errStr is controlled by parm.generateErrStr. NOTE: these variables used to be globals "gageErrStr" and "gageErrNum" */ char errStr[AIR_STRLEN_LARGE]; int errNum; /* takes values from the gageErr enum */ /* what fraction of the values in the kernel support had to be invented (by bleeding out the margin) in order to satisfy a probe that was near the edge (any axis, either high or low) of the volume. However, this value is NOT meaningfully set if there is no clamping, and the probe location as fallen outside the volume */ double edgeFrac; } gageContext; /* ******** gagePerVolume ** ** information that is specific to one volume, and to one kind of ** volume. */ typedef struct gagePerVolume_t { int verbose; /* verbosity */ const struct gageKind_t *kind; /* what kind of volume is this for */ gageQuery query; /* the query, recursively expanded */ int needD[GAGE_DERIV_MAX+1];/* which derivatives need to be calculated for the query (above) in this volume */ const Nrrd *nin; /* the volume to sample within */ int flag[GAGE_PVL_FLAG_MAX+1];/* for the kind-specific flags .. */ double *iv3, *iv2, *iv1; /* 3D, 2D, 1D, value caches. These are cubical, square, and linear arrays, all length fd on each edge. Currently gageIv3Fill() fills the iv3 (at a latter point this will be delegated back to the gageKind to facilitate bricking), and currently the tuple axis (with length valLen) always slowest. However, use of iv2 and iv1 is entirely up the kind's filter method. */ double (*lup)(const void *ptr, size_t I); /* nrrd{F,D}Lookup[] element, according to nin->type and double */ double *answer; /* main buffer to hold all the answers */ double **directAnswer; /* array of pointers into answer */ void *data; /* extra data, parameters, buffers, etc. required for answering some items (as per the gageItemEntry->needData) managed with kind->pvlDataNew, kind->pvlDataCopy, kind->pvlDataUpdate, and kind->pvlDataNix, so there is no channel for extra info to be passed into the pvl->data, other that what was put into kind->data */ } gagePerVolume; /* ******** gageKind struct ** ** all the information and functions that are needed to handle one ** kind of volume (such as scalar, vector, etc.) ** ** these are either statically allocated (e.g. gageKindScl, gageKindVec, ** tenGageKind), or dynamically allocated, which case the kind itself ** needs a constructor (e.g. tenDwiGageKindNew()). The "dynamicAlloc" ** variable indicates this distinction. ** ** Having dynamically allocated kinds raises the possibility of having ** to set and update (or modify and update) their internal state, ** which is currently completely outside the update framework of gage. ** So as far as the core gage functions are concerned, all kinds are ** static, because there is nothing to modify. It also means that ** those who dynamically create kinds should try to minimize the ** state info that can/must be dynamically modified (i.e. maybe ** the kind constructor should take all the various parameters, ** and set everything in a single shot). */ typedef struct gageKind_t { int dynamicAlloc; /* non-zero if this kind struct was dynamically allocated */ char name[AIR_STRLEN_SMALL]; /* short identifying string for kind */ const airEnum *enm; /* such as gageScl. NB: the "unknown" value in the enum *must* be 0. */ unsigned int baseDim, /* dimension that x,y,z axes start on (e.g. 0 for scalars, 1 for vectors) */ valLen; /* number of scalars per data point, -or- 0 to represent "this value will be learned later at runtime" */ int itemMax; /* such as GAGE_SCL_ITEM_MAX */ gageItemEntry *table; /* array of gageItemEntry's, indexed by the item value, -or- NULL if the table cannot be statically allocated (not because it can come in different sizes, but because it needs to be a modified version of the compile-time table */ void (*iv3Print)(FILE *, /* such as _gageSclIv3Print() */ gageContext *, gagePerVolume *), (*filter)(gageContext *, /* such as _gageSclFilter() */ gagePerVolume *), (*answer)(gageContext *, /* such as _gageSclAnswer() */ gagePerVolume *), /* for allocating, copying, updating, and nixing the pervolume->data */ /* pvlDataNew and pvlDataCopy can use biff, but: --> they must use GAGE key (and not callback's library's key), and --> pvlDataNix can not use biff */ *(*pvlDataNew)(const struct gageKind_t *kind), *(*pvlDataCopy)(const struct gageKind_t *kind, const void *pvlDataOld), *(*pvlDataNix)(const struct gageKind_t *kind, void *pvlData); int (*pvlDataUpdate)(const struct gageKind_t *kind, const gageContext *ctx, const gagePerVolume *pvl, const void *data); void *data; /* extra information about the kind of volume that's being probed. Likely used by filter, answer, and the pvlData functions */ } gageKind; /* ******** gageItemSpec struct ** ** dumb little way to store a kind/item pair. Formerly known ** as a gageQuerySpec. */ typedef struct { const gageKind *kind; /* the kind of the volume to measure */ int item; /* the quantity to measure */ } gageItemSpec; /* ******** gageItemPack struct ** ** A way of coherently representing a related set of gageItems. This ** kind of inter-item relationship may eventually be part of the ** definition of a gageKind. ** ** These are intended to be set up at compile-time, just like nearly all ** the gageKinds. ** ** This is not to be confused with the gageMultiItem, which is (or ** will be) a list of items, set at run-time, to learn from a given ** volume. */ typedef struct { const gageKind *kind; int item[GAGE_ITEM_PACK_PART_MAX+1]; } gageItemPack; /* ******** gageStackBlurParm struct ** ** all parameters associated with blurring one volume to form a "stack" */ typedef struct { unsigned int num; /* # of blurring scales == # volumes */ double *scale; /* scale parameter for each blurred volume */ double sigmaMax, /* nrrdKernelDiscreteGaussian is implemented with airBesselInExpScaled, which has numerical issues for very large kernel sizes. Instead of doing the blurring in one step, the diffusion is done iteratively, with steps in diffusion time of sigmaMax^2 */ padValue; /* padding value for nrrdBoundaryPad */ NrrdKernelSpec *kspec; /* NOTE: parm[0] will get over-written as part of running the resampler at each scale */ int dataCheck, /* when checking given stack to see if its the blurring of a volume, actually go in and look at values of first (probably the least blurred) volume */ boundary, /* passed to nrrdResampleBoundarySet */ renormalize, /* passed to nrrdResampleRenormalizeSet */ oneDim, /* for experimental purposes: blur *only* along the first (fastest) axis */ verbose; } gageStackBlurParm; /* ******** gageOptimSigContext struct ** ** The parameters and state needed to optimize (via gageOptimSigCalculate) the ** locations for reconstructing scale-space, in the specific case of using ** nrrdKernelDiscreteGaussian for blurring, and using the ** nrrdKernelHermiteScaleSpaceFlag for reconstructing along scale. The ** "samples" are the pre-blurred correct blurrings along scale, of which there ** are usually about a dozen or fewer. The purpose of gageOptimSigCalculate ** is to find where to put those samples so that the error of reconstructing ** at any other scale is in some sense minimized. ** ** This code was re-written August 2013; one of the big changes is that there ** is no longer a need for a big 4-D array of pre-computed blurrings. */ typedef struct { /* INPUT ------------------------- */ /* these are set (once) at context creation time */ unsigned int dim, /* what dimension of point-spread function to optimize based on; either 1, 2, or 3 */ sampleNumMax, /* max number of samples to optimize */ trueImgNum; /* how many samples along scale to use for the correct reference blurrings; this discretization determines the accuracy of the error measurement integrated across scales. For allMeasr *other* than nrrdMeasureLinf, this determines the number of error values summarized with allMeasr. With the re-write of this code, setting this is not as consequential as it used to be; now it only controls the allocation of nerr. One could imagine another tweak that shifted truImgNum to the gageOptimSigCalculate or similar call */ double sigmaRange[2]; /* range of sigma values that should be studied */ double cutoff; /* second parm to nrrdKernelDiscreteGaussian */ /* NOTE: the image blurring to sample a particular scale is always by nrrdKernelDiscreteGaussian; this code is not built for other kinds blurring kernels, even though that would be interesting to pursue */ /* these are set with calls to gageOptimSigCalculate or gageOptimSigPlot */ NrrdKernelSpec *kssSpec; /* how to interpolate across scale; should be nrrdKernelHermiteScaleSpaceFlag for kspec->kernel; the main purpose of the sigma optimization that this code does */ unsigned int sampleNum, /* how many scale samples to optimize */ maxIter; /* allowed iterations in optimization */ int imgMeasr, /* how to measure error at each reconstructed scale (in the scale-interpolated image) */ allMeasr; /* how to summarize errors across all scales */ double convEps; /* convergence threshold */ /* INTERNAL ------------------------- */ /* NOTE: all internal computations are parameterized by a tau-like quantity termed rho, rather than sigma */ unsigned int sx, sy, sz; /* image size for testing; determined by dim, sigmaRange[1], and cutoff */ Nrrd *nerr, /* 1D array of all errors, across scale, with length trueImgNum */ *ninterp, /* last scale interpolation result */ *ndiff; /* diff between truth and the single recon last computed in ninterp */ double rhoRange[2], /* rho(sigmaRange) */ *kloc, /* allocated for length sx to evaluation locations of nrrdKernelDiscreteGaussian */ *kern, *ktmp1, *ktmp2, /* allocated for length sx to store a high-quality nrrdKernelDiscreteGaussian evaluation, or buffers related to that */ kone[1]; /* stores 1.0 */ gageContext *gctx; /* context around pvlBase, pvlSS, and nsampleImg */ /* buffers for kernel evaluation */ /* except for pvlBase, these are allocated for sampleNumMax */ gagePerVolume *pvlBase, /* the base pvl for getting answers */ **pvlSS; /* for gage; pvlSS is the stack pervolume, for the sampleNum volumes in nsampvol */ Nrrd **nsampleImg; /* current set of scale-interpolated samples */ double *sampleSigma, /* current locations of nsampleImg in sigma (this array is for the gageStack) */ *sampleRho, /* current locations of nsampleImg in rho */ *sampleTmp, /* buffer for sample location info */ *sampleErrMax, /* for tracking per-sample-pair errors in Linf */ *step; /* per-point stepsize for gradient descent */ /* OUTPUT ------------------------- */ double finalErr; /* error of converged points */ } gageOptimSigContext; /* defaultsGage.c */ GAGE_EXPORT const char *gageBiffKey; GAGE_EXPORT int gageDefVerbose; GAGE_EXPORT double gageDefGradMagCurvMin; GAGE_EXPORT int gageDefRenormalize; GAGE_EXPORT int gageDefCheckIntegrals; GAGE_EXPORT int gageDefK3Pack; GAGE_EXPORT int gageDefCurvNormalSide; GAGE_EXPORT double gageDefKernelIntegralNearZero; GAGE_EXPORT int gageDefDefaultCenter; GAGE_EXPORT int gageDefStackUse; GAGE_EXPORT int gageDefStackNormalizeRecon; GAGE_EXPORT int gageDefStackNormalizeDeriv; GAGE_EXPORT double gageDefStackNormalizeDerivBias; GAGE_EXPORT double gageDefStackBlurSigmaMax; GAGE_EXPORT int gageDefOrientationFromSpacing; GAGE_EXPORT int gageDefGenerateErrStr; GAGE_EXPORT int gageDefTwoDimZeroZ; /* miscGage.c */ GAGE_EXPORT const int gagePresent; GAGE_EXPORT double gageZeroNormal[3]; GAGE_EXPORT const airEnum *const gageErr; GAGE_EXPORT const airEnum *const gageKernel; GAGE_EXPORT const airEnum *const gageItemPackPart; GAGE_EXPORT void gageParmReset(gageParm *parm); GAGE_EXPORT void gagePointReset(gagePoint *point); GAGE_EXPORT gageItemSpec *gageItemSpecNew(void); GAGE_EXPORT void gageItemSpecInit(gageItemSpec *isp); GAGE_EXPORT gageItemSpec *gageItemSpecNix(gageItemSpec *isp); /* kind.c */ GAGE_EXPORT int gageKindCheck(const gageKind *kind); GAGE_EXPORT unsigned int gageKindTotalAnswerLength(const gageKind *kind); GAGE_EXPORT unsigned int gageKindAnswerLength(const gageKind *kind, int item); GAGE_EXPORT int gageKindAnswerOffset(const gageKind *kind, int item); GAGE_EXPORT int gageKindVolumeCheck(const gageKind *kind, const Nrrd *nrrd); /* print.c */ GAGE_EXPORT void gageQueryPrint(FILE *file, const gageKind *kind, gageQuery query); /* sclfilter.c */ typedef void (gageScl3PFilter_t)(gageShape *shape, double *iv3, double *iv2, double *iv1, double *fw00, double *fw11, double *fw22, double *val, double *gvec, double *hess, const int *needD); GAGE_EXPORT gageScl3PFilter_t gageScl3PFilter2; GAGE_EXPORT gageScl3PFilter_t gageScl3PFilter4; GAGE_EXPORT gageScl3PFilter_t gageScl3PFilter6; GAGE_EXPORT gageScl3PFilter_t gageScl3PFilter8; GAGE_EXPORT void gageScl3PFilterN(gageShape *shape, int fd, double *iv3, double *iv2, double *iv1, double *fw00, double *fw11, double *fw22, double *val, double *gvec, double *hess, const int *needD); /* scl.c */ GAGE_EXPORT const airEnum *const gageScl; /* HEY: this should perhaps be a "const gageKind", but pointers for general kinds may want to point to this, or to the return of a dynamic kind generator like tenDwiGageKindNew(), which is most certainly not "const gageKind". */ GAGE_EXPORT gageKind *const gageKindScl; GAGE_EXPORT const gageItemPack *const gageItemPackSclValue; /* vecGage.c (together with vecprint.c, these contain everything to implement the "vec" kind, and could be used as examples of what it takes to create a new gageKind) */ GAGE_EXPORT const airEnum *const gageVec; GAGE_EXPORT gageKind *const gageKindVec; /* shape.c */ GAGE_EXPORT void gageShapeReset(gageShape *shp); GAGE_EXPORT gageShape *gageShapeNew(void); GAGE_EXPORT gageShape *gageShapeCopy(const gageShape *shp); GAGE_EXPORT gageShape *gageShapeNix(gageShape *shape); GAGE_EXPORT int gageShapeSet(gageShape *shp, const Nrrd *nin, int baseDim); GAGE_EXPORT void gageShapeWtoI(const gageShape *shape, double index[3], const double world[3]); GAGE_EXPORT void gageShapeItoW(const gageShape *shape, double world[3], const double index[3]); GAGE_EXPORT int gageShapeEqual(const gageShape *shp1, const char *name1, const gageShape *shp2, const char *name2); GAGE_EXPORT void gageShapeBoundingBox(double min[3], double max[3], const gageShape *shape); /* the organization of the next two files used to be according to what the first argument is, not what appears in the function name, but that's just a complete mess now */ /* pvl.c */ GAGE_EXPORT int gageVolumeCheck(const gageContext *ctx, const Nrrd *nin, const gageKind *kind); GAGE_EXPORT gagePerVolume *gagePerVolumeNew(gageContext *ctx, const Nrrd *nin, const gageKind *kind); GAGE_EXPORT gagePerVolume *gagePerVolumeNix(gagePerVolume *pvl); GAGE_EXPORT const double *gageAnswerPointer(const gageContext *ctx, const gagePerVolume *pvl, int item); GAGE_EXPORT unsigned int gageAnswerLength(const gageContext *ctx, const gagePerVolume *pvl, int item); GAGE_EXPORT int gageQueryReset(gageContext *ctx, gagePerVolume *pvl); GAGE_EXPORT int gageQuerySet(gageContext *ctx, gagePerVolume *pvl, gageQuery query); GAGE_EXPORT int gageQueryAdd(gageContext *ctx, gagePerVolume *pvl, gageQuery query); GAGE_EXPORT int gageQueryItemOn(gageContext *ctx, gagePerVolume *pvl, int item); /* optimsig.c */ GAGE_EXPORT int gageOptimSigSet(double *scale, unsigned int num, unsigned int sigmaMax); GAGE_EXPORT gageOptimSigContext * gageOptimSigContextNew(unsigned int dim, unsigned int sampleNumMax, unsigned int trueImgNum, double sigmaMin, double sigmaMax, double cutoff); GAGE_EXPORT gageOptimSigContext *gageOptimSigContextNix(gageOptimSigContext *oscx); GAGE_EXPORT int gageOptimSigCalculate(gageOptimSigContext *oscx, /* output */ double *sigma, unsigned int sigmaNum, const NrrdKernelSpec *kssSpec, int imgMeasr, int allMeasr, unsigned int maxIter, double convEps); GAGE_EXPORT int gageOptimSigErrorPlot(gageOptimSigContext *oscx, Nrrd *nout, const double *sigma, unsigned int sigmaNum, const NrrdKernelSpec *kssSpec, int imgMeasr); GAGE_EXPORT int gageOptimSigErrorPlotSliding(gageOptimSigContext *oscx, Nrrd *nout, double windowRho, unsigned int sampleNum, const NrrdKernelSpec *kssSpec, int imgMeasr); /* stack.c */ GAGE_EXPORT double gageTauOfTee(double tee); GAGE_EXPORT double gageTeeOfTau(double tau); GAGE_EXPORT double gageSigOfTau(double tau); GAGE_EXPORT double gageTauOfSig(double sig); GAGE_EXPORT double gageStackWtoI(gageContext *ctx, double swrl, int *outside); GAGE_EXPORT double gageStackItoW(gageContext *ctx, double si, int *outside); GAGE_EXPORT int gageStackPerVolumeNew(gageContext *ctx, gagePerVolume **pvlStack, const Nrrd *const *nblur, unsigned int blnum, const gageKind *kind); GAGE_EXPORT int gageStackPerVolumeAttach(gageContext *ctx, gagePerVolume *pvlBase, gagePerVolume **pvlStack, const double *stackPos, unsigned int blnum); GAGE_EXPORT int gageStackProbe(gageContext *ctx, double xi, double yi, double zi, double si); GAGE_EXPORT int gageStackProbeSpace(gageContext *ctx, double x, double y, double z, double s, int indexSpace, int clamp); /* stackBlur.c */ GAGE_EXPORT gageStackBlurParm *gageStackBlurParmNew(void); GAGE_EXPORT gageStackBlurParm *gageStackBlurParmNix(gageStackBlurParm *sbp); GAGE_EXPORT int gageStackBlurParmScaleSet(gageStackBlurParm *sbp, unsigned int num, double scaleMin, double scaleMax, int uniform, int optim); GAGE_EXPORT int gageStackBlurParmKernelSet(gageStackBlurParm *sbp, const NrrdKernelSpec *kspec, int renormalize); GAGE_EXPORT int gageStackBlurParmSigmaMaxSet(gageStackBlurParm *sbp, double sigmaMax); GAGE_EXPORT int gageStackBlurParmBoundarySet(gageStackBlurParm *sbp, int boundary, double padValue); GAGE_EXPORT int gageStackBlurParmVerboseSet(gageStackBlurParm *sbp, int verbose); GAGE_EXPORT int gageStackBlurParmOneDimSet(gageStackBlurParm *sbp, int oneDim); GAGE_EXPORT int gageStackBlurParmCheck(gageStackBlurParm *sbp); GAGE_EXPORT int gageStackBlur(Nrrd *const nblur[], gageStackBlurParm *sbp, const Nrrd *nin, const gageKind *kind); GAGE_EXPORT int gageStackBlurCheck(const Nrrd *const nblur[], gageStackBlurParm *sbp, const Nrrd *nin, const gageKind *kind); GAGE_EXPORT int gageStackBlurGet(Nrrd *const nblur[], int *recomputedP, gageStackBlurParm *sbp, const char *format, const Nrrd *nin, const gageKind *kind); GAGE_EXPORT int gageStackBlurManage(Nrrd ***nblurP, int *recomputedP, gageStackBlurParm *sbp, const char *format, int saveIfComputed, NrrdEncoding *enc, const Nrrd *nin, const gageKind *kind); /* ctx.c */ GAGE_EXPORT gageContext *gageContextNew(void); GAGE_EXPORT gageContext *gageContextCopy(gageContext *ctx); GAGE_EXPORT gageContext *gageContextNix(gageContext *ctx); GAGE_EXPORT void gageParmSet(gageContext *ctx, int which, double val); GAGE_EXPORT int gagePerVolumeIsAttached(const gageContext *ctx, const gagePerVolume *pvl); GAGE_EXPORT int gagePerVolumeAttach(gageContext *ctx, gagePerVolume *pvl); GAGE_EXPORT int gagePerVolumeDetach(gageContext *ctx, gagePerVolume *pvl); GAGE_EXPORT int gageKernelSet(gageContext *ctx, int which, const NrrdKernel *k, const double *kparm); GAGE_EXPORT void gageKernelReset(gageContext *ctx); GAGE_EXPORT int gageProbe(gageContext *ctx, double xi, double yi, double zi); GAGE_EXPORT int gageProbeSpace(gageContext *ctx, double x, double y, double z, int indexSpace, int clamp); /* update.c */ GAGE_EXPORT int gageUpdate(gageContext *ctx); /* st.c */ GAGE_EXPORT int gageStructureTensor(Nrrd *nout, const Nrrd *nin, int dScale, int iScale, int dsmp); /* deconvolve.c */ GAGE_EXPORT int gageDeconvolve(Nrrd *nout, double *lastDiffP, const Nrrd *nin, const gageKind *kind, const NrrdKernelSpec *ksp, int typeOut, unsigned int maxIter, int saveAnyway, double step, double epsilon, int verbose); GAGE_EXPORT int gageDeconvolveSeparableKnown(const NrrdKernelSpec *ksp); GAGE_EXPORT int gageDeconvolveSeparable(Nrrd *nout, const Nrrd *nin, const gageKind *kind, const NrrdKernelSpec *ksp, int typeOut); #ifdef __cplusplus } #endif #endif /* GAGE_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/bin/0000775000175000017500000000000012203513754015146 5ustar domibeldomibelteem-1.11.0~svn6057/src/bin/airSanity.c0000664000175000017500000000362412165631065017265 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include int main(int argc, char *argv[]) { int aret; char *me; AIR_UNUSED(argc); me = argv[0]; aret = airSanity(); if (airInsane_not == aret) { char stmp[AIR_STRLEN_SMALL]; fprintf(stderr, "%s: air sanity check passed.\n", me); fprintf(stderr, "\n"); fprintf(stderr, "airMyEndian() == %d\n", airMyEndian()); fprintf(stderr, "AIR_QNANHIBIT == %d\n", AIR_QNANHIBIT); fprintf(stderr, "AIR_DIO == %d\n", AIR_DIO); fprintf(stderr, "sizeof(size_t) = %s\n", airSprintSize_t(stmp, sizeof(size_t))); fprintf(stderr, "sizeof(void*) = %s\n", airSprintSize_t(stmp, sizeof(void*))); return 0; } /* else */ fprintf(stderr, "%s: air sanity check FAILED:\n%s\n", me, airInsaneErr(aret)); return 1; } teem-1.11.0~svn6057/src/bin/pprobe.c0000664000175000017500000004452012165631065016611 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #define SPACING(spc) (AIR_EXISTS(spc) ? spc: nrrdDefaultSpacing) /* copied this from ten.h; I don't want gage to depend on ten */ #define PROBE_MAT2LIST(l, m) ( \ (l)[1] = (m)[0], \ (l)[2] = (m)[3], \ (l)[3] = (m)[6], \ (l)[4] = (m)[4], \ (l)[5] = (m)[7], \ (l)[6] = (m)[8] ) void printans(FILE *file, const double *ans, int len) { int a; AIR_UNUSED(file); for (a=0; a<=len-1; a++) { if (a) { printf(", "); } printf("%g", ans[a]); } } static const char *probeInfo = ("Uses gageProbe() to query scalar or vector volumes " "at a single probe location."); int main(int argc, const char *argv[]) { gageKind *kind; const char *me; char *whatS, *err, *outS, *stackSavePath; hestParm *hparm; hestOpt *hopt = NULL; NrrdKernelSpec *k00, *k11, *k22, *kSS, *kSSblur; float pos[3], lineInfo[4]; double gmc, rangeSS[2], posSS; unsigned int ansLen, numSS, ninSSIdx, lineStepNum; int what, E=0, renorm, SSnormd, SSuniform, verbose, orientationFromSpacing; const double *answer; Nrrd *nin, **ninSS=NULL, *nout=NULL; gageContext *ctx; gagePerVolume *pvl; limnPolyData *lpld=NULL; airArray *mop; int worldSpace; Nrrd *ngrad=NULL, *nbmat=NULL; double bval, eps; unsigned int *skip, skipNum; gageStackBlurParm *sbp; mop = airMopNew(); me = argv[0]; hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hparm->elideSingleOtherType = AIR_TRUE; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL, "\"kind\" of volume (\"scalar\", \"vector\", or \"tensor\")", NULL, NULL, meetHestGageKind); hestOptAdd(&hopt, "p", "x y z", airTypeFloat, 3, 3, pos, NULL, "the position in space at which to probe"); hestOptAdd(&hopt, "wsp", NULL, airTypeInt, 0, 0, &worldSpace, NULL, "if using this option, position (\"-p\") will be in world " "space, instead of index space (the default)"); hestOptAdd(&hopt, "pi", "lpld in", airTypeOther, 1, 1, &lpld, "", "input polydata (overrides \"-p\")", NULL, NULL, limnHestPolyDataLMPD); hestOptAdd(&hopt, "pl", "x y z s", airTypeFloat, 4, 4, lineInfo, "0 0 0 0", "probe along line, instead of at point. " "The \"-p\" three coords are the line start point. " "If \"s\" is zero, (x,y,z) is the line end point. " "If \"s\" is non-zero, (x,y,z) is the line direction, " "which is scaled to have length \"s\", " "and then used as the step between line samples. "); hestOptAdd(&hopt, "pln", "num", airTypeUInt, 1, 1, &lineStepNum, "0", "if non-zero, number of steps of probing to do along line, " "which overrides \"-p\" and \"-pi\""); hestOptAdd(&hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1", "verbosity level"); hestOptAdd(&hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL, "the quantity (scalar, vector, or matrix) to learn by probing"); hestOptAdd(&hopt, "eps", "epsilon", airTypeDouble, 1, 1, &eps, "0", "if non-zero, and if query is a scalar, epsilon around probe " "location where we will do discrete differences to find the " "gradient and hessian (for debugging)"); hestOptAdd(&hopt, "k00", "kern00", airTypeOther, 1, 1, &k00, "tent", "kernel for gageKernel00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kern11", airTypeOther, 1, 1, &k11, "cubicd:1,0", "kernel for gageKernel11", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kern22", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "kernel for gageKernel22", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "ssn", "SS #", airTypeUInt, 1, 1, &numSS, "0", "how many scale-space samples to evaluate, or, " "0 to turn-off all scale-space behavior"); hestOptAdd(&hopt, "ssr", "scale range", airTypeDouble, 2, 2, rangeSS, "nan nan", "range of scales in scale-space"); hestOptAdd(&hopt, "sss", "scale save path", airTypeString, 1, 1, &stackSavePath, "", "give a non-empty path string (like \"./\") to save out " "the pre-blurred volumes computed for the stack"); hestOptAdd(&hopt, "ssp", "SS pos", airTypeDouble, 1, 1, &posSS, "0", "position at which to sample in scale-space"); hestOptAdd(&hopt, "kssblur", "kernel", airTypeOther, 1, 1, &kSSblur, "dgauss:1,5", "blurring kernel, to sample scale space", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "kss", "kernel", airTypeOther, 1, 1, &kSS, "tent", "kernel for reconstructing from scale space samples", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "ssnd", "ssnd", airTypeInt, 1, 1, &SSnormd, "0", "enable derivative normalization based on scale space"); hestOptAdd(&hopt, "ssu", NULL, airTypeInt, 0, 0, &SSuniform, NULL, "do uniform samples along sigma, and not (by default) " "samples according to the logarithm of diffusion time"); hestOptAdd(&hopt, "rn", NULL, airTypeInt, 0, 0, &renorm, NULL, "renormalize kernel weights at each new sample location. " "\"Accurate\" kernels don't need this; doing it always " "makes things go slower"); hestOptAdd(&hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc, "0.0", "For curvature-based queries, use zero when gradient " "magnitude is below this"); hestOptAdd(&hopt, "ofs", "ofs", airTypeInt, 0, 0, &orientationFromSpacing, NULL, "If only per-axis spacing is available, use that to " "guess orientation info"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output array, when probing on polydata vertices"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, probeInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); what = airEnumVal(kind->enm, whatS); if (!what) { /* 0 indeed always means "unknown" for any gageKind */ fprintf(stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n", me, whatS, kind->name); hestUsage(stderr, hopt, me, hparm); hestGlossary(stderr, hopt, hparm); airMopError(mop); return 1; } if (ELL_4V_LEN(lineInfo) && !lineStepNum) { fprintf(stderr, "%s: gave line info (\"-pl\") but not " "# samples (\"-pln\")", me); hestUsage(stderr, hopt, me, hparm); hestGlossary(stderr, hopt, hparm); airMopError(mop); return 1; } /* special set-up required for DWI kind */ if (!strcmp(TEN_DWI_GAGE_KIND_NAME, kind->name)) { if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &bval, &skip, &skipNum, nin)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } if (skipNum) { fprintf(stderr, "%s: sorry, can't do DWI skipping in tenDwiGage", me); airMopError(mop); return 1; } /* this could stand to use some more command-line arguments */ if (tenDwiGageKindSet(kind, 50, 1, bval, 0.001, ngrad, nbmat, tenEstimate1MethodLLS, tenEstimate2MethodQSegLLS, /* randSeed */ 7919)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } } ansLen = kind->table[what].answerLength; /* for setting up pre-blurred scale-space samples */ if (numSS) { sbp = gageStackBlurParmNew(); airMopAdd(mop, sbp, (airMopper)gageStackBlurParmNix, airMopAlways); ninSS = AIR_CAST(Nrrd **, calloc(numSS, sizeof(Nrrd *))); if (!ninSS) { fprintf(stderr, "%s: couldn't allocate ninSS", me); airMopError(mop); return 1; } for (ninSSIdx=0; ninSSIdxkernel, k00->parm); if (!E) E |= gageKernelSet(ctx, gageKernel11, k11->kernel, k11->parm); if (!E) E |= gageKernelSet(ctx, gageKernel22, k22->kernel, k22->parm); if (numSS) { gagePerVolume **pvlSS; gageParmSet(ctx, gageParmStackUse, AIR_TRUE); gageParmSet(ctx, gageParmStackNormalizeDeriv, SSnormd ? AIR_TRUE : AIR_FALSE); if (!E) E |= !(pvlSS = AIR_CAST(gagePerVolume **, calloc(numSS, sizeof(gagePerVolume *)))); if (!E) airMopAdd(mop, pvlSS, (airMopper)airFree, airMopAlways); if (!E) E |= gageStackPerVolumeNew(ctx, pvlSS, AIR_CAST(const Nrrd*const*, ninSS), numSS, kind); if (!E) E |= gageStackPerVolumeAttach(ctx, pvl, pvlSS, sbp->scale, numSS); if (!E) E |= gageKernelSet(ctx, gageKernelStack, kSS->kernel, kSS->parm); } else { if (!E) E |= gagePerVolumeAttach(ctx, pvl); } if (!E) E |= gageQueryItemOn(ctx, pvl, what); if (!E) E |= gageUpdate(ctx); if (E) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } /* test with original context */ answer = gageAnswerPointer(ctx, pvl, what); if (lpld) { /* probing on locations of polydata */ double *dout, xyzw[4]; unsigned int vidx, ai; nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (1 == ansLen) { E = nrrdAlloc_va(nout, nrrdTypeDouble, 1, AIR_CAST(size_t, lpld->xyzwNum)); } else { E = nrrdAlloc_va(nout, nrrdTypeDouble, 2, AIR_CAST(size_t, ansLen), AIR_CAST(size_t, lpld->xyzwNum)); } if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } dout = AIR_CAST(double *, nout->data); for (vidx=0; vidxxyzwNum; vidx++) { ELL_4V_COPY(xyzw, lpld->xyzw + 4*vidx); ELL_4V_HOMOG(xyzw, xyzw); if (gageProbeSpace(ctx, xyzw[0], xyzw[1], xyzw[2], !worldSpace, AIR_TRUE)) { fprintf(stderr, "%s: trouble:\n%s\n(%d)\n", me, ctx->errStr, ctx->errNum); airMopError(mop); return 1; } for (ai=0; aidata); ELL_3V_COPY(start, pos); if (lineInfo[3]) { double tmp; /* stepping along vector */ ELL_3V_COPY(dir, 0 + lineInfo); ELL_3V_SET(end, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_NORM(dir, dir, tmp); if (!tmp) { fprintf(stderr, "%s: requested vector stepping, but vlen = 0", me); airMopError(mop); return 1; } ELL_3V_SCALE(dir, lineInfo[3], dir); } else { /* stepping between points */ ELL_3V_SET(dir, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_COPY(end, 0 + lineInfo); } for (vidx=0; vidxerrStr, ctx->errNum); airMopError(mop); return 1; } for (ai=0; aierrStr, ctx->errNum); airMopError(mop); return 1; } printf("%s: %s(%g,%g,%g) = ", me, airEnumStr(kind->enm, what), pos[0], pos[1], pos[2]); printans(stdout, answer, ansLen); printf("\n"); if (eps && 1 == ansLen) { double v[3][3][3], fes, ee; int xo, yo, zo; if (!worldSpace) { fprintf(stderr, "\n%s: WARNING!!: not probing in world-space (via " "\"-wsp\") likely leads to errors in estimated " "derivatives\n\n", me); } gageParmSet(ctx, gageParmVerbose, 0); #define PROBE(x, y, z) \ ((numSS \ ? gageStackProbeSpace(ctx, x, y, z, posSS, !worldSpace, AIR_FALSE) \ : gageProbeSpace(ctx, x, y, z, !worldSpace, AIR_FALSE)), answer[0]) for (xo=0; xo<=2; xo++) { for (yo=0; yo<=2; yo++) { for (zo=0; zo<=2; zo++) { v[xo][yo][zo] = PROBE(pos[0] + (xo-1)*eps, pos[1] + (yo-1)*eps, pos[2] + (zo-1)*eps); } } } printf("%s: approx gradient(%s) at (%g,%g,%g) = %f %f %f\n", me, airEnumStr(kind->enm, what), pos[0], pos[1], pos[2], (v[2][1][1] - v[0][1][1])/(2*eps), (v[1][2][1] - v[1][0][1])/(2*eps), (v[1][1][2] - v[1][1][0])/(2*eps)); fes = 4*eps*eps; ee = eps*eps; printf("%s: approx hessian(%s) at (%g,%g,%g) = \n" "%f %f %f\n" " %f %f\n" " %f\n", me, airEnumStr(kind->enm, what), pos[0], pos[1], pos[2], (v[0][1][1] - 2*v[1][1][1] + v[2][1][1])/ee, (v[2][2][1] - v[0][2][1] - v[2][0][1] + v[0][0][1])/fes, (v[2][1][2] - v[0][1][2] - v[2][1][0] + v[0][1][0])/fes, (v[1][2][1] - 2*v[1][1][1] + v[1][0][1])/ee, (v[1][2][2] - v[1][0][2] - v[1][2][0] + v[1][0][0])/fes, (v[1][1][2] - 2*v[1][1][1] + v[1][1][0])/ee); } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/bin/emap.c0000664000175000017500000002005012165631065016234 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static const char *emapInfo = ("Creates environment maps based on limn's \"checker\" " "normal quantization methods. By taking into account " "camera parameters, this allows for both lights in " "both world and view space. Solely out of laziness, " "the nrrd format is used for specifying the lights, but not " "to worry: you can use a simple un-adorned text file, " "defining one light per line, with 7 values per light: " "0/1 (world/view space), R\tG\tB color, and " "X\tY\tZ position."); int main(int argc, const char *argv[]) { hestOpt *hopt=NULL; hestParm *hparm; Nrrd *nlight, *nmap, *ndebug; const char *me; char *outS, *errS, *debugS; airArray *mop; float amb[3], *linfo, *debug, *map; unsigned li, ui, vi; int qn, bits, method, doerr; limnLight *light; limnCamera *cam; double u, v, r, w, V2W[9], diff, WW[3], VV[3]; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hparm->elideSingleEmptyStringDefault = AIR_TRUE; cam = limnCameraNew(); airMopAdd(mop, cam, (airMopper)limnCameraNix, airMopAlways); hestOptAdd(&hopt, "i", "nlight", airTypeOther, 1, 1, &nlight, NULL, "input nrrd containing light information", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "b", "# bits", airTypeInt, 1, 1, &bits, "16", "number of bits to use for normal quantization, " "between 8 and 16 inclusive. "); hestOptAdd(&hopt, "amb", "ambient RGB", airTypeFloat, 3, 3, amb, "0 0 0", "ambient light color"); hestOptAdd(&hopt, "fr", "from point", airTypeDouble, 3, 3, cam->from,"1 0 0", "position of camera, used to determine view vector"); hestOptAdd(&hopt, "at", "at point", airTypeDouble, 3, 3, cam->at, "0 0 0", "camera look-at point, used to determine view vector"); hestOptAdd(&hopt, "up", "up vector", airTypeDouble, 3, 3, cam->up, "0 0 1", "camera pseudo-up vector, used to determine view coordinates"); hestOptAdd(&hopt, "rh", NULL, airTypeInt, 0, 0, &(cam->rightHanded), NULL, "use a right-handed UVN frame (V points down)"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, NULL, "file to write output envmap to"); hestOptAdd(&hopt, "d", "filename", airTypeString, 1, 1, &debugS, "", "Use this option to save out (to the given filename) a rendering " "of the front (on the left) and back (on the right) of a sphere " "as shaded with the new environment map. U increases " "right-ward, V increases downward. The back sphere half is " "rendered as though the front half was removed"); hestOptAdd(&hopt, "err", NULL, airTypeInt, 0, 0, &doerr, NULL, "If using \"-d\", make the image represent the error between the " "real and quantized vector"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, emapInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); switch(bits) { case 16: method = limnQN16octa; break; case 15: method = limnQN15octa; break; case 14: method = limnQN14octa; break; case 13: method = limnQN13octa; break; case 12: method = limnQN12octa; break; case 11: method = limnQN11octa; break; case 10: method = limnQN10octa; break; case 9: method = limnQN9octa; break; case 8: method = limnQN8octa; break; default: fprintf(stderr, "%s: requested #bits (%d) not in valid range [8,16]\n", me, bits); airMopError(mop); return 1; } if (!(nrrdTypeFloat == nlight->type && 2 == nlight->dim && 7 == nlight->axis[0].size && LIMN_LIGHT_NUM >= nlight->axis[1].size)) { fprintf(stderr, "%s: nlight isn't valid format for light specification, " "must be: float type, 2-dimensional, 7\tx\tN size, N <= %d\n", me, LIMN_LIGHT_NUM); airMopError(mop); return 1; } light = limnLightNew(); airMopAdd(mop, light, (airMopper)limnLightNix, airMopAlways); limnLightAmbientSet(light, amb[0], amb[1], amb[2]); for (li=0; liaxis[1].size; li++) { linfo = (float *)(nlight->data) + 7*li; limnLightSet(light, li, !!linfo[0], linfo[1], linfo[2], linfo[3], linfo[4], linfo[5], linfo[6]); } cam->neer = -0.000000001; cam->dist = 0; cam->faar = 0.0000000001; cam->atRelative = AIR_TRUE; if (limnCameraUpdate(cam) || limnLightUpdate(light, cam)) { airMopAdd(mop, errS = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: problem with camera or lights:\n%s\n", me, errS); airMopError(mop); return 1; } nmap=nrrdNew(); airMopAdd(mop, nmap, (airMopper)nrrdNuke, airMopAlways); if (limnEnvMapFill(nmap, limnLightDiffuseCB, method, light)) { airMopAdd(mop, errS = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: problem making environment map:\n%s\n", me, errS); airMopError(mop); return 1; } map = (float *)nmap->data; if (nrrdSave(outS, nmap, NULL)) { fprintf(stderr, "%s: trouble:\n%s", me, errS = biffGetDone(NRRD)); free(errS); return 1; } if (airStrlen(debugS)) { ELL_34M_EXTRACT(V2W, cam->V2W); ndebug = nrrdNew(); nrrdMaybeAlloc_va(ndebug, nrrdTypeFloat, 3, AIR_CAST(size_t, 3), AIR_CAST(size_t, 1024), AIR_CAST(size_t, 512)); airMopAdd(mop, ndebug, (airMopper)nrrdNuke, airMopAlways); debug = (float *)ndebug->data; for (vi=0; vi<=511; vi++) { v = AIR_AFFINE(0, vi, 511, -0.999, 0.999); for (ui=0; ui<=511; ui++) { u = AIR_AFFINE(0, ui, 511, -0.999, 0.999); r = sqrt(u*u + v*v); if (r > 1) { continue; } w = sqrt(1 - r*r); /* first, the near side of the sphere */ ELL_3V_SET(VV, u, v, -w); ELL_3MV_MUL(WW, V2W, VV); qn = limnVtoQN_d[method](WW); if (doerr) { limnQNtoV_d[method](VV, qn); ELL_3V_SUB(WW, WW, VV); diff = ELL_3V_LEN(WW); ELL_3V_SET_TT(debug + 3*(ui + 1024*vi), float, diff, diff, diff); } else { ELL_3V_COPY(debug + 3*(ui + 1024*vi), map + 3*qn); } /* second, the far side of the sphere */ ELL_3V_SET(VV, u, v, w); ELL_3MV_MUL(WW, V2W, VV); qn = limnVtoQN_d[method](WW); if (doerr) { limnQNtoV_d[method](VV, qn); ELL_3V_SUB(WW, WW, VV); diff = ELL_3V_LEN(WW); ELL_3V_SET_TT(debug + 3*(ui + 512 + 1024*vi), float, diff, diff, diff); } else { ELL_3V_COPY(debug + 3*(ui + 512 + 1024*vi), map + 3*qn); } } } nrrdSave(debugS, ndebug, NULL); } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/bin/gprobe.c0000664000175000017500000010247412174674257016615 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include static void printans(FILE *file, const double *ans, unsigned int len) { unsigned int ai; AIR_UNUSED(file); for (ai=0; aidim)) { biffAddf(GAGE, "%s: ngrid must be 2 (not %u)", me, _ngrid->dim); return 1; } if ((ctx->stackPos && _ngrid->axis[0].size != 5) || (!ctx->stackPos && _ngrid->axis[0].size != 4)) { biffAddf(GAGE, "%s: if %susing stack, need " "ngrid->axis[0].size = %u = 1 + %u (not %u)", me, (ctx->stackPos ? "" : "not "), (ctx->stackPos ? 4 : 3) + 1, (ctx->stackPos ? 4 : 3), AIR_UINT(_ngrid->axis[0].size)); return 1; } mop = airMopNew(); ngrid = nrrdNew(); airMopAdd(mop, ngrid, (airMopper)nrrdNuke, airMopAlways); if (ctx->stackPos) { if (nrrdConvert(ngrid, _ngrid, nrrdTypeDouble)) { biffMovef(GAGE, NRRD, "%s: trouble converting ngrid", me); airMopError(mop); return 1; } } else { Nrrd *ntmp; ptrdiff_t minIdx[2], maxIdx[2]; minIdx[0] = minIdx[1] = 0; maxIdx[0] = 4; /* pad by one sample */ maxIdx[1] = AIR_CAST(ptrdiff_t, _ngrid->axis[1].size-1); /* no padding */ ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(ntmp, _ngrid, nrrdTypeDouble) || nrrdPad_nva(ngrid, ntmp, minIdx, maxIdx, nrrdBoundaryPad, 0.0)) { biffMovef(GAGE, NRRD, "%s: trouble converting/padding ngrid", me); airMopError(mop); return 1; } } grid = AIR_CAST(double *, ngrid->data); gridDim = AIR_ROUNDUP_UI(grid[0]); if (gridDim + 1 != ngrid->axis[1].size) { biffAddf(GAGE, "%s: ngrid->axis[1].size = %u but expected %u = 1 + %u", me, AIR_UINT(ngrid->axis[1].size), 1 + gridDim, gridDim); airMopError(mop); return 1; } answer = gageAnswerPointer(ctx, pvl, what); ansLen = pvl->kind->table[what].answerLength; baseDim = 1 == ansLen ? 0 : 1; dim = baseDim + gridDim; if (dim > NRRD_DIM_MAX) { biffAddf(GAGE, "%s: output dimension %u unreasonable", me, dim); airMopError(mop); return 1; } if (ansLen > 1) { sizeOut[0] = ansLen; coordOut[0] = 0; } NN = 1; for (aidx=0; aidxtype]; for (II=0; II 1) { fprintf(stderr, "z = "); } fprintf(stderr, " %s/%s", airSprintSize_t(stmp[0], coordOut[2]), airSprintSize_t(stmp[1], sizeOut[2])); fflush(stderr); if (verbose > 1) { fprintf(stderr, "\n"); } } ELL_4V_COPY(pos, grid + 1 + 5*0); for (aidx=0; aidx (%u %u) -> %g %g %g %g (%s)\n", me, AIR_UINT(II), AIR_UINT(coordOut[0+baseDim]), AIR_UINT(coordOut[1+baseDim]), pos[0], pos[1], pos[2], pos[3], indexSpace ? "index" : "world"); */ E = (ctx->stackPos ? gageStackProbeSpace(ctx, pos[0], pos[1], pos[2], pos[3], indexSpace, clamp) : gageProbeSpace(ctx, pos[0], pos[1], pos[2], indexSpace, clamp)); if (E) { biffAddf(GAGE, "%s: trouble at II=%s =(%g,%g,%g,%g):\n%s\n(%d)\n", me, airSprintSize_t(stmp[0], II), pos[0], pos[1], pos[2], pos[3], ctx->errStr, ctx->errNum); airMopError(mop); return 1; } if (1 == ansLen) { ins(nout->data, II, *answer); } else { for (aidx=0; aidxdata, aidx + ansLen*II, answer[aidx]); } } NRRD_COORD_INCR(coordOut, sizeOut, dim, baseDim); } if (verbose && verbose <= 1) { fprintf(stderr, "\n"); } if (!indexSpace) { /* set the output space directions, but (being conservative/cautious) only do so when grid had specified world-space positions */ /* HEY: untested! whipped up out of frustration for GLK Bonn talk */ nout->spaceDim = 3; if (baseDim) { nrrdSpaceVecSetNaN(nout->axis[0].spaceDirection); } nrrdSpaceVecCopy(nout->spaceOrigin, grid + 1 + 5*0); for (aidx=0; aidxaxis[baseDim+aidx].spaceDirection, grid + 1 + 5*(1+aidx)); } } airMopOkay(mop); return 0; } static const char *probeInfo = ("Shows off the functionality of the gage library. " "Uses gageProbe() to query various kinds of volumes " "to learn various measured or derived quantities."); int main(int argc, const char *argv[]) { gageKind *kind; const char *me; char *whatS, *err, *outS, *stackFnameFormat; hestParm *hparm; hestOpt *hopt = NULL; NrrdKernelSpec *k00, *k11, *k22, *kSS, *kSSblur; int what, E=0, renorm, uniformSS, optimSS, verbose, zeroZ, orientationFromSpacing, probeSpaceIndex, normdSS; unsigned int iBaseDim, oBaseDim, axi, numSS, seed; const double *answer; Nrrd *nin, *_npos, *npos, *_ngrid, *ngrid, *nout, **ninSS=NULL; Nrrd *ngrad=NULL, *nbmat=NULL; size_t six, siy, siz, sox, soy, soz; double bval=0, eps, gmc, rangeSS[2], *pntPos, scale[3], posSS, biasSS, dsix, dsiy, dsiz, dsox, dsoy, dsoz; gageContext *ctx; gagePerVolume *pvl=NULL; double t0, t1, rscl[3], min[3], maxOut[3], maxIn[3]; airArray *mop; unsigned int ansLen, *skip, skipNum, pntPosNum; gageStackBlurParm *sbp; int otype, clamp; char stmp[4][AIR_STRLEN_SMALL]; me = argv[0]; /* parse environment variables first, in case they break nrrdDefault* or nrrdState* variables in a way that nrrdSanity() should see */ nrrdDefaultGetenv(); nrrdStateGetenv(); /* no harm done in making sure we're sane */ if (!nrrdSanity()) { fprintf(stderr, "******************************************\n"); fprintf(stderr, "******************************************\n"); fprintf(stderr, "\n"); fprintf(stderr, " %s: nrrd sanity check FAILED.\n", me); fprintf(stderr, "\n"); fprintf(stderr, " This means that either nrrd can't work on this " "platform, or (more likely)\n"); fprintf(stderr, " there was an error in the compilation options " "and variable definitions\n"); fprintf(stderr, " for how Teem was built here.\n"); fprintf(stderr, "\n"); fprintf(stderr, " %s\n", err = biffGetDone(NRRD)); fprintf(stderr, "\n"); fprintf(stderr, "******************************************\n"); fprintf(stderr, "******************************************\n"); free(err); return 1; } mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, AIR_CAST(airMopper, hestParmFree), airMopAlways); hparm->elideSingleOtherType = AIR_TRUE; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL, "\"kind\" of volume (\"scalar\", \"vector\", " "\"tensor\", or \"dwi\")", NULL, NULL, meetHestGageKind); hestOptAdd(&hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1", "verbosity level"); hestOptAdd(&hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL, "the quantity (scalar, vector, or matrix) to learn by probing"); hestOptAdd(&hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc, "0.0", "For curvature-based queries, use zero when gradient " "magnitude is below this"); hestOptAdd(&hopt, "ofs", "ofs", airTypeInt, 0, 0, &orientationFromSpacing, NULL, "If only per-axis spacing is available, use that to " "contrive full orientation info"); hestOptAdd(&hopt, "seed", "N", airTypeUInt, 1, 1, &seed, "42", "RNG seed; mostly for debugging"); hestOptAdd(&hopt, "c", "bool", airTypeBool, 1, 1, &clamp, "false", "clamp positions as part of probing"); hestOptAdd(&hopt, "zz", "bool", airTypeBool, 1, 1, &zeroZ, "false", "enable \"zeroZ\" behavior in gage that partially " "implements working with 3D images as if they are 2D"); hestOptAdd(&hopt, "k00", "kern00", airTypeOther, 1, 1, &k00, "tent", "kernel for gageKernel00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kern11", airTypeOther, 1, 1, &k11, "cubicd:1,0", "kernel for gageKernel11", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kern22", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "kernel for gageKernel22", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "rn", NULL, airTypeInt, 0, 0, &renorm, NULL, "renormalize kernel weights at each new sample location. " "\"Accurate\" kernels don't need this; doing it always " "makes things go slower"); hestOptAdd(&hopt, "ssn", "SS #", airTypeUInt, 1, 1, &numSS, "0", "how many scale-space samples to evaluate, or, " "0 to turn-off all scale-space behavior"); hestOptAdd(&hopt, "ssr", "scale range", airTypeDouble, 2, 2, rangeSS, "nan nan", "range of scales in scale-space"); hestOptAdd(&hopt, "kssb", "kernel", airTypeOther, 1, 1, &kSSblur, "dgauss:1,5", "blurring kernel, to sample scale space", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "kssr", "kernel", airTypeOther, 1, 1, &kSS, "hermite", "kernel for reconstructing from scale space samples", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "ssu", NULL, airTypeInt, 0, 0, &uniformSS, NULL, "do uniform samples along sigma, and not (by default) " "samples according to the effective diffusion scale"); hestOptAdd(&hopt, "sso", NULL, airTypeInt, 0, 0, &optimSS, NULL, "if not using \"-ssu\", use pre-computed optimal " "sigmas when possible"); hestOptAdd(&hopt, "ssnd", NULL, airTypeInt, 0, 0, &normdSS, NULL, "normalize derivatives by scale"); hestOptAdd(&hopt, "ssnb", "bias", airTypeDouble, 1, 1, &biasSS, "0.0", "bias on scale-based derivative normalization"); hestOptAdd(&hopt, "ssf", "SS read/save format", airTypeString, 1, 1, &stackFnameFormat, "", "printf-style format (including a \"%u\") for the " "filenames from which to read " "pre-blurred volumes computed for the stack, if they " "exist and match the stack parameters, and where to save " "them if they had to be re-computed. Leave this as empty " "string to disable this."); hestOptAdd(&hopt, "s", "sclX sclY sxlZ", airTypeDouble, 3, 3, scale, "1 1 1", "scaling factor for resampling on each axis " "(>1.0 : supersampling); use \"-ssp\" (and \"-psi\")" "to specify scale position of sampling"); hestOptAdd(&hopt, "ssp", "pos", airTypeDouble, 1, 1, &posSS, "0", "when using scale-space, scale-position at which to probe"); hestOptAdd(&hopt, "pg", "nrrd", airTypeOther, 1, 1, &_ngrid, "", "overrides \"-s\": " "2-D nrrd which specifies origin and direction vectors " "for sampling grid", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "pi", "nrrd", airTypeOther, 1, 1, &_npos, "", "overrides \"-pv\": probes at this list of 3-vec or " "4-vec positions", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "pp", "pos", airTypeDouble, 3, 4, &pntPos, "nan nan nan", "overrides \"-pi\": only sample at this specified point", &pntPosNum); hestOptAdd(&hopt, "eps", "epsilon", airTypeDouble, 1, 1, &eps, "0", "if non-zero, and if query is a scalar, and if using \"pp\" " "to query at a single point, then do epsilon offset probes " "to calculate discrete differences, to find the numerical " "gradient and hessian (for debugging)"); hestOptAdd(&hopt, "psi", "p", airTypeBool, 1, 1, &probeSpaceIndex, "false", "whether the probe location specification (by any of " "the four previous flags) are in index space"); hestOptAdd(&hopt, "t", "type", airTypeEnum, 1, 1, &otype, "float", "type of output volume", NULL, nrrdType); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output volume"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, probeInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, AIR_CAST(airMopper, hestOptFree), airMopAlways); airMopAdd(mop, hopt, AIR_CAST(airMopper, hestParseFree), airMopAlways); what = airEnumVal(kind->enm, whatS); if (!what) { /* 0 indeed always means "unknown" for any gageKind */ fprintf(stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n", me, whatS, kind->name); hestUsage(stderr, hopt, me, hparm); hestGlossary(stderr, hopt, hparm); airMopError(mop); return 1; } /* special set-up required for DWI kind */ if (!strcmp(TEN_DWI_GAGE_KIND_NAME, kind->name)) { if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &bval, &skip, &skipNum, nin)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } if (skipNum) { fprintf(stderr, "%s: sorry, can't do DWI skipping in tenDwiGage", me); airMopError(mop); return 1; } /* this could stand to use some more command-line arguments */ if (tenDwiGageKindSet(kind, 50, 1, bval, 0.001, ngrad, nbmat, tenEstimate1MethodLLS, tenEstimate2MethodQSegLLS, seed)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } } /* for setting up pre-blurred scale-space samples */ if (numSS) { unsigned int vi; int recompute; sbp = gageStackBlurParmNew(); airMopAdd(mop, sbp, (airMopper)gageStackBlurParmNix, airMopAlways); if (gageStackBlurParmVerboseSet(sbp, verbose) || gageStackBlurParmScaleSet(sbp, numSS, rangeSS[0], rangeSS[1], uniformSS, optimSS) || gageStackBlurParmKernelSet(sbp, kSSblur, AIR_TRUE) || gageStackBlurParmBoundarySet(sbp, nrrdBoundaryBleed, AIR_NAN) || gageStackBlurManage(&ninSS, &recompute, sbp, stackFnameFormat, AIR_TRUE, NULL, nin, kind)) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble getting volume stack:\n%s\n", me, err); airMopError(mop); return 1; } airMopAdd(mop, ninSS, airFree, airMopAlways); if (verbose > 2) { fprintf(stderr, "%s: sampling scale range %g--%g %suniformly:\n", me, rangeSS[0], rangeSS[1], uniformSS ? "" : "non-"); for (vi=0; viscale[vi]); } } } else { ninSS = NULL; sbp = NULL; } /*** **** Except for the gageProbe() call in the inner loop below, **** and the gageContextNix() call at the very end, all the gage **** calls which set up (and take down) the context and state are here. ***/ ctx = gageContextNew(); airMopAdd(mop, ctx, AIR_CAST(airMopper, gageContextNix), airMopAlways); gageParmSet(ctx, gageParmGradMagCurvMin, gmc); gageParmSet(ctx, gageParmVerbose, verbose); gageParmSet(ctx, gageParmTwoDimZeroZ, zeroZ); gageParmSet(ctx, gageParmRenormalize, renorm ? AIR_TRUE : AIR_FALSE); gageParmSet(ctx, gageParmCheckIntegrals, AIR_TRUE); gageParmSet(ctx, gageParmOrientationFromSpacing, orientationFromSpacing); E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(ctx, nin, kind)); if (!E) E |= gageKernelSet(ctx, gageKernel00, k00->kernel, k00->parm); if (!E) E |= gageKernelSet(ctx, gageKernel11, k11->kernel, k11->parm); if (!E) E |= gageKernelSet(ctx, gageKernel22, k22->kernel, k22->parm); if (numSS) { gagePerVolume **pvlSS; gageParmSet(ctx, gageParmStackUse, AIR_TRUE); gageParmSet(ctx, gageParmStackNormalizeDerivBias, biasSS); gageParmSet(ctx, gageParmStackNormalizeDeriv, normdSS); if (!E) E |= !(pvlSS = AIR_CAST(gagePerVolume **, calloc(numSS, sizeof(gagePerVolume *)))); if (!E) airMopAdd(mop, pvlSS, (airMopper)airFree, airMopAlways); if (!E) E |= gageStackPerVolumeNew(ctx, pvlSS, AIR_CAST(const Nrrd*const*, ninSS), numSS, kind); if (!E) E |= gageStackPerVolumeAttach(ctx, pvl, pvlSS, sbp->scale, numSS); if (!E) E |= gageKernelSet(ctx, gageKernelStack, kSS->kernel, kSS->parm); } else { if (!E) E |= gagePerVolumeAttach(ctx, pvl); } if (!E) E |= gageQueryItemOn(ctx, pvl, what); if (!E) E |= gageUpdate(ctx); if (E) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } answer = gageAnswerPointer(ctx, pvl, what); ansLen = kind->table[what].answerLength; /*** **** end gage setup. ***/ if (verbose) { fprintf(stderr, "%s: kernel support = %d^3 samples\n", me, 2*ctx->radius); } if (ELL_3V_EXISTS(pntPos)) { /* only interested in a single point, make sure we have the right info about the point WRT scale stuff */ if (numSS) { if (!(4 == pntPosNum && ELL_4V_EXISTS(pntPos))) { fprintf(stderr, "%s: need a 4-vec position with scale-space", me); airMopError(mop); return 1; } } else { if (!(3 == pntPosNum)) { fprintf(stderr, "%s: need a 3-vec position (w/out scale-space)", me); airMopError(mop); return 1; } } if (numSS ? gageStackProbeSpace(ctx, pntPos[0], pntPos[1], pntPos[2], pntPos[3], probeSpaceIndex, clamp) : gageProbeSpace(ctx, pntPos[0], pntPos[1], pntPos[2], probeSpaceIndex, clamp)) { fprintf(stderr, "%s: trouble probing: (errNum %d) %s\n", me, ctx->errNum, ctx->errStr); airMopError(mop); return 1; } if (numSS) { printf("%s: %s(%s:%g,%g,%g,%g) = ", me, airEnumStr(kind->enm, what), probeSpaceIndex ? "index" : "world", pntPos[0], pntPos[1], pntPos[2], pntPos[3]); } else { printf("%s: %s(%s:%g,%g,%g) = ", me, airEnumStr(kind->enm, what), probeSpaceIndex ? "index" : "world", pntPos[0], pntPos[1], pntPos[2]); } printans(stdout, answer, ansLen); printf("\n"); /* we're done, get out of here */ /* except if we're supposed to debug derivatives */ if (eps && 1 == ansLen) { double v[3][3][3], fes, ee; int xo, yo, zo; if (probeSpaceIndex) { fprintf(stderr, "\n%s: WARNING!!: not probing in world-space (via " "\"-wsp\") likely leads to errors in estimated " "derivatives\n\n", me); } gageParmSet(ctx, gageParmVerbose, 0); #define PROBE(x, y, z) \ ((numSS \ ? gageStackProbeSpace(ctx, x, y, z, posSS, \ probeSpaceIndex, clamp) \ : gageProbeSpace(ctx, x, y, z, probeSpaceIndex, \ clamp)),answer[0]) for (xo=0; xo<=2; xo++) { for (yo=0; yo<=2; yo++) { for (zo=0; zo<=2; zo++) { v[xo][yo][zo] = PROBE(pntPos[0] + (xo-1)*eps, pntPos[1] + (yo-1)*eps, pntPos[2] + (zo-1)*eps); } } } printf("%s: approx gradient(%s) at (%g,%g,%g) = %f %f %f\n", me, airEnumStr(kind->enm, what), pntPos[0], pntPos[1], pntPos[2], (v[2][1][1] - v[0][1][1])/(2*eps), (v[1][2][1] - v[1][0][1])/(2*eps), (v[1][1][2] - v[1][1][0])/(2*eps)); fes = 4*eps*eps; ee = eps*eps; printf("%s: approx hessian(%s) at (%g,%g,%g) = \n" "%f %f %f\n" " %f %f\n" " %f\n", me, airEnumStr(kind->enm, what), pntPos[0], pntPos[1], pntPos[2], (v[0][1][1] - 2*v[1][1][1] + v[2][1][1])/ee, (v[2][2][1] - v[0][2][1] - v[2][0][1] + v[0][0][1])/fes, (v[2][1][2] - v[0][1][2] - v[2][1][0] + v[0][1][0])/fes, (v[1][2][1] - 2*v[1][1][1] + v[1][0][1])/ee, (v[1][2][2] - v[1][0][2] - v[1][2][0] + v[1][0][0])/fes, (v[1][1][2] - 2*v[1][1][1] + v[1][1][0])/ee); } airMopOkay(mop); return 0; } if (_npos) { /* given a nrrd of probe locations */ double *pos, (*ins)(void *v, size_t I, double d); size_t II, NN; unsigned int aidx; if (!(2 == _npos->dim && (3 == _npos->axis[0].size || 4 == _npos->axis[0].size))) { fprintf(stderr, "%s: need npos 2-D 3-by-N or 4-by-N " "(not %u-D %u-by-N)\n", me, _npos->dim, AIR_UINT(_npos->axis[0].size)); airMopError(mop); return 1; } if ((numSS && 3 == _npos->axis[0].size) || (!numSS && 4 == _npos->axis[0].size)) { fprintf(stderr, "%s: have %u point coords but %s using scale-space\n", me, AIR_UINT(_npos->axis[0].size), numSS ? "are" : "are not"); airMopError(mop); return 1; } NN = _npos->axis[1].size; npos = nrrdNew(); airMopAdd(mop, npos, AIR_CAST(airMopper, nrrdNuke), airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, AIR_CAST(airMopper, nrrdNuke), airMopAlways); if (nrrdConvert(npos, _npos, nrrdTypeDouble) || nrrdMaybeAlloc_va(nout, otype, 2, AIR_CAST(size_t, ansLen), AIR_CAST(size_t, NN))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble with npos or nout:\n%s\n", me, err); airMopError(mop); return 1; } pos = AIR_CAST(double *, npos->data); ins = nrrdDInsert[nout->type]; for (II=0; IIdata, II, *answer); } else { for (aidx=0; aidxdata, aidx + ansLen*II, answer[aidx]); } } /* if (numSS) { printf("%s: %s(%s:%g,%g,%g,%g) = ", me, airEnumStr(kind->enm, what), probeSpaceIndex ? "index" : "world", pos[0], pos[1], pos[2], pos[3]); } else { printf("%s: %s(%s:%g,%g,%g) = ", me, airEnumStr(kind->enm, what), probeSpaceIndex ? "index" : "world", pos[0], pos[1], pos[2]); } printans(stdout, answer, ansLen); printf("\n"); */ pos += _npos->axis[0].size; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving output:\n%s\n", me, err); airMopError(mop); return 1; } /* we're done, get out of here */ airMopOkay(mop); exit(0); } /* else, we're sampling on some kind of grid */ ngrid = nrrdNew(); airMopAdd(mop, ngrid, (airMopper)nrrdNuke, airMopAlways); iBaseDim = kind->baseDim; oBaseDim = 1 == ansLen ? 0 : 1; if (!_ngrid) { /* did not get a grid, have to use "-s" args to define it */ double *grid; size_t gridSize[NRRD_DIM_MAX]; six = nin->axis[0+iBaseDim].size; siy = nin->axis[1+iBaseDim].size; siz = nin->axis[2+iBaseDim].size; dsix = AIR_CAST(double, six); dsiy = AIR_CAST(double, siy); dsiz = AIR_CAST(double, siz); sox = AIR_CAST(size_t, scale[0]*dsix); soy = AIR_CAST(size_t, scale[1]*dsiy); soz = AIR_CAST(size_t, scale[2]*dsiz); dsox = AIR_CAST(double, sox); dsoy = AIR_CAST(double, soy); dsoz = AIR_CAST(double, soz); rscl[0] = dsix/dsox; rscl[1] = dsiy/dsoy; rscl[2] = dsiz/dsoz; if (verbose) { fprintf(stderr, "%s: creating %u x %s x %s x %s output\n", me, ansLen, airSprintSize_t(stmp[1], sox), airSprintSize_t(stmp[2], soy), airSprintSize_t(stmp[3], soz)); fprintf(stderr, "%s: effective scaling is %g %g %g\n", me, rscl[0], rscl[1], rscl[2]); } gridSize[0] = numSS ? 5 : 4; gridSize[1] = 4; if (nrrdMaybeAlloc_nva(ngrid, nrrdTypeDouble, 2, gridSize)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble making ngrid:\n%s\n", me, err); airMopError(mop); return 1; } grid = AIR_CAST(double *, ngrid->data); if (nrrdCenterCell == ctx->shape->center) { ELL_3V_SET(min, -0.5, -0.5, -0.5); ELL_3V_SET(maxOut, dsox-0.5, dsoy-0.5, dsoz-0.5); ELL_3V_SET(maxIn, dsix-0.5, dsiy-0.5, dsiz-0.5); } else { ELL_3V_SET(min, 0, 0, 0); ELL_3V_SET(maxOut, dsox-1, dsoy-1, dsoz-1); ELL_3V_SET(maxIn, dsix-1, dsiy-1, dsiz-1); } ELL_4V_SET(grid + gridSize[0]*0, 3, NRRD_POS(ctx->shape->center, min[0], maxIn[0], sox, 0), NRRD_POS(ctx->shape->center, min[1], maxIn[1], soy, 0), NRRD_POS(ctx->shape->center, min[2], maxIn[2], soz, 0)); ELL_4V_SET(grid + gridSize[0]*1, dsox, AIR_DELTA(min[0], 1, maxOut[0], min[0], maxIn[0]), 0, 0); ELL_4V_SET(grid + gridSize[0]*2, dsoy, 0, AIR_DELTA(min[1], 1, maxOut[1], min[1], maxIn[1]), 0); ELL_4V_SET(grid + gridSize[0]*3, dsoz, 0, 0, AIR_DELTA(min[2], 1, maxOut[2], min[2], maxIn[2])); if (numSS) { if (!probeSpaceIndex) { double idxSS = AIR_NAN; unsigned int vi; /* there's actually work to do here, weirdly: gageProbe can either work in index space, or in world space, but the vprobe-style sampling is index-space-centric, so we have to convert the world-space stack position to index space, to be consistent with the way that the grid sampling will be defined. So, we have to actually replicate work that is done by _gageProbeSpace() in converting from world to index space */ /* HEY: the way that idxSS is set is very strange */ for (vi=0; viscale[vi], posSS, sbp->scale[vi+1])) { idxSS = vi + AIR_AFFINE(sbp->scale[vi], posSS, sbp->scale[vi+1], 0, 1); if (verbose > 1) { fprintf(stderr, "%s: scale pos %g -> idx %g = %u + %g\n", me, posSS, idxSS, vi, AIR_AFFINE(sbp->scale[vi], posSS, sbp->scale[vi+1], 0, 1)); } break; } } if (vi == numSS-1) { fprintf(stderr, "%s: scale pos %g outside range %g=%g, %g=%g\n", me, posSS, rangeSS[0], sbp->scale[0], rangeSS[1], sbp->scale[numSS-1]); airMopError(mop); return 1; } grid[4 + 5*0] = idxSS; } else { grid[4 + 5*0] = posSS; } grid[4 + 5*1] = 0; grid[4 + 5*2] = 0; grid[4 + 5*3] = 0; } } else { /* we did get a grid, here we only copy from _ngrid to ngrid, and let further massaging be done in gridProbe below */ six = siy = siz = 0; sox = soy = soz = 0; if (nrrdCopy(ngrid, _ngrid)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble copying ngrid:\n%s\n", me, err); airMopError(mop); return 1; } } /* probe onto grid */ nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); gageParmSet(ctx, gageParmVerbose, verbose); t0 = airTime(); if (gridProbe(ctx, pvl, what, nout, otype, ngrid, (_ngrid ? probeSpaceIndex /* user specifies grid space */ : AIR_TRUE), /* copying vprobe index-space behavior */ verbose, clamp)) { /* note hijacking of GAGE key */ airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble probing on grid:\n%s\n", me, err); airMopError(mop); return 1; } t1 = airTime(); if (verbose) { fprintf(stderr, "probe rate = %g KHz\n", AIR_CAST(double, nrrdElementNumber(nout)/ansLen) / (1000.0*(t1-t0))); } /* massage output some */ nrrdContentSet_va(nout, "gprobe", nin, "%s", airEnumStr(kind->enm, what)); if (!_ngrid) { /* did not get a grid, have to emulate vprobe per-axis behavior */ for (axi=0; axi<3; axi++) { nout->axis[axi+oBaseDim].label = airStrdup(nin->axis[axi+iBaseDim].label); nout->axis[axi+oBaseDim].center = ctx->shape->center; } nrrdBasicInfoCopy(nout, nin, (NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_SPACEORIGIN_BIT | NRRD_BASIC_INFO_OLDMIN_BIT | NRRD_BASIC_INFO_OLDMAX_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT)); if (ctx->shape->fromOrientation) { if (nin->space) { nrrdSpaceSet(nout, nin->space); } else { nrrdSpaceDimensionSet(nout, nin->spaceDim); } nrrdSpaceVecCopy(nout->spaceOrigin, nin->spaceOrigin); for (axi=0; axi<3; axi++) { double tmp; nrrdSpaceVecScale(nout->axis[axi+oBaseDim].spaceDirection, rscl[axi], nin->axis[axi+iBaseDim].spaceDirection); tmp = AIR_AFFINE(min[axi], 0, maxOut[axi], min[axi], maxIn[axi]); nrrdSpaceVecScaleAdd2(nout->spaceOrigin, 1.0, nout->spaceOrigin, tmp, nin->axis[axi+iBaseDim].spaceDirection); } } else { for (axi=0; axi<3; axi++) { nout->axis[axi+oBaseDim].spacing = rscl[axi]*nin->axis[axi+iBaseDim].spacing; } } } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/bin/talkweb.c0000664000175000017500000004064312165631065016755 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #define TKWB "talkweb" #define TKWB_TAG_TOC 0 #define TKWB_TAG_TITLE 1 #define TKWB_TAG_IMAGE 2 #define TKWB_TAG_FIRST 3 #define TKWB_TAG_PREV 4 #define TKWB_TAG_NEXT 5 #define TKWB_TAG_LAST 6 #define TKWB_TAG_TEXT 7 #define TKWB_TAG_MAX 7 static const char *tkwbInfo = ("Generates HTML pages from slide images and text. " "This program takes multiple inputs: a template for the table of contents " "that will become \"index.html\" (\"-i\"), a template for the pages " "generated for each slide e.g. \"slide000.html\" (\"-t\"), and a script " "text file that contains all the information that will go into the slide " "pages. The format of this file is:\n " "\t\t- Separator line indicating slide transitions, e.g. \"-------------\"\n " "\t\t- Title of first slide (one line)\n " "\t\t- Filename for image to put on first slide (one line)\n " "\t\t- Body of HTML text to put with the slide image (multiple lines)\n " "\t\t- Separator\n " "followed by information for the second slide, and so forth. Textual " "subtitutions are performed in the template files, according to the " "replacement tags (\"-r\", see below). Within the slide pages, navigation " "arrows are based on remaining command-line options." ); int tkwbArrayIncr = 16; typedef struct { char *title, *image, *text; } tkwbSlide; tkwbSlide * tkwbSlideNew(char *title, char *image, char *text) { tkwbSlide *ret; ret = (tkwbSlide*)calloc(1, sizeof(tkwbSlide)); if (ret) { ret->title = airStrdup(title); ret->image = airStrdup(image); ret->text = airStrdup(text); } return ret; } tkwbSlide * tkwbSlideNix(tkwbSlide *slide) { slide->title = (char *)airFree(slide->title); slide->image = (char *)airFree(slide->image); slide->text = (char *)airFree(slide->text); slide = (tkwbSlide *)airFree(slide); return NULL; } typedef union { tkwbSlide ***ps; char ***pc; void **v; } _tkwbU; int tkwbReadFileToString(char **strP, int *hitEOF, FILE *file, char *stop) { char **all, line[AIR_STRLEN_HUGE]; airArray *allArr; unsigned int allLen; unsigned int lineLen, lineIdx, totalLen; _tkwbU uu; uu.pc = &all; allArr = airArrayNew(uu.v, &allLen, sizeof(char*), tkwbArrayIncr); airArrayPointerCB(allArr, airNull, airFree); lineLen = airOneLine(file, line, AIR_STRLEN_HUGE); totalLen = 0; while (lineLen && (!( airStrlen(stop) && !strcmp(line, stop) )) ) { lineIdx = airArrayLenIncr(allArr, 1); /* HEY error checking */ all[lineIdx] = (char *)calloc(strlen(line) + strlen("\n") + 1, sizeof(char)); sprintf(all[lineIdx], "%s\n", line); totalLen += strlen(line) + 1; lineLen = airOneLine(file, line, AIR_STRLEN_HUGE); } if (hitEOF) { *hitEOF = !lineLen; } *strP = (char*)calloc(totalLen+1, sizeof(char)); strcpy(*strP, ""); for (lineIdx=0; lineIdx 1 )) { biffAddf(TKWB, "%s: didn't get a stop delimiter from %s", me, filename); airMopError(mop); return 1; } uu.ps = &slide; slideArr = airArrayNew(uu.v, NULL, sizeof(tkwbSlide*), tkwbArrayIncr); airMopAdd(mop, slideArr, (airMopper)airArrayNix, airMopAlways); hitEOF = notReally = AIR_FALSE; while (!hitEOF) { slideIdx = airArrayLenIncr(slideArr, 1); /* HEY error checking */ len = airOneLine(file, line, AIR_STRLEN_HUGE); if (!len) { /* got EOF after a division marker, that's okay */ notReally = AIR_TRUE; break; } title = airStrdup(line); len = airOneLine(file, line, AIR_STRLEN_HUGE); if (!len) { break; } image = airStrdup(line); if (tkwbReadFileToString(&text, &hitEOF, file, stop)) { biffAddf(TKWB, "%s: couldn't read in slide %d", me, slideIdx); airMopError(mop); return 1; } slide[slideIdx] = tkwbSlideNew(title, image, text); airMopAdd(pmop, slide[slideIdx], (airMopper)tkwbSlideNix, airMopAlways); } if (!hitEOF && !notReally) { biffAddf(TKWB, "%s: got incomplete slide info for slide %d\n", me, slideIdx); airMopError(mop); return 1; } if (!notReally) { slideIdx = airArrayLenIncr(slideArr, 1); /* HEY error checking */ } slide[slideIdx] = NULL; *slideP = slide; airMopOkay(mop); return 0; } int tkwbExpandImageInfo(tkwbSlide **slide) { static const char me[]="tkwbExpandImageInfo"; char *image; Nrrd *nimg; int si, sx, sy, len; airArray *mop; mop = airMopNew(); nimg = nrrdNew(); airMopAdd(mop, nimg, (airMopper)nrrdNuke, airMopAlways); for (si=0; slide[si]; si++) { if (nrrdLoad(nimg, slide[si]->image, NULL)) { biffMovef(TKWB, NRRD, "%s: trouble reading slide image \"%s\"", me, slide[si]->image); airMopError(mop); return 1; } if (!nrrdFormatPNG->fitsInto(nimg, nrrdEncodingGzip, AIR_TRUE)) { biffMovef(TKWB, NRRD, "%s: slide image \"%s\" doesn't seem to be an image", me, slide[si]->image); airMopError(mop); return 1; } sx = nimg->axis[nimg->dim-2].size; sy = nimg->axis[nimg->dim-1].size; len = (strlen("") + strlen(slide[si]->image) + 1); image = (char *)calloc(len, sizeof(char)); sprintf(image, "", sx, sy, slide[si]->image); free(slide[si]->image); slide[si]->image = image; } airMopOkay(mop); return 0; } int tkwbWriteStringToFile(const char *filename, const char *content) { static const char me[]="tkwbWriteStringToFile"; FILE *file; if (!(file = fopen(filename, "wb"))) { biffAddf(TKWB, "%s: trouble opening file \"%s\": %s", me, filename, strerror(errno)); return 1; } fprintf(file, "%s", content); fclose(file); return 0; } int _tkwbStringSubst(char **sP, /* string to search in */ char *f, /* find */ char *r) { /* replace */ char *p, /* place where find was found */ *n; /* new string */ p = strstr(*sP, f); if (!p) { /* nothing to do */ return 0; } n = (char*)calloc(strlen(*sP) - strlen(f) + strlen(r) + 1, sizeof(char)); strncpy(n, *sP, p - *sP); strncpy(n + (p - *sP), r, strlen(r)); strcpy(n + (p - *sP) + strlen(r), p + strlen(f)); free(*sP); *sP = n; return _tkwbStringSubst(sP, f, r); } /* ** NOTE: this will re-allocate *stringP if a substitution is done */ void tkwbStringSubst(char **sP, /* string to search in */ char *f, /* find */ char *r) { /* replace */ _tkwbStringSubst(sP, f, r); return; } int tkwbWriteIndex(char *_indx, tkwbSlide **slide, char *tag[TKWB_TAG_MAX+1]) { static const char me[]="tkwbWriteIndex"; char *repl, *indx, tmp[AIR_STRLEN_MED]; int replLen, si; airArray *mop; mop = airMopNew(); replLen = 0; replLen += strlen("
      \n"); for (si=0; slide[si]; si++) { replLen += (strlen("
    1. \n") + strlen(slide[si]->title)); } replLen += strlen("
    \n"); if (!(repl = (char*)calloc(replLen+1, sizeof(char)))) { biffAddf(TKWB, "%s: couldn't allocate link buffer!", me); airMopError(mop); return 1; } airMopAdd(mop, repl, airFree, airMopAlways); strcpy(repl, "
      \n"); for (si=0; slide[si]; si++) { sprintf(tmp, "
    1. %s\n", si+1, slide[si]->title); strcat(repl, tmp); } strcat(repl, "
    "); indx = airStrdup(_indx); tkwbStringSubst(&indx, tag[TKWB_TAG_TOC], repl); airMopAdd(mop, indx, airFree, airMopAlways); if (tkwbWriteStringToFile("index.html", indx)) { biffAddf(TKWB, "%s: couldn't write \"index.html\"", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } int tkwbWriteSlides(tkwbSlide **slide, int numSlides, char *tmpl, char *tag[TKWB_TAG_MAX+1], char *link[4]) { static const char me[]="tkwbWriteSlides"; char *text, name[AIR_STRLEN_MED], frst[AIR_STRLEN_MED], prev[AIR_STRLEN_MED], next[AIR_STRLEN_MED], last[AIR_STRLEN_MED]; int si; airArray *mop; mop = airMopNew(); sprintf(frst, "%s", link[0]); sprintf(last, "%s", numSlides, link[3]); for (si=0; sititle); tkwbStringSubst(&text, tag[TKWB_TAG_IMAGE], slide[si]->image); tkwbStringSubst(&text, tag[TKWB_TAG_TEXT], slide[si]->text); if (si) { tkwbStringSubst(&text, tag[TKWB_TAG_FIRST], frst); sprintf(prev, "%s", si, link[1]); tkwbStringSubst(&text, tag[TKWB_TAG_PREV], prev); } if (si < numSlides-1) { tkwbStringSubst(&text, tag[TKWB_TAG_LAST], last); sprintf(next, "%s", si+2, link[2]); tkwbStringSubst(&text, tag[TKWB_TAG_NEXT], next); } airMopAdd(mop, text, airFree, airMopAlways); sprintf(name, "slide%03d.html", si+1); if (tkwbWriteStringToFile(name, text)) { biffAddf(TKWB, "%s: couldn't write \"%s\"", me, name); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } int tkwbDoit(char *indxS, char *tmplS, char *scriptS, char *tag[TKWB_TAG_MAX+1], char *link[4]) { static const char me[]="tkwbDoit"; char *indx, *tmpl; tkwbSlide **slide; airArray *mop; int numSlides; mop = airMopNew(); if (tkwbReadTemplate(&indx, indxS)) { biffAddf(TKWB, "%s: trouble reading in index template file", me); airMopError(mop); return 1; } airMopAdd(mop, indx, airFree, airMopAlways); if (tkwbReadTemplate(&tmpl, tmplS)) { biffAddf(TKWB, "%s: trouble reading in slide template file", me); airMopError(mop); return 1; } airMopAdd(mop, tmpl, airFree, airMopAlways); if (tkwbReadSlides(&slide, scriptS, mop)) { biffAddf(TKWB, "%s: trouble reading in slide script", me); airMopError(mop); return 1; } airMopAdd(mop, slide, airFree, airMopAlways); if (tkwbExpandImageInfo(slide)) { biffAddf(TKWB, "%s: trouble learning details of images", me); airMopError(mop); return 1; } if (tkwbWriteIndex(indx, slide, tag)) { biffAddf(TKWB, "%s: trouble writing index.html", me); airMopError(mop); return 1; } for (numSlides=0; slide[numSlides]; numSlides++) ; if (tkwbWriteSlides(slide, numSlides, tmpl, tag, link)) { biffAddf(TKWB, "%s: trouble writing slide pages", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } int main(int argc, const char *argv[]) { const char *me; char *err, *indxS, *tmplS, *scriptS, *pretag[TKWB_TAG_MAX+1], *tag[AIR_STRLEN_MED], *frstLink, *prevLink, *nextLink, *lastLink, *link[4]; hestOpt *hopt = NULL; airArray *mop; int ti; me = argv[0]; hestOptAdd(&hopt, "i", "index", airTypeString, 1, 1, &indxS, NULL, "*index* template HTML filename. This will be turned into " "the \"index.html\" index file, after the links to all the " "slides have been substituted in."); hestOptAdd(&hopt, "t", "slide", airTypeString, 1, 1, &tmplS, NULL, "*slide* template HTML filename. " "The text of this includes the tags " "that are replaced with their per-slide values, to produce the " "HTML file for each slide's page. "); hestOptAdd(&hopt, "s", "script", airTypeString, 1, 1, &scriptS, NULL, "script filename. This file contains information about each " "slide: the slide title, the slide image filename, and the " "HTML text to accompany the slide image."); hestOptAdd(&hopt, "r", "tags", airTypeString, TKWB_TAG_MAX+1, TKWB_TAG_MAX+1, pretag, "TOC TITLE IMAGE FIRST PREV NEXT LAST TEXT", "replacement tags that will be converted into links. " "The actual replcement tag is the string given here embedded " "in an HTML comment (no space). So saying \"TOC\" means the " "actual replacement tag will be \"\". The first tag " "is replaced in the index template; all others are in the " "slide template. " "In order, the tags are for:\n " "\b\bo In the index template, the list of links to slide pages\n " "\b\bo The slide title\n " "\b\bo The slide image\n " "\b\bo The link to the first slide\n " "\b\bo The link to the previous slide\n " "\b\bo The link to the next slide\n " "\b\bo The link to the last slide\n " "\b\bo The text accompanying each slide"); hestOptAdd(&hopt, "first", "text", airTypeString, 1, 1, &frstLink, "|<<", "Snippet of HTML text to be converted into " "link to first slide. Some image could be used here. " "Following three arguments are similar. "); hestOptAdd(&hopt, "prev", "text", airTypeString, 1, 1, &prevLink, "<--", "HTML for link to previous slide"); hestOptAdd(&hopt, "next", "text", airTypeString, 1, 1, &nextLink, "-->", "HTML for link to next slide"); hestOptAdd(&hopt, "last", "text", airTypeString, 1, 1, &lastLink, ">>|", "HTML for link to last slide"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, tkwbInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); link[0] = frstLink; link[1] = prevLink; link[2] = nextLink; link[3] = lastLink; for (ti=0; ti<=TKWB_TAG_MAX; ti++) { tag[ti] = (char *)calloc(strlen(pretag[ti]) + strlen("") + 1, sizeof(char)); airMopAdd(mop, tag[ti], airFree, airMopAlways); sprintf(tag[ti], "", pretag[ti]); } if (tkwbDoit(indxS, tmplS, scriptS, tag, link)) { airMopAdd(mop, err = biffGetDone(TKWB), airFree, airMopAlways); fprintf(stderr, "%s: error:\n%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/bin/spots.c0000664000175000017500000002076112165631065016473 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static const char *spotsInfo = ("Generate reaction-diffusion textures based on " "Turing's second example formulation (page 65) of " "his 1954 paper \"The Chemical Basis of Morphogenesis.\" "); int main(int argc, const char *argv[]) { const char *me; char *err; hestOpt *hopt = NULL; airArray *mop; char *outS; alanContext *actx; int *size, sizeLen, fi, si, wrap, nt, cfn, ha, maxi; unsigned int srnd; double deltaT, mch, xch, alphabeta[2], time0, time1, deltaX, react, rrange; Nrrd *ninit=NULL, *nten=NULL, *nparm=NULL; me = argv[0]; hestOptAdd(&hopt, "s", "sx sy", airTypeInt, 2, 3, &size, "128 128", "size of texture, and also determines its dimension", &sizeLen); hestOptAdd(&hopt, "srand", "N", airTypeUInt, 1, 1, &srnd, "42", "number to seed random number generator with. This uses " "airDrandMT(), so it should be portable."); hestOptAdd(&hopt, "i", "tensors", airTypeOther, 1, 1, &nten, "", "diffusion tensors to use for guiding the texture generation. " "If used, over-rides the \"-s\" option, both for setting " "texture dimension and size. If you want upsampling, you " "do it yourself before sending it here.", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "ha", NULL, airTypeInt, 0, 0, &ha, NULL, "use the homogenous anisotropy assumption- that the spatial " "derivative of the diffusion tensor is negligible when " "computing the diffusive term "); hestOptAdd(&hopt, "wrap", NULL, airTypeInt, 0, 0, &wrap, NULL, "wrap edges of texture around a topological torus (which " "makes a texture suitable for tiling)"); hestOptAdd(&hopt, "ab", "alpha beta", airTypeDouble, 2, 2, alphabeta, "16.0 12.0", "the growth and decay parameters appearing in the reaction " "terms of the reaction-diffusion equations. The default " "values were the ones published by Turing."); hestOptAdd(&hopt, "sr", "react", airTypeDouble, 1, 1, &react, "1.0", "scaling of reaction term"); hestOptAdd(&hopt, "rr", "range range", airTypeDouble, 1, 1, &rrange, "4.0", "amount of random noise to add to inital textures"); hestOptAdd(&hopt, "dt", "time", airTypeDouble, 1, 1, &deltaT, "1.0", "time-step size in Euler integration. Can be larger, at " "risk of hitting divergent instability."); hestOptAdd(&hopt, "dx", "size", airTypeDouble, 1, 1, &deltaX, "1.3", "nominal size of simulation grid element."); hestOptAdd(&hopt, "mch", "change", airTypeDouble, 1, 1, &mch, "0.00001", "the minimum significant change (averaged over the whole " "texture) in the first morphogen: to signify convergence"); hestOptAdd(&hopt, "xch", "change", airTypeDouble, 1, 1, &xch, "6", "the maximum allowable change (averaged over the whole " "texture) in the first morphogen: to signify divergence"); hestOptAdd(&hopt, "maxi", "# iter", airTypeInt, 1, 1, &maxi, "0", "maximum number of iterations to run for, or \"0\" to have " "no limit based on iteration count"); hestOptAdd(&hopt, "fi", "frame inter", airTypeInt, 1, 1, &fi, "0", "the number of iterations between which to save out an 8-bit " "image of the texture, or \"0\" to disable such action"); hestOptAdd(&hopt, "si", "snap inter", airTypeInt, 1, 1, &si, "0", "the number of iterations between which to save out a complete " "floating-point snapshot of the morphogen state, suitable for " "later re-initialization, or \"0\" to disable such action"); hestOptAdd(&hopt, "cfn", NULL, airTypeInt, 0, 0, &cfn, NULL, "when saving out frames or snapshots, use a constant filename, " "instead of incrementing it each save"); hestOptAdd(&hopt, "nt", "# threads", airTypeInt, 1, 1, &nt, "1", (airThreadCapable ? "number of threads to use in computation" : "number of \"threads\" to use in computation, which is " "moot here because this Teem build doesn't support " "multi-threading. ")); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL, "filename for output of final converged (two-channel) texture"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, spotsInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); actx = alanContextNew(); airMopAdd(mop, actx, (airMopper)alanContextNix, airMopAlways); if (nten) { if (alanDimensionSet(actx, nten->dim - 1) || alanTensorSet(actx, nten, 1)) { airMopAdd(mop, err = biffGetDone(ALAN), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting parameters:\n%s\n", me, err); airMopError(mop); return 1; } } else { alanDimensionSet(actx, sizeLen); if (2 == sizeLen) { alan2DSizeSet(actx, size[0], size[1]); } else { alan3DSizeSet(actx, size[0], size[1], size[2]); } } airSrandMT(srnd); if (alanParmSet(actx, alanParmVerbose, 1) || alanParmSet(actx, alanParmTextureType, alanTextureTypeTuring) || alanParmSet(actx, alanParmK, 0.0125) || alanParmSet(actx, alanParmAlpha, alphabeta[0]) || alanParmSet(actx, alanParmBeta, alphabeta[1]) || alanParmSet(actx, alanParmDeltaX, deltaX) || alanParmSet(actx, alanParmDeltaT, deltaT) || alanParmSet(actx, alanParmReact, react) || alanParmSet(actx, alanParmMinAverageChange, mch) || alanParmSet(actx, alanParmMaxPixelChange, xch) || alanParmSet(actx, alanParmMaxIteration, maxi) || alanParmSet(actx, alanParmRandRange, rrange) || alanParmSet(actx, alanParmSaveInterval, si) || alanParmSet(actx, alanParmFrameInterval, fi) || alanParmSet(actx, alanParmConstantFilename, cfn) || alanParmSet(actx, alanParmWrapAround, wrap) || alanParmSet(actx, alanParmHomogAniso, ha) || alanParmSet(actx, alanParmNumThreads, nt)) { airMopAdd(mop, err = biffGetDone(ALAN), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting parameters:\n%s\n", me, err); airMopError(mop); return 1; } if (alanUpdate(actx) || alanInit(actx, ninit, nparm)) { airMopAdd(mop, err = biffGetDone(ALAN), airFree, airMopAlways); fprintf(stderr, "%s: trouble initializing texture: %s\n", me, err); airMopError(mop); return 1; } fprintf(stderr, "%s: going to run (%d threads) ...\n", me, actx->numThreads); time0 = airTime(); if (alanRun(actx)) { airMopAdd(mop, err = biffGetDone(ALAN), airFree, airMopAlways); fprintf(stderr, "%s: trouble generating texture: %s\n", me, err); airMopError(mop); return 1; } time1 = airTime(); fprintf(stderr, "%s: stopped after %d iterations (%g seconds): %s\n", me, actx->iter, time1 - time0, airEnumDesc(alanStop, actx->stop)); if (nrrdSave(outS, actx->nlev, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/bin/gkms.c0000664000175000017500000000763412165631065016270 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #define GKMS "gkms" int main(int argc, const char **argv) { int i, ret; const char *me; char *argv0 = NULL, *err; hestParm *hparm; airArray *mop; me = argv[0]; /* no harm done in making sure we're sane */ if (!nrrdSanity()) { fprintf(stderr, "******************************************\n"); fprintf(stderr, "******************************************\n"); fprintf(stderr, "\n"); fprintf(stderr, " %s: nrrd sanity check FAILED.\n", me); fprintf(stderr, "\n"); fprintf(stderr, " This means that either nrrd can't work on this " "platform, or (more likely)\n"); fprintf(stderr, " there was an error in the compilation options " "and variable definitions\n"); fprintf(stderr, " for Teem.\n"); fprintf(stderr, "\n"); fprintf(stderr, " %s\n", err = biffGetDone(NRRD)); fprintf(stderr, "\n"); fprintf(stderr, "******************************************\n"); fprintf(stderr, "******************************************\n"); free(err); return 1; } mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hparm->elideSingleEnumType = AIR_TRUE; hparm->elideSingleOtherType = AIR_TRUE; hparm->elideSingleOtherDefault = AIR_FALSE; hparm->elideSingleNonExistFloatDefault = AIR_TRUE; hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hparm->elideSingleEmptyStringDefault = AIR_TRUE; hparm->elideMultipleEmptyStringDefault = AIR_TRUE; hparm->cleverPluralizeOtherY = AIR_TRUE; hparm->columns = 78; /* if there are no arguments, then we give general usage information */ if (1 >= argc) { baneGkmsUsage(GKMS, hparm); airMopError(mop); exit(1); } /* else, we should see if they're asking for a command we know about */ /* baneGkmsCmdList[] is NULL-terminated */ for (i=0; baneGkmsCmdList[i]; i++) { if (!strcmp(argv[1], baneGkmsCmdList[i]->name)) break; } if (baneGkmsCmdList[i]) { /* yes, we have that command */ /* initialize variables used by the various commands */ argv0 = AIR_CAST(char*, malloc(strlen(GKMS) + strlen(argv[1]) + 2)); airMopMem(mop, &argv0, airMopAlways); sprintf(argv0, "%s %s", GKMS, argv[1]); /* run the individual unu program, saving its exit status */ ret = baneGkmsCmdList[i]->main(argc-2, argv+2, argv0, hparm); if (1 == ret) { airMopAdd(mop, err=biffGetDone(BANE), airFree, airMopAlways); fprintf(stderr, "%s: error:\n%s", argv0, err); } else if (2 == ret) { /* gkms command has already handled printing error messages */ ret = 1; } } else { fprintf(stderr, "%s: unrecognized command: \"%s\"; type \"%s\" for " "complete list\n", me, argv[1], me); ret = 1; } airMopDone(mop, ret); return ret; } teem-1.11.0~svn6057/src/bin/cubic.c0000664000175000017500000000503112165631065016401 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include char *me; void usage(void) { /* 0 1 2 3 (4) */ fprintf(stderr, "usage: %s \n", me); fprintf(stderr, "for cubic x^3 + Ax^2 + Bx + C == 0\n"); exit(1); } int main(int argc, char **argv) { char buf[512]; double ans0, ans1, ans2, A, B, C; int ret; double r[3]; me = argv[0]; if (argc != 4) { usage(); } sprintf(buf, "%s %s %s", argv[1], argv[2], argv[3]); if (3 != sscanf(buf, "%lf %lf %lf", &A, &B, &C)) { fprintf(stderr, "%s: couldn't parse 3 floats from command line\n", me); exit(1); } ell_debug = AIR_TRUE; ret = ell_cubic(r, A, B, C, AIR_TRUE); ans0 = C + r[0]*(B + r[0]*(A + r[0])); switch(ret) { case ell_cubic_root_single: printf("1 single root: %f -> %f\n", r[0], ans0); break; case ell_cubic_root_triple: printf("1 triple root: %f -> %f\n", r[0], ans0); break; case ell_cubic_root_single_double: ans1 = C + r[1]*(B + r[1]*(A + r[1])); printf("1 single root %f -> %f, 1 double root %f -> %f\n", r[0], ans0, r[1], ans1); break; case ell_cubic_root_three: ans1 = C + r[1]*(B + r[1]*(A + r[1])); ans2 = C + r[2]*(B + r[2]*(A + r[2])); printf("3 distinct roots:\n %f -> %f\n %f -> %f\n %f -> %f\n", r[0], ans0, r[1], ans1, r[2], ans2); break; default: printf("%s: something fatally wacky happened\n", me); exit(1); } exit(0); } teem-1.11.0~svn6057/src/bin/nrrdSanity.c0000664000175000017500000000633712165631065017463 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include int main(int argc, char *argv[]) { char *me, *err; int enc, form; me = argv[0]; if (2 == argc) { if (!strcmp(argv[1], "--version")) { printf("Teem version %s (%s)\n", airTeemVersion, airTeemReleaseDate); exit(0); } else if (!strcmp(argv[1], "--help")) { char par1[] = "\n Usage: nrrdSanity\n "; char par2[] = "\t\t\t\t" "nrrdSanity calls the nrrdSanity() check to verify the correctness " "of all the information (set at compile-time) about the architecture, " "such as endianness, 32/64-bit, and the size of various types, as " "well as running sanity checks on the global default (nrrdDefault*) " "and state (nrrdState*) variables. "; char par3[] = "\t\t\t\t" "As a convenience, nrrdSanity also list the availability of the " "different formats and data encodings (for Nrrd files) supported " "by this build.\n "; _hestPrintStr(stdout, 1, 0, 78, par1, AIR_FALSE); _hestPrintStr(stdout, 1, 0, 78, par2, AIR_FALSE); _hestPrintStr(stdout, 1, 0, 78, par3, AIR_FALSE); exit(0); } else { fprintf(stderr, "%s: unexpected arguments; " "\"%s --help\" for more information\n", me, me); exit(1); } } /* else it was run with no arguments */ if (!nrrdSanity()) { printf("%s: nrrd sanity check FAILED:\n%s\n", me, err = biffGet(NRRD)); free(err); return 1; } else { printf("%s: nrrd sanity check passed.\n", me); printf("\n"); printf("%s: encodings supported in this build:\n", me); for (enc=nrrdEncodingTypeUnknown+1; encavailable() ? "yes" : "not available"); } printf("%s: formats supported in this build:\n", me); for (form=nrrdFormatTypeUnknown+1; formavailable() ? "yes" : "not available"); } } return 0; } teem-1.11.0~svn6057/src/bin/GNUmakefile0000664000175000017500000001463612165631065017235 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Name of the library goes here. #### #### L := bin #### #### We're not a library at all actually ... #### # boilerplate: default targets (except usable) and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### By the ordering and guarding of includes, this will ALWAYS be the #### last "library" makefile to be parsed, regardless of the directory #### in which make started. ## BINS: all the command-line executables associated with teem ## BINS = airSanity cubic nrrdSanity overrgb emap talkweb \ unu pprobe vprobe gprobe qbert mrender miter gkms ungantry ilk \ tend spots ninspect deconv puller ## _binD(bins): list of development locations for given bins ## _binI(bins): list of install locations for given bins ## _binDI(bins): list of install and development locations for given bins ## _binD = $(foreach bin,$(1),$(TEEM_SRC)/bin/$(bin)) _binI = $(foreach bin,$(1),$(BDEST)/$(bin)) _binDI = $(foreach bin,$(1),$(TEEM_SRC)/bin/$(bin) $(BDEST)/$(bin)) ## BINS.{DEV,INST}: full paths to development and install versions ## BINS.DEV := $(call _binD,$(BINS)) BINS.INST := $(call _binI,$(BINS)) ## Entry-point targets for binaries. There is no "usable" for ## binaries, since they aren't libraries. ## bin/dev: $(BINS.DEV) bin/install: $(BINS.INST) bin/clean: $(RM) $(addsuffix $(DOTEXE),$(BINS.DEV)) bin/clobber: bin/clean $(RM) $(addsuffix $(DOTEXE),$(BINS.INST)) ## All the prerequisite usables of the bins, **RECURSIVELY EXPANDED** ## (or not? since things here are depending on meet, and yet not all ## Teem libraries are listed here). The calls to need() will work ## correctly because we've gotten here only after going through all ## the other library makesfiles. The arguments to need() are ## libraries for which we need a header and/or libraries we need to ## link against. ## airSanity.need = $(call meneed,air) cubic.need = $(call meneed,ell) nrrdSanity.need = $(call meneed,nrrd biff) undos.need = $(call meneed,hest air) overrgb.need = $(call meneed,nrrd hest biff air) qbert.need = $(call meneed,bane gage nrrd hest air) ilk.need = $(call meneed,moss unrrdu nrrd ell biff hest air) emap.need = $(call meneed,limn nrrd ell biff hest air) gkms.need = $(call meneed,bane nrrd biff air) ninspect.need = $(call meneed,nrrd biff hest air) unu.need = $(call meneed,unrrdu nrrd biff hest air) miter.need = $(call meneed,mite hoover limn nrrd ell biff air) ungantry.need = $(call meneed,gage nrrd biff hest air) talkweb.need = $(call meneed,nrrd biff hest air) tend.need = $(call meneed,ten limn gage dye unrrdu nrrd ell biff air) mrender.need = $(call meneed,meet ten hoover limn gage nrrd biff hest air) vprobe.need = $(call meneed,meet ten limn gage nrrd ell biff hest air) gprobe.need = $(call meneed,meet ten limn gage nrrd ell biff hest air) deconv.need = $(call meneed,meet ten limn gage nrrd ell biff hest air) pprobe.need = $(call meneed,meet ten limn gage nrrd ell biff hest air) spots.need = $(call meneed,alan nrrd ell biff hest air) puller.need = $(call meneed,meet pull ten gage nrrd ell biff hest air) ## Old: "We need some way of expressing the fact that a given binary ## (install and dev) depends on the usable of its top dependency." ## Problem: Its not sufficient to use the "top dependency" because the ## dependencies are not strict subsets/supersets: mrender's "top" ## dependency is hoover, but hoover doesn't depend on gage, which ## mrender needs ## $(call _binDI,airSanity) : $(call used,$(airSanity.need)) $(call _binDI,cubic) : $(call used,$(cubic.need)) $(call _binDI,nrrdSanity) : $(call used,$(nrrdSanity.need)) $(call _binDI,undos) : $(call used,$(undos.need)) $(call _binDI,overrgb) : $(call used,$(overrgb.need)) $(call _binDI,qbert) : $(call used,$(qbert.need)) $(call _binDI,ilk) : $(call used,$(ilk.need)) $(call _binDI,emap) : $(call used,$(emap.need)) $(call _binDI,vprobe) : $(call used,$(vprobe.need)) $(call _binDI,gprobe) : $(call used,$(gprobe.need)) $(call _binDI,deconv) : $(call used,$(deconv.need)) $(call _binDI,pprobe) : $(call used,$(pprobe.need)) $(call _binDI,gkms) : $(call used,$(gkms.need)) $(call _binDI,ninspect) : $(call used,$(ninspect.need)) $(call _binDI,unu) : $(call used,$(unu.need)) $(call _binDI,mrender) : $(call used,$(mrender.need)) $(call _binDI,miter) : $(call used,$(miter.need)) $(call _binDI,ungantry) : $(call used,$(ungantry.need)) $(call _binDI,talkweb) : $(call used,$(talkweb.need)) $(call _binDI,tend) : $(call used,$(tend.need)) $(call _binDI,spots) : $(call used,$(spots.need)) $(call _binDI,puller) : $(call used,$(puller.need)) ## How to make bins.dev and bins.inst: both kinds link against the ## same (installed) libraries and include the same (installed) ## headers, so the commands aren't actually any different ... ## $(TEEM_SRC)/bin/% $(BDEST)/% : $(TEEM_SRC)/bin/%.c $(P) $(CC) $(CFLAGS) $(BIN_CFLAGS) $(IPATH) -o $@ $< \ $(LPATH) $(call link,$($(notdir $@).need)) \ $(call xtern.Lpath,$($(notdir $@).need)) \ $(call xtern.link,$($(notdir $@).need)) -lm # we're not a library; template.mk doesn't apply to us endif teem-1.11.0~svn6057/src/bin/ninspect.c0000664000175000017500000003005212165631065017140 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include /* for macros only */ #define NINSPECT "ninspect" int fixproj(Nrrd *nproj[3], Nrrd *nvol) { static const char me[]="fixproj"; airArray *mop; Nrrd *ntmp[3], *nt; int sz[3], ii, map[3], h[3], E, mi; size_t rsz[3]; double vec[3][3], dot[3], sp[3], parm[NRRD_KERNEL_PARMS_NUM]; mop = airMopNew(); if (!( ELL_3V_EXISTS(nvol->axis[0].spaceDirection) && ELL_3V_EXISTS(nvol->axis[1].spaceDirection) && ELL_3V_EXISTS(nvol->axis[2].spaceDirection) )) { biffAddf(NINSPECT, "%s: space directions don't exist for all 3 axes", me); airMopError(mop); return 1; } airMopAdd(mop, nt = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp[0] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp[1] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp[2] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); /* RL AP SI */ ELL_3V_SET(vec[0], 1, 0, 0); ELL_3V_SET(vec[1], 0, 1, 0); ELL_3V_SET(vec[2], 0, 0, 1); for (ii=0; ii<3; ii++) { dot[0] = ELL_3V_DOT(vec[ii], nvol->axis[0].spaceDirection); dot[1] = ELL_3V_DOT(vec[ii], nvol->axis[1].spaceDirection); dot[2] = ELL_3V_DOT(vec[ii], nvol->axis[2].spaceDirection); dot[0] = AIR_ABS(dot[0]); dot[1] = AIR_ABS(dot[1]); dot[2] = AIR_ABS(dot[2]); map[ii] = ELL_MAX3_IDX(dot[0], dot[1], dot[2]); } ELL_3V_SET(h, 1, 0, 0); E = 0; for (ii=0; ii<3; ii++) { if (h[map[ii]] != map[h[ii]]) { if (!E) E |= nrrdAxesSwap(ntmp[ii], nproj[map[ii]], 1, 2); } else { if (!E) E |= nrrdCopy(ntmp[ii], nproj[map[ii]]); } } if (E) { biffMovef(NINSPECT, NRRD, "%s: trouble with nrrd operations", me); airMopError(mop); return 1; } E = 0; if (nvol->axis[map[0]].spaceDirection[0] > 0) { if (!E) E |= nrrdFlip(nt, ntmp[1], 0+1); if (!E) E |= nrrdCopy(ntmp[1], nt); if (!E) E |= nrrdFlip(nt, ntmp[2], 0+1); if (!E) E |= nrrdCopy(ntmp[2], nt); } if (nvol->axis[map[1]].spaceDirection[1] > 0) { if (!E) E |= nrrdFlip(nt, ntmp[0], 0+1); if (!E) E |= nrrdCopy(ntmp[0], nt); if (!E) E |= nrrdFlip(nt, ntmp[2], 1+1); if (!E) E |= nrrdCopy(ntmp[2], nt); } if (nvol->axis[map[2]].spaceDirection[2] > 0) { if (!E) E |= nrrdFlip(nt, ntmp[0], 1+1); if (!E) E |= nrrdCopy(ntmp[0], nt); if (!E) E |= nrrdFlip(nt, ntmp[1], 1+1); if (!E) E |= nrrdCopy(ntmp[1], nt); } if (E) { biffMovef(NINSPECT, NRRD, "%s: trouble with nrrd operations", me); airMopError(mop); return 1; } for (ii=0; ii<3; ii++) { sz[ii] = nvol->axis[map[ii]].size; sp[ii] = ELL_3V_LEN(nvol->axis[map[ii]].spaceDirection); } mi = ELL_MIN3_IDX(sp[0], sp[1], sp[2]); sz[0] = (int)(sz[0]*sp[0]/sp[mi]); sz[1] = (int)(sz[1]*sp[1]/sp[mi]); sz[2] = (int)(sz[2]*sp[2]/sp[mi]); parm[0] = 1; ELL_3V_SET(rsz, 3, sz[1], sz[2]); nrrdSimpleResample(nproj[0], ntmp[0], nrrdKernelBox, parm, rsz, NULL); ELL_3V_SET(rsz, 3, sz[0], sz[2]); nrrdSimpleResample(nproj[1], ntmp[1], nrrdKernelBox, parm, rsz, NULL); ELL_3V_SET(rsz, 3, sz[0], sz[1]); nrrdSimpleResample(nproj[2], ntmp[2], nrrdKernelBox, parm, rsz, NULL); airMopOkay(mop); return 0; } int ninspect_proj(Nrrd *nout, Nrrd *nin, int axis, int smart, float amount) { static const char me[]="ninspect_proj"; airArray *mop; Nrrd *ntmpA, *ntmpB, **nrgb; int bins; if (!(nout && nin)) { biffAddf(NINSPECT, "%s: got NULL pointer", me); return 1; } if (!( AIR_IN_CL(0, axis, 2) )) { biffAddf(NINSPECT, "%s: given axis %d outside valid range [0,1,2]", me, axis); return 1; } /* allocate a bunch of nrrds to use as basically temp variables */ mop = airMopNew(); airMopAdd(mop, ntmpA = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmpB = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); /* HEY: this used to be nrgb[3], but its passing to nrrdJoin caused "dereferencing type-punned pointer might break strict-aliasing rules" warning; GLK not sure how else to fix it */ nrgb = AIR_CALLOC(3, Nrrd*); airMopAdd(mop, nrgb, airFree, airMopAlways); airMopAdd(mop, nrgb[0] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nrgb[1] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nrgb[2] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); /* these arguments to nrrdHistoEq will control its behavior */ bins = 3000; /* equalization will use a histogram with this many bins */ /* the following idiom is one way of handling the fact that any non-trivial nrrd call can fail, and if it does, then any subsequent nrrd calls should be avoided (to be perfectly safe), so that you can get the error message from biff. Because of the left-to-right ordering ensured for logical expressions, this will all be called in sequence until one of them has a non-zero return. If he had exception handling, we'd put all the nrrd calls in one "try" block. */ if (nrrdProject(ntmpA, nin, axis, nrrdMeasureSum, nrrdTypeDefault) || nrrdHistoEq(ntmpB, ntmpA, NULL, bins, smart, amount) || nrrdQuantize(nrgb[0], ntmpB, NULL, 8) || nrrdProject(ntmpA, nin, axis, nrrdMeasureVariance, nrrdTypeDefault) || nrrdHistoEq(ntmpB, ntmpA, NULL, bins, smart, amount) || nrrdQuantize(nrgb[1], ntmpB, NULL, 8) || nrrdProject(ntmpA, nin, axis, nrrdMeasureMax, nrrdTypeDefault) || nrrdQuantize(nrgb[2], ntmpA, NULL, 8) || nrrdJoin(nout, (const Nrrd*const*)nrgb, 3, 0, AIR_TRUE)) { biffMovef(NINSPECT, NRRD, "%s: trouble with nrrd operations", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } int doit(Nrrd *nout, Nrrd *nin, int smart, float amount) { static const char me[]="doit"; Nrrd *nproj[3]; airArray *mop; int axis, srl, sap, ssi, E, margin, which; size_t min[3]; if (!(nout && nin)) { biffAddf(NINSPECT, "%s: got NULL pointer", me); return 1; } if (!(3 == nin->dim)) { biffAddf(NINSPECT, "%s: given nrrd has dimension %d, not 3\n", me, nin->dim); return 1; } mop = airMopNew(); airMopAdd(mop, nproj[0]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nproj[1]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nproj[2]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); /* how much space to put between and around the projections */ margin = 6; /* do projections for each axis, with some progress indication to sterr */ for (axis=0; axis<=2; axis++) { fprintf(stderr, "%s: doing axis %d projections ... ", me, axis); fflush(stderr); if (ninspect_proj(nproj[axis], nin, axis, smart, amount)) { fprintf(stderr, "ERROR\n"); biffAddf(NINSPECT, "%s: trouble doing projections for axis %d", me, axis); airMopError(mop); return 1; } fprintf(stderr, "done\n"); } if (nrrdSpaceRightAnteriorSuperior == nin->space) { if (fixproj(nproj, nin)) { fprintf(stderr, "ERROR\n"); biffAddf(NINSPECT, "%s: trouble orienting projections", me); airMopError(mop); return 1; } } srl = nproj[1]->axis[0+1].size; sap = nproj[0]->axis[0+1].size; ssi = nproj[1]->axis[1+1].size; /* allocate output as 8-bit color image. We know output type is nrrdTypeUChar because ninspect_proj finishes each projection with nrrdQuantize to 8-bits */ if (nrrdMaybeAlloc_va(nout, nrrdTypeUChar, 3, AIR_CAST(size_t, 3), AIR_CAST(size_t, srl + 3*margin + sap), AIR_CAST(size_t, ssi + 3*margin + sap))) { biffMovef(NINSPECT, NRRD, "%s: couldn't allocate output", me); airMopError(mop); return 1; } min[0] = 0; E = 0; which = 0; if (!E) { min[1] = margin; min[2] = margin; which = 1; } if (!E) E |= nrrdInset(nout, nout, nproj[1], min); if (!E) { min[1] = margin; min[2] = 2*margin + ssi; which = 2; } if (!E) E |= nrrdInset(nout, nout, nproj[2], min); if (!E) { min[1] = 2*margin + srl; min[2] = margin; which = 3; } if (!E) E |= nrrdInset(nout, nout, nproj[0], min); if (E) { biffAddf(NINSPECT, NRRD, "%s: couldn't composite output (which = %d)", me, which); airMopError(mop); return 1; } airMopOkay(mop); return 0; } void ninspect_usage(void) { fprintf(stderr, "\nusage: %s \n\n", NINSPECT); fprintf(stderr, ": must be a 3-D array in NRRD or " "NRRD-compatible format.\n"); fprintf(stderr, ": will be saved out in whatever format " "is implied by the\n"); fprintf(stderr, " extension (if recognized), or else in NRRD format\n"); } static const char *info = ("Quick way of seeing what's inside a 3D volume. A color image " "of three axis-aligned projections is composed of histogram-" "equalized and quantized images of the summation (red), " "variance (green), and maximum (blue) intensity projections. " "If volume is orientation in RAS space, then a standard " "orientation is used for projections and projections are " "upsampled (with box kernel) to have isotropic pixels."); int main(int argc, const char *argv[]) { hestOpt *hopt=NULL; airArray *mop; const char *me; char *outS, *err; Nrrd *nin, *nout; NrrdIoState *nio; float heqamount; me = argv[0]; mop = airMopNew(); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input nrrd to project. Must be three dimensional.", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "amt", "heq", airTypeFloat, 1, 1, &heqamount, "0.5", "how much to apply histogram equalization to projection images"); hestOptAdd(&hopt, "o", "img out", airTypeString, 1, 1, &outS, NULL, "output image to save to. Will try to use whatever " "format is implied by extension, but will fall back to PPM."); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); nrrdStateDisableContent = AIR_TRUE; if (doit(nout, nin, 1, heqamount)) { err=biffGetDone(NINSPECT); airMopAdd(mop, err, airFree, airMopAlways); fprintf(stderr, "%s: trouble creating output:\n%s", me, err); airMopError(mop); return 1; } if (nrrdFormatPNG->nameLooksLike(outS) && !nrrdFormatPNG->available()) { fprintf(stderr, "(%s: using PPM format for output)\n", me); nio->format = nrrdFormatPNM; } if (nrrdSave(outS, nout, nio)) { err=biffGetDone(NRRD); airMopAdd(mop, err, airFree, airMopAlways); fprintf(stderr, "%s: trouble saving output image \"%s\":\n%s", me, outS, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/bin/deconv.c0000664000175000017500000001107212165631065016574 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #define SPACING(spc) (AIR_EXISTS(spc) ? spc: nrrdDefaultSpacing) /* copied this from ten.h; I don't want gage to depend on ten */ #define PROBE_MAT2LIST(l, m) ( \ (l)[1] = (m)[0], \ (l)[2] = (m)[3], \ (l)[3] = (m)[6], \ (l)[4] = (m)[4], \ (l)[5] = (m)[7], \ (l)[6] = (m)[8] ) static const char *deconvInfo = ("Does deconvolution. "); int main(int argc, const char *argv[]) { gageKind *kind; const char *me; char *outS, *err; hestParm *hparm; hestOpt *hopt = NULL; NrrdKernelSpec *ksp; int otype, separ, ret; unsigned int maxIter; double epsilon, lastDiff, step; Nrrd *nin, *nout; airArray *mop; mop = airMopNew(); me = argv[0]; hparm = hestParmNew(); airMopAdd(mop, hparm, AIR_CAST(airMopper, hestParmFree), airMopAlways); hparm->elideSingleOtherType = AIR_TRUE; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL, "\"kind\" of volume (\"scalar\", \"vector\", " "\"tensor\", or \"dwi\")", NULL, NULL, meetHestGageKind); hestOptAdd(&hopt, "k00", "kernel", airTypeOther, 1, 1, &ksp, NULL, "convolution kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "mi", "max # iters", airTypeUInt, 1, 1, &maxIter, "100", "maximum number of iterations with which to compute the " "deconvolution"); hestOptAdd(&hopt, "e", "epsilon", airTypeDouble, 1, 1, &epsilon, "0.00000001", "convergence threshold"); hestOptAdd(&hopt, "s", "step", airTypeDouble, 1, 1, &step, "1.0", "scaling of value update"); hestOptAdd(&hopt, "t", "type", airTypeOther, 1, 1, &otype, "default", "type to save output as. By default (not using this option), " "the output type is the same as the input type", NULL, NULL, &unrrduHestMaybeTypeCB); hestOptAdd(&hopt, "sep", "bool", airTypeBool, 1, 1, &separ, "false", "use fast separable deconvolution instead of brain-dead " "brute-force iterative method"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output volume"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, deconvInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, AIR_CAST(airMopper, hestOptFree), airMopAlways); airMopAdd(mop, hopt, AIR_CAST(airMopper, hestParseFree), airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, AIR_CAST(airMopper, nrrdNuke), airMopAlways); if (separ) { ret = gageDeconvolveSeparable(nout, nin, kind, ksp, otype); } else { ret = gageDeconvolve(nout, &lastDiff, nin, kind, ksp, otype, maxIter, AIR_TRUE, step, epsilon, 1); } if (ret) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/bin/puller.c0000664000175000017500000017267112202515664016634 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* ** NOTE: all the "#ifdef DEFT" directives refer to an FLTK2-based GUI ** for some parts of Teem called "Deft". Unfortunately FLTK2 has been ** abandoned, and Deft is not released or supported in any way. The ** Deft-related code is preserved for legacy purposes. */ #ifdef DEFT #include "Deft.h" #include "Contour.h" #include "Viewer.h" #include "ViewerUI.h" #include "TensorGlyph.h" #include "TensorGlyphUI.h" #include "Slider.h" #include "TriPlane.h" #include "TriPlaneUI.h" #endif #include #include static const char *info = ("Command-line interface to the \"pull\" library. " "Published research using this tool or the \"pull\" library " "should cite the paper: \n " "\t\tGordon L. Kindlmann, Ra{\\'u}l San Jos{\\'e} Est{\\'e}par, Stephen M. Smith,\n " "\t\tCarl-Fredrik Westin. Sampling and Visualizing Creases with Scale-Space\n " "\t\tParticles. IEEE Trans. on Visualization and Computer Graphics,\n " "\t\t15(6):1415-1424 (2009)."); #ifdef DEFT typedef struct { fltk::FloatInput *scaleVecInput[3]; fltk::ValueInput *glyphScaleRadInput; Deft::Slider *isoval; Deft::Slider *strength; Deft::Slider *quality; Deft::Slider *alpha, *beta, *cwell, *gamma; /* Deft::Slider *height; */ Deft::Slider *ccSelect, *rho, *sclMean, *sclWind; fltk::CheckButton *ccSingle; Deft::Contour *contour; Deft::Scene *scene; Deft::Viewer *viewer; Deft::TensorGlyph *glyph, *hedge; fltk::IntInput *iters; fltk::FloatInput *radius; fltk::ValueInput *verbose; pullContext *pctx; Nrrd *nPosOut, *nTenOut, *nFrcOut, *nten, *ntmp, *nenr, *nscl, *nidcc, *nstrn, *nqual, *ncovar, *ntcovar, *nstab, *nintern, *nstuck, *nfrcOld, *nfrcNew, *nposOld, *nposNew, *nrgb, *nccrgb, *ncval, *ncmap, *ncmapOut, *nblur; NrrdResampleContext *rsmc; const Nrrd *norig; NrrdRange *cvalRange; limnPolyData *phistLine, *phistTube; Deft::PolyData *phistSurf; double icvalr[2], sclMin, sclMax, strnMin, qualMin, scaleVec[3], glyphScaleRad, energyIncreasePermitFrac; } pullBag; void verbose_cb(fltk::Widget *widget, pullBag *bag) { fltk::ValueInput *val; val = (fltk::ValueInput *)widget; pullVerboseSet(bag->pctx, (int)val->value()); } void isovalue_cb(fltk::Widget *widget, pullBag *bag) { Deft::Slider *slider; slider = (Deft::Slider *)widget; if (bag->contour) { bag->contour->extract(slider->value()); } bag->viewer->redraw(); } void outputGet(pullBag *bag) { char me[]="outputGet", *err; size_t cropMin[2], cropMax[2]; if (pullOutputGet(bag->nPosOut, bag->nTenOut, bag->nstrn, /* may be NULL */ bag->scaleVec, bag->glyphScaleRad, bag->pctx) || pullPropGet(bag->nscl, pullPropScale, bag->pctx) || (bag->pctx->ispec[pullInfoQuality] ? pullInfoGet(bag->nqual, pullInfoQuality, bag->pctx) : 0) || pullPropGet(bag->nenr, pullPropEnergy, bag->pctx) || pullPropGet(bag->nidcc, pullPropIdCC, bag->pctx) || pullPropGet(bag->nstuck, pullPropStuck, bag->pctx) || pullPropGet(bag->ncovar, pullPropNeighCovar7Ten, bag->pctx) #if PULL_TANCOVAR || pullPropGet(bag->ntcovar, pullPropNeighTanCovar, bag->pctx) #endif || pullPropGet(bag->nstab, pullPropStability, bag->pctx) || pullPropGet(bag->nintern, pullPropNeighInterNum, bag->pctx) || pullPropGet(bag->nFrcOut, pullPropForce, bag->pctx) || (pullPhistEnabled ? pullPositionHistoryGet(bag->phistLine, bag->pctx) : 0)) { err = biffGetDone(PULL); fprintf(stderr, "%s: error getting pull output:\n%s\n", me, err); free(err); exit(1); } cropMin[0] = 0; cropMin[1] = 0; cropMax[0] = 2; cropMax[1] = bag->nPosOut->axis[1].size-1; if ((!bag->pctx->iter ? 0 : (nrrdCopy(bag->nfrcOld, bag->nfrcNew) || nrrdCopy(bag->nposOld, bag->nposNew))) || nrrdConvert(bag->nten, bag->nTenOut, nrrdTypeFloat) /* hacks to visualize the (tan) covariance tensors || nrrdCopy(bag->nten, bag->ncovar) || nrrdCopy(bag->nten, bag->ntcovar) */ || nrrdCrop(bag->ntmp, bag->nPosOut, cropMin, cropMax) || nrrdConvert(bag->nposNew, bag->ntmp, nrrdTypeFloat) || nrrdCrop(bag->ntmp, bag->nFrcOut, cropMin, cropMax) || nrrdConvert(bag->nfrcNew, bag->ntmp, nrrdTypeFloat) || (!bag->pctx->iter ? (nrrdCopy(bag->nfrcOld, bag->nfrcNew) || nrrdCopy(bag->nposOld, bag->nposNew)) : 0)) { err = biffGetDone(NRRD); fprintf(stderr, "%s: another error 0\n%s\n", me, err); free(err); exit(1); } } void outputShow(pullBag *bag) { char me[]="outputShow", *err; float *rgb; unsigned int ii, nn, *idcc; unsigned char *stuck; int first; double *cval, emean, estdv, *pos; /* if (limnPolyDataSpiralTubeWrap(bag->phistTube, bag->phistLine, (1 << limnPolyDataInfoRGBA) | (1 << limnPolyDataInfoNorm), NULL, 8, 8, bag->glyph->glyphScale()/5)) { err = biffGetDone(LIMN); fprintf(stderr, "%s: another error 1\n%s\n", me, err); free(err); exit(1); } */ if (pullPhistEnabled) { bag->phistSurf->changed(); } bag->ncval = bag->nenr; /* bag->ncval = bag->nstrn; */ /* bag->ncval = bag->nstuck; */ /* bag->ncval = bag->nscl; */ if (bag->ncval) { nrrdRangeSet(bag->cvalRange, bag->ncval, AIR_FALSE); } else { bag->cvalRange->min = AIR_NAN; bag->cvalRange->max = AIR_NAN; } if (bag->ncval) { cval = AIR_CAST(double *, bag->ncval->data); } else { cval = NULL; } if (cval) { nn = bag->ncval->axis[0].size; emean = 0; for (ii=0; iicvalRange->hasNonExist) { fprintf(stderr, "!%s: cval range %g -- %g (%s), mean %g, stdv %g\n", me, bag->cvalRange->min, bag->cvalRange->max, bag->cvalRange->hasNonExist ? "HAS non-exist" : "no non-exist", emean, estdv); } bag->cvalRange->min = AIR_LERP(0.7, bag->cvalRange->min, emean - 2*estdv); bag->cvalRange->max = AIR_LERP(0.7, bag->cvalRange->max, emean + 2*estdv); } float *cmapOut; if (bag->ncmap && bag->ncval && AIR_EXISTS(bag->cvalRange->min) && AIR_EXISTS(bag->cvalRange->max)) { /* double mmin, mmax; */ fprintf(stderr, "!%s: cval cmap range %g %g ----------- \n", me, bag->cvalRange->min, bag->cvalRange->max); /* mmin = -0.0342937; mmax = -0.0105725; */ /* */ /* bag->cvalRange->min = AIR_LERP(0.05, mmin, mmax); bag->cvalRange->max = AIR_LERP(0.3, mmin, mmax); */ /* */ if (nrrdApply1DRegMap(bag->ncmapOut, bag->ncval, bag->cvalRange, bag->ncmap, nrrdTypeFloat, AIR_TRUE)) { err = biffGetDone(NRRD); fprintf(stderr, "%s: cmap error\n%s\n", me, err); free(err); exit(1); } cmapOut = AIR_CAST(float *, bag->ncmapOut->data); } else { cmapOut = NULL; } idcc = AIR_CAST(unsigned int *, bag->nidcc->data); stuck = AIR_CAST(unsigned char *, bag->nstuck->data); nn = bag->nPosOut->axis[1].size; pos = AIR_CAST(double *, bag->nPosOut->data); first = bag->nrgb->axis[1].size != bag->nPosOut->axis[1].size; /* fprintf(stderr, "!%s: %u %u -> %d\n", me, AIR_UINT(bag->nrgb->axis[1].size), AIR_UINT(bag->nPosOut->axis[1].size), first); */ if (first) { if (nrrdMaybeAlloc_va(bag->nrgb, nrrdTypeFloat, 2, AIR_CAST(size_t, 3), bag->nPosOut->axis[1].size)) { err = biffGetDone(NRRD); fprintf(stderr, "%s: error creating RGB:\n%s\n", me, err); free(err); exit(1); } } bag->icvalr[0] = bag->cvalRange->min; bag->icvalr[1] = bag->cvalRange->max; double *strnOut; strnOut = (bag->nstrn ? AIR_CAST(double *, bag->nstrn->data) : NULL); double *qualOut; qualOut = (bag->nqual ? AIR_CAST(double *, bag->nqual->data) : NULL); rgb = (float*)bag->nrgb->data; for (ii=0; iinccrgb->data; /* ee = bag->cvalRange->min - (bag->icvalr[1] - bag->icvalr[0])/50;*/ ee = bag->cvalRange->min; if (bag->pctx->CCNum && ccrgb) { rgb[0 + 3*ii] = ccrgb[0 + 3*idcc[ii]]; rgb[1 + 3*ii] = ccrgb[1 + 3*idcc[ii]]; rgb[2 + 3*ii] = ccrgb[2 + 3*idcc[ii]]; } else if (cmapOut) { ELL_3V_COPY(rgb + 3*ii, cmapOut + 3*ii); } else { ELL_3V_SET(rgb + 3*ii, 0.95, 0.95, 0.95); /* if (AIR_EXISTS(cval[ii])) { rgb[1 + 3*ii] = AIR_AFFINE(ee, cval[ii], bag->cvalRange->max, 0, 1); rgb[1 + 3*ii] = sqrt(rgb[1 + 3*ii]); rgb[0 + 3*ii] = rgb[1 + 3*ii]; } else { rgb[1 + 3*ii] = 0; rgb[0 + 3*ii] = 1; } rgb[2 + 3*ii] = stuck[ii]; */ } } if (1) { float *ten, *pos; double *posOut; ten = AIR_CAST(float *, bag->nten->data); posOut = AIR_CAST(double *, bag->nPosOut->data); pos = AIR_CAST(float *, bag->nposNew->data); for (ii=0; iisclMin-0.00001, posOut[3], bag->sclMax+0.00001) )) { ten[0] = 0; } else if (strnOut && strnOut[ii] < bag->strnMin) { ten[0] = 0; } else if (qualOut && qualOut[ii] < bag->qualMin-0.000001) { ten[0] = 0; } else if (bag->pctx->CCNum && (bag->ccSingle->value() ? idcc[ii] != bag->ccSelect->value() : idcc[ii] > bag->ccSelect->value())) { ten[0] = 0; } else { ten[0] = 1; } /* if (4589 == ii) { fprintf(stderr, "!%s: point %u/%u at (%g,%g,%g)=(%g,%g,%g,%g) got ten[0] %g\n", me, ii, nn, pos[0], pos[1], pos[1], posOut[0], posOut[1], posOut[2], posOut[3], ten[0]); } */ pos += 3; posOut += 4; ten += 7; } } if (bag->nPosOut->axis[1].size) { bag->glyph->dataSet(bag->nPosOut->axis[1].size, (float*)bag->nten->data, 7, (float*)bag->nposNew->data, 3, rgb, 3, NULL); bag->glyph->update(); /* bag->hedge->dataSet(bag->nPosOut->axis[1].size, (float*)bag->nfrcNew->data, 3, (float*)bag->nposOld->data, 3, rgb, 3, NULL); bag->hedge->update(); */ bag->viewer->redraw(); fltk::flush(); } else { fprintf(stderr, "!%s: got zero tensors out!\n", me); } return; } void iter_cb(void *_bag) { pullBag *bag; bag = AIR_CAST(pullBag *, _bag); outputGet(bag); outputShow(bag); } void step_cb(fltk::Widget *, pullBag *bag) { /* static double lastthresh = -42; */ char me[]="step_cb", *err; static unsigned int itersTotal=0; unsigned int iters = bag->iters->ivalue(); bag->pctx->iterParm.max += iters; if (pullRun(bag->pctx)) { err = biffGetDone(PULL); fprintf(stderr, "%s: error running pull:\n%s\n", me, err); free(err); exit(1); } itersTotal += iters; fprintf(stderr, "!%s: enr = %g; time = %g sec; %u iters (%g iters/sec)\n", me, bag->pctx->energy, bag->pctx->timeRun, itersTotal, itersTotal/bag->pctx->timeRun); outputGet(bag); outputShow(bag); for (unsigned int ci=pullCountUnknown+1; cipctx->count[ci]) { fprintf(stderr, " %u: %s\n", bag->pctx->count[ci], airEnumStr(pullCount, ci)); } } } void gammaSet_cb(fltk::Widget *, pullBag *bag) { char me[]="gammaSet_cb"; if (pullGammaLearn(bag->pctx)) { char *err = biffGetDone(PULL); fprintf(stderr, "%s: problem learning gamma:\n%s", me, err); free(err); } if (bag->pctx->sysParm.gamma > bag->gamma->maximum()) { bag->gamma->maximum(2*bag->pctx->sysParm.gamma); } bag->gamma->value(bag->pctx->sysParm.gamma); } void cc_cb(fltk::Widget *, pullBag *bag) { char me[]="cc_cb"; unsigned int cc; float *rgb; if (pullCCFind(bag->pctx) || pullCCSort(bag->pctx, (bag->pctx->ispec[pullInfoQuality] ? pullInfoQuality : 0), bag->rho->value())) { char *err = biffGetDone(PULL); fprintf(stderr, "%s: problem finding/sorting CCs:\n%s", me, err); free(err); } printf("%s: found %u CCs\n", me, bag->pctx->CCNum); bag->ccSelect->range(0, bag->pctx->CCNum-1); if (bag->nccrgb->axis[1].size != bag->pctx->CCNum) { airSrandMT(AIR_UINT(airTime())); if (nrrdMaybeAlloc_va(bag->nccrgb, nrrdTypeFloat, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, bag->pctx->CCNum))) { char *err = biffGetDone(NRRD); fprintf(stderr, "%s: problem alloc'ing cc rgb:\n%s", me, err); free(err); } rgb = (float*)bag->nccrgb->data; ELL_3V_SET(rgb + 0*3, 0.95, 0.95, 0.95); for (cc=0; ccpctx->CCNum; cc++) { rgb[0 + 3*cc] = AIR_AFFINE(0, airDrandMT(), 1, 0.3, 1.0); rgb[1 + 3*cc] = AIR_AFFINE(0, airDrandMT(), 1, 0.3, 1.0); rgb[2 + 3*cc] = AIR_AFFINE(0, airDrandMT(), 1, 0.3, 1.0); } } outputGet(bag); outputShow(bag); } void ccSelect_cb(fltk::Widget *, pullBag *bag) { outputShow(bag); } void scaleGlyph_cb(fltk::Widget *, pullBag *bag) { bag->scaleVec[0] = bag->scaleVecInput[0]->fvalue(); bag->scaleVec[1] = bag->scaleVecInput[1]->fvalue(); bag->scaleVec[2] = bag->scaleVecInput[2]->fvalue(); bag->glyphScaleRad = bag->glyphScaleRadInput->value(); outputGet(bag); outputShow(bag); } void reblur_cb(fltk::Widget *, pullBag *bag) { static const char me[]="reblur_cb"; double kparm[NRRD_KERNEL_PARMS_NUM], scl; int E; if (!bag->pctx->haveScale) { return; } scl = bag->sclMean->value(); if (bag->pctx->flag.scaleIsTau) { kparm[0] = gageSigOfTau(scl); printf("!%s: tau = %g ---> sigma = %g\n", me, scl, kparm[0]); } else { kparm[0] = scl; printf("!%s: sigma = %g\n", me, kparm[0]); } kparm[1] = 3; E = 0; for (unsigned int axi=0; axi<3; axi++) { if (!E) E |= nrrdResampleKernelSet(bag->rsmc, axi, nrrdKernelDiscreteGaussian, kparm); } if (!E) E |= nrrdResampleExecute(bag->rsmc, bag->nblur); if (E) { char *err = biffGetDone(NRRD); fprintf(stderr, "%s: problem resampling to scale %g:\n%s", me, bag->sclMean->value(), err); free(err); } outputShow(bag); return; } void scale_cb(fltk::Widget *, pullBag *bag) { double sclMean, sclWind; if (bag->pctx->haveScale) { sclMean = bag->sclMean->value(); sclWind = bag->sclWind->value(); bag->sclMin = sclMean - sclWind/2; bag->sclMax = sclMean + sclWind/2; } else { bag->sclMin = 0; bag->sclMax = 0; } outputShow(bag); return; } void alpha_cb(fltk::Widget *, pullBag *bag) { pullSysParmSet(bag->pctx, pullSysParmAlpha, bag->alpha->value()); } void beta_cb(fltk::Widget *, pullBag *bag) { pullSysParmSet(bag->pctx, pullSysParmBeta, bag->beta->value()); } void cwell_cb(fltk::Widget *, pullBag *bag) { double *parm; parm = bag->pctx->energySpecR->parm; parm[1] = bag->cwell->value(); pullSysParmSet(bag->pctx, pullSysParmEnergyIncreasePermit, bag->energyIncreasePermitFrac*bag->cwell->value()); { unsigned int ii, nn; double xx, yy, de; FILE *file; if ((file = fopen("eplot.txt", "w"))) { nn = 800; for (ii=0; iipctx->energySpecR->energy->eval(&de, xx, parm); fprintf(file, "%f %f\n", xx, yy); } fclose(file); } } } void gamma_cb(fltk::Widget *, pullBag *bag) { pullSysParmSet(bag->pctx, pullSysParmGamma, bag->gamma->value()); } void strength_cb(fltk::Widget *, pullBag *bag) { bag->strnMin = bag->strength->value(); outputShow(bag); } void quality_cb(fltk::Widget *, pullBag *bag) { bag->qualMin = bag->quality->value(); outputShow(bag); } void save_cb(fltk::Widget *, pullBag *bag) { static const char me[]="save_cb"; unsigned int ii, nn, count; float *ten; Nrrd *nPosSel, *nStrnSel; double *posSel, *posAll, *strnSel, *strnAll; if (!( 0.0 == ELL_3V_LEN(bag->scaleVec) )) { fprintf(stderr, "%s: refusing to save with non-zero scaleVec %g %g %g\n", me, bag->scaleVec[0], bag->scaleVec[1], bag->scaleVec[2]); return; } nPosSel = nrrdNew(); if (bag->nstrn) { nStrnSel = nrrdNew(); } else { nStrnSel = NULL; } outputGet(bag); outputShow(bag); /* will exploit its masking of ten[0] */ count = 0; nn = bag->nPosOut->axis[1].size; ten = AIR_CAST(float *, bag->nten->data); for (ii=0; iinPosOut->data); posSel = AIR_CAST(double *, nPosSel->data); if (bag->nstrn) { nrrdMaybeAlloc_va(nStrnSel, nrrdTypeDouble, 1, AIR_CAST(size_t, count)); strnAll = AIR_CAST(double *, bag->nstrn->data); strnSel = AIR_CAST(double *, nStrnSel->data); } else { strnSel = NULL; strnAll = NULL; } ten = AIR_CAST(float *, bag->nten->data); count = 0; for (ii=0; iinPosOut, NULL); if (0) { nrrdSave("pos-sel.nrrd", nPosSel, NULL); } else { char fname[512]; FILE *ff; unsigned int ii=0; for (ii=0, ff=NULL; AIR_TRUE; ii++) { ff = airFclose(ff); sprintf(fname, "pos-sel-%03u.nrrd", ii); if (!(ff = fopen(fname, "rb"))) { break; } } ff = airFclose(ff); nrrdSave(fname, nPosSel, NULL); } nrrdSave("covar-all.nrrd", bag->ncovar, NULL); #if PULL_TANCOVAR nrrdSave("tcovar-all.nrrd", bag->ntcovar, NULL); #endif nrrdSave("stab-all.nrrd", bag->nstab, NULL); nrrdSave("intern-all.nrrd", bag->nintern, NULL); if (bag->nstrn) { nrrdSave("strn-all.nrrd", bag->nstrn, NULL); nrrdSave("strn-sel.nrrd", nStrnSel, NULL); } nrrdNuke(nPosSel); if (bag->nstrn) { nrrdNuke(nStrnSel); } return; } #endif int main(int argc, const char **argv) { hestOpt *hopt=NULL; hestParm *hparm; airArray *mop; const char *me; #ifdef DEFT float fr[3], at[3], up[3], fovy, neer, faar, dist, bg[3]; int imgSize[2], ortho, rght, atrel, camkeep; float anisoThresh, anisoThreshMin, glyphScale, haloTraceBound, glyphHaloWidth, glyphNormPow, glyphEvalPow, sqdSharp; int glyphType, glyphFacetRes, aniso; pullBag bag; #endif char *err, *outS, *extraOutBaseS, *addLogS, *cachePathSS; FILE *addLog; meetPullVol **vspec; meetPullInfo **idef; Nrrd *nPosIn=NULL, *nPosOut; pullEnergySpec *enspR, *enspS, *enspWin; NrrdKernelSpec *k00, *k11, *k22, *kSSrecon, *kSSblur; pullContext *pctx; int E=0, ret=0; unsigned int vsi, vspecNum, idefNum; double ssrange[2], scaleVec[3], glyphScaleRad; /* things that used to be set directly inside pullContext */ int energyFromStrength, nixAtVolumeEdgeSpace, constraintBeforeSeedThresh, binSingle, liveThresholdOnInit, permuteOnRebin, noPopCntlWithZeroAlpha, useBetaForGammaLearn, restrictiveAddToBins, noAdd, unequalShapesAllow, popCntlEnoughTest, convergenceIgnoresPopCntl, zeroZ, oneDim; int verbose; int interType, allowCodimension3Constraints, scaleIsTau, useHalton, pointPerVoxel; unsigned int samplesAlongScaleNum, pointNumInitial, ppvZRange[2], snap, iterMax, stuckIterMax, constraintIterMax, popCntlPeriod, addDescent, iterCallback, rngSeed, progressBinMod, threadNum, eipHalfLife; double jitter, stepInitial, constraintStepMin, radiusSpace, binWidthSpace, radiusScale, alpha, beta, gamma, theta, wall, energyIncreasePermit, backStepScale, opporStepScale, energyDecreaseMin, energyDecreasePopCntlMin, neighborTrueProb, probeProb, fracNeighNixedMax; gageStackBlurParm *sbp; mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); nPosOut = nrrdNew(); airMopAdd(mop, nPosOut, (airMopper)nrrdNuke, airMopAlways); hparm->respFileEnable = AIR_TRUE; me = argv[0]; #ifdef DEFT unsigned int baryRes; int saveAndQuit, fog; hestOptAdd(&hopt, "csqvmm", "min max", airTypeDouble, 2, 2, Deft::colorSclQuantityValueMinMax, "nan nan", "min/max values for cutting planes of scalar values"); hestOptAdd(&hopt, "saq", "save & quit", airTypeInt, 0, 0, &saveAndQuit, NULL, "save image and quit, for batch processing"); hestOptAdd(&hopt, "cmap", "nin", airTypeOther, 1, 1, &(bag.ncmap), "", "colormap for particles", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "fr", "from point", airTypeFloat, 3, 3, fr, "3 4 5", "position of camera, used to determine view vector"); hestOptAdd(&hopt, "at", "at point", airTypeFloat, 3, 3, at, "0 0 0", "camera look-at point, used to determine view vector"); hestOptAdd(&hopt, "up", "up vector", airTypeFloat, 3, 3, up, "0 0 1", "camera pseudo-up vector, used to determine view coordinates"); hestOptAdd(&hopt, "rh", NULL, airTypeInt, 0, 0, &rght, NULL, "normally, use a right-handed UVN frame (V points down), " "but in Deft this is always true"); hestOptAdd(&hopt, "fv", "fov", airTypeFloat, 1, 1, &fovy, "20", "vertical field-of-view, in degrees"); hestOptAdd(&hopt, "or", NULL, airTypeInt, 0, 0, &ortho, NULL, "use orthogonal projection instead of perspective"); hestOptAdd(&hopt, "dn", "near clip", airTypeFloat, 1, 1, &neer, "-2", "position of near clipping plane, relative to look-at point"); hestOptAdd(&hopt, "di", "image", airTypeFloat, 1, 1, &dist, "0.0", "normally, distance to image plane, " "but in Deft this is always 0.0"); hestOptAdd(&hopt, "df", "far clip", airTypeFloat, 1, 1, &faar, "2", "position of far clipping plane, relative to look-at point"); hestOptAdd(&hopt, "ar", NULL, airTypeInt, 0, 0, &atrel, NULL, "normally: near, image, and far plane distances are relative to " "the *at* point, instead of the eye point, " "but for Deft, this is always true"); hestOptAdd(&hopt, "usecam", NULL, airTypeInt, 0, 0, &camkeep, NULL, "hack: by default, a camera reset is done to put the volume " "in view. Use this to say that the camera specified by the " "flags above should be preserved and used"); hestOptAdd(&hopt, "bg", "R G B", airTypeFloat, 3, 3, bg, "0.2 0.3 0.4", "background color"); hestOptAdd(&hopt, "fog", NULL, airTypeInt, 0, 0, &fog, NULL, "hack: turn on fog"); hestOptAdd(&hopt, "is", "su sv", airTypeInt, 2, 2, imgSize, "640 480", "initial window size"); /* this tensor stuff is here because we're hijacking the tensor glyph object for doing the particle display ... */ hestOptAdd(&hopt, "a", "aniso", airTypeEnum, 1, 1, &aniso, NULL, "anisotropy metric to make volume of", NULL, tenAniso); hestOptAdd(&hopt, "atr", "aniso thresh", airTypeFloat, 1, 1, &anisoThresh, "0.85", "Glyphs will be drawn only for tensors with anisotropy " "greater than this threshold"); hestOptAdd(&hopt, "atrm", "aniso thresh min", airTypeFloat, 1, 1, &anisoThreshMin, "0.4", "lower bound on aniso thresh"); hestOptAdd(&hopt, "g", "glyph shape", airTypeEnum, 1, 1, &glyphType, "sqd", "shape of glyph to use for display. Possibilities " "include \"box\", \"sphere\"=\"sph\", \"cylinder\"=\"cyl\", and " "\"superquad\"=\"sqd\"", NULL, tenGlyphType); hestOptAdd(&hopt, "gsc", "scale", airTypeFloat, 1, 1, &glyphScale, "0.25", "over-all glyph size"); hestOptAdd(&hopt, "htb", "trace", airTypeFloat, 1, 1, &haloTraceBound, "1.0", "halo trace bound"); hestOptAdd(&hopt, "ghw", "hwidth", airTypeFloat, 1, 1, &glyphHaloWidth, "0.0", "glyph halo width"); hestOptAdd(&hopt, "gnp", "npow", airTypeFloat, 1, 1, &glyphNormPow, "1.0", "pow() exponent for compressing range of norms"); hestOptAdd(&hopt, "gep", "epow", airTypeFloat, 1, 1, &glyphEvalPow, "1.0", "pow() exponent for compressing single eigenvalues"); hestOptAdd(&hopt, "br", "barycentric res", airTypeInt, 1, 1, &baryRes, "50", "resolution of sampling of tensor shape palette"); hestOptAdd(&hopt, "gr", "glyph res", airTypeInt, 1, 1, &glyphFacetRes, "7", "resolution of polygonalization of glyphs (other than box)"); hestOptAdd(&hopt, "sh", "sharpness", airTypeFloat, 1, 1, &sqdSharp, "2.5", "for superquadric glyphs, how much to sharp edges form as a " "function of differences between eigenvalues. Higher values " "mean that edges form more easily"); #endif hestOptAdd(&hopt, "int", "int", airTypeEnum, 1, 1, &interType, "justr", "inter-particle energy type", NULL, pullInterType); hestOptAdd(&hopt, "enr", "spec", airTypeOther, 1, 1, &enspR, "cotan", "inter-particle energy, radial component", NULL, NULL, pullHestEnergySpec); hestOptAdd(&hopt, "ens", "spec", airTypeOther, 1, 1, &enspS, "zero", "inter-particle energy, scale component", NULL, NULL, pullHestEnergySpec); hestOptAdd(&hopt, "enw", "spec", airTypeOther, 1, 1, &enspWin, "butter:16,0.8", "windowing to create locality with additive " "scale-space interaction (\"-int add\")", NULL, NULL, pullHestEnergySpec); hestOptAdd(&hopt, "zz", "bool", airTypeBool, 1, 1, &zeroZ, "false", "always constrain Z=0, to process 2D images"); hestOptAdd(&hopt, "1d", "bool", airTypeBool, 1, 1, &oneDim, "false", "for scale-space, only blur along fastest axis"); hestOptAdd(&hopt, "efs", "bool", airTypeBool, 1, 1, &energyFromStrength, "false", "whether or not strength contributes to particle-image energy"); hestOptAdd(&hopt, "nave", "bool", airTypeBool, 1, 1, &nixAtVolumeEdgeSpace, "false", "whether or not to nix points at edge of volume, where gage had " "to invent values for kernel support"); hestOptAdd(&hopt, "cbst", "bool", airTypeBool, 1, 1, &constraintBeforeSeedThresh, "false", "during initialization, try constraint satisfaction before " "testing seedThresh"); hestOptAdd(&hopt, "noadd", NULL, airTypeBool, 0, 0, &noAdd, NULL, "turn off adding during population control"); hestOptAdd(&hopt, "usa", "bool", airTypeBool, 1, 1, &unequalShapesAllow, "false", "allow volumes to have different shapes (false is safe as " "different volume sizes are often accidental)"); hestOptAdd(&hopt, "pcet", "bool", airTypeBool, 1, 1, &popCntlEnoughTest, "true", "use neighbor-counting \"enough\" heuristic to " "bail out of pop cntl"); hestOptAdd(&hopt, "cipc", "bool", airTypeBool, 1, 1, &convergenceIgnoresPopCntl, "false", "convergence test doesn't care if there has been " "recent changes due to population control"); hestOptAdd(&hopt, "nobin", NULL, airTypeBool, 0, 0, &binSingle, NULL, "turn off spatial binning (which prevents multi-threading " "from being useful), for debugging or speed-up measurement"); hestOptAdd(&hopt, "lti", "bool", airTypeBool, 1, 1, &liveThresholdOnInit, "true", "impose liveThresh on initialization"); hestOptAdd(&hopt, "por", "bool", airTypeBool, 1, 1, &permuteOnRebin, "true", "permute points during rebinning"); hestOptAdd(&hopt, "npcwza", "bool", airTypeBool, 1, 1, &noPopCntlWithZeroAlpha, "false", "no pop cntl with zero alpha"); hestOptAdd(&hopt, "ubfgl", "bool", airTypeBool, 1, 1, &useBetaForGammaLearn, "false", "use beta for gamma learning"); hestOptAdd(&hopt, "ratb", "bool", airTypeBool, 1, 1, &restrictiveAddToBins, "true", "be choosy when adding points to bins to avoid overlap"); hestOptAdd(&hopt, "svec", "vec", airTypeDouble, 3, 3, scaleVec, "0 0 0", "if non-zero (length), vector to use for displaying scale " "in 3-space"); hestOptAdd(&hopt, "gssr", "rad", airTypeDouble, 1, 1, &glyphScaleRad, "0.0", "if non-zero (length), scaling of scale to cylindrical tensors"); hestOptAdd(&hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1", "verbosity level"); hestOptAdd(&hopt, "vol", "vol0 vol1", airTypeOther, 1, -1, &vspec, NULL, "input volumes, in format ::", &vspecNum, NULL, meetHestPullVol); hestOptAdd(&hopt, "info", "info0 info1", airTypeOther, 1, -1, &idef, NULL, "info definitions, in format " "[-c]::[::]", &idefNum, NULL, meetHestPullInfo); hestOptAdd(&hopt, "k00", "kern00", airTypeOther, 1, 1, &k00, "cubic:1,0", "kernel for gageKernel00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kern11", airTypeOther, 1, 1, &k11, "cubicd:1,0", "kernel for gageKernel11", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kern22", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "kernel for gageKernel22", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "sscp", "path", airTypeString, 1, 1, &cachePathSS, "./", "path (without trailing /) for where to read/write " "pre-blurred volumes for scale-space"); hestOptAdd(&hopt, "kssb", "kernel", airTypeOther, 1, 1, &kSSblur, "dgauss:1,5", "blurring kernel, to sample scale space", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "kssr", "kernel", airTypeOther, 1, 1, &kSSrecon, "hermite", "kernel for reconstructing from scale space samples", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "nss", "# scl smpls", airTypeUInt, 1, 1, &samplesAlongScaleNum, "1", "if using \"-ppv\", number of samples along scale axis " "for each spatial position"); hestOptAdd(&hopt, "np", "# points", airTypeUInt, 1, 1, &pointNumInitial, "1000", "number of points to start in system"); hestOptAdd(&hopt, "halton", NULL, airTypeInt, 0, 0, &useHalton, NULL, "use Halton sequence initialization instead of " "uniform random"); hestOptAdd(&hopt, "ppv", "# pnts/vox", airTypeInt, 1, 1, &pointPerVoxel, "0", "number of points per voxel to start in simulation " "(need to have a seed thresh vol, overrides \"-np\")"); hestOptAdd(&hopt, "ppvzr", "z range", airTypeUInt, 2, 2, ppvZRange, "1 0", "range of Z slices (1st num < 2nd num) to do ppv in, or, " "\"1 0\" for whole volume"); hestOptAdd(&hopt, "jit", "jitter", airTypeDouble, 1, 1, &jitter, "0", "amount of jittering to do with ppv"); hestOptAdd(&hopt, "pi", "npos", airTypeOther, 1, 1, &nPosIn, "", "4-by-N array of positions to start at (overrides \"-np\")", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "step", "step", airTypeDouble, 1, 1, &stepInitial, "1", "initial step size for gradient descent"); hestOptAdd(&hopt, "csm", "step", airTypeDouble, 1, 1, &constraintStepMin, "0.0001", "convergence criterion for constraint satisfaction"); hestOptAdd(&hopt, "snap", "# iters", airTypeUInt, 1, 1, &snap, "0", "if non-zero, # iters between saved snapshots"); hestOptAdd(&hopt, "maxi", "# iters", airTypeUInt, 1, 1, &iterMax, "0", "if non-zero, max # iterations to run whole system"); hestOptAdd(&hopt, "stim", "# iters", airTypeUInt, 1, 1, &stuckIterMax, "5", "if non-zero, max # iterations to allow a particle " " to be stuck before nixing"); hestOptAdd(&hopt, "maxci", "# iters", airTypeUInt, 1, 1, &constraintIterMax, "15", "if non-zero, max # iterations for contraint enforcement"); hestOptAdd(&hopt, "irad", "scale", airTypeDouble, 1, 1, &radiusSpace, "1", "particle radius in spatial domain"); hestOptAdd(&hopt, "srad", "scale", airTypeDouble, 1, 1, &radiusScale, "1", "particle radius in scale domain"); hestOptAdd(&hopt, "bws", "bin width", airTypeDouble, 1, 1, &binWidthSpace, "1.001", "spatial bin width as multiple of spatial radius"); hestOptAdd(&hopt, "alpha", "alpha", airTypeDouble, 1, 1, &alpha, "0.5", "blend between particle-image (alpha=0) and " "inter-particle (alpha=1) energies"); hestOptAdd(&hopt, "beta", "beta", airTypeDouble, 1, 1, &beta, "1.0", "when using Phi2 energy, blend between pure " "space repulsion (beta=0) and " "scale attraction (beta=1)"); hestOptAdd(&hopt, "gamma", "gamma", airTypeDouble, 1, 1, &gamma, "1.0", "scaling factor on energy from strength"); hestOptAdd(&hopt, "theta", "theta", airTypeDouble, 1, 1, &theta, "0.0", "slope of increasing livethresh wrt scale"); hestOptAdd(&hopt, "wall", "k", airTypeDouble, 1, 1, &wall, "0.0", "spring constant on walls"); hestOptAdd(&hopt, "eip", "k", airTypeDouble, 1, 1, &energyIncreasePermit, "0.0", "amount by which its okay for *per-particle* energy to increase " "during gradient descent process"); hestOptAdd(&hopt, "ess", "scl", airTypeDouble, 1, 1, &backStepScale, "0.5", "when energy goes up instead of down, scale step " "size by this"); hestOptAdd(&hopt, "oss", "scl", airTypeDouble, 1, 1, &opporStepScale, "1.0", "opportunistic scaling (hopefully up, >1) of step size " "on every iteration"); hestOptAdd(&hopt, "edmin", "frac", airTypeDouble, 1, 1, &energyDecreaseMin, "0.0001", "convergence threshold: stop when fractional improvement " "(decrease) in energy dips below this"); hestOptAdd(&hopt, "edpcmin", "frac", airTypeDouble, 1, 1, &energyDecreasePopCntlMin, "0.01", "population control is triggered when energy improvement " "goes below this threshold"); hestOptAdd(&hopt, "fnnm", "frac", airTypeDouble, 1, 1, &fracNeighNixedMax, "0.25", "don't nix if this fraction (or more) of neighbors " "have been nixed"); hestOptAdd(&hopt, "pcp", "period", airTypeUInt, 1, 1, &popCntlPeriod, "20", "# iters to wait between attempts at population control"); hestOptAdd(&hopt, "iad", "# iters", airTypeUInt, 1, 1, &addDescent, "10", "# iters to run descent on tentative new points during PC"); hestOptAdd(&hopt, "icb", "# iters", airTypeUInt, 1, 1, &iterCallback, "1", "periodicity of calling rendering callback"); hestOptAdd(&hopt, "ac3c", "ac3c", airTypeBool, 1, 1, &allowCodimension3Constraints, "false", "allow codimensions 3 constraints"); hestOptAdd(&hopt, "sit", "sit", airTypeBool, 1, 1, &scaleIsTau, "false", "scale is tau"); hestOptAdd(&hopt, "rng", "seed", airTypeUInt, 1, 1, &rngSeed, "42", "base seed value for RNGs"); hestOptAdd(&hopt, "pbm", "mod", airTypeUInt, 1, 1, &progressBinMod, "50", "progress bin mod"); hestOptAdd(&hopt, "eiphl", "hl", airTypeUInt, 1, 1, &eipHalfLife, "0", "half-life of energyIncreasePermute (\"-eip\")"); hestOptAdd(&hopt, "nt", "# threads", airTypeInt, 1, 1, &threadNum, "1", (airThreadCapable ? "number of threads hoover should use" : "if threads where enabled in this Teem build, this is how " "you would control the number of threads to use")); hestOptAdd(&hopt, "nprob", "prob", airTypeDouble, 1, 1, &neighborTrueProb, "1.0", "do full neighbor discovery with this probability"); hestOptAdd(&hopt, "pprob", "prob", airTypeDouble, 1, 1, &probeProb, "1.0", "probe local image values with this probability"); hestOptAdd(&hopt, "addlog", "fname", airTypeString, 1, 1, &addLogS, "", "name of file in which to log all particle additions"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "filename for saving computed positions"); hestOptAdd(&hopt, "eob", "base", airTypeString, 1, 1, &extraOutBaseS, "", "save extra info (besides position), and use this string as " "the base of the filenames. Not using this means the extra " "info is not saved."); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); #ifdef DEFT if (const char *envS = getenv("DEFT_HOME")) { strcpy(Deft::homeDir, envS); strcat(Deft::homeDir, "/"); } else { fprintf(stderr, "%s: WARNING: \"DEFT_HOME\" environment variable " "not set; assuming \".\"\n", me); strcpy(Deft::homeDir, "./"); } #endif /* airEnumPrint(stderr, gageScl); exit(0); */ if (airStrlen(addLogS)) { if (!(addLog = airFopen(addLogS, stdout, "w"))) { fprintf(stderr, "%s: couldn't open %s for writing", me, addLogS); airMopError(mop); return 1; } airMopAdd(mop, addLog, (airMopper)airFclose, airMopAlways); } else { addLog = NULL; } pctx = pullContextNew(); airMopAdd(mop, pctx, (airMopper)pullContextNix, airMopAlways); if (pullVerboseSet(pctx, verbose) || pullFlagSet(pctx, pullFlagZeroZ, zeroZ) || pullFlagSet(pctx, pullFlagEnergyFromStrength, energyFromStrength) || pullFlagSet(pctx, pullFlagNixAtVolumeEdgeSpace, nixAtVolumeEdgeSpace) || pullFlagSet(pctx, pullFlagConstraintBeforeSeedThresh, constraintBeforeSeedThresh) || pullFlagSet(pctx, pullFlagPopCntlEnoughTest, popCntlEnoughTest) || pullFlagSet(pctx, pullFlagConvergenceIgnoresPopCntl, convergenceIgnoresPopCntl) || pullFlagSet(pctx, pullFlagBinSingle, binSingle) || pullFlagSet(pctx, pullFlagNoAdd, noAdd) || pullFlagSet(pctx, pullFlagPermuteOnRebin, permuteOnRebin) || pullFlagSet(pctx, pullFlagNoPopCntlWithZeroAlpha, noPopCntlWithZeroAlpha) || pullFlagSet(pctx, pullFlagUseBetaForGammaLearn, useBetaForGammaLearn) || pullFlagSet(pctx, pullFlagRestrictiveAddToBins, restrictiveAddToBins) || pullFlagSet(pctx, pullFlagAllowCodimension3Constraints, allowCodimension3Constraints) || pullFlagSet(pctx, pullFlagScaleIsTau, scaleIsTau) || pullInitUnequalShapesAllowSet(pctx, unequalShapesAllow) || pullIterParmSet(pctx, pullIterParmSnap, snap) || pullIterParmSet(pctx, pullIterParmMax, iterMax) || pullIterParmSet(pctx, pullIterParmStuckMax, stuckIterMax) || pullIterParmSet(pctx, pullIterParmConstraintMax, constraintIterMax) || pullIterParmSet(pctx, pullIterParmPopCntlPeriod, popCntlPeriod) || pullIterParmSet(pctx, pullIterParmAddDescent, addDescent) || pullIterParmSet(pctx, pullIterParmCallback, iterCallback) || pullIterParmSet(pctx, pullIterParmEnergyIncreasePermitHalfLife, eipHalfLife) || pullSysParmSet(pctx, pullSysParmStepInitial, stepInitial) || pullSysParmSet(pctx, pullSysParmConstraintStepMin, constraintStepMin) || pullSysParmSet(pctx, pullSysParmRadiusSpace, radiusSpace) || pullSysParmSet(pctx, pullSysParmRadiusScale, radiusScale) || pullSysParmSet(pctx, pullSysParmBinWidthSpace, binWidthSpace) || pullSysParmSet(pctx, pullSysParmAlpha, alpha) || pullSysParmSet(pctx, pullSysParmBeta, beta) || pullSysParmSet(pctx, pullSysParmGamma, gamma) || pullSysParmSet(pctx, pullSysParmTheta, theta) || pullSysParmSet(pctx, pullSysParmWall, wall) || pullSysParmSet(pctx, pullSysParmEnergyIncreasePermit, energyIncreasePermit) || pullSysParmSet(pctx, pullSysParmEnergyDecreaseMin, energyDecreaseMin) || pullSysParmSet(pctx, pullSysParmFracNeighNixedMax, fracNeighNixedMax) || pullSysParmSet(pctx, pullSysParmEnergyDecreasePopCntlMin, energyDecreasePopCntlMin) || pullSysParmSet(pctx, pullSysParmBackStepScale, backStepScale) || pullSysParmSet(pctx, pullSysParmOpporStepScale, opporStepScale) || pullSysParmSet(pctx, pullSysParmNeighborTrueProb, neighborTrueProb) || pullSysParmSet(pctx, pullSysParmProbeProb, probeProb) || pullRngSeedSet(pctx, rngSeed) || pullProgressBinModSet(pctx, progressBinMod) || pullThreadNumSet(pctx, threadNum) || pullInterEnergySet(pctx, interType, enspR, enspS, enspWin) || pullInitLiveThreshUseSet(pctx, liveThresholdOnInit) || pullLogAddSet(pctx, addLog)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble with flags:\n%s", me, err); airMopError(mop); return 1; } if (nPosIn) { E = pullInitGivenPosSet(pctx, nPosIn); } else if (pointPerVoxel) { E = pullInitPointPerVoxelSet(pctx, pointPerVoxel, ppvZRange[0], ppvZRange[1], samplesAlongScaleNum, jitter); } else if (useHalton) { E = pullInitHaltonSet(pctx, pointNumInitial, 0); } else { E = pullInitRandomSet(pctx, pointNumInitial); } if (E) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble with flags:\n%s", me, err); airMopError(mop); return 1; } sbp = gageStackBlurParmNew(); airMopAdd(mop, sbp, (airMopper)gageStackBlurParmNix, airMopAlways); if (gageStackBlurParmBoundarySet(sbp, nrrdBoundaryBleed, AIR_NAN) /* gageStackBlurParmBoundarySet(sbp, nrrdBoundaryWrap, AIR_NAN) */ /* though this verbosity could in principle be different */ || gageStackBlurParmVerboseSet(sbp, verbose) || gageStackBlurParmOneDimSet(sbp, oneDim)) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble with stack blur parms:\n%s", me, err); airMopError(mop); return 1; } if (meetPullVolLoadMulti(vspec, vspecNum, cachePathSS, kSSblur, sbp, verbose) || meetPullVolAddMulti(pctx, vspec, vspecNum, k00, k11, k22, kSSrecon) || meetPullInfoAddMulti(pctx, idef, idefNum)) { airMopAdd(mop, err = biffGetDone(MEET), airFree, airMopAlways); fprintf(stderr, "%s: trouble with volumes or infos:\n%s", me, err); airMopError(mop); return 1; } if (pullStart(pctx)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble starting system:\n%s", me, err); airMopError(mop); return 1; } /* -------------------------------------------------- */ /* not sure when this table was created, don't have heart to nix it * * hght scl tang1 tang2 mode scl strength * ridge surface: -1 evec2 - - -eval2 * ridge line: -1 evec2 evec1 - -eval1 * all ridges: -1 evec2 evec1 +1 ?? * valley surface: +1 evec0 - - eval0 * valley line: +1 evec0 evec1 - eval1 * all lines: +1 evec0 evec1 -1 */ ssrange[0] = FLT_MAX; ssrange[1] = -FLT_MAX; for (vsi=0; vsinumSS) { ssrange[0] = AIR_MIN(ssrange[0], vol->rangeSS[0]); ssrange[1] = AIR_MAX(ssrange[1], vol->rangeSS[1]); } } if (pctx->flag.scaleIsTau) { ssrange[0] = gageTauOfSig(ssrange[0]); ssrange[1] = gageTauOfSig(ssrange[1]); } #ifdef DEFT /* -------------------------------------------------- */ /* initialize bag and its UI */ bag.pctx = pctx; bag.nPosOut = nrrdNew(); bag.nTenOut = nrrdNew(); bag.nFrcOut = nrrdNew(); bag.nposOld = nrrdNew(); bag.nposNew = nrrdNew(); bag.nten = nrrdNew(); bag.ntmp = nrrdNew(); bag.nenr = nrrdNew(); bag.nscl = nrrdNew(); bag.nidcc = nrrdNew(); bag.ncovar = nrrdNew(); #if PULL_TANCOVAR bag.ntcovar = nrrdNew(); #endif bag.nstab = nrrdNew(); bag.nintern = nrrdNew(); if (pctx->ispec[pullInfoStrength]) { printf("!%s: trouble creating strength nrrd\n", me); bag.nstrn = nrrdNew(); } else { bag.nstrn = NULL; } if (pctx->ispec[pullInfoQuality]) { printf("!%s: trouble creating quality nrrd\n", me); bag.nqual = nrrdNew(); } else { bag.nqual = NULL; } bag.nstuck = nrrdNew(); bag.nfrcOld = nrrdNew(); bag.nfrcNew = nrrdNew(); bag.nrgb = nrrdNew(); bag.nccrgb = nrrdNew(); bag.ncmapOut = nrrdNew(); bag.nblur = nrrdNew(); bag.norig = vspec[0]->nin; nrrdCopy(bag.nblur, bag.norig); bag.rsmc = nrrdResampleContextNew(); E = 0; if (!E) E |= nrrdResampleDefaultCenterSet(bag.rsmc, nrrdDefaultCenter); if (!E) E |= nrrdResampleInputSet(bag.rsmc, bag.norig); for (unsigned int axi=0; axi<3; axi++) { if (!E) E |= nrrdResampleSamplesSet(bag.rsmc, axi, bag.norig->axis[axi].size); if (!E) E |= nrrdResampleRangeFullSet(bag.rsmc, axi); } if (!E) E |= nrrdResampleBoundarySet(bag.rsmc, nrrdBoundaryBleed); if (!E) E |= nrrdResampleTypeOutSet(bag.rsmc, nrrdTypeDefault); if (!E) E |= nrrdResampleRenormalizeSet(bag.rsmc, AIR_TRUE); if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting up resampler:\n%s", me, err); airMopError(mop); return 1; } /* bag.ncval is just a pointer to other nrrds */ bag.cvalRange = nrrdRangeNew(AIR_NAN, AIR_NAN); ELL_3V_COPY(bag.scaleVec, scaleVec); bag.glyphScaleRad = glyphScaleRad; bag.scene = new Deft::Scene(); bag.scene->bgColor(bg[0], bg[1], bg[2]); int winy = 10; int incy; fltk::Window *win = new fltk::Window(400, 600, "pull UI"); win->begin(); fltk::Button *stepButton = new fltk::Button(10, winy, 50, incy=20, "step"); stepButton->callback((fltk::Callback*)step_cb, &bag); bag.verbose = new fltk::ValueInput(100, winy, 30, 20, "verb"); bag.verbose->value(pctx->verbose); bag.verbose->callback((fltk::Callback*)verbose_cb, &bag); bag.iters = new fltk::IntInput(200, winy, 50, 20, "# iters"); bag.iters->value(1); if (ssrange[1] > ssrange[0]) { fltk::Button *gamButton = new fltk::Button(260, winy, 50, 20, "gamma"); gamButton->callback((fltk::Callback*)gammaSet_cb, &bag); } fltk::Button *ccButton = new fltk::Button(360, winy, 30, 20, "CC"); ccButton->callback((fltk::Callback*)cc_cb, &bag); winy += incy + 5; fltk::Button *saveButton = new fltk::Button(10, winy, 50, 20, "save"); saveButton->callback((fltk::Callback*)save_cb, &bag); bag.scaleVecInput[0] = new fltk::FloatInput(120, winy, 35, 20, "scaleVec"); bag.scaleVecInput[1] = new fltk::FloatInput(160, winy, 35, 20, ""); bag.scaleVecInput[2] = new fltk::FloatInput(200, winy, 35, 20, ""); bag.scaleVecInput[0]->value(scaleVec[0]); bag.scaleVecInput[1]->value(scaleVec[1]); bag.scaleVecInput[2]->value(scaleVec[2]); bag.scaleVecInput[0]->callback((fltk::Callback*)scaleGlyph_cb, &bag); bag.scaleVecInput[1]->callback((fltk::Callback*)scaleGlyph_cb, &bag); bag.scaleVecInput[2]->callback((fltk::Callback*)scaleGlyph_cb, &bag); bag.glyphScaleRadInput = new fltk::ValueInput(300, winy, 45, 20, "gssr"); bag.glyphScaleRadInput->range(0.0, 100.0); bag.glyphScaleRadInput->step(0.1); bag.glyphScaleRadInput->linesize(0.1); bag.glyphScaleRadInput->value(glyphScaleRad); bag.glyphScaleRadInput->callback((fltk::Callback*)scaleGlyph_cb, &bag); winy += incy; bag.alpha = new Deft::Slider(0, winy, win->w(), incy=55, "alpha"); bag.alpha->align(fltk::ALIGN_LEFT); bag.alpha->range(0, 1); bag.alpha->value(pctx->sysParm.alpha); bag.alpha->fastUpdate(1); bag.alpha->callback((fltk::Callback*)alpha_cb, &bag); if (pullInterTypeAdditive == pctx->interType) { winy += incy; bag.beta = new Deft::Slider(0, winy, win->w(), incy=55, "beta"); bag.beta->align(fltk::ALIGN_LEFT); bag.beta->range(0, 1); bag.beta->value(pctx->sysParm.beta); bag.beta->fastUpdate(1); bag.beta->callback((fltk::Callback*)beta_cb, &bag); } if (pullEnergyCubicWell == pctx->energySpecR->energy || pullEnergyBetterCubicWell == pctx->energySpecR->energy) { winy += incy; bag.cwell = new Deft::Slider(0, winy, win->w(), incy=55, "well depth"); bag.cwell->align(fltk::ALIGN_LEFT); bag.cwell->range(-0.04, 0); bag.cwell->value(bag.pctx->energySpecR->parm[1]); bag.cwell->fastUpdate(1); bag.cwell->callback((fltk::Callback*)cwell_cb, &bag); /* remember eip as fraction of well depth */ bag.energyIncreasePermitFrac = energyIncreasePermit/bag.pctx->energySpecR->parm[1]; } else { bag.energyIncreasePermitFrac = AIR_NAN; } winy += incy; bag.gamma = new Deft::Slider(0, winy, win->w(), incy=55, "gamma"); bag.gamma->align(fltk::ALIGN_LEFT); bag.gamma->range(0, 2*pctx->sysParm.gamma); bag.gamma->value(pctx->sysParm.gamma); bag.gamma->fastUpdate(1); bag.gamma->callback((fltk::Callback*)gamma_cb, &bag); winy += incy; bag.ccSelect = new Deft::Slider(0, winy, win->w(), incy=55, "CC Select"); bag.ccSelect->align(fltk::ALIGN_LEFT); bag.ccSelect->range(0, 0); bag.ccSelect->step(1); bag.ccSelect->value(0); bag.ccSelect->fastUpdate(1); bag.ccSelect->callback((fltk::Callback*)ccSelect_cb, &bag); bag.ccSingle = new fltk::CheckButton(130, winy+4, 50, 20, "Single"); bag.ccSingle->value(0); bag.ccSingle->callback((fltk::Callback*)ccSelect_cb, &bag); winy += incy; bag.rho = new Deft::Slider(0, winy, win->w(), incy=55, "rho"); bag.rho->align(fltk::ALIGN_LEFT); bag.rho->range(0, 1.0); bag.rho->value(0.5); bag.rho->fastUpdate(1); bag.rho->callback((fltk::Callback*)cc_cb, &bag); if (ssrange[1] > ssrange[0]) { winy += incy; bag.sclMean = new Deft::Slider(0, winy, win->w(), incy=55, "scale mean"); bag.sclMean->align(fltk::ALIGN_LEFT); bag.sclMean->range(ssrange[0], ssrange[1]); bag.sclMean->value((ssrange[0] + ssrange[1])/2); bag.sclMean->fastUpdate(1); bag.sclMean->callback((fltk::Callback*)scale_cb, &bag); fltk::Button *reblurButton = new fltk::Button(130, winy+4, 50, 20, "reblur"); reblurButton->callback((fltk::Callback*)reblur_cb, &bag); winy += incy; bag.sclWind = new Deft::Slider(0, winy, win->w(), incy=55, "scale window"); bag.sclWind->align(fltk::ALIGN_LEFT); bag.sclWind->range(0, ssrange[1] - ssrange[0]); bag.sclWind->value(ssrange[1] - ssrange[0]); bag.sclWind->fastUpdate(1); bag.sclWind->callback((fltk::Callback*)scale_cb, &bag); scale_cb(NULL, &bag); } else { bag.sclMin = bag.sclMax = 0; } if (pctx->ispec[pullInfoStrength]) { winy += incy; bag.strength = new Deft::Slider(0, winy, win->w(), incy=55, "strength"); bag.strength->align(fltk::ALIGN_LEFT); bag.strength->range(0, 1); bag.strength->callback((fltk::Callback*)strength_cb, &bag); bag.strength->fastUpdate(1); bag.strength->value(0); } else { bag.strnMin = 0; } if (pctx->ispec[pullInfoQuality]) { winy += incy; bag.quality = new Deft::Slider(0, winy, win->w(), incy=55, "quality"); bag.quality->align(fltk::ALIGN_LEFT); bag.quality->range(-0.1, 1); bag.quality->callback((fltk::Callback*)quality_cb, &bag); bag.quality->fastUpdate(1); bag.quality->value(-0.1); } else { bag.qualMin = 0; } /* winy += incy; bag.height = new Deft::Slider(0, winy, win->w(), incy=55, "height"); bag.height->align(fltk::ALIGN_LEFT); bag.height->range(0, 10); bag.height->value(10); bag.height->fastUpdate(1); bag.height->callback((fltk::Callback*)strength_cb, &bag); */ win->end(); win->show(); /* -------------------------------------------------- */ bag.viewer = new Deft::Viewer(bag.scene, imgSize[0], imgSize[1]); if (camkeep) { bag.viewer->camera(fr[0], fr[1], fr[2], at[0], at[1], at[2], up[0], up[1], up[2], fovy, neer, faar); } bag.viewer->resizable(bag.viewer); bag.viewer->end(); const char *fakeArgv[2] = {"Deft_pull", NULL}; bag.viewer->show(1, (char**)fakeArgv); if (ortho) { /* total hack */ bag.viewer->keyboard('p', 0, 0); } /* bag.viewer->helpPrint(); */ Deft::ViewerUI *viewerUI = new Deft::ViewerUI(bag.viewer); viewerUI->show(); fltk::flush(); /* -------------------------------------------------- */ if (gageKindScl == vspec[0]->kind) { bag.contour = new Deft::Contour(); bag.contour->volumeSet(bag.nblur); bag.contour->twoSided(true); bag.scene->objectAdd(bag.contour); } else { bag.contour = NULL; } /* -------------------------------------------------- */ Deft::TensorGlyph *glyph = new Deft::TensorGlyph(); glyph->dynamic(true); glyph->twoSided(true); glyph->anisoType(aniso); glyph->anisoThreshMin(anisoThreshMin); glyph->anisoThresh(anisoThresh); glyph->glyphType(glyphType); glyph->superquadSharpness(sqdSharp); glyph->glyphResolution(glyphFacetRes); if (tenGlyphTypeBetterquad) { glyph->barycentricResolution(baryRes); } else { glyph->barycentricResolution(20); } glyph->glyphScale(glyphScale); glyph->glyphHaloWidth(glyphHaloWidth); glyph->haloTraceBound(haloTraceBound); glyph->glyphNormPow(glyphNormPow); glyph->glyphEvalPow(glyphEvalPow); glyph->rgbEvecParmSet(tenAniso_Cl2, 0, 0.7, 1.0, 2.3, 1.0); glyph->rgbEvecParmSet(tenAniso_Cl2, 0, 0, 1.0, 1, 0.0); glyph->maskThresh(0.5); /* void rgbParmSet(int aniso, unsigned int evec, double maxSat, double isoGray, double gamma, double modulate); */ bag.scene->objectAdd(glyph); bag.glyph = glyph; Deft::TensorGlyphUI *glyphUI = new Deft::TensorGlyphUI(bag.glyph, bag.viewer); glyphUI->show(); /* -------------------------------------------------- */ /* Deft::TensorGlyph *hedge = new Deft::TensorGlyph(); hedge->dynamic(true); hedge->anisoType(aniso); hedge->anisoThreshMin(anisoThreshMin); hedge->anisoThresh(anisoThresh); hedge->glyphType(glyphType); hedge->superquadSharpness(sqdSharp); hedge->glyphResolution(glyphFacetRes); hedge->glyphScale(glyphScale/4); hedge->rgbParmSet(tenAniso_Cl2, 0, 0.7, 1.0, 2.3, 1.0); hedge->rgbParmSet(tenAniso_Cl2, 0, 0, 1.0, 1, 0.0); hedge->maskThresh(0.0); bag.hedge = hedge; bag.scene->objectAdd(hedge); Deft::TensorGlyphUI *hedgeUI = new Deft::TensorGlyphUI(bag.hedge, bag.viewer); hedgeUI->show(); */ /* -------------------------------------------------- */ Deft::Volume *vol = new Deft::Volume(vspec[0]->kind, bag.nblur); fprintf(stderr, "!%s: vol = %p *********************\n", me, vol); Deft::TriPlane *triplane = new Deft::TriPlane(vol); /* ** HEY: you can eventually segfault if this isn't set here ** shouldn't doing so be optional? */ if (gageKindScl == vspec[0]->kind) { triplane->alphaMaskQuantity(Deft::alphaMaskSclQuantityValue); triplane->alphaMaskThreshold(0); triplane->colorQuantity(Deft::colorSclQuantityValue); } else if (tenGageKind == vspec[0]->kind) { triplane->alphaMaskQuantity(Deft::alphaMaskTenQuantityConfidence); triplane->alphaMaskThreshold(0); triplane->colorQuantity(Deft::colorTenQuantityRgbLinear); } NrrdKernelSpec *ksp = nrrdKernelSpecNew(); double kparm[10]; kparm[0] = 1.0; nrrdKernelSpecSet(ksp, nrrdKernelTent, kparm); triplane->kernel(gageKernel00, ksp); ELL_3V_SET(kparm, 1, 1, 0); nrrdKernelSpecSet(ksp, nrrdKernelBCCubicD, kparm); triplane->kernel(gageKernel11, ksp); nrrdKernelSpecSet(ksp, nrrdKernelBCCubicDD, kparm); triplane->kernel(gageKernel22, ksp); triplane->visible(false); bag.scene->groupAdd(triplane); Deft::TriPlaneUI *planeUI = new Deft::TriPlaneUI(triplane, bag.viewer); planeUI->show(); /* -------------------------------------------------- */ if (gageKindScl == vspec[0]->kind) { fltk::Window *window = new fltk::Window(400, 80, "isovalue"); window->begin(); window->resizable(window); winy = 0; bag.isoval = new Deft::Slider(0, winy, window->w(), incy=55, "isovalue"); winy += incy; bag.isoval->align(fltk::ALIGN_LEFT); bag.isoval->range(bag.contour->minimum(), bag.contour->maximum()); bag.isoval->value(bag.contour->maximum()); bag.contour->wireframe(false); bag.isoval->fastUpdate(0); bag.isoval->callback((fltk::Callback*)isovalue_cb, &bag); /* bag.isoval->value(1.0); */ window->end(); window->show(argc,(char**)argv); } if (pullPhistEnabled) { bag.phistLine = limnPolyDataNew(); /* bag.phistTube = limnPolyDataNew(); */ bag.phistSurf = new Deft::PolyData(bag.phistLine, false); bag.phistSurf->wireframe(false); bag.phistSurf->normalsUse(true); bag.phistSurf->colorUse(true); bag.phistSurf->visible(true); bag.phistSurf->changed(); bag.scene->objectAdd(bag.phistSurf); } fltk::flush(); fltk::redraw(); /* this is to get "output" prior to any iterations, but after seeding and initialization */ outputGet(&bag); outputShow(&bag); if (!camkeep) { bag.viewer->cameraReset(); } if (0) { Nrrd *nplot; nplot = nrrdNew(); if (pullEnergyPlot(pctx, nplot, 1, 0, 0, 601)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble plotting:\n%s", me, err); airMopError(mop); return 1; } nrrdSave("eplot.nrrd", nplot, NULL); nplot = nrrdNuke(nplot); #if PULL_HINTER pctx->nhinter = nrrdNew(); #endif } if (fog) { bag.viewer->fog(true); bag.viewer->redraw(); ret = fltk::wait(); } if (saveAndQuit) { bag.scene->draw(); bag.viewer->screenDump(); } else { /* set up callbacks in pull */ pullCallbackSet(pctx, iter_cb, &bag); /* this returns when the user quits */ ret = fltk::run(); } #else /* else not running as Deft, but as command-line */ if (!E) E |= pullRun(pctx); if (E) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble 3:\n%s", me, err); airMopError(mop); return 1; } if (pullOutputGet(nPosOut, NULL, NULL, NULL, 0.0, pctx)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble 3.1:\n%s", me, err); airMopError(mop); return 1; } nrrdSave(outS, nPosOut, NULL); if (airStrlen(extraOutBaseS)) { Nrrd *nstrn, *nstab, *nintern; char fname[3][AIR_STRLEN_MED]; nstrn = nrrdNew(); nstab = nrrdNew(); nintern = nrrdNew(); airMopAdd(mop, nstrn, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nstab, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nintern, (airMopper)nrrdNuke, airMopAlways); if (pullInfoGet(nstrn, pullInfoStrength, pctx) || pullPropGet(nstab, pullPropStability, pctx) || pullPropGet(nintern, pullPropNeighInterNum, pctx)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble 3.2:\n%s", me, err); airMopError(mop); return 1; } sprintf(fname[0], "%s-strn.nrrd", extraOutBaseS); sprintf(fname[1], "%s-stab.nrrd", extraOutBaseS); sprintf(fname[2], "%s-intern.nrrd", extraOutBaseS); if (nrrdSave(fname[0], nstrn, NULL) || nrrdSave(fname[1], nstab, NULL) || nrrdSave(fname[2], nintern, NULL)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble 3.3:\n%s", me, err); airMopError(mop); return 1; } } #endif pullFinish(pctx); airMopOkay(mop); return ret; } teem-1.11.0~svn6057/src/bin/ilk.c0000664000175000017500000002645412202226015016072 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include static const char *ilkInfo = ("(I)mage (L)inear Trans(X-->K)forms. " "Applies linear (homogenous coordinate) transforms " "to a given image, using the given kernel for " "resampling. "); int main(int argc, const char *argv[]) { const char *me; char *errS, *outS; hestOpt *hopt=NULL; hestParm *hparm; airArray *mop; Nrrd *nin, *nout; NrrdKernelSpec *ksp; mossSampler *msp; double mat[6], **matList, *origInfo, origMat[6], origInvMat[6], ox, oy, min[2], max[2]; int d, bound, ax0, size[2]; /* HEY size[] should be size_t */ unsigned int matListLen, _bkgLen, i, avgNum; float *bkg, *_bkg, scale[4]; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hparm->elideSingleEnumType = AIR_TRUE; hparm->elideSingleOtherType = AIR_TRUE; hparm->elideSingleOtherDefault = AIR_FALSE; hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hparm->respFileEnable = AIR_TRUE; hestOptAdd(&hopt, "i", "image", airTypeOther, 1, 1, &nin, "-", "input image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "0", "origin", airTypeOther, 1, 1, &origInfo, "p:0,0", "where to location (0,0) prior to applying transforms.\n " "\b\bo \"u:,\" locate origin in a unit box " "[0,1]x[0,1] which covers the original image\n " "\b\bo \"p:,\" locate origin at a particular " "pixel location, in the index space of the image", NULL, NULL, mossHestOrigin); hestOptAdd(&hopt, "t", "xform0", airTypeOther, 1, -1, &matList, NULL, "transform(s) to apply to image. Transforms " "are applied in the order in which they appear.\n " "\b\bo \"identity\": no geometric transform, just resampling\n " "\b\bo \"translate:x,y\": shift image by vector (x,y), as " "measured in pixels\n " "\b\bo \"rotate:ang\": rotate CCW by ang degrees\n " "\b\bo \"scale:xs,ys\": scale by xs in X, and ys in Y\n " "\b\bo \"shear:fix,amnt\": shear by amnt, keeping fixed " "the pixels along a direction degrees from the X axis\n " "\b\bo \"flip:ang\": flip along axis an angle degrees from " "the X axis\n " "\b\bo \"a,b,tx,c,d,ty\": specify the transform explicitly " "in row-major order (opposite of PostScript) ", &matListLen, NULL, mossHestTransform); hestOptAdd(&hopt, "k", "kernel", airTypeOther, 1, 1, &ksp, "cubic:0,0.5", "reconstruction kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "min", "xMin yMin", airTypeDouble, 2, 2, min, "nan nan", "lower bounding corner of output image. Default (by not " "using this option) is the lower corner of input image. "); hestOptAdd(&hopt, "max", "xMax yMax", airTypeDouble, 2, 2, max, "nan nan", "upper bounding corner of output image. Default (by not " "using this option) is the upper corner of input image. "); hestOptAdd(&hopt, "b", "boundary", airTypeEnum, 1, 1, &bound, "bleed", "what to do when sampling outside original image.\n " "\b\bo \"bleed\": copy values at image border outward\n " "\b\bo \"wrap\": do wrap-around on image locations\n " "\b\bo \"pad\": use a given background value (via \"-bg\")", NULL, nrrdBoundary); hestOptAdd(&hopt, "bg", "bg0 bg1", airTypeFloat, 1, -1, &_bkg, "nan", "background color to use with boundary behavior \"pad\". " "Defaults to all zeroes.", &_bkgLen); hestOptAdd(&hopt, "s", "xSize ySize", airTypeOther, 2, 2, scale, "x1 x1", "For each axis, information about how many samples in output:\n " "\b\bo \"x\": number of output samples is some scaling of " " the number input of samples; multiplied by \n " "\b\bo \"\": specify exact number of samples", NULL, NULL, &unrrduHestScaleCB); hestOptAdd(&hopt, "a", "avg #", airTypeUInt, 1, 1, &avgNum, "0", "number of averages (if there there is only one " "rotation)"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "file to write output nrrd to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, ilkInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); /* HEY: this is commented out because there is a memory otherwise; this needs to be debugged */ /* airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); */ nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); msp = mossSamplerNew(); airMopAdd(mop, msp, (airMopper)mossSamplerNix, airMopAlways); msp->boundary = bound; if (mossSamplerKernelSet(msp, ksp->kernel, ksp->parm)) { fprintf(stderr, "%s: trouble with sampler:\n%s\n", me, errS = biffGetDone(MOSS)); free(errS); airMopError(mop); return 1; } if (nrrdBoundaryPad == bound) { if (_bkgLen != MOSS_NCOL(nin)) { char stmp[AIR_STRLEN_SMALL]; fprintf(stderr, "%s: got %d background colors, image has %s colors\n", me, _bkgLen, airSprintSize_t(stmp, MOSS_NCOL(nin))); airMopError(mop); return 1; } else { bkg = _bkg; } } else { /* maybe warn user if they gave a background that won't be used? */ /* No- because hest is stupid, and right now we always parse the single (default) "nan" for this argument! */ bkg = NULL; } ax0 = MOSS_AXIS0(nin); if (!( AIR_EXISTS(nin->axis[ax0+0].min) && AIR_EXISTS(nin->axis[ax0+0].max))) { nrrdAxisInfoMinMaxSet(nin, ax0+0, mossDefCenter); } if (!( AIR_EXISTS(nin->axis[ax0+1].min) && AIR_EXISTS(nin->axis[ax0+1].max))) { nrrdAxisInfoMinMaxSet(nin, ax0+1, mossDefCenter); } min[0] = AIR_EXISTS(min[0]) ? min[0] : nin->axis[ax0+0].min; max[0] = AIR_EXISTS(max[0]) ? max[0] : nin->axis[ax0+0].max; min[1] = AIR_EXISTS(min[1]) ? min[1] : nin->axis[ax0+1].min; max[1] = AIR_EXISTS(max[1]) ? max[1] : nin->axis[ax0+1].max; for (d=0; d<2; d++) { switch(AIR_CAST(int, scale[0 + 2*d])) { case 0: /* same number of samples as input */ size[d] = AIR_CAST(int, nin->axis[ax0+d].size); break; case 1: /* scaling of input # samples */ size[d] = AIR_CAST(int, scale[1 + 2*d]*nin->axis[ax0+d].size); break; case 2: /* explicit # of samples */ size[d] = AIR_CAST(int, scale[1 + 2*d]); break; default: /* error */ fprintf(stderr, "%s: scale[0 + 2*%d] == %d unexpected\n", me, AIR_CAST(int, scale[0 + 2*d]), d); airMopError(mop); return 1; } } /* find origin-based pre- and post- translate */ if (0 == origInfo[0]) { /* absolute pixel position */ mossMatTranslateSet(origMat, -origInfo[1], -origInfo[2]); } else { /* in unit box [0,1]x[0,1] */ ox = AIR_AFFINE(0.0, origInfo[1], 1.0, nin->axis[ax0+0].min, nin->axis[ax0+0].max); oy = AIR_AFFINE(0.0, origInfo[2], 1.0, nin->axis[ax0+1].min, nin->axis[ax0+1].max); mossMatTranslateSet(origMat, -ox, -oy); } mossMatInvert(origInvMat, origMat); /* form complete transform */ mossMatIdentitySet(mat); mossMatLeftMultiply(mat, origMat); for (i=0; iaxis[ax0+0].min) || !AIR_EXISTS(nin->axis[ax0+0].max)) { nrrdAxisInfoMinMaxSet(nin, ax0+0, mossDefCenter); } if (!AIR_EXISTS(nin->axis[ax0+1].min) || !AIR_EXISTS(nin->axis[ax0+1].max)) { nrrdAxisInfoMinMaxSet(nin, ax0+1, mossDefCenter); } if (avgNum > 1) { unsigned int ai; double angleMax, angle, mrot[6]; Nrrd *ntmp, *nacc; NrrdIter *itA, *itB; int E; ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); nacc = nrrdNew(); airMopAdd(mop, nacc, (airMopper)nrrdNuke, airMopAlways); itA = nrrdIterNew(); airMopAdd(mop, itA, (airMopper)nrrdIterNix, airMopAlways); itB = nrrdIterNew(); airMopAdd(mop, itB, (airMopper)nrrdIterNix, airMopAlways); E = 0; angleMax = atan2(mat[3], mat[0]); fprintf(stderr, "%s: %u angles ", me, avgNum); for (ai=0; ai /* learning columns #include #include */ #define UNU "unu" int main(int argc, const char **argv) { int i, ret; const char *me; char *argv0 = NULL; hestParm *hparm; airArray *mop; me = argv[0]; /* parse environment variables first, in case they break nrrdDefault* or nrrdState* variables in a way that nrrdSanity() should see */ nrrdDefaultGetenv(); nrrdStateGetenv(); /* if user hasn't tried to set nrrdStateKindNoop by an environment variable, we set it to false, since its probably what people expect */ if (!getenv(nrrdEnvVarStateKindNoop)) { nrrdStateKindNoop = AIR_FALSE; } /* if user hasn't tried to set nrrdStateKeyValuePairsPropagate by an envvar, we set it to true, since that's probably what unu users expect */ if (!getenv(nrrdEnvVarStateKeyValuePairsPropagate)) { nrrdStateKeyValuePairsPropagate = AIR_TRUE; } /* no harm done in making sure we're sane */ nrrdSanityOrDie(me); mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hparm->elideSingleEnumType = AIR_TRUE; hparm->elideSingleOtherType = AIR_TRUE; hparm->elideSingleOtherDefault = AIR_TRUE; hparm->elideSingleNonExistFloatDefault = AIR_TRUE; hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hparm->elideSingleEmptyStringDefault = AIR_TRUE; hparm->elideMultipleEmptyStringDefault = AIR_TRUE; hparm->columns = unrrduDefNumColumns; /* learning columns if (1) { struct winsize ws; ioctl(1, TIOCGWINSZ, &ws); hparm->columns = ws.ws_col - 1; } */ hparm->greedySingleString = AIR_TRUE; /* if there are no arguments, then we give general usage information */ if (1 >= argc) { unrrduUsageUnu("unu", hparm); airMopError(mop); exit(1); } /* else, we see if its --version */ if (!strcmp("--version", argv[1])) { printf("Teem version %s (%s)\n", airTeemVersion, airTeemReleaseDate); exit(0); } /* else, we should see if they're asking for a command we know about */ for (i=0; unrrduCmdList[i]; i++) { if (!strcmp(argv[1], unrrduCmdList[i]->name)) { break; } if (!strcmp("--help", argv[1]) && !strcmp("about", unrrduCmdList[i]->name)) { break; } } /* unrrduCmdList[] is NULL-terminated */ if (unrrduCmdList[i]) { /* yes, we have that command */ /* initialize variables used by the various commands */ argv0 = AIR_CALLOC(strlen(UNU) + strlen(argv[1]) + 2, char); airMopMem(mop, &argv0, airMopAlways); sprintf(argv0, "%s %s", UNU, argv[1]); /* run the individual unu program, saving its exit status */ ret = unrrduCmdList[i]->main(argc-2, argv+2, argv0, hparm); } else { fprintf(stderr, "%s: unrecognized command \"%s\"; type \"%s\" for " "complete list\n", me, argv[1], me); ret = 1; } airMopDone(mop, ret); return ret; } teem-1.11.0~svn6057/src/bin/tend.c0000664000175000017500000000644712165631065016262 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #define TEND "tend" int main(int argc, const char **argv) { int i, ret; const char *me; char *argv0 = NULL; hestParm *hparm; airArray *mop; me = argv[0]; /* parse environment variables first, in case they break nrrdDefault* or nrrdState* variables in a way that nrrdSanity() should see */ nrrdDefaultGetenv(); nrrdStateGetenv(); /* no harm done in making sure we're sane */ nrrdSanityOrDie(me); mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hparm->elideSingleEnumType = AIR_TRUE; hparm->elideSingleOtherType = AIR_TRUE; hparm->elideSingleOtherDefault = AIR_FALSE; hparm->elideSingleNonExistFloatDefault = AIR_TRUE; hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hparm->elideSingleEmptyStringDefault = AIR_TRUE; hparm->elideMultipleEmptyStringDefault = AIR_TRUE; hparm->cleverPluralizeOtherY = AIR_TRUE; hparm->columns = 78; /* if there are no arguments, then we give general usage information */ if (1 >= argc) { unrrduUsage(TEND, hparm, tendTitle, tendCmdList); airMopError(mop); exit(1); } /* else, we see if its --version */ if (!strcmp("--version", argv[1])) { printf("Teem version %s (%s)\n", airTeemVersion, airTeemReleaseDate); exit(0); } /* else, we should see if they're asking for a command we know about */ for (i=0; tendCmdList[i]; i++) { if (!strcmp(argv[1], tendCmdList[i]->name)) break; if (!strcmp("--help", argv[1]) && !strcmp("about", tendCmdList[i]->name)) { break; } } if (tendCmdList[i]) { /* yes, we have that command */ /* initialize variables used by the various commands */ argv0 = (char *)calloc(strlen(TEND) + strlen(argv[1]) + 2, sizeof(char)); airMopMem(mop, &argv0, airMopAlways); sprintf(argv0, "%s %s", TEND, argv[1]); /* run the individual unu program, saving its exit status */ ret = tendCmdList[i]->main(argc-2, argv+2, argv0, hparm); } else { fprintf(stderr, "%s: unrecognized command: \"%s\"; type \"%s\" for " "complete list\n", me, argv[1], me); ret = 1; } airMopDone(mop, ret); return ret; } teem-1.11.0~svn6057/src/bin/miter.c0000664000175000017500000003173312165631065016444 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include static const char *miteInfo = ("A simple but effective little volume renderer."); int main(int argc, const char *argv[]) { airArray *mop; hestOpt *hopt=NULL; hestParm *hparm=NULL; miteUser *muu; const char *me; char *errS, *outS, *shadeStr, *normalStr, debugStr[AIR_STRLEN_MED]; int renorm, baseDim, verbPix[2], offfr; int E, Ecode, Ethread; float ads[3], isScale; double turn, eye[3], eyedist, gmc; double v[NRRD_SPACE_DIM_MAX]; Nrrd *nin; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); muu = miteUserNew(); airMopAdd(mop, muu, (airMopper)miteUserNix, airMopAlways); hparm->respFileEnable = AIR_TRUE; hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hestOptAdd(&hopt, "i", "nsin", airTypeOther, 1, 1, &(muu->nsin), "", "input scalar volume to render", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "vi", "nvin", airTypeOther, 1, 1, &(muu->nvin), "", "input vector volume to render", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "ti", "ntin", airTypeOther, 1, 1, &(muu->ntin), "", "input tensor volume to render", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "txf", "nin", airTypeOther, 1, -1, &(muu->ntxf), NULL, "one or more transfer functions", &(muu->ntxfNum), NULL, nrrdHestNrrd); limnHestCameraOptAdd(&hopt, muu->hctx->cam, NULL, "0 0 0", "0 0 1", NULL, NULL, NULL, "nan nan", "nan nan", "20"); hestOptAdd(&hopt, "offfr", NULL, airTypeInt, 0, 0, &offfr, NULL, "the given eye point (\"-fr\") is to be interpreted " "as an offset from the at point."); hestOptAdd(&hopt, "ffr", "fake from", airTypeDouble, 3, 3, &(muu->fakeFrom), "nan nan nan", "eye point to use for view-dependent transfer functions. " "By default (not using this option), the point used is the " "normally specified camera eye point."); hestOptAdd(&hopt, "turn", "angle", airTypeDouble, 1, 1, &turn, "0.0", "angle (degrees) by which to rotate the from point around " "true up, for making stereo pairs. Positive means move " "towards positive U (the right)"); hestOptAdd(&hopt, "am", "ambient", airTypeFloat, 3, 3, muu->lit->amb, "1 1 1", "ambient light color"); hestOptAdd(&hopt, "ld", "light pos", airTypeFloat, 3, 3, muu->lit->_dir[0], "0 0 -1", "view space light position (extended to infinity)"); hestOptAdd(&hopt, "is", "image size", airTypeInt, 2, 2, muu->hctx->imgSize, "256 256", "image dimensions"); hestOptAdd(&hopt, "iss", "scale", airTypeFloat, 1, 1, &isScale, "1.0", "scaling of image size (from \"is\")"); hestOptAdd(&hopt, "ads", "ka kd ks", airTypeFloat, 3, 3, ads, "0.1 0.6 0.3", "phong components"); hestOptAdd(&hopt, "sp", "spec pow", mite_at, 1, 1, &(muu->rangeInit[miteRangeSP]), "30", "phong specular power"); hestOptAdd(&hopt, "k00", "kernel", airTypeOther, 1, 1, &(muu->ksp[gageKernel00]), "tent", "value reconstruction kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kernel", airTypeOther, 1, 1, &(muu->ksp[gageKernel11]), "cubicd:1,0", "first derivative kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kernel", airTypeOther, 1, 1, &(muu->ksp[gageKernel22]), "cubicdd:1,0", "second derivative kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "ss", "shading spec", airTypeString, 1, 1, &shadeStr, "phong:gage(scalar:n)", "how to do shading"); hestOptAdd(&hopt, "ns", "normal spec", airTypeString, 1, 1, &normalStr, "", "\"normal\" to use for those miteVal's that need one"); hestOptAdd(&hopt, "side", "normal side", airTypeInt, 1, 1, &(muu->normalSide), "1", "how to interpret gradients as normals:\n " "\b\bo \"1\": normal points to lower values (higher == " "more \"inside\")\n " "\b\bo \"0\": \"two-sided\": dot-products are abs()'d\n " "\b\bo \"-1\": normal points to higher values (lower == " "more \"inside\")"); hestOptAdd(&hopt, "rn", NULL, airTypeBool, 0, 0, &renorm, NULL, "renormalize kernel weights at each new sample location. " "\"Accurate\" kernels don't need this; doing it always " "makes things go slower"); hestOptAdd(&hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc, "0.0", "For curvature-based transfer functions, set curvature to " "zero when gradient magnitude is below this"); hestOptAdd(&hopt, "step", "size", airTypeDouble, 1, 1, &(muu->rayStep), "0.01", "step size along ray in world space"); hestOptAdd(&hopt, "ref", "size", airTypeDouble, 1, 1, &(muu->refStep), "0.01", "\"reference\" step size (world space) for doing " "opacity correction in compositing"); hestOptAdd(&hopt, "vp", "verbose pixel", airTypeInt, 2, 2, verbPix, "-1 -1", "pixel for which to turn on verbose messages"); hestOptAdd(&hopt, "n1", "near1", airTypeDouble, 1, 1, &(muu->opacNear1), "0.99", "opacity close enough to 1.0 to terminate ray"); hestOptAdd(&hopt, "nt", "# threads", airTypeInt, 1, 1, &(muu->hctx->numThreads), "1", (airThreadCapable ? "number of threads hoover should use" : "if pthreads where enabled in this Teem build, this is how " "you would control the number of threads hoover should use")); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, NULL, "file to write output nrrd to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, miteInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (muu->nsin) { nin = muu->nsin; baseDim = 0; } else if (muu->nvin) { nin = muu->nvin; baseDim = 1; } else if (muu->ntin) { nin = muu->ntin; baseDim = 1; } else { fprintf(stderr, "%s: didn't get any volumes to render!\n", me); airMopError(mop); return 1; } /* finish processing command-line args */ muu->rangeInit[miteRangeKa] = ads[0]; muu->rangeInit[miteRangeKd] = ads[1]; muu->rangeInit[miteRangeKs] = ads[2]; gageParmSet(muu->gctx0, gageParmGradMagCurvMin, gmc); gageParmSet(muu->gctx0, gageParmRenormalize, renorm ? AIR_TRUE : AIR_FALSE); muu->verbUi = verbPix[0]; muu->verbVi = verbPix[1]; if (offfr) { ELL_3V_INCR(muu->hctx->cam->from, muu->hctx->cam->at); } muu->hctx->imgSize[0] = AIR_CAST(int, isScale*muu->hctx->imgSize[0]); muu->hctx->imgSize[1] = AIR_CAST(int, isScale*muu->hctx->imgSize[1]); muu->nout = nrrdNew(); airMopAdd(mop, muu->nout, (airMopper)nrrdNuke, airMopAlways); ELL_3V_SET(muu->lit->col[0], 1, 1, 1); muu->lit->on[0] = AIR_TRUE; muu->lit->vsp[0] = AIR_TRUE; if (AIR_EXISTS(muu->hctx->cam->uRange[0]) && AIR_EXISTS(muu->hctx->cam->uRange[1]) && AIR_EXISTS(muu->hctx->cam->vRange[0]) && AIR_EXISTS(muu->hctx->cam->vRange[1])) { /* someone went to the trouble of setting the U,V minmax, which means they probably don't want the "fov"-based view window, whether or not the "fov" value came from the command-line or from the (unavoidable) default */ muu->hctx->cam->fov = AIR_NAN; } if (limnCameraAspectSet(muu->hctx->cam, muu->hctx->imgSize[0], muu->hctx->imgSize[1], nrrdCenterCell) || limnCameraUpdate(muu->hctx->cam) || limnLightUpdate(muu->lit, muu->hctx->cam)) { airMopAdd(mop, errS = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting camera:\n%s\n", me, errS); airMopError(mop); return 1; } if (turn) { turn *= AIR_PI/180; ELL_3V_SUB(eye, muu->hctx->cam->from, muu->hctx->cam->at); ELL_3V_NORM(eye, eye, eyedist); ELL_3V_SCALE_ADD2(muu->hctx->cam->from, cos(turn), eye, sin(turn), muu->hctx->cam->U); ELL_3V_SCALE(muu->hctx->cam->from, eyedist, muu->hctx->cam->from); if (limnCameraUpdate(muu->hctx->cam)) { airMopAdd(mop, errS = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting camera (again):\n%s\n", me, errS); airMopError(mop); return 1; } } /* fprintf(stderr, "%s: camera info\n", me); fprintf(stderr, " U = {%g,%g,%g}\n", muu->hctx->cam->U[0], muu->hctx->cam->U[1], muu->hctx->cam->U[2]); fprintf(stderr, " V = {%g,%g,%g}\n", muu->hctx->cam->V[0], muu->hctx->cam->V[1], muu->hctx->cam->V[2]); fprintf(stderr, " N = {%g,%g,%g}\n", muu->hctx->cam->N[0], muu->hctx->cam->N[1], muu->hctx->cam->N[2]); */ airStrcpy(muu->shadeStr, AIR_STRLEN_MED, shadeStr); airStrcpy(muu->normalStr, AIR_STRLEN_MED, normalStr); if (0) { muu->hctx->volSize[0] = nin->axis[baseDim+0].size; muu->hctx->volSize[1] = nin->axis[baseDim+1].size; muu->hctx->volSize[2] = nin->axis[baseDim+2].size; /* Get the proper spacing from the NRRD volume */ nrrdSpacingCalculate ( nin, baseDim+0, &(muu->hctx->volSpacing[0]), v ); nrrdSpacingCalculate ( nin, baseDim+1, &(muu->hctx->volSpacing[1]), v ); nrrdSpacingCalculate ( nin, baseDim+2, &(muu->hctx->volSpacing[2]), v ); } else { if (gageShapeSet(muu->shape, nin, baseDim)) { fprintf(stderr, "%s: problem with shape:\n%s\n", me, errS = biffGetDone(GAGE)); free(errS); airMopError(mop); return 1; } muu->hctx->shape = muu->shape; } muu->hctx->user = muu; muu->hctx->renderBegin = (hooverRenderBegin_t *)miteRenderBegin; muu->hctx->threadBegin = (hooverThreadBegin_t *)miteThreadBegin; muu->hctx->rayBegin = (hooverRayBegin_t *)miteRayBegin; muu->hctx->sample = (hooverSample_t *)miteSample; muu->hctx->rayEnd = (hooverRayEnd_t *)miteRayEnd; muu->hctx->threadEnd = (hooverThreadEnd_t *)miteThreadEnd; muu->hctx->renderEnd = (hooverRenderEnd_t *)miteRenderEnd; if (!airThreadCapable && 1 != muu->hctx->numThreads) { fprintf(stderr, "%s: This Teem not compiled with " "multi-threading support.\n", me); fprintf(stderr, "%s: ==> can't use %d threads; only using 1\n", me, muu->hctx->numThreads); muu->hctx->numThreads = 1; } fprintf(stderr, "%s: rendering ... ", me); fflush(stderr); E = hooverRender(muu->hctx, &Ecode, &Ethread); if (E) { if (hooverErrInit == E) { errS = biffGetDone(HOOVER); } else { errS = biffGetDone(MITE); } airMopAdd(mop, errS, airFree, airMopAlways); fprintf(stderr, "%s: %s error (code %d, thread %d):\n%s\n", me, airEnumStr(hooverErr, E), Ecode, Ethread, errS); airMopError(mop); return 1; } fprintf(stderr, "\n"); fprintf(stderr, "%s: rendering time = %g secs\n", me, muu->rendTime); fprintf(stderr, "%s: sampling rate = %g Khz\n", me, muu->sampRate); if (muu->ndebug) { /* if its been generated, we should save it */ sprintf(debugStr, "%04d-%04d-debug.nrrd", verbPix[0], verbPix[1]); if (nrrdSave(debugStr, muu->ndebug, NULL)) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving ray debug:\n%s\n", me, errS); airMopError(mop); return 1; } } if (nrrdSave(outS, muu->nout, NULL)) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving image:\n%s\n", me, errS); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/bin/vprobe.c0000664000175000017500000005356312174674257016640 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include static const char *probeInfo = ("Shows off the functionality of the gage library. " "Uses gageProbe() to query various kinds of volumes " "to learn various measured or derived quantities. " "Can set environment variable TEEM_VPROBE_HACK_ZI " "to limit probing to a single z slice."); int main(int argc, const char *argv[]) { gageKind *kind; const char *me; char *whatS, *err, *outS, *stackReadFormat, *stackSaveFormat; hestParm *hparm; hestOpt *hopt = NULL; NrrdKernelSpec *k00, *k11, *k22, *kSS, *kSSblur; int what, E=0, renorm, SSuniform, SSoptim, verbose, zeroZ, orientationFromSpacing, SSnormd; unsigned int iBaseDim, oBaseDim, axi, numSS, ninSSIdx, seed; const double *answer; Nrrd *nin, *nout, **ninSS=NULL; Nrrd *ngrad=NULL, *nbmat=NULL; size_t ai, ansLen, idx, xi, yi, zi, six, siy, siz, sox, soy, soz; double bval=0, gmc, rangeSS[2], wrlSS, idxSS=AIR_NAN, dsix, dsiy, dsiz, dsox, dsoy, dsoz; gageContext *ctx; gagePerVolume *pvl=NULL; double t0, t1, x, y, z, scale[3], rscl[3], min[3], maxOut[3], maxIn[3]; airArray *mop; unsigned int hackZi, *skip, skipNum; double (*ins)(void *v, size_t I, double d); gageStackBlurParm *sbp; char hackKeyStr[]="TEEM_VPROBE_HACK_ZI", *hackValStr; int otype, hackSet; char stmp[4][AIR_STRLEN_SMALL]; me = argv[0]; /* parse environment variables first, in case they break nrrdDefault* or nrrdState* variables in a way that nrrdSanity() should see */ nrrdDefaultGetenv(); nrrdStateGetenv(); /* no harm done in making sure we're sane */ if (!nrrdSanity()) { fprintf(stderr, "******************************************\n"); fprintf(stderr, "******************************************\n"); fprintf(stderr, "\n"); fprintf(stderr, " %s: nrrd sanity check FAILED.\n", me); fprintf(stderr, "\n"); fprintf(stderr, " This means that either nrrd can't work on this " "platform, or (more likely)\n"); fprintf(stderr, " there was an error in the compilation options " "and variable definitions\n"); fprintf(stderr, " for how Teem was built here.\n"); fprintf(stderr, "\n"); fprintf(stderr, " %s\n", err = biffGetDone(NRRD)); fprintf(stderr, "\n"); fprintf(stderr, "******************************************\n"); fprintf(stderr, "******************************************\n"); free(err); return 1; } mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, AIR_CAST(airMopper, hestParmFree), airMopAlways); hparm->elideSingleOtherType = AIR_TRUE; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL, "\"kind\" of volume (\"scalar\", \"vector\", " "\"tensor\", or \"dwi\")", NULL, NULL, meetHestGageKind); hestOptAdd(&hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1", "verbosity level"); hestOptAdd(&hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL, "the quantity (scalar, vector, or matrix) to learn by probing"); hestOptAdd(&hopt, "s", "sclX sclY sxlZ", airTypeDouble, 3, 3, scale, "1.0 1.0 1.0", "scaling factor for resampling on each axis " "(>1.0 : supersampling)"); hestOptAdd(&hopt, "k00", "kern00", airTypeOther, 1, 1, &k00, "tent", "kernel for gageKernel00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kern11", airTypeOther, 1, 1, &k11, "cubicd:1,0", "kernel for gageKernel11", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kern22", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "kernel for gageKernel22", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "seed", "N", airTypeUInt, 1, 1, &seed, "42", "RNG seed; mostly for debugging"); hestOptAdd(&hopt, "zz", "bool", airTypeBool, 1, 1, &zeroZ, "false", "enable \"zeroZ\" behavior in gage that partially " "implements working with 3D images as if they are 2D"); hestOptAdd(&hopt, "ssn", "SS #", airTypeUInt, 1, 1, &numSS, "0", "how many scale-space samples to evaluate, or, " "0 to turn-off all scale-space behavior"); hestOptAdd(&hopt, "ssr", "scale range", airTypeDouble, 2, 2, rangeSS, "nan nan", "range of scales in scale-space"); hestOptAdd(&hopt, "ssrf", "SS read format", airTypeString, 1, 1, &stackReadFormat, "", "printf-style format (including a \"%u\") for the " "filenames from which to *read* " "pre-blurred volumes computed for the stack"); hestOptAdd(&hopt, "sssf", "SS save format", airTypeString, 1, 1, &stackSaveFormat, "", "printf-style format (including a \"%u\") for the " "filenames in which to *save* " "pre-blurred volumes computed for the stack"); hestOptAdd(&hopt, "ssw", "SS pos", airTypeDouble, 1, 1, &wrlSS, "0", "\"world\"-space position (true sigma) " "at which to sample in scale-space"); hestOptAdd(&hopt, "kssb", "kernel", airTypeOther, 1, 1, &kSSblur, "dgauss:1,5", "blurring kernel, to sample scale space", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "kssr", "kernel", airTypeOther, 1, 1, &kSS, "hermite", "kernel for reconstructing from scale space samples", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "ssu", NULL, airTypeInt, 0, 0, &SSuniform, NULL, "do uniform samples along sigma, and not (by default) " "samples according to the effective diffusion scale"); hestOptAdd(&hopt, "sso", NULL, airTypeInt, 0, 0, &SSoptim, NULL, "if not using \"-ssu\", use pre-computed optimal " "sigmas when possible"); hestOptAdd(&hopt, "ssnd", NULL, airTypeInt, 0, 0, &SSnormd, NULL, "normalize derivatives by scale"); hestOptAdd(&hopt, "rn", NULL, airTypeInt, 0, 0, &renorm, NULL, "renormalize kernel weights at each new sample location. " "\"Accurate\" kernels don't need this; doing it always " "makes things go slower"); hestOptAdd(&hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc, "0.0", "For curvature-based queries, use zero when gradient " "magnitude is below this"); hestOptAdd(&hopt, "ofs", "ofs", airTypeInt, 0, 0, &orientationFromSpacing, NULL, "If only per-axis spacing is available, use that to " "contrive full orientation info"); hestOptAdd(&hopt, "t", "type", airTypeEnum, 1, 1, &otype, "float", "type of output volume", NULL, nrrdType); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output volume"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, probeInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, AIR_CAST(airMopper, hestOptFree), airMopAlways); airMopAdd(mop, hopt, AIR_CAST(airMopper, hestParseFree), airMopAlways); what = airEnumVal(kind->enm, whatS); if (!what) { /* 0 indeed always means "unknown" for any gageKind */ fprintf(stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n", me, whatS, kind->name); hestUsage(stderr, hopt, me, hparm); hestGlossary(stderr, hopt, hparm); airMopError(mop); return 1; } /* special set-up required for DWI kind */ if (!strcmp(TEN_DWI_GAGE_KIND_NAME, kind->name)) { if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &bval, &skip, &skipNum, nin)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } if (skipNum) { fprintf(stderr, "%s: sorry, can't do DWI skipping in tenDwiGage", me); airMopError(mop); return 1; } /* this could stand to use some more command-line arguments */ if (tenDwiGageKindSet(kind, 50, 1, bval, 0.001, ngrad, nbmat, tenEstimate1MethodLLS, tenEstimate2MethodQSegLLS, seed)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } } /* for setting up pre-blurred scale-space samples */ if (numSS) { unsigned int vi; sbp = gageStackBlurParmNew(); airMopAdd(mop, sbp, (airMopper)gageStackBlurParmNix, airMopAlways); ninSS = AIR_CAST(Nrrd **, calloc(numSS, sizeof(Nrrd *))); if (!ninSS) { fprintf(stderr, "%s: couldn't allocate ninSS", me); airMopError(mop); return 1; } for (ninSSIdx=0; ninSSIdxscale[vi], wrlSS, sbp->scale[vi+1])) { idxSS = vi + AIR_AFFINE(sbp->scale[vi], wrlSS, sbp->scale[vi+1], 0, 1); if (verbose > 1) { fprintf(stderr, "%s: scale pos %g -> idx %g = %u + %g\n", me, wrlSS, idxSS, vi, AIR_AFFINE(sbp->scale[vi], wrlSS, sbp->scale[vi+1], 0, 1)); } break; } } if (vi == numSS-1) { fprintf(stderr, "%s: scale pos %g outside range %g=%g, %g=%g\n", me, wrlSS, rangeSS[0], sbp->scale[0], rangeSS[1], sbp->scale[numSS-1]); airMopError(mop); return 1; } } else { ninSS = NULL; sbp = NULL; } /*** **** Except for the gageProbe() call in the inner loop below, **** and the gageContextNix() call at the very end, all the gage **** calls which set up (and take down) the context and state are here. ***/ ctx = gageContextNew(); airMopAdd(mop, ctx, AIR_CAST(airMopper, gageContextNix), airMopAlways); gageParmSet(ctx, gageParmGradMagCurvMin, gmc); gageParmSet(ctx, gageParmVerbose, verbose); gageParmSet(ctx, gageParmTwoDimZeroZ, zeroZ); gageParmSet(ctx, gageParmRenormalize, renorm ? AIR_TRUE : AIR_FALSE); gageParmSet(ctx, gageParmCheckIntegrals, AIR_TRUE); gageParmSet(ctx, gageParmOrientationFromSpacing, orientationFromSpacing); E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(ctx, nin, kind)); if (!E) E |= gageKernelSet(ctx, gageKernel00, k00->kernel, k00->parm); if (!E) E |= gageKernelSet(ctx, gageKernel11, k11->kernel, k11->parm); if (!E) E |= gageKernelSet(ctx, gageKernel22, k22->kernel, k22->parm); if (numSS) { gagePerVolume **pvlSS; gageParmSet(ctx, gageParmStackUse, AIR_TRUE); gageParmSet(ctx, gageParmStackNormalizeDeriv, SSnormd); if (!E) E |= !(pvlSS = AIR_CAST(gagePerVolume **, calloc(numSS, sizeof(gagePerVolume *)))); if (!E) airMopAdd(mop, pvlSS, (airMopper)airFree, airMopAlways); if (!E) E |= gageStackPerVolumeNew(ctx, pvlSS, AIR_CAST(const Nrrd*const*, ninSS), numSS, kind); if (!E) E |= gageStackPerVolumeAttach(ctx, pvl, pvlSS, sbp->scale, numSS); if (!E) E |= gageKernelSet(ctx, gageKernelStack, kSS->kernel, kSS->parm); } else { if (!E) E |= gagePerVolumeAttach(ctx, pvl); } if (!E) E |= gageQueryItemOn(ctx, pvl, what); if (!E) E |= gageUpdate(ctx); if (E) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } answer = gageAnswerPointer(ctx, pvl, what); /*** **** end gage setup. ***/ ansLen = kind->table[what].answerLength; iBaseDim = kind->baseDim; oBaseDim = 1 == ansLen ? 0 : 1; six = nin->axis[0+iBaseDim].size; siy = nin->axis[1+iBaseDim].size; siz = nin->axis[2+iBaseDim].size; dsix = AIR_CAST(double, six); dsiy = AIR_CAST(double, siy); dsiz = AIR_CAST(double, siz); sox = AIR_CAST(size_t, scale[0]*dsix); soy = AIR_CAST(size_t, scale[1]*dsiy); soz = AIR_CAST(size_t, scale[2]*dsiz); dsox = AIR_CAST(double, sox); dsoy = AIR_CAST(double, soy); dsoz = AIR_CAST(double, soz); if (verbose) { fprintf(stderr, "%s: six,y,z = %u %u %u\n", me, AIR_UINT(six), AIR_UINT(siy), AIR_UINT(siz)); fprintf(stderr, "%s: sox,y,z = %u %u %u\n", me, AIR_UINT(sox), AIR_UINT(soy), AIR_UINT(soz)); } rscl[0] = dsix/dsox; rscl[1] = dsiy/dsoy; rscl[2] = dsiz/dsoz; if (verbose) { fprintf(stderr, "%s: kernel support = %d^3 samples\n", me, 2*ctx->radius); fprintf(stderr, "%s: effective scaling is %g %g %g\n", me, rscl[0], rscl[1], rscl[2]); } /* else, normal volume probing */ if (ansLen > 1) { if (verbose) { fprintf(stderr, "%s: creating %s x %s x %s x %s output\n", me, airSprintSize_t(stmp[0], ansLen), airSprintSize_t(stmp[1], sox), airSprintSize_t(stmp[2], soy), airSprintSize_t(stmp[3], soz)); } if (!E) E |= nrrdMaybeAlloc_va(nout=nrrdNew(), otype, 4, ansLen, sox, soy, soz); } else { if (verbose) { fprintf(stderr, "%s: creating %s x %s x %s output\n", me, airSprintSize_t(stmp[0], sox), airSprintSize_t(stmp[1], soy), airSprintSize_t(stmp[2], soz)); } if (!E) E |= nrrdMaybeAlloc_va(nout=nrrdNew(), otype, 3, sox, soy, soz); } if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } airMopAdd(mop, nout, AIR_CAST(airMopper, nrrdNuke), airMopAlways); hackSet = nrrdGetenvUInt(&hackZi, &hackValStr, hackKeyStr); if (AIR_FALSE == hackSet) { fprintf(stderr, "%s: couldn't parse value of \"%s\" (\"%s\") as uint\n", me, hackKeyStr, hackValStr); airMopError(mop); return 1; } if (AIR_TRUE == hackSet) { fprintf(stderr, "%s: %s hack on: will only measure Zi=%u\n", me, hackKeyStr, hackZi); } if (nrrdCenterCell == ctx->shape->center) { ELL_3V_SET(min, -0.5, -0.5, -0.5); ELL_3V_SET(maxOut, dsox-0.5, dsoy-0.5, dsoz-0.5); ELL_3V_SET(maxIn, dsix-0.5, dsiy-0.5, dsiz-0.5); } else { ELL_3V_SET(min, 0, 0, 0); ELL_3V_SET(maxOut, dsox-1, dsoy-1, dsoz-1); ELL_3V_SET(maxIn, dsix-1, dsiy-1, dsiz-1); } t0 = airTime(); ins = nrrdDInsert[nout->type]; gageParmSet(ctx, gageParmVerbose, verbose/10); for (zi=0; zi 1) { fprintf(stderr, "z = "); } fprintf(stderr, " %s/%s", airSprintSize_t(stmp[0], zi), airSprintSize_t(stmp[1], soz-1)); fflush(stderr); if (verbose > 1) { fprintf(stderr, "\n"); } } if (AIR_TRUE == hackSet) { if (hackZi != zi) { continue; } } z = AIR_AFFINE(min[2], zi, maxOut[2], min[2], maxIn[2]); for (yi=0; yi 2) { fprintf(stderr, " (%u,%u)/(%u,%u)", AIR_UINT(xi), AIR_UINT(yi), AIR_UINT(sox), AIR_UINT(soy)); fflush(stderr); } x = AIR_AFFINE(min[0], xi, maxOut[0], min[0], maxIn[0]); idx = xi + sox*(yi + soy*zi); E = (numSS ? gageStackProbe(ctx, x, y, z, idxSS) : gageProbe(ctx, x, y, z)); if (E) { fprintf(stderr, "%s: trouble at i=(%s,%s,%s) -> f=(%g,%g,%g):\n%s\n(%d)\n", me, airSprintSize_t(stmp[0], xi), airSprintSize_t(stmp[1], yi), airSprintSize_t(stmp[2], zi), x, y, z, ctx->errStr, ctx->errNum); airMopError(mop); return 1; } if (1 == ansLen) { ins(nout->data, idx, *answer); } else { for (ai=0; ai<=ansLen-1; ai++) { ins(nout->data, ai + ansLen*idx, answer[ai]); } } } } } /* HEY: this isn't actually correct in general, but is true for gageKindScl and gageKindVec */ nrrdContentSet_va(nout, "probe", nin, "%s", airEnumStr(kind->enm, what)); for (axi=0; axi<3; axi++) { /* HEY: why not using nrrdAxisInfoCopy? */ nout->axis[axi+oBaseDim].label = airStrdup(nin->axis[axi+iBaseDim].label); nout->axis[axi+oBaseDim].center = ctx->shape->center; nout->axis[axi+oBaseDim].kind = nin->axis[axi+iBaseDim].kind; } nrrdBasicInfoCopy(nout, nin, (NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_SPACEORIGIN_BIT | NRRD_BASIC_INFO_OLDMIN_BIT | NRRD_BASIC_INFO_OLDMAX_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT)); if (ctx->shape->fromOrientation) { if (nin->space) { nrrdSpaceSet(nout, nin->space); } else { nrrdSpaceDimensionSet(nout, nin->spaceDim); } nrrdSpaceVecCopy(nout->spaceOrigin, nin->spaceOrigin); for (axi=0; axi<3; axi++) { nrrdSpaceVecScale(nout->axis[axi+oBaseDim].spaceDirection, rscl[axi], nin->axis[axi+iBaseDim].spaceDirection); z = AIR_AFFINE(min[axi], 0, maxOut[axi], min[axi], maxIn[axi]); nrrdSpaceVecScaleAdd2(nout->spaceOrigin, 1.0, nout->spaceOrigin, z, nin->axis[axi+iBaseDim].spaceDirection); } } else { for (axi=0; axi<3; axi++) { nout->axis[axi+oBaseDim].spacing = rscl[axi]*nin->axis[axi+iBaseDim].spacing; } } fprintf(stderr, "\n"); t1 = airTime(); fprintf(stderr, "probe rate = %g KHz\n", dsox*dsoy*dsoz/(1000.0*(t1-t0))); if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/bin/mrender.c0000664000175000017500000006113312165631065016755 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #define MREND "mrender" static const char *info = ("A demonstration of hoover, gage, and nrrd measures. " "Uses hoover to cast rays through a volume (scalar, vector, or " "tensor), gage to " "measure one of various quantities along the rays, and a " "specified nrrd measure to reduce all the values along a ray " "down to one scalar, which is saved in the output (double) " "image."); /* -------------------------------------------------------------- */ /* Even though the gageContext is really thread-specific, and therefore doesn't really belong in mrendUser, the first context from which all others is copied is logically shared across threads, as are the input parameter it contains. There is a per-thread gageContext pointer in mrendThread */ typedef struct { Nrrd *nin; /* input volume to render */ gageKind *kind; /* the kind of volume it is */ int verbPixel[2]; /* which pixel to do verbose stuff on */ double rayStep, /* distance between sampling planes */ fromNaN; /* what to convert non-existent value to */ int whatq, /* what to measure along the ray */ measr; /* how to reduce the ray samples to a scalar */ /* we have a separate copy of the kernel specs so that hest can set these, and then we'll gageKernelSet() them in the context in order to do the proper error checking- hest can't do the error checking that we need. */ NrrdKernelSpec *ksp[GAGE_KERNEL_MAX+1]; gageShape *shape; /* used to document ItoW transform */ gageContext *gctx0; /* gage input and parent thread state */ hooverContext *hctx; /* hoover input and state */ char *outS; /* (managed by hest) output filename */ double imgOrig[3], /* output: set as extra info about camera */ imgU[3], imgV[3]; airArray *mrmop; } mrendUser; mrendUser * mrendUserNew(void) { mrendUser *uu; int i; uu = AIR_CALLOC(1, mrendUser); uu->nin = NULL; uu->kind = NULL; uu->rayStep = 0.0; uu->whatq = gageSclUnknown; uu->measr = nrrdMeasureUnknown; for (i=gageKernelUnknown+1; iksp[i] = NULL; } uu->shape = gageShapeNew(); uu->gctx0 = gageContextNew(); uu->hctx = hooverContextNew(); uu->outS = NULL; uu->mrmop = airMopNew(); airMopAdd(uu->mrmop, uu->shape, (airMopper)gageShapeNix, airMopAlways); airMopAdd(uu->mrmop, uu->gctx0, (airMopper)gageContextNix, airMopAlways); airMopAdd(uu->mrmop, uu->hctx, (airMopper)hooverContextNix, airMopAlways); return uu; } mrendUser * mrendUserNix(mrendUser *uu) { if (uu) { airMopOkay(uu->mrmop); airFree(uu); } return NULL; } int mrendUserCheck(mrendUser *uu) { static const char me[]="mrendUserCheck"; if (3 + uu->kind->baseDim != uu->nin->dim) { biffAddf(MREND, "%s: input nrrd needs %d dimensions, not %d", me, + uu->kind->baseDim, uu->nin->dim); return 1; } if (!( uu->nin->axis[0].center == uu->nin->axis[1].center && uu->nin->axis[0].center == uu->nin->axis[2].center )) { biffAddf(MREND, "%s: axes 0,1,2 centerings (%s,%s,%s) not equal", me, airEnumStr(nrrdCenter, uu->nin->axis[0].center), airEnumStr(nrrdCenter, uu->nin->axis[1].center), airEnumStr(nrrdCenter, uu->nin->axis[2].center)); return 1; } if (1 != uu->kind->table[uu->whatq].answerLength) { biffAddf(MREND, "%s: quantity %s (in %s volumes) isn't a scalar; " "can't render it", me, airEnumStr(uu->kind->enm, uu->whatq), uu->kind->name); return 1; } return 0; } /* -------------------------------------------------------------- */ typedef struct mrendRender_t { double time0, time1; /* render start and end times */ Nrrd *nout; /* output image: always 2D array of doubles */ double *imgData; /* output image data */ int sx, sy, /* image dimensions */ totalSamples; /* total number of samples used for all rays */ struct mrendThread_t *tinfo[HOOVER_THREAD_MAX]; } mrendRender; typedef struct mrendThread_t { double *val, /* array of ray samples */ rayLen, /* length of ray segment between near and far */ rayStep; /* ray step needed FOR THIS RAY, to acheive sampling on planes (same scaling of uu->rayStep) */ int thrid, /* thread ID */ valLen, /* allocated size of val */ valNum, /* number of values set in val (index of next value) */ ui, vi, /* image coords */ numSamples, /* total number of samples this thread has done */ verbose; /* blah blah blah blah */ gageContext *gctx; /* thread-specific gage context (or copy of uu->gctx0 for the first thread) */ const double *answer; /* pointer to the SINGLE answer we care about */ } mrendThread; int mrendRenderBegin(mrendRender **rrP, mrendUser *uu) { static const char me[]="mrendRenderBegin"; gagePerVolume *pvl; int E; unsigned int thr; /* this assumes that mrendUserCheck(uu) has passed */ *rrP = AIR_CALLOC(1, mrendRender); airMopAdd(uu->mrmop, *rrP, airFree, airMopAlways); /* pvl managed via parent context */ (*rrP)->time0 = airTime(); E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(uu->gctx0, uu->nin, uu->kind)); if (!E) E |= gagePerVolumeAttach(uu->gctx0, pvl); if (!E) E |= gageKernelSet(uu->gctx0, gageKernel00, uu->ksp[gageKernel00]->kernel, uu->ksp[gageKernel00]->parm); if (!E) E |= gageKernelSet(uu->gctx0, gageKernel11, uu->ksp[gageKernel11]->kernel, uu->ksp[gageKernel11]->parm); if (!E) E |= gageKernelSet(uu->gctx0, gageKernel22, uu->ksp[gageKernel22]->kernel, uu->ksp[gageKernel22]->parm); if (!E) E |= gageQueryItemOn(uu->gctx0, pvl, uu->whatq); if (!E) E |= gageUpdate(uu->gctx0); if (E) { biffMovef(MREND, GAGE, "%s: gage trouble", me); return 1; } fprintf(stderr, "%s: kernel support = %d^3 samples\n", me, 2*uu->gctx0->radius); if (nrrdMaybeAlloc_va((*rrP)->nout=nrrdNew(), nrrdTypeDouble, 2, AIR_CAST(size_t, uu->hctx->imgSize[0]), AIR_CAST(size_t, uu->hctx->imgSize[1]))) { biffMovef(MREND, NRRD, "%s: nrrd trouble", me); return 1; } (*rrP)->nout->axis[0].min = uu->hctx->cam->uRange[0]; (*rrP)->nout->axis[0].max = uu->hctx->cam->uRange[1]; (*rrP)->nout->axis[1].min = uu->hctx->cam->vRange[0]; (*rrP)->nout->axis[1].max = uu->hctx->cam->vRange[1]; airMopAdd(uu->mrmop, (*rrP)->nout, (airMopper)nrrdNuke, airMopAlways); (*rrP)->imgData = AIR_CAST(double*, (*rrP)->nout->data); (*rrP)->sx = uu->hctx->imgSize[0]; (*rrP)->sy = uu->hctx->imgSize[1]; for (thr=0; thrhctx->numThreads; thr++) { (*rrP)->tinfo[thr] = AIR_CALLOC(1, mrendThread); airMopAdd(uu->mrmop, (*rrP)->tinfo[thr], airFree, airMopAlways); } return 0; } int mrendRenderEnd(mrendRender *rr, mrendUser *uu) { static const char me[]="mrendRenderEnd"; unsigned int thr; /* add up # samples from all threads */ rr->totalSamples = 0; for (thr=0; thrhctx->numThreads; thr++) { rr->totalSamples += rr->tinfo[thr]->numSamples; } rr->time1 = airTime(); fprintf(stderr, "\n"); fprintf(stderr, "%s: rendering time = %g secs\n", me, rr->time1 - rr->time0); fprintf(stderr, "%s: sampling rate = %g KHz\n", me, rr->totalSamples/(1000.0*(rr->time1 - rr->time0))); if (nrrdSave(uu->outS, rr->nout, NULL)) { biffMovef(MREND, NRRD, "%s: trouble saving image", me); return 1; } return 0; } /* -------------------------------------------------------------- */ int mrendThreadBegin(mrendThread **ttP, mrendRender *rr, mrendUser *uu, int whichThread) { /* allocating the mrendThreads should be part of the thread body, but as long as there isn't a mutex around registering them with the airMop in the mrendRender, then all that needs to be done as part of mrendRenderBegin (see above) */ (*ttP) = rr->tinfo[whichThread]; if (!whichThread) { /* this is the first thread- it just points to the parent gageContext */ (*ttP)->gctx = uu->gctx0; } else { /* we have to generate a new gageContext */ (*ttP)->gctx = gageContextCopy(uu->gctx0); } (*ttP)->answer = gageAnswerPointer((*ttP)->gctx, (*ttP)->gctx->pvl[0], uu->whatq); (*ttP)->val = NULL; (*ttP)->valLen = 0; (*ttP)->valNum = 0; (*ttP)->rayLen = 0; (*ttP)->thrid = whichThread; (*ttP)->numSamples = 0; (*ttP)->verbose = 0; return 0; } int mrendThreadEnd(mrendThread *tt, mrendRender *rr, mrendUser *uu) { AIR_UNUSED(rr); AIR_UNUSED(uu); if (tt->thrid) { tt->gctx = gageContextNix(tt->gctx); } tt->val = AIR_CAST(double*, airFree(tt->val)); return 0; } /* -------------------------------------------------------------- */ int mrendRayBegin(mrendThread *tt, mrendRender *rr, mrendUser *uu, int uIndex, int vIndex, double rayLen, double rayStartWorld[3], double rayStartIndex[3], double rayDirWorld[3], double rayDirIndex[3]) { static const char me[]="mrendRayBegin"; int newLen; AIR_UNUSED(rr); AIR_UNUSED(rayStartWorld); AIR_UNUSED(rayStartIndex); AIR_UNUSED(rayDirWorld); AIR_UNUSED(rayDirIndex); tt->ui = uIndex; tt->vi = vIndex; if (!( -1 == uu->verbPixel[0] && -1 == uu->verbPixel[1] )) { if (uIndex == uu->verbPixel[0] && vIndex == uu->verbPixel[1]) { fprintf(stderr, "\n%s: verbose for pixel (%d,%d)\n", me, uu->verbPixel[0], uu->verbPixel[1]); gageParmSet(tt->gctx, gageParmVerbose, 6); tt->verbose = 6; } else { gageParmSet(tt->gctx, gageParmVerbose, AIR_FALSE); tt->verbose = 0; } } tt->rayLen = rayLen; tt->rayStep = (uu->rayStep*tt->rayLen / (uu->hctx->cam->vspFaar - uu->hctx->cam->vspNeer)); newLen = AIR_ROUNDUP(rayLen/tt->rayStep) + 1; if (!tt->val || newLen > tt->valLen) { tt->val = AIR_CAST(double*, airFree(tt->val)); tt->valLen = newLen; tt->val = AIR_CALLOC(newLen, double); } tt->valNum = 0; if (!uIndex) { fprintf(stderr, "%d/%d ", vIndex, uu->hctx->imgSize[1]); fflush(stderr); } if (0 == uIndex && 0 == vIndex) { ELL_3V_COPY(uu->imgOrig, rayStartWorld); } else if (1 == uIndex && 0 == vIndex) { ELL_3V_COPY(uu->imgU, rayStartWorld); /* will fix later */ } else if (0 == uIndex && 1 == vIndex) { ELL_3V_COPY(uu->imgV, rayStartWorld); /* will fix later */ } fflush(stderr); return 0; } int mrendRayEnd(mrendThread *tt, mrendRender *rr, mrendUser *uu) { double answer; if (tt->valNum) { nrrdMeasureLine[uu->measr](&answer, nrrdTypeDouble, tt->val, nrrdTypeDouble, tt->valNum, 0, tt->rayLen); answer = AIR_EXISTS(answer) ? answer : uu->fromNaN; rr->imgData[(tt->ui) + (rr->sx)*(tt->vi)] = answer; } else { rr->imgData[(tt->ui) + (rr->sx)*(tt->vi)] = 0.0; } return 0; } /* -------------------------------------------------------------- */ double mrendSample(mrendThread *tt, mrendRender *rr, mrendUser *uu, int num, double rayT, int inside, double samplePosWorld[3], double samplePosIndex[3]) { static const char me[]="mrendSample"; AIR_UNUSED(rr); AIR_UNUSED(uu); AIR_UNUSED(num); AIR_UNUSED(rayT); AIR_UNUSED(samplePosWorld); if (tt->verbose) { fprintf(stderr, "%s: wrld(%g,%g,%g) -> indx(%g,%g,%g) -> %s\n", me, samplePosWorld[0], samplePosWorld[1], samplePosWorld[2], samplePosIndex[0], samplePosIndex[1], samplePosIndex[2], inside ? "INSIDE" : "(outside)"); } if (inside) { if (gageProbe(tt->gctx, samplePosIndex[0], samplePosIndex[1], samplePosIndex[2])) { biffAddf(MREND, "%s: gage trouble: %s (%d)", me, tt->gctx->errStr, tt->gctx->errNum); return AIR_NAN; } if (tt->verbose) { fprintf(stderr, "%s: val[%d] = %g\n", me, tt->valNum, *(tt->answer)); } tt->val[tt->valNum++] = *(tt->answer); if (tt->verbose) { fprintf(stderr, " ........ %g\n", tt->val[tt->valNum-1]); } tt->numSamples++; } return tt->rayStep; } /* -------------------------------------------------------------- */ #if 0 this was nixed once mrender learned to handle volume of general kind, instead of being restricted to scalars /* ** learned: if you're playing games with strings with two passes, where ** you first generate the set of strings in order to calculate their ** cumulative length, and then (2nd pass) concatenate the strings ** together, be very sure that the generation of the strings on the ** two passes is identical. Had a very troublesome memory error because ** I was using short version of the description string to determine ** allocation, and then the long version in the second pass */ char * mrendGage(char *prefix) { char *line, *ret; int i, len; /* 1st pass through- determine needed buffer size */ len = 0; for (i=airEnumUnknown(gageScl)+1; !airEnumValCheck(gageScl, i); i++) { if (1 == gageKindScl->table[i].answerLength) { line = airEnumFmtDesc(gageScl, i, AIR_FALSE, "\n \b\bo \"%s\": %s"); len += strlen(line); free(line); } } ret = AIR_CALLOC(strlen(prefix) + len + 1, char); if (ret) { strcpy(ret, prefix); /* 2nd pass through: create output */ for (i=airEnumUnknown(gageScl)+1; !airEnumValCheck(gageScl, i); i++) { if (1 == gageKindScl->table[i].answerLength) { line = airEnumFmtDesc(gageScl, i, AIR_FALSE, "\n \b\bo \"%s\": %s"); strcat(ret, line); free(line); } } } return ret; } #endif int main(int argc, const char *argv[]) { hestOpt *hopt=NULL; hestParm *hparm; int E, Ecode, Ethread, renorm, offfr; const char *me; char *errS, *whatS; mrendUser *uu; float isScale; airArray *mop; double gmc, turn, eye[3], eyedist; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hparm->respFileEnable = AIR_TRUE; uu = mrendUserNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); airMopAdd(mop, uu, (airMopper)mrendUserNix, airMopAlways); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &(uu->nin), NULL, "input nrrd to render", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "k", "kind", airTypeOther, 1, 1, &(uu->kind), NULL, "\"kind\" of volume (\"scalar\", \"vector\", or \"tensor\")", NULL, NULL, meetHestGageKind); limnHestCameraOptAdd(&hopt, uu->hctx->cam, NULL, "0 0 0", "0 0 1", NULL, NULL, NULL, "nan nan", "nan nan", "20"); hestOptAdd(&hopt, "offfr", NULL, airTypeInt, 0, 0, &offfr, NULL, "the given eye point (\"-fr\") is to be interpreted " "as an offset from the at point."); hestOptAdd(&hopt, "turn", "angle", airTypeDouble, 1, 1, &turn, "0.0", "angle (degrees) by which to rotate the from point around " "true up, for making stereo pairs. Positive means move " "towards positive U (the right)"); hestOptAdd(&hopt, "is", "image size", airTypeInt, 2, 2, uu->hctx->imgSize, "256 256", "image dimensions"); hestOptAdd(&hopt, "iss", "scale", airTypeFloat, 1, 1, &isScale, "1.0", "scaling of image size (from \"is\")"); hestOptAdd(&hopt, "k00", "kernel", airTypeOther, 1, 1, &(uu->ksp[gageKernel00]), "tent", "value reconstruction kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kernel", airTypeOther, 1, 1, &(uu->ksp[gageKernel11]), "cubicd:1,0", "first derivative kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kernel", airTypeOther, 1, 1, &(uu->ksp[gageKernel22]), "cubicdd:1,0", "second derivative kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "rn", NULL, airTypeBool, 0, 0, &renorm, NULL, "renormalize kernel weights at each new sample location. " "\"Accurate\" kernels don't need this; doing it always " "makes things go slower"); hestOptAdd(&hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL, "the quantity (scalar, vector, or matrix) to learn by probing"); hestOptAdd(&hopt, "m", "measure", airTypeEnum, 1, 1, &(uu->measr), NULL, "how to collapse list of ray samples into one scalar. " NRRD_MEASURE_DESC, NULL, nrrdMeasure); hestOptAdd(&hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc, "0.0", "For curvature-related queries, set answer to zero when " "gradient magnitude is below this"); hestOptAdd(&hopt, "fn", "from nan", airTypeDouble, 1, 1, &(uu->fromNaN), "nan", "When histo-based measures generate NaN answers, the " "value that should be substituted for NaN."); hestOptAdd(&hopt, "step", "size", airTypeDouble, 1, 1, &(uu->rayStep), "0.01", "step size along ray in world space"); hestOptAdd(&hopt, "nt", "# threads", airTypeInt, 1, 1, &(uu->hctx->numThreads), "1", "number of threads hoover should use"); hestOptAdd(&hopt, "vp", "img coords", airTypeInt, 2, 2, &(uu->verbPixel), "-1 -1", "pixel coordinates for which to turn on all verbose " "debugging messages, or \"-1 -1\" to disable this."); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &(uu->outS), "-", "file to write output nrrd to. Defaults to stdout (\"-\")."); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); uu->hctx->imgSize[0] = AIR_CAST(int, isScale*uu->hctx->imgSize[0]); uu->hctx->imgSize[1] = AIR_CAST(int, isScale*uu->hctx->imgSize[1]); uu->whatq = airEnumVal(uu->kind->enm, whatS); if (-1 == uu->whatq) { /* -1 indeed always means "unknown" for any gageKind */ fprintf(stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n", me, whatS, uu->kind->name); hestUsage(stderr, hopt, me, hparm); hestGlossary(stderr, hopt, hparm); airMopError(mop); return 1; } if (mrendUserCheck(uu)) { fprintf(stderr, "%s: problem with input parameters:\n%s\n", me, errS = biffGetDone(MREND)); free(errS); airMopError(mop); return 1; } if (gageShapeSet(uu->shape, uu->nin, uu->kind->baseDim)) { fprintf(stderr, "%s: problem with shape:\n%s\n", me, errS = biffGetDone(GAGE)); free(errS); airMopError(mop); return 1; } gageParmSet(uu->gctx0, gageParmGradMagCurvMin, gmc); /* gageParmSet(uu->gctx0, gageParmRequireAllSpacings, AIR_FALSE); */ gageParmSet(uu->gctx0, gageParmRenormalize, renorm); fprintf(stderr, "%s: will render %s of %s in %s volume\n", me, airEnumStr(nrrdMeasure, uu->measr), airEnumStr(uu->kind->enm, uu->whatq), uu->kind->name); if (offfr) { ELL_3V_INCR(uu->hctx->cam->from, uu->hctx->cam->at); } if (limnCameraAspectSet(uu->hctx->cam, uu->hctx->imgSize[0], uu->hctx->imgSize[1], nrrdCenterCell) || limnCameraUpdate(uu->hctx->cam)) { airMopAdd(mop, errS = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting camera:\n%s\n", me, errS); airMopError(mop); return 1; } if (turn) { turn *= AIR_PI/180; ELL_3V_SUB(eye, uu->hctx->cam->from, uu->hctx->cam->at); ELL_3V_NORM(eye, eye, eyedist); ELL_3V_SCALE_ADD2(uu->hctx->cam->from, cos(turn), eye, sin(turn), uu->hctx->cam->U); ELL_3V_SCALE(uu->hctx->cam->from, eyedist, uu->hctx->cam->from); if (limnCameraUpdate(uu->hctx->cam)) { airMopAdd(mop, errS = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting camera (again):\n%s\n", me, errS); airMopError(mop); return 1; } } /* fprintf(stderr, "%s: camera info\n", me); fprintf(stderr, " U = {%g,%g,%g}\n", uu->hctx->cam->U[0], uu->hctx->cam->U[1], uu->hctx->cam->U[2]); fprintf(stderr, " V = {%g,%g,%g}\n", uu->hctx->cam->V[0], uu->hctx->cam->V[1], uu->hctx->cam->V[2]); fprintf(stderr, " N = {%g,%g,%g}\n", uu->hctx->cam->N[0], uu->hctx->cam->N[1], uu->hctx->cam->N[2]); */ /* set remaining fields of hoover context */ if (0) { /* this is the old code that assumed everything about orientation was determined by per-axis spacing (pre-gageShape) */ unsigned int base; base = uu->kind->baseDim; uu->hctx->volSize[0] = uu->nin->axis[base+0].size; uu->hctx->volSize[1] = uu->nin->axis[base+1].size; uu->hctx->volSize[2] = uu->nin->axis[base+2].size; uu->hctx->volSpacing[0] = uu->nin->axis[base+0].spacing; uu->hctx->volSpacing[1] = uu->nin->axis[base+1].spacing; uu->hctx->volSpacing[2] = uu->nin->axis[base+2].spacing; if (nrrdCenterUnknown != uu->nin->axis[base].center) { uu->hctx->volCentering = uu->nin->axis[base].center; fprintf(stderr, "%s: setting volCentering to %s\n", me, airEnumStr(nrrdCenter, uu->nin->axis[base].center)); } fprintf(stderr, "!%s: uu->hctx->volCentering = %d\n", me, uu->hctx->volCentering); } else { uu->hctx->shape = uu->shape; } /* this is reasonable for now */ uu->hctx->imgCentering = nrrdCenterCell; uu->hctx->user = uu; uu->hctx->renderBegin = (hooverRenderBegin_t *)mrendRenderBegin; uu->hctx->threadBegin = (hooverThreadBegin_t *)mrendThreadBegin; uu->hctx->rayBegin = (hooverRayBegin_t *)mrendRayBegin; uu->hctx->sample = (hooverSample_t *)mrendSample; uu->hctx->rayEnd = (hooverRayEnd_t *)mrendRayEnd; uu->hctx->threadEnd = (hooverThreadEnd_t *)mrendThreadEnd; uu->hctx->renderEnd = (hooverRenderEnd_t *)mrendRenderEnd; if (!airThreadCapable && 1 != uu->hctx->numThreads) { fprintf(stderr, "%s: This Teem not compiled with " "multi-threading support.\n", me); fprintf(stderr, "%s: --> can't use %d threads; only using 1\n", me, uu->hctx->numThreads); uu->hctx->numThreads = 1; } E = hooverRender(uu->hctx, &Ecode, &Ethread); if (E) { if (hooverErrInit == E) { fprintf(stderr, "%s: ERROR (code %d, thread %d):\n%s\n", me, Ecode, Ethread, errS = biffGetDone(HOOVER)); free(errS); } else { fprintf(stderr, "%s: ERROR (code %d, thread %d):\n%s\n", me, Ecode, Ethread, errS = biffGetDone(MREND)); free(errS); } airMopError(mop); return 1; } if (1) { ELL_3V_SUB(uu->imgU, uu->imgU, uu->imgOrig); ELL_3V_SUB(uu->imgV, uu->imgV, uu->imgOrig); fprintf(stderr, "%s: loc(0,0) = (%g,%g,%g)\n", me, uu->imgOrig[0], uu->imgOrig[1], uu->imgOrig[2]); fprintf(stderr, "%s: loc(1,0) - loc(0,0) = (%g,%g,%g)\n", me, uu->imgU[0], uu->imgU[1], uu->imgU[2]); fprintf(stderr, "%s: loc(0,1) - loc(0,0) = (%g,%g,%g)\n", me, uu->imgV[0], uu->imgV[1], uu->imgV[2]); } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/bin/ungantry.c0000664000175000017500000001364112165631065017171 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include char info[]="Gantry tilt be gone! This program is actually of limited " "utility: it can only change the tilt by shearing with the " "X and Z axis fixed, by some angle \"around\" the X axis, assuming " "that (X,Y,Z) is a right-handed frame. "; int main(int argc, const char *argv[]) { hestParm *hparm; hestOpt *hopt = NULL; gageContext *ctx; gagePerVolume *pvl; Nrrd *nin, *nout; const char *me; char *outS; float angle; double xs, ys, zs, y, z, padval; const double *val; int sx, sy, sz, E, xi, yi, zi, clamp; NrrdKernelSpec *gantric; void *out; double (*insert)(void *v, size_t I, double d); me = argv[0]; hparm = hestParmNew(); hparm->elideSingleOtherType = AIR_TRUE; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume, in nrrd format", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "a", "angle", airTypeFloat, 1, 1, &angle, NULL, "angle, in degrees, of the gantry tilt around the X axis. " "This is opposite of the amount of tweak we apply."); hestOptAdd(&hopt, "k", "kern", airTypeOther, 1, 1, &gantric, "tent", "The kernel to use for resampling. Chances are, there " "is no justification for anything more than \"tent\". " "Possibilities include:\n " "\b\bo \"box\": nearest neighbor interpolation\n " "\b\bo \"tent\": linear interpolation\n " "\b\bo \"cubic:B,C\": Mitchell/Netravali BC-family of " "cubics:\n " "\t\t\"cubic:1,0\": B-spline; maximal blurring\n " "\t\t\"cubic:0,0.5\": Catmull-Rom; good interpolating kernel\n " "\b\bo \"quartic:A\": 1-parameter family of " "interpolating quartics (\"quartic:0.0834\" is most accurate)\n " "\b\bo \"gauss:S,C\": Gaussian blurring, with standard deviation " "S and cut-off at C standard deviations", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "clamp", NULL, airTypeInt, 0, 0, &clamp, NULL, "clamp sampling positions to inside original volume, " "effectively does a bleed of the boundary values"); hestOptAdd(&hopt, "p", "pad value", airTypeDouble, 1, 1, &padval, "0.0", "when NOT doing clampging (no \"-clamp\"), what value to the " "boundary of the volume with"); hestOptAdd(&hopt, "o", "output", airTypeString, 1, 1, &outS, NULL, "output volume in nrrd format"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); sx = nin->axis[0].size; sy = nin->axis[1].size; sz = nin->axis[2].size; xs = nin->axis[0].spacing; ys = nin->axis[1].spacing; zs = nin->axis[2].spacing; if (!(AIR_EXISTS(xs) && AIR_EXISTS(ys) && AIR_EXISTS(zs))) { fprintf(stderr, "%s: all axis spacings must exist in input nrrd\n", me); exit(1); } fprintf(stderr, "%s: input and output have dimensions %d %d %d\n", me, sx, sy, sz); /* start by just copying the nrrd; then we'll meddle with the values */ if (nrrdCopy(nout = nrrdNew(), nin)) { fprintf(stderr, "%s: trouble:\n%s\n", me, biffGet(NRRD)); exit(1); } out = nout->data; insert = nrrdDInsert[nout->type]; ctx = gageContextNew(); gageParmSet(ctx, gageParmVerbose, 1); gageParmSet(ctx, gageParmRenormalize, AIR_TRUE); E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(ctx, nin, gageKindScl)); if (!E) E |= gagePerVolumeAttach(ctx, pvl); if (!E) E |= gageKernelSet(ctx, gageKernel00, gantric->kernel, gantric->parm); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclValue); if (!E) E |= gageUpdate(ctx); if (E) { fprintf(stderr, "%s: trouble:\n%s\n", me, biffGet(GAGE)); exit(1); } gageParmSet(ctx, gageParmVerbose, 0); val = gageAnswerPointer(ctx, pvl, gageSclValue); for (zi=0; zi #include #include #include #include #define QBERT "qbert" #define QBERT_HIST_BINS 1024 /* histogram size for v, g, and h */ int qbertSaveAll = AIR_FALSE; /* can be used to save output of every stage */ /* ** This padding/resampling is to get axis[i]'s size >= sz[i], which ** is only needed if the input volume is smaller along any of the axes ** than the desired output volume. */ int qbertSizeUp(Nrrd *nout, Nrrd *nin, unsigned int *sz, NrrdKernelSpec *uk) { char me[]="qbertSizeUp"; int i, anyneed, need; ptrdiff_t padMin[3], padMax[3]; NrrdResampleInfo *rsi; airArray *mop; mop = airMopNew(); rsi=nrrdResampleInfoNew(); airMopAdd(mop, rsi, (airMopper)nrrdResampleInfoNix, airMopAlways); anyneed = 0; if (uk) { for (i=0; i<=2; i++) { anyneed |= need = sz[i] - nin->axis[i].size; fprintf(stderr, "%s: sz[%d] = %u -> need = %d --> ", me, i, AIR_UINT(nin->axis[i].size), need); need = AIR_MAX(0, need); fprintf(stderr, "%d --> %s resample\n", need, need ? "WILL" : "won't"); if (need) { rsi->kernel[i] = uk->kernel; memcpy(rsi->parm[i], uk->parm, uk->kernel->numParm*sizeof(double)); if (!AIR_EXISTS(nin->axis[i].min)) { nin->axis[i].min = 0.0; } if (!AIR_EXISTS(nin->axis[i].max)) { nin->axis[i].max = nin->axis[i].size-1; } rsi->min[i] = nin->axis[i].min; rsi->max[i] = nin->axis[i].max; rsi->samples[i] = sz[i]; nin->axis[i].center = nrrdCenterNode; } else { rsi->kernel[i] = NULL; } } if (anyneed) { rsi->boundary = nrrdBoundaryBleed; rsi->type = nrrdTypeFloat; rsi->renormalize = AIR_TRUE; rsi->clamp = AIR_TRUE; fprintf(stderr, "%s: resampling ... ", me); fflush(stderr); if (nrrdSpatialResample(nout, nin, rsi)) { biffMovef(QBERT, NRRD, "%s: trouble upsampling", me); airMopError(mop); return 1; } fprintf(stderr, "done\n"); } } else { for (i=0; i<=2; i++) { char stmp[AIR_STRLEN_SMALL]; anyneed |= need = sz[i] - nin->axis[i].size; fprintf(stderr, "%s: sz[%d] = %s -> need = %d --> ", me, i, airSprintSize_t(stmp, nin->axis[i].size), need); need = AIR_MAX(0, need); fprintf(stderr, "%d --> ", need); padMin[i] = 0 - (int)floor(need/2.0); padMax[i] = nin->axis[i].size - 1 + (int)ceil(need/2.0); fprintf(stderr, "pad indices: [%d..%d]\n", AIR_CAST(int, padMin[i]), AIR_CAST(int, padMax[i])); } if (anyneed) { fprintf(stderr, "%s: padding ... ", me); fflush(stderr); if (nrrdPad_va(nout, nin, padMin, padMax, nrrdBoundaryPad, 0.0)) { biffMovef(QBERT, NRRD, "%s: trouble padding", me); airMopError(mop); return 1; } fprintf(stderr, "done\n"); } } if (!anyneed) { if (nrrdCopy(nout, nin)) { biffMovef(QBERT, NRRD, "%s: trouble copying", me); airMopError(mop); return 1; } } if (qbertSaveAll) { fprintf(stderr, "%s: saving up.nrrd\n", me); nrrdSave("up.nrrd", nout, NULL); } airMopOkay(mop); return 0; } /* ** resampling to get axis[i]'s size down to exactly sz[i] */ int qbertSizeDown(Nrrd *nout, Nrrd *nin, unsigned int *sz, NrrdKernelSpec *dk) { char me[]="qbertSizeDown"; NrrdResampleInfo *rsi; int need; unsigned int i; airArray *mop; mop = airMopNew(); rsi = nrrdResampleInfoNew(); airMopAdd(mop, rsi, (airMopper)nrrdResampleInfoNix, airMopAlways); rsi->boundary = nrrdBoundaryBleed; rsi->type = nrrdTypeFloat; rsi->renormalize = AIR_TRUE; need = 0; for (i=0; i<=2; i++) { if (nin->axis[i].size > sz[i]) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; need = 1; rsi->kernel[i] = dk->kernel; memcpy(rsi->parm[i], dk->parm, dk->kernel->numParm*sizeof(double)); rsi->samples[i] = sz[i]; if (!AIR_EXISTS(nin->axis[i].min)) { nin->axis[i].min = 0.0; } if (!AIR_EXISTS(nin->axis[i].max)) { nin->axis[i].max = nin->axis[i].size-1; } rsi->min[i] = nin->axis[i].min; rsi->max[i] = nin->axis[i].max; nin->axis[i].center = nrrdCenterNode; fprintf(stderr, "%s: downsampling axis %d from %s to %s samples\n", me, i, airSprintSize_t(stmp1, nin->axis[i].size), airSprintSize_t(stmp2, rsi->samples[i])); } else { rsi->kernel[i] = NULL; } } if (need) { fprintf(stderr, "%s: resampling ... ", me); fflush(stderr); if (nrrdSpatialResample(nout, nin, rsi)) { biffMovef(QBERT, NRRD, "%s: trouble resampling", me); airMopError(mop); return 1; } fprintf(stderr, "done\n"); } else { if (nrrdCopy(nout, nin)) { biffMovef(QBERT, NRRD, "%s: trouble copying", me); airMopError(mop); return 1; } } if (qbertSaveAll) { fprintf(stderr, "%s: saving down.nrrd\n", me); nrrdSave("down.nrrd", nout, NULL); } airMopOkay(mop); return 0; } /* ** probing to getting floating point V, G, and maybe H values */ int qbertProbe(Nrrd *nout, Nrrd *nin, NrrdKernelSpec *k00, NrrdKernelSpec *k11, NrrdKernelSpec *k22, int doH, unsigned int *sz) { char me[]="qbertProbe", prog[AIR_STRLEN_SMALL]; gageContext *ctx; gagePerVolume *pvl; const double *val, *gmag, *scnd; float *vghF; int E; unsigned int i, j, k; airArray *mop; doH = !!doH; mop = airMopNew(); ctx = gageContextNew(); airMopAdd(mop, ctx, (airMopper)gageContextNix, airMopAlways); nin->axis[0].center = nrrdCenterNode; nin->axis[1].center = nrrdCenterNode; nin->axis[2].center = nrrdCenterNode; if (!(pvl = gagePerVolumeNew(ctx, nin, gageKindScl))) { biffMovef(QBERT, GAGE, "%s: gage trouble", me); airMopError(mop); return 1; } gageParmSet(ctx, gageParmVerbose, 0); gageParmSet(ctx, gageParmRenormalize, AIR_TRUE); gageParmSet(ctx, gageParmCheckIntegrals, AIR_TRUE); E = 0; if (!E) E |= gagePerVolumeAttach(ctx, pvl); /* about kernel setting for probing: currently, the way that probing is done is ONLY on grid locations, and never in between voxels. That means that the kernels set below are really only used for creating discrete convolution masks at unit locations */ if (!E) E |= gageKernelSet(ctx, gageKernel00, k00->kernel, k00->parm); if (!E) E |= gageKernelSet(ctx, gageKernel11, k11->kernel, k11->parm); if (!E) E |= gageKernelSet(ctx, gageKernel22, k22->kernel, k22->parm); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclValue); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclGradMag); if (doH) { if (!E) E |= gageQueryItemOn(ctx, pvl, gageScl2ndDD); } if (!E) E |= gageUpdate(ctx); if (E) { biffMovef(QBERT, GAGE, "%s: gage trouble", me); airMopError(mop); return 1; } gageParmSet(ctx, gageParmVerbose, 0); val = gageAnswerPointer(ctx, pvl, gageSclValue); gmag = gageAnswerPointer(ctx, pvl, gageSclGradMag); scnd = gageAnswerPointer(ctx, pvl, gageScl2ndDD); if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 4, AIR_CAST(size_t, 2+doH), AIR_CAST(size_t, sz[0]), AIR_CAST(size_t, sz[1]), AIR_CAST(size_t, sz[2]))) { biffMovef(QBERT, NRRD, "%s: couldn't allocate floating point VG%s volume", me, doH ? "H" : ""); airMopError(mop); return 1; } vghF = (float *)nout->data; fprintf(stderr, "%s: probing ... ", me); fflush(stderr); for (k=0; kaxis[0].size; doH = !!(nval == 3); vghF = (float *)nvghF->data; minv = maxv = vghF[0 + nval*0]; ming = maxg = vghF[1 + nval*0]; if (doH) { minh = maxh = vghF[2 + nval*0]; } for (i=0; i ", me, minv, maxv); /* just because we're bastards, we're going to enforce minv >= 0 for types that started as unsigned integral types. Downsampling with a ringing kernel can have produced negative values, so this change to minv can actually restrict the range, in contrast to to the changes to ming, minh, and maxh below */ if (nrrdTypeIsUnsigned[nin->type]) { minv = AIR_MAX(minv, 0.0); } fprintf(stderr, "[%g .. %g]\n", minv, maxv); fprintf(stderr, "%s: grads: [%g .. %g] -> ", me, ming, maxg); ming = 0; fprintf(stderr, "[%g .. %g]\n", ming, maxg); if (doH) { fprintf(stderr, "%s: 2ndDDs: [%g .. %g] -> ", me, minh, maxh); if (maxh > -minh) minh = -maxh; else maxh = -minh; fprintf(stderr, "[%g .. %g]\n", minh, maxh); } fprintf(stderr, "%s: using %d-bin histograms\n", me, bins); E = 0; if (!E) E |= nrrdMaybeAlloc_va(nvhist, nrrdTypeInt, 1, AIR_CAST(size_t, bins)); if (!E) E |= nrrdMaybeAlloc_va(nghist, nrrdTypeInt, 1, AIR_CAST(size_t, bins)); if (doH) { if (!E) E |= nrrdMaybeAlloc_va(nhhist, nrrdTypeInt, 1, AIR_CAST(size_t, bins)); } if (E) { biffMovef(QBERT, NRRD, "%s: couldn't allocate %d %d-bin histograms", me, nval, bins); return 1; } nvhist->axis[0].min = minv; nvhist->axis[0].max = maxv; nghist->axis[0].min = ming; nghist->axis[0].max = maxg; vhist = (int *)nvhist->data; ghist = (int *)nghist->data; memset(vhist, 0, bins*sizeof(int)); memset(ghist, 0, bins*sizeof(int)); if (doH) { nhhist->axis[0].min = minh; nhhist->axis[0].max = maxh; hhist = (int *)nhhist->data; memset(hhist, 0, bins*sizeof(int)); } vghF = (float *)nvghF->data; for (i=0; iaxis[0].size; doH = !!(nval == 3); minv = nvhist->axis[0].min; maxv = nvhist->axis[0].max; ming = nghist->axis[0].min; maxg = nghist->axis[0].max; vhist = (int *)nvhist->data; ghist = (int *)nghist->data; if (doH) { minh = nhhist->axis[0].min; maxh = nhhist->axis[0].max; hhist = (int *)nhhist->data; } lose = (int)(perc[0]*sz[0]*sz[1]*sz[2]/100); bins = nvhist->axis[0].size; i = bins-1; while (lose > 0) { /* HEY: we're nibbling only from top, even though for signed value types, there could be a tail at low negative values (had this problem with some CT data) */ lose -= vhist[i--]; } maxv = AIR_AFFINE(0, i, bins-1, minv, maxv); lose = (int)(perc[1]*sz[0]*sz[1]*sz[2]/100); bins = nghist->axis[0].size; i = bins-1; while (lose > 0) { /* nibble from top */ lose -= ghist[i--]; } maxg = AIR_AFFINE(0, i, bins-1, ming, maxg); if (doH) { lose = (int)(perc[2]*sz[0]*sz[1]*sz[2]/100); bins = nhhist->axis[0].size; i = 0; while (lose > 0) { /* nibble from top and bottom at equal rates */ lose -= hhist[i] + hhist[bins-1-i]; i++; } minh = AIR_AFFINE(0, i, bins-1, minh, maxh); maxh = -minh; } fprintf(stderr, "%s: new values (ignored %5d): [%g .. %g]\n", me, (int)(perc[0]*sz[0]*sz[1]*sz[2]/100), minv, maxv); fprintf(stderr, "%s: new grads (ignored %5d): [%g .. %g]\n", me, (int)(perc[1]*sz[0]*sz[1]*sz[2]/100), ming, maxg); if (doH) { fprintf(stderr, "%s: new 2ndDDs (ignored %5d): [%g .. %g]\n", me, (int)(perc[2]*sz[0]*sz[1]*sz[2]/100), minh, maxh); fprintf(stderr, "%s: putting 2ndDD in range 1 to 169 (0.0 -> 85)\n", me); } if (nrrdMaybeAlloc_va(nvgh, nrrdTypeUChar, 4, AIR_CAST(size_t, nval), AIR_CAST(size_t, sz[0]), AIR_CAST(size_t, sz[1]), AIR_CAST(size_t, sz[2]))) { biffMovef(QBERT, NRRD, "%s: couldn't allocate 8-bit VG%s volume", me, doH ? "H" : ""); return 1; } vgh = (unsigned char*)nvgh->data; vghF = (float*)nvghF->data; for (i=0; imax *= 0.8; } if (!E) E |= nrrdQuantize(nscB, nscA, range, 8); if (!E) E |= nrrdFlip(nscA, nscB, 1); if (!E) E |= nrrdSave(name, nscA, NULL); if (E) { biffMovef(QBERT, NRRD, "%s: trouble generating/saving scatterplot", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } static const char qbertInfo[]= "Generates volume datasets friendly to hardware-based " "volume renderers. " "The main value of this is a means of combining the functions of " "resampling a dataset to a particular size, measuring first (and " "optionally second) derivatives, and doing some semi-intelligent " "quantization of the derivative values down to 8-bits (if quantization " "is desired). The various up and down sampling, as well as the " "the VGH measurements, can be done with various nrrd kernels. Also, " "histogram-equalized " "VG and VH scatterplots can be generated at a specified resolution."; int main(int argc, const char *argv[]) { const char *me; char *outS, *errS; Nrrd *nin, *npad, *nrsmp, *nvghF, *nvhist, *nghist, *nhhist, *nvgh; int E, i, ups, notdoH, useFloat, scat; unsigned int sz[3]; NrrdKernelSpec *k00, *k11, *k22; double amin[4], amax[4], spacing[4]; float vperc, gperc, hperc, perc[3]; NrrdKernelSpec *dk, *uk; hestParm *hparm; hestOpt *hopt = NULL; airArray *mop; mop = airMopNew(); me = argv[0]; hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hparm->elideSingleOtherType = AIR_TRUE; hparm->elideSingleNonExistFloatDefault = AIR_TRUE; hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume, in nrrd format", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "vg", NULL, airTypeInt, 0, 0, ¬doH, NULL, "Make a 2-channel VG volume, instead of the usual (default) " "3-channel VGH volume."); hestOptAdd(&hopt, "f", NULL, airTypeInt, 0, 0, &useFloat, NULL, "Keep the output volume in floating point, instead of " "(by default) quantizing down to 8-bits. The " "\"-vp\", \"-gp\", and \"-hp\" options become moot."); hestOptAdd(&hopt, "d", "dimX dimY dimZ", airTypeUInt, 3, 3, sz, NULL, "dimensions of output volume"); hestOptAdd(&hopt, "up", NULL, airTypeInt, 0, 0, &ups, NULL, "Instead of just padding axes up to dimensions given " "with \"-d\" when original dimensions are smaller, do filtered " "upsampling."); hestOptAdd(&hopt, "uk", "upsample k", airTypeOther, 1, 1, &uk, "cubic:0,0.5", "kernel to use when doing the upsampling enabled by \"-up\"", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "dk", "downsample k", airTypeOther, 1, 1, &dk, "tent", "kernel to use when DOWNsampling volume to fit with specified " "dimensions. NOTE: ringing can be problematic here.", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k00", "kern00", airTypeOther, 1, 1, &k00, "tent", "kernel for gageKernel00, used to probe values (\"V\") " "in the volume that has been padded/resampled to fit in the " "dimensions given by \"-d\"", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kern11", airTypeOther, 1, 1, &k11, "cubicd:1,0", "kernel for gageKernel11, used with k00 to probe " "gradient magnitudes (\"G\")", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kern22", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "kernel for gageKernel22, used with k00,k11 to " "probe Hessian-based 2nd derivatives (\"H\")", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "vp", "V excl perc", airTypeFloat, 1, 1, &vperc, "0.000", "Percent of voxels to through away in quantization (if doing " "quantization) based their data value being too high or " "too low. "); hestOptAdd(&hopt, "gp", "G perc", airTypeFloat, 1, 1, &gperc, "0.002", "Like \"-vp\", but for gradient magnitudes. "); hestOptAdd(&hopt, "hp", "H perc", airTypeFloat, 1, 1, &hperc, "0.004", "Like \"-vp\", but for Hessian-based 2nd derivatives. "); hestOptAdd(&hopt, "scat", "scat size", airTypeInt, 1, 1, &scat, "0", "generate VG (and VH) scatterplots with this resolution. " "Size 0 means \"no scatterplots\". The scatterplots are " "histogram equalized, quantized, and saved out as PGM images " "named \"vg.pgm\" (and \"vh.pgm\")."); hestOptAdd(&hopt, "o", "output", airTypeString, 1, 1, &outS, NULL, "output volume in nrrd format"); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, qbertInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (3 != nin->dim) { fprintf(stderr, "%s: input nrrd is %-dimensional, not 3\n", me, nin->dim); airMopError(mop); exit(1); } if (!AIR_EXISTS(nin->axis[0].spacing)) { nrrdAxisInfoSpacingSet(nin, 0); } if (!AIR_EXISTS(nin->axis[1].spacing)) { nrrdAxisInfoSpacingSet(nin, 1); } if (!AIR_EXISTS(nin->axis[2].spacing)) { nrrdAxisInfoSpacingSet(nin, 2); } npad = nrrdNew(); airMopAdd(mop, npad, (airMopper)nrrdNuke, airMopAlways); if (qbertSizeUp(npad, nin, sz, ups ? uk : NULL)) { airMopAdd(mop, errS=biffGetDone(QBERT), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, errS); airMopError(mop); exit(1); } nrsmp = nrrdNew(); airMopAdd(mop, nrsmp, (airMopper)nrrdNuke, airMopAlways); if (qbertSizeDown(nrsmp, npad, sz, dk)) { airMopAdd(mop, errS=biffGetDone(QBERT), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, errS); airMopError(mop); exit(1); } airMopSub(mop, npad, (airMopper)nrrdNuke); npad = nrrdNuke(npad); /* this axis info is being saved so that it can be re-enstated at the end */ spacing[0] = amin[0] = amax[0] = AIR_NAN; nrrdAxisInfoGet_nva(nrsmp, nrrdAxisInfoSpacing, spacing+1); nrrdAxisInfoGet_nva(nrsmp, nrrdAxisInfoMin, amin+1); nrrdAxisInfoGet_nva(nrsmp, nrrdAxisInfoMax, amax+1); /* if we had to downsample, we may have enstated axis mins and maxs where they didn't exist before, and those shouldn't be saved in output. But we can't just copy axis mins and maxs from the original input because padding could have changed them. If no axis mins and maxs existed on the input nrrd, these will all be nan, so they won't be saved out. NOTE: we're only nixing axis min/max information, not spacing. */ for (i=0; i<=2; i++) { if (!AIR_EXISTS(nin->axis[i].min)) amin[1+i] = AIR_NAN; if (!AIR_EXISTS(nin->axis[i].max)) amax[1+i] = AIR_NAN; } nvghF = nrrdNew(); airMopAdd(mop, nvghF, (airMopper)nrrdNuke, airMopAlways); if (qbertProbe(nvghF, nrsmp, k00, k11, k22, !notdoH, sz)) { airMopAdd(mop, errS=biffGetDone(QBERT), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, errS); airMopError(mop); exit(1); } airMopSub(mop, nrsmp, (airMopper)nrrdNuke); nrsmp = nrrdNuke(nrsmp); if (useFloat) { /* we're done! */ if (scat && (qbertScat(nvghF, 1, scat, "vg.pgm") || (!notdoH && qbertScat(nvghF, 2, scat, "vh.pgm")))) { airMopAdd(mop, errS=biffGetDone(QBERT), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, errS); airMopError(mop); exit(1); } E = nrrdSave(outS, nvghF, NULL); } else { nvhist = nrrdNew(); nghist = nrrdNew(); nhhist = nrrdNew(); airMopAdd(mop, nvhist, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nghist, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nhhist, (airMopper)nrrdNuke, airMopAlways); if (qbertMakeVghHists(nvhist, nghist, nhhist, sz, QBERT_HIST_BINS, nvghF, nin)) { fprintf(stderr, "%s: trouble:\n%s\n", me, errS = biffGetDone(QBERT)); free(errS); exit(1); } nvgh = nrrdNew(); airMopAdd(mop, nvgh, (airMopper)nrrdNuke, airMopAlways); ELL_3V_SET(perc, vperc, gperc, hperc); if (qbertMakeVgh(nvgh, nvhist, nghist, nhhist, sz, perc, nvghF)) { airMopAdd(mop, errS=biffGetDone(QBERT), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, errS); airMopError(mop); exit(1); } airMopSub(mop, nvghF, (airMopper)nrrdNuke); nvghF = nrrdNuke(nvghF); if (scat && (qbertScat(nvgh, 1, scat, "vg.pgm") || (!notdoH && qbertScat(nvgh, 2, scat, "vh.pgm")))) { airMopAdd(mop, errS=biffGetDone(QBERT), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, errS); airMopError(mop); exit(1); } /* do final decoration of axes */ nrrdAxisInfoSet_va(nvgh, nrrdAxisInfoLabel, !notdoH ? "vgh" : "vg", "x", "y", "z"); nrrdAxisInfoSet_nva(nvgh, nrrdAxisInfoMin, amin); nrrdAxisInfoSet_nva(nvgh, nrrdAxisInfoMax, amax); nrrdAxisInfoSet_nva(nvgh, nrrdAxisInfoSpacing, spacing); nrrdContentSet_va(nvgh, "qbert", nin, ""); E = nrrdSave(outS, nvgh, NULL); } if (E) { airMopAdd(mop, errS=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving output:\n%s\n", me, errS); airMopError(mop); exit(1); } /* HEY: why am I getting memory-in-use with purify? */ airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/bin/overrgb.c0000664000175000017500000002016212165631065016764 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include static const char *overInfo = ( "Composites an RGBA nrrd over " "a background color (or image), after doing gamma correction, " "then quantizes to an 8-bit image. Actually, the " "input nrrd can have more than 4 values per pixel, " "but only the first four are used. If the RGBA nrrd " "is floating point, the values are taken at face value; " "if it is fixed point, the values interpreted as having " "been quantized (so that 8-bit RGBA images will act as " "you expect). When compositing with a background image, the given " "background image does not have to be the same size as the input " "image; it will be resampled (with linear interpolation) to fit. "); double docontrast(double val, double cfp, double cpow) { double v; if (val < cfp) { v = AIR_AFFINE(0.0, val, cfp, 0.0, 1.0); v = pow(v, cpow); val = AIR_AFFINE(0.0, v, 1.0, 0.0, cfp); } else { v = AIR_AFFINE(cfp, val, 1.0, 1.0, 0.0); v = pow(v, cpow); val = AIR_AFFINE(1.0, v, 0.0, cfp, 1.0); } return val; } int main(int argc, const char *argv[]) { hestOpt *hopt=NULL; Nrrd *nin, *nout, /* initial input and final output */ *ninD, /* input converted to double */ *_nbg, /* given background image (optional) */ *nbg, /* resampled background image */ *nrgbaD; /* rgba input as double */ const char *me; char *outS, *errS; double gamma, contr, cfp, cpow, back[3], *rgbaD, r, g, b, a; airArray *mop; int E; size_t min[3], max[3], sx, sy, pi; unsigned char *outUC, *bgUC; NrrdResampleInfo *rinfo; me = argv[0]; mop = airMopNew(); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input nrrd to composite", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "c", "contrast", airTypeDouble, 1, 1, &contr, "0.0", "contrast to apply to RGB values, before gamma. \"0.0\" " "means no change, \"1.0\" means thresholding, \"-1.0\" " "means a complete washout."); hestOptAdd(&hopt, "cfp", "fixed point", airTypeDouble, 1, 1, &cfp, "0.5", "component level that doesn't change with contrast"); hestOptAdd(&hopt, "g", "gamma", airTypeDouble, 1, 1, &gamma, "1.0", "gamma to apply to image data, after contrast"); hestOptAdd(&hopt, "b", "background", airTypeDouble, 3, 3, back, "0 0 0", "background color to composite against; white is " "1 1 1, not 255 255 255."); hestOptAdd(&hopt, "bi", "nbg", airTypeOther, 1, 1, &_nbg, "", "8-bit RGB background image to composite against", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, NULL, "file to write output PPM image to"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, overInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!(3 == nin->dim && 4 <= nin->axis[0].size)) { fprintf(stderr, "%s: doesn't look like an RGBA nrrd\n", me); airMopError(mop); return 1; } if (nrrdTypeBlock == nin->type) { fprintf(stderr, "%s: can't use a %s nrrd\n", me, airEnumStr(nrrdType, nrrdTypeBlock)); airMopError(mop); return 1; } sx = nin->axis[1].size; sy = nin->axis[2].size; if (_nbg) { if (!(3 == _nbg->dim && 3 == _nbg->axis[0].size && 2 <= _nbg->axis[1].size && 2 <= _nbg->axis[2].size && nrrdTypeUChar == _nbg->type)) { fprintf(stderr, "%s: background not an 8-bit RGB image\n", me); airMopError(mop); return 1; } nbg = nrrdNew(); airMopAdd(mop, nbg, (airMopper)nrrdNuke, airMopAlways); if (sx == _nbg->axis[1].size && sy == _nbg->axis[2].size) { /* no resampling needed, just copy */ E = nrrdCopy(nbg, _nbg); } else { /* have to resample background image to fit. */ /* because we're using the old resampler, we have to kill off any space direction information, which is incompatible with setting per-axis min and max, as is required by the old resampler */ nrrdOrientationReduce(_nbg, _nbg, AIR_FALSE); rinfo = nrrdResampleInfoNew(); airMopAdd(mop, rinfo, (airMopper)nrrdResampleInfoNix, airMopAlways); rinfo->kernel[0] = NULL; nrrdKernelParse(&(rinfo->kernel[1]), rinfo->parm[1], "tent"); rinfo->min[1] = _nbg->axis[1].min = 0; rinfo->max[1] = _nbg->axis[1].max = _nbg->axis[1].size-1; rinfo->samples[1] = sx; nrrdKernelParse(&(rinfo->kernel[2]), rinfo->parm[2], "tent"); rinfo->min[2] = _nbg->axis[2].min = 0; rinfo->max[2] = _nbg->axis[2].max = _nbg->axis[2].size-1; rinfo->samples[2] = sy; rinfo->renormalize = AIR_TRUE; rinfo->round = AIR_TRUE; E = nrrdSpatialResample(nbg, _nbg, rinfo); } if (E) { fprintf(stderr, "%s: trouble:\n%s", me, errS = biffGetDone(NRRD)); free(errS); return 1; } } else { nbg = NULL; } ninD = nrrdNew(); airMopAdd(mop, ninD, (airMopper)nrrdNuke, airMopAlways); nrgbaD = nrrdNew(); airMopAdd(mop, nrgbaD, (airMopper)nrrdNuke, airMopAlways); nout=nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); E = 0; if (nrrdTypeIsIntegral[nin->type]) { if (!E) E |= nrrdUnquantize(ninD, nin, nrrdTypeDouble); } else if (nrrdTypeFloat == nin->type) { if (!E) E |= nrrdConvert(ninD, nin, nrrdTypeDouble); } else { if (!E) E |= nrrdCopy(ninD, nin); } min[0] = min[1] = min[2] = 0; max[0] = 3; max[1] = sx-1; max[2] = sy-1; if (!E) E |= nrrdCrop(nrgbaD, ninD, min, max); if (!E) E |= nrrdPPM(nout, sx, sy); if (E) { fprintf(stderr, "%s: trouble:\n%s", me, errS = biffGetDone(NRRD)); free(errS); return 1; } contr = AIR_CLAMP(-1, contr, 1); cpow = tan(AIR_AFFINE(-1.000001, contr, 1.000001, 0, AIR_PI/2)); outUC = (unsigned char*)nout->data; bgUC = nbg ? (unsigned char *)nbg->data : NULL; rgbaD = (double *)nrgbaD->data; for (pi=0; piidtagNext, ** even though it really shouldn't have to */ int pullCCFind(pullContext *pctx) { static const char me[]="pullCCFind"; airArray *mop, *eqvArr; unsigned int passIdx, binIdx, pointIdx, neighIdx, eqvNum, pointNum, *idmap; pullBin *bin; pullPoint *point, *her; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (_pullIterate(pctx, pullProcessModeNeighLearn)) { biffAddf(PULL, "%s: trouble with %s for CC", me, airEnumStr(pullProcessMode, pullProcessModeNeighLearn)); return 1; } mop = airMopNew(); pointNum = pullPointNumber(pctx); eqvArr = airArrayNew(NULL, NULL, 2*sizeof(unsigned int), pointNum); airMopAdd(mop, eqvArr, (airMopper)airArrayNuke, airMopAlways); idmap = AIR_CAST(unsigned int *, calloc(pointNum, sizeof(unsigned int))); airMopAdd(mop, idmap, airFree, airMopAlways); /* to be safe, renumber all points, so that we know that the idtags are contiguous, starting at 0. HEY: this should handled by having a map from real point->idtag to a point number assigned just for the sake of doing CCs */ pctx->idtagNext = 0; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; point->idtag = pctx->idtagNext++; } } /* same stupidity copied from limn/polymod.c:limnPolyDataCCFind */ eqvNum = 0; for (passIdx=0; passIdx<2; passIdx++) { if (passIdx) { airArrayLenPreSet(eqvArr, eqvNum); } for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; for (neighIdx=0; neighIdxneighPointNum; neighIdx++) { if (0 == passIdx) { ++eqvNum; } else { her = point->neighPoint[neighIdx]; airEqvAdd(eqvArr, point->idtag, her->idtag); } } } } } /* do the CC analysis */ pctx->CCNum = airEqvMap(eqvArr, idmap, pointNum); /* assign idcc's */ for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; point->idCC = idmap[point->idtag]; } } airMopOkay(mop); return 0; } /* ** measure something about connected componts already found ** ** measrInfo can be 0 to say "measure # particles in CC", or ** it can be a scalar pullInfo ** ** rho == 0: only size, rho == 1: only measrInfo */ int pullCCMeasure(pullContext *pctx, Nrrd *nmeasr, int measrInfo, double rho) { static const char me[]="pullCCMeasure"; airArray *mop; unsigned int binIdx, pointIdx, ii; double *meas, *size; pullBin *bin; pullPoint *point; if (!( pctx && nmeasr )) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!pctx->CCNum) { biffAddf(PULL, "%s: CCNum == 0: haven't yet learned CCs?", me); return 1; } if (measrInfo) { if (airEnumValCheck(pullInfo, measrInfo)) { biffAddf(PULL, "%s: measrInfo %d not a valid %s", me, measrInfo, pullInfo->name); return 1; } if (1 != pullInfoLen(measrInfo)) { biffAddf(PULL, "%s: measrInfo %s (%d) isn't a scalar (len %u)", me, airEnumStr(pullInfo, measrInfo), measrInfo, pullInfoLen(measrInfo)); return 1; } if (!pctx->ispec[measrInfo]) { biffAddf(PULL, "%s: no ispec set for measrInfo %s (%d)", me, airEnumStr(pullInfo, measrInfo), measrInfo); return 1; } } /* else measrInfo is zero, they want to know # points */ /* in any case nmeasr is allocated for doubles */ if (nrrdMaybeAlloc_va(nmeasr, nrrdTypeDouble, 1, AIR_CAST(size_t, pctx->CCNum))) { biffMovef(PULL, NRRD, "%s: couldn't alloc nmeasr", me); return 1; } meas = AIR_CAST(double *, nmeasr->data); mop = airMopNew(); /* HEY: don't actually need to allocate and set size[], if measrInfo == 0 */ if (!(size = AIR_CAST(double *, calloc(pctx->CCNum, sizeof(double))))) { biffAddf(PULL, "%s: couldn't alloc size", me); airMopError(mop); return 1; } airMopAdd(mop, size, airFree, airMopAlways); /* finally, do measurement */ for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; size[point->idCC]++; meas[point->idCC] += (measrInfo ? pullPointScalar(pctx, point, measrInfo, NULL, NULL) : 1); } } if (measrInfo) { for (ii=0; iiCCNum; ii++) { meas[ii] /= size[ii]; meas[ii] = AIR_LERP(rho, size[ii], meas[ii]); } } airMopOkay(mop); return 0; } typedef struct { unsigned int i; double d; } ccpair; /* we intend to sort in *descending* order */ static int ccpairCompare(const void *_a, const void *_b) { const ccpair *a, *b; a = AIR_CAST(const ccpair *, _a); b = AIR_CAST(const ccpair *, _b); return (a->d < b->d ? +1 : (a->d > b->d ? -1 : 0)); } /* ******** pullCCSort ** ** sort CCs in pull context ** ** measrInfo == 0: sort by size, else, ** sort by blend of size and measrInfo ** rho == 0: only size, rho == 1: only measrInfo */ int pullCCSort(pullContext *pctx, int measrInfo, double rho) { static const char me[]="pullCCSort"; ccpair *pair; Nrrd *nmeasr; airArray *mop; unsigned int ii, *revm, binIdx, pointIdx; double *measr; pullBin *bin; pullPoint *point; int E; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!pctx->CCNum) { biffAddf(PULL, "%s: haven't yet learned CCs?", me); return 1; } #define CALLOC(T) (AIR_CAST(T *, calloc(pctx->CCNum, sizeof(T)))) mop = airMopNew(); if (!(nmeasr = nrrdNew()) || airMopAdd(mop, nmeasr, (airMopper)nrrdNuke, airMopAlways) || !(pair = CALLOC(ccpair)) || airMopAdd(mop, pair, airFree, airMopAlways) || !(revm = CALLOC(unsigned int)) || airMopAdd(mop, revm, airFree, airMopAlways)) { biffAddf(PULL, "%s: couldn't allocate everything", me); airMopError(mop); return 1; } #undef CALLOC if (!measrInfo) { /* sorting by size */ E = pullCCMeasure(pctx, nmeasr, 0, 0.0); } else { /* sorting by some blend of size and pullInfo */ E = pullCCMeasure(pctx, nmeasr, measrInfo, rho); } if (E) { biffAddf(PULL, "%s: problem measuring CCs", me); airMopError(mop); return 1; } measr = AIR_CAST(double *, nmeasr->data); for (ii=0; iiCCNum; ii++) { pair[ii].i = ii; pair[ii].d = measr[ii]; } qsort(pair, pctx->CCNum, sizeof(ccpair), ccpairCompare); for (ii=0; iiCCNum; ii++) { revm[pair[ii].i] = ii; } for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; point->idCC = revm[point->idCC]; } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/pull/trace.c0000664000175000017500000006071612174665521016635 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" pullTrace * pullTraceNew(void) { pullTrace *ret; ret = AIR_CALLOC(1, pullTrace); if (ret) { ret->seedPos[0] = ret->seedPos[1] = AIR_NAN; ret->seedPos[2] = ret->seedPos[3] = AIR_NAN; ret->nvert = nrrdNew(); ret->nstrn = nrrdNew(); ret->nvelo = nrrdNew(); ret->seedIdx = 0; ret->whyStop[0] = ret->whyStop[1] = pullTraceStopUnknown; ret->whyNowhere = pullTraceStopUnknown; } return ret; } pullTrace * pullTraceNix(pullTrace *pts) { if (pts) { nrrdNuke(pts->nvert); nrrdNuke(pts->nstrn); nrrdNuke(pts->nvelo); free(pts); } return NULL; } int pullTraceSet(pullContext *pctx, pullTrace *pts, int recordStrength, double scaleDelta, double halfScaleWin, double velocityMax, unsigned int arrIncr, const double _seedPos[4]) { static const char me[]="pullTraceSet"; pullPoint *point; airArray *mop, *trceArr[2], *hstrnArr[2]; double *trce[2], ssrange[2], *vert, *hstrn[2], *strn, *velo, seedPos[4], travmax; int constrFail; unsigned int dirIdx, lentmp, tidx, oidx, vertNum; if (!( pctx && pts && _seedPos )) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!( AIR_EXISTS(scaleDelta) && scaleDelta > 0.0 )) { biffAddf(PULL, "%s: need existing scaleDelta > 0 (not %g)", me, scaleDelta); return 1; } if (!( halfScaleWin > 0 )) { biffAddf(PULL, "%s: need halfScaleWin > 0", me); return 1; } if (!(pctx->constraint)) { biffAddf(PULL, "%s: given context doesn't have constraint set", me); return 1; } if (recordStrength && !pctx->ispec[pullInfoStrength]) { biffAddf(PULL, "%s: want to record strength but %s not set in context", me, airEnumStr(pullInfo, pullInfoStrength)); return 1; } if (pullConstraintScaleRange(pctx, ssrange)) { biffAddf(PULL, "%s: trouble getting scale range", me); return 1; } /* re-initialize termination descriptions (in case of trace re-use) */ pts->whyStop[0] = pts->whyStop[1] = pullTraceStopUnknown; pts->whyNowhere = pullTraceStopUnknown; /* enforce zeroZ */ ELL_4V_COPY(seedPos, _seedPos); if (pctx->flag.zeroZ) { seedPos[2] = 0.0; } /* save seedPos in any case */ ELL_4V_COPY(pts->seedPos, seedPos); mop = airMopNew(); point = pullPointNew(pctx); /* we'll want to decrement idtagNext later */ airMopAdd(mop, point, (airMopper)pullPointNix, airMopAlways); /* travmax is passed to _pullConstraintSatisfy; the intention is that constraint satisfaction should not fail because the point traveled too far (so make travmax large); the termination of the trace based on velocity is handled here, not by _pullConstraintSatisfy */ travmax = 10.0*scaleDelta*velocityMax/pctx->voxelSizeSpace; ELL_4V_COPY(point->pos, seedPos); if (_pullConstraintSatisfy(pctx->task[0], point, travmax, &constrFail)) { biffAddf(PULL, "%s: constraint sat on seed point", me); airMopError(mop); return 1; } /* fprintf(stderr, "!%s: seed=(%g,%g,%g,%g) -> %s (%g,%g,%g,%g)\n", me, seedPos[0], seedPos[1], seedPos[2], seedPos[3], constrFail ? "!NO!" : "(yes)", point->pos[0] - seedPos[0], point->pos[1] - seedPos[1], point->pos[2] - seedPos[2], point->pos[3] - seedPos[3]); */ if (constrFail) { pts->whyNowhere = pullTraceStopConstrFail; airMopOkay(mop); pctx->idtagNext -= 1; /* HACK */ return 0; } if (pctx->flag.zeroZ && point->pos[2] != 0) { biffAddf(PULL, "%s: zeroZ violated (a)", me); airMopError(mop); return 1; } /* else constraint sat worked at seed point; we have work to do */ for (dirIdx=0; dirIdx<2; dirIdx++) { trceArr[dirIdx] = airArrayNew((void**)(trce + dirIdx), NULL, 4*sizeof(double), arrIncr); airMopAdd(mop, trceArr[dirIdx], (airMopper)airArrayNuke, airMopAlways); if (recordStrength) { hstrnArr[dirIdx] = airArrayNew((void**)(hstrn + dirIdx), NULL, sizeof(double), arrIncr); airMopAdd(mop, hstrnArr[dirIdx], (airMopper)airArrayNuke, airMopAlways); } else { hstrnArr[dirIdx] = NULL; hstrn[dirIdx] = NULL; } } for (dirIdx=0; dirIdx<2; dirIdx++) { unsigned int step; double dscl; dscl = (!dirIdx ? -1 : +1)*scaleDelta; step = 0; while (1) { if (!step) { /* first step in both directions requires special tricks */ if (0 == dirIdx) { /* save constraint sat of seed point */ tidx = airArrayLenIncr(trceArr[0], 1); ELL_4V_COPY(trce[0] + 4*tidx, point->pos); if (recordStrength) { tidx = airArrayLenIncr(hstrnArr[0], 1); hstrn[0][0] = pullPointScalar(pctx, point, pullInfoStrength, NULL, NULL); } } else { /* re-set position from constraint sat of seed pos */ ELL_4V_COPY(point->pos, trce[0] + 4*0); } } /* nudge position along scale */ point->pos[3] += dscl; if (!AIR_IN_OP(ssrange[0], point->pos[3], ssrange[1])) { /* if we've stepped outside the range of scale for the volume containing the constraint manifold, we're done */ pts->whyStop[dirIdx] = pullTraceStopBounds; break; } if (AIR_ABS(point->pos[3] - seedPos[3]) > halfScaleWin) { /* we've moved along scale as far as allowed */ pts->whyStop[dirIdx] = pullTraceStopLength; break; } /* re-assert constraint */ /* fprintf(stderr, "%s(%u): pos = %g %g %g %g.... \n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); */ if (_pullConstraintSatisfy(pctx->task[0], point, travmax, &constrFail)) { biffAddf(PULL, "%s: dir %u, step %u", me, dirIdx, step); airMopError(mop); return 1; } /* fprintf(stderr, "%s(%u): ... %s(%d); pos = %g %g %g %g\n", me, point->idtag, constrFail ? "FAIL" : "(ok)", constrFail, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); */ if (constrFail) { /* constraint sat failed; no error, we're just done with stepping for this direction */ pts->whyStop[dirIdx] = pullTraceStopConstrFail; break; } if (pctx->flag.zeroZ && point->pos[2] != 0) { biffAddf(PULL, "%s: zeroZ violated (b)", me); airMopError(mop); return 1; } if (trceArr[dirIdx]->len >= 2) { /* see if we're moving too fast, by comparing with previous point */ double pos0[3], pos1[3], diff[3], vv; unsigned int ii; ii = trceArr[dirIdx]->len-2; ELL_3V_COPY(pos0, trce[dirIdx] + 4*(ii+0)); ELL_3V_COPY(pos1, trce[dirIdx] + 4*(ii+1)); ELL_3V_SUB(diff, pos1, pos0); vv = ELL_3V_LEN(diff)/scaleDelta; /* fprintf(stderr, "%s(%u): velo %g %s velocityMax %g => %s\n", me, point->idtag, vv, vv > velocityMax ? ">" : "<=", velocityMax, vv > velocityMax ? "FAIL" : "(ok)"); */ if (vv > velocityMax) { pts->whyStop[dirIdx] = pullTraceStopSpeeding; break; } } /* else save new point on trace */ tidx = airArrayLenIncr(trceArr[dirIdx], 1); ELL_4V_COPY(trce[dirIdx] + 4*tidx, point->pos); if (recordStrength) { tidx = airArrayLenIncr(hstrnArr[dirIdx], 1); hstrn[dirIdx][tidx] = pullPointScalar(pctx, point, pullInfoStrength, NULL, NULL); } step++; } } /* transfer trace halves to pts->nvert */ vertNum = trceArr[0]->len + trceArr[1]->len; if (0 == vertNum || 1 == vertNum || 2 == vertNum) { pts->whyNowhere = pullTraceStopStub; airMopOkay(mop); pctx->idtagNext -= 1; /* HACK */ return 0; } if (nrrdMaybeAlloc_va(pts->nvert, nrrdTypeDouble, 2, AIR_CAST(size_t, 4), AIR_CAST(size_t, vertNum)) || nrrdMaybeAlloc_va(pts->nvelo, nrrdTypeDouble, 1, AIR_CAST(size_t, vertNum))) { biffMovef(PULL, NRRD, "%s: allocating output", me); airMopError(mop); return 1; } if (recordStrength) { if (nrrdSlice(pts->nstrn, pts->nvert, 0 /* axis */, 0 /* pos */)) { biffMovef(PULL, NRRD, "%s: allocating output", me); airMopError(mop); return 1; } } vert = AIR_CAST(double *, pts->nvert->data); if (recordStrength) { strn = AIR_CAST(double *, pts->nstrn->data); } else { strn = NULL; } velo = AIR_CAST(double *, pts->nvelo->data); lentmp = trceArr[0]->len; oidx = 0; for (tidx=0; tidxseedIdx = oidx-1; lentmp = trceArr[1]->len; for (tidx=0; tidxnvelo->axis[0].size; if (1 == lentmp) { velo[0] = 0.0; } else { for (tidx=0; tidxidtagNext -= 1; /* HACK */ return 0; } typedef union { pullTrace ***trace; void **v; } blahblahUnion; pullTraceMulti * pullTraceMultiNew(void) { /* static const char me[]="pullTraceMultiNew"; */ pullTraceMulti *ret; blahblahUnion bbu; ret = AIR_CALLOC(1, pullTraceMulti); if (ret) { ret->trace = NULL; ret->traceNum = 0; ret->traceArr = airArrayNew((bbu.trace = &(ret->trace), bbu.v), &(ret->traceNum), sizeof(pullTrace*), _PULL_TRACE_MULTI_INCR); airArrayPointerCB(ret->traceArr, NULL, /* because we get handed pullTrace structs that have already been allocated (and then we own them) */ (void *(*)(void *))pullTraceNix); } return ret; } int pullTraceMultiAdd(pullTraceMulti *mtrc, pullTrace *trc, int *addedP) { static const char me[]="pullTraceMultiAdd"; unsigned int indx; if (!(mtrc && trc && addedP)) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!(trc->nvert->data && trc->nvert->axis[1].size >= 3)) { /* for now getting a stub trace is not an error biffAddf(PULL, "%s: got stub trace", me); return 1; */ *addedP = AIR_FALSE; return 0; } if (!(trc->nvelo->data && trc->nvelo->axis[0].size == trc->nvert->axis[1].size)) { biffAddf(PULL, "%s: velo data inconsistent", me); return 1; } *addedP = AIR_TRUE; indx = airArrayLenIncr(mtrc->traceArr, 1); if (!mtrc->trace) { biffAddf(PULL, "%s: alloc error", me); return 1; } mtrc->trace[indx] = trc; return 0; } int pullTraceMultiFilterConcaveDown(Nrrd *nfilt, const pullTraceMulti *mtrc, double winLenFrac) { static const char me[]="pullTraceMultiFilterConcaveDown"; unsigned int ti; int *filt; if (!(nfilt && mtrc)) { biffAddf(PULL, "%s: got NULL pointer (%p %p)", me, AIR_VOIDP(nfilt), AIR_CVOIDP(mtrc)); return 1; } if (!(AIR_EXISTS(winLenFrac) && AIR_IN_OP(0.0, winLenFrac, 1.0))) { biffAddf(PULL, "%s: winLenFrac %g doesn't exist or not in [0,1]", me, winLenFrac); return 1; } if (nrrdMaybeAlloc_va(nfilt, nrrdTypeInt, 1, mtrc->traceNum)) { biffMovef(PULL, NRRD, "%s: trouble creating output", me); return 1; } filt = AIR_CAST(int *, nfilt->data); for (ti=0; titraceNum; ti++) { unsigned winLen; const pullTrace *trc; const double *velo; unsigned int schange, pidx, lentmp; double dv, dv0=0.0, rdv, dv1; trc = mtrc->trace[ti]; lentmp = trc->nvert->axis[1].size; velo = AIR_CAST(const double *, trc->nvelo->data); winLen = AIR_CAST(unsigned int, winLenFrac*lentmp); if (winLen < 3) { continue; } schange = 0; rdv = 0.0; for (pidx=0; pidx 0.0; } return 0; } int pullTraceMultiPlotAdd(Nrrd *nplot, const pullTraceMulti *mtrc, const Nrrd *nfilt, int strengthUse, unsigned int trcIdxMin, unsigned int trcNum) { static const char me[]="pullTraceMultiPlot"; double ssRange[2], vRange[2], velHalf, *plot; unsigned int sizeS, sizeV, trcIdx, trcIdxMax; int *filt; if (!(nplot && mtrc)) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (nrrdCheck(nplot)) { biffMovef(PULL, NRRD, "%s: trouble with nplot", me); return 1; } if (nfilt) { if (nrrdCheck(nfilt)) { biffMovef(PULL, NRRD, "%s: trouble with nfilt", me); return 1; } if (!(1 == nfilt->dim && nrrdTypeInt == nfilt->type)) { biffAddf(PULL, "%s: didn't get 1-D array of %s (got %u-D of %s)", me, airEnumStr(nrrdType, nrrdTypeInt), nfilt->dim, airEnumStr(nrrdType, nfilt->type)); return 1; } } if (!(2 == nplot->dim && nrrdTypeDouble == nplot->type)) { biffAddf(PULL, "%s: didn't get 2-D array of %s (got %u-D of %s)", me, airEnumStr(nrrdType, nrrdTypeDouble), nplot->dim, airEnumStr(nrrdType, nplot->type)); return 1; } if (!(trcIdxMin < mtrc->traceNum)) { biffAddf(PULL, "%s: trcIdxMin %u not < traceNum %u", me, trcIdxMin, mtrc->traceNum); return 1; } if (trcNum) { trcIdxMax = trcIdxMin + trcNum-1; if (!(trcIdxMax < mtrc->traceNum)) { biffAddf(PULL, "%s: trcIdxMax %u = %u+%u-1 not < traceNum %u", me, trcIdxMax, trcIdxMin, trcNum, mtrc->traceNum); return 1; } } else { trcIdxMax = mtrc->traceNum-1; } ssRange[0] = nplot->axis[0].min; ssRange[1] = nplot->axis[0].max; vRange[0] = nplot->axis[1].min; vRange[1] = nplot->axis[1].max; if (!( AIR_EXISTS(ssRange[0]) && AIR_EXISTS(ssRange[1]) && AIR_EXISTS(vRange[0]) && AIR_EXISTS(vRange[1]) )) { biffAddf(PULL, "%s: need both axis 0 (%g,%g) and 1 (%g,%g) min,max", me, ssRange[0], ssRange[1], vRange[0], vRange[1]); return 1; } if (0 != vRange[0]) { biffAddf(PULL, "%s: expected vRange[0] == 0 not %g", me, vRange[0]); return 1; } /* HEY: this is a sneaky hack; the non-linear encoding of velocity along this axis means that the max velocity is actually infinite, but this seems like the least wrong way of storing this information in the place where it belongs (in the output plot nrrd) instead of assuming it will always be passed the same in successive calls */ velHalf = vRange[1]/2.0; plot = AIR_CAST(double *, nplot->data); filt = (nfilt ? AIR_CAST(int *, nfilt->data) : NULL); sizeS = AIR_CAST(unsigned int, nplot->axis[0].size); sizeV = AIR_CAST(unsigned int, nplot->axis[1].size); for (trcIdx=trcIdxMin; trcIdx<=trcIdxMax; trcIdx++) { unsigned int pntIdx, pntNum; const pullTrace *trc; const double *vert, *velo, *strn; if (filt && !filt[trcIdx]) { continue; } trc = mtrc->trace[trcIdx]; if (pullTraceStopStub == trc->whyNowhere) { continue; } vert = AIR_CAST(double *, trc->nvert->data); velo = AIR_CAST(double *, trc->nvelo->data); strn = AIR_CAST(double *, (strengthUse && trc->nstrn ? trc->nstrn->data : NULL)); pntNum = trc->nvert->axis[1].size; for (pntIdx=0; pntIdxtraceNum; ti++) { ret += sizeof(pullTrace); ret += nsizeof(mtrc->trace[ti]->nvert); ret += nsizeof(mtrc->trace[ti]->nstrn); ret += nsizeof(mtrc->trace[ti]->nvelo); } ret += sizeof(pullTrace*)*(mtrc->traceArr->size); return ret; } pullTraceMulti * pullTraceMultiNix(pullTraceMulti *mtrc) { if (mtrc) { airArrayNuke(mtrc->traceArr); free(mtrc); } return NULL; } #define PULL_MTRC_MAGIC "PULLMTRC0001" #define DEMARK_STR "======" static int tracewrite(FILE *file, const pullTrace *trc, unsigned int ti) { static const char me[]="tracewrite"; fprintf(file, "%s %u\n", DEMARK_STR, ti); ell_4v_print_d(file, trc->seedPos); #define WRITE(FF) \ if (trc->FF && trc->FF->data) { \ if (nrrdWrite(file, trc->FF, NULL)) { \ biffMovef(PULL, NRRD, "%s: trouble with " #FF , me); \ return 1; \ } \ } else { \ fprintf(file, "NULL"); \ } \ fprintf(file, "\n") fprintf(file, "nrrds: vert strn velo = %d %d %d\n", trc->nvert && trc->nvert->data, trc->nstrn && trc->nstrn->data, trc->nvelo && trc->nvelo->data); WRITE(nvert); WRITE(nstrn); WRITE(nvelo); fprintf(file, "%u\n", trc->seedIdx); fprintf(file, "%s %s %s\n", airEnumStr(pullTraceStop, trc->whyStop[0]), airEnumStr(pullTraceStop, trc->whyStop[1]), airEnumStr(pullTraceStop, trc->whyNowhere)); #undef WRITE return 0; } int pullTraceMultiWrite(FILE *file, const pullTraceMulti *mtrc) { static const char me[]="pullTraceMultiWrite"; unsigned int ti; if (!(file && mtrc)) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } fprintf(file, "%s\n", PULL_MTRC_MAGIC); fprintf(file, "%u traces\n", mtrc->traceNum); for (ti=0; titraceNum; ti++) { if (tracewrite(file, mtrc->trace[ti], ti)) { biffAddf(PULL, "%s: trace %u/%u", me, ti, mtrc->traceNum); return 1; } } return 0; } static int traceread(pullTrace *trc, FILE *file, unsigned int _ti) { static const char me[]="traceread"; char line[AIR_STRLEN_MED], name[AIR_STRLEN_MED]; unsigned int ti, lineLen; int stops[3], hackhack, vertHN, strnHN, veloHN; /* HN == have nrrd */ sprintf(name, "separator"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(PULL, "%s: didn't get %s line", me, name); return 1; } if (1 != sscanf(line, DEMARK_STR " %u", &ti)) { biffAddf(PULL, "%s: \"%s\" doesn't look like %s line", me, line, name); return 1; } if (ti != _ti) { biffAddf(PULL, "%s: read trace index %u but wanted %u", me, ti, _ti); return 1; } sprintf(name, "seed pos"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(PULL, "%s: didn't get %s line", me, name); return 1; } if (4 != sscanf(line, "%lg %lg %lg %lg", trc->seedPos + 0, trc->seedPos + 1, trc->seedPos + 2, trc->seedPos + 3)) { biffAddf(PULL, "%s: couldn't parse %s line \"%s\" as 4 doubles", me, name, line); return 1; } sprintf(name, "have nrrds"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(PULL, "%s: didn't get %s line", me, name); return 1; } if (3 != sscanf(line, "nrrds: vert strn velo = %d %d %d", &vertHN, &strnHN, &veloHN)) { biffAddf(PULL, "%s: couldn't parse %s line", me, name); return 1; } #define READ(FF) \ if (FF##HN) { \ if (nrrdRead(trc->n##FF, file, NULL)) { \ biffMovef(PULL, NRRD, "%s: trouble with " #FF , me); \ return 1; \ } \ fgetc(file); \ } else { \ airOneLine(file, line, AIR_STRLEN_MED); \ } hackhack = nrrdStateVerboseIO; /* should be fixed in Teem v2 */ nrrdStateVerboseIO = 0; READ(vert); READ(strn); READ(velo); nrrdStateVerboseIO = hackhack; sprintf(name, "seed idx"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(PULL, "%s: didn't get %s line", me, name); return 1; } if (1 != sscanf(line, "%u", &(trc->seedIdx))) { biffAddf(PULL, "%s: didn't parse uint from %s line \"%s\"", me, name, line); return 1; } sprintf(name, "stops"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(PULL, "%s: didn't get %s line", me, name); return 1; } if (3 != airParseStrE(stops, line, " ", 3, pullTraceStop)) { biffAddf(PULL, "%s: didn't see 3 %s on %s line \"%s\"", me, pullTraceStop->name, name, line); return 1; } return 0; } int pullTraceMultiRead(pullTraceMulti *mtrc, FILE *file) { static const char me[]="pullTraceMultiRead"; char line[AIR_STRLEN_MED], name[AIR_STRLEN_MED]; unsigned int lineLen, ti, tnum; pullTrace *trc; if (!(mtrc && file)) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } airArrayLenSet(mtrc->traceArr, 0); sprintf(name, "magic"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(PULL, "%s: didn't get %s line", me, name); return 1; } if (strcmp(line, PULL_MTRC_MAGIC)) { biffAddf(PULL, "%s: %s line \"%s\" not expected \"%s\"", me, name, line, PULL_MTRC_MAGIC); return 1; } sprintf(name, "# of traces"); lineLen = airOneLine(file, line, AIR_STRLEN_MED); if (!lineLen) { biffAddf(PULL, "%s: didn't get %s line", me, name); return 1; } if (1 != sscanf(line, "%u traces", &tnum)) { biffAddf(PULL, "%s: \"%s\" doesn't look like %s line", me, line, name); return 1; } for (ti=0; tiverbose = 0; vol->name = NULL; vol->kind = NULL; vol->ninSingle = NULL; vol->ninScale = NULL; vol->scaleNum = 0; vol->scalePos = NULL; vol->scaleDerivNorm = AIR_FALSE; vol->scaleDerivNormBias = 0.0; vol->ksp00 = nrrdKernelSpecNew(); vol->ksp11 = nrrdKernelSpecNew(); vol->ksp22 = nrrdKernelSpecNew(); vol->kspSS = nrrdKernelSpecNew(); GAGE_QUERY_RESET(vol->pullValQuery); vol->gctx = NULL; vol->gpvl = NULL; vol->gpvlSS = NULL; /* this is turned OFF in volumes that have infos that aren't seedthresh, see pullInfoSpecAdd() */ vol->seedOnly = AIR_TRUE; vol->forSeedPreThresh = AIR_FALSE; } return vol; } pullVolume * pullVolumeNix(pullVolume *vol) { if (vol) { airFree(vol->name); airFree(vol->scalePos); vol->ksp00 = nrrdKernelSpecNix(vol->ksp00); vol->ksp11 = nrrdKernelSpecNix(vol->ksp11); vol->ksp22 = nrrdKernelSpecNix(vol->ksp22); vol->kspSS = nrrdKernelSpecNix(vol->kspSS); if (vol->gctx) { vol->gctx = gageContextNix(vol->gctx); } airFree(vol->gpvlSS); airFree(vol); } return NULL; } /* ** used to set all the fields of pullVolume at once, including the ** gageContext inside the pullVolume ** ** OLD description ... ** used both for top-level volumes in the pullContext (pctx->vol[i]) ** in which case pctx is non-NULL, ** and for the per-task volumes (task->vol[i]), ** in which case pctx is NULL ** ................... */ int _pullVolumeSet(const pullContext *pctx, int taskCopy, pullVolume *vol, const gageKind *kind, int verbose, const char *name, const Nrrd *ninSingle, const Nrrd *const *ninScale, double *scalePos, unsigned int ninNum, int scaleDerivNorm, double scaleDerivNormBias, const NrrdKernelSpec *ksp00, const NrrdKernelSpec *ksp11, const NrrdKernelSpec *ksp22, const NrrdKernelSpec *kspSS) { static const char me[]="_pullVolumeSet"; int E; unsigned int vi; if (!( vol && kind && airStrlen(name) && ksp00 && ksp11 && ksp22 )) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!ninSingle) { biffAddf(PULL, "%s: needed non-NULL ninSingle", me); return 1; } if (!taskCopy) { for (vi=0; vivolNum; vi++) { if (pctx->vol[vi] == vol) { biffAddf(PULL, "%s: already got vol %p as vol[%u]", me, AIR_VOIDP(vol), vi); return 1; } } } if (ninNum) { if (!( ninNum >= 2 )) { biffAddf(PULL, "%s: need at least 2 volumes (not %u)", me, ninNum); return 1; } if (!scalePos) { biffAddf(PULL, "%s: need non-NULL scalePos with ninNum %u", me, ninNum); return 1; } if (!ninScale) { biffAddf(PULL, "%s: need non-NULL ninScale with ninNum %u", me, ninNum); return 1; } } vol->verbose = verbose; vol->kind = kind; vol->gctx = gageContextNew(); gageParmSet(vol->gctx, gageParmVerbose, vol->verbose > 0 ? vol->verbose - 1 : 0); gageParmSet(vol->gctx, gageParmRenormalize, AIR_FALSE); /* because we're likely only using accurate kernels */ gageParmSet(vol->gctx, gageParmStackNormalizeRecon, AIR_FALSE); vol->scaleDerivNorm = scaleDerivNorm; gageParmSet(vol->gctx, gageParmStackNormalizeDeriv, scaleDerivNorm); vol->scaleDerivNormBias = scaleDerivNormBias; gageParmSet(vol->gctx, gageParmStackNormalizeDerivBias, scaleDerivNormBias); gageParmSet(vol->gctx, gageParmTwoDimZeroZ, pctx->flag.zeroZ); gageParmSet(vol->gctx, gageParmCheckIntegrals, AIR_TRUE); E = 0; if (!E) E |= gageKernelSet(vol->gctx, gageKernel00, ksp00->kernel, ksp00->parm); if (!E) E |= gageKernelSet(vol->gctx, gageKernel11, ksp11->kernel, ksp11->parm); if (!E) E |= gageKernelSet(vol->gctx, gageKernel22, ksp22->kernel, ksp22->parm); if (ninScale) { if (!kspSS) { biffAddf(PULL, "%s: got NULL kspSS", me); return 1; } gageParmSet(vol->gctx, gageParmStackUse, AIR_TRUE); if (!E) E |= !(vol->gpvl = gagePerVolumeNew(vol->gctx, ninSingle, kind)); vol->gpvlSS = AIR_CAST(gagePerVolume **, calloc(ninNum, sizeof(gagePerVolume *))); if (!E) E |= gageStackPerVolumeNew(vol->gctx, vol->gpvlSS, ninScale, ninNum, kind); if (!E) E |= gageStackPerVolumeAttach(vol->gctx, vol->gpvl, vol->gpvlSS, scalePos, ninNum); if (!E) E |= gageKernelSet(vol->gctx, gageKernelStack, kspSS->kernel, kspSS->parm); } else { vol->gpvlSS = NULL; if (!E) E |= !(vol->gpvl = gagePerVolumeNew(vol->gctx, ninSingle, kind)); if (!E) E |= gagePerVolumeAttach(vol->gctx, vol->gpvl); } if (E) { biffMovef(PULL, GAGE, "%s: trouble (%s %s)", me, ninSingle ? "ninSingle" : "", ninScale ? "ninScale" : ""); return 1; } gageQueryReset(vol->gctx, vol->gpvl); /* the query is the single thing remaining unset in the gageContext */ vol->name = airStrdup(name); if (!vol->name) { biffAddf(PULL, "%s: couldn't strdup name (len %u)", me, AIR_CAST(unsigned int, airStrlen(name))); return 1; } if (vol->verbose) { printf("%s: ---- vol=%p, name = %p = |%s|\n", me, AIR_VOIDP(vol), AIR_VOIDP(vol->name), vol->name); if (0 != vol->scaleDerivNormBias) { printf("%s: ---- scale deriv norm bias = %g\n", me, vol->scaleDerivNormBias); } } nrrdKernelSpecSet(vol->ksp00, ksp00->kernel, ksp00->parm); nrrdKernelSpecSet(vol->ksp11, ksp11->kernel, ksp11->parm); nrrdKernelSpecSet(vol->ksp22, ksp22->kernel, ksp22->parm); if (ninScale) { vol->ninSingle = ninSingle; vol->ninScale = ninScale; vol->scaleNum = ninNum; vol->scalePos = AIR_CAST(double *, calloc(ninNum, sizeof(double))); if (!vol->scalePos) { biffAddf(PULL, "%s: couldn't calloc scalePos", me); return 1; } for (vi=0; viscalePos[vi] = scalePos[vi]; } nrrdKernelSpecSet(vol->kspSS, kspSS->kernel, kspSS->parm); } else { vol->ninSingle = ninSingle; vol->ninScale = NULL; vol->scaleNum = 0; /* leave kspSS as is (unset) */ } return 0; } /* ** the effect is to give pctx ownership of the vol */ int pullVolumeSingleAdd(pullContext *pctx, const gageKind *kind, char *name, const Nrrd *nin, const NrrdKernelSpec *ksp00, const NrrdKernelSpec *ksp11, const NrrdKernelSpec *ksp22) { static const char me[]="pullVolumeSingleSet"; pullVolume *vol; vol = pullVolumeNew(); if (_pullVolumeSet(pctx, AIR_FALSE /* taskCopy */, vol, kind, pctx->verbose, name, nin, NULL, NULL, 0, AIR_FALSE, 0.0, ksp00, ksp11, ksp22, NULL)) { biffAddf(PULL, "%s: trouble", me); return 1; } /* add this volume to context */ if (pctx->verbose) { printf("%s: adding pctx->vol[%u] = %p\n", me, pctx->volNum, AIR_VOIDP(vol)); } pctx->vol[pctx->volNum] = vol; pctx->volNum++; return 0; } /* ** the effect is to give pctx ownership of the vol */ int pullVolumeStackAdd(pullContext *pctx, const gageKind *kind, char *name, const Nrrd *nin, const Nrrd *const *ninSS, double *scalePos, unsigned int ninNum, int scaleDerivNorm, double scaleDerivNormBias, const NrrdKernelSpec *ksp00, const NrrdKernelSpec *ksp11, const NrrdKernelSpec *ksp22, const NrrdKernelSpec *kspSS) { static const char me[]="pullVolumeStackAdd"; pullVolume *vol; vol = pullVolumeNew(); if (_pullVolumeSet(pctx, AIR_FALSE /* taskCopy */, vol, kind, pctx->verbose, name, nin, ninSS, scalePos, ninNum, scaleDerivNorm, scaleDerivNormBias, ksp00, ksp11, ksp22, kspSS)) { biffAddf(PULL, "%s: trouble", me); return 1; } /* add this volume to context */ pctx->vol[pctx->volNum++] = vol; return 0; } /* ** this is only used to create pullVolumes for the pullTasks ** ** DOES use biff */ pullVolume * _pullVolumeCopy(const pullContext *pctx, const pullVolume *volOrig) { static const char me[]="pullVolumeCopy"; pullVolume *volNew; volNew = pullVolumeNew(); if (_pullVolumeSet(pctx, AIR_TRUE /* taskCopy */, volNew, volOrig->kind, volOrig->verbose, volOrig->name, volOrig->ninSingle, volOrig->ninScale, volOrig->scalePos, volOrig->scaleNum, volOrig->scaleDerivNorm, volOrig->scaleDerivNormBias, volOrig->ksp00, volOrig->ksp11, volOrig->ksp22, volOrig->kspSS)) { biffAddf(PULL, "%s: trouble creating new volume", me); return NULL; } volNew->seedOnly = volOrig->seedOnly; volNew->forSeedPreThresh = volOrig->forSeedPreThresh; /* _pullVolumeSet just created a new (per-task) gageContext, and it will not learn the items from the info specs, so we have to add query here */ if (gageQuerySet(volNew->gctx, volNew->gpvl, volOrig->gpvl->query) || gageUpdate(volNew->gctx)) { biffMovef(PULL, GAGE, "%s: trouble with new volume gctx", me); return NULL; } return volNew; } int _pullInsideBBox(pullContext *pctx, double pos[4]) { return (AIR_IN_CL(pctx->bboxMin[0], pos[0], pctx->bboxMax[0]) && AIR_IN_CL(pctx->bboxMin[1], pos[1], pctx->bboxMax[1]) && AIR_IN_CL(pctx->bboxMin[2], pos[2], pctx->bboxMax[2]) && AIR_IN_CL(pctx->bboxMin[3], pos[3], pctx->bboxMax[3])); } /* ** sets: ** pctx->haveScale ** pctx->voxelSizeSpace, voxelSizeScale ** pctx->bboxMin ([0] through [3], always) ** pctx->bboxMax (same) */ int _pullVolumeSetup(pullContext *pctx) { static const char me[]="_pullVolumeSetup"; unsigned int ii, numScale; /* first see if there are any gage problems */ for (ii=0; iivolNum; ii++) { if (pctx->verbose) { printf("%s: gageUpdate(vol[%u])\n", me, ii); } if (pctx->vol[ii]->gctx) { if (gageUpdate(pctx->vol[ii]->gctx)) { biffMovef(PULL, GAGE, "%s: trouble setting up gage on vol " "%u/%u (\"%s\")", me, ii, pctx->volNum, pctx->vol[ii]->name); return 1; } } else { biffAddf(PULL, "%s: vol[%u] has NULL gctx", me, ii); } } pctx->voxelSizeSpace = 0.0; for (ii=0; iivolNum; ii++) { double min[3], max[3]; gageContext *gctx; gctx = pctx->vol[ii]->gctx; gageShapeBoundingBox(min, max, gctx->shape); if (!ii) { ELL_3V_COPY(pctx->bboxMin, min); ELL_3V_COPY(pctx->bboxMax, max); } else { ELL_3V_MIN(pctx->bboxMin, pctx->bboxMin, min); ELL_3V_MIN(pctx->bboxMax, pctx->bboxMax, max); } pctx->voxelSizeSpace += ELL_3V_LEN(gctx->shape->spacing)/sqrt(3.0); if (ii && !pctx->initParm.unequalShapesAllow) { if (!gageShapeEqual(pctx->vol[0]->gctx->shape, pctx->vol[0]->name, pctx->vol[ii]->gctx->shape, pctx->vol[ii]->name)) { biffMovef(PULL, GAGE, "%s: need equal shapes, but vol 0 and %u different", me, ii); return 1; } } } pctx->voxelSizeSpace /= pctx->volNum; /* have now computed bbox{Min,Max}[0,1,2]; now do bbox{Min,Max}[3] */ pctx->bboxMin[3] = pctx->bboxMax[3] = 0.0; pctx->haveScale = AIR_FALSE; pctx->voxelSizeScale = 0.0; numScale = 0; for (ii=0; iivolNum; ii++) { if (pctx->vol[ii]->ninScale) { double sclMin, sclMax, sclStep; unsigned int si; numScale ++; sclMin = pctx->vol[ii]->scalePos[0]; if (pctx->flag.scaleIsTau) { sclMin = gageTauOfSig(sclMin); } sclMax = pctx->vol[ii]->scalePos[pctx->vol[ii]->scaleNum-1]; if (pctx->flag.scaleIsTau) { sclMax = gageTauOfSig(sclMax); } sclStep = 0; for (si=0; sivol[ii]->scaleNum-1; si++) { double scl0, scl1; scl1 = pctx->vol[ii]->scalePos[si+1]; scl0 = pctx->vol[ii]->scalePos[si]; if (pctx->flag.scaleIsTau) { scl1 = gageTauOfSig(scl1); scl0 = gageTauOfSig(scl0); } sclStep += (scl1 - scl0); } sclStep /= pctx->vol[ii]->scaleNum-1; pctx->voxelSizeScale += sclStep; if (!pctx->haveScale) { pctx->bboxMin[3] = sclMin; pctx->bboxMax[3] = sclMax; pctx->haveScale = AIR_TRUE; } else { /* we already know haveScale; expand existing range */ pctx->bboxMin[3] = AIR_MIN(sclMin, pctx->bboxMin[3]); pctx->bboxMax[3] = AIR_MAX(sclMax, pctx->bboxMax[3]); } } } if (numScale) { pctx->voxelSizeScale /= numScale; } if (pctx->verbose) { printf("%s: bboxMin (%g,%g,%g,%g) max (%g,%g,%g,%g)\n", me, pctx->bboxMin[0], pctx->bboxMin[1], pctx->bboxMin[2], pctx->bboxMin[3], pctx->bboxMax[0], pctx->bboxMax[1], pctx->bboxMax[2], pctx->bboxMax[3]); printf("%s: voxelSizeSpace %g Scale %g\n", me, pctx->voxelSizeSpace, pctx->voxelSizeScale); } /* _energyInterParticle() depends on this error checking */ if (pctx->haveScale) { if (pullInterTypeJustR == pctx->interType) { biffAddf(PULL, "%s: need scale-aware intertype (not %s) with " "a scale-space volume", me, airEnumStr(pullInterType, pullInterTypeJustR)); return 1; } } else { /* don't have scale */ if (pullInterTypeJustR != pctx->interType) { biffAddf(PULL, "%s: can't use scale-aware intertype (%s) without " "a scale-space volume", me, airEnumStr(pullInterType, pctx->interType)); return 1; } } if (pctx->flag.energyFromStrength && !(pctx->ispec[pullInfoStrength] && pctx->haveScale)) { biffAddf(PULL, "%s: sorry, can use energyFromStrength only with both " "a scale-space volume, and a strength info", me); return 1; } return 0; } /* ** basis of pullVolumeLookup ** ** uses biff, returns UINT_MAX in case of error */ unsigned int _pullVolumeIndex(const pullContext *pctx, const char *volName) { static const char me[]="_pullVolumeIndex"; unsigned int vi; if (!( pctx && volName )) { biffAddf(PULL, "%s: got NULL pointer", me); return UINT_MAX; } if (0 == pctx->volNum) { biffAddf(PULL, "%s: given context has no volumes", me); return UINT_MAX; } for (vi=0; vivolNum; vi++) { if (!strcmp(pctx->vol[vi]->name, volName)) { break; } } if (vi == pctx->volNum) { biffAddf(PULL, "%s: no volume has name \"%s\"", me, volName); return UINT_MAX; } return vi; } const pullVolume * pullVolumeLookup(const pullContext *pctx, const char *volName) { static const char me[]="pullVolumeLookup"; unsigned int vi; vi = _pullVolumeIndex(pctx, volName); if (UINT_MAX == vi) { biffAddf(PULL, "%s: trouble looking up \"%s\"", me, volName); return NULL; } return pctx->vol[vi]; } int pullConstraintScaleRange(pullContext *pctx, double ssrange[2]) { static const char me[]="pullConstraintScaleRange"; pullVolume *cvol; if (!(pctx && ssrange)) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!(pctx->constraint)) { biffAddf(PULL, "%s: given context doesn't have constraint set", me); return 1; } if (!(pctx->ispec[pctx->constraint])) { biffAddf(PULL, "%s: info %s not set for constriant", me, airEnumStr(pullInfo, pctx->constraint)); return 1; } cvol = pctx->vol[pctx->ispec[pctx->constraint]->volIdx]; if (!cvol->ninScale) { biffAddf(PULL, "%s: volume \"%s\" has constraint but no scale-space", me, cvol->name); return 1; } ssrange[0] = cvol->scalePos[0]; ssrange[1] = cvol->scalePos[cvol->scaleNum-1]; if (pctx->flag.scaleIsTau) { ssrange[0] = gageTauOfSig(ssrange[0]); ssrange[1] = gageTauOfSig(ssrange[1]); } return 0; } teem-1.11.0~svn6057/src/pull/corePull.c0000664000175000017500000004270512174666321017321 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" int _pullVerbose = 0; #define _DECREASE(ell, enn) ( \ 2*((ell) - (enn)) \ / ( (AIR_ABS(ell) + AIR_ABS(enn)) \ ? (AIR_ABS(ell) + AIR_ABS(enn)) \ : 1 ) \ ) /* ** this is the core of the worker threads: as long as there are bins ** left to process, get the next one, and process it */ int _pullProcess(pullTask *task) { static const char me[]="_pullProcess"; unsigned int binIdx; while (task->pctx->binNextIdx < task->pctx->binNum) { /* get the index of the next bin to process */ if (task->pctx->threadNum > 1) { airThreadMutexLock(task->pctx->binMutex); } /* note that we entirely skip bins with no points */ do { binIdx = task->pctx->binNextIdx; if (task->pctx->binNextIdx < task->pctx->binNum) { task->pctx->binNextIdx++; } } while (binIdx < task->pctx->binNum && 0 == task->pctx->bin[binIdx].pointNum); if (task->pctx->threadNum > 1) { airThreadMutexUnlock(task->pctx->binMutex); } if (binIdx == task->pctx->binNum) { /* no more bins to process! */ break; } if (task->pctx->verbose > 1) { fprintf(stderr, "%s(%u): calling pullBinProcess(%u)\n", me, task->threadIdx, binIdx); } if (pullBinProcess(task, binIdx)) { biffAddf(PULL, "%s(%u): had trouble on bin %u", me, task->threadIdx, binIdx); return 1; } } return 0; } /* the main loop for each worker thread */ void * _pullWorker(void *_task) { static const char me[]="_pushWorker"; pullTask *task; task = (pullTask *)_task; while (1) { if (task->pctx->verbose > 1) { fprintf(stderr, "%s(%u): waiting on barrier A\n", me, task->threadIdx); } /* pushFinish sets finished prior to the barriers */ airThreadBarrierWait(task->pctx->iterBarrierA); if (task->pctx->finished) { if (task->pctx->verbose > 1) { fprintf(stderr, "%s(%u): done!\n", me, task->threadIdx); } break; } /* else there's work to do . . . */ if (task->pctx->verbose > 1) { fprintf(stderr, "%s(%u): starting to process\n", me, task->threadIdx); } if (_pullProcess(task)) { /* HEY clearly not threadsafe to have errors . . . */ biffAddf(PULL, "%s: thread %u trouble", me, task->threadIdx); task->pctx->finished = AIR_TRUE; } if (task->pctx->verbose > 1) { fprintf(stderr, "%s(%u): waiting on barrier B\n", me, task->threadIdx); } airThreadBarrierWait(task->pctx->iterBarrierB); } return _task; } int pullStart(pullContext *pctx) { static const char me[]="pullStart"; unsigned int tidx; if (pctx->verbose) { fprintf(stderr, "%s: hello %p\n", me, AIR_VOIDP(pctx)); } pctx->iter = 0; /* have to initialize this here because of seedOnly hack */ /* the ordering of steps below is important! e.g. gage context has to be set up (_pullVolumeSetup) by before its copied (_pullTaskSetup) */ if (_pullContextCheck(pctx) || _pullVolumeSetup(pctx) || _pullInfoSetup(pctx) || _pullTaskSetup(pctx) || _pullBinSetup(pctx)) { biffAddf(PULL, "%s: trouble starting to set up context", me); return 1; } if (!(pctx->flag.startSkipsPoints)) { if (_pullPointSetup(pctx)) { biffAddf(PULL, "%s: trouble setting up points", me); return 1; } } if (pctx->threadNum > 1) { pctx->binMutex = airThreadMutexNew(); pctx->iterBarrierA = airThreadBarrierNew(pctx->threadNum); pctx->iterBarrierB = airThreadBarrierNew(pctx->threadNum); /* start threads 1 and up running; they'll all hit iterBarrierA */ for (tidx=1; tidxthreadNum; tidx++) { if (pctx->verbose > 1) { fprintf(stderr, "%s: spawning thread %d\n", me, tidx); } airThreadStart(pctx->task[tidx]->thread, _pullWorker, (void *)(pctx->task[tidx])); } } else { pctx->binMutex = NULL; pctx->iterBarrierA = NULL; pctx->iterBarrierB = NULL; } if (pctx->verbose) { fprintf(stderr, "%s: setup for %u threads done\n", me, pctx->threadNum); } pctx->timeIteration = 0; pctx->timeRun = 0; return 0; } /* ** this is called *after* pullOutputGet ** ** should nix everything created by the many _pull*Setup() functions */ int pullFinish(pullContext *pctx) { static const char me[]="pullFinish"; unsigned int tidx; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } pctx->finished = AIR_TRUE; if (pctx->threadNum > 1) { if (pctx->verbose > 1) { fprintf(stderr, "%s: finishing workers\n", me); } airThreadBarrierWait(pctx->iterBarrierA); /* worker threads now pass barrierA and see that finished is AIR_TRUE, and then bail, so now we collect them */ for (tidx=pctx->threadNum; tidx>0; tidx--) { if (tidx-1) { airThreadJoin(pctx->task[tidx-1]->thread, &(pctx->task[tidx-1]->returnPtr)); } } pctx->binMutex = airThreadMutexNix(pctx->binMutex); pctx->iterBarrierA = airThreadBarrierNix(pctx->iterBarrierA); pctx->iterBarrierB = airThreadBarrierNix(pctx->iterBarrierB); } /* no need for _pullVolumeFinish(pctx), at least not now */ /* no need for _pullInfoFinish(pctx), at least not now */ _pullTaskFinish(pctx); _pullBinFinish(pctx); _pullPointFinish(pctx); /* yes, nixed bins deleted pnts inside, but other buffers still have to be freed */ return 0; } /* ** _pullIterate ** ** (documentation) ** ** NB: this implements the body of thread 0, the master thread */ int _pullIterate(pullContext *pctx, int mode) { static const char me[]="_pullIterate"; double time0; int myError, E; unsigned int ti; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(pullProcessMode, mode)) { biffAddf(PULL, "%s: process mode %d unrecognized", me, mode); return 1; } if (!pctx->task) { biffAddf(PULL, "%s: NULL task array, didn't call pullStart()?", me); return 1; } /* if this is descent, pull down eip a bit */ if (pullProcessModeDescent == mode) { pctx->sysParm.energyIncreasePermit *= pctx->eipScale; } #if PULL_HINTER /* zero-out/alloc hinter if need be */ if (pullProcessModeDescent == mode && pctx->nhinter) { if (nrrdMaybeAlloc_va(pctx->nhinter, nrrdTypeFloat, 2, AIR_CAST(size_t, _PULL_HINTER_SIZE), AIR_CAST(size_t, _PULL_HINTER_SIZE))) { biffMovef(PULL, NRRD, "%s: setting up nhinter", me); return 1; } } #endif /* tell all tasks what mode they're in */ for (ti=0; tithreadNum; ti++) { pctx->task[ti]->processMode = mode; } if (pctx->verbose) { fprintf(stderr, "%s(%s): iter %d goes w/ eip %g, %u pnts, enr %g%s\n", me, airEnumStr(pullProcessMode, mode), pctx->iter, pctx->sysParm.energyIncreasePermit, pullPointNumber(pctx), _pullEnergyTotal(pctx), (pctx->flag.permuteOnRebin ? " (por)" : "")); } time0 = airTime(); pctx->pointNum = pullPointNumber(pctx); /* the _pullWorker checks finished after iterBarrierA */ pctx->finished = AIR_FALSE; /* initialize index of next bin to be doled out to threads */ pctx->binNextIdx=0; if (pctx->threadNum > 1) { airThreadBarrierWait(pctx->iterBarrierA); } myError = AIR_FALSE; if (_pullProcess(pctx->task[0])) { biffAddf(PULL, "%s: master thread trouble w/ iter %u", me, pctx->iter); pctx->finished = AIR_TRUE; myError = AIR_TRUE; } if (pctx->threadNum > 1) { airThreadBarrierWait(pctx->iterBarrierB); } if (pctx->finished) { if (!myError) { /* we didn't set finished- one of the workers must have */ biffAddf(PULL, "%s: worker error on iter %u", me, pctx->iter); } return 1; } if (pctx->verbose) { if (pctx->pointNum > _PULL_PROGRESS_POINT_NUM_MIN) { fprintf(stderr, ".\n"); /* finishing line of progress indicators */ } } /* depending on mode, run one of the iteration finishers */ E = 0; switch (mode) { case pullProcessModeDescent: E = _pullIterFinishDescent(pctx); /* includes rebinning */ break; case pullProcessModeNeighLearn: E = _pullIterFinishNeighLearn(pctx); break; case pullProcessModeAdding: E = _pullIterFinishAdding(pctx); break; case pullProcessModeNixing: E = _pullIterFinishNixing(pctx); break; default: biffAddf(PULL, "%s: process mode %d unrecognized", me, mode); return 1; break; } if (E) { pctx->finished = AIR_TRUE; biffAddf(PULL, "%s: trouble finishing iter %u", me, pctx->iter); return 1; } pctx->timeIteration = airTime() - time0; #if PULL_HINTER if (pullProcessModeDescent == mode && pctx->nhinter) { char fname[AIR_STRLEN_SMALL]; sprintf(fname, "hinter-%05u.nrrd", pctx->iter); if (nrrdSave(fname, pctx->nhinter, NULL)) { biffMovef(PULL, NRRD, "%s: saving hinter to %s", me, fname); return 1; } } #endif return 0; } int pullRun(pullContext *pctx) { static const char me[]="pullRun"; char poutS[AIR_STRLEN_MED]; Nrrd *npos; double time0, time1, enrLast, enrNew=AIR_NAN, enrDecrease=AIR_NAN, enrDecreaseAvg=AIR_NAN; int converged; unsigned firstIter; if (pctx->verbose) { fprintf(stderr, "%s: hello\n", me); } time0 = airTime(); firstIter = pctx->iter; if (pctx->verbose) { fprintf(stderr, "%s: doing priming iteration (iter now %u)\n", me, pctx->iter); } if (_pullIterate(pctx, pullProcessModeDescent)) { biffAddf(PULL, "%s: trouble on priming iter %u", me, pctx->iter); return 1; } pctx->iter += 1; enrLast = enrNew = _pullEnergyTotal(pctx); if (pctx->verbose) { fprintf(stderr, "%s: starting system energy = %g\n", me, enrLast); } enrDecrease = enrDecreaseAvg = 0; converged = AIR_FALSE; while ((pctx->iterParm.min && pctx->iter <= pctx->iterParm.min) || ((!pctx->iterParm.max || pctx->iter < pctx->iterParm.max) && !converged)) { /* this per iteration init had been missing for a very long time */ pctx->addNum = pctx->nixNum = 0; if (pctx->iterParm.snap && !(pctx->iter % pctx->iterParm.snap)) { npos = nrrdNew(); sprintf(poutS, "snap.%06d.pos.nrrd", pctx->iter); if (pullOutputGet(npos, NULL, NULL, NULL, 0.0, pctx)) { biffAddf(PULL, "%s: couldn't get snapshot for iter %d", me, pctx->iter); return 1; } if (nrrdSave(poutS, npos, NULL)) { biffMovef(PULL, NRRD, "%s: couldn't save snapshot for iter %d", me, pctx->iter); return 1; } npos = nrrdNuke(npos); } if (_pullIterate(pctx, pullProcessModeDescent)) { biffAddf(PULL, "%s: trouble on iter %d", me, pctx->iter); return 1; } enrNew = _pullEnergyTotal(pctx); enrDecrease = _DECREASE(enrLast, enrNew); if (firstIter + 1 == pctx->iter) { /* we need some way of artificially boosting enrDecreaseAvg when we're just starting, so that we thwart the convergence test, which we do because we don't have the history of iterations that enrDecreaseAvg is supposed to describe. Using some scaling of enrDecrease is one possible hack. */ enrDecreaseAvg = 3*enrDecrease; } else { enrDecreaseAvg = (2*enrDecreaseAvg + enrDecrease)/3; } if (pctx->verbose) { fprintf(stderr, "%s: ___ done iter %u: " "e=%g,%g, de=%g,%g (%g), s=%g,%g\n", me, pctx->iter, enrLast, enrNew, enrDecrease, enrDecreaseAvg, pctx->sysParm.energyDecreaseMin, _pullStepInterAverage(pctx), _pullStepConstrAverage(pctx)); } if (pctx->iterParm.popCntlPeriod) { if ((pctx->iterParm.popCntlPeriod - 1) == (pctx->iter % pctx->iterParm.popCntlPeriod) && enrDecreaseAvg < pctx->sysParm.energyDecreasePopCntlMin && (pctx->sysParm.alpha != 0 || !pctx->flag.noPopCntlWithZeroAlpha)) { if (pctx->verbose) { fprintf(stderr, "%s: ***** enr decrease %g < %g: " "trying pop cntl ***** \n", me, enrDecreaseAvg, pctx->sysParm.energyDecreasePopCntlMin); } if (_pullIterate(pctx, pullProcessModeNeighLearn) || _pullIterate(pctx, pullProcessModeAdding) || _pullIterate(pctx, pullProcessModeNixing)) { biffAddf(PULL, "%s: trouble with %s for pop cntl on iter %u", me, airEnumStr(pullProcessMode, pctx->task[0]->processMode), pctx->iter); return 1; } } else { if (pctx->verbose > 2) { fprintf(stderr, "%s: ***** no pop cntl:\n", me); fprintf(stderr, " iter=%u %% period=%u = %u != %u\n", pctx->iter, pctx->iterParm.popCntlPeriod, pctx->iter % pctx->iterParm.popCntlPeriod, pctx->iterParm.popCntlPeriod - 1); fprintf(stderr, " en dec avg = %g >= %g\n", enrDecreaseAvg, pctx->sysParm.energyDecreasePopCntlMin); fprintf(stderr, " npcwza %s && alpha = %g\n", pctx->flag.noPopCntlWithZeroAlpha ? "true" : "false", pctx->sysParm.alpha); } } } pctx->iter += 1; enrLast = enrNew; converged = ((pctx->flag.convergenceIgnoresPopCntl || (!pctx->iterParm.popCntlPeriod || (!pctx->addNum && !pctx->nixNum))) && AIR_IN_CL(0, enrDecreaseAvg, pctx->sysParm.energyDecreaseMin)); if (pctx->verbose) { fprintf(stderr, "%s: converged %d = (%d || (%d || (%d && %d))) " "&& (0 <= %g <= %g)=%d\n", me, converged, pctx->flag.convergenceIgnoresPopCntl, !pctx->iterParm.popCntlPeriod, !pctx->addNum, !pctx->nixNum, enrDecreaseAvg, pctx->sysParm.energyDecreaseMin, AIR_IN_OP(0, enrDecreaseAvg, pctx->sysParm.energyDecreaseMin)); } if (converged && pctx->verbose) { printf("%s: enrDecreaseAvg %g < %g: converged!!\n", me, enrDecreaseAvg, pctx->sysParm.energyDecreaseMin); } _pullPointStepEnergyScale(pctx, pctx->sysParm.opporStepScale); /* call the callback */ if (!(pctx->iter % pctx->iterParm.callback) && pctx->iter_cb) { pctx->iter_cb(pctx->data_cb); } } if (pctx->verbose) { printf("%s: done ((%d|%d)&%d) @iter %u: enr %g, enrDec = %g,%g " "%u stuck\n", me, !pctx->iterParm.max, pctx->iter < pctx->iterParm.max, !converged, pctx->iter, enrNew, enrDecrease, enrDecreaseAvg, pctx->stuckNum); } time1 = airTime(); pctx->timeRun += time1 - time0; pctx->energy = enrNew; if (0) { /* probe inter-particle energy function */ unsigned int szimg=300, ri, si; Nrrd *nout; pullPoint *pa, *pb; double rdir[3], len, r, s, *out, enr, egrad[4]; airRandMTState *rng; rng = pctx->task[0]->rng; nout = nrrdNew(); nrrdMaybeAlloc_va(nout, nrrdTypeDouble, 3, AIR_CAST(size_t, 3), AIR_CAST(size_t, szimg), AIR_CAST(size_t, szimg)); out = AIR_CAST(double *, nout->data); pa = pullPointNew(pctx); pb = pullPointNew(pctx); airNormalRand_r(pa->pos + 0, pa->pos + 1, rng); airNormalRand_r(pa->pos + 2, pa->pos + 3, rng); airNormalRand_r(rdir + 0, rdir + 1, rng); airNormalRand_r(rdir + 2, NULL, rng); ELL_3V_NORM(rdir, rdir, len); for (si=0; sisysParm.radiusScale, 1.5*pctx->sysParm.radiusScale); for (ri=0; risysParm.radiusSpace, 1.5*pctx->sysParm.radiusSpace); ELL_3V_SCALE_ADD2(pb->pos, 1.0, pa->pos, r, rdir); pb->pos[3] = pa->pos[3] + s; /* now points are in desired test positions */ enr = _pullEnergyInterParticle(pctx, pa, pb, AIR_ABS(r), AIR_ABS(s), egrad); ELL_3V_SET(out + 3*(ri + szimg*si), enr, ELL_3V_DOT(egrad, rdir), egrad[3]); } } nrrdSave("eprobe.nrrd", nout, NULL); pullPointNix(pa); pullPointNix(pb); nrrdNuke(nout); } return 0; } teem-1.11.0~svn6057/src/pull/binningPull.c0000664000175000017500000003561412174667307020023 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" /* ** because the pullContext keeps an array of bins (not pointers to them) ** we have Init and Done functions (not New and Nix) */ void _pullBinInit(pullBin *bin) { bin->point = NULL; bin->pointNum = 0; bin->pointArr = NULL; bin->neighBin = NULL; return; } /* ** bins own the points they contain- so this frees them */ void _pullBinDone(pullBin *bin) { unsigned int idx; for (idx=0; idxpointNum; idx++) { bin->point[idx] = pullPointNix(bin->point[idx]); } bin->pointArr = airArrayNuke(bin->pointArr); bin->neighBin = (pullBin **)airFree(bin->neighBin); return; } int _pullBinNeighborSet(pullContext *pctx, pullBin *bin) { static const char me[]="_pullBinNeighborSet"; unsigned int neiIdx, neiNum, be[4], binIdx; unsigned int xi, yi, zi, si, xx, yy, zz, ss, xmax, ymax, zmax, smax; pullBin *nei[3*3*3*3]; int xmin, ymin, zmin, smin; binIdx = AIR_UINT(bin - pctx->bin); /* annoyingly, have to recover the bin coordinates */ ELL_4V_COPY(be, pctx->binsEdge); xi = binIdx % be[0]; binIdx = (binIdx - xi)/be[0]; yi = binIdx % be[1]; binIdx = (binIdx - yi)/be[1]; zi = binIdx % be[2]; si = (binIdx - zi)/be[2]; neiNum = 0; bin->neighBin = (pullBin **)airFree(bin->neighBin); smin = AIR_MAX(0, (int)si-1); smax = AIR_MIN(si+1, be[3]-1); zmin = AIR_MAX(0, (int)zi-1); zmax = AIR_MIN(zi+1, be[2]-1); ymin = AIR_MAX(0, (int)yi-1); ymax = AIR_MIN(yi+1, be[1]-1); xmin = AIR_MAX(0, (int)xi-1); xmax = AIR_MIN(xi+1, be[0]-1); for (ss=smin; ss<=smax; ss++) { for (zz=zmin; zz<=zmax; zz++) { for (yy=ymin; yy<=ymax; yy++) { for (xx=xmin; xx<=xmax; xx++) { nei[neiNum++] = pctx->bin + xx + be[0]*(yy + be[1]*(zz + be[2]*ss)); } } } } if (!( bin->neighBin = AIR_CALLOC(1+neiNum, pullBin*) )) { biffAddf(PULL, "%s: couldn't calloc array of %u neighbor pointers", me, 1+neiNum); return 1; } for (neiIdx=0; neiIdxneighBin[neiIdx] = nei[neiIdx]; } /* NULL-terminate the bin array */ bin->neighBin[neiIdx] = NULL; return 0; } /* ** bins on boundary now extend to infinity; so the only time this ** returns NULL (indicating error) is for non-existent positions */ pullBin * _pullBinLocate(pullContext *pctx, double *posWorld) { static const char me[]="_pullBinLocate"; unsigned int axi, eidx[4], binIdx; if (!ELL_4V_EXISTS(posWorld)) { biffAddf(PULL, "%s: non-existent position (%g,%g,%g,%g)", me, posWorld[0], posWorld[1], posWorld[2], posWorld[3]); return NULL; } if (pctx->flag.binSingle) { binIdx = 0; } else { for (axi=0; axi<4; axi++) { eidx[axi] = airIndexClamp(pctx->bboxMin[axi], posWorld[axi], pctx->bboxMax[axi], pctx->binsEdge[axi]); } binIdx = (eidx[0] + pctx->binsEdge[0]*( eidx[1] + pctx->binsEdge[1]*( eidx[2] + pctx->binsEdge[2] * eidx[3]))); } return pctx->bin + binIdx; } /* ** this makes the bin the owner of the point */ int _pullBinPointAdd(pullContext *pctx, pullBin *bin, pullPoint *point) { static const char me[]="_pullBinPointAdd"; int pntI; pullPtrPtrUnion pppu; AIR_UNUSED(pctx); if (!(bin->pointArr)) { pppu.points = &(bin->point); bin->pointArr = airArrayNew(pppu.v, &(bin->pointNum), sizeof(pullPoint *), _PULL_BIN_INCR); if (!( bin->pointArr )) { biffAddf(PULL, "%s: couldn't create point array", me); return 1; } } if (!( bin->neighBin )) { /* set up neighbor bin vector if not done so already */ if (_pullBinNeighborSet(pctx, bin)) { biffAddf(PULL, "%s: couldn't initialize neighbor bins", me); return 1; } } pntI = airArrayLenIncr(bin->pointArr, 1); bin->point[pntI] = point; return 0; } /* ** the bin loses track of the point, caller responsible for ownership, ** even though caller never identifies it by pointer, which is weird */ void _pullBinPointRemove(pullContext *pctx, pullBin *bin, int loseIdx) { AIR_UNUSED(pctx); bin->point[loseIdx] = bin->point[bin->pointNum-1]; airArrayLenIncr(bin->pointArr, -1); return; } /* ** adds point to context */ int pullBinsPointAdd(pullContext *pctx, pullPoint *point, pullBin **binP) { static const char me[]="pullBinsPointAdd"; pullBin *bin; if (binP) { *binP = NULL; } if (!( bin = _pullBinLocate(pctx, point->pos) )) { biffAddf(PULL, "%s: can't locate point %p %u", me, AIR_CAST(void*, point), point->idtag); return 1; } if (binP) { *binP = bin; } if (_pullBinPointAdd(pctx, bin, point)) { biffAddf(PULL, "%s: trouble adding point %p %u", me, AIR_CAST(void*, point), point->idtag); return 1; } return 0; } int pullBinsPointMaybeAdd(pullContext *pctx, pullPoint *point, /* output */ pullBin **binP, int *added) { static const char me[]="pullBinsPointMaybeAdd"; pullBin *bin; unsigned int idx; int okay; if (binP) { *binP = NULL; } if (!(pctx && point && added)) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!( bin = _pullBinLocate(pctx, point->pos) )) { biffAddf(PULL, "%s: can't locate point %p %u", me, AIR_CAST(void*, point), point->idtag); return 1; } if (binP) { *binP = bin; } if (pctx->flag.restrictiveAddToBins) { okay = AIR_TRUE; for (idx=0; idxpointNum; idx++) { double diff[4], len; ELL_4V_SUB(diff, point->pos, bin->point[idx]->pos); ELL_3V_SCALE(diff, 1/pctx->sysParm.radiusSpace, diff); diff[3] /= pctx->sysParm.radiusScale; len = ELL_4V_LEN(diff); if (len < _PULL_BINNING_MAYBE_ADD_THRESH) { okay = AIR_FALSE; break; } } if (okay) { if (_pullBinPointAdd(pctx, bin, point)) { biffAddf(PULL, "%s: trouble adding point %p %u", me, AIR_CAST(void*, point), point->idtag); return 1; } *added = AIR_TRUE; } else { *added = AIR_FALSE; } } else { if (_pullBinPointAdd(pctx, bin, point)) { biffAddf(PULL, "%s: trouble adding point %p %u", me, AIR_CAST(void*, point), point->idtag); return 1; } *added = AIR_TRUE; } return 0; } int _pullBinSetup(pullContext *pctx) { static const char me[]="_pullBinSetup"; unsigned ii; double volEdge[4], width; /* the maximum distance of interaction is when one particle is sitting on the edge of another particle's sphere of influence, *NOT*, when the spheres of influence of two particles are tangent: particles interact with potential fields of other particles, but there is no interaction between potential fields. */ width = (pctx->sysParm.radiusSpace ? pctx->sysParm.radiusSpace : 0.1); pctx->maxDistSpace = pctx->sysParm.binWidthSpace*width; width = (pctx->sysParm.radiusScale ? pctx->sysParm.radiusScale : 0.1); pctx->maxDistScale = 1*width; if (pctx->verbose) { printf("%s: radiusSpace = %g -(%g)-> maxDistSpace = %g\n", me, pctx->sysParm.radiusSpace, pctx->sysParm.binWidthSpace, pctx->maxDistSpace); printf("%s: radiusScale = %g ----> maxDistScale = %g\n", me, pctx->sysParm.radiusScale, pctx->maxDistScale); } if (pctx->flag.binSingle) { pctx->binsEdge[0] = 1; pctx->binsEdge[1] = 1; pctx->binsEdge[2] = 1; pctx->binsEdge[3] = 1; pctx->binNum = 1; } else { volEdge[0] = pctx->bboxMax[0] - pctx->bboxMin[0]; volEdge[1] = pctx->bboxMax[1] - pctx->bboxMin[1]; volEdge[2] = pctx->bboxMax[2] - pctx->bboxMin[2]; volEdge[3] = pctx->bboxMax[3] - pctx->bboxMin[3]; if (pctx->verbose) { printf("%s: volEdge = %g %g %g %g\n", me, volEdge[0], volEdge[1], volEdge[2], volEdge[3]); } pctx->binsEdge[0] = AIR_CAST(unsigned int, floor(volEdge[0]/pctx->maxDistSpace)); pctx->binsEdge[0] = pctx->binsEdge[0] ? pctx->binsEdge[0] : 1; pctx->binsEdge[1] = AIR_CAST(unsigned int, floor(volEdge[1]/pctx->maxDistSpace)); pctx->binsEdge[1] = pctx->binsEdge[1] ? pctx->binsEdge[1] : 1; pctx->binsEdge[2] = AIR_CAST(unsigned int, floor(volEdge[2]/pctx->maxDistSpace)); pctx->binsEdge[2] = pctx->binsEdge[2] ? pctx->binsEdge[2] : 1; pctx->binsEdge[3] = AIR_CAST(unsigned int, floor(volEdge[3]/pctx->maxDistScale)); pctx->binsEdge[3] = pctx->binsEdge[3] ? pctx->binsEdge[3] : 1; /* hack to observe things at bin boundaries ELL_3V_SET(pctx->binsEdge, 3, 3, 3); */ if (pctx->verbose) { printf("%s: binsEdge=(%u,%u,%u,%u)\n", me, pctx->binsEdge[0], pctx->binsEdge[1], pctx->binsEdge[2], pctx->binsEdge[3]); } pctx->binNum = (pctx->binsEdge[0]*pctx->binsEdge[1] *pctx->binsEdge[2]*pctx->binsEdge[3]); } if (pctx->binNum > PULL_BIN_MAXNUM) { biffAddf(PULL, "%s: sorry, #bins %u > PULL_BIN_MAXNUM %u. Try " "increasing pctx->sysParm.binWidthSpace (%g)", me, pctx->binNum, PULL_BIN_MAXNUM, pctx->sysParm.binWidthSpace); return 1; } if (pctx->verbose) { printf("%s: trying to allocate %u bins . . . \n", me, pctx->binNum); } pctx->bin = AIR_CALLOC(pctx->binNum, pullBin); if (!( pctx->bin )) { biffAddf(PULL, "%s: couln't allocate %u bins", me, pctx->binNum); return 1; } if (pctx->verbose) { printf("%s: done allocating. Initializing . . . \n", me); } for (ii=0; iibinNum; ii++) { _pullBinInit(pctx->bin + ii); } if (pctx->verbose) { printf("%s: done initializing.\n", me); } if (pctx->flag.binSingle) { if (!( pctx->bin[0].neighBin = AIR_CALLOC(2, pullBin*) )) { biffAddf(PULL, "%s: trouble allocating for single bin?", me); return 1; } pctx->bin[0].neighBin[0] = pctx->bin + 0; pctx->bin[0].neighBin[1] = NULL; } return 0; } void _pullBinFinish(pullContext *pctx) { unsigned int ii; for (ii=0; iibinNum; ii++) { _pullBinDone(pctx->bin + ii); } pctx->bin = (pullBin *)airFree(pctx->bin); ELL_4V_SET(pctx->binsEdge, 0, 0, 0, 0); pctx->binNum = 0; } /* ** sets pctx->stuckNum ** resets all task[]->stuckNum ** reallocates pctx->tmpPointPerm and pctx->tmpPointPtr ** the point of this is to do rebinning ** ** This function is only called by the master thread, this ** does *not* have to be thread-safe in any way */ int _pullIterFinishDescent(pullContext *pctx) { static const char me[]="_pullIterFinishDescent"; unsigned int oldBinIdx, pointIdx, taskIdx, runIdx, pointNum; pullBin *oldBin; pullPoint *point; int added; _pullNixTheNixed(pctx); pctx->stuckNum = 0; for (taskIdx=0; taskIdxthreadNum; taskIdx++) { pctx->stuckNum += pctx->task[taskIdx]->stuckNum; pctx->task[taskIdx]->stuckNum = 0; } /* even w/ a single bin, we may still have to permute the points */ pointNum = pullPointNumber(pctx); if (pointNum != pctx->tmpPointNum) { if (pctx->verbose) { printf("!%s: changing total point # %u --> %u\n", me, pctx->tmpPointNum, pointNum); } airFree(pctx->tmpPointPerm); airFree(pctx->tmpPointPtr); pctx->tmpPointPtr = AIR_CAST(pullPoint **, calloc(pointNum, sizeof(pullPoint*))); pctx->tmpPointPerm = AIR_CAST(unsigned int *, calloc(pointNum, sizeof(unsigned int))); if (!( pctx->tmpPointPtr && pctx->tmpPointPerm )) { biffAddf(PULL, "%s: couldn't allocate tmp buffers %p %p", me, AIR_VOIDP(pctx->tmpPointPtr), AIR_VOIDP(pctx->tmpPointPerm)); return 1; } pctx->tmpPointNum = pointNum; } runIdx = 0; for (oldBinIdx=0; oldBinIdxbinNum; oldBinIdx++) { oldBin = pctx->bin + oldBinIdx; while (oldBin->pointNum) { /* tricky: we can't traverse bin->point[], because of how it is re-ordered on point removal, so we always grab point[0] */ pctx->tmpPointPtr[runIdx++] = oldBin->point[0]; _pullBinPointRemove(pctx, oldBin, 0); } } airShuffle_r(pctx->task[0]->rng, pctx->tmpPointPerm, pointNum, pctx->flag.permuteOnRebin); if (pctx->flag.permuteOnRebin && pctx->verbose) { printf("%s: permuting %u points\n", me, pointNum); } for (pointIdx=0; pointIdxtmpPointPtr[pctx->tmpPointPerm[pointIdx]]; /* if (1 == pctx->iter && 102 == point->idtag) { _pullDebugSanity(pctx->task[0], point); } */ /* Sun Jul 14 01:30:41 CDT 2013: the previous code was basically just pullBinsPointAdd(). But when working with codim-3 constraints (like finding spatial maxima with maximal strength along scale), its easy for many points to start piling on top of each other; that is the problem that pullFlagRestrictiveAddToBins was designed to solve. */ if (pctx->constraint && 0 == pctx->constraintDim) { if (pullBinsPointMaybeAdd(pctx, point, NULL, &added)) { biffAddf(PULL, "%s: trouble binning? point %u", me, point->idtag); return 1; } if (!added) { /* the point wasn't owned by any bin, and now it turns out no bin wanted to own it, so we have to remove it */ /* in the case of point maxima searching; this mainly happened because two points at the same spatial positition slowly descended along scale towards each other at the scale of maximal strength */ point = pullPointNix(point); } } else { if (pullBinsPointAdd(pctx, point, NULL)) { biffAddf(PULL, "%s: trouble binning point %u", me, point->idtag); return 1; } point = NULL; } pctx->tmpPointPtr[pctx->tmpPointPerm[pointIdx]] = NULL; } return 0; } teem-1.11.0~svn6057/src/pull/enumsPull.c0000664000175000017500000001670412165631065017515 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" /* --------------------------------------------------------- */ static const char * _pullSourceStr[] = { "(unknown pullSource)", "gage", "prop" }; static const airEnum _pullSource = { "pullSource", PULL_SOURCE_MAX, _pullSourceStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const pullSource = &_pullSource; /* --------------------------------------------------------- */ static const char * _pullInfoStr[] = { "(unknown pullInfo)", "ten", "teni", "hess", "in", "ingradvec", "hght", "hghtgradvec", "hghthessian", "hghtlapl", "seedprethresh", "seedthresh", "livethresh", "livethresh2", "livethresh3", "tan1", "tan2", "negtan1", "negtan2", "isoval", "isogradvec", "isohessian", "strength", "quality" }; static const int _pullInfoVal[] = { pullInfoUnknown, pullInfoTensor, /* [7] tensor here */ pullInfoTensorInverse, /* [7] inverse of tensor here */ pullInfoHessian, /* [9] hessian used for force distortion */ pullInfoInside, /* [1] containment scalar */ pullInfoInsideGradient, /* [3] containment vector */ pullInfoHeight, /* [1] for gravity */ pullInfoHeightGradient, /* [3] for gravity */ pullInfoHeightHessian, /* [9] for gravity */ pullInfoHeightLaplacian, /* [1] */ pullInfoSeedPreThresh, /* [1] */ pullInfoSeedThresh, /* [1] scalar for thresholding seeding */ pullInfoLiveThresh, /* [1] */ pullInfoLiveThresh2, /* [1] */ pullInfoLiveThresh3, /* [1] */ pullInfoTangent1, /* [3] first tangent to constraint surf */ pullInfoTangent2, /* [3] second tangent to constraint surf */ pullInfoNegativeTangent1, /* [3] */ pullInfoNegativeTangent2, /* [3] */ pullInfoIsovalue, /* [1] */ pullInfoIsovalueGradient, /* [3] */ pullInfoIsovalueHessian, /* [9] */ pullInfoStrength, /* [1] */ pullInfoQuality /* [1] */ }; static const char * _pullInfoStrEqv[] = { "ten", "teni", "hess", "in", "ingradvec", "hght", "h", "hghtgradvec", "hgvec", "hghthessian", "hhess", "hghtlapl", "hlapl", "seedprethresh", "spthr", "seedthresh", "sthr", "livethresh", "lthr", "livethresh2", "lthr2", "livethresh3", "lthr3", "tan1", "tan2", "ntan1", "negtan1", "ntan2", "negtan2", "isoval", "iso", "isogradvec", "isogvec", "isohessian", "isohess", "strength", "strn", "quality", "qual", "" }; static const int _pullInfoValEqv[] = { pullInfoTensor, pullInfoTensorInverse, pullInfoHessian, pullInfoInside, pullInfoInsideGradient, pullInfoHeight, pullInfoHeight, pullInfoHeightGradient, pullInfoHeightGradient, pullInfoHeightHessian, pullInfoHeightHessian, pullInfoHeightLaplacian, pullInfoHeightLaplacian, pullInfoSeedPreThresh, pullInfoSeedPreThresh, pullInfoSeedThresh, pullInfoSeedThresh, pullInfoLiveThresh, pullInfoLiveThresh, pullInfoLiveThresh2, pullInfoLiveThresh2, pullInfoLiveThresh3, pullInfoLiveThresh3, pullInfoTangent1, pullInfoTangent2, pullInfoNegativeTangent1, pullInfoNegativeTangent1, pullInfoNegativeTangent2, pullInfoNegativeTangent2, pullInfoIsovalue, pullInfoIsovalue, pullInfoIsovalueGradient, pullInfoIsovalueGradient, pullInfoIsovalueHessian, pullInfoIsovalueHessian, pullInfoStrength, pullInfoStrength, pullInfoQuality, pullInfoQuality }; static const airEnum _pullInfo = { "pullInfo", PULL_INFO_MAX, _pullInfoStr, _pullInfoVal, NULL, _pullInfoStrEqv, _pullInfoValEqv, AIR_FALSE }; const airEnum *const pullInfo = &_pullInfo; /* --------------------------------------------------------- */ const char * _pullPropStr[] = { "(unknown_prop)", "idtag", /* pullPropIdtag */ "idcc", /* pullPropIdCC */ "energy", /* pullPropEnergy */ "stepEnergy", /* pullPropStepEnergy */ "stepConstr", /* pullPropStepConstr */ "stuck", /* pullPropStuck */ "position", /* pullPropPosition */ "force", /* pullPropForce */ "neighDistMean", /* pullPropNeighDistMean */ "scale", /* pullPropScale */ "neighCovar", /* pullPropNeighCovar */ "neighCovar7Ten", /* pullPropNeighCovar7Ten */ "neighTanCovar", /* pullPropNeighTanCovar */ "internum", /* pullPropNeighInterNum */ "neighCovarTrace", /* pullPropNeighCovarTrace */ "neighCovarDet", /* pullPropNeighCovarDet */ "stability" /* pullPropStability */ }; static const airEnum _pullProp = { "pullProp", PULL_PROP_MAX, _pullPropStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const pullProp = &_pullProp; /* --------------------------------------------------------- */ const char * _pullProcessModeStr[] = { "(unknown_mode)", "descent", "nlearn", "adding", "nixing" }; const airEnum _pullProcessMode = { "process mode", PULL_PROCESS_MODE_MAX, _pullProcessModeStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const pullProcessMode = &_pullProcessMode; /* --------------------------------------------------------- */ const char * _pullTraceStopStr[] = { "(unknown_trace_stop)", "speeding", "constrfail", "bounds", "length", "stub" }; const airEnum _pullTraceStop = { "trace stop", PULL_TRACE_STOP_MAX, _pullTraceStopStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const pullTraceStop = &_pullTraceStop; /* --------------------------------------------------------- */ const char * _pullCountStr[] = { "(unknown_countable)", "descent", "teststep", "enrg(img)", "frc(img)", "enrg(pts)", "frc(pts)", "probe", "constr", "adding", "nixing", "pts stuck", "pts", "CC", "iter" }; const airEnum _pullCount = { "countable", PULL_COUNT_MAX, _pullCountStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const pullCount = &_pullCount; /* --------------------------------------------------------- */ const char * _pullConstraintFailStr[PULL_CONSTRAINT_FAIL_MAX+1] = { "(unknown or no contraint fail)", "projected gradient 0 (A)", "projected gradient 0 (B)", "iter max exceeded", "travel exceeded" }; const airEnum _pullConstraintFail = { "constraint fail", PULL_CONSTRAINT_FAIL_MAX, _pullConstraintFailStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const pullConstraintFail = &_pullConstraintFail; teem-1.11.0~svn6057/src/pull/contextPull.c0000664000175000017500000006737212165631065020061 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" pullContext * pullContextNew(void) { pullContext *pctx; unsigned int ii; pctx = (pullContext *)calloc(1, sizeof(pullContext)); if (!pctx) { return NULL; } _pullInitParmInit(&(pctx->initParm)); _pullIterParmInit(&(pctx->iterParm)); _pullSysParmInit(&(pctx->sysParm)); _pullFlagInit(&(pctx->flag)); pctx->verbose = 0; pctx->threadNum = 1; pctx->rngSeed = 42; pctx->progressBinMod = 50; pctx->iter_cb = NULL; pctx->data_cb = NULL; for (ii=0; iivol[ii] = NULL; } pctx->volNum = 0; for (ii=0; ii<=PULL_INFO_MAX; ii++) { pctx->ispec[ii] = NULL; pctx->infoIdx[ii] = UINT_MAX; } pctx->interType = pullInterTypeUnknown; pctx->energySpecR = pullEnergySpecNew(); pctx->energySpecS = pullEnergySpecNew(); pctx->energySpecWin = pullEnergySpecNew(); pctx->haltonOffset = 0; ELL_4V_SET(pctx->bboxMin, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); ELL_4V_SET(pctx->bboxMax, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); pctx->infoTotalLen = 0; /* will be set later */ pctx->idtagNext = 0; pctx->haveScale = AIR_FALSE; pctx->constraint = 0; pctx->constraintDim = -1; pctx->targetDim = -1; pctx->finished = AIR_FALSE; pctx->maxDistSpace = AIR_NAN; pctx->maxDistScale = AIR_NAN; pctx->voxelSizeSpace = AIR_NAN; pctx->voxelSizeScale = AIR_NAN; pctx->eipScale = 1.0; pctx->bin = NULL; ELL_4V_SET(pctx->binsEdge, 0, 0, 0, 0); pctx->binNum = 0; pctx->binNextIdx = 0; pctx->tmpPointPerm = NULL; pctx->tmpPointPtr = NULL; pctx->tmpPointNum = 0; /* pctx->binMutex setup my pullStart */ pctx->task = NULL; pctx->iterBarrierA = NULL; pctx->iterBarrierB = NULL; #if PULL_HINTER pctx->nhinter = nrrdNew(); #endif pctx->logAdd = NULL; pctx->timeIteration = 0; pctx->timeRun = 0; pctx->energy = AIR_NAN; pctx->addNum = 0; pctx->nixNum = 0; pctx->stuckNum = 0; pctx->pointNum = 0; pctx->iter = 0; for (ii=pullCountUnknown; iicount[ii] = 0; } return pctx; } /* ** this should only nix things created by pullContextNew, or the things ** (vols and ispecs) that were explicitly added to this context */ pullContext * pullContextNix(pullContext *pctx) { unsigned int ii; if (pctx) { for (ii=0; iivolNum; ii++) { pctx->vol[ii] = pullVolumeNix(pctx->vol[ii]); } pctx->volNum = 0; for (ii=0; ii<=PULL_INFO_MAX; ii++) { if (pctx->ispec[ii]) { pctx->ispec[ii] = pullInfoSpecNix(pctx->ispec[ii]); } } pctx->energySpecR = pullEnergySpecNix(pctx->energySpecR); pctx->energySpecS = pullEnergySpecNix(pctx->energySpecS); pctx->energySpecWin = pullEnergySpecNix(pctx->energySpecWin); #if PULL_HINTER nrrdNuke(pctx->nhinter); #endif /* handled elsewhere: bin, task, iterBarrierA, iterBarrierB */ airFree(pctx); } return NULL; } int _pullMiscParmCheck(pullContext *pctx) { static const char me[]="_pullMiscParmCheck"; double denr; if (!( AIR_IN_CL(1, pctx->threadNum, PULL_THREAD_MAXNUM) )) { biffAddf(PULL, "%s: pctx->threadNum (%d) outside valid range [1,%d]", me, pctx->threadNum, PULL_THREAD_MAXNUM); return 1; } if (airEnumValCheck(pullInterType, pctx->interType)) { biffAddf(PULL, "%s: pctx->interType %d not a valid %s", me, pctx->interType, pullInterType->name); return 1; } /* HEY: error checking on energySpec's seems rather spotty . . . */ if (pullEnergyUnknown == pctx->energySpecR->energy) { biffAddf(PULL, "%s: need to set space energy", me); return 1; } if (pullInterTypeJustR == pctx->interType || pullInterTypeUnivariate == pctx->interType) { if (pullEnergyZero != pctx->energySpecS->energy) { biffAddf(PULL, "%s: can't use scale energy %s with inter type %s", me, pctx->energySpecS->energy->name, airEnumStr(pullInterType, pctx->interType)); return 1; } } else { if (pullEnergyZero == pctx->energySpecS->energy) { biffAddf(PULL, "%s: need a non-zero scale energy for inter type %s", me, airEnumStr(pullInterType, pctx->interType)); return 1; } } /* make sure that spatial repulsion is really repulsive at r=0 */ pctx->energySpecR->energy->eval(&denr, 0.0000001, pctx->energySpecR->parm); if (!( denr < 0 )) { biffAddf(PULL, "%s: spatial energy doesn't have negative slope near r=0", me); return 1; } return 0; } int _pullContextCheck(pullContext *pctx) { static const char me[]="_pullContextCheck"; unsigned int ii, ccount; int gotIspec, gotConstr; const pullInfoSpec *lthr, *strn; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (_pullInitParmCheck(&(pctx->initParm)) || _pullIterParmCheck(&(pctx->iterParm)) || _pullSysParmCheck(&(pctx->sysParm)) || _pullMiscParmCheck(pctx)) { biffAddf(PULL, "%s: problem with parameters", me); return 1; } if (!pctx->volNum) { biffAddf(PULL, "%s: have no volumes set", me); return 1; } gotConstr = 0; gotIspec = AIR_FALSE; for (ii=0; ii<=PULL_INFO_MAX; ii++) { if (pctx->ispec[ii]) { if (pctx->ispec[ii]->constraint) { if (1 != pullInfoLen(ii)) { biffAddf(PULL, "%s: can't use non-scalar (len %u) %s as constraint", me, pullInfoLen(ii), airEnumStr(pullInfo, ii)); return 1; } if (pullSourceGage != pctx->ispec[ii]->source) { biffAddf(PULL, "%s: sorry, constraints can currently only " "come from %s", me, airEnumStr(pullSource, pullSourceGage)); return 1; } if (gotConstr) { biffAddf(PULL, "%s: can't also have %s constraint, already have " "constraint on %s ", me, airEnumStr(pullInfo, ii), airEnumStr(pullInfo, gotConstr)); return 1; } /* elso no problems having constraint on ii */ gotConstr = ii; } /* make sure we have extra info as necessary */ switch (ii) { case pullInfoInside: case pullInfoHeight: case pullInfoHeightLaplacian: case pullInfoSeedThresh: case pullInfoLiveThresh: case pullInfoLiveThresh2: case pullInfoLiveThresh3: case pullInfoIsovalue: case pullInfoStrength: if (!( AIR_EXISTS(pctx->ispec[ii]->scale) && AIR_EXISTS(pctx->ispec[ii]->zero) )) { biffAddf(PULL, "%s: %s info needs scale (%g) and zero (%g)", me, airEnumStr(pullInfo, ii), pctx->ispec[ii]->scale, pctx->ispec[ii]->zero); return 1; } break; } gotIspec = AIR_TRUE; } } if (!gotIspec) { biffAddf(PULL, "%s: have no infos set", me); return 1; } if (pctx->ispec[pullInfoStrength]) { if (pullSourceGage != pctx->ispec[pullInfoStrength]->source) { biffAddf(PULL, "%s: %s info must come from %s (not %s)", me, airEnumStr(pullInfo, pullInfoStrength), airEnumStr(pullSource, pullSourceGage), airEnumStr(pullSource, pctx->ispec[pullInfoStrength]->source)); return 1; } } if (pctx->ispec[pullInfoInside]) { if (!pctx->ispec[pullInfoInsideGradient]) { biffAddf(PULL, "%s: want %s but don't have %s set", me, airEnumStr(pullInfo, pullInfoInside), airEnumStr(pullInfo, pullInfoInsideGradient)); return 1; } } if (pctx->ispec[pullInfoTangent2]) { if (!pctx->ispec[pullInfoTangent1]) { biffAddf(PULL, "%s: want %s but don't have %s set", me, airEnumStr(pullInfo, pullInfoTangent2), airEnumStr(pullInfo, pullInfoTangent1)); return 1; } } if (pctx->ispec[pullInfoNegativeTangent2]) { if (!pctx->ispec[pullInfoNegativeTangent1]) { biffAddf(PULL, "%s: want %s but don't have %s set", me, airEnumStr(pullInfo, pullInfoNegativeTangent2), airEnumStr(pullInfo, pullInfoNegativeTangent1)); return 1; } } ccount = 0; ccount += !!(pctx->ispec[pullInfoTangent1]); ccount += !!(pctx->ispec[pullInfoTangent2]); ccount += !!(pctx->ispec[pullInfoNegativeTangent1]); ccount += !!(pctx->ispec[pullInfoNegativeTangent2]); if (4 == ccount) { biffAddf(PULL, "%s: can't specify all 4 tangents together", me); return 1; } if (3 == ccount && !pctx->flag.allowCodimension3Constraints) { biffAddf(PULL, "%s: must turn on allowCodimension3Constraints " "with 3 tangents", me); return 1; } if (pctx->ispec[pullInfoHeight]) { if (!( pctx->ispec[pullInfoHeightGradient] )) { biffAddf(PULL, "%s: want %s but don't have %s set", me, airEnumStr(pullInfo, pullInfoHeight), airEnumStr(pullInfo, pullInfoHeightGradient)); return 1; } if (pctx->ispec[pullInfoHeight]->constraint) { if (!pctx->ispec[pullInfoHeightHessian]) { biffAddf(PULL, "%s: want constrained %s but don't have %s set", me, airEnumStr(pullInfo, pullInfoHeight), airEnumStr(pullInfo, pullInfoHeightHessian)); return 1; } if (!( pctx->ispec[pullInfoTangent1] || pctx->ispec[pullInfoNegativeTangent1] )) { if (!pctx->flag.allowCodimension3Constraints) { biffAddf(PULL, "%s: want constrained %s but need (at least) " "%s or %s set (maybe enable " "pullFlagAllowCodimension3Constraints?)", me, airEnumStr(pullInfo, pullInfoHeight), airEnumStr(pullInfo, pullInfoTangent1), airEnumStr(pullInfo, pullInfoNegativeTangent1)); return 1; } } } } if (pctx->ispec[pullInfoHeightLaplacian]) { if (!( pctx->ispec[pullInfoHeight] )) { biffAddf(PULL, "%s: want %s but don't have %s set", me, airEnumStr(pullInfo, pullInfoHeightLaplacian), airEnumStr(pullInfo, pullInfoHeight)); return 1; } } if (pctx->ispec[pullInfoIsovalue]) { if (!( pctx->ispec[pullInfoIsovalueGradient] && pctx->ispec[pullInfoIsovalueHessian] )) { biffAddf(PULL, "%s: want %s but don't have %s and %s set", me, airEnumStr(pullInfo, pullInfoIsovalue), airEnumStr(pullInfo, pullInfoIsovalueGradient), airEnumStr(pullInfo, pullInfoIsovalueHessian)); return 1; } } if ((lthr = pctx->ispec[pullInfoLiveThresh]) && (strn = pctx->ispec[pullInfoStrength]) && lthr->volIdx == strn->volIdx && lthr->item == strn->item && lthr->scale*strn->scale < 0) { biffAddf(PULL, "%s: %s and %s refer to same item (%s in %s), but have " "scaling factors with different signs (%g and %g); really?", me, airEnumStr(pullInfo, pullInfoLiveThresh), airEnumStr(pullInfo, pullInfoStrength), airEnumStr(pctx->vol[lthr->volIdx]->kind->enm, lthr->item), lthr->volName, lthr->scale, strn->scale); return 1; } if (pullInitMethodPointPerVoxel == pctx->initParm.method) { if (!( pctx->ispec[pullInfoSeedThresh] )) { biffAddf(PULL, "%s: sorry, need to have %s info set with %s init", me, airEnumStr(pullInfo, pullInfoSeedThresh), "point-per-voxel" /* HEY no airEnum for this */); return 1; } } return 0; } /* ** the API for this is most certainly going to change; the ** tensor output at this point is a hack created for vis purposes */ int pullOutputGetFilter(Nrrd *nPosOut, Nrrd *nTenOut, Nrrd *nStrengthOut, const double _scaleVec[3], double scaleRad, pullContext *pctx, unsigned int idtagMin, unsigned int idtagMax) { static const char me[]="pullOutputGetFilter"; unsigned int binIdx, pointNum, pointIdx, outIdx; int E; double *posOut, *tenOut, *strnOut, scaleVec[3], scaleDir[3], scaleMag; pullBin *bin; pullPoint *point; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (nStrengthOut && !pctx->ispec[pullInfoStrength]) { biffAddf(PULL, "%s: can't save out %s info that hasn't been set", me, airEnumStr(pullInfo, pullInfoStrength)); return 1; } if (!AIR_EXISTS(scaleRad)) { biffAddf(PULL, "%s: got non-existent scaleRad %g", me, scaleRad); return 1; } if (!_scaleVec) { ELL_3V_SET(scaleVec, 0, 0, 0); ELL_3V_SET(scaleDir, 0, 0, 0); scaleMag = 0; } else { ELL_3V_COPY(scaleVec, _scaleVec); if (ELL_3V_LEN(scaleVec)) { ELL_3V_NORM(scaleDir, scaleVec, scaleMag); } else { ELL_3V_SET(scaleDir, 0, 0, 0); scaleMag = 0; } } pointNum = pullPointNumberFilter(pctx, idtagMin, idtagMax); E = AIR_FALSE; if (nPosOut) { E |= nrrdMaybeAlloc_va(nPosOut, nrrdTypeDouble, 2, AIR_CAST(size_t, 4), AIR_CAST(size_t, pointNum)); } if (nTenOut) { E |= nrrdMaybeAlloc_va(nTenOut, nrrdTypeDouble, 2, AIR_CAST(size_t, 7), AIR_CAST(size_t, pointNum)); } if (nStrengthOut) { E |= nrrdMaybeAlloc_va(nStrengthOut, nrrdTypeDouble, 1, AIR_CAST(size_t, pointNum)); } if (E) { biffMovef(PULL, NRRD, "%s: trouble allocating outputs", me); return 1; } posOut = nPosOut ? (double*)(nPosOut->data) : NULL; tenOut = nTenOut ? (double*)(nTenOut->data) : NULL; strnOut = nStrengthOut ? (double*)(nStrengthOut->data) : NULL; outIdx = 0; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; if (!( idtagMin <= point->idtag && (0 == idtagMax || point->idtag <= idtagMax) )) { continue; } /** to find idtag of point at particular location **/ /* if (AIR_ABS(514.113 - point->pos[0]) < 0.5 && AIR_ABS(453.519 - point->pos[1]) < 0.5 && AIR_ABS(606.723 - point->pos[ 2 ]) < 0.5) { printf("!%s: point %u at (%g,%g,%g,%g) ##############\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); } */ if (nPosOut) { ELL_4V_COPY(posOut + 4*outIdx, point->pos); if (pctx->haveScale && scaleMag) { double *tpos, tvec[3], sc; tpos = posOut + 4*outIdx; sc = ELL_3V_DOT(tpos, scaleDir); ELL_3V_SCALE(tvec, sc, scaleDir); ELL_3V_SUB(tpos, tpos, tvec); ELL_3V_SCALE(tvec, scaleMag*tpos[3], scaleDir); ELL_3V_ADD2(tpos, tpos, tvec); } /* if (4523 == point->idtag) { fprintf(stderr, "!%s: point %u at index %u and pos %g %g %g %g\n", me, point->idtag, outIdx, (posOut + 4*outIdx)[0], (posOut + 4*outIdx)[1], (posOut + 4*outIdx)[2], (posOut + 4*outIdx)[3]); } */ } if (nStrengthOut) { strnOut[outIdx] = pullPointScalar(pctx, point, pullInfoStrength, NULL, NULL); } if (nTenOut) { double scl, tout[7]; scl = 1; if (pctx->ispec[pullInfoTensor]) { TEN_T_COPY(tout, point->info + pctx->infoIdx[pullInfoTensor]); } else if (pctx->ispec[pullInfoHeightHessian]) { double *hess, eval[3], evec[9], eceil, maxeval, elen; unsigned int maxi; hess = point->info + pctx->infoIdx[pullInfoHeightHessian]; if (0) { /* do this if using general symmetric tensor glyphs */ TEN_M2T(tout, hess); tout[0] = 1.0; } if (0) { /* for spheres and only spheres */ TEN_T_SET(tout, 1, 1, 0, 0, 1, 0, 1); } else { ell_3m_eigensolve_d(eval, evec, hess, 10); eval[0] = AIR_ABS(eval[0]); eval[1] = AIR_ABS(eval[1]); eval[2] = AIR_ABS(eval[2]); /* elen = ELL_3V_LEN(eval); */ elen = (eval[0]+eval[1]+eval[2]); eceil = elen ? 10/elen : 10; eval[0] = eval[0] ? AIR_MIN(eceil, 1.0/eval[0]) : eceil; eval[1] = eval[1] ? AIR_MIN(eceil, 1.0/eval[1]) : eceil; eval[2] = eval[2] ? AIR_MIN(eceil, 1.0/eval[2]) : eceil; maxi = ELL_MAX3_IDX(eval[0], eval[1], eval[2]); maxeval = eval[maxi]; ELL_3V_SCALE(eval, 1/maxeval, eval); tenMakeSingle_d(tout, 1, eval, evec); if (scaleRad && pctx->ispec[pullInfoHeight]->constraint) { double emin, sig; if (pctx->flag.scaleIsTau) { sig = gageSigOfTau(point->pos[3]); } else { sig = point->pos[3]; } tenEigensolve_d(eval, evec, tout); /* lazy way to sort */ emin = eval[2]; if (1 == pctx->constraintDim) { eval[1] = scaleRad*sig + emin/2; eval[2] = scaleRad*sig + emin/2; } if (2 == pctx->constraintDim) { double eavg; eavg = (2*eval[0] + eval[2])/3; eval[0] = eavg; eval[1] = eavg; eval[2] = scaleRad*sig + emin/2; } tenMakeSingle_d(tout, 1, eval, evec); } } } else if (0 /* another hack for general symmetric tensor glyphs */ && pctx->constraint && (pctx->ispec[pullInfoIsovalueHessian])) { double *hess; hess = point->info + pctx->infoIdx[pullInfoIsovalueHessian]; TEN_M2T(tout, hess); tout[0] = 1.0; } else if (pctx->constraint && (pctx->ispec[pullInfoHeightGradient] || pctx->ispec[pullInfoIsovalueGradient])) { double *grad, norm[3], len, mat[9], out[9]; grad = point->info + (pctx->ispec[pullInfoHeightGradient] ? pctx->infoIdx[pullInfoHeightGradient] : pctx->infoIdx[pullInfoIsovalueGradient]); ELL_3V_NORM(norm, grad, len); ELL_3MV_OUTER(out, norm, norm); ELL_3M_IDENTITY_SET(mat); ELL_3M_SCALE_INCR(mat, -0.95, out); TEN_M2T(tout, mat); tout[0] = 1; } else { TEN_T_SET(tout, 1, 1, 0, 0, 1, 0, 1); } TEN_T_SCALE(tout, scl, tout); TEN_T_COPY(tenOut + 7*outIdx, tout); /* if (4523 == point->idtag) { fprintf(stderr, "!%s: point %u at index %u and ten (%g) %g %g %g %g %g %g\n", me, point->idtag, outIdx, tout[0], tout[1], tout[2], tout[3], tout[4], tout[5], tout[6]); } */ } /* if (nTenOut) */ ++outIdx; } } return 0; } int pullOutputGet(Nrrd *nPosOut, Nrrd *nTenOut, Nrrd *nStrengthOut, const double scaleVec[3], double scaleRad, pullContext *pctx) { static const char me[]="pullOutputGet"; if (pullOutputGetFilter(nPosOut, nTenOut, nStrengthOut, scaleVec, scaleRad, pctx, 0, 0)) { biffAddf(PULL, "%s: trouble", me); return 1; } return 0; } int pullPropGet(Nrrd *nprop, int prop, pullContext *pctx) { static const char me[]="pullPropGet"; int typeOut; size_t size[2]; unsigned int dim, pointNum, pointIdx, binIdx, *out_ui, outIdx; double *out_d, covar[16]; float *out_f, *pnc; unsigned char *out_uc; pullBin *bin; pullPoint *point; pointNum = pullPointNumber(pctx); switch(prop) { case pullPropEnergy: case pullPropStepEnergy: case pullPropStepConstr: case pullPropScale: case pullPropNeighCovarTrace: case pullPropNeighCovarDet: case pullPropStability: dim = 1; size[0] = pointNum; typeOut = nrrdTypeDouble; break; case pullPropIdtag: case pullPropIdCC: case pullPropNeighInterNum: dim = 1; size[0] = pointNum; typeOut = nrrdTypeUInt; break; case pullPropStuck: dim = 1; size[0] = pointNum; /* HEY should be nrrdTypeUInt, no? */ typeOut = nrrdTypeUChar; break; case pullPropPosition: case pullPropForce: dim = 2; size[0] = 4; size[1] = pointNum; typeOut = nrrdTypeDouble; break; case pullPropNeighDistMean: dim = 1; size[0] = pointNum; typeOut = nrrdTypeDouble; break; case pullPropNeighCovar: dim = 2; size[0] = 10; size[1] = pointNum; typeOut = nrrdTypeFloat; break; case pullPropNeighCovar7Ten: case pullPropNeighTanCovar: dim = 2; size[0] = 7; size[1] = pointNum; typeOut = nrrdTypeFloat; break; default: biffAddf(PULL, "%s: prop %d unrecognized", me, prop); return 1; break; } if (nrrdMaybeAlloc_nva(nprop, typeOut, dim, size)) { biffMovef(PULL, NRRD, "%s: trouble allocating output", me); return 1; } out_d = AIR_CAST(double *, nprop->data); out_f = AIR_CAST(float *, nprop->data); out_ui = AIR_CAST(unsigned int *, nprop->data); out_uc = AIR_CAST(unsigned char *, nprop->data); outIdx = 0; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; pnc = point->neighCovar; switch(prop) { case pullPropEnergy: out_d[outIdx] = point->energy; break; case pullPropStepEnergy: out_d[outIdx] = point->stepEnergy; break; case pullPropStepConstr: out_d[outIdx] = point->stepConstr; break; case pullPropIdtag: out_ui[outIdx] = point->idtag; break; case pullPropIdCC: out_ui[outIdx] = point->idCC; break; case pullPropNeighInterNum: out_ui[outIdx] = point->neighInterNum; break; case pullPropStuck: out_uc[outIdx] = ((point->status & PULL_STATUS_STUCK_BIT) ? point->stuckIterNum : 0); break; case pullPropPosition: ELL_4V_COPY(out_d + 4*outIdx, point->pos); break; case pullPropForce: ELL_4V_COPY(out_d + 4*outIdx, point->force); break; case pullPropNeighDistMean: out_d[outIdx] = point->neighDistMean; break; case pullPropScale: out_d[outIdx] = (pctx->flag.scaleIsTau ? gageSigOfTau(point->pos[3]) : point->pos[3]); break; /* 0:xx 1:xy 2:xz 3:xs 1 4:yy 5:yz 6:ys 2 5 7:zz 8:zs 3 6 8 9:ss */ case pullPropNeighCovar: ELL_10V_COPY(out_f + 10*outIdx, point->neighCovar); break; case pullPropNeighCovar7Ten: TEN_T_SET(out_f + 7*outIdx, 1.0f, pnc[0], pnc[1], pnc[2], pnc[4], pnc[5], pnc[7]); break; case pullPropNeighTanCovar: #if PULL_TANCOVAR TEN_T_SET(out_f + 7*outIdx, 1.0f, point->neighTanCovar[0], point->neighTanCovar[1], point->neighTanCovar[2], point->neighTanCovar[3], point->neighTanCovar[4], point->neighTanCovar[5]); #else TEN_T_SET(out_f + 7*outIdx, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); #endif break; case pullPropNeighCovarTrace: out_d[outIdx] = pnc[0] + pnc[4] + pnc[7] + pnc[9]; break; case pullPropNeighCovarDet: if (pctx->haveScale) { ELL_4V_SET(covar + 0, pnc[0], pnc[1], pnc[2], pnc[3]); ELL_4V_SET(covar + 4, pnc[1], pnc[4], pnc[5], pnc[6]); ELL_4V_SET(covar + 8, pnc[2], pnc[5], pnc[7], pnc[8]); ELL_4V_SET(covar + 12, pnc[3], pnc[6], pnc[8], pnc[9]); out_d[outIdx] = ELL_4M_DET(covar); } else { ELL_3V_SET(covar + 0, pnc[0], pnc[1], pnc[2]); ELL_3V_SET(covar + 3, pnc[1], pnc[4], pnc[5]); ELL_3V_SET(covar + 6, pnc[2], pnc[5], pnc[7]); out_d[outIdx] = ELL_3M_DET(covar); } break; case pullPropStability: out_d[outIdx] = point->stability; break; default: biffAddf(PULL, "%s: prop %d unrecognized", me, prop); return 1; break; } ++outIdx; } /* for (pointIdx) */ } return 0; } int pullPositionHistoryGet(limnPolyData *pld, pullContext *pctx) { static const char me[]="pullPositionHistoryGet"; #if PULL_PHIST pullBin *bin; pullPoint *point; unsigned int binIdx, pointIdx, pointNum, vertNum, vertIdx, primIdx, phistIdx, phistNum; if (!(pld && pctx)) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } pointNum = 0; vertNum = 0; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; vertNum += point->phistArr->len; pointNum++; } } if (limnPolyDataAlloc(pld, 1 << limnPolyDataInfoRGBA, vertNum, vertNum, pointNum)) { biffMovef(PULL, LIMN, "%s: couldn't allocate output", me); return 1; } primIdx = 0; vertIdx = 0; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; phistNum = point->phistArr->len; for (phistIdx=0; phistIdxxyzw + 4*vertIdx, point->phist + 5*phistIdx); (pld->xyzw + 4*vertIdx)[3] = 1; cond = AIR_CAST(int, (point->phist + 5*phistIdx)[4]); switch (cond) { case pullCondOld: ELL_3V_SET(rgb, 128, 128, 128); break; case pullCondConstraintSatA: ELL_3V_SET(rgb, 0, 255, 0); break; case pullCondConstraintSatB: ELL_3V_SET(rgb, 0, 0, 255); break; case pullCondEnergyTry: ELL_3V_SET(rgb, 255, 255, 255); break; case pullCondEnergyBad: ELL_3V_SET(rgb, 255, 0, 0); break; case pullCondConstraintFail: ELL_3V_SET(rgb, 255, 0, 255); break; case pullCondNew: ELL_3V_SET(rgb, 128, 255, 128); break; } ELL_4V_SET(pld->rgba + 4*vertIdx, rgb[0], rgb[1], rgb[2], 255); pld->indx[vertIdx] = vertIdx; vertIdx++; } pld->type[primIdx] = limnPrimitiveLineStrip; pld->icnt[primIdx] = phistNum; primIdx++; } } return 0; #else AIR_UNUSED(pld); AIR_UNUSED(pctx); biffAddf(PULL, "%s: sorry, not compiled with PULL_PHIST", me); return 1; #endif } teem-1.11.0~svn6057/src/pull/initPull.c0000664000175000017500000001512712165631065017327 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" void _pullInitParmInit(pullInitParm *initParm) { initParm->method = pullInitMethodUnknown; initParm->liveThreshUse = AIR_FALSE; initParm->unequalShapesAllow = AIR_FALSE; initParm->jitter = 1.0; initParm->numInitial = 0; initParm->haltonStartIndex = 0; initParm->samplesAlongScaleNum = 0; initParm->ppvZRange[0] = 1; initParm->ppvZRange[1] = 0; initParm->pointPerVoxel = 0; initParm->npos = NULL; return; } #define CHECK(thing, min, max) \ if (!( AIR_EXISTS(iparm->thing) \ && min <= iparm->thing && iparm->thing <= max )) { \ biffAddf(PULL, "%s: initParm->" #thing " %g not in range [%g,%g]", \ me, iparm->thing, min, max); \ return 1; \ } int _pullInitParmCheck(pullInitParm *iparm) { static const char me[]="_pullInitParmCheck"; if (!AIR_IN_OP(pullInitMethodUnknown, iparm->method, pullInitMethodLast)) { biffAddf(PULL, "%s: init method %d not valid", me, iparm->method); return 1; } CHECK(jitter, 0.0, 2.0); switch (iparm->method) { case pullInitMethodGivenPos: if (nrrdCheck(iparm->npos)) { biffMovef(PULL, NRRD, "%s: got a broken npos", me); return 1; } if (!( 2 == iparm->npos->dim && 4 == iparm->npos->axis[0].size && (nrrdTypeDouble == iparm->npos->type || nrrdTypeFloat == iparm->npos->type) )) { biffAddf(PULL, "%s: npos not a 2-D 4-by-N array of %s or %s" "(got %u-D %u-by-X of %s)", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeDouble), iparm->npos->dim, AIR_CAST(unsigned int, iparm->npos->axis[0].size), airEnumStr(nrrdType, iparm->npos->type)); return 1; } break; case pullInitMethodPointPerVoxel: if (iparm->pointPerVoxel < -3001 || iparm->pointPerVoxel > 10) { biffAddf(PULL, "%s: pointPerVoxel %d unreasonable", me, iparm->pointPerVoxel); return 1; } if (-1 == iparm->pointPerVoxel) { biffAddf(PULL, "%s: pointPerVoxel should be < -1 or >= 1", me); return 1; } if (0 == iparm->jitter && 1 < iparm->pointPerVoxel) { biffAddf(PULL, "%s: must have jitter > 0 if pointPerVoxel (%d) > 1", me, iparm->pointPerVoxel); return 1; } break; case pullInitMethodRandom: case pullInitMethodHalton: if (!( iparm->numInitial >= 1 )) { biffAddf(PULL, "%s: iparm->numInitial (%d) not >= 1\n", me, iparm->numInitial); return 1; } break; /* no check needed on haltonStartIndex */ default: biffAddf(PULL, "%s: init method %d valid but not handled?", me, iparm->method); return 1; } return 0; } int pullInitRandomSet(pullContext *pctx, unsigned int numInitial) { static const char me[]="pullInitRandomSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!numInitial) { biffAddf(PULL, "%s: need non-zero numInitial", me); return 1; } pctx->initParm.method = pullInitMethodRandom; pctx->initParm.numInitial = numInitial; return 0; } int pullInitHaltonSet(pullContext *pctx, unsigned int numInitial, unsigned int startIndex) { static const char me[]="pullInitHaltonSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!numInitial) { biffAddf(PULL, "%s: need non-zero numInitial", me); return 1; } pctx->initParm.method = pullInitMethodHalton; pctx->initParm.numInitial = numInitial; pctx->initParm.haltonStartIndex = startIndex; return 0; } int pullInitPointPerVoxelSet(pullContext *pctx, int pointPerVoxel, unsigned int zSlcMin, unsigned int zSlcMax, unsigned int alongScaleNum, double jitter) { static const char me[]="pullInitPointPerVoxelSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!pointPerVoxel) { biffAddf(PULL, "%s: need non-zero pointPerVoxel", me); return 1; } if (!AIR_EXISTS(jitter)) { biffAddf(PULL, "%s: got non-existent jitter %g", me, jitter); return 1; } pctx->initParm.method = pullInitMethodPointPerVoxel; pctx->initParm.pointPerVoxel = pointPerVoxel; pctx->initParm.samplesAlongScaleNum = alongScaleNum; pctx->initParm.ppvZRange[0] = zSlcMin; pctx->initParm.ppvZRange[1] = zSlcMax; pctx->initParm.jitter = jitter; return 0; } int pullInitGivenPosSet(pullContext *pctx, const Nrrd *npos) { static const char me[]="pullInitGivenPosSet"; if (!(pctx && npos)) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } pctx->initParm.method = pullInitMethodGivenPos; pctx->initParm.npos = npos; return 0; } int pullInitLiveThreshUseSet(pullContext *pctx, int liveThreshUse) { static const char me[]="pullInitLiveThreshUseSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } pctx->initParm.liveThreshUse = liveThreshUse; return 0; } int pullInitUnequalShapesAllowSet(pullContext *pctx, int allow) { static const char me[]="pullInitUnequalShapesAllowSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } pctx->initParm.unequalShapesAllow = allow; return 0; } #undef CHECK FILE * _pullPointAddLog = NULL; teem-1.11.0~svn6057/src/pull/actionPull.c0000664000175000017500000013216412174666706017654 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" /* ** issues: ** does everything work on the first iteration ** how to handle the needed extra probe for d strength / d scale ** how are force/energy along scale handled differently than in space? */ static double _pointDistSqrd(pullContext *pctx, pullPoint *AA, pullPoint *BB) { double diff[4]; ELL_4V_SUB(diff, AA->pos, BB->pos); ELL_3V_SCALE(diff, 1/pctx->sysParm.radiusSpace, diff); diff[3] /= pctx->sysParm.radiusScale; return ELL_4V_DOT(diff, diff); } /* ** this sets, in task->neighPoint (*NOT* point->neighPoint), all the ** points in neighboring bins with which we might possibly interact, ** and returns the number of such points. */ static unsigned int _neighBinPoints(pullTask *task, pullBin *bin, pullPoint *point, double distTest) { static const char me[]="_neighBinPoints"; unsigned int nn, herPointIdx, herBinIdx; pullBin *herBin; pullPoint *herPoint; nn = 0; herBinIdx = 0; while ((herBin = bin->neighBin[herBinIdx])) { for (herPointIdx=0; herPointIdxpointNum; herPointIdx++) { herPoint = herBin->point[herPointIdx]; /* printf("!%s(%u): neighbin %u has point %u\n", me, point->idtag, herBinIdx, herPoint->idtag); */ /* can't interact with myself, or anything nixed */ if (point != herPoint && !(herPoint->status & PULL_STATUS_NIXME_BIT)) { if (distTest && _pointDistSqrd(task->pctx, point, herPoint) > distTest) { continue; } if (nn+1 < _PULL_NEIGH_MAXNUM) { task->neighPoint[nn++] = herPoint; /* printf("%s(%u): neighPoint[%u] = %u\n", me, point->idtag, nn-1, herPoint->idtag); */ } else { fprintf(stderr, "%s: hit max# (%u) poss. neighbors (from bins)\n", me, _PULL_NEIGH_MAXNUM); } } } herBinIdx++; } /* also have to consider things in the add queue */ for (herPointIdx=0; herPointIdxaddPointNum; herPointIdx++) { herPoint = task->addPoint[herPointIdx]; if (point != herPoint) { if (distTest && _pointDistSqrd(task->pctx, point, herPoint) > distTest) { continue; } if (nn+1 < _PULL_NEIGH_MAXNUM) { task->neighPoint[nn++] = herPoint; } else { fprintf(stderr, "%s: hit max# (%u) poss neighs (add queue len %u)\n", me, _PULL_NEIGH_MAXNUM, task->addPointNum); } } } return nn; } /* ** compute the energy at "me" due to "she", and ** the gradient vector of her energy (probably pointing towards her) ** ** we're passed spaceDist to save us work of recomputing sqrt() ** ** egrad will be NULL if this is being called only to assess ** the energy at this point, rather than for learning how to move it */ double _pullEnergyInterParticle(pullContext *pctx, pullPoint *me, const pullPoint *she, double spaceDist, double scaleDist, /* output */ double egrad[4]) { char meme[]="pullEnergyInterParticle"; double diff[4], spaceRad, scaleRad, rr, ss, uu, beta, en, den, enR, denR, enS, denS, enWR, enWS, denWR, denWS, *parmR, *parmS, *parmW, (*evalR)(double *, double, const double parm[PULL_ENERGY_PARM_NUM]), (*evalS)(double *, double, const double parm[PULL_ENERGY_PARM_NUM]), (*evalW)(double *, double, const double parm[PULL_ENERGY_PARM_NUM]); int scaleSgn; /* the vector "diff" goes from her, to me, in both space and scale */ ELL_4V_SUB(diff, me->pos, she->pos); /* computed by caller: spaceDist = ELL_3V_LEN(diff); */ /* computed by caller: scaleDist = AIR_ABS(diff[3]); */ spaceRad = pctx->sysParm.radiusSpace; scaleRad = pctx->sysParm.radiusScale; rr = spaceDist/spaceRad; if (pctx->haveScale) { ss = scaleDist/scaleRad; scaleSgn = airSgn(diff[3]); } else { ss = 0; scaleSgn = 1; } if (rr > 1 || ss > 1) { if (egrad) { ELL_4V_SET(egrad, 0, 0, 0, 0); } return 0; } if (rr == 0 && ss == 0) { fprintf(stderr, "%s: pos(%u) == pos(%u) !! (%g,%g,%g,%g)\n", meme, me->idtag, she->idtag, me->pos[0], me->pos[1], me->pos[2], me->pos[3]); if (egrad) { ELL_4V_SET(egrad, 0, 0, 0, 0); } return 0; } #if PULL_HINTER if (pullProcessModeDescent == pctx->task[0]->processMode && pctx->nhinter && pctx->nhinter->data) { unsigned int ri, si, sz; float *hint; hint = AIR_CAST(float *, pctx->nhinter->data); sz = pctx->nhinter->axis[0].size; ri = airIndex(-1.0, rr, 1.0, sz); si = airIndex(-1.0, ss*scaleSgn, 1.0, sz); hint[ri + sz*si] += 1; } #endif parmR = pctx->energySpecR->parm; evalR = pctx->energySpecR->energy->eval; parmS = pctx->energySpecS->parm; evalS = pctx->energySpecS->energy->eval; switch (pctx->interType) { case pullInterTypeJustR: /* _pullVolumeSetup makes sure that !pctx->haveScale iff pullInterTypeJustR == pctx->interType */ en = evalR(&denR, rr, parmR); if (egrad) { denR *= 1.0/(spaceRad*spaceDist); ELL_3V_SCALE(egrad, denR, diff); egrad[3] = 0; } break; case pullInterTypeUnivariate: uu = sqrt(rr*rr + ss*ss); en = evalR(&den, uu, parmR); if (egrad) { ELL_3V_SCALE(egrad, den/(uu*spaceRad*spaceRad), diff); egrad[3] = den*diff[3]/(uu*scaleRad*scaleRad); } break; case pullInterTypeSeparable: enR = evalR(&denR, rr, parmR); enS = evalS(&denS, ss, parmS); en = enR*enS; if (egrad) { ELL_3V_SCALE(egrad, denR*enS/(spaceRad*spaceDist), diff); egrad[3] = enR*airSgn(diff[3])*denS/scaleRad; } break; case pullInterTypeAdditive: parmW = pctx->energySpecWin->parm; evalW = pctx->energySpecWin->energy->eval; enR = evalR(&denR, rr, parmR); enS = evalS(&denS, ss, parmS); enWR = evalW(&denWR, rr, parmW); enWS = evalW(&denWS, ss, parmW); beta = pctx->sysParm.beta; en = AIR_LERP(beta, enR*enWS, enS*enWR); if (egrad) { double egradR[4], egradS[4]; ELL_3V_SCALE(egradR, denR*enWS/(spaceRad*spaceDist), diff); ELL_3V_SCALE(egradS, denWR*enS/(spaceRad*spaceDist), diff); egradR[3] = enR*scaleSgn*denWS/scaleRad; egradS[3] = enWR*scaleSgn*denS/scaleRad; ELL_4V_LERP(egrad, beta, egradR, egradS); } break; default: fprintf(stderr, "!%s: sorry, intertype %d unimplemented", meme, pctx->interType); en = AIR_NAN; if (egrad) { ELL_4V_SET(egrad, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); } break; } /* printf("%s: %u <-- %u = %g,%g,%g -> egrad = %g,%g,%g, enr = %g\n", meme, me->idtag, she->idtag, diff[0], diff[1], diff[2], egrad[0], egrad[1], egrad[2], enr); */ return en; } int pullEnergyPlot(pullContext *pctx, Nrrd *nplot, double xx, double yy, double zz, unsigned int res) { static const char meme[]="pullEnergyPlot"; pullPoint *me, *she; airArray *mop; double dir[3], len, *plot, _rr, _ss, rr, ss, enr, egrad[4]; size_t size[3]; unsigned int ri, si; if (!( pctx && nplot )) { biffAddf(PULL, "%s: got NULL pointer", meme); return 1; } ELL_3V_SET(dir, xx, yy, zz); if (!ELL_3V_LEN(dir)) { biffAddf(PULL, "%s: need non-zero length dir", meme); return 1; } ELL_3V_NORM(dir, dir, len); ELL_3V_SET(size, 3, res, res); if (nrrdMaybeAlloc_nva(nplot, nrrdTypeDouble, 3, size)) { biffMovef(PULL, NRRD, "%s: trouble allocating output", meme); return 1; } mop = airMopNew(); me = pullPointNew(pctx); she = pullPointNew(pctx); airMopAdd(mop, me, (airMopper)pullPointNix, airMopAlways); airMopAdd(mop, she, (airMopper)pullPointNix, airMopAlways); ELL_4V_SET(me->pos, 0, 0, 0, 0); plot = AIR_CAST(double *, nplot->data); for (si=0; sisysParm.radiusScale; for (ri=0; risysParm.radiusSpace; ELL_3V_SCALE(she->pos, rr, dir); she->pos[3] = ss; enr = _pullEnergyInterParticle(pctx, me, she, AIR_ABS(rr), AIR_ABS(ss), egrad); plot[0] = enr; plot[1] = ELL_3V_DOT(egrad, dir); plot[2] = egrad[3]; plot += 3; } } airMopOkay(mop); return 0; } /* ** computes energy from neighboring points. The non-NULLity of ** "egradSum" determines the energy *gradient* is computed (with ** possible constraint modifications) and stored there ** ** always computed: ** point->neighInterNum ** point->neighDistMean ** ** if pullProcessModeNeighLearn == task->processMode: ** point->neighCovar ** point->neighTanCovar ** point->neighNum ** ** 0=0 1=1 2=2 3=3 ** (4) 4=5 5=6 6=7 ** (8) (9) 7=10 8=11 ** (12) (13) (14) 9=15 */ double _pullEnergyFromPoints(pullTask *task, pullBin *bin, pullPoint *point, /* output */ double egradSum[4]) { static const char me[]="_pullEnergyFromPoints"; double energySum, spaDistSqMax; /* modeWghtSum */ int nlist, /* we enable the re-use of neighbor lists between inters, or, at system start, creation of neighbor lists */ ntrue; /* we search all possible neighbors availble in the bins (either because !nlist, or, this iter we learn true subset of interacting neighbors). This could also be called "dontreuse" or something like that */ unsigned int nidx, nnum; /* how much of task->neigh[] we use */ /* set nlist and ntrue */ if (pullProcessModeNeighLearn == task->processMode) { /* we're here to both learn and store the true interacting neighbors */ nlist = AIR_TRUE; ntrue = AIR_TRUE; } else if (pullProcessModeAdding == task->processMode || pullProcessModeNixing == task->processMode) { /* we assume that the work of learning neighbors has already been done, so we can reuse them now */ nlist = AIR_TRUE; ntrue = AIR_FALSE; } else if (pullProcessModeDescent == task->processMode) { if (task->pctx->sysParm.neighborTrueProb < 1) { nlist = AIR_TRUE; if (egradSum) { /* We allow the neighbor list optimization only when we're also asked to compute the energy gradient, since that's the first part of moving the particle. */ ntrue = (0 == task->pctx->iter || (airDrandMT_r(task->rng) < task->pctx->sysParm.neighborTrueProb)); } else { /* When we're not getting the energy gradient, we're being called to test the waters at possible new locations, in which case we can't be changing the effective neighborhood */ ntrue = AIR_TRUE; } } else { /* never trying neighborhood caching */ nlist = AIR_FALSE; ntrue = AIR_TRUE; } } else { fprintf(stderr, "%s: process mode %d unrecognized!\n", me, task->processMode); return AIR_NAN; } /* NOTE: can't have both nlist and ntrue false. possibilities are: ** ** nlist: ** true false ** true X X ** ntrue: ** false X */ /* printf("!%s(%u), nlist = %d, ntrue = %d\n", me, point->idtag, nlist, ntrue); */ /* set nnum and task->neigh[] */ if (ntrue) { /* this finds the over-inclusive set of all possible interacting points, based on bin membership as well the task's add queue */ nnum = _neighBinPoints(task, bin, point, 1.0); if (nlist) { airArrayLenSet(point->neighPointArr, 0); } } else { /* (nlist true) this iter we re-use this point's existing neighbor list, copying it into the the task's neighbor list to simulate the action of _neighBinPoints() */ nnum = point->neighPointNum; for (nidx=0; nidxneighPoint[nidx] = point->neighPoint[nidx]; } } /* loop through neighbor points */ spaDistSqMax = (task->pctx->sysParm.radiusSpace * task->pctx->sysParm.radiusSpace); /* printf("%s: radiusSpace = %g -> spaDistSqMax = %g\n", me, task->pctx->sysParm.radiusSpace, spaDistSqMax); */ /* modeWghtSum = 0; */ energySum = 0; point->neighInterNum = 0; point->neighDistMean = 0.0; if (pullProcessModeNeighLearn == task->processMode) { ELL_10V_ZERO_SET(point->neighCovar); point->stability = 0.0; #if PULL_TANCOVAR if (task->pctx->ispec[pullInfoTangent1]) { double *tng; float outer[9]; tng = point->info + task->pctx->infoIdx[pullInfoTangent1]; ELL_3MV_OUTER_TT(outer, float, tng, tng); point->neighTanCovar[0] = outer[0]; point->neighTanCovar[1] = outer[1]; point->neighTanCovar[2] = outer[2]; point->neighTanCovar[3] = outer[4]; point->neighTanCovar[4] = outer[5]; point->neighTanCovar[5] = outer[8]; } #endif } if (egradSum) { ELL_4V_SET(egradSum, 0, 0, 0, 0); } for (nidx=0; nidxneighPoint[nidx]; if (herPoint->status & PULL_STATUS_NIXME_BIT) { /* this point is not long for this world, pass over it */ continue; } ELL_4V_SUB(diff, point->pos, herPoint->pos); /* me - her */ spaDistSq = ELL_3V_DOT(diff, diff); /* printf("!%s: %u:%g,%g,%g <-- %u:%g,%g,%g = sqd %g %s %g\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], herPoint->idtag, herPoint->pos[0], herPoint->pos[1], herPoint->pos[2], spaDistSq, spaDistSq > spaDistSqMax ? ">" : "<=", spaDistSqMax); */ if (spaDistSq > spaDistSqMax) { continue; } sclDist = AIR_ABS(diff[3]); if (sclDist > task->pctx->sysParm.radiusScale) { continue; } spaDist = sqrt(spaDistSq); /* we pass spaDist to avoid recomputing sqrt(), and sclDist for stupid consistency */ enr = _pullEnergyInterParticle(task->pctx, point, herPoint, spaDist, sclDist, egradSum ? egrad : NULL); #if 0 /* sanity checking on energy derivatives */ if (enr && egradSum) { double _pos[4], tdf[4], ee[2], eps=0.000001, apegrad[4], quot[4]; unsigned int cord, pan; ELL_4V_COPY(_pos, point->pos); for (cord=0; cord<=3; cord++) { for (pan=0; pan<=1; pan++) { point->pos[cord] = _pos[cord] + (!pan ? -1 : +1)*eps; ELL_4V_SUB(tdf, point->pos, herPoint->pos); ee[pan] = _pullEnergyInterParticle(task->pctx, point, herPoint, ELL_3V_LEN(tdf), AIR_ABS(tdf[3]), NULL); } point->pos[cord] = _pos[cord]; apegrad[cord] = (ee[1] - ee[0])/(2*eps); quot[cord] = apegrad[cord]/egrad[cord]; } if ( AIR_ABS(1.0 - quot[0]) > 0.01 || AIR_ABS(1.0 - quot[1]) > 0.01 || AIR_ABS(1.0 - quot[2]) > 0.01 || (task->pctx->haveScale && AIR_ABS(1.0 - quot[3]) > 0.01) ) { printf("!%s(%u<-%u): ---------- claim egrad (%g,%g,%g,%g)\n", me, point->idtag, herPoint->idtag, egrad[0], egrad[1], egrad[2], egrad[3]); printf("!%s(%u<-%u): measr egrad (%g,%g,%g,%g)\n", me, point->idtag, herPoint->idtag, apegrad[0], apegrad[1], apegrad[2], apegrad[3]); printf("!%s(%u<-%u): quot (%g,%g,%g,%g)\n", me, point->idtag, herPoint->idtag, quot[0], quot[1], quot[2], quot[3]); } ELL_4V_COPY(point->pos, _pos); } #endif if (enr) { /* there is some non-zero energy due to her; and we assume that its not just a fluke zero-crossing of the potential profile */ double ndist; point->neighInterNum++; if (nlist && ntrue) { unsigned int ii; /* we have to record that we had an interaction with this point */ ii = airArrayLenIncr(point->neighPointArr, 1); point->neighPoint[ii] = herPoint; } energySum += enr; ELL_3V_SCALE(diff, 1.0/task->pctx->sysParm.radiusSpace, diff); if (task->pctx->haveScale) { diff[3] /= task->pctx->sysParm.radiusScale; } ndist = ELL_4V_LEN(diff); point->neighDistMean += ndist; if (pullProcessModeNeighLearn == task->processMode) { float outer[16]; ELL_4MV_OUTER_TT(outer, float, diff, diff); point->neighCovar[0] += outer[0]; point->neighCovar[1] += outer[1]; point->neighCovar[2] += outer[2]; point->neighCovar[3] += outer[3]; point->neighCovar[4] += outer[5]; point->neighCovar[5] += outer[6]; point->neighCovar[6] += outer[7]; point->neighCovar[7] += outer[10]; point->neighCovar[8] += outer[11]; point->neighCovar[9] += outer[15]; #if PULL_TANCOVAR if (task->pctx->ispec[pullInfoTangent1]) { double *tng; tng = herPoint->info + task->pctx->infoIdx[pullInfoTangent1]; ELL_3MV_OUTER_TT(outer, float, tng, tng); point->neighTanCovar[0] += outer[0]; point->neighTanCovar[1] += outer[1]; point->neighTanCovar[2] += outer[2]; point->neighTanCovar[3] += outer[4]; point->neighTanCovar[4] += outer[5]; point->neighTanCovar[5] += outer[8]; } #endif } if (egradSum) { ELL_4V_INCR(egradSum, egrad); } } } #define CNORM(M) \ sqrt(M[0]*M[0] + 2*M[1]*M[1] + 2*M[2]*M[2] + 2*M[3]*M[3] \ + M[4]*M[4] + 2*M[5]*M[5] + 2*M[6]*M[6] \ + M[7]*M[7] + 2*M[8]*M[8] \ + M[9]*M[9]) #define CTRACE(M) (M[0] + M[4] + M[7] + M[9]) /* finish computing things averaged over neighbors */ if (point->neighInterNum) { point->neighDistMean /= point->neighInterNum; if (pullProcessModeNeighLearn == task->processMode) { double Css, trc; ELL_10V_SCALE(point->neighCovar, 1.0f/point->neighInterNum, point->neighCovar); Css = point->neighCovar[9]; trc = CTRACE(point->neighCovar); point->stability = AIR_CAST(float, (trc ? (task->pctx->targetDim * Css)/trc : 0.0)); #if PULL_TANCOVAR /* using 1 + # neigh because this includes tan1 of point itself */ ELL_6V_SCALE(point->neighTanCovar, 1.0f/(1 + point->neighInterNum), point->neighTanCovar); #endif } } else { /* we had no neighbors at all */ point->neighDistMean = 0.0; /* shouldn't happen in any normal case */ /* point->neighCovar,neighTanCovar stay as initialized above */ } return energySum; } static double _energyFromImage(pullTask *task, pullPoint *point, /* output */ double egradSum[4]) { static const char me[]="_energyFromImage"; double energy, grad3[3], gamma; int probed; /* not sure I have the logic for this right int ptrue; if (task->pctx->probeProb < 1 && allowProbeProb) { if (egrad) { ptrue = (0 == task->pctx->iter || airDrandMT_r(task->rng) < task->pctx->probeProb); } else { ptrue = AIR_FALSE; } } else { ptrue = AIR_TRUE; } */ probed = AIR_FALSE; /* a better name for this would be "probe only if you haven't already probed" */ #define MAYBEPROBE \ if (!probed) { \ if (pullProbe(task, point)) { \ fprintf(stderr, "%s: problem probing!!!\n%s\n", me, biffGetDone(PULL)); \ } \ probed = AIR_TRUE; \ } gamma = task->pctx->sysParm.gamma; energy = 0; if (egradSum) { ELL_4V_SET(egradSum, 0, 0, 0, 0); } if (task->pctx->flag.energyFromStrength && task->pctx->ispec[pullInfoStrength]) { double deltaScale, str0, str1, scl0, scl1, enr; int sign; if (!egradSum) { /* just need the strength */ MAYBEPROBE; enr = pullPointScalar(task->pctx, point, pullInfoStrength, NULL, NULL); energy += -gamma*enr; } else { /* need strength and its gradient */ /* randomize choice between forward and backward difference */ /* NOTE: since you only need one bit of random, you could re-used a random int and look through its bits to determine forw vs back differences, but this is probably not the bottleneck */ sign = 2*AIR_CAST(int, airRandInt_r(task->rng, 2)) - 1; deltaScale = task->pctx->bboxMax[3] - task->pctx->bboxMin[3]; deltaScale *= sign*_PULL_STRENGTH_ENERGY_DELTA_SCALE; scl1 = (point->pos[3] += deltaScale); pullProbe(task, point); /* *not* MAYBEPROBE */ str1 = pullPointScalar(task->pctx, point, pullInfoStrength, NULL, NULL); scl0 = (point->pos[3] -= deltaScale); MAYBEPROBE; str0 = pullPointScalar(task->pctx, point, pullInfoStrength, NULL, NULL); energy += -gamma*str0; egradSum[3] += -gamma*(str1 - str0)/(scl1 - scl0); /* if (1560 < task->pctx->iter && 2350 == point->idtag) { printf("%s(%u): egrad[3] = %g*((%g-%g)/(%g-%g) = %g/%g = %g)" " = %g -> %g\n", me, point->idtag, task->pctx->sysParm.gamma, str1, str0, scl1, scl0, str1 - str0, scl1 - scl0, (str1 - str0)/(scl1 - scl0), task->pctx->sysParm.gamma*(str1 - str0)/(scl1 - scl0), egradSum[3]); } */ } } /* Note that height doesn't contribute to the energy if there is a constraint associated with it */ if (task->pctx->ispec[pullInfoHeight] && !task->pctx->ispec[pullInfoHeight]->constraint && !(task->pctx->ispec[pullInfoHeightLaplacian] && task->pctx->ispec[pullInfoHeightLaplacian]->constraint)) { MAYBEPROBE; energy += pullPointScalar(task->pctx, point, pullInfoHeight, grad3, NULL); if (egradSum) { ELL_3V_INCR(egradSum, grad3); } } if (task->pctx->ispec[pullInfoIsovalue] && !task->pctx->ispec[pullInfoIsovalue]->constraint) { /* we're only going towards an isosurface, not constrained to it ==> set up a parabolic potential well around the isosurface */ double val; MAYBEPROBE; val = pullPointScalar(task->pctx, point, pullInfoIsovalue, grad3, NULL); energy += val*val; if (egradSum) { ELL_3V_SCALE_INCR(egradSum, 2*val, grad3); } } return energy; } #undef MAYBEPROBE /* ** NOTE that the "egrad" being non-NULL has consequences for what gets ** computed in _energyFromImage and _pullEnergyFromPoints: ** ** NULL "egrad": we're simply learning the energy (and want to know it ** as truthfully as possible) for the sake of inspecting system state ** ** non-NULL "egrad": we're learning the current energy, but the real point ** is to determine how to move the point to lower energy ** ** the ignoreImage flag is a hack, to allow _pullPointProcessAdding to ** do descent on a new point according to other points, but not the ** image. */ double _pullPointEnergyTotal(pullTask *task, pullBin *bin, pullPoint *point, int ignoreImage, /* output */ double egrad[4]) { static const char me[]="_pullPointEnergyTotal"; double enrIm, enrPt, egradIm[4], egradPt[4], energy; ELL_4V_SET(egradIm, 0, 0, 0, 0); ELL_4V_SET(egradPt, 0, 0, 0, 0); if (!( ignoreImage || 1.0 == task->pctx->sysParm.alpha )) { enrIm = _energyFromImage(task, point, egrad ? egradIm : NULL); task->pctx->count[pullCountEnergyFromImage] += 1; if (egrad) { task->pctx->count[pullCountForceFromImage] += 1; } } else { enrIm = 0; } if (task->pctx->sysParm.alpha > 0.0) { enrPt = _pullEnergyFromPoints(task, bin, point, egrad ? egradPt : NULL); task->pctx->count[pullCountEnergyFromPoints] += 1; if (egrad) { task->pctx->count[pullCountForceFromPoints] += 1; } } else { enrPt = 0; } energy = AIR_LERP(task->pctx->sysParm.alpha, enrIm, enrPt); /* printf("!%s(%u): energy = lerp(%g, im %g, pt %g) = %g\n", me, point->idtag, task->pctx->sysParm.alpha, enrIm, enrPt, energy); */ if (egrad) { ELL_4V_LERP(egrad, task->pctx->sysParm.alpha, egradIm, egradPt); /* printf("!%s(%u): egradIm = %g %g %g %g\n", me, point->idtag, egradIm[0], egradIm[1], egradIm[2], egradIm[3]); printf("!%s(%u): egradPt = %g %g %g %g\n", me, point->idtag, egradPt[0], egradPt[1], egradPt[2], egradPt[3]); printf("!%s(%u): ---> force = %g %g %g %g\n", me, point->idtag, force[0], force[1], force[2], force[3]); */ } if (task->pctx->sysParm.wall) { unsigned int axi; double dwe; /* derivative of wall energy */ for (axi=0; axi<4; axi++) { dwe = point->pos[axi] - task->pctx->bboxMin[axi]; if (dwe > 0) { /* pos not below min */ dwe = point->pos[axi] - task->pctx->bboxMax[axi]; if (dwe < 0) { /* pos not above max */ dwe = 0; } } energy += task->pctx->sysParm.wall*dwe*dwe/2; if (egrad) { egrad[axi] += task->pctx->sysParm.wall*dwe; } } } if (!AIR_EXISTS(energy)) { fprintf(stderr, "!%s(%u): oops- non-exist energy %g\n", me, point->idtag, energy); } return energy; } /* ** distance limit on a particles motion in both r and s, ** in rs-normalized space (sqrt((r/radiusSpace)^2 + (s/radiusScale)^2)) ** ** This means that if particles are jammed together in space, ** they aren't allowed to move very far in scale, either, which ** is a little weird, but probably okay. */ double _pullDistLimit(pullTask *task, pullPoint *point) { double ret; if (point->neighDistMean == 0 /* no known neighbors from last iter */ || pullEnergyZero == task->pctx->energySpecR->energy) { ret = 1; } else { ret = _PULL_DIST_CAP_RSNORM*point->neighDistMean; } /* HEY: maybe task->pctx->voxelSizeSpace or voxelSizeScale should be considered here? */ return ret; } /* ** here is where the energy gradient is converted into force */ int _pullPointProcessDescent(pullTask *task, pullBin *bin, pullPoint *point, int ignoreImage) { static const char me[]="_pullPointProcessDescent"; double energyOld, energyNew, egrad[4], force[4], posOld[4]; int stepBad, giveUp, hailMary; task->pctx->count[pullCountDescent] += 1; if (!point->stepEnergy) { fprintf(stderr, "\n\n\n%s: whoa, point %u step is zero!!\n\n\n\n", me, point->idtag); /* HEY: need to track down how this can originate! */ /* biffAddf(PULL, "%s: point %u step is zero!", me, point->idtag); return 1; */ point->stepEnergy = task->pctx->sysParm.stepInitial/100; } /* learn the energy at old location, and the energy gradient */ energyOld = _pullPointEnergyTotal(task, bin, point, ignoreImage, egrad); ELL_4V_SCALE(force, -1, egrad); if (!( AIR_EXISTS(energyOld) && ELL_4V_EXISTS(force) )) { biffAddf(PULL, "%s: point %u non-exist energy or force", me, point->idtag); return 1; } /* if (1560 < task->pctx->iter && 2350 == point->idtag) { printf("!%s(%u): old pos = %g %g %g %g\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); printf("!%s(%u): energyOld = %g; force = %g %g %g %g\n", me, point->idtag, energyOld, force[0], force[1], force[2], force[3]); } */ if (!ELL_4V_DOT(force, force)) { /* this particle has no reason to go anywhere; BUT we still have to enforce constraint if we have one */ int constrFail = 0; if (task->pctx->constraint) { if (_pullConstraintSatisfy(task, point, 100.0*_PULL_CONSTRAINT_TRAVEL_MAX, &constrFail)) { biffAddf(PULL, "%s: trouble", me); return 1; } } if (constrFail) { /* biffAddf(PULL, "%s: couldn't satisfy constraint on unforced %u: %s", me, point->idtag, airEnumStr(pullConstraintFail, constrFail)); return 1; */ fprintf(stderr, "%s: *** constr sat fail on unfrced %u: " "%s (si# %u;%u)\n", me, point->idtag, airEnumStr(pullConstraintFail, constrFail), point->stuckIterNum, task->pctx->iterParm.stuckMax); point->status |= PULL_STATUS_STUCK_BIT; point->stuckIterNum += 1; if (task->pctx->iterParm.stuckMax && point->stuckIterNum > task->pctx->iterParm.stuckMax) { point->status |= PULL_STATUS_NIXME_BIT; } } point->energy = energyOld; return 0; } if (task->pctx->constraint && (task->pctx->ispec[pullInfoTangent1] || task->pctx->ispec[pullInfoNegativeTangent1])) { /* we have a constraint, so do something to get the force more tangential to the constraint manifold (only in the spatial axes) */ double proj[9], pfrc[3]; _pullConstraintTangent(task, point, proj); ELL_3MV_MUL(pfrc, proj, force); ELL_3V_COPY(force, pfrc); /* force[3] untouched */ } /* if (1560 < task->pctx->iter && 2350 == point->idtag) { printf("!%s(%u): post-constraint tan: force = %g %g %g %g\n", me, point->idtag, force[0], force[1], force[2], force[3]); printf(" precap stepEnergy = %g\n", point->stepEnergy); } */ /* Cap particle motion. The point is only allowed to move at most unit distance in rs-normalized space, which may mean that motion in r or s is effectively cramped by crowding in the other axis, oh well. Also, particle motion is limited in terms of spatial voxel size, and (if haveScale) the average distance between scale samples */ if (1) { double capvec[4], spcLen, sclLen, max, distLimit; /* limits based on distLimit in rs-normalized space */ distLimit = _pullDistLimit(task, point); ELL_4V_SCALE(capvec, point->stepEnergy, force); spcLen = ELL_3V_LEN(capvec)/task->pctx->sysParm.radiusSpace; sclLen = AIR_ABS(capvec[3])/task->pctx->sysParm.radiusScale; max = AIR_MAX(spcLen, sclLen); if (max > distLimit) { point->stepEnergy *= distLimit/max; } /* limits based on voxelSizeSpace and voxelSizeScale */ ELL_4V_SCALE(capvec, point->stepEnergy, force); spcLen = ELL_3V_LEN(capvec)/task->pctx->voxelSizeSpace; if (task->pctx->haveScale) { sclLen = AIR_ABS(capvec[3])/task->pctx->voxelSizeScale; max = AIR_MAX(spcLen, sclLen); } else { max = spcLen; } if (max > _PULL_DIST_CAP_VOXEL) { point->stepEnergy *= _PULL_DIST_CAP_VOXEL/max; } } /* if (1560 < task->pctx->iter && 2350 == point->idtag) { printf(" postcap stepEnergy = %g\n", point->stepEnergy); } */ /* turn off stuck bit, will turn it on again if needed */ point->status &= ~PULL_STATUS_STUCK_BIT; ELL_4V_COPY(posOld, point->pos); _pullPointHistInit(point); _pullPointHistAdd(point, pullCondOld); /* try steps along force until we succcessfully lower energy */ hailMary = AIR_FALSE; do { int constrFail, energyIncr; giveUp = AIR_FALSE; ELL_4V_SCALE_ADD2(point->pos, 1.0, posOld, point->stepEnergy, force); /* if (1560 < task->pctx->iter && 2350 == point->idtag) { printf("!%s(%u): (iter %u) try pos = %g %g %g %g%s\n", me, point->idtag, task->pctx->iter, point->pos[0], point->pos[1], point->pos[2], point->pos[3], hailMary ? " (Hail Mary)" : ""); } */ if (task->pctx->haveScale) { point->pos[3] = AIR_CLAMP(task->pctx->bboxMin[3], point->pos[3], task->pctx->bboxMax[3]); } task->pctx->count[pullCountTestStep] += 1; _pullPointHistAdd(point, pullCondEnergyTry); if (task->pctx->constraint) { if (_pullConstraintSatisfy(task, point, _PULL_CONSTRAINT_TRAVEL_MAX, &constrFail)) { biffAddf(PULL, "%s: trouble", me); return 1; } } else { constrFail = AIR_FALSE; } /* if (1560 < task->pctx->iter && 2350 == point->idtag) { printf("!%s(%u): post constr = %g %g %g %g (%d)\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3], constrFail); } */ if (constrFail) { energyNew = AIR_NAN; } else { energyNew = _pullPointEnergyTotal(task, bin, point, ignoreImage, NULL); } energyIncr = energyNew > (energyOld + task->pctx->sysParm.energyIncreasePermit); /* if (1560 < task->pctx->iter && 2350 == point->idtag) { printf("!%s(%u): constrFail %d; enr Old New = %g %g -> enrIncr %d\n", me, point->idtag, constrFail, energyOld, energyNew, energyIncr); } */ stepBad = (constrFail || energyIncr); if (stepBad) { point->stepEnergy *= task->pctx->sysParm.backStepScale; if (constrFail) { _pullPointHistAdd(point, pullCondConstraintFail); } else { _pullPointHistAdd(point, pullCondEnergyBad); } /* you have a problem if you had a non-trivial force, but you can't ever seem to take a small enough step to reduce energy */ if (point->stepEnergy < 0.00000001) { /* this can happen if the force is due to a derivative of feature strength with respect to scale, which is measured WITHOUT enforcing the constraint, while particle updates are done WITH the constraint, in which case the computed force can be completely misleading. Thus, as a last-ditch effort, we try moving in the opposite direction (against the force) to see if that helps */ if (task->pctx->verbose > 1) { printf("%s: %u %s (%u); (%g,%g,%g,%g) stepEnr %g\n", me, point->idtag, hailMary ? "STUCK!" : "stuck?", point->stuckIterNum, point->pos[0], point->pos[1], point->pos[2], point->pos[3], point->stepEnergy); } if (!hailMary) { ELL_4V_SCALE(force, -1, force); /* if (4819 == point->idtag || 4828 == point->idtag) { printf("!%s(%u): force now %g %g %g %g\n", me, point->idtag, force[0], force[1], force[2], force[3]); } */ hailMary = AIR_TRUE; } else { /* The hail Mary pass missed too; something is really odd. This can happen when the previous iteration did a sloppy job enforcing the constraint, so before we move on, we enforce it, twice for good measure, so that things may be better next time around */ if (task->pctx->constraint) { if (_pullConstraintSatisfy(task, point, _PULL_CONSTRAINT_TRAVEL_MAX, &constrFail) || _pullConstraintSatisfy(task, point, _PULL_CONSTRAINT_TRAVEL_MAX, &constrFail)) { biffAddf(PULL, "%s: trouble", me); return 1; } } energyNew = _pullPointEnergyTotal(task, bin, point, ignoreImage, NULL); point->stepEnergy = task->pctx->sysParm.stepInitial; point->status |= PULL_STATUS_STUCK_BIT; point->stuckIterNum += 1; giveUp = AIR_TRUE; } } } } while (stepBad && !giveUp); /* Hail Mary worked if (hailMary && !stepBad). It does sometimes work. */ /* now: unless we gave up, energy decreased, and, if we have one, constraint has been met */ /* if (1560 < task->pctx->iter && 2350 == point->idtag) { printf("!%s(%u):iter %u changed (%g,%g,%g,%g)->(%g,%g,%g,%g)\n", me, point->idtag, task->pctx->iter, posOld[0], posOld[1], posOld[2], posOld[3], point->pos[0], point->pos[1], point->pos[2], point->pos[3]); } */ _pullPointHistAdd(point, pullCondNew); ELL_4V_COPY(point->force, force); /* not recorded for the sake of this function, but for system accounting */ point->energy = energyNew; if (!AIR_EXISTS(energyNew)) { biffAddf(PULL, "%s: point %u has non-exist final energy %g\n", me, point->idtag, energyNew); return 1; } /* if its not stuck, reset stuckIterNum */ if (!(point->status & PULL_STATUS_STUCK_BIT)) { point->stuckIterNum = 0; } else if (task->pctx->iterParm.stuckMax && point->stuckIterNum > task->pctx->iterParm.stuckMax) { /* else if it is stuck then its up to us to set NIXME based on point->stuckIterNum */ point->status |= PULL_STATUS_NIXME_BIT; } return 0; } int _pullPointProcess(pullTask *task, pullBin *bin, pullPoint *point) { static const char me[]="_pullPointProcess"; int E; E = 0; switch (task->processMode) { case pullProcessModeDescent: E = _pullPointProcessDescent(task, bin, point, !task->pctx->haveScale /* ignoreImage */); break; case pullProcessModeNeighLearn: E = _pullPointProcessNeighLearn(task, bin, point); break; case pullProcessModeAdding: if (!task->pctx->flag.noAdd) { E = _pullPointProcessAdding(task, bin, point); } break; case pullProcessModeNixing: E = _pullPointProcessNixing(task, bin, point); break; default: biffAddf(PULL, "%s: process mode %d unrecognized", me, task->processMode); return 1; break; } if (E) { biffAddf(PULL, "%s: trouble", me); return 1; } if (task->pctx->flag.zeroZ) { point->pos[2] = 0; } return 0; } int pullBinProcess(pullTask *task, unsigned int myBinIdx) { static const char me[]="pullBinProcess"; pullBin *myBin; unsigned int myPointIdx; if (task->pctx->verbose > 2) { printf("%s(%s): doing bin %u\n", me, airEnumStr(pullProcessMode, task->processMode), myBinIdx); } myBin = task->pctx->bin + myBinIdx; for (myPointIdx=0; myPointIdxpointNum; myPointIdx++) { pullPoint *point; if (task->pctx->verbose > 1 && task->pctx->pointNum > _PULL_PROGRESS_POINT_NUM_MIN && !task->pctx->flag.binSingle && task->pctx->progressBinMod && 0 == myBinIdx % task->pctx->progressBinMod) { printf("."); fflush(stdout); } point = myBin->point[myPointIdx]; if (task->pctx->verbose > 2) { printf("%s(%s) processing (bin %u)->point[%u] %u\n", me, airEnumStr(pullProcessMode, task->processMode), myBinIdx, myPointIdx, point->idtag); } if (_pullPointProcess(task, myBin, point)) { biffAddf(PULL, "%s: on point %u of bin %u\n", me, myPointIdx, myBinIdx); return 1; } task->stuckNum += (point->status & PULL_STATUS_STUCK_BIT); } /* for myPointIdx */ return 0; } int pullGammaLearn(pullContext *pctx) { static const char me[]="pullGammaLearn"; unsigned int binIdx, pointIdx, pointNum; pullBin *bin; pullPoint *point; pullTask *task; double deltaScale, scl, beta, wellX=0, wellY=0, *strdd, *gmag, meanGmag, meanStrdd, wght, wghtSum; airArray *mop; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!pctx->haveScale) { biffAddf(PULL, "%s: not using scale-space", me); return 1; } if (pullInterTypeAdditive == pctx->interType) { if (pullEnergyButterworthParabola != pctx->energySpecS->energy) { biffAddf(PULL, "%s: want %s energy along scale, not %s", me, pullEnergyButterworthParabola->name, pctx->energySpecS->energy->name); return 1; } } else if (pullInterTypeSeparable == pctx->interType) { wellY = pctx->energySpecR->energy->well(&wellX, pctx->energySpecR->parm); if (!( wellY < 0 )) { biffAddf(PULL, "%s: spatial energy %s didn't have well", me, pctx->energySpecR->energy->name); return 1; } if (pullEnergyBspln != pctx->energySpecS->energy) { biffAddf(PULL, "%s: want %s energy along scale, not %s", me, pullEnergyBspln->name, pctx->energySpecS->energy->name); return 1; } } else { biffAddf(PULL, "%s: need %s or %s inter type, not %s", me, airEnumStr(pullInterType, pullInterTypeAdditive), airEnumStr(pullInterType, pullInterTypeSeparable), airEnumStr(pullInterType, pctx->interType)); return 1; } pointNum = pullPointNumber(pctx); if (!pointNum) { biffAddf(PULL, "%s: had no points!", me); return 1; } mop = airMopNew(); strdd = AIR_CALLOC(pointNum, double); airMopAdd(mop, strdd, airFree, airMopAlways); gmag = AIR_CALLOC(pointNum, double); airMopAdd(mop, gmag, airFree, airMopAlways); if (!(strdd && gmag)) { biffAddf(PULL, "%s: couldn't alloc two buffers of %u doubles", me, pointNum); airMopError(mop); return 1; } task = pctx->task[0]; pointIdx = 0; deltaScale = pctx->bboxMax[3] - pctx->bboxMin[3]; deltaScale *= _PULL_STRENGTH_ENERGY_DELTA_SCALE; for (binIdx=0; binIdxbinNum; binIdx++) { unsigned int pidx; bin = pctx->bin + binIdx; for (pidx=0; pidxpointNum; pidx++) { double str[3], _ss, _gr; point = bin->point[pidx]; point->pos[3] += deltaScale; pullProbe(task, point); str[2] = pullPointScalar(pctx, point, pullInfoStrength, NULL, NULL); point->pos[3] -= 2*deltaScale; pullProbe(task, point); str[0] = pullPointScalar(pctx, point, pullInfoStrength, NULL, NULL); point->pos[3] += deltaScale; pullProbe(task, point); str[1] = pullPointScalar(pctx, point, pullInfoStrength, NULL, NULL); _ss = (str[0] - 2*str[1] + str[2])/(deltaScale*deltaScale); if (_ss < 0.0) { _gr = (str[2] - str[0])/(2*deltaScale); _gr = AIR_ABS(_gr); strdd[pointIdx] = _ss; gmag[pointIdx] = _gr; pointIdx++; } } } if (!pointIdx) { biffAddf(PULL, "%s: no points w/ 2nd deriv of strn wrt scale < 0", me); airMopError(mop); return 1; } /* resetting pointNum to actual number of points used */ pointNum = pointIdx; /* learn meanGmag, with sqrt() sneakiness to discount high gmags */ meanGmag = 0.0; for (pointIdx=0; pointIdxsysParm.radiusScale; if (pullInterTypeAdditive == pctx->interType) { /* want to satisfy str''(s) = enr''(s) ** ==> -gamma*strdd = 2*beta/(radiusScale)^2 ** ==> gamma = -2*beta/(strdd*(radiusScale)^2) ** (beta = 1) ==> gamma = -2/(strdd*(radiusScale)^2) ** NOTE: The difference from what is in the paper is a factor of 2, ** and the ability to include the influence of beta */ beta = (pctx->flag.useBetaForGammaLearn ? pctx->sysParm.beta : 1.0); pctx->sysParm.gamma = -2*beta/(meanStrdd*scl*scl); } else if (pullInterTypeSeparable == pctx->interType) { /* want to satisfy str''(s) = enr''(s); wellY < 0 ** ==> gamma*strdd = wellY*8/(radiusScale)^2 ** gamma = wellY*8/(strdd*(radiusScale)^2) */ pctx->sysParm.gamma = wellY*8/(meanStrdd*scl*scl); pctx->sysParm.gamma *= pctx->sysParm.separableGammaLearnRescale; } else { biffAddf(PULL, "%s: sorry %s inter type unimplemented", me, airEnumStr(pullInterType, pctx->interType)); airMopError(mop); return 1; } if (pctx->verbose) { printf("%s: learned gamma %g\n", me, pctx->sysParm.gamma); } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/pull/privatePull.h0000664000175000017500000001771412174667044020055 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __cplusplus extern "C" { #endif /* ** These heuristics/parameters have been adjusted, and don't seem to need ** any further tweaking in order to work for all the datasets used so far */ /* size/allocation increment for per-bin airArray */ #define _PULL_BIN_INCR 32 /* size/allocation increment for pullTrace airArray in pullTraceMulti */ #define _PULL_TRACE_MULTI_INCR 1024 /* this has to be big enough to do experiments where binning is turned off */ #define _PULL_NEIGH_MAXNUM 4096 /* used by pullBinsPointMaybeAdd; don't add a point if its (normalized) distance to an existing point is less than this */ #define _PULL_BINNING_MAYBE_ADD_THRESH 0.1 /* only try adding a point if the normalized neighbor offset sum is greater than this (making this too small only wastes time, by descending and testing a point that can't help reduce energy */ #define _PULL_NEIGH_OFFSET_SUM_THRESH 0.2 /* how far to place new points from isolated points (as a fraction of radiusSpace), when not using an energy with an explicit well */ #define _PULL_NEWPNT_DIST 0.6 /* scaling factor between point->neighDistMean and distance cap; higher values allow for more adventurous explorations . . . */ #define _PULL_DIST_CAP_RSNORM 2.0 /* travel distance limit in terms of voxelSizeSpace and voxelSizeScale */ #define _PULL_DIST_CAP_VOXEL 1.5 /* where along s axis to probe energySpecS to see if its attractive or repulsive along scale */ #define _PULL_TARGET_DIM_S_PROBE 0.05 /* tentative new points aren't allowed to move further than this (in rs-normalized space) from the original old point */ #define _PULL_NEWPNT_STRAY_DIST 1.3 /* fraction of bboxMax[3]-bboxMin[3] to use as step along scale for discrete differencing needed to find the gradient (for moving along scale) and 2nd derivative (for learning gamma) of strength */ #define _PULL_STRENGTH_ENERGY_DELTA_SCALE 0.0002 /* maximum distance, in multiples of voxelSizeSpace, that a point is allowed to move between start and end of constraint satisfaction */ #define _PULL_CONSTRAINT_TRAVEL_MAX 2 /* max reasonable value of iterParm->constraintIterMax */ #define _PULL_CONSTRAINT_ITER_MAX 50 /* the main _iterate() function does progress indication if the number of points is larger than this */ #define _PULL_PROGRESS_POINT_NUM_MIN 100 /* limit on # times we allow random or halton (non-ppv) seeding to fail */ #define _PULL_RANDOM_SEED_TRY_MAX 1000000 /* limit on stepEnergy */ #define _PULL_STEP_ENERGY_MAX FLT_MAX /* resolution of histogram of (r,s) coords of interactions ("hinter") */ #define _PULL_HINTER_SIZE 601 /* initPull.c */ extern void _pullInitParmInit(pullInitParm *initParm); extern int _pullInitParmCheck(pullInitParm *iparm); /* parmPull.c */ extern void _pullIterParmInit(pullIterParm *iterParm); extern int _pullIterParmCheck(pullIterParm *iterParm); extern void _pullSysParmInit(pullSysParm *sysParm); extern int _pullSysParmCheck(pullSysParm *sysParm); extern void _pullFlagInit(pullFlag *flag); /* volumePull.c */ extern pullVolume *_pullVolumeCopy(const pullContext *pctx, const pullVolume *pvol); extern int _pullVolumeSetup(pullContext *pctx); extern int _pullInsideBBox(pullContext *pctx, double pos[4]); extern unsigned int _pullVolumeIndex(const pullContext *pctx, const char *volName); /* infoPull.c */ extern unsigned int _pullInfoLen[PULL_INFO_MAX+1]; extern void (*_pullInfoCopy[10])(double *, const double *); extern int _pullInfoSetup(pullContext *pctx); /* contextPull.c */ extern int _pullContextCheck(pullContext *pctx); /* taskPull.c */ extern pullTask *_pullTaskNew(pullContext *pctx, int threadIdx); extern pullTask *_pullTaskNix(pullTask *task); extern int _pullTaskSetup(pullContext *pctx); extern void _pullTaskFinish(pullContext *pctx); /* actionPull.c */ extern double _pullPrayCorner[2][2][3]; extern size_t _pullPrayRes[2]; extern double _pullDistLimit(pullTask *task, pullPoint *point); extern double _pullEnergyFromPoints(pullTask *task, pullBin *bin, pullPoint *point, /* output */ double egradSum[4]); extern double _pullPointEnergyTotal(pullTask *task, pullBin *bin, pullPoint *point, int ignoreImage, double force[4]); extern int _pullPointProcessDescent(pullTask *task, pullBin *bin, pullPoint *point, int ignoreImage); extern double _pullEnergyInterParticle(pullContext *pctx, pullPoint *me, const pullPoint *she, double spaceDist, double scaleDist, double egrad[4]); /* constraints.c */ extern int _pullConstraintSatisfy(pullTask *task, pullPoint *point, double travelMax, int *constrFailP); extern void _pullConstraintTangent(pullTask *task, pullPoint *point, double proj[9]); extern int _pullConstraintDim(const pullContext *pctx); /* pointPull.c */ #if PULL_PHIST extern void _pullPointHistInit(pullPoint *point); extern void _pullPointHistAdd(pullPoint *point, int cond); #else #define _pullPointHistInit(p) /* no-op */ #define _pullPointHistAdd(p, c) /* no-op */ #endif extern double _pullStepInterAverage(const pullContext *pctx); extern double _pullStepConstrAverage(const pullContext *pctx); extern double _pullEnergyTotal(const pullContext *pctx); extern void _pullPointStepEnergyScale(pullContext *pctx, double scale); extern int _pullPointSetup(pullContext *pctx); extern void _pullPointFinish(pullContext *pctx); /* popcntl.c */ extern int _pullPointProcessNeighLearn(pullTask *task, pullBin *bin, pullPoint *point); extern int _pullPointProcessAdding(pullTask *task, pullBin *bin, pullPoint *point); extern int _pullPointProcessNixing(pullTask *task, pullBin *bin, pullPoint *point); extern int _pullIterFinishNeighLearn(pullContext *pctx); extern int _pullIterFinishAdding(pullContext *pctx); extern int _pullIterFinishNixing(pullContext *pctx); extern void _pullNixTheNixed(pullContext *pctx); /* binningPull.c */ extern void _pullBinInit(pullBin *bin); extern void _pullBinDone(pullBin *bin); extern pullBin *_pullBinLocate(pullContext *pctx, double *pos); extern void _pullBinPointRemove(pullContext *pctx, pullBin *bin, int loseIdx); extern int _pullBinSetup(pullContext *pctx); extern int _pullIterFinishDescent(pullContext *pctx); extern void _pullBinFinish(pullContext *pctx); /* corePull.c */ extern int _pullVerbose; extern int _pullProcess(pullTask *task); extern void *_pullWorker(void *_task); extern int _pullIterate(pullContext *pctx, int mode); #ifdef __cplusplus } #endif teem-1.11.0~svn6057/src/pull/energy.c0000664000175000017500000005254212165631065017022 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" const char * _pullInterTypeStr[PULL_INTER_TYPE_MAX+1] = { "(unknown_inter)", "justR", "univariate", "separable", "additive" }; const char * _pullInterTypeStrEqv[] = { "r", "justr", "univariate", "univar", "uni", "separable", "separ", "sep", "additive", "add", "" }; const int _pullInterTypeValEqv[] = { pullInterTypeJustR, pullInterTypeJustR, pullInterTypeUnivariate, pullInterTypeUnivariate, pullInterTypeUnivariate, pullInterTypeSeparable, pullInterTypeSeparable, pullInterTypeSeparable, pullInterTypeAdditive, pullInterTypeAdditive }; const airEnum _pullInterType = { "interaction type", PULL_INTER_TYPE_MAX, _pullInterTypeStr, NULL, NULL, _pullInterTypeStrEqv, _pullInterTypeValEqv, AIR_FALSE }; const airEnum *const pullInterType = &_pullInterType; #define SPRING "spring" #define GAUSS "gauss" #define BSPLN "bspln" #define BUTTER "butter" #define COTAN "cotan" #define CUBIC "cubic" #define QUARTIC "quartic" #define CWELL "cwell" #define BWELL "bwell" #define QWELL "qwell" #define HWELL "hwell" #define ZERO "zero" #define BPARAB "bparab" const char * _pullEnergyTypeStr[PULL_ENERGY_TYPE_MAX+1] = { "(unknown_energy)", SPRING, GAUSS, BSPLN, BUTTER, COTAN, CUBIC, QUARTIC, CWELL, BWELL, QWELL, HWELL, ZERO, BPARAB }; const char * _pullEnergyTypeDesc[PULL_ENERGY_TYPE_MAX+1] = { "unknown_energy", "Hooke's law-based potential, with a tunable region of attraction", "Gaussian potential", "uniform cubic B-spline basis function", "like a Gaussian, but a lot wider", "Cotangent-based potential (from Meyer et al. SMI '05)", "Cubic thing", "Quartic thing", "Piecewice cubic with tunable well location and depth", "Better piecewice cubic with tunable well location and depth", "Single quartic with tunable well location", "Single heptic with tunable well location", "no energy", "butterworth-windowed spatial repel and scale attract" }; const airEnum _pullEnergyType = { "energy", PULL_ENERGY_TYPE_MAX, _pullEnergyTypeStr, NULL, _pullEnergyTypeDesc, NULL, NULL, AIR_FALSE }; const airEnum *const pullEnergyType = &_pullEnergyType; double _pullEnergyNoWell(double *wx, const double *parm) { AIR_UNUSED(wx); AIR_UNUSED(parm); return 0.0; } /* ---------------------------------------------------------------- ** ------------------------------ UNKNOWN ------------------------- ** ---------------------------------------------------------------- */ double _pullEnergyUnknownEval(double *denr, double dist, const double *parm) { static const char me[]="_pullEnergyUnknownEval"; AIR_UNUSED(dist); AIR_UNUSED(parm); *denr = AIR_NAN; fprintf(stderr, "%s: ERROR- using unknown energy.\n", me); return AIR_NAN; } pullEnergy _pullEnergyUnknown = { "unknown", 0, _pullEnergyNoWell, _pullEnergyUnknownEval }; const pullEnergy *const pullEnergyUnknown = &_pullEnergyUnknown; /* ---------------------------------------------------------------- ** ------------------------------ SPRING -------------------------- ** ---------------------------------------------------------------- ** 1 parms: ** parm[0]: width of pull region. Used to be width beyond 1.0, but ** now things are scrunched to fit both repelling and attractive ** region inside [0,1] ** ** learned: "1/2" is not 0.5 !!!!! */ double _pullEnergySpringEval(double *denr, double dist, const double *parm) { /* static const char me[]="_pullEnergySpringEval"; */ double enr, xx, pull; pull = parm[0]; /* support used to be [0,1 + pull], but now is scrunched to [0,1], so hack "dist" to match old parameterization */ dist = AIR_AFFINE(0, dist, 1, 0, 1+pull); xx = dist - 1.0; if (xx > pull) { enr = 0; *denr = 0; } else if (xx > 0) { enr = xx*xx*(xx*xx/(4*pull*pull) - 2*xx/(3*pull) + 1.0/2.0); *denr = xx*(xx*xx/(pull*pull) - 2*xx/pull + 1); } else { enr = xx*xx/2; *denr = xx; } /* if (!AIR_EXISTS(ret)) { printf("!%s: dist=%g, pull=%g, blah=%d --> ret=%g\n", me, dist, pull, blah, ret); } */ return enr; } int _pullEnergySpringWell(const double *parm) { return (parm[0] > 0.0); } const pullEnergy _pullEnergySpring = { SPRING, 1, _pullEnergyNoWell, /* HEY: is this true? */ _pullEnergySpringEval }; const pullEnergy *const pullEnergySpring = &_pullEnergySpring; /* ---------------------------------------------------------------- ** ------------------------------ GAUSS -------------------------- ** ---------------------------------------------------------------- ** 0 parms: for simplicity we're now always cutting off at 4 sigmas */ /* HEY: copied from teem/src/nrrd/kernel.c */ #define _GAUSS(x, sig, cut) ( \ x >= sig*cut ? 0 \ : exp(-x*x/(2.0*sig*sig))) #define _DGAUSS(x, sig, cut) ( \ x >= sig*cut ? 0 \ : -exp(-x*x/(2.0*sig*sig))*(x/(sig*sig))) double _pullEnergyGaussEval(double *denr, double dist, const double *parm) { AIR_UNUSED(parm); *denr = _DGAUSS(dist, 0.25, 4); return _GAUSS(dist, 0.25, 4); } const pullEnergy _pullEnergyGauss = { GAUSS, 0, _pullEnergyNoWell, _pullEnergyGaussEval }; const pullEnergy *const pullEnergyGauss = &_pullEnergyGauss; /* ---------------------------------------------------------------- ** ------------------------------ BSPLN -------------------------- ** ---------------------------------------------------------------- ** 0 parms */ /* HEY: copied from teem/src/nrrd/bsplKernel.c */ #define BSPL(ret, t, x) \ if (x < 1) { \ ret = (4 + 3*(-2 + x)*x*x)/6; \ } else if (x < 2) { \ t = (-2 + x); \ ret = -t*t*t/6; \ } else { \ ret = 0; \ } #define DBSPL(ret, t, x) \ if (x < 1) { \ ret = (-4 + 3*x)*x/2; \ } else if (x < 2) { \ t = (-2 + x); \ ret = -t*t/2; \ } else { \ ret = 0; \ } double _pullEnergyBsplnEval(double *denr, double dist, const double *parm) { double tmp, ret; AIR_UNUSED(parm); dist *= 2; DBSPL(*denr, tmp, dist); *denr *= 2; BSPL(ret, tmp, dist); return ret; } const pullEnergy _pullEnergyBspln = { BSPLN, 0, _pullEnergyNoWell, _pullEnergyBsplnEval }; const pullEnergy *const pullEnergyBspln = &_pullEnergyBspln; /* ---------------------------------------------------------------- ** ------------------------------ BUTTER -------------------------- ** ---------------------------------------------------------------- ** 2 parms: order (an integer) and "cut-ff" (where height==0.5) */ double _pullEnergyButterworthEval(double *denr, double x, const double *parm) { int n; double cut, denom, enr; n = AIR_CAST(int, parm[0]); cut = parm[1]; denom = 1 + airIntPow(x/cut, 2*n); enr = 1/denom; *denr = -2*n*airIntPow(x/cut, 2*n - 1)*enr*enr/cut; return enr; } const pullEnergy _pullEnergyButterworth= { BUTTER, 2, _pullEnergyNoWell, _pullEnergyButterworthEval }; const pullEnergy *const pullEnergyButterworth = &_pullEnergyButterworth; /* ---------------------------------------------------------------- ** ------------------------------ COTAN --------------------------- ** ---------------------------------------------------------------- ** 0 parms! */ double _pullEnergyCotanEval(double *denr, double dist, const double *parm) { double pot, cc, enr; AIR_UNUSED(parm); pot = AIR_PI/2.0; cc = 1.0/(FLT_MIN + tan(dist*pot)); enr = dist > 1 ? 0 : cc + dist*pot - pot; *denr = dist > 1 ? 0 : -cc*cc*pot; return enr; } const pullEnergy _pullEnergyCotan = { COTAN, 0, _pullEnergyNoWell, _pullEnergyCotanEval }; const pullEnergy *const pullEnergyCotan = &_pullEnergyCotan; /* ---------------------------------------------------------------- ** ------------------------------ CUBIC --------------------------- ** ---------------------------------------------------------------- ** 0 parms! */ double _pullEnergyCubicEval(double *denr, double dist, const double *parm) { double omr, enr; AIR_UNUSED(parm); if (dist <= 1) { omr = 1 - dist; enr = omr*omr*omr; *denr = -3*omr*omr; } else { enr = *denr = 0; } return enr; } const pullEnergy _pullEnergyCubic = { CUBIC, 0, _pullEnergyNoWell, _pullEnergyCubicEval }; const pullEnergy *const pullEnergyCubic = &_pullEnergyCubic; /* ---------------------------------------------------------------- ** ----------------------------- QUARTIC -------------------------- ** ---------------------------------------------------------------- ** 0 parms! */ double _pullEnergyQuarticEval(double *denr, double dist, const double *parm) { double omr, enr; AIR_UNUSED(parm); if (dist <= 1) { omr = 1 - dist; enr = 2.132*omr*omr*omr*omr; *denr = -4*2.132*omr*omr*omr; } else { enr = *denr = 0; } return enr; } const pullEnergy _pullEnergyQuartic = { QUARTIC, 0, _pullEnergyNoWell, _pullEnergyQuarticEval }; const pullEnergy *const pullEnergyQuartic = &_pullEnergyQuartic; /* ---------------------------------------------------------------- ** ------------------ tunable piece-wise cubic -------------------- ** ---------------------------------------------------------------- ** 2 parm: wellX, wellY */ double _pullEnergyCubicWellEval(double *denr, double x, const double *parm) { double a, b, c, d, e, wx, wy, enr; wx = parm[0]; wy = parm[1]; a = (3*(-1 + wy))/wx; b = (-3*(-1 + wy))/(wx*wx); c = -(1 - wy)/(wx*wx*wx); d = (-3*wy)/((wx-1)*(wx-1)); e = (-2*wy)/((wx-1)*(wx-1)*(wx-1)); if (x < wx) { enr = 1 + x*(a + x*(b + c*x)); *denr = a + x*(2*b + 3*c*x); } else if (x < 1) { double _x; _x = x - wx; enr = wy + _x*_x*(d + e*_x); *denr = _x*(2*d + 3*e*_x); } else { enr = 0; *denr = 0; } return enr; } double _pullEnergyCubicWellWell(double *wx, const double *parm) { *wx = parm[0]; return AIR_MIN(0.0, parm[1]); } const pullEnergy _pullEnergyCubicWell = { CWELL, 2, _pullEnergyCubicWellWell, _pullEnergyCubicWellEval }; const pullEnergy *const pullEnergyCubicWell = &_pullEnergyCubicWell; /* ---------------------------------------------------------------- ** --------------- better tunable piece-wise cubic ---------------- ** ---------------------------------------------------------------- ** 2 parm: wellX, wellY */ double _pullEnergyBetterCubicWellEval(double *denr, double x, const double *parm) { double a, b, c, d, e, f, g, wx, wy, xmo, xmoo, xmooo, enr; /* HEY: it would be good if there was a place to store these intermediate values, so that they don't need to be computed for *every*single* inter-particle interaction */ wx = parm[0]; wy = parm[1]; xmo = wx-1; xmoo = xmo*xmo; xmooo = xmoo*xmo; a = -3*(xmoo + (-1 + 2*wx)*wy)/(xmoo*wx); b = 3*(xmoo + (-1 + wx*(2 + wx))*wy)/(xmoo*wx*wx); c = (-1 + wy - wx*(-2 + wx + 2*(1 + wx)*wy))/(xmoo*wx*wx*wx); d = ((-1 + 3*wx)*wy)/xmooo; e = -(6*wx*wy)/xmooo; f = (3*(1 + wx)*wy)/xmooo; g = -(2*wy)/xmooo; if (x < wx) { enr = 1 + x*(a + x*(b + x*c)); *denr = a + x*(2*b + x*3*c); } else if (x < 1) { enr = d + x*(e + x*(f + x*g)); *denr = e + x*(2*f + x*3*g); } else { enr = 0; *denr = 0; } return enr; } double _pullEnergyBetterCubicWellWell(double *wx, const double *parm) { *wx = parm[0]; return AIR_MIN(0.0, parm[1]); } const pullEnergy _pullEnergyBetterCubicWell = { BWELL, 2, _pullEnergyBetterCubicWellWell, _pullEnergyBetterCubicWellEval }; const pullEnergy *const pullEnergyBetterCubicWell = &_pullEnergyBetterCubicWell; /* ---------------------------------------------------------------- ** ----------------- tunable single quartic well ------------------ ** ---------------------------------------------------------------- ** 1 parm: well radius */ double _pullEnergyQuarticWellEval(double *denr, double x, const double *parm) { double a, b, c, d, w, enr; w = parm[0]; a = (12*w)/(1 - 4*w); b = 3 + 9/(-1 + 4*w); c = (8 + 4*w)/(1 - 4*w); d = 3/(-1 + 4*w); enr = 1 + x*(a + x*(b + x*(c + x*d))); *denr = a + x*(2*b + x*(3*c + x*4*d)); return enr; } double _pullEnergyQuarticWellWell(double *wx, const double *parm) { double t; *wx = parm[0]; t = *wx - 1; return t*t*t*t/(4*(*wx) - 1); } const pullEnergy _pullEnergyQuarticWell = { QWELL, 1, _pullEnergyQuarticWellWell, _pullEnergyQuarticWellEval }; const pullEnergy *const pullEnergyQuarticWell = &_pullEnergyQuarticWell; /* ---------------------------------------------------------------- ** ------------------ tunable single heptic well ------------------ ** ---------------------------------------------------------------- ** 1 parm: well radius */ double _pullEnergyHepticWellEval(double *denr, double x, const double *parm) { double a, b, c, d, e, f, g, w, enr; w = parm[0]; a = (42*w)/(1 - 7*w); b = 15 + 36/(-1 + 7*w); c = -20 + 90/(1 - 7*w); d = (105*(1 + w))/(-1 + 7*w); e = -((42*(2 + w))/(-1 + 7*w)); f = (7*(5 + w))/(-1 + 7*w); g = 6/(1 - 7*w); enr = 1 + x*(a + x*(b + x*(c + x*(d + x*(e + x*(f + g*x)))))); *denr = a + x*(2*b + x*(3*c + x*(4*d + x*(5*e + x*(6*f + x*7*g))))); return enr; } double _pullEnergyHepticWellWell(double *wx, const double *parm) { double t; *wx = parm[0]; t = *wx - 1; return t*t*t*t*t*t*t/(7*(*wx) - 1); } const pullEnergy _pullEnergyHepticWell = { HWELL, 1, _pullEnergyHepticWellWell, _pullEnergyHepticWellEval }; const pullEnergy *const pullEnergyHepticWell = &_pullEnergyHepticWell; /* ---------------------------------------------------------------- ** ------------------------------- ZERO --------------------------- ** ---------------------------------------------------------------- ** 0 parms: */ double _pullEnergyZeroEval(double *denr, double dist, const double *parm) { AIR_UNUSED(dist); AIR_UNUSED(parm); *denr = 0; return 0; } const pullEnergy _pullEnergyZero = { ZERO, 0, _pullEnergyNoWell, _pullEnergyZeroEval }; const pullEnergy *const pullEnergyZero = &_pullEnergyZero; /* ---------------------------------------------------------------- ** ------------------------------- BPARAB ------------------------- ** ---------------------------------------------------------------- ** 3 parms, the first two are for butterworth, ** parm[2] is a shift (probably negative) on the parabola */ double _pullEnergyBParabEval(double *denr, double x, const double *parm) { double ben, dben; ben = _pullEnergyButterworthEval(&dben, x, parm); *denr = 2*x*ben + x*x*dben; return (x*x + parm[2])*ben; } const pullEnergy _pullEnergyButterworthParabola = { BPARAB, 3, _pullEnergyNoWell, _pullEnergyBParabEval }; const pullEnergy *const pullEnergyButterworthParabola = &_pullEnergyButterworthParabola; /* ---------------------------------------------------------------- ** ---------------------------------------------------------------- ** ---------------------------------------------------------------- */ const pullEnergy *const pullEnergyAll[PULL_ENERGY_TYPE_MAX+1] = { &_pullEnergyUnknown, /* 0 */ &_pullEnergySpring, /* 1 */ &_pullEnergyGauss, /* 2 */ &_pullEnergyBspln, /* 3 */ &_pullEnergyButterworth, /* 4 */ &_pullEnergyCotan, /* 5 */ &_pullEnergyCubic, /* 6 */ &_pullEnergyQuartic, /* 7 */ &_pullEnergyCubicWell, /* 8 */ &_pullEnergyBetterCubicWell, /* 9 */ &_pullEnergyQuarticWell, /* 10 */ &_pullEnergyHepticWell, /* 11 */ &_pullEnergyZero, /* 12 */ &_pullEnergyButterworthParabola /* 13 */ }; pullEnergySpec * pullEnergySpecNew() { pullEnergySpec *ensp; int pi; ensp = (pullEnergySpec *)calloc(1, sizeof(pullEnergySpec)); if (ensp) { ensp->energy = pullEnergyUnknown; for (pi=0; piparm[pi] = AIR_NAN; } } return ensp; } void pullEnergySpecSet(pullEnergySpec *ensp, const pullEnergy *energy, const double parm[PULL_ENERGY_PARM_NUM]) { unsigned int pi; if (ensp && energy && parm) { ensp->energy = energy; for (pi=0; piparm[pi] = parm[pi]; } } return; } void pullEnergySpecCopy(pullEnergySpec *esDst, const pullEnergySpec *esSrc) { if (esDst && esSrc) { pullEnergySpecSet(esDst, esSrc->energy, esSrc->parm); } return; } pullEnergySpec * pullEnergySpecNix(pullEnergySpec *ensp) { airFree(ensp); return NULL; } int pullEnergySpecParse(pullEnergySpec *ensp, const char *_str) { static const char me[]="pullEnergySpecParse"; char *str, *col, *_pstr, *pstr; int etype; unsigned int pi, haveParm; airArray *mop; double pval; if (!( ensp && _str )) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } /* see if its the name of something that needs no parameters */ etype = airEnumVal(pullEnergyType, _str); if (pullEnergyTypeUnknown != etype) { /* the string is the name of some energy */ ensp->energy = pullEnergyAll[etype]; if (0 != ensp->energy->parmNum) { biffAddf(PULL, "%s: need %u parm%s for %s energy, but got none", me, ensp->energy->parmNum, (1 == ensp->energy->parmNum ? "" : "s"), ensp->energy->name); return 1; } /* the energy needs 0 parameters */ for (pi=0; piparm[pi] = AIR_NAN; } return 0; } /* start parsing parms after ':' */ mop = airMopNew(); str = airStrdup(_str); airMopAdd(mop, str, (airMopper)airFree, airMopAlways); col = strchr(str, ':'); if (!col) { biffAddf(PULL, "%s: either \"%s\" is not a recognized energy, " "or it is an energy with parameters, and there's no " "\":\" separator to indicate parameters", me, str); airMopError(mop); return 1; } *col = '\0'; etype = airEnumVal(pullEnergyType, str); if (pullEnergyTypeUnknown == etype) { biffAddf(PULL, "%s: didn't recognize \"%s\" as a %s", me, str, pullEnergyType->name); airMopError(mop); return 1; } ensp->energy = pullEnergyAll[etype]; if (0 == ensp->energy->parmNum) { biffAddf(PULL, "%s: \"%s\" energy has no parms, but got something", me, ensp->energy->name); return 1; } _pstr = pstr = col+1; /* code lifted from teem/src/nrrd/kernel.c, should probably refactor. . . */ for (haveParm=0; haveParmenergy->parmNum; haveParm++) { if (!pstr) { break; } if (1 != sscanf(pstr, "%lg", &pval)) { biffAddf(PULL, "%s: trouble parsing \"%s\" as double (in \"%s\")", me, _pstr, _str); airMopError(mop); return 1; } ensp->parm[haveParm] = pval; if ((pstr = strchr(pstr, ','))) { pstr++; if (!*pstr) { biffAddf(PULL, "%s: nothing after last comma in \"%s\" (in \"%s\")", me, _pstr, _str); airMopError(mop); return 1; } } } /* haveParm is now the number of parameters that were parsed. */ if (haveParm < ensp->energy->parmNum) { biffAddf(PULL, "%s: parsed only %u of %u required parms (for %s energy)" "from \"%s\" (in \"%s\")", me, haveParm, ensp->energy->parmNum, ensp->energy->name, _pstr, _str); airMopError(mop); return 1; } else { if (pstr) { biffAddf(PULL, "%s: \"%s\" (in \"%s\") has more than %u doubles", me, _pstr, _str, ensp->energy->parmNum); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } int _pullHestEnergyParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { static const char me[]="_pullHestForceParse"; pullEnergySpec **enspP; char *perr; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } enspP = (pullEnergySpec **)ptr; *enspP = pullEnergySpecNew(); if (pullEnergySpecParse(*enspP, str)) { perr = biffGetDone(PULL); airStrcpy(err, AIR_STRLEN_HUGE, perr); free(perr); return 1; } return 0; } hestCB _pullHestEnergySpec = { sizeof(pullEnergySpec*), "energy specification", _pullHestEnergyParse, (airMopper)pullEnergySpecNix }; hestCB * pullHestEnergySpec = &_pullHestEnergySpec; teem-1.11.0~svn6057/src/pull/defaultsPull.c0000664000175000017500000000250712165631065020171 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" const int pullPresent = 42; #if PULL_PHIST const int pullPhistEnabled = 1; #else const int pullPhistEnabled = 0; #endif const char * pullBiffKey = "pull"; teem-1.11.0~svn6057/src/pull/constraints.c0000664000175000017500000007215212174666612020105 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" /* #define PRAYING 0 */ #define __IF_DEBUG if (0) /* #define __IF_DEBUG if(9518 == point->idtag) */ /* typedef struct { double val, absval, grad[3]; } stateIso; static int probeIso(pullTask *task, pullPoint *point, unsigned int iter, int cond, double pos[3], stateIso *state) { static const char me[]="probeIso"; ELL_3V_COPY(point->pos, pos); / * NB: not touching point->pos[3] * / _pullPointHistAdd(point, cond); if (pullProbe(task, point)) { biffAddf(PULL, "%s: on iter %u", me, iter); return 1; } state->val = pullPointScalar(task->pctx, point, pullInfoIsovalue, state->grad, NULL); state->absval = AIR_ABS(state->val); return 0; } */ /* NOTE: this assumes variables "iter" (uint) and "me" (char*) */ #define NORMALIZE_ERR(dir, grad, len) \ if (task->pctx->flag.zeroZ) grad[2]=0; \ ELL_3V_NORM((dir), (grad), (len)); \ if (!(len)) { \ biffAddf(PULL, "%s: got zero grad at (%g,%g,%g,%g) on iter %u\n", me,\ point->pos[0], point->pos[1], point->pos[2], \ point->pos[3], iter); \ return 1; \ } #define NORMALIZE(dir, grad, len) \ if (task->pctx->flag.zeroZ) grad[2]=0; \ ELL_3V_NORM((dir), (grad), (len)); \ if (!(len)) { \ ELL_3V_SET((dir), 0, 0, 0) ; \ } /* ------------------------------- isosurface */ #define PROBE(v, av, g) if (pullProbe(task, point)) { \ biffAddf(PULL, "%s: on iter %u", me, iter); \ return 1; \ } \ (v) = pullPointScalar(task->pctx, point, \ pullInfoIsovalue, (g), NULL); \ (av) = AIR_ABS(v) #define SAVE(state, aval, val, grad, pos) \ state[0] = aval; \ state[1] = val; \ ELL_3V_COPY(state + 1 + 1, grad); \ ELL_3V_COPY(state + 1 + 1 + 3, pos) #define RESTORE(aval, val, grad, pos, state) \ aval = state[0]; \ val = state[1]; \ ELL_3V_COPY(grad, state + 1 + 1); \ ELL_3V_COPY(pos, state + 1 + 1 + 3) static int constraintSatIso(pullTask *task, pullPoint *point, double stepMax, unsigned int iterMax, /* output */ int *constrFailP) { static const char me[]="constraintSatIso"; double step, /* current step size */ val, aval, /* last and current function values */ hack, /* how to control re-tries in the context of a single for-loop, instead of a nested do-while loop */ grad[4], dir[3], len, state[1 + 1 + 3 + 3]; unsigned int iter = 0; /* 0: initial probe, 1..iterMax: probes in loop */ PROBE(val, aval, grad); SAVE(state, aval, val, grad, point->pos); hack = 1; for (iter=1; iter<=iterMax; iter++) { /* consider? http://en.wikipedia.org/wiki/Halley%27s_method */ NORMALIZE(dir, grad, len); if (!len) { /* no gradient; back off */ hack *= task->pctx->sysParm.backStepScale; RESTORE(aval, val, grad, point->pos, state); continue; } step = -val/len; /* the newton-raphson step */ step = step > 0 ? AIR_MIN(stepMax, step) : AIR_MAX(-stepMax, step); ELL_3V_SCALE_INCR(point->pos, hack*step, dir); _pullPointHistAdd(point, pullCondConstraintSatA); PROBE(val, aval, grad); if (aval <= state[0]) { /* we're no further from the root */ if (AIR_ABS(step) < stepMax*task->pctx->sysParm.constraintStepMin) { /* we have converged! */ break; } SAVE(state, aval, val, grad, point->pos); hack = 1; } else { /* oops, try again, don't update dir or len, reset val */ hack *= task->pctx->sysParm.backStepScale; RESTORE(aval, val, grad, point->pos, state); } } if (iter > iterMax) { *constrFailP = pullConstraintFailIterMaxed; } else { *constrFailP = AIR_FALSE; } return 0; } #undef PROBE #undef SAVE #undef RESTORE /* ------------------------------- laplacian */ #define PROBE(l) if (pullProbe(task, point)) { \ biffAddf(PULL, "%s: on iter %u", me, iter); \ return 1; \ } \ (l) = pullPointScalar(task->pctx, point, \ pullInfoHeightLaplacian, NULL, NULL); #define PROBEG(l, g) \ PROBE(l); \ pullPointScalar(task->pctx, point, pullInfoHeight, (g), NULL); static int constraintSatLapl(pullTask *task, pullPoint *point, double stepMax, unsigned int iterMax, /* output */ int *constrFailP) { static const char me[]="constraintSatLapl"; double step, /* current step size */ valLast, val, /* last and current function values */ grad[4], dir[3], len, posOld[3], posNew[3], tmpv[3]; double a=0, b=1, s, fa, fb, fs, tmp, diff; int side = 0; unsigned int iter = 0; /* 0: initial probe, 1..iterMax: probes in loop */ step = stepMax/2; PROBEG(val, grad); if (0 == val) { /* already exactly at the zero, we're done. This actually happens! */ /* printf("!%s: a lapl == 0!\n", me); */ return 0; } valLast = val; NORMALIZE(dir, grad, len); /* first phase: follow normalized gradient until laplacian sign change */ for (iter=1; iter<=iterMax; iter++) { double sgn; ELL_3V_COPY(posOld, point->pos); sgn = airSgn(val); /* lapl < 0 => downhill; lapl > 0 => uphill */ ELL_3V_SCALE_INCR(point->pos, sgn*step, dir); _pullPointHistAdd(point, pullCondConstraintSatA); PROBEG(val, grad); if (val*valLast < 0) { /* laplacian has changed sign; stop looking */ break; } valLast = val; NORMALIZE(dir, grad, len); } if (iter > iterMax) { *constrFailP = pullConstraintFailIterMaxed; return 0; } /* second phase: find the zero-crossing, looking between f(posOld)=valLast and f(posNew)=val */ ELL_3V_COPY(posNew, point->pos); ELL_3V_SUB(tmpv, posNew, posOld); len = ELL_3V_LEN(tmpv); fa = valLast; fb = val; if (AIR_ABS(fa) < AIR_ABS(fb)) { ELL_SWAP2(a, b, tmp); ELL_SWAP2(fa, fb, tmp); } for (iter=1; iter<=iterMax; iter++) { s = AIR_AFFINE(fa, 0, fb, a, b); ELL_3V_LERP(point->pos, s, posOld, posNew); _pullPointHistAdd(point, pullCondConstraintSatB); PROBE(fs); if (0 == fs) { /* exactly nailed the zero, we're done. This actually happens! */ printf("!%s: b lapl == 0!\n", me); break; } /* "Illinois" false-position. Dumb, but it works. */ if (fs*fb > 0) { /* not between s and b */ b = s; fb = fs; if (+1 == side) { fa /= 2; } side = +1; } else { /* not between a and s */ a = s; fa = fs; if (-1 == side) { fb /= 2; } side = -1; } diff = (b - a)*len; if (AIR_ABS(diff) < stepMax*task->pctx->sysParm.constraintStepMin) { /* converged! */ break; } } if (iter > iterMax) { *constrFailP = pullConstraintFailIterMaxed; } else { *constrFailP = AIR_FALSE; } return 0; } #undef PROBE #undef PROBEG /* ------------------------------------------- height (line xor surf) */ static int probeHeight(pullTask *task, pullPoint *point, /* output */ double *heightP, double grad[3], double hess[9]) { static const char me[]="probeHeight"; if (pullProbe(task, point)) { biffAddf(PULL, "%s: trouble", me); return 1; } *heightP = pullPointScalar(task->pctx, point, pullInfoHeight, grad, hess); return 0; } /* ** creaseProj ** ** eigenvectors (with non-zero eigenvalues) of output posproj are ** tangents to the directions along which particle is allowed to move ** *downward* (in height) for constraint satisfaction (according to ** tangent 1 or tangents 1&2) ** ** negproj is the same, but for points moving upwards (according to ** negativetangent1 or negativetangent 1&2) */ static void creaseProj(pullTask *task, pullPoint *point, int tang1Use, int tang2Use, int negtang1Use, int negtang2Use, /* output */ double posproj[9], double negproj[9]) { /* #if PRAYING */ static const char me[]="creaseProj"; /* #endif */ double pp[9]; double *tng; ELL_3M_ZERO_SET(posproj); if (tang1Use) { tng = point->info + task->pctx->infoIdx[pullInfoTangent1]; __IF_DEBUG { fprintf(stderr, "!%s: tng1 = %g %g %g\n", me, tng[0], tng[1], tng[2]); } ELL_3MV_OUTER(pp, tng, tng); ELL_3M_ADD2(posproj, posproj, pp); } if (tang2Use) { tng = point->info + task->pctx->infoIdx[pullInfoTangent2]; ELL_3MV_OUTER(pp, tng, tng); ELL_3M_ADD2(posproj, posproj, pp); } ELL_3M_ZERO_SET(negproj); if (negtang1Use) { tng = point->info + task->pctx->infoIdx[pullInfoNegativeTangent1]; ELL_3MV_OUTER(pp, tng, tng); ELL_3M_ADD2(negproj, negproj, pp); } if (negtang2Use) { tng = point->info + task->pctx->infoIdx[pullInfoNegativeTangent2]; ELL_3MV_OUTER(pp, tng, tng); ELL_3M_ADD2(negproj, negproj, pp); } if (!tang1Use && !tang2Use && !negtang1Use && !negtang2Use) { /* we must be after points, and so need freedom to go after them */ /* for now we do this via posproj not negproj; see haveNada below */ ELL_3M_IDENTITY_SET(posproj); } return; } /* HEY: body of probeHeight could really be expanded in here */ #define PROBE(height, grad, hess, posproj, negproj) \ if (probeHeight(task, point, \ &(height), (grad), (hess))) { \ biffAddf(PULL, "%s: trouble on iter %u", me, iter); \ return 1; \ } \ creaseProj(task, point, tang1Use, tang2Use, \ negtang1Use, negtang2Use, posproj, negproj) #define SAVE(state, height, grad, hess, posproj, negproj, pos) \ state[0] = height; \ ELL_3V_COPY(state + 1, grad); \ ELL_3M_COPY(state + 1 + 3, hess); \ ELL_3M_COPY(state + 1 + 3 + 9, posproj); \ ELL_3M_COPY(state + 1 + 3 + 9 + 9, negproj); \ ELL_3V_COPY(state + 1 + 3 + 9 + 9 + 9, pos) #define RESTORE(height, grad, hess, posproj, negproj, pos, state) \ height = state[0]; \ ELL_3V_COPY(grad, state + 1); \ ELL_3M_COPY(hess, state + 1 + 3); \ ELL_3M_COPY(posproj, state + 1 + 3 + 9); \ ELL_3M_COPY(negproj, state + 1 + 3 + 9 + 9); \ ELL_3V_COPY(pos, state + 1 + 3 + 9 + 9 + 9) #define POSNORM(d1, d2, pdir, plen, pgrad, grad, hess, posproj) \ ELL_3MV_MUL(pgrad, posproj, grad); \ if (task->pctx->flag.zeroZ) pgrad[2]=0; \ ELL_3V_NORM(pdir, pgrad, plen); \ d1 = ELL_3V_DOT(grad, pdir); \ d2 = ELL_3MV_CONTR(hess, pdir) #define NEGNORM(d1, d2, pdir, plen, pgrad, grad, hess, negproj) \ ELL_3MV_MUL(pgrad, negproj, grad); \ if (task->pctx->flag.zeroZ) pgrad[2]=0; \ ELL_3V_NORM(pdir, pgrad, plen); \ d1 = -ELL_3V_DOT(grad, pdir); \ d2 = -ELL_3MV_CONTR(hess, pdir) #define PRINT(prefix) \ fprintf(stderr, "-------------- probe results %s (%u @ %g,%g,%g,%g):\n", \ prefix, point->idtag, point->pos[0], point->pos[1], \ point->pos[2], point->pos[3]); \ fprintf(stderr, "-- val = %g\n", val); \ fprintf(stderr, "-- grad = %g %g %g\n", grad[0], grad[1], grad[2]); \ fprintf(stderr,"-- hess = %g %g %g; %g %g %g; %g %g %g\n", \ hess[0], hess[1], hess[2], \ hess[3], hess[4], hess[5], \ hess[6], hess[7], hess[8]); \ fprintf(stderr, "-- posproj = %g %g %g; %g %g %g; %g %g %g\n", \ posproj[0], posproj[1], posproj[2], \ posproj[3], posproj[4], posproj[5], \ posproj[6], posproj[7], posproj[8]); \ fprintf(stderr, "-- negproj = %g %g %g; %g %g %g; %g %g %g\n", \ negproj[0], negproj[1], negproj[2], \ negproj[3], negproj[4], negproj[5], \ negproj[6], negproj[7], negproj[8]) static int constraintSatHght(pullTask *task, pullPoint *point, int tang1Use, int tang2Use, int negtang1Use, int negtang2Use, double stepMax, unsigned int iterMax, int *constrFailP) { static const char me[]="constraintSatHght"; double val, grad[3], hess[9], posproj[9], negproj[9], state[1+3+9+9+9+3], hack, step, d1, d2, pdir[3], plen, pgrad[3]; /* #if PRAYING */ double _tmpv[3]={0,0,0}; /* #endif */ int havePos, haveNeg, haveNada, zeroGmagOkay; unsigned int iter = 0; /* 0: initial probe, 1..iterMax: probes in loop */ /* http://en.wikipedia.org/wiki/Newton%27s_method_in_optimization */ zeroGmagOkay = (1 < task->pctx->iter && 0 == task->pctx->constraintDim); havePos = tang1Use || tang2Use; haveNeg = negtang1Use || negtang2Use; haveNada = !havePos && !haveNeg; __IF_DEBUG { double stpmin; /* HEY: shouldn't stpmin also be used later in this function? */ stpmin = task->pctx->voxelSizeSpace*task->pctx->sysParm.constraintStepMin; fprintf(stderr, "!%s(%u): starting at %g %g %g %g\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); fprintf(stderr, "!%s: pt %d %d nt %d %d (nada %d) " "stepMax %g, iterMax %u\n", me, tang1Use, tang2Use, negtang1Use, negtang2Use, haveNada, stepMax, iterMax); fprintf(stderr, "!%s: stpmin = %g = voxsize %g * parm.stepmin %g\n", me, stpmin, task->pctx->voxelSizeSpace, task->pctx->sysParm.constraintStepMin); } _pullPointHistAdd(point, pullCondOld); PROBE(val, grad, hess, posproj, negproj); __IF_DEBUG { PRINT("initial probe"); } SAVE(state, val, grad, hess, posproj, negproj, point->pos); hack = 1; for (iter=1; iter<=iterMax; iter++) { __IF_DEBUG { fprintf(stderr, "!%s: =============== begin iter %u\n", me, iter); } /* HEY: no opportunistic increase of hack? */ if (havePos || haveNada) { POSNORM(d1, d2, pdir, plen, pgrad, grad, hess, posproj); if (!plen) { if (zeroGmagOkay && ELL_3M_FROB(hess)) { /* getting to actual zero gradient is possible when looking for point extrema (or saddles), and its not a problem, so as a lousy hack we set step=0 and skip to the convergence test */ step = 0; goto convtestA; } /* this use to be a biff error, which got to be annoying */ *constrFailP = pullConstraintFailProjGradZeroA; return 0; } step = (d2 <= 0 ? -plen : -d1/d2); __IF_DEBUG { fprintf(stderr, "!%s: (+) iter %u step = (%g <= 0 ? %g : %g) --> %g\n", me, iter, d2, -plen, -d1/d2, step); } step = step > 0 ? AIR_MIN(stepMax, step) : AIR_MAX(-stepMax, step); convtestA: if (AIR_ABS(step) < stepMax*task->pctx->sysParm.constraintStepMin) { /* no further iteration needed; we're converged */ __IF_DEBUG { fprintf(stderr, " |step| %g < %g*%g = %g ==> converged!\n", AIR_ABS(step), stepMax, task->pctx->sysParm.constraintStepMin, stepMax*task->pctx->sysParm.constraintStepMin); } if (!haveNeg) { break; } else { goto nextstep; } } /* else we have to take a significant step */ __IF_DEBUG { fprintf(stderr, " -> step %g, |pdir| = %g\n", step, ELL_3V_LEN(pdir)); ELL_3V_COPY(_tmpv, point->pos); fprintf(stderr, " -> pos (%g,%g,%g,%g) += " "%g * %g * (%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3], hack, step, pdir[0], pdir[1], pdir[2]); } ELL_3V_SCALE_INCR(point->pos, hack*step, pdir); __IF_DEBUG { ELL_3V_SUB(_tmpv, _tmpv, point->pos); fprintf(stderr, " -> moved to %g %g %g %g\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); fprintf(stderr, " (moved %g)\n", ELL_3V_LEN(_tmpv)); } _pullPointHistAdd(point, pullCondConstraintSatA); PROBE(val, grad, hess, posproj, negproj); __IF_DEBUG { fprintf(stderr, " (+) probed at (%g,%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); PRINT("after move"); fprintf(stderr, " val(%g,%g,%g,%g)=%g %s state[0]=%g\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3], val, val <= state[0] ? "<=" : ">", state[0]); } if (val <= state[0]) { /* we made progress */ __IF_DEBUG { fprintf(stderr, " (+) progress!\n"); } SAVE(state, val, grad, hess, posproj, negproj, point->pos); hack = 1; } else { /* oops, we went uphill instead of down; try again */ __IF_DEBUG { fprintf(stderr, " val *increased*; backing hack from %g to %g\n", hack, hack*task->pctx->sysParm.backStepScale); } hack *= task->pctx->sysParm.backStepScale; RESTORE(val, grad, hess, posproj, negproj, point->pos, state); __IF_DEBUG { fprintf(stderr, " restored to pos (%g,%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); } } } nextstep: if (haveNeg) { /* HEY: copy and paste from above, minus fluff, and with A->B */ NEGNORM(d1, d2, pdir, plen, pgrad, grad, hess, negproj); if (!plen && !haveNeg) { if (zeroGmagOkay && ELL_3M_FROB(hess)) { step = 0; goto convtestB; } /* this use to be a biff error, which got to be annoying */ *constrFailP = pullConstraintFailProjGradZeroB; return 0; } step = (d2 <= 0 ? -plen : -d1/d2); __IF_DEBUG { fprintf(stderr, "!%s: -+) iter %u step = (%g <= 0 ? %g : %g) --> %g\n", me, iter, d2, -plen, -d1/d2, step); } step = step > 0 ? AIR_MIN(stepMax, step) : AIR_MAX(-stepMax, step); convtestB: if (AIR_ABS(step) < stepMax*task->pctx->sysParm.constraintStepMin) { __IF_DEBUG { fprintf(stderr, " |step| %g < %g*%g = %g ==> converged!\n", AIR_ABS(step), stepMax, task->pctx->sysParm.constraintStepMin, stepMax*task->pctx->sysParm.constraintStepMin); } /* no further iteration needed; we're converged */ break; } /* else we have to take a significant step */ __IF_DEBUG { fprintf(stderr, " -> step %g, |pdir| = %g\n", step, ELL_3V_LEN(pdir)); ELL_3V_COPY(_tmpv, point->pos); fprintf(stderr, " -> pos (%g,%g,%g,%g) += " "%g * %g * (%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3], hack, step, pdir[0], pdir[1], pdir[2]); } ELL_3V_SCALE_INCR(point->pos, hack*step, pdir); __IF_DEBUG { ELL_3V_SUB(_tmpv, _tmpv, point->pos); fprintf(stderr, " -> moved to %g %g %g %g\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); fprintf(stderr, " (moved %g)\n", ELL_3V_LEN(_tmpv)); } _pullPointHistAdd(point, pullCondConstraintSatA); PROBE(val, grad, hess, posproj, negproj); __IF_DEBUG { fprintf(stderr, " (-) probed at (%g,%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); PRINT("after move"); fprintf(stderr, " val(%g,%g,%g,%g)=%g %s state[0]=%g\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3], val, val >= state[0] ? ">=" : "<", state[0]); } if (val >= state[0]) { /* we made progress */ __IF_DEBUG { fprintf(stderr, " (-) progress!\n"); } SAVE(state, val, grad, hess, posproj, negproj, point->pos); hack = 1; } else { /* oops, we went uphill instead of down; try again */ __IF_DEBUG { fprintf(stderr, " val *increased*; backing hack from %g to %g\n", hack, hack*task->pctx->sysParm.backStepScale); } hack *= task->pctx->sysParm.backStepScale; RESTORE(val, grad, hess, posproj, negproj, point->pos, state); __IF_DEBUG { fprintf(stderr, " restored to pos (%g,%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); } } } } if (iter > iterMax) { *constrFailP = pullConstraintFailIterMaxed; } else { *constrFailP = AIR_FALSE; } /* printf("!%s: %d %s\n", me, *constrFailP, *constrFailP ? "FAILED!" : "ok"); */ return 0; } #undef PROBE #undef POSNORM #undef NEGNORM #undef SAVE #undef RESTORE /* ------------------------------------------- */ /* HEY: have to make sure that scale position point->pos[3] ** is not modified anywhere in here: constraints are ONLY spatial ** ** This uses biff, but only for showstopper problems */ int _pullConstraintSatisfy(pullTask *task, pullPoint *point, double travelMax, /* output */ int *constrFailP) { static const char me[]="_pullConstraintSatisfy"; double stepMax; unsigned int iterMax; double pos3Orig[3], pos3Diff[3], travel; ELL_3V_COPY(pos3Orig, point->pos); stepMax = task->pctx->voxelSizeSpace; iterMax = task->pctx->iterParm.constraintMax; /* dlim = _pullDistLimit(task, point); if (iterMax*stepMax > dlim) { stepMax = dlim/iterMax; } */ /* fprintf(stderr, "!%s(%d): hi ==== %g %g %g, stepMax = %g, iterMax = %u\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], stepMax, iterMax); */ task->pctx->count[pullCountConstraintSatisfy] += 1; switch (task->pctx->constraint) { case pullInfoHeightLaplacian: /* zero-crossing edges */ if (constraintSatLapl(task, point, stepMax/4, 4*iterMax, constrFailP)) { biffAddf(PULL, "%s: trouble", me); return 1; } break; case pullInfoIsovalue: if (constraintSatIso(task, point, stepMax, iterMax, constrFailP)) { biffAddf(PULL, "%s: trouble", me); return 1; } break; case pullInfoHeight: if (constraintSatHght(task, point, !!task->pctx->ispec[pullInfoTangent1], !!task->pctx->ispec[pullInfoTangent2], !!task->pctx->ispec[pullInfoNegativeTangent1], !!task->pctx->ispec[pullInfoNegativeTangent2], stepMax, iterMax, constrFailP)) { biffAddf(PULL, "%s: trouble", me); return 1; } break; default: fprintf(stderr, "%s: constraint on %s (%d) unimplemented!!\n", me, airEnumStr(pullInfo, task->pctx->constraint), task->pctx->constraint); } ELL_3V_SUB(pos3Diff, pos3Orig, point->pos); travel = ELL_3V_LEN(pos3Diff)/task->pctx->voxelSizeSpace; if (travel > travelMax) { *constrFailP = pullConstraintFailTravel; } /* fprintf(stderr, "!%s(%u) %s @ (%g,%g,%g) = (%g,%g,%g) + (%g,%g,%g)\n", me, point->idtag, (*constrFailP ? airEnumStr(pullConstraintFail, *constrFailP) : "#GOOD#"), point->pos[0], point->pos[1], point->pos[2], pos3Diff[0], pos3Diff[1], pos3Diff[2], pos3Orig[0], pos3Orig[1], pos3Orig[2]); */ return 0; } #undef NORMALIZE /* ** _pullConstraintTangent ** ** eigenvectors (with non-zero eigenvalues) of output proj are ** (hopefully) approximate tangents to the manifold to which particles ** are constrained. It is *not* the local tangent of the directions ** along which particles are allowed to move during constraint ** satisfaction (that is given by creaseProj for creases) ** ** this can assume that probe() has just been called */ void _pullConstraintTangent(pullTask *task, pullPoint *point, /* output */ double proj[9]) { double vec[4], nvec[3], outer[9], len, posproj[9], negproj[9]; ELL_3M_IDENTITY_SET(proj); /* NOTE: we are starting with identity . . . */ switch (task->pctx->constraint) { case pullInfoHeight: creaseProj(task, point, !!task->pctx->ispec[pullInfoTangent1], !!task->pctx->ispec[pullInfoTangent2], !!task->pctx->ispec[pullInfoNegativeTangent1], !!task->pctx->ispec[pullInfoNegativeTangent2], posproj, negproj); /* .. and subracting out output from creaseProj */ ELL_3M_SUB(proj, proj, posproj); ELL_3M_SUB(proj, proj, negproj); break; case pullInfoHeightLaplacian: case pullInfoIsovalue: if (pullInfoHeightLaplacian == task->pctx->constraint) { /* using gradient of height as approx normal to laplacian 0-crossing */ pullPointScalar(task->pctx, point, pullInfoHeight, vec, NULL); } else { pullPointScalar(task->pctx, point, pullInfoIsovalue, vec, NULL); } ELL_3V_NORM(nvec, vec, len); if (len) { /* .. or and subracting out tensor product of normal with itself */ ELL_3MV_OUTER(outer, nvec, nvec); ELL_3M_SUB(proj, proj, outer); } break; } return; } /* ** returns the *dimension* (not codimension) of the constraint manifold: ** 0 for points ** 1 for lines ** 2 for surfaces ** ** a -1 return value represents a biff-able error */ int _pullConstraintDim(const pullContext *pctx) { static const char me[]="_pullConstraintDim"; int ret, t1, t2, nt1, nt2; switch (pctx->constraint) { case pullInfoHeightLaplacian: /* zero-crossing edges */ ret = 2; break; case pullInfoIsovalue: ret = 2; break; case pullInfoHeight: t1 = !!pctx->ispec[pullInfoTangent1]; t2 = !!pctx->ispec[pullInfoTangent2]; nt1 = !!pctx->ispec[pullInfoNegativeTangent1]; nt2 = !!pctx->ispec[pullInfoNegativeTangent2]; switch (t1 + t2 + nt1 + nt2) { case 0: case 3: ret = 0; break; case 1: ret = 2; break; case 2: ret = 1; break; default: biffAddf(PULL, "%s: can't simultaneously use all tangents " "(%s,%s,%s,%s) as this implies co-dimension of -1", me, airEnumStr(pullInfo, pullInfoTangent1), airEnumStr(pullInfo, pullInfoTangent2), airEnumStr(pullInfo, pullInfoNegativeTangent1), airEnumStr(pullInfo, pullInfoNegativeTangent2)); return -1; } break; default: biffAddf(PULL, "%s: constraint on %s (%d) unimplemented", me, airEnumStr(pullInfo, pctx->constraint), pctx->constraint); return -1; } return ret; } teem-1.11.0~svn6057/src/pull/GNUmakefile0000664000175000017500000000402412165631065017427 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := pull #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### tendCalc.o nixed $(L).NEED = ten limn gage ell nrrd biff hest air $(L).PUBLIC_HEADERS = pull.h $(L).PRIVATE_HEADERS = privatePull.h $(L).OBJS = defaultsPull.o energy.o infoPull.o volumePull.o taskPull.o \ binningPull.o corePull.o contextPull.o actionPull.o constraints.o \ pointPull.o popcntl.o ccPull.o parmPull.o initPull.o enumsPull.o trace.o $(L).TESTS = test/eparse test/circ #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/pull/sources.cmake0000664000175000017500000000063711733274706020055 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(PULL_SOURCES actionPull.c binningPull.c constraints.c contextPull.c parmPull.c initPull.c corePull.c defaultsPull.c energy.c infoPull.c pointPull.c privatePull.h pull.h taskPull.c volumePull.c popcntl.c ccPull.c enumsPull.c trace.c ) ADD_TEEM_LIBRARY(pull ${PULL_SOURCES}) teem-1.11.0~svn6057/src/pull/taskPull.c0000664000175000017500000001402112174667044017324 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" pullTask * _pullTaskNew(pullContext *pctx, int threadIdx) { static const char me[]="_pullTaskNew"; pullTask *task; unsigned int ii; pullPtrPtrUnion pppu; task = AIR_CALLOC(1, pullTask); if (!task) { biffAddf(PULL, "%s: couldn't allocate task", me); return NULL; } task->pctx = pctx; for (ii=0; iivolNum; ii++) { if (!(task->vol[ii] = _pullVolumeCopy(pctx, pctx->vol[ii]))) { biffAddf(PULL, "%s: trouble copying vol %u/%u", me, ii, pctx->volNum); return NULL; } } if (0) { gagePerVolume *pvl; const double *ans; double pos[3]; int gret; for (ii=0; iivolNum; ii++) { pvl = task->vol[ii]->gctx->pvl[0]; printf("!%s: vol[%u] query:\n", me, ii); gageQueryPrint(stdout, pvl->kind, pvl->query); ans = gageAnswerPointer(task->vol[ii]->gctx, pvl, gageSclValue); ELL_3V_SET(pos, 0.6, 0.6, 0.3); gret = gageProbeSpace(task->vol[ii]->gctx, pos[0], pos[1], pos[2], AIR_FALSE, AIR_TRUE); printf("!%s: (%d) val(%g,%g,%g) = %g\n", me, gret, pos[0], pos[1], pos[2], *ans); ELL_3V_SET(pos, 0.5, 0.0, 0.0); gret = gageProbeSpace(task->vol[ii]->gctx, pos[0], pos[1], pos[2], AIR_FALSE, AIR_TRUE); printf("!%s: (%d) val(%g,%g,%g) = %g\n", me, gret, pos[0], pos[1], pos[2], *ans); } } /* now set up all pointers for per-task pullInfos */ for (ii=0; ii<=PULL_INFO_MAX; ii++) { const pullVolume *vol; if (pctx->ispec[ii]) { if (pullSourceGage == pctx->ispec[ii]->source) { vol = task->vol[pctx->ispec[ii]->volIdx]; task->ans[ii] = gageAnswerPointer(vol->gctx, vol->gpvl, pctx->ispec[ii]->item); if (pctx->verbose) { printf("%s: task->ans[%u] = (%s) %p\n", me, ii, vol->kind->name, AIR_CVOIDP(task->ans[ii])); } } else { task->ans[ii] = NULL; } } else { task->ans[ii] = NULL; } } /* HEY: any idea why there is so little error checking in the below? */ /* initialize to descent because that's what's needed for the end of point initialization, when initial energy must be learned */ task->processMode = pullProcessModeDescent; task->probeSeedPreThreshOnly = AIR_FALSE; if (pctx->threadNum > 1) { task->thread = airThreadNew(); } task->threadIdx = threadIdx; task->rng = airRandMTStateNew(pctx->rngSeed + threadIdx); task->pointBuffer = pullPointNew(pctx); pctx->idtagNext = 0; /* because pullPointNew incremented it */ task->neighPoint = AIR_CAST(pullPoint **, calloc(_PULL_NEIGH_MAXNUM, sizeof(pullPoint*))); task->addPoint = NULL; task->addPointNum = 0; pppu.points = &(task->addPoint); task->addPointArr = airArrayNew(pppu.v, &(task->addPointNum), sizeof(pullPoint*), /* not exactly the right semantics . . . */ PULL_POINT_NEIGH_INCR); task->nixPoint = NULL; task->nixPointNum = 0; pppu.points = &(task->nixPoint); task->nixPointArr = airArrayNew(pppu.v, &(task->nixPointNum), sizeof(pullPoint*), /* not exactly the right semantics . . . */ PULL_POINT_NEIGH_INCR); task->returnPtr = NULL; task->stuckNum = 0; return task; } pullTask * _pullTaskNix(pullTask *task) { unsigned int ii; if (task) { for (ii=0; iipctx->volNum; ii++) { task->vol[ii] = pullVolumeNix(task->vol[ii]); } if (task->pctx->threadNum > 1) { task->thread = airThreadNix(task->thread); } task->rng = airRandMTStateNix(task->rng); task->pointBuffer = pullPointNix(task->pointBuffer); airFree(task->neighPoint); task->addPointArr = airArrayNuke(task->addPointArr); task->nixPointArr = airArrayNuke(task->nixPointArr); airFree(task); } return NULL; } /* ** _pullTaskSetup sets: **** pctx->task **** pctx->task[] */ int _pullTaskSetup(pullContext *pctx) { static const char me[]="_pullTaskSetup"; unsigned int tidx; pctx->task = (pullTask **)calloc(pctx->threadNum, sizeof(pullTask *)); if (!(pctx->task)) { biffAddf(PULL, "%s: couldn't allocate array of tasks", me); return 1; } for (tidx=0; tidxthreadNum; tidx++) { if (pctx->verbose) { printf("%s: creating task %u/%u\n", me, tidx, pctx->threadNum); } pctx->task[tidx] = _pullTaskNew(pctx, tidx); if (!(pctx->task[tidx])) { biffAddf(PULL, "%s: couldn't allocate task %d", me, tidx); return 1; } } return 0; } void _pullTaskFinish(pullContext *pctx) { unsigned int tidx; for (tidx=0; tidxthreadNum; tidx++) { pctx->task[tidx] = _pullTaskNix(pctx->task[tidx]); } airFree(pctx->task); pctx->task = NULL; return; } teem-1.11.0~svn6057/src/pull/parmPull.c0000664000175000017500000003513012165631065017317 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" /* the Init() functions are up here for easier reference */ void _pullIterParmInit(pullIterParm *iterParm) { iterParm->min = 0; iterParm->max = 0; iterParm->stuckMax = 4; iterParm->constraintMax = 15; iterParm->popCntlPeriod = 10; iterParm->addDescent = 10; iterParm->callback = 1; iterParm->snap = 0; iterParm->energyIncreasePermitHalfLife = 0; return; } void _pullSysParmInit(pullSysParm *sysParm) { sysParm->alpha = 0.5; sysParm->beta = 0.5; sysParm->gamma = 1; sysParm->separableGammaLearnRescale = 8; sysParm->theta = 0.0; sysParm->wall = 1; sysParm->radiusSpace = 1; sysParm->radiusScale = 1; sysParm->binWidthSpace = 1.001; /* supersititious */ sysParm->neighborTrueProb = 1.0; sysParm->probeProb = 1.0; sysParm->stepInitial = 1; sysParm->opporStepScale = 1.0; sysParm->backStepScale = 0.5; sysParm->constraintStepMin = 0.0001; sysParm->energyDecreaseMin = 0.001; sysParm->energyDecreasePopCntlMin = 0.02; sysParm->energyIncreasePermit = 0.0; sysParm->fracNeighNixedMax = 0.25; return; } void _pullFlagInit(pullFlag *flag) { flag->permuteOnRebin = AIR_FALSE; flag->noPopCntlWithZeroAlpha = AIR_FALSE; flag->useBetaForGammaLearn = AIR_FALSE; flag->restrictiveAddToBins = AIR_TRUE; flag->energyFromStrength = AIR_FALSE; flag->nixAtVolumeEdgeSpace = AIR_FALSE; flag->constraintBeforeSeedThresh = AIR_FALSE; flag->noAdd = AIR_FALSE; flag->popCntlEnoughTest = AIR_TRUE; /* really needs to be true by default */ flag->convergenceIgnoresPopCntl = AIR_FALSE; /* false by default for backwards compatibility, even thought this was probably a mistake */ flag->binSingle = AIR_FALSE; flag->allowCodimension3Constraints = AIR_FALSE; flag->scaleIsTau = AIR_FALSE; flag->startSkipsPoints = AIR_FALSE; /* must be false by default */ flag->zeroZ = AIR_FALSE; return; } int _pullIterParmCheck(pullIterParm *iterParm) { static const char me[]="_pullIterParmCheck"; if (!( 1 <= iterParm->constraintMax && iterParm->constraintMax <= 500 )) { biffAddf(PULL, "%s: iterParm->constraintMax %u not in range [%u,%u]", me, iterParm->constraintMax, 1, _PULL_CONSTRAINT_ITER_MAX); return 1; } return 0; } int pullIterParmSet(pullContext *pctx, int which, unsigned int pval) { static const char me[]="pullIterParmSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!AIR_IN_OP(pullIterParmUnknown, which, pullIterParmLast)) { biffAddf(PULL, "%s: iter parm %d not valid", me, which); return 1; } switch(which) { case pullIterParmMin: pctx->iterParm.min = pval; break; case pullIterParmMax: pctx->iterParm.max = pval; break; case pullIterParmStuckMax: pctx->iterParm.stuckMax = pval; break; case pullIterParmConstraintMax: pctx->iterParm.constraintMax = pval; break; case pullIterParmPopCntlPeriod: pctx->iterParm.popCntlPeriod = pval; break; case pullIterParmAddDescent: pctx->iterParm.addDescent = pval; break; case pullIterParmCallback: pctx->iterParm.callback = pval; break; case pullIterParmSnap: pctx->iterParm.snap = pval; break; case pullIterParmEnergyIncreasePermitHalfLife: pctx->iterParm.energyIncreasePermitHalfLife = pval; if (pval) { pctx->eipScale = pow(0.5, 1.0/pval); } else { pctx->eipScale = 1; } break; default: biffAddf(me, "%s: sorry, iter parm %d valid but not handled?", me, which); return 1; } return 0; } #define CHECK(thing, min, max) \ if (!( AIR_EXISTS(sysParm->thing) \ && min <= sysParm->thing && sysParm->thing <= max )) { \ biffAddf(PULL, "%s: sysParm->" #thing " %g not in range [%g,%g]", \ me, sysParm->thing, min, max); \ return 1; \ } int _pullSysParmCheck(pullSysParm *sysParm) { static const char me[]="_pullSysParmCheck"; /* these reality-check bounds are somewhat arbitrary */ CHECK(alpha, 0.0, 1.0); CHECK(beta, 0.0, 1.0); /* HEY: no check on gamma? */ /* no check on theta */ CHECK(wall, 0.0, 100.0); CHECK(radiusSpace, 0.000001, 80.0); CHECK(radiusScale, 0.000001, 80.0); CHECK(binWidthSpace, 1.0, 15.0); CHECK(neighborTrueProb, 0.02, 1.0); CHECK(probeProb, 0.02, 1.0); if (!( AIR_EXISTS(sysParm->stepInitial) && sysParm->stepInitial > 0 )) { biffAddf(PULL, "%s: sysParm->stepInitial %g not > 0", me, sysParm->stepInitial); return 1; } CHECK(opporStepScale, 1.0, 5.0); CHECK(backStepScale, 0.01, 0.99); CHECK(constraintStepMin, 0.00000000000000001, 0.1); CHECK(energyDecreaseMin, -0.2, 1.0); CHECK(energyDecreasePopCntlMin, -1.0, 1.0); CHECK(energyIncreasePermit, 0.0, 1.0); CHECK(fracNeighNixedMax, 0.01, 0.99); return 0; } #undef CHECK int pullSysParmSet(pullContext *pctx, int which, double pval) { static const char me[]="pullSysParmSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!AIR_IN_OP(pullSysParmUnknown, which, pullSysParmLast)) { biffAddf(PULL, "%s: sys parm %d not valid", me, which); return 1; } switch(which) { case pullSysParmAlpha: pctx->sysParm.alpha = pval; break; case pullSysParmBeta: pctx->sysParm.beta = pval; break; case pullSysParmGamma: pctx->sysParm.gamma = pval; break; case pullSysParmSeparableGammaLearnRescale: pctx->sysParm.separableGammaLearnRescale = pval; break; case pullSysParmTheta: pctx->sysParm.theta = pval; break; case pullSysParmStepInitial: pctx->sysParm.stepInitial = pval; break; case pullSysParmRadiusSpace: pctx->sysParm.radiusSpace = pval; break; case pullSysParmRadiusScale: pctx->sysParm.radiusScale = pval; break; case pullSysParmBinWidthSpace: pctx->sysParm.binWidthSpace = pval; break; case pullSysParmNeighborTrueProb: pctx->sysParm.neighborTrueProb = pval; break; case pullSysParmProbeProb: pctx->sysParm.probeProb = pval; break; case pullSysParmOpporStepScale: pctx->sysParm.opporStepScale = pval; break; case pullSysParmBackStepScale: pctx->sysParm.backStepScale = pval; break; case pullSysParmEnergyDecreasePopCntlMin: pctx->sysParm.energyDecreasePopCntlMin = pval; break; case pullSysParmEnergyDecreaseMin: pctx->sysParm.energyDecreaseMin = pval; break; case pullSysParmConstraintStepMin: pctx->sysParm.constraintStepMin = pval; break; case pullSysParmEnergyIncreasePermit: pctx->sysParm.energyIncreasePermit = pval; break; case pullSysParmFracNeighNixedMax: pctx->sysParm.fracNeighNixedMax = pval; break; case pullSysParmWall: pctx->sysParm.wall = pval; break; default: biffAddf(me, "%s: sorry, sys parm %d valid but not handled?", me, which); return 1; } return 0; } /* ******** pullFlagSet ** ** uniform way of setting all the boolean-ish flags */ int pullFlagSet(pullContext *pctx, int which, int flag) { static const char me[]="pullFlagSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!AIR_IN_OP(pullFlagUnknown, which, pullFlagLast)) { biffAddf(PULL, "%s: flag %d not valid", me, which); return 1; } switch (which) { case pullFlagPermuteOnRebin: pctx->flag.permuteOnRebin = flag; break; case pullFlagNoPopCntlWithZeroAlpha: pctx->flag.noPopCntlWithZeroAlpha = flag; break; case pullFlagUseBetaForGammaLearn: pctx->flag.useBetaForGammaLearn = flag; break; case pullFlagRestrictiveAddToBins: pctx->flag.restrictiveAddToBins = flag; break; case pullFlagEnergyFromStrength: pctx->flag.energyFromStrength = flag; break; case pullFlagNixAtVolumeEdgeSpace: pctx->flag.nixAtVolumeEdgeSpace = flag; break; case pullFlagConstraintBeforeSeedThresh: pctx->flag.constraintBeforeSeedThresh = flag; break; case pullFlagNoAdd: pctx->flag.noAdd = flag; break; case pullFlagPopCntlEnoughTest: pctx->flag.popCntlEnoughTest = flag; break; case pullFlagConvergenceIgnoresPopCntl: pctx->flag.convergenceIgnoresPopCntl = flag; break; case pullFlagBinSingle: pctx->flag.binSingle = flag; break; case pullFlagAllowCodimension3Constraints: pctx->flag.allowCodimension3Constraints = flag; break; case pullFlagScaleIsTau: pctx->flag.scaleIsTau = flag; break; case pullFlagStartSkipsPoints: pctx->flag.startSkipsPoints = flag; break; case pullFlagZeroZ: pctx->flag.zeroZ = flag; break; default: biffAddf(me, "%s: sorry, flag %d valid but not handled?", me, which); return 1; } return 0; } /* ** HEY: its really confusing to have the array of per-CONTEXT volumes. ** I know they're there to be copied upon task creation to create the ** per-TASK volumes, but its easy to think that one is supposed to be ** doing something with them, or that changes to them will have some ** effect . . . */ int pullVerboseSet(pullContext *pctx, int verbose) { static const char me[]="pullVerboseSet"; unsigned int volIdx, taskIdx; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } pctx->verbose = verbose; for (volIdx=0; volIdxvolNum; volIdx++) { int v; v = verbose > 0 ? verbose - 1 : 0; gageParmSet(pctx->vol[volIdx]->gctx, gageParmVerbose, v); } for (taskIdx=0; taskIdxthreadNum; taskIdx++) { for (volIdx=0; volIdxvolNum; volIdx++) { int v; v = verbose > 0 ? verbose - 1 : 0; gageParmSet(pctx->task[taskIdx]->vol[volIdx]->gctx, gageParmVerbose, v); } } return 0; } int pullThreadNumSet(pullContext *pctx, unsigned int threadNum) { static const char me[]="pullThreadNumSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } pctx->threadNum = threadNum; return 0; } int pullRngSeedSet(pullContext *pctx, unsigned int rngSeed) { static const char me[]="pullRngSeedSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } pctx->rngSeed = rngSeed; return 0; } int pullProgressBinModSet(pullContext *pctx, unsigned int bmod) { static const char me[]="pullProgressBinModSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } pctx->progressBinMod = bmod; return 0; } int pullCallbackSet(pullContext *pctx, void (*iter_cb)(void *data_cb), void *data_cb) { static const char me[]="pullCallbackSet"; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } pctx->iter_cb = iter_cb; pctx->data_cb = data_cb; return 0; } /* ******** pullInterEnergySet ** ** This is the single function for setting the inter-particle energy, ** which is clumsy because which pullEnergySpecs are necessary is ** different depending on interType. Pass NULL for those not needed. ** ** Note that all the pullEnergySpecs inside the pctx are created ** by pullContextNew, so they should never be NULL. When a pullEnergySpec ** is not needed for a given interType, we set it to pullEnergyZero ** and a vector of NaNs. */ int pullInterEnergySet(pullContext *pctx, int interType, const pullEnergySpec *enspR, const pullEnergySpec *enspS, const pullEnergySpec *enspWin) { static const char me[]="pullInterEnergySet"; unsigned int zpi; double zeroParm[PULL_ENERGY_PARM_NUM]; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!AIR_IN_OP(pullInterTypeUnknown, interType, pullInterTypeLast)) { biffAddf(PULL, "%s: interType %d not valid", me, interType); return 1; } for (zpi=0; zpienergySpec##X, ensp##X) switch (interType) { case pullInterTypeJustR: /* 1: phi(r,s) = phi_r(r) */ case pullInterTypeUnivariate: /* 2: phi(r,s) = phi_r(u); u = sqrt(r*r+s*s) */ CHECK_N_COPY(R); pullEnergySpecSet(pctx->energySpecS, pullEnergyZero, zeroParm); pullEnergySpecSet(pctx->energySpecWin, pullEnergyZero, zeroParm); break; case pullInterTypeSeparable: /* 3: phi(r,s) = phi_r(r)*phi_s(s) */ CHECK_N_COPY(R); CHECK_N_COPY(S); pullEnergySpecSet(pctx->energySpecWin, pullEnergyZero, zeroParm); break; case pullInterTypeAdditive: /* 4: phi(r,s) = beta*phi_r(r)*win(s) + (1-beta)*win(r)*phi_s(s) */ CHECK_N_COPY(R); CHECK_N_COPY(S); CHECK_N_COPY(Win); break; default: biffAddf(PULL, "%s: interType %d valid but no handled?", me, interType); return 1; } #undef CHECK_N_COPY pctx->interType = interType; return 0; } /* ** you can pass in a NULL FILE* if you want */ int pullLogAddSet(pullContext *pctx, FILE *flog) { static const char me[]="pullLogAddSet"; if (!(pctx)) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } pctx->logAdd = flog; return 0; } teem-1.11.0~svn6057/src/pull/infoPull.c0000664000175000017500000003325012165631065017314 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" /* --------------------------------------------- */ unsigned int _pullInfoLen[PULL_INFO_MAX+1] = { 0, /* pullInfoUnknown */ 7, /* pullInfoTensor */ 7, /* pullInfoTensorInverse */ 9, /* pullInfoHessian */ 1, /* pullInfoInside */ 3, /* pullInfoInsideGradient */ 1, /* pullInfoHeight */ 3, /* pullInfoHeightGradient */ 9, /* pullInfoHeightHessian */ 1, /* pullInfoHeightLaplacian */ 1, /* pullInfoSeedPreThresh */ 1, /* pullInfoSeedThresh */ 1, /* pullInfoLiveThresh */ 1, /* pullInfoLiveThresh2 */ 1, /* pullInfoLiveThresh3 */ 3, /* pullInfoTangent1 */ 3, /* pullInfoTangent2 */ 3, /* pullInfoNegativeTangent1 */ 3, /* pullInfoNegativeTangent2 */ 1, /* pullInfoIsovalue */ 3, /* pullInfoIsovalueGradient */ 9, /* pullInfoIsovalueHessian */ 1, /* pullInfoStrength */ 1, /* pullInfoQuality */ }; unsigned int pullInfoLen(int info) { unsigned int ret; if (!airEnumValCheck(pullInfo, info)) { ret = _pullInfoLen[info]; } else { ret = 0; } return ret; } unsigned int pullPropLen(int prop) { unsigned int ret; switch (prop) { case pullPropIdtag: case pullPropIdCC: case pullPropEnergy: case pullPropStepEnergy: case pullPropStepConstr: case pullPropStuck: case pullPropNeighDistMean: case pullPropScale: case pullPropStability: ret = 1; break; case pullPropPosition: case pullPropForce: ret = 4; break; case pullPropNeighCovar: ret = 10; break; case pullPropNeighCovar7Ten: ret = 7; break; case pullPropNeighTanCovar: ret = 6; break; default: ret = 0; break; } return ret; } pullInfoSpec * pullInfoSpecNew(void) { pullInfoSpec *ispec; ispec = AIR_CAST(pullInfoSpec *, calloc(1, sizeof(pullInfoSpec))); if (ispec) { ispec->info = pullInfoUnknown; ispec->source = pullSourceUnknown; ispec->volName = NULL; ispec->item = 0; /* should be the unknown item for any kind */ ispec->prop = pullPropUnknown; ispec->scale = AIR_NAN; ispec->zero = AIR_NAN; ispec->constraint = AIR_FALSE; ispec->volIdx = UINT_MAX; } return ispec; } pullInfoSpec * pullInfoSpecNix(pullInfoSpec *ispec) { if (ispec) { airFree(ispec->volName); airFree(ispec); } return NULL; } int pullInfoSpecAdd(pullContext *pctx, pullInfoSpec *ispec) { static const char me[]="pullInfoSpecAdd"; unsigned int ii, vi, haveLen, needLen; const gageKind *kind; if (!( pctx && ispec )) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(pullInfo, ispec->info)) { biffAddf(PULL, "%s: %d not a valid %s value", me, ispec->info, pullInfo->name); return 1; } if (airEnumValCheck(pullSource, ispec->source)) { biffAddf(PULL, "%s: %d not a valid %s value", me, ispec->source, pullSource->name); return 1; } if (pctx->ispec[ispec->info]) { biffAddf(PULL, "%s: already set info %s (%d)", me, airEnumStr(pullInfo, ispec->info), ispec->info); return 1; } for (ii=0; ii<=PULL_INFO_MAX; ii++) { if (pctx->ispec[ii] == ispec) { biffAddf(PULL, "%s(%s): already got ispec %p as ispec[%u]", me, airEnumStr(pullInfo, ispec->info), AIR_VOIDP(ispec), ii); return 1; } } if (pctx->verbose) { printf("%s: ispec %s from vol %s\n", me, airEnumStr(pullInfo, ispec->info), ispec->volName); } needLen = pullInfoLen(ispec->info); if (pullSourceGage == ispec->source) { vi = _pullVolumeIndex(pctx, ispec->volName); if (UINT_MAX == vi) { biffAddf(PULL, "%s(%s): no volume has name \"%s\"", me, airEnumStr(pullInfo, ispec->info), ispec->volName); return 1; } kind = pctx->vol[vi]->kind; if (airEnumValCheck(kind->enm, ispec->item)) { biffAddf(PULL, "%s(%s): %d not a valid \"%s\" item", me, airEnumStr(pullInfo, ispec->info), ispec->item, kind->name); return 1; } haveLen = kind->table[ispec->item].answerLength; if (needLen != haveLen) { biffAddf(PULL, "%s(%s): need len %u, but \"%s\" item \"%s\" has len %u", me, airEnumStr(pullInfo, ispec->info), needLen, kind->name, airEnumStr(kind->enm, ispec->item), haveLen); return 1; } /* very tricky: seedOnly is initialized to true for everything */ if (pullInfoSeedThresh != ispec->info && pullInfoSeedPreThresh != ispec->info) { /* if the info is neither seedthresh nor seedprethresh, then the volume will have to be probed after the first iter, so turn *off* seedOnly */ pctx->vol[vi]->seedOnly = AIR_FALSE; } /* less tricky: turn on forSeedPreThresh as needed; its initialized to false */ if (pullInfoSeedPreThresh == ispec->info) { pctx->vol[vi]->forSeedPreThresh = AIR_TRUE; if (pctx->verbose) { printf("%s: volume %u %s used for %s\n", me, vi, pctx->vol[vi]->name, airEnumStr(pullInfo, pullInfoSeedPreThresh)); } } /* now set item in gage query */ if (gageQueryItemOn(pctx->vol[vi]->gctx, pctx->vol[vi]->gpvl, ispec->item)) { biffMovef(PULL, GAGE, "%s: trouble adding item %u to vol %u", me, ispec->item, vi); return 1; } ispec->volIdx = vi; } else if (pullSourceProp == ispec->source) { haveLen = pullPropLen(ispec->prop); if (needLen != haveLen) { biffAddf(PULL, "%s: need len %u, but \"%s\" \"%s\" has len %u", me, needLen, pullProp->name, airEnumStr(pullProp, ispec->prop), haveLen); return 1; } } else { biffAddf(PULL, "%s: sorry, source %s unsupported", me, airEnumStr(pullSource, ispec->source)); return 1; } if (haveLen > 9) { biffAddf(PULL, "%s: sorry, answer length (%u) > 9 unsupported", me, haveLen); return 1; } pctx->ispec[ispec->info] = ispec; return 0; } /* ** sets: ** pctx->infoIdx[] ** pctx->infoTotalLen ** pctx->constraint ** pctx->constraintDim ** pctx->targetDim (non-trivial logic for scale-space!) */ int _pullInfoSetup(pullContext *pctx) { static const char me[]="_pullInfoSetup"; unsigned int ii; pctx->infoTotalLen = 0; pctx->constraint = 0; pctx->constraintDim = 0; for (ii=0; ii<=PULL_INFO_MAX; ii++) { if (pctx->ispec[ii]) { pctx->infoIdx[ii] = pctx->infoTotalLen; if (pctx->verbose) { printf("!%s: infoIdx[%u] (%s) = %u\n", me, ii, airEnumStr(pullInfo, ii), pctx->infoIdx[ii]); } pctx->infoTotalLen += pullInfoLen(ii); if (!pullInfoLen(ii)) { biffAddf(PULL, "%s: got zero-length answer for ispec[%u]", me, ii); return 1; } if (pctx->ispec[ii]->constraint) { /* pullVolume *cvol; */ pctx->constraint = ii; /* cvol = pctx->vol[pctx->ispec[ii]->volIdx]; */ } } } if (pctx->constraint) { pctx->constraintDim = _pullConstraintDim(pctx); if (-1 == pctx->constraintDim) { biffAddf(PULL, "%s: problem learning constraint dimension", me); return 1; } if (!pctx->flag.allowCodimension3Constraints && !pctx->constraintDim) { biffAddf(PULL, "%s: got constr dim 0 but co-dim 3 not allowed", me); return 1; } if (pctx->haveScale) { double *parmS, denS, (*evalS)(double *, double, const double parm[PULL_ENERGY_PARM_NUM]); switch (pctx->interType) { case pullInterTypeUnivariate: pctx->targetDim = 1 + pctx->constraintDim; break; case pullInterTypeSeparable: /* HEY! need to check if this is true given enr and ens! */ pctx->targetDim = pctx->constraintDim; break; case pullInterTypeAdditive: parmS = pctx->energySpecS->parm; evalS = pctx->energySpecS->energy->eval; evalS(&denS, _PULL_TARGET_DIM_S_PROBE, parmS); if (denS > 0) { /* at small positive s, derivative was positive ==> attractive */ pctx->targetDim = pctx->constraintDim; } else { /* derivative was negative ==> repulsive */ pctx->targetDim = 1 + pctx->constraintDim; } break; default: biffAddf(PULL, "%s: sorry, intertype %s not handled here", me, airEnumStr(pullInterType, pctx->interType)); break; } } else { pctx->targetDim = pctx->constraintDim; } } else { pctx->constraintDim = 0; pctx->targetDim = 0; } if (pctx->verbose) { printf("!%s: infoTotalLen=%u, constr=%d, constr,targetDim = %d,%d\n", me, pctx->infoTotalLen, pctx->constraint, pctx->constraintDim, pctx->targetDim); } return 0; } static void _infoCopy1(double *dst, const double *src) { dst[0] = src[0]; } static void _infoCopy2(double *dst, const double *src) { dst[0] = src[0]; dst[1] = src[1]; } static void _infoCopy3(double *dst, const double *src) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; } static void _infoCopy4(double *dst, const double *src) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; } static void _infoCopy5(double *dst, const double *src) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; } static void _infoCopy6(double *dst, const double *src) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; dst[5] = src[5]; } static void _infoCopy7(double *dst, const double *src) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; } static void _infoCopy8(double *dst, const double *src) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; dst[7] = src[7]; } static void _infoCopy9(double *dst, const double *src) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; dst[7] = src[7]; dst[8] = src[8]; } void (*_pullInfoCopy[10])(double *, const double *) = { NULL, _infoCopy1, _infoCopy2, _infoCopy3, _infoCopy4, _infoCopy5, _infoCopy6, _infoCopy7, _infoCopy8, _infoCopy9 }; int pullInfoGet(Nrrd *ninfo, int info, pullContext *pctx) { static const char me[]="pullInfoGet"; size_t size[2]; unsigned int dim, pointNum, pointIdx, binIdx, outIdx, alen, aidx; double *out_d; pullBin *bin; pullPoint *point; if (airEnumValCheck(pullInfo, info)) { biffAddf(PULL, "%s: info %d not valid", me, info); return 1; } pointNum = pullPointNumber(pctx); alen = pullInfoLen(info); aidx = pctx->infoIdx[info]; if (1 == alen) { dim = 1; size[0] = pointNum; } else { dim = 2; size[0] = alen; size[1] = pointNum; } if (nrrdMaybeAlloc_nva(ninfo, nrrdTypeDouble, dim, size)) { biffMovef(PULL, NRRD, "%s: trouble allocating output", me); return 1; } out_d = AIR_CAST(double *, ninfo->data); outIdx = 0; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; _pullInfoCopy[alen](out_d + outIdx, point->info + aidx); outIdx += alen; } } return 0; } /* HEY this was written in a hurry; ** needs to be checked against parsing code */ int pullInfoSpecSprint(char str[AIR_STRLEN_LARGE], const pullContext *pctx, const pullInfoSpec *ispec) { static const char me[]="pullInfoSpecSprint"; const pullVolume *pvol; char stmp[AIR_STRLEN_LARGE]; if (!( str && pctx && ispec )) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } strcpy(str, ""); /* HEY: no bounds checking! */ strcat(str, airEnumStr(pullInfo, ispec->info)); if (ispec->constraint) { strcat(str, "-c"); } strcat(str, ":"); if (pullSourceGage == ispec->source) { if (UINT_MAX == ispec->volIdx) { biffAddf(PULL, "%s: never learned volIdx for \"%s\"", me, ispec->volName); return 1; } strcat(str, ispec->volName); strcat(str, ":"); pvol = pctx->vol[ispec->volIdx]; strcat(str, airEnumStr(pvol->kind->enm, ispec->item)); } else if (pullSourceProp == ispec->source) { strcat(str, airEnumStr(pullProp, ispec->prop)); } else { biffAddf(PULL, "%s: unexplained source %d", me, ispec->source); return 1; } if ( (pullSourceGage == ispec->source && 1 == pullInfoLen(ispec->info)) || (pullSourceProp == ispec->source && 1 == pullPropLen(ispec->prop)) ) { sprintf(stmp, "%g", ispec->zero); strcat(str, stmp); strcat(str, ":"); sprintf(stmp, "%g", ispec->scale); strcat(str, stmp); } return 0; } teem-1.11.0~svn6057/src/pull/pointPull.c0000664000175000017500000012634512174666232017526 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" /* ** HEY: this has to be threadsafe, at least threadsafe when there ** are no errors, because this can now be called from multiple ** tasks during population control */ pullPoint * pullPointNew(pullContext *pctx) { static const char me[]="pullPointNew"; pullPoint *pnt; unsigned int ii; size_t pntSize; pullPtrPtrUnion pppu; if (!pctx) { biffAddf(PULL, "%s: got NULL pointer", me); return NULL; } if (!pctx->infoTotalLen) { biffAddf(PULL, "%s: can't allocate points w/out infoTotalLen set\n", me); return NULL; } /* Allocate the pullPoint so that it has pctx->infoTotalLen doubles. The pullPoint declaration has info[1], hence the "- 1" below */ pntSize = sizeof(pullPoint) + sizeof(double)*(pctx->infoTotalLen - 1); pnt = AIR_CAST(pullPoint *, calloc(1, pntSize)); if (!pnt) { biffAddf(PULL, "%s: couldn't allocate point (info len %u)\n", me, pctx->infoTotalLen - 1); return NULL; } pnt->idtag = pctx->idtagNext++; pnt->idCC = 0; pnt->neighPoint = NULL; pnt->neighPointNum = 0; pppu.points = &(pnt->neighPoint); pnt->neighPointArr = airArrayNew(pppu.v, &(pnt->neighPointNum), sizeof(pullPoint *), PULL_POINT_NEIGH_INCR); pnt->neighPointArr->noReallocWhenSmaller = AIR_TRUE; pnt->neighDistMean = 0; ELL_10V_ZERO_SET(pnt->neighCovar); pnt->stability = 0.0; #if PULL_TANCOVAR ELL_6V_ZERO_SET(pnt->neighTanCovar); #endif pnt->neighInterNum = 0; pnt->stuckIterNum = 0; #if PULL_PHIST pnt->phist = NULL; pnt->phistNum = 0; pnt->phistArr = airArrayNew(AIR_CAST(void**, &(pnt->phist)), &(pnt->phistNum), 5*sizeof(double), 32); #endif pnt->status = 0; ELL_4V_SET(pnt->pos, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); pnt->energy = AIR_NAN; ELL_4V_SET(pnt->force, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); pnt->stepEnergy = pctx->sysParm.stepInitial; pnt->stepConstr = pctx->sysParm.stepInitial; for (ii=0; iiinfoTotalLen; ii++) { pnt->info[ii] = AIR_NAN; } return pnt; } pullPoint * pullPointNix(pullPoint *pnt) { pnt->neighPointArr = airArrayNuke(pnt->neighPointArr); #if PULL_PHIST pnt->phistArr = airArrayNuke(pnt->phistArr); #endif airFree(pnt); return NULL; } #if PULL_PHIST void _pullPointHistInit(pullPoint *point) { airArrayLenSet(point->phistArr, 0); return; } void _pullPointHistAdd(pullPoint *point, int cond) { unsigned int phistIdx; phistIdx = airArrayLenIncr(point->phistArr, 1); ELL_4V_COPY(point->phist + 5*phistIdx, point->pos); (point->phist + 5*phistIdx)[3] = 1.0; (point->phist + 5*phistIdx)[4] = cond; return; } #endif /* ** HEY: there should be something like a "map" over all the points, ** which could implement all these redundant functions */ unsigned int pullPointNumberFilter(const pullContext *pctx, unsigned int idtagMin, unsigned int idtagMax) { unsigned int binIdx, pointNum; const pullBin *bin; const pullPoint *point; pointNum = 0; for (binIdx=0; binIdxbinNum; binIdx++) { unsigned int pointIdx; bin = pctx->bin + binIdx; if (0 == idtagMin && 0 == idtagMax) { pointNum += bin->pointNum; } else { for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; pointNum += (idtagMin <= point->idtag && (0 == idtagMax || point->idtag <= idtagMax)); } } } return pointNum; } unsigned int pullPointNumber(const pullContext *pctx) { return pullPointNumberFilter(pctx, 0, 0); } double _pullEnergyTotal(const pullContext *pctx) { unsigned int binIdx, pointIdx; const pullBin *bin; const pullPoint *point; double sum; sum = 0; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; sum += point->energy; } } return sum; } void _pullPointStepEnergyScale(pullContext *pctx, double scale) { unsigned int binIdx, pointIdx; const pullBin *bin; pullPoint *point; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; point->stepEnergy *= scale; point->stepEnergy = AIR_MIN(point->stepEnergy, _PULL_STEP_ENERGY_MAX); } } return; } double _pullStepInterAverage(const pullContext *pctx) { unsigned int binIdx, pointIdx, pointNum; const pullBin *bin; const pullPoint *point; double sum, avg; sum = 0; pointNum = 0; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; pointNum += bin->pointNum; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; sum += point->stepEnergy; } } avg = (!pointNum ? AIR_NAN : sum/pointNum); return avg; } /* ^^^ vvv HEY HEY HEY: COPY + PASTE COPY + PASTE COPY + PASTE */ double _pullStepConstrAverage(const pullContext *pctx) { unsigned int binIdx, pointIdx, pointNum; const pullBin *bin; const pullPoint *point; double sum, avg; sum = 0; pointNum = 0; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; pointNum += bin->pointNum; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; sum += point->stepConstr; } } avg = (!pointNum ? AIR_NAN : sum/pointNum); return avg; } /* ** convenience function for learning a scalar AND its gradient or hessian ** ** NOTE: this is where pullInfoSeedThresh and pullInfoLiveThresh are ** adjusted according to sysParm.theta (kind of a hack) */ double pullPointScalar(const pullContext *pctx, const pullPoint *point, int sclInfo, /* output */ double grad[3], double hess[9]) { static const char me[]="pullPointScalar"; double scl; const pullInfoSpec *ispec; int gradInfo[1+PULL_INFO_MAX] = { 0, /* pullInfoUnknown */ 0, /* pullInfoTensor */ 0, /* pullInfoTensorInverse */ 0, /* pullInfoHessian */ pullInfoInsideGradient, /* pullInfoInside */ 0, /* pullInfoInsideGradient */ pullInfoHeightGradient, /* pullInfoHeight */ 0, /* pullInfoHeightGradient */ 0, /* pullInfoHeightHessian */ 0, /* pullInfoHeightLaplacian */ 0, /* pullInfoSeedPreThresh */ 0, /* pullInfoSeedThresh */ 0, /* pullInfoLiveThresh */ 0, /* pullInfoLiveThresh2 */ 0, /* pullInfoLiveThresh3 */ 0, /* pullInfoTangent1 */ 0, /* pullInfoTangent2 */ 0, /* pullInfoNegativeTangent1 */ 0, /* pullInfoNegativeTangent2 */ pullInfoIsovalueGradient, /* pullInfoIsovalue */ 0, /* pullInfoIsovalueGradient */ 0, /* pullInfoIsovalueHessian */ 0, /* pullInfoStrength */ }; int hessInfo[1+PULL_INFO_MAX] = { 0, /* pullInfoUnknown */ 0, /* pullInfoTensor */ 0, /* pullInfoTensorInverse */ 0, /* pullInfoHessian */ 0, /* pullInfoInside */ 0, /* pullInfoInsideGradient */ pullInfoHeightHessian, /* pullInfoHeight */ 0, /* pullInfoHeightGradient */ 0, /* pullInfoHeightHessian */ 0, /* pullInfoHeightLaplacian */ 0, /* pullInfoSeedPreThresh */ 0, /* pullInfoSeedThresh */ 0, /* pullInfoLiveThresh */ 0, /* pullInfoLiveThresh2 */ 0, /* pullInfoLiveThresh3 */ 0, /* pullInfoTangent1 */ 0, /* pullInfoTangent2 */ 0, /* pullInfoNegativeTangent1 */ 0, /* pullInfoNegativeTangent2 */ pullInfoIsovalueHessian, /* pullInfoIsovalue */ 0, /* pullInfoIsovalueGradient */ 0, /* pullInfoIsovalueHessian */ 0, /* pullInfoStrength */ }; const unsigned int *infoIdx; infoIdx = pctx->infoIdx; ispec = pctx->ispec[sclInfo]; /* NB: this "scl" is not scale-space scale; its the scaling of the scalar. this is getting confusing ... */ scl = point->info[infoIdx[sclInfo]]; scl = (scl - ispec->zero)*ispec->scale; if (0 && _pullVerbose) { if (pullInfoSeedThresh == sclInfo) { printf("!%s: seed thresh (%g - %g)*%g == %g\n", me, point->info[infoIdx[sclInfo]], ispec->zero, ispec->scale, scl); } } /* HEY: this logic is confused and the implementation is confused; this should be removed before release */ if (pullInfoLiveThresh == sclInfo || pullInfoSeedThresh == sclInfo) { scl -= (pctx->sysParm.theta)*(point->pos[3])*(point->pos[3]); } if (0 && _pullVerbose) { if (pullInfoSeedThresh == sclInfo) { printf("!%s: ---> w/ theta %g -> %g\n", me, pctx->sysParm.theta, scl); } } /* learned: this wasn't thought through: the idea was that the height *laplacian* answer should be transformed by the *height* zero and scale. scale might make sense, but not zero. This cost a few hours of tracking down the fact that the first zero-crossing detection phase of the lapl constraint was failing because the laplacian was vacillating around hspec->zero, not 0.0 . . . if (pullInfoHeightLaplacian == sclInfo) { const pullInfoSpec *hspec; hspec = pctx->ispec[pullInfoHeight]; scl = (scl - hspec->zero)*hspec->scale; } else { scl = (scl - ispec->zero)*ispec->scale; } */ /* printf("%s = (%g - %g)*%g = %g*%g = %g = %g\n", airEnumStr(pullInfo, sclInfo), point->info[infoIdx[sclInfo]], ispec->zero, ispec->scale, point->info[infoIdx[sclInfo]] - ispec->zero, ispec->scale, (point->info[infoIdx[sclInfo]] - ispec->zero)*ispec->scale, scl); */ if (grad && gradInfo[sclInfo]) { const double *ptr = point->info + infoIdx[gradInfo[sclInfo]]; ELL_3V_SCALE(grad, ispec->scale, ptr); } if (hess && hessInfo[sclInfo]) { const double *ptr = point->info + infoIdx[hessInfo[sclInfo]]; ELL_3M_SCALE(hess, ispec->scale, ptr); } return scl; } int pullProbe(pullTask *task, pullPoint *point) { static const char me[]="pullProbe"; unsigned int ii, gret=0; int edge; /* fprintf(stderr, "!%s: task->probeSeedPreThreshOnly = %d\n", me, task->probeSeedPreThreshOnly); */ #if 0 static int opened=AIR_FALSE; static FILE *flog; #endif #if 0 static int logIdx=0, logDone=AIR_FALSE, logStarted=AIR_FALSE; static Nrrd *nlog; static double *log=NULL; if (!logStarted) { if (81 == point->idtag) { printf("\n\n%s: ###### HELLO begin logging . . .\n\n\n", me); /* knowing the logIdx at the end of logging . . . */ nlog = nrrdNew(); nrrdMaybeAlloc_va(nlog, nrrdTypeDouble, 2, AIR_CAST(size_t, 25), AIR_CAST(size_t, 2754)); log = AIR_CAST(double*, nlog->data); logStarted = AIR_TRUE; } } #endif if (!ELL_4V_EXISTS(point->pos)) { fprintf(stderr, "%s: pnt %u non-exist pos (%g,%g,%g,%g)\n\n!!!\n\n\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); biffAddf(PULL, "%s: pnt %u non-exist pos (%g,%g,%g,%g)\n\n!!!\n\n\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); return 1; /* can't probe, but make it go away as quickly as possible */ /* ELL_4V_SET(point->pos, 0, 0, 0, 0); point->status |= PULL_STATUS_NIXME_BIT; return 0; */ } if (task->pctx->verbose > 3) { printf("%s: hello; probing %u volumes\n", me, task->pctx->volNum); } edge = AIR_FALSE; task->pctx->count[pullCountProbe] += 1; for (ii=0; iipctx->volNum; ii++) { pullVolume *vol; vol = task->vol[ii]; if (task->probeSeedPreThreshOnly && !(vol->forSeedPreThresh)) { /* we're here *only* to probe SeedPreThresh, and this volume isn't used for that */ continue; } if (task->pctx->iter && vol->seedOnly) { /* its after the 1st iteration (#0), and this vol is only for seeding */ continue; } /* HEY should task->vol[ii]->scaleNum be the using-scale-space test? */ if (!task->vol[ii]->ninScale) { /* if (81 == point->idtag) { printf("%s: probing vol[%u] @ %g %g %g\n", me, ii, point->pos[0], point->pos[1], point->pos[2]); } */ gret = gageProbeSpace(task->vol[ii]->gctx, point->pos[0], point->pos[1], point->pos[2], AIR_FALSE /* index-space */, AIR_TRUE /* clamp */); } else { if (task->pctx->verbose > 3) { printf("%s: vol[%u] has scale (%u)-> " "gageStackProbeSpace(%p) (v %d)\n", me, ii, task->vol[ii]->scaleNum, AIR_VOIDP(task->vol[ii]->gctx), task->vol[ii]->gctx->verbose); } /* if (81 == point->idtag) { printf("%s: probing vol[%u] @ %g %g %g %g\n", me, ii, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); } */ gret = gageStackProbeSpace(task->vol[ii]->gctx, point->pos[0], point->pos[1], point->pos[2], (task->pctx->flag.scaleIsTau ? gageSigOfTau(point->pos[3]) : point->pos[3]), AIR_FALSE /* index-space */, AIR_TRUE /* clamp */); } if (gret) { biffAddf(PULL, "%s: probe failed on vol %u/%u: (%d) %s", me, ii, task->pctx->volNum, task->vol[ii]->gctx->errNum, task->vol[ii]->gctx->errStr); return 1; } /* if (!edge && AIR_ABS(point->pos[1] - 67) < 1 && AIR_ABS(point->pos[2] - 67) < 1 && point->pos[3] > 3.13 && !!task->vol[ii]->gctx->edgeFrac) { fprintf(stderr, "!%s(%u @ %g,%g,%g,%g): " "vol[%u]->gctx->edgeFrac %g => edge bit on\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3], ii, task->vol[ii]->gctx->edgeFrac); } */ edge |= !!task->vol[ii]->gctx->edgeFrac; } if (edge) { point->status |= PULL_STATUS_EDGE_BIT; } else { point->status &= ~PULL_STATUS_EDGE_BIT; } /* maybe is a little stupid to have the infos indexed this way, since it means that we always have to loop through all indices, but at least the compiler can unroll it . . . */ for (ii=0; ii<=PULL_INFO_MAX; ii++) { unsigned int alen, aidx; const pullInfoSpec *ispec; ispec = task->pctx->ispec[ii]; if (ispec) { alen = _pullInfoLen[ii]; aidx = task->pctx->infoIdx[ii]; if (pullSourceGage == ispec->source) { _pullInfoCopy[alen](point->info + aidx, task->ans[ii]); /* if (81 == point->idtag) { pullVolume *vol; pullInfoSpec *isp; isp = task->pctx->ispec[ii]; vol = task->pctx->vol[isp->volIdx]; if (1 == alen) { printf("!%s: info[%u] %s: %s(\"%s\") = %g\n", me, ii, airEnumStr(pullInfo, ii), airEnumStr(vol->kind->enm, isp->item), vol->name, task->ans[ii][0]); } else { unsigned int vali; printf("!%s: info[%u] %s: %s(\"%s\") =\n", me, ii, airEnumStr(pullInfo, ii), airEnumStr(vol->kind->enm, isp->item), vol->name); for (vali=0; valians[ii][vali]); } } } */ } else if (pullSourceProp == ispec->source) { switch (ispec->prop) { case pullPropIdtag: point->info[aidx] = point->idtag; break; case pullPropIdCC: point->info[aidx] = point->idCC; break; case pullPropEnergy: point->info[aidx] = point->energy; break; case pullPropStepEnergy: point->info[aidx] = point->stepEnergy; break; case pullPropStepConstr: point->info[aidx] = point->stepConstr; break; case pullPropStuck: point->info[aidx] = ((point->status & PULL_STATUS_STUCK_BIT) ? point->stuckIterNum : 0); break; case pullPropPosition: ELL_4V_COPY(point->info + aidx, point->pos); break; case pullPropForce: ELL_4V_COPY(point->info + aidx, point->force); break; case pullPropNeighDistMean: point->info[aidx] = point->neighDistMean; break; case pullPropScale: point->info[aidx] = point->pos[3]; break; case pullPropNeighCovar: ELL_10V_COPY(point->info + aidx, point->neighCovar); break; case pullPropNeighCovar7Ten: TEN_T_SET(point->info + aidx, 1.0f, point->neighCovar[0], point->neighCovar[1], point->neighCovar[2], point->neighCovar[4], point->neighCovar[5], point->neighCovar[7]); break; #if PULL_TANCOVAR case pullPropNeighTanCovar: TEN_T_SET(point->info + aidx, 1.0f, point->neighTanCovar[0], point->neighTanCovar[1], point->neighTanCovar[2], point->neighTanCovar[3], point->neighTanCovar[4], point->neighTanCovar[5]); break; #endif case pullPropStability: point->info[aidx] = point->stability; break; default: break; } } } } #if 0 if (logStarted && !logDone) { unsigned int ai; /* the actual logging */ log[0] = point->idtag; ELL_4V_COPY(log + 1, point->pos); for (ai=0; ai<20; ai++) { log[5 + ai] = point->info[ai]; } log += nlog->axis[0].size; logIdx++; if (1 == task->pctx->iter && 81 == point->idtag) { printf("\n\n%s: ###### OKAY done logging (%u). . .\n\n\n", me, logIdx); nrrdSave("probelog.nrrd", nlog, NULL); nlog = nrrdNuke(nlog); logDone = AIR_TRUE; } } #endif #if 0 if (!opened) { flog = fopen("flog.txt", "w"); opened = AIR_TRUE; } if (opened) { fprintf(flog, "%s(%u): spthr(%g,%g,%g,%g) = %g\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3], point->info[task->pctx->infoIdx[pullInfoSeedPreThresh]]); } #endif return 0; } static int _threshFail(const pullContext *pctx, const pullPoint *point, int info) { /* static const char me[]="_threshFail"; */ double val; int ret; if (pctx->ispec[info]) { val = pullPointScalar(pctx, point, info, NULL, NULL); ret = (val < 0); /* fprintf(stderr, "%s(%s): val=%g -> ret=%d\n", me, airEnumStr(pullInfo, info), val, ret); */ } else { ret = AIR_FALSE; } return ret; } int pullPointInitializePerVoxel(const pullContext *pctx, const unsigned int pointIdx, pullPoint *point, pullVolume *scaleVol, /* output */ int *createFailP) { static const char me[]="pullPointInitializePerVoxel"; unsigned int vidx[3], pix; double iPos[3]; airRandMTState *rng; pullVolume *seedVol; gageShape *seedShape; int reject, rejectEdge, constrFail; unsigned int k; seedVol = pctx->vol[pctx->ispec[pullInfoSeedThresh]->volIdx]; seedShape = seedVol->gctx->shape; rng = pctx->task[0]->rng; /* Obtain voxel and indices from pointIdx */ /* axis ordering for this is x, y, z, scale */ pix = pointIdx; if (pctx->initParm.pointPerVoxel > 0) { pix /= pctx->initParm.pointPerVoxel; } else { pix *= -pctx->initParm.pointPerVoxel; } vidx[0] = pix % seedShape->size[0]; pix = (pix - vidx[0])/seedShape->size[0]; vidx[1] = pix % seedShape->size[1]; pix = (pix - vidx[1])/seedShape->size[1]; if (pctx->initParm.ppvZRange[0] <= pctx->initParm.ppvZRange[1]) { unsigned int zrn; zrn = pctx->initParm.ppvZRange[1] - pctx->initParm.ppvZRange[0] + 1; vidx[2] = (pix % zrn) + pctx->initParm.ppvZRange[0]; pix = (pix - (pix % zrn))/zrn; } else { vidx[2] = pix % seedShape->size[2]; pix = (pix - vidx[2])/seedShape->size[2]; } for (k=0; k<=2; k++) { iPos[k] = vidx[k] + pctx->initParm.jitter*(airDrandMT_r(rng)-0.5); } gageShapeItoW(seedShape, point->pos, iPos); if (pctx->flag.zeroZ) { point->pos[2] = 0.0; } if (0 && _pullVerbose) { printf("!%s: pointIdx %u -> vidx %u %u %u (%u)\n" " -> iPos %g %g %g -> wPos %g %g %g\n", me, pointIdx, vidx[0], vidx[1], vidx[2], pix, iPos[0], iPos[1], iPos[2], point->pos[0], point->pos[1], point->pos[2]); } /* Compute sigma coordinate from pix */ if (pctx->haveScale) { int outside; double aidx, bidx; /* pix should already be integer in [0, pctx->samplesAlongScaleNum-1)]. */ aidx = pix + pctx->initParm.jitter*(airDrandMT_r(rng)-0.5); bidx = AIR_AFFINE(-0.5, aidx, pctx->initParm.samplesAlongScaleNum-0.5, 0.0, scaleVol->scaleNum-1); point->pos[3] = gageStackItoW(scaleVol->gctx, bidx, &outside); if (pctx->flag.scaleIsTau) { point->pos[3] = gageTauOfSig(point->pos[3]); } if (0 && _pullVerbose) { printf("!%s(%u): pix %u -> a %g b %g -> wpos %g\n", me, point->idtag, pix, aidx, bidx, point->pos[3]); } } else { point->pos[3] = 0; } if (pctx->ispec[pullInfoSeedPreThresh]) { /* we first do a special-purpose probe just for SeedPreThresh */ pctx->task[0]->probeSeedPreThreshOnly = AIR_TRUE; if (pullProbe(pctx->task[0], point)) { biffAddf(PULL, "%s: pre-probing pointIdx %u of world", me, pointIdx); return 1; } pctx->task[0]->probeSeedPreThreshOnly = AIR_FALSE; if (_threshFail(pctx, point, pullInfoSeedPreThresh)) { reject = AIR_TRUE; /* HEY! this obviously need to be re-written */ goto finish; } } /* else, we didn't have a SeedPreThresh, or we did, and we passed it. Now we REDO the probe, including possibly re-learning SeedPreThresh, which is silly, but the idea is that this is a small price compared to what has been saved by avoiding all the gageProbe()s on volumes unrelated to SeedPreThresh */ if (pullProbe(pctx->task[0], point)) { biffAddf(PULL, "%s: probing pointIdx %u of world", me, pointIdx); return 1; } constrFail = AIR_FALSE; reject = AIR_FALSE; /* Check we pass pre-threshold */ if (!reject) reject |= _threshFail(pctx, point, pullInfoSeedPreThresh); if (!pctx->flag.constraintBeforeSeedThresh) { /* we should be guaranteed to have a seed thresh info */ if (!reject) reject |= _threshFail(pctx, point, pullInfoSeedThresh); if (pctx->initParm.liveThreshUse) { if (!reject) reject |= _threshFail(pctx, point, pullInfoLiveThresh); if (!reject) reject |= _threshFail(pctx, point, pullInfoLiveThresh2); if (!reject) reject |= _threshFail(pctx, point, pullInfoLiveThresh3); } } if (!reject && pctx->constraint) { if (_pullConstraintSatisfy(pctx->task[0], point, 10*_PULL_CONSTRAINT_TRAVEL_MAX, &constrFail)) { biffAddf(PULL, "%s: on pnt %u", me, pointIdx); return 1; } reject |= constrFail; /* post constraint-satisfaction, we certainly have to assert thresholds */ if (!reject) reject |= _threshFail(pctx, point, pullInfoSeedThresh); if (pctx->initParm.liveThreshUse) { if (!reject) reject |= _threshFail(pctx, point, pullInfoLiveThresh); if (!reject) reject |= _threshFail(pctx, point, pullInfoLiveThresh2); if (!reject) reject |= _threshFail(pctx, point, pullInfoLiveThresh3); } if (pctx->flag.nixAtVolumeEdgeSpace && (point->status & PULL_STATUS_EDGE_BIT)) { rejectEdge = AIR_TRUE; } else { rejectEdge = AIR_FALSE; } reject |= rejectEdge; if (pctx->verbose > 1) { fprintf(stderr, "%s(%u): constr %d, seed %d, thresh %d %d %d, edge %d\n", me, point->idtag, constrFail, _threshFail(pctx, point, pullInfoSeedThresh), _threshFail(pctx, point, pullInfoLiveThresh), _threshFail(pctx, point, pullInfoLiveThresh2), _threshFail(pctx, point, pullInfoLiveThresh3), rejectEdge); } } else { constrFail = AIR_FALSE; } finish: /* Gather consensus */ if (reject) { *createFailP = AIR_TRUE; } else { *createFailP = AIR_FALSE; } return 0; } static void _pullUnitToWorld(const pullContext *pctx, const pullVolume *scaleVol, double wrld[4], const double unit[4]) { /* static const char me[]="_pullUnitToWorld"; */ wrld[0] = AIR_AFFINE(0.0, unit[0], 1.0, pctx->bboxMin[0], pctx->bboxMax[0]); wrld[1] = AIR_AFFINE(0.0, unit[1], 1.0, pctx->bboxMin[1], pctx->bboxMax[1]); wrld[2] = AIR_AFFINE(0.0, unit[2], 1.0, pctx->bboxMin[2], pctx->bboxMax[2]); if (pctx->haveScale) { double sridx; int outside; sridx = AIR_AFFINE(0.0, unit[3], 1.0, 0, scaleVol->scaleNum-1); wrld[3] = gageStackItoW(scaleVol->gctx, sridx, &outside); if (pctx->flag.scaleIsTau) { wrld[3] = gageTauOfSig(wrld[3]); } } else { wrld[3] = 0.0; } /* fprintf(stderr, "!%s: (%g,%g,%g,%g) --> (%g,%g,%g,%g)\n", me, unit[0], unit[1], unit[2], unit[3], wrld[0], wrld[1], wrld[2], wrld[3]); */ return; } int pullPointInitializeRandomOrHalton(pullContext *pctx, const unsigned int pointIdx, pullPoint *point, pullVolume *scaleVol) { static const char me[]="pullPointInitializeRandomOrHalton"; int reject, verbo; airRandMTState *rng; unsigned int threshFailCount = 0, spthreshFailCount = 0, constrFailCount = 0; rng = pctx->task[0]->rng; do { double rpos[4]; reject = AIR_FALSE; _pullPointHistInit(point); /* Populate tentative random point */ if (pullInitMethodHalton == pctx->initParm.method) { airHalton(rpos, (pointIdx + threshFailCount + constrFailCount + pctx->haltonOffset + pctx->initParm.haltonStartIndex), airPrimeList, 4); /* fprintf(stderr, "!%s(%u/%u): halton(%u=%u+%u+%u+%u+%u) => %g %g %g %g\n", me, pointIdx, pctx->idtagNext, (pointIdx + threshFailCount + constrFailCount + pctx->haltonOffset + pctx->initParm.haltonStartIndex), pointIdx, threshFailCount, constrFailCount, pctx->haltonOffset, pctx->initParm.haltonStartIndex, rpos[0], rpos[1], rpos[2], rpos[3]); */ /* fprintf(stderr, "%g %g %g %g ", rpos[0], rpos[1], rpos[2], rpos[3]); */ } else { ELL_3V_SET(rpos, airDrandMT_r(rng), airDrandMT_r(rng), airDrandMT_r(rng)); if (pctx->haveScale) { rpos[3] = airDrandMT_r(rng); } } _pullUnitToWorld(pctx, scaleVol, point->pos, rpos); if (pctx->flag.zeroZ) { point->pos[2] = 0.0; } /* verbo = (AIR_ABS(-0.246015 - point->pos[0]) < 0.1 && AIR_ABS(-144.78 - point->pos[0]) < 0.1 && AIR_ABS(-85.3813 - point->pos[0]) < 0.1); */ verbo = AIR_FALSE; if (verbo) { fprintf(stderr, "%s: verbo on for point %u at %g %g %g %g\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); } _pullPointHistAdd(point, pullCondOld); /* HEY copy and paste */ if (pctx->ispec[pullInfoSeedPreThresh]) { /* we first do a special-purpose probe just for SeedPreThresh */ pctx->task[0]->probeSeedPreThreshOnly = AIR_TRUE; if (pullProbe(pctx->task[0], point)) { biffAddf(PULL, "%s: pre-probing pointIdx %u of world", me, pointIdx); return 1; } pctx->task[0]->probeSeedPreThreshOnly = AIR_FALSE; if (_threshFail(pctx, point, pullInfoSeedPreThresh)) { threshFailCount++; spthreshFailCount++; reject = AIR_TRUE; goto reckoning; } } if (pullProbe(pctx->task[0], point)) { biffAddf(PULL, "%s: probing pointIdx %u of world", me, pointIdx); return 1; } /* Check we pass pre-threshold */ #define THRESH_TEST(INFO) \ if (pctx->ispec[INFO] && _threshFail(pctx, point, INFO)) { \ threshFailCount++; \ reject = AIR_TRUE; \ goto reckoning; \ } if (!pctx->flag.constraintBeforeSeedThresh) { THRESH_TEST(pullInfoSeedThresh); if (pctx->initParm.liveThreshUse) { THRESH_TEST(pullInfoLiveThresh); THRESH_TEST(pullInfoLiveThresh2); THRESH_TEST(pullInfoLiveThresh3); } } if (pctx->constraint) { int constrFail; if (_pullConstraintSatisfy(pctx->task[0], point, _PULL_CONSTRAINT_TRAVEL_MAX, &constrFail)) { biffAddf(PULL, "%s: trying constraint on point %u", me, pointIdx); return 1; } if (constrFail) { constrFailCount++; reject = AIR_TRUE; goto reckoning; } /* post constraint-satisfaction, we certainly have to assert thresholds */ THRESH_TEST(pullInfoSeedThresh); if (pctx->initParm.liveThreshUse) { THRESH_TEST(pullInfoLiveThresh); THRESH_TEST(pullInfoLiveThresh2); THRESH_TEST(pullInfoLiveThresh3); } } reckoning: if (reject) { if (threshFailCount + constrFailCount >= _PULL_RANDOM_SEED_TRY_MAX) { /* Very bad luck; we've too many times */ biffAddf(PULL, "%s: failed too often (%u times) placing point %u: " "%u fails on thresh (%u on pre-thresh), %u on constr", me, _PULL_RANDOM_SEED_TRY_MAX, pointIdx, threshFailCount, spthreshFailCount, constrFailCount); return 1; } } } while (reject); if (pullInitMethodHalton == pctx->initParm.method) { pctx->haltonOffset += threshFailCount + constrFailCount; } return 0; } int pullPointInitializeGivenPos(pullContext *pctx, const double *posData, const unsigned int pointIdx, pullPoint *point, /* output */ int *createFailP) { static const char me[]="pullPointInitializeGivenPos"; int reject, rejectEdge; /* Copy nrrd point into pullPoint */ ELL_4V_COPY(point->pos, posData + 4*pointIdx); if (pctx->flag.zeroZ) { point->pos[2] = 0.0; } /* if (AIR_ABS(247.828 - point->pos[0]) < 0.1 && AIR_ABS(66.8817 - point->pos[1]) < 0.1 && AIR_ABS(67.0031 - point->pos[2]) < 0.1) { fprintf(stderr, "%s: --------- point %u at %g %g %g %g\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); } */ /* we're dictating positions, but still have to do initial probe, and possibly liveThresholding */ if (pullProbe(pctx->task[0], point)) { biffAddf(PULL, "%s: probing pointIdx %u of npos", me, pointIdx); return 1; } reject = AIR_FALSE; if (pctx->flag.nixAtVolumeEdgeSpace && (point->status & PULL_STATUS_EDGE_BIT)) { rejectEdge = AIR_TRUE; } else { rejectEdge = AIR_FALSE; } reject |= rejectEdge; if (pctx->initParm.liveThreshUse) { if (!reject) reject |= _threshFail(pctx, point, pullInfoLiveThresh); if (!reject) reject |= _threshFail(pctx, point, pullInfoLiveThresh2); if (!reject) reject |= _threshFail(pctx, point, pullInfoLiveThresh3); } /* if (reject) { fprintf(stderr, "!%s(%u): edge %d thresh %d %d %d\n", me, point->idtag, rejectEdge, _threshFail(pctx, point, pullInfoLiveThresh), _threshFail(pctx, point, pullInfoLiveThresh2), _threshFail(pctx, point, pullInfoLiveThresh3)); } */ if (reject) { *createFailP = AIR_TRUE; } else { *createFailP = AIR_FALSE; } return 0; } /* ** _pullPointSetup sets: ** ** This is only called by the master thread ** ** this should set stuff to be like after an update stage and ** just before the rebinning */ int _pullPointSetup(pullContext *pctx) { static const char me[]="_pullPointSetup"; char doneStr[AIR_STRLEN_SMALL]; unsigned int pointIdx, binIdx, tick, pn; pullPoint *point; pullBin *bin; int createFail,added; airArray *mop; Nrrd *npos; pullVolume *seedVol, *scaleVol; gageShape *seedShape; double factor, *posData; unsigned int totalNumPoints, voxNum, ii; /* on using pullBinsPointMaybeAdd: This is used only in the context of constraints; it makes sense to be a little more selective about adding points, so that they aren't completely piled on top of each other. This relies on _PULL_BINNING_MAYBE_ADD_THRESH: its tempting to set this value high, to more aggressively limit the number of points added, but that's really the job of population control, and we can't always guarantee that constraint manifolds will be well-sampled (with respect to pctx->radiusSpace and pctx->radiusScale) to start with */ if (pctx->verbose) { printf("%s: beginning . . . ", me); fflush(stdout); } mop = airMopNew(); switch (pctx->initParm.method) { case pullInitMethodGivenPos: npos = nrrdNew(); airMopAdd(mop, npos, (airMopper)nrrdNuke, airMopAlways); /* even if npos came in as double, we have to copy it */ if (nrrdConvert(npos, pctx->initParm.npos, nrrdTypeDouble)) { biffMovef(PULL, NRRD, "%s: trouble converting npos", me); airMopError(mop); return 1; } posData = AIR_CAST(double *, npos->data); if (pctx->initParm.numInitial || pctx->initParm.pointPerVoxel) { printf("%s: with npos, overriding both numInitial (%u) " "and pointPerVoxel (%d)\n", me, pctx->initParm.numInitial, pctx->initParm.pointPerVoxel); } totalNumPoints = AIR_CAST(unsigned int, npos->axis[1].size); break; case pullInitMethodPointPerVoxel: npos = NULL; posData = NULL; if (pctx->initParm.numInitial && pctx->verbose) { printf("%s: pointPerVoxel %d overrides numInitial (%u)\n", me, pctx->initParm.pointPerVoxel, pctx->initParm.numInitial); } /* Obtain number of voxels */ seedVol = pctx->vol[pctx->ispec[pullInfoSeedThresh]->volIdx]; seedShape = seedVol->gctx->shape; if (pctx->initParm.ppvZRange[0] <= pctx->initParm.ppvZRange[1]) { unsigned int zrn; if (!( pctx->initParm.ppvZRange[0] < seedShape->size[2] && pctx->initParm.ppvZRange[1] < seedShape->size[2] )) { biffAddf(PULL, "%s: ppvZRange[%u,%u] outside volume [0,%u]", me, pctx->initParm.ppvZRange[0], pctx->initParm.ppvZRange[1], seedShape->size[2]-1); airMopError(mop); return 1; } zrn = pctx->initParm.ppvZRange[1] - pctx->initParm.ppvZRange[0] + 1; voxNum = seedShape->size[0]*seedShape->size[1]*zrn; if (pctx->verbose) { printf("%s: vol size %u %u [%u,%u] -> voxNum %u\n", me, seedShape->size[0], seedShape->size[1], pctx->initParm.ppvZRange[0], pctx->initParm.ppvZRange[1], voxNum); } } else { voxNum = seedShape->size[0]*seedShape->size[1]*seedShape->size[2]; if (pctx->verbose) { printf("%s: vol size %u %u %u -> voxNum %u\n", me, seedShape->size[0], seedShape->size[1], seedShape->size[2], voxNum); } } /* Compute total number of points */ if (pctx->initParm.pointPerVoxel > 0) { factor = pctx->initParm.pointPerVoxel; } else { factor = -1.0/pctx->initParm.pointPerVoxel; } if (pctx->haveScale) { unsigned int sasn; sasn = pctx->initParm.samplesAlongScaleNum; totalNumPoints = AIR_CAST(unsigned int, voxNum * factor * sasn); } else { totalNumPoints = AIR_CAST(unsigned int, voxNum * factor); } break; case pullInitMethodRandom: case pullInitMethodHalton: npos = NULL; posData = NULL; totalNumPoints = pctx->initParm.numInitial; break; default: biffAddf(PULL, "%s: pullInitMethod %d not handled!", me, pctx->initParm.method); airMopError(mop); return 1; break; } if (pctx->verbose) { printf("%s: initializing/seeding . . . ", me); fflush(stdout); } /* find first scale volume, if there is one; this is used by some seeders to determine placement along the scale axis */ scaleVol = NULL; for (ii=0; iivolNum; ii++) { if (pctx->vol[ii]->ninScale) { scaleVol = pctx->vol[ii]; break; } } /* Start adding points */ tick = totalNumPoints/1000; point = NULL; for (pointIdx = 0; pointIdx < totalNumPoints; pointIdx++) { int E; if (pctx->verbose) { if (tick < 100 || 0 == pointIdx % tick) { printf("%s", airDoneStr(0, pointIdx, totalNumPoints, doneStr)); fflush(stdout); } } if (pctx->verbose > 5) { printf("\n%s: setting up point = %u/%u\n", me, pointIdx, totalNumPoints); } /* Create point */ if (!point) { point = pullPointNew(pctx); } /* Filling array according to initialization method */ E = 0; switch(pctx->initParm.method) { case pullInitMethodRandom: case pullInitMethodHalton: E = pullPointInitializeRandomOrHalton(pctx, pointIdx, point, scaleVol); createFail = AIR_FALSE; break; case pullInitMethodPointPerVoxel: E = pullPointInitializePerVoxel(pctx, pointIdx, point, scaleVol, &createFail); break; case pullInitMethodGivenPos: E = pullPointInitializeGivenPos(pctx, posData, pointIdx, point, &createFail); break; } if (E) { biffAddf(PULL, "%s: trouble trying point %u (id %u)", me, pointIdx, point->idtag); airMopError(mop); return 1; } if (createFail) { /* We were not successful in creating a point; not an error */ continue; } /* else, the point is ready for binning */ if (pctx->constraint) { if (pullBinsPointMaybeAdd(pctx, point, NULL, &added)) { biffAddf(PULL, "%s: trouble binning point %u", me, point->idtag); airMopError(mop); return 1; } /* if (4523 == point->idtag) { fprintf(stderr, "!%s(%u): ----- added=%d at %g %g %g %g\n", me, point->idtag, added, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); } */ if (added) { point = NULL; } } else { if (pullBinsPointAdd(pctx, point, NULL)) { biffAddf(PULL, "%s: trouble binning point %u", me, point->idtag); airMopError(mop); return 1; } point = NULL; } } /* Done looping through total number of points */ if (pctx->verbose) { printf("%s\n", airDoneStr(0, pointIdx, totalNumPoints, doneStr)); } if (point) { /* we created a new test point, but it was never placed in the volume */ /* so, HACK: undo pullPointNew . . . */ point = pullPointNix(point); pctx->idtagNext -= 1; } /* Final check: do we have any points? */ pn = pullPointNumber(pctx); if (!pn) { char stmp1[AIR_STRLEN_MED], stmp2[AIR_STRLEN_MED]; int guess=AIR_FALSE; sprintf(stmp1, "%s: seeded 0 points", me); if (pctx->ispec[pullInfoSeedThresh]) { guess=AIR_TRUE; sprintf(stmp2, " (? bad seedthresh %g ?)", pctx->ispec[pullInfoSeedThresh]->zero); strcat(stmp1, stmp2); } if (pctx->flag.nixAtVolumeEdgeSpace) { guess=AIR_TRUE; sprintf(stmp2, " (? flag.nixAtVolumeEdgeSpace true ?)"); strcat(stmp1, stmp2); } if (!guess) { sprintf(stmp2, " (no guess as to why)"); strcat(stmp1, stmp2); } biffAddf(PULL, "%s", stmp1); airMopError(mop); return 1; } if (pctx->verbose) { fprintf(stderr, "%s: initialized to %u points (idtagNext = %u)\n", me, pn, pctx->idtagNext); } /* if (1) { Nrrd *ntmp; ntmp = nrrdNew(); pullOutputGet(ntmp, NULL, NULL, NULL, 0.0, pctx); nrrdSave("pos-in.nrrd", ntmp, NULL); nrrdNuke(ntmp); } */ pctx->tmpPointPtr = AIR_CAST(pullPoint **, calloc(pn, sizeof(pullPoint*))); pctx->tmpPointPerm = AIR_CAST(unsigned int *, calloc(pn, sizeof(unsigned int))); if (!( pctx->tmpPointPtr && pctx->tmpPointPerm )) { biffAddf(PULL, "%s: couldn't allocate tmp buffers %p %p", me, AIR_VOIDP(pctx->tmpPointPtr), AIR_VOIDP(pctx->tmpPointPerm)); airMopError(mop); return 1; } pctx->tmpPointNum = pn; /* now that all points have been added, set their energy to help inspect initial state */ for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; point->energy = _pullPointEnergyTotal(pctx->task[0], bin, point, /* ignoreImage */ !pctx->haveScale, point->force); } } if (pctx->verbose) { printf("%s: all done. ", me); fflush(stdout); } airMopOkay(mop); return 0; } void _pullPointFinish(pullContext *pctx) { airFree(pctx->tmpPointPtr); airFree(pctx->tmpPointPerm); return ; } teem-1.11.0~svn6057/src/pull/test/0000775000175000017500000000000012203513757016334 5ustar domibeldomibelteem-1.11.0~svn6057/src/pull/test/eparse.c0000664000175000017500000000503112165631065017756 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../pull.h" char *info = ("Tests parsing of energy, and its methods."); int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt=NULL; airArray *mop; pullEnergySpec *ensp; unsigned int pi, xi, nn; double xx, supp; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "energy", "spec", airTypeOther, 1, 1, &ensp, NULL, "specification of force function to use", NULL, NULL, pullHestEnergySpec); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); fprintf(stderr, "%s: parsed energy \"%s\", with %u parms.\n", me, ensp->energy->name, ensp->energy->parmNum); for (pi=0; pienergy->parmNum; pi++) { fprintf(stderr, "parm[%u] == %g\n", pi, ensp->parm[pi]); } fprintf(stderr, "\n"); nn = 600; supp = 1.0; for (xi=1; xienergy->eval(&dummy, x1, ensp->parm); e0 = ensp->energy->eval(&dummy, x0, ensp->parm); ee = ensp->energy->eval(&ff, xx, ensp->parm); printf("%g %g %g %g\n", xx, ee, ff, (e1 - e0)/(x1 - x0)); } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/pull/test/circ.c0000664000175000017500000002246112165631065017425 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../pull.h" #define TOP_MARGIN 3 char *info = ("Sees if \"adhoc\" is inefficient."); enum { stepStyleUnknown, /* 0 */ stepStyleSmall, /* 1 */ stepStyleDescent, /* 2 */ stepStyleRandomUniform, /* 3 */ stepStyleRandomCool, /* 4 */ stepStyleLast }; #define STEP_STYLE_MAX 4 const char * _stepStyleStr[STEP_STYLE_MAX+1] = { "(unknown_style)", "small", "descent", "runiform", "rcool" }; const airEnum _stepStyle = { "step style", STEP_STYLE_MAX, _stepStyleStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const stepStyle = &_stepStyle; static double tpimod(double phi) { if (phi > 0) { phi = fmod(phi, 2*AIR_PI); } else { phi = 2*AIR_PI + fmod(-phi, 2*AIR_PI); } return phi; } static double enrPairwise(double *grad, double me, double she, pullEnergySpec *ensp, double radius) { double ad, td, rr, *parm, enr, denr, (*eval)(double *, double, const double parm[PULL_ENERGY_PARM_NUM]); ad = AIR_ABS(me - she); if (0 == ad) { if (grad) { *grad = 0; } return 0; } td = AIR_ABS(me - (she + 2*AIR_PI)); if (td < ad) { she += 2*AIR_PI; ad = td; } else { td = AIR_ABS(me - (she - 2*AIR_PI)); if (td < ad) { she -= 2*AIR_PI; ad = td; } } rr = ad/radius; if (rr > 1) { if (grad) { *grad = 0; } return 0; } parm = ensp->parm; eval = ensp->energy->eval; enr = eval(&denr, rr, parm); if (grad) { *grad = denr * airSgn(me - she)/radius; } return enr; } static double enrSingle(double *gradP, double me, unsigned int meIdx, double *pos, unsigned int posNum, pullEnergySpec *ensp, double radius) { unsigned int ii; double enr, gg, grad; enr = 0; grad = 0; me = tpimod(me); for (ii=0; ii enr0; if (badstep) { step[ii] *= backoff; } } while (badstep); step[ii] *= creepup; me = tpos; break; case stepStyleRandomUniform: tpos = AIR_AFFINE(0.0, airDrandMT(), 1.0, 0.0, 2*AIR_PI); enr1 = enrSingle(NULL, tpos, ii, posOut, pntNum, ensp, radius); if (enr1 < enr0) { me = tpos; } break; case stepStyleRandomCool: airNormalRand(&tpos, NULL); tpos = me + tpos*cool; enr1 = enrSingle(NULL, tpos, ii, posOut, pntNum, ensp, radius); if (enr1 < enr0) { me = tpos; } break; } posOut[ii] = tpimod(me); } return; } int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt=NULL; airArray *mop; char *outS; pullEnergySpec *ensp, *enspCharge; unsigned int pntNum, pntIdx, iter, iterMax, rngSeed, posNum, coolHalfLife; double stdvMin, radius, *pos, *step, backoff, creepup, stepInitial; airArray *posArr; Nrrd *npos; int sstyle, verbose; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "0", "verbosity"); hestOptAdd(&hopt, "energy", "spec", airTypeOther, 1, 1, &ensp, NULL, "specification of force function to use", NULL, NULL, pullHestEnergySpec); hestOptAdd(&hopt, "ss", "step style", airTypeEnum, 1, 1, &sstyle, NULL, "minimization step style", NULL, stepStyle); hestOptAdd(&hopt, "rad", "radius", airTypeDouble, 1, 1, &radius, NULL, "radius of particle"); hestOptAdd(&hopt, "esm", "eng stdv min", airTypeDouble, 1, 1, &stdvMin, "0.05", "minimum stdv of particle energies"); hestOptAdd(&hopt, "bo", "backoff", airTypeDouble, 1, 1, &backoff, "0.1", "backoff in gradient descent"); hestOptAdd(&hopt, "step", "step", airTypeDouble, 1, 1, &stepInitial, "1.0", "initial step size in gradient descent"); hestOptAdd(&hopt, "cu", "creepup", airTypeDouble, 1, 1, &creepup, "1.1", "creepup in gradient descent"); hestOptAdd(&hopt, "chl", "cool half life", airTypeUInt, 1, 1, &coolHalfLife, "20", "cool half life"); hestOptAdd(&hopt, "np", "# part", airTypeUInt, 1, 1, &pntNum, "42", "# of particles in simulation"); hestOptAdd(&hopt, "maxi", "max iter", airTypeUInt, 1, 1, &iterMax, "1000", "max number of iterations"); hestOptAdd(&hopt, "seed", "seed", airTypeUInt, 1, 1, &rngSeed, "42", "random number generator seed"); hestOptAdd(&hopt, "o", "out", airTypeString, 1, 1, &outS, "-", "output filename"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); airSrandMT(rngSeed); posNum = 0; posArr = airArrayNew(AIR_CAST(void **, &pos), &posNum, pntNum*sizeof(double), iterMax/10); airMopAdd(mop, posArr, (airMopper)airArrayNuke, airMopAlways); step = AIR_CALLOC(pntNum, double); airMopAdd(mop, step, airFree, airMopAlways); for (pntIdx=0; pntIdx 1) { fprintf(stderr, "%s: iter %u stdv %g cool %g\n", me, iter, stdv, cool); } if (stdv < stdvMin) { fprintf(stderr, "%s: converged in %u iters (stdv %g < %g)\n", me, iter, stdv, stdvMin); break; } } npos = nrrdNew(); airMopAdd(mop, npos, (airMopper)nrrdNix, airMopAlways); if (nrrdWrap_va(npos, pos, nrrdTypeDouble, 2, AIR_CAST(size_t, pntNum), AIR_CAST(size_t, posNum)) || nrrdSave(outS, npos, NULL)) { char *err; airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem saving output:\n%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/pull/popcntl.c0000664000175000017500000004427512165631065017214 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pull.h" #include "privatePull.h" int _pullPointProcessNeighLearn(pullTask *task, pullBin *bin, pullPoint *point) { /* sneaky: we just learn (and discard) the energy, and this function will do the work of learning the neighbors */ _pullEnergyFromPoints(task, bin, point, NULL); return 0; } static double _pointEnergyOfNeighbors(pullTask *task, pullBin *bin, pullPoint *point, double *fracNixed) { double enr; unsigned int ii, xx; pullPoint *her; enr = 0; xx = 0; for (ii=0; iineighPointNum; ii++) { her = point->neighPoint[ii]; if (her->status & PULL_STATUS_NIXME_BIT) { xx += 1; } else { enr += _pullEnergyFromPoints(task, bin, her, NULL); } } *fracNixed = (point->neighPointNum ? AIR_CAST(double, xx)/point->neighPointNum : 0); return enr; } int _pullPointProcessAdding(pullTask *task, pullBin *bin, pullPoint *point) { static const char me[]="_pullPointProcessAdding"; unsigned int npi, iter, api; double noffavg[4], npos[4], enrWith, enrWithout, fracNixed, newSpcDist, tmp; pullPoint *newpnt; int E; task->pctx->count[pullCountAdding] += 1; if (point->neighPointNum && task->pctx->targetDim && task->pctx->flag.popCntlEnoughTest) { unsigned int plenty, tardim; tardim = task->pctx->targetDim; if (task->pctx->flag.zeroZ && tardim > 1) { /* GLK unsure of tardim == 1 logic here */ tardim -= 1; } plenty = (1 == tardim ? 3 : (2 == tardim ? 7 : (3 == tardim ? 13 /* = 1 + 12 = 1 + coordination number of 3D sphere packing */ : 0 /* shouldn't get here */))); /* if (0 == (point->idtag % 100)) { printf("!%s: #num %d >?= plenty %d\n", me, point->neighPointNum, plenty); } */ if (point->neighPointNum >= plenty) { /* there's little chance that adding points will reduce energy */ return 0; } } /* printf("!%s: point->pos = (%g,%g,%g,%g)\n", me, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); printf("!%s: point->neighPointNum = %u\n", me, point->neighPointNum); printf("!%s: task->pctx->targetDim = %u\n", me, task->pctx->targetDim); printf("!%s: task->pctx->flag.popCntlEnoughTest = %d\n", me, task->pctx->flag.popCntlEnoughTest); */ ELL_4V_SET(noffavg, 0, 0, 0, 0); for (npi=0; npineighPointNum; npi++) { double off[4]; ELL_4V_SUB(off, point->pos, point->neighPoint[npi]->pos); /* normalize the offset */ ELL_3V_SCALE(off, 1/task->pctx->sysParm.radiusSpace, off); if (task->pctx->haveScale) { off[3] /= task->pctx->sysParm.radiusScale; } ELL_4V_INCR(noffavg, off); } if (point->neighPointNum) { /* if (0 == (point->idtag % 100)) { printf("%s: len(offavg) %g >?= thresh %g\n", me, ELL_4V_LEN(noffavg)/point->neighPointNum, _PULL_NEIGH_OFFSET_SUM_THRESH); } */ if (ELL_4V_LEN(noffavg)/point->neighPointNum < _PULL_NEIGH_OFFSET_SUM_THRESH) { /* we have neighbors, and they seem to be balanced well enough; don't try to add */ return 0; } } if (task->pctx->energySpecR->energy->well(&newSpcDist, task->pctx->energySpecR->parm)) { /* HEY: if we don't actually have a well, what is the point of guessing a new distance? */ newSpcDist = _PULL_NEWPNT_DIST; } /* compute offset (normalized) direction from current point location */ if (!point->neighPointNum) { /* we had no neighbors, have to pretend like we did */ airNormalRand_r(noffavg + 0, noffavg + 1, task->rng); airNormalRand_r(noffavg + 2, noffavg + 3, task->rng); if (!task->pctx->flag.zeroZ) { noffavg[2] = 0; } if (!task->pctx->haveScale) { noffavg[3] = 0; } } if (task->pctx->constraint) { double proj[9], tmpvec[3]; _pullConstraintTangent(task, point, proj); ELL_3MV_MUL(tmpvec, proj, noffavg); ELL_3V_COPY(noffavg, tmpvec); } ELL_4V_NORM(noffavg, noffavg, tmp); ELL_3V_SCALE(noffavg, task->pctx->sysParm.radiusSpace, noffavg); noffavg[3] *= task->pctx->sysParm.radiusScale; ELL_4V_SCALE(noffavg, newSpcDist, noffavg); /* set new point location */ ELL_4V_ADD2(npos, noffavg, point->pos); /* printf("!%s: new test pos @ (%g,%g,%g,%g)\n", me, npos[0], npos[1], npos[2], npos[3]); */ if (!_pullInsideBBox(task->pctx, npos)) { if (task->pctx->verbose > 2) { printf("%s: new pnt would start (%g,%g,%g,%g) outside bbox, nope\n", me, npos[0], npos[1], npos[2], npos[3]); } return 0; } /* initial pos is good, now we start getting serious */ newpnt = pullPointNew(task->pctx); if (!newpnt) { biffAddf(PULL, "%s: couldn't spawn new point from %u", me, point->idtag); return 1; } ELL_4V_COPY(newpnt->pos, npos); /* set status to indicate this is an unbinned point, with no knowledge of its neighbors */ newpnt->status |= PULL_STATUS_NEWBIE_BIT; /* satisfy constraint if needed */ if (task->pctx->constraint) { int constrFail; if (_pullConstraintSatisfy(task, newpnt, _PULL_CONSTRAINT_TRAVEL_MAX, &constrFail)) { biffAddf(PULL, "%s: on newbie point %u (spawned from %u)", me, newpnt->idtag, point->idtag); pullPointNix(newpnt); return 1; } if (constrFail) { /* constraint satisfaction failed, which isn't an error for us, we just don't try to add this point. Can do immediate nix because no neighbors know about this point. */ pullPointNix(newpnt); return 0; } if (!_pullInsideBBox(task->pctx, newpnt->pos)) { if (task->pctx->verbose > 2) { printf("%s: post constr newpnt %u (%g,%g,%g,%g) outside bbox; nope\n", me, newpnt->idtag, newpnt->pos[0], newpnt->pos[1], newpnt->pos[2], newpnt->pos[3]); } newpnt = pullPointNix(newpnt); return 0; } } /* printf("!%s: new test pos @ (%g,%g,%g,%g)\n", me, newpnt->pos[0], newpnt->pos[1], newpnt->pos[2], newpnt->pos[3]); */ /* do some descent, on this point only, which (HACK!) we do by changing the per-task process mode . . . */ task->processMode = pullProcessModeDescent; E = 0; for (iter=0; iterpctx->iterParm.addDescent; iter++) { double diff[4]; if (!E) E |= _pullPointProcessDescent(task, bin, newpnt, /* ignoreImage; actually it is this use which motivated the creation of ignoreImage */ AIR_TRUE); if (newpnt->status & PULL_STATUS_STUCK_BIT) { if (task->pctx->verbose > 2) { printf("%s: possible newpnt %u stuck @ iter %u; nope\n", me, newpnt->idtag, iter); } newpnt = pullPointNix(newpnt); /* if we don't change the mode back, then pullBinProcess() won't know to try adding for the rest of the bins it sees, bad HACK */ task->processMode = pullProcessModeAdding; return 0; } if (!_pullInsideBBox(task->pctx, newpnt->pos)) { if (task->pctx->verbose > 2) { printf("%s: newpnt %u went (%g,%g,%g,%g) outside bbox; nope\n", me, newpnt->idtag, newpnt->pos[0], newpnt->pos[1], newpnt->pos[2], newpnt->pos[3]); } newpnt = pullPointNix(newpnt); task->processMode = pullProcessModeAdding; return 0; } ELL_4V_SUB(diff, newpnt->pos, point->pos); ELL_3V_SCALE(diff, 1/task->pctx->sysParm.radiusSpace, diff); diff[3] /= task->pctx->sysParm.radiusScale; if (ELL_4V_LEN(diff) > _PULL_NEWPNT_STRAY_DIST) { if (task->pctx->verbose > 2) { printf("%s: newpnt %u went too far %g from old point %u; nope\n", me, newpnt->idtag, ELL_4V_LEN(diff), point->idtag); } newpnt = pullPointNix(newpnt); task->processMode = pullProcessModeAdding; return 0; } /* still okay to continue descending */ newpnt->stepEnergy *= task->pctx->sysParm.opporStepScale; } /* printf("!%s: new test pos @ (%g,%g,%g,%g)\n", me, newpnt->pos[0], newpnt->pos[1], newpnt->pos[2], newpnt->pos[3]); { double posdiff[4]; ELL_4V_SUB(posdiff, newpnt->pos, point->pos); printf("!%s: at dist %g\n", me, ELL_4V_LEN(posdiff)); } */ /* now that newbie point is final test location, see if it meets the live thresh, if there is one */ if (task->pctx->ispec[pullInfoLiveThresh] && 0 > pullPointScalar(task->pctx, newpnt, pullInfoLiveThresh, NULL, NULL)) { /* didn't meet threshold */ newpnt = pullPointNix(newpnt); task->processMode = pullProcessModeAdding; return 0; } if (task->pctx->ispec[pullInfoLiveThresh2] /* HEY: copy & paste */ && 0 > pullPointScalar(task->pctx, newpnt, pullInfoLiveThresh2, NULL, NULL)) { /* didn't meet threshold */ newpnt = pullPointNix(newpnt); task->processMode = pullProcessModeAdding; return 0; } if (task->pctx->ispec[pullInfoLiveThresh3] /* HEY: copy & paste */ && 0 > pullPointScalar(task->pctx, newpnt, pullInfoLiveThresh3, NULL, NULL)) { /* didn't meet threshold */ newpnt = pullPointNix(newpnt); task->processMode = pullProcessModeAdding; return 0; } /* see if the new point should be nixed because its at a volume edge */ if (task->pctx->flag.nixAtVolumeEdgeSpace && (newpnt->status & PULL_STATUS_EDGE_BIT)) { newpnt = pullPointNix(newpnt); task->processMode = pullProcessModeAdding; return 0; } /* no problem with live thresh, test energy by first learn neighbors */ /* we have to add newpnt to task's add queue, just so that its neighbors can see it as a possible neighbor */ api = airArrayLenIncr(task->addPointArr, 1); task->addPoint[api] = newpnt; task->processMode = pullProcessModeNeighLearn; _pullEnergyFromPoints(task, bin, newpnt, NULL); /* and teach the neighbors their neighbors (possibly including newpnt) */ for (npi=0; npineighPointNum; npi++) { _pullEnergyFromPoints(task, bin, newpnt->neighPoint[npi], NULL); } /* now back to the actual mode we came here with */ task->processMode = pullProcessModeAdding; enrWith = (_pullEnergyFromPoints(task, bin, newpnt, NULL) + _pointEnergyOfNeighbors(task, bin, newpnt, &fracNixed)); newpnt->status |= PULL_STATUS_NIXME_BIT; /* turn nixme on */ enrWithout = _pointEnergyOfNeighbors(task, bin, newpnt, &fracNixed); newpnt->status &= ~PULL_STATUS_NIXME_BIT; /* turn nixme off */ if (enrWith < enrWithout) { /* energy is clearly lower *with* newpnt, so we want to add it, which means keeping it in the add queue where it already is */ if (task->pctx->logAdd) { double posdiff[4]; ELL_4V_SUB(posdiff, newpnt->pos, point->pos); fprintf(task->pctx->logAdd, "%u %g %g %g %g %g %g\n", newpnt->idtag, ELL_3V_LEN(posdiff)/task->pctx->sysParm.radiusSpace, AIR_ABS(posdiff[3])/task->pctx->sysParm.radiusScale, newpnt->pos[0], newpnt->pos[1], newpnt->pos[2], newpnt->pos[3]); } } else { /* adding point is not an improvement, remove it from the add queue */ airArrayLenIncr(task->addPointArr, -1); /* ugh, have to signal to neighbors that its no longer their neighbor. HEY this is the part that is totally screwed with multiple threads */ task->processMode = pullProcessModeNeighLearn; newpnt->status |= PULL_STATUS_NIXME_BIT; for (npi=0; npineighPointNum; npi++) { _pullEnergyFromPoints(task, bin, newpnt->neighPoint[npi], NULL); } task->processMode = pullProcessModeAdding; newpnt = pullPointNix(newpnt); } return 0; } int _pullPointProcessNixing(pullTask *task, pullBin *bin, pullPoint *point) { double enrWith, enrNeigh, enrWithout, fracNixed; task->pctx->count[pullCountNixing] += 1; /* if there's a live thresh, do we meet it? */ if (task->pctx->ispec[pullInfoLiveThresh] && 0 > pullPointScalar(task->pctx, point, pullInfoLiveThresh, NULL, NULL)) { point->status |= PULL_STATUS_NIXME_BIT; return 0; } /* HEY copy & paste */ if (task->pctx->ispec[pullInfoLiveThresh2] && 0 > pullPointScalar(task->pctx, point, pullInfoLiveThresh2, NULL, NULL)) { point->status |= PULL_STATUS_NIXME_BIT; return 0; } /* HEY copy & paste */ if (task->pctx->ispec[pullInfoLiveThresh3] && 0 > pullPointScalar(task->pctx, point, pullInfoLiveThresh3, NULL, NULL)) { point->status |= PULL_STATUS_NIXME_BIT; return 0; } /* if many neighbors have been nixed, then system is far from convergence, so energy is not a very meaningful guide to whether to nix this point NOTE that we use this function to *learn* fracNixed */ enrNeigh = _pointEnergyOfNeighbors(task, bin, point, &fracNixed); if (fracNixed < task->pctx->sysParm.fracNeighNixedMax) { /* is energy lower without us around? */ enrWith = enrNeigh + _pullEnergyFromPoints(task, bin, point, NULL); point->status |= PULL_STATUS_NIXME_BIT; /* turn nixme on */ enrWithout = _pointEnergyOfNeighbors(task, bin, point, &fracNixed); if (enrWith <= enrWithout) { /* Energy isn't distinctly lowered without the point, so keep it; turn off nixing. If enrWith == enrWithout == 0, as happens to isolated points, then the difference between "<=" and "<" keeps the isolated points from getting nixed */ point->status &= ~PULL_STATUS_NIXME_BIT; /* turn nixme off */ } /* else energy is certainly higher with the point, do nix it */ } return 0; } int _pullIterFinishNeighLearn(pullContext *pctx) { static const char me[]="_pullIterFinishNeighLearn"; /* a no-op for now */ AIR_UNUSED(pctx); AIR_UNUSED(me); return 0; } int _pullIterFinishAdding(pullContext *pctx) { static const char me[]="_pullIterFinishAdding"; unsigned int taskIdx; pctx->addNum = 0; for (taskIdx=0; taskIdxthreadNum; taskIdx++) { pullTask *task; task = pctx->task[taskIdx]; if (task->addPointNum) { unsigned int pointIdx; int added; for (pointIdx=0; pointIdxaddPointNum; pointIdx++) { pullPoint *point; pullBin *bin; point = task->addPoint[pointIdx]; point->status &= ~PULL_STATUS_NEWBIE_BIT; if (pullBinsPointMaybeAdd(pctx, point, &bin, &added)) { biffAddf(PULL, "%s: trouble binning new point %u", me, point->idtag); return 1; } if (added) { pctx->addNum++; } else { unsigned int npi, xpi; if (pctx->verbose) { printf("%s: decided NOT to add new point %u\n", me, point->idtag); } /* HEY: copied from above */ /* ugh, have to signal to neigs that its no longer their neighbor */ task->processMode = pullProcessModeNeighLearn; point->status |= PULL_STATUS_NIXME_BIT; for (npi=0; npineighPointNum; npi++) { _pullEnergyFromPoints(task, bin, point->neighPoint[npi], NULL); } task->processMode = pullProcessModeAdding; /* can't do immediate nix for reasons GLK doesn't quite understand */ xpi = airArrayLenIncr(task->nixPointArr, 1); task->nixPoint[xpi] = point; } } airArrayLenSet(task->addPointArr, 0); } } if (pctx->verbose && pctx->addNum) { printf("%s: ADDED %u\n", me, pctx->addNum); } return 0; } void _pullNixTheNixed(pullContext *pctx) { unsigned int binIdx; pctx->nixNum = 0; for (binIdx=0; binIdxbinNum; binIdx++) { pullBin *bin; unsigned int pointIdx; bin = pctx->bin + binIdx; pointIdx = 0; while (pointIdx < bin->pointNum) { pullPoint *point; point = bin->point[pointIdx]; if (pctx->flag.nixAtVolumeEdgeSpace && (point->status & PULL_STATUS_EDGE_BIT)) { point->status |= PULL_STATUS_NIXME_BIT; } if (point->status & PULL_STATUS_NIXME_BIT) { pullPointNix(point); /* copy last point pointer to this slot */ bin->point[pointIdx] = bin->point[bin->pointNum-1]; airArrayLenIncr(bin->pointArr, -1); /* will decrement bin->pointNum */ pctx->nixNum++; } else { pointIdx++; } } } return; } int _pullIterFinishNixing(pullContext *pctx) { static const char me[]="_pullIterFinishNixing"; unsigned int taskIdx; _pullNixTheNixed(pctx); /* finish nixing the things that we decided not to add */ for (taskIdx=0; taskIdxthreadNum; taskIdx++) { pullTask *task; task = pctx->task[taskIdx]; if (task->nixPointNum) { unsigned int xpi; for (xpi=0; xpinixPointNum; xpi++) { pullPointNix(task->nixPoint[xpi]); } airArrayLenSet(task->nixPointArr, 0); } } if (pctx->verbose && pctx->nixNum) { printf("%s: NIXED %u\n", me, pctx->nixNum); } return 0; } teem-1.11.0~svn6057/src/pull/pull.h0000664000175000017500000015311712174666437016525 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULL_HAS_BEEN_INCLUDED #define PULL_HAS_BEEN_INCLUDED #include #include #include #include #include #include #include #include /* ** This library was created to implement the research and methods of: ** ** Gordon L. Kindlmann, Ra{\'u}l San Jos{\'e} Est{\'e}par, ** Stephen M. Smith, Carl-Fredrik Westin. ** Sampling and Visualizing Creases with Scale-Space Particles. ** IEEE Trans. on Visualization and Computer Graphics, ** 15(6):1415-1424 (2009) ** ** Further information and usage examples: ** http://people.cs.uchicago.edu/~glk/ssp/ ** ** The library is still being actively developed to support research on ** particle systems for general feature sampling. At some point, for ** example, it should subsume the "push" library for glyph packing in ** tensor fields. */ #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(pull_EXPORTS) || defined(teem_EXPORTS) # define PULL_EXPORT extern __declspec(dllexport) # else # define PULL_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define PULL_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define PULL pullBiffKey #define PULL_THREAD_MAXNUM 512 #define PULL_VOLUME_MAXNUM 4 #define PULL_POINT_NEIGH_INCR 16 #define PULL_BIN_MAXNUM 40000000 /* sanity check on max number bins */ #define PULL_PHIST 0 #define PULL_HINTER 0 #define PULL_TANCOVAR 1 /* ******** pullInfo enum ** ** all the things that might be *learned* about the local neighborhood ** that are used as ingredients in the computation of particle motion. ** This info was originally learned only from gage, but now (according ** to value of pullSource) can come from other kinds of information. ** ** There are multiple scalars (and associated) derivatives that can ** be used for dynamics: ** - Inside: just for nudging things to stay inside a mask ** - Height: value for computer-vision-y features of ridges, valleys, ** and edges. Setting pullInfoHeight as a constraint does valley ** sampling (flip the sign to get ridges), based on the various ** "tangents" Setting pullInfoHeightLaplacian as a constraint * does zero-crossing edge detection. ** - Isovalue: just for implicit surfaces f=0 ** - Strength: some measure of feature strength, with the assumption ** that it can't be analytically differentiated in space or scale. */ enum { pullInfoUnknown, /* 0 */ pullInfoTensor, /* 1: [7] tensor here */ pullInfoTensorInverse, /* 2: [7] inverse of tensor here */ pullInfoHessian, /* 3: [9] hessian used for force distortion */ pullInfoInside, /* 4: [1] containment scalar */ pullInfoInsideGradient, /* 5: [3] containment vector */ pullInfoHeight, /* 6: [1] for gravity, and edge and crease feature detection */ pullInfoHeightGradient, /* 7: [3] */ pullInfoHeightHessian, /* 8: [9] */ pullInfoHeightLaplacian, /* 9: [1] for zero-crossing edge detection */ pullInfoSeedPreThresh, /* 10: [1] scalar for pre-thresholding seeding, so that points can be quickly eliminated (e.g. prior to constraint satisfaction) */ pullInfoSeedThresh, /* 11: [1] scalar for thresholding seeding */ pullInfoLiveThresh, /* 12: [1] scalar for thresholding extant particles, AND for future additions from population control */ pullInfoLiveThresh2, /* 13: [1] another pullInfoLiveThresh */ pullInfoLiveThresh3, /* 14: [1] yet another pullInfoLiveThresh */ pullInfoTangent1, /* 15: [3] first tangent to motion allowed for constraint satisfaction */ pullInfoTangent2, /* 16: [3] second tangent to motion allowed for constraint satisfaction */ pullInfoNegativeTangent1, /* 17: [3] like the tangents, but with a negated objective function */ pullInfoNegativeTangent2, /* 18: [3] second negative tangent */ pullInfoIsovalue, /* 19: [1] for isosurface extraction */ pullInfoIsovalueGradient, /* 20: [3] */ pullInfoIsovalueHessian, /* 21: [9] */ pullInfoStrength, /* 22: [1] */ pullInfoQuality, /* 23: [1] */ pullInfoLast }; #define PULL_INFO_MAX 23 /* ********* pullProp* enum: the various properties of particles in the system ** ** These are things that are not learned from the image data, but are ** descriptions of a particle, its neighborhood of particles in the ** system, and the current state of system computation. As of revision ** 5080, these may refer to information *computed* from the per-particle ** info, but which may not itself be saved as a field in pullPoint. ** ** consider adding: dot between normalized directions of force and movmt */ enum { pullPropUnknown, /* 0: nobody knows */ pullPropIdtag, /* 1: [1] idtag (unsigned int) */ pullPropIdCC, /* 2: [1] idcc (unsigned int) */ pullPropEnergy, /* 3: [1] energy from last iteration */ pullPropStepEnergy, /* 4: [1] step size for minimizing energy */ pullPropStepConstr, /* 5: [1] step size for constraint satis. */ pullPropStuck, /* 6: [1] how many iters its been stuck */ pullPropPosition, /* 7: [4] position */ pullPropForce, /* 8: [4] force accumulation */ pullPropNeighDistMean, /* 9: [1] "mean distance" to neighbors */ pullPropScale, /* 10: [1] scale position */ pullPropNeighCovar, /* 11: [10] unique coeffs of covariance matrix of offsets to all interacting neighbors, in rs-normalized space, updated only during pullProcessModeNeighLearn. The layout is: 0:xx 1:xy 2:xz 3:xs . 4:yy 5:yz 6:ys . 7:zz 8:zs . 9:ss */ pullPropNeighCovar7Ten, /* 12: [7] spatial 3x3 submatrix of covariance, formatted as ten-compatible 7-tensor */ pullPropNeighTanCovar, /* 13: [6] covariance of "tangents" of neighbors, (e.g. pullInfoTangent1 for crease surfaces) including point itself */ pullPropNeighInterNum, /* 14: [1] # neighbors last iter */ pullPropNeighCovarTrace, /* 15: [1] trace of NeighCovar */ pullPropNeighCovarDet, /* 16: [1] det of NeighCovar */ pullPropStability, /* 17: [1] some measure of NeighCovar */ pullPropLast }; #define PULL_PROP_MAX 17 /* ** the components of a point's status that are set as a bitflag ** in point->status */ enum { pullStatusUnknown, /* 0: nobody knows */ pullStatusStuck, /* 1: couldn't move to decrease energy */ #define PULL_STATUS_STUCK_BIT (1<< 1) pullStatusNewbie, /* 2: not binned, testing if the system would be better with me in it */ #define PULL_STATUS_NEWBIE_BIT (1<< 2) pullStatusNixMe, /* 3: nix me at the *end* of this iter, and don't look at me for energy during this iteration */ #define PULL_STATUS_NIXME_BIT (1<< 3) pullStatusEdge, /* 4: at the spatial edge of one of the volumes: gage had to invent values for some samples in the kernel support */ #define PULL_STATUS_EDGE_BIT (1<< 4) pullStatusLast }; /* ******** pullInterType* enum ** ** the different types of scale-space interaction that can happen ** in scale-space. The descriptions here overlook the normalization ** by radiusScale and radiusSpace */ enum { pullInterTypeUnknown, /* 0 */ pullInterTypeJustR, /* 1: phi(r,s) = phi_r(r) */ pullInterTypeUnivariate, /* 2: phi(r,s) = phi_r(u); u = sqrt(r*r+s*s) */ pullInterTypeSeparable, /* 3: phi(r,s) = phi_r(r)*phi_s(s) */ pullInterTypeAdditive, /* 4: phi(r,s) = beta*phi_r(r)*win(s) + (1-beta)*win(r)*phi_s(s) */ pullInterTypeLast }; #define PULL_INTER_TYPE_MAX 4 /* ******** pullEnergyType* enum ** ** the different shapes of potential energy profiles that can be used */ enum { pullEnergyTypeUnknown, /* 0 */ pullEnergyTypeSpring, /* 1 */ pullEnergyTypeGauss, /* 2 */ pullEnergyTypeBspln, /* 3 */ pullEnergyTypeButterworth, /* 4 */ pullEnergyTypeCotan, /* 5 */ pullEnergyTypeCubic, /* 6 */ pullEnergyTypeQuartic, /* 7 */ pullEnergyTypeCubicWell, /* 8 */ pullEnergyTypeBetterCubicWell, /* 9 */ pullEnergyTypeQuarticWell, /* 10 */ pullEnergyTypeHepticWell, /* 11 */ pullEnergyTypeZero, /* 12 */ pullEnergyTypeButterworthParabola, /* 13 */ pullEnergyTypeLast }; #define PULL_ENERGY_TYPE_MAX 13 #define PULL_ENERGY_PARM_NUM 3 enum { pullProcessModeUnknown, /* 0 */ pullProcessModeDescent, /* 1 */ pullProcessModeNeighLearn, /* 2 */ pullProcessModeAdding, /* 3 */ pullProcessModeNixing, /* 4 */ pullProcessModeLast }; #define PULL_PROCESS_MODE_MAX 4 /* ** the conditions under which a point may find itself at some position */ enum { pullCondUnknown, /* 0 */ pullCondOld, /* 1 */ pullCondConstraintSatA, /* 2 */ pullCondConstraintSatB, /* 3 */ pullCondEnergyTry, /* 4 */ pullCondConstraintFail, /* 5 */ pullCondEnergyBad, /* 6 */ pullCondNew, /* 7 */ pullCondLast }; /* ** the places that "info" can be learned from. Originally this was ** strictly from gage and no where else; it can be handy to allow it ** to originate from different places as well, but still flow through ** the channels now organized around pullInfo */ enum { pullSourceUnknown, /* 0 */ pullSourceGage, /* 1: measured from gage */ pullSourceProp, /* 2: copied from a pullProp */ pullSourceLast }; #define PULL_SOURCE_MAX 2 /* ** the different kinds of computations and entities that one can ** count, for book-keeping and meta-optimization purposes */ enum { pullCountUnknown, /* 0 */ pullCountDescent, /* 1 */ pullCountTestStep, /* 2 */ pullCountEnergyFromImage, /* 3 */ pullCountForceFromImage, /* 4 */ pullCountEnergyFromPoints, /* 5 */ pullCountForceFromPoints, /* 6 */ pullCountProbe, /* 7 */ pullCountConstraintSatisfy, /* 8 */ pullCountAdding, /* 9 */ pullCountNixing, /* 10 */ pullCountPointsStuck, /* 11 */ pullCountPoints, /* 12 */ pullCountCC, /* 13 */ pullCountIteration, /* 14 */ pullCountLast }; #define PULL_COUNT_MAX 14 /* ** reasons for pullTraceSet to stop (or go nowhere) */ enum { pullTraceStopUnknown, /* 0 */ pullTraceStopSpeeding, /* 1 */ pullTraceStopConstrFail, /* 2 */ pullTraceStopBounds, /* 3 */ pullTraceStopLength, /* 4 */ pullTraceStopStub, /* 5 */ pullTraceStopLast }; #define PULL_TRACE_STOP_MAX 5 /* ** Defines how par-particle information can be learned. This is ** typically via measurements in the image by gage, but other sources ** are possible (as indicated by the source field). ** ** There is a basic design decision in whether to make pullInfos the ** most general representation of information (including referring to ** pullProps), or, whether pullProps should be the general thing, ** which can then refer to some pullInfo. In the long run, these ** should probably be merged. In the short term, there is better ** infrastructure for representing and parsing pullInfos, so that has ** been leveraged to include other things as well. */ typedef struct pullInfoSpec_t { /* ------ INPUT ------ */ int info, /* from the pullInfo* enum: what is the "info" that this infospec defines */ source; /* from the pullSource* enum: where does this information come from */ char *volName; /* volume name */ int item, /* which gage item (for pullSourceGage) */ prop; /* which pull property (for pullSourceProp) */ double scale, /* scaling factor (including sign) */ zero; /* for height and inside: where is zero, for seedThresh, threshold value */ int constraint; /* (for scalar items) minimizing this is a constraint to enforce per-point per-iteration, not merely a contribution to the point's energy */ /* ------ INTERNAL ------ */ unsigned int volIdx; /* which volume (for pullSourceGage) */ } pullInfoSpec; /* ******** pullPoint ** */ typedef struct pullPoint_t { unsigned int idtag, /* unique point ID */ idCC; /* id for connected component analysis */ struct pullPoint_t **neighPoint; /* list of neighboring points */ unsigned int neighPointNum; airArray *neighPointArr; /* airArray around neighPoint and neighNum (no callbacks used here) */ double neighDistMean; /* average of distance to neighboring points with whom this point interacted, in rs-normalized space */ float neighCovar[10], /* unique coeffs in 4x4 covariance matrix of neighbors with whom this point interacted */ #if PULL_TANCOVAR neighTanCovar[6], /* covariance of "tangent" info of neighbors */ #endif stability; /* the scalar stability measure */ unsigned int neighInterNum, /* number of particles with which I had some non-zero interaction on last iteration */ stuckIterNum; /* how many iterations I've been stuck */ #if PULL_PHIST double *phist; /* history of positions tried in the last iter, in sets of 5 doubles: (x,y,z,t,info) */ unsigned int phistNum; /* number of positions stored */ airArray *phistArr; /* airArray around phist */ #endif int status; /* bit-flag of status info */ double pos[4], /* position in space and scale */ energy, /* energy accumulator for this iteration */ force[4], /* force accumulator for this iteration */ stepEnergy, /* step size for energy minimization */ stepConstr, /* step size for constraint satisfaction; HEY: this doens't seem to be really used? */ info[1]; /* all information learned from gage that matters for particle dynamics. This is sneakily allocated for *more*, depending on needs, so this MUST be last field */ } pullPoint; /* ******** pullBin ** ** the data structure for doing spatial binning. */ typedef struct pullBin_t { pullPoint **point; /* dyn. alloc. array of point pointers */ unsigned int pointNum; /* # of points in this bin */ airArray *pointArr; /* airArray around point and pointNum (no callbacks used here) */ struct pullBin_t **neighBin; /* NULL-terminated list of all neighboring bins, including myself */ } pullBin; /* ******** pullEnergy ** ** the functions which determine inter-point forces ** ** NOTE: the eval() function probably does NOT check to see it was passed ** a non-NULL pointer into which to store the derivative of energy ("denr") ** ** Thu Apr 10 12:40:08 EDT 2008: nixed the "support" function, since it ** was annoying to deal with variable support potentials. Now everything ** cuts off at dist=1. You can still use the parm vector to change the ** shape inside the support. */ typedef struct { char name[AIR_STRLEN_SMALL]; unsigned int parmNum; double (*well)(double *wx, const double parm[PULL_ENERGY_PARM_NUM]); double (*eval)(double *denr, double dist, const double parm[PULL_ENERGY_PARM_NUM]); } pullEnergy; typedef struct { const pullEnergy *energy; double parm[PULL_ENERGY_PARM_NUM]; } pullEnergySpec; /* ** In the interests of simplicity (and with the cost of some redundancy), ** this is going to copied per-task, which is why it contains the gageContext ** The idea is that the first of these is somehow set up by the user ** or something, and the rest of them are created within pull per-task. */ typedef struct { int verbose; /* blah blah blah */ char *name; /* how the volume will be identified (like its a variable name) */ const gageKind *kind; const Nrrd *ninSingle; /* don't own */ const Nrrd *const *ninScale; /* don't own; NOTE: only one of ninSingle and ninScale can be non-NULL */ unsigned int scaleNum; /* number of scale-space samples (volumes) */ double *scalePos; /* location of all samples in scale */ int scaleDerivNorm; /* normalize derivatives based on scale */ double scaleDerivNormBias; /* bias on derivative normalization by scale */ NrrdKernelSpec *ksp00, /* for sampling tensor field */ *ksp11, /* for gradient of mask, other 1st derivs */ *ksp22, /* for 2nd derivatives */ *kspSS; /* for reconstructing from scale-space samples */ gageQuery pullValQuery; /* if this is a pullValGageKind volume, then we don't have a real gageContext, and we have to manage our own query */ gageContext *gctx; /* do own, and set based on info here */ gagePerVolume *gpvl, /* stupid gage API . . . */ **gpvlSS; /* stupid gage API . . . */ int seedOnly, /* volume only required for seeding, for either pullInfoSeedThresh or pullInfoSeedPreThresh */ forSeedPreThresh; /* we learn pullInfoSeedPreThresh from this */ } pullVolume; /* ******** pullTask ** ** The information specific for a thread. */ typedef struct pullTask_t { struct pullContext_t *pctx; /* parent's context; not const because the tasks assign themselves bins to do work */ pullVolume *vol[PULL_VOLUME_MAXNUM]; /* volumes copied from parent */ const double *ans[PULL_INFO_MAX+1]; /* answer *pointers* for all possible infos, pointing into per-task per-volume gctxs (or into above per-task pullValAnswer), OR: NULL if that info is not being used */ int processMode, /* what kind of point processing is being done by this task right now */ probeSeedPreThreshOnly; /* hack-ish flag to communicate to pullProbe that we only care about SeedPreThresh */ airThread *thread; /* my thread */ unsigned int threadIdx; /* which thread am I */ airRandMTState *rng; /* state for my RNG */ pullPoint *pointBuffer, /* place for copying point into during strength ascent computation; can't be statically allocated because pullPoint size is known only at run-time */ **neighPoint; /* array of point pointers, either all possible points from neighbor bins, or last learned interacting neighbors */ pullPoint **addPoint; /* points to add before next iter */ unsigned int addPointNum; /* # of points to add */ airArray *addPointArr; /* airArray around addPoint, addPointNum */ pullPoint **nixPoint; /* points to nix before next iter */ unsigned int nixPointNum; /* # of points to nix */ airArray *nixPointArr; /* airArray around nixPoint, nixPointNum */ void *returnPtr; /* for airThreadJoin */ unsigned int stuckNum; /* # stuck particles seen by this task */ } pullTask; /* ******** pullInitMethod* enum ** ** the different ways pull can be initialized */ enum { pullInitMethodUnknown, /* 0 */ pullInitMethodRandom, /* 1 */ pullInitMethodHalton, /* 2 */ pullInitMethodPointPerVoxel, /* 3 */ pullInitMethodGivenPos, /* 4 */ pullInitMethodLast }; /* ******** pullInitParm ** ** none of this is directly user-set; set with pullInit*Set function ** (note that there is no pullInit* enum; these values are too diverse) */ typedef struct { int method; /* from pullInitMethod* enum */ int liveThreshUse, /* use the liveThresh info as a criterion for seeding */ unequalShapesAllow; /* when taking in volumes, allow their shapes to be unuequal. Some confusing usage problems are due to using differently shaped volumes */ double jitter; /* w/ PointPerVoxel, how much to jitter index space positions */ unsigned int numInitial, /* w/ Random OR Halton, # points to start with */ haltonStartIndex, /* w/ Halton, first index to use */ samplesAlongScaleNum, /* w/ PointPerVoxel, # of samples along scale (distributed uniformly in scale's *index* space*/ ppvZRange[2]; /* (hack to permit seeding only in part of volume, when initialization is painfully the main bottleneck) w/ PointPerVoxel, range of indices along Z to do seeding by pointPerVoxel, or, {1,0} to do the whole volume as normal */ int pointPerVoxel; /* w/ PointPerVoxel, if this (ppv) is > 0, then use ppv points per voxel. If ppv < 0, then jitter point point every -ppv'th voxel (so ppv=-1 is same as ppv=1) */ const Nrrd *npos; /* positions (4xN array) to start with */ } pullInitParm; /* ******** pullIterParm* enum ** ** parameters related to iterations and their periods */ enum { pullIterParmUnknown, /* if non-zero, minimum number of iterations for whole system. */ pullIterParmMin, /* if non-zero, max number of iterations for whole system. if zero: no explicit limit on the number of iterations */ pullIterParmMax, /* if non-zero, max number of iterations we allow something to be continuously stuck before nixing it */ pullIterParmStuckMax, /* if non-zero, max number of iterations for enforcing each constraint */ pullIterParmConstraintMax, /* how many intervals to wait between attemps at population control, or, 0 to say: "no pop cntl" */ pullIterParmPopCntlPeriod, /* how many iterations for which to run descent on tentative new points during population control, so that they end up at a good location, at which we can meaningfully test whether adding the point will reducing system energy */ pullIterParmAddDescent, /* periodicity with which to call the pullContext->iter_cb */ pullIterParmCallback, /* if non-zero, interval between iters at which output snapshots are saved */ pullIterParmSnap, /* the half-life of energyIncreasePermit, in terms of iterations, or 0 if no such decay is wanted */ pullIterParmEnergyIncreasePermitHalfLife, pullIterParmLast }; typedef struct { unsigned int min, max, popCntlPeriod, addDescent, constraintMax, stuckMax, callback, snap, energyIncreasePermitHalfLife; } pullIterParm; /* ******** pullSysParm* enum ** ** various continuous parameters. It should be understood that the number ** of these is a reflection of the experimentation and exploration that ** went into the creation of these particles systems, rather than a need ** for onerous parameter tweaking in order to get something useful done. ** Except for alpha, beta, and gamma, these have reasonable defaults. */ enum { pullSysParmUnknown, /* balance between particle-image energy E_i and inter-particle energy E_ij, the most important parameter in the governing equation of the system. alpha = 0: only particle-image; alpha = 1: only inter-particle */ pullSysParmAlpha, /* when using pullInterAdditive ("Phi_2") inter-particle energy, beta sets the balance between spatial repulsion and scale attraction. (if not using this energy, this is moot) beta = 0: only spatial repulsion; beta = 1: only scale attraction */ pullSysParmBeta, /* when energyFromStrength is non-zero: scaling factor on energy from strength */ pullSysParmGamma, /* when learning a suitable gamma from data (via pullGammaLearn) in the context of separable energy functions, how much to scale the learned gamma. Making this greater than 1.0 helps let the energy from strength overpower the slight repulsion along scale that might exist by the separable phi construction. */ pullSysParmSeparableGammaLearnRescale, /* to be more selective for pullInfoSeedThresh and pullInfoLiveThresh at higher scales, set theta > 0, and the effective threshold will be base threshold + theta*scale. HOWEVER, the way this is implemented is a hack: the probed strength value is decremented by theta*scale */ pullSysParmTheta, /* initial (time) step for dynamics */ pullSysParmStepInitial, /* radius/scaling of inter-particle interactions in the spatial domain */ pullSysParmRadiusSpace, /* radius/scaling of inter-particle interactions in the scale domain */ pullSysParmRadiusScale, /* spatial width of bin, as multiple of pullSysParmRadiusSpace (the width along scale is set to pullSysParmRadiusScale). Can't be lower than 1, but may be usefully set greater that 1 to reduce the total number of bins, especially if caching neighbor lists */ pullSysParmBinWidthSpace, /* probability that we find the true neighbors of the particle, as opposed to using a cached list */ pullSysParmNeighborTrueProb, /* probability that we do image probing to find out what's really going on */ pullSysParmProbeProb, /* (>= 1.0) how much to opportunistically scale up step size (for energy minimization) with every iteration */ pullSysParmOpporStepScale, /* (< 1.0) when energy goes up instead of down, or when constraint satisfaction seems to be going the wrong way, how to scale (down) step size */ pullSysParmBackStepScale, /* pseudo-convergence threshold that controls when population control is activated (has to be higher than (less strict) energyDecreaseMin */ pullSysParmEnergyDecreasePopCntlMin, /* epsilon amount by which its okay for particle energy to increase, in the context of gradient descent */ pullSysParmEnergyIncreasePermit, /* convergence threshold: stop when fractional improvement (decrease) in total system energy dips below this */ pullSysParmEnergyDecreaseMin, /* convergence threshold for constraint satisfaction: finished if stepsize goes below this times pctx->voxelSizeSpace */ pullSysParmConstraintStepMin, /* spring constant on bbox wall */ pullSysParmWall, /* when doing population control nixing, don't nix a particle if this fraction of its neighbors have already been nixed (Section 3.5 of paper implies that this value should be 0.5; lower values also work) */ pullSysParmFracNeighNixedMax, pullSysParmLast }; typedef struct { double alpha, beta, gamma, separableGammaLearnRescale, theta, wall, radiusSpace, radiusScale, binWidthSpace, neighborTrueProb, probeProb, stepInitial, opporStepScale, backStepScale, constraintStepMin, energyDecreaseMin, energyDecreasePopCntlMin, energyIncreasePermit, fracNeighNixedMax; } pullSysParm; /* ******** pullConstraintFail* enum ** ** the various ways constriant satisfaction can fail */ enum { pullConstraintFailUnknown, /* 0 */ pullConstraintFailProjGradZeroA, /* 1 */ pullConstraintFailProjGradZeroB, /* 2 */ pullConstraintFailIterMaxed, /* 3 */ pullConstraintFailTravel, /* 4 */ pullConstraintFailLast }; #define PULL_CONSTRAINT_FAIL_MAX 4 /* ******** pullFlag* enum ** ** the various booleans */ enum { pullFlagUnknown, /* permute points during rebinning between iters, to randomize their ordering within their bin (bins are still processed in scanline order) */ pullFlagPermuteOnRebin, /* when alpha is exactly zero, probably with the purpose of migrating particles along scale towards the scale of maximal strength while avoiding all inter-particle interaction, don't try any population control */ pullFlagNoPopCntlWithZeroAlpha, /* when learning gamma via Eq. 26 of the paper, also use beta to more accurately reflect the 2nd derivative of energy wrt scale */ pullFlagUseBetaForGammaLearn, /* whether or not to deny adding points to bins where there are close points already */ pullFlagRestrictiveAddToBins, /* if non-zero, strength is a particle-image energy term that is minimized by motion along scale, which in turn requires extra probing to determine the strength gradient along scale. */ pullFlagEnergyFromStrength, /* if non-zero, nix points that got near enough to the volume edge that gage had to invent values for the kernel support */ pullFlagNixAtVolumeEdgeSpace, /* if non-zero, during initialization, try constraint satisfaction (if there is a constraint) before testing whether the seedThresh is met. Doing the constraint will take longer, but a point is more likely to meet a threshold based on feature strength */ pullFlagConstraintBeforeSeedThresh, /* do no adding during population control */ pullFlagNoAdd, /* use the targetDim-based "enough" heuristic to guess whether adding a point could usefully reduce system energy. On by default; turn this off when using a large-support energy that involves more neighbors than the single-neighbor-deep energies that are normally used (like cwell:0.66,x or qwell:0.64) */ pullFlagPopCntlEnoughTest, /* if this is true, then the convergence test ignores whether or not there have been adds or nixes with the last round of pop cntl; otherwise (the default, for backwards compatibility), any adds or nixes would prevent convergence */ pullFlagConvergenceIgnoresPopCntl, /* no binning: all particles can potentially interact (for debugging) */ pullFlagBinSingle, /* whether to allow codimension-3 (point) constraint manifolds. Typical uses of constraints are for extracting lines and surfaces */ pullFlagAllowCodimension3Constraints, /* what we call scale is not sigma but rather the tau of gage */ pullFlagScaleIsTau, /* pullStart should skip initializing the points */ pullFlagStartSkipsPoints, /* constrain the third world-space coordinate ("z") to be zero, at all times, so that pull can be used to process 2D images */ pullFlagZeroZ, pullFlagLast }; typedef struct { int permuteOnRebin, noPopCntlWithZeroAlpha, useBetaForGammaLearn, restrictiveAddToBins, energyFromStrength, nixAtVolumeEdgeSpace, constraintBeforeSeedThresh, popCntlEnoughTest, convergenceIgnoresPopCntl, noAdd, binSingle, allowCodimension3Constraints, scaleIsTau, startSkipsPoints, zeroZ; } pullFlag; /* ******** pullContext ** ** everything for doing one computation ** */ typedef struct pullContext_t { /* INPUT ----------------------------- */ pullInitParm initParm; /* parms for initialization, set with the pullInit*Set() functions */ pullIterParm iterParm; /* parms about iterations and periods, set with pullIterParmSet() */ pullSysParm sysParm; /* continuous parameters for system, set with pullSysParmSet() */ pullFlag flag; /* all flags, set with pullFlagSet() */ int verbose; /* verbosity level, set with pullVerboseSet() */ unsigned int threadNum, /* number of threads to use, set with pullThreadNumSet() */ rngSeed, /* seed value for random number generator, NOT directly related to seed point placement (as name might suggest), set with pullRngSeedSet() */ progressBinMod; /* if non-zero, progress indication by printing "." is given when the bin index is a multiple of this; higher numbers give less feedback, set with pullProgressBinModSet() */ void (*iter_cb)(void *data_cb); /* callback to call from pullRun() every iterParm.callback iterations. This and data_cb are set w/ pullCallbackSet() */ void *data_cb; /* data to pass to callback */ pullVolume *vol[PULL_VOLUME_MAXNUM]; /* the volumes we analyze (we DO OWN), set by either pullVolumeSingleAdd() or pullVolumeStackAdd() */ unsigned int volNum; /* actual length of vol[] used */ pullInfoSpec *ispec[PULL_INFO_MAX+1]; /* info ii is in effect if ispec[ii] is non-NULL (and we DO OWN ispec[ii]), set by pullInfoSpecAdd() */ int interType; /* from the pullInterType* enum. This and the energy specs below are set by pullInterEnergySet() */ pullEnergySpec *energySpecR, /* starting point for radial potential energy function, phi_r */ *energySpecS, /* second energy potential function, for scale-space behavior, phi_s */ *energySpecWin; /* function used to window phi_r along s, and phi_s along r, for use with pullInterTypeAdditive */ /* INTERNAL -------------------------- */ unsigned int haltonOffset; /* with pullInitMethodHalton, add this to the index to sequence generation, to account for the points previously generated (which did not meet the constraint satisfaction) */ double bboxMin[4], bboxMax[4]; /* scale-space bounding box of all volumes: region over which binning is defined. In 3-D space, the bbox is axis aligned, even when the volume is not so aligned, which means that some bins might be under- or un- utilized, oh well. bboxMin[3] and bboxMax[3] are the bounds of the volume in *scale* (sigma), not t, or tau */ unsigned int infoTotalLen, /* total length of the info buffers needed, which determines size of allocated binPoint */ infoIdx[PULL_INFO_MAX+1], /* index of answer within pullPoint->info */ idtagNext; /* next per-point igtag value */ int haveScale, /* non-zero iff one of the volumes is in scale-space */ constraint, /* if non-zero, we have a constraint to satisfy, and this is its info number */ constraintDim, /* dimension of *spatial* constraint manifold we're working on; or -1 if unknown/unset */ targetDim, /* dimension of total constraint manifold which can be different than constraintDim because of scale-space, and either repulsive (+1) or attractive (+0) behavior along scale; or -1 if unknown/unset */ finished; /* used to signal all threads to return */ double maxDistSpace, /* max dist of point-point interaction in the spatial axes.*/ maxDistScale, /* max dist of point-point interaction along scale */ voxelSizeSpace, /* mean spatial voxel edge length, for limiting travel distance for descent and constraint satisfaction */ voxelSizeScale, /* mean voxel edge length in space, for limiting travel (along scale) distance during descent */ eipScale; /* how to scale energyIncreasePermit at each iteration, in accordance with energyIncreasePermitHalfLife */ pullBin *bin; /* volume of bins (see binsEdge, binNum) */ unsigned int binsEdge[4], /* # bins along each volume edge, determined by maxEval and scale */ binNum, /* total # bins in grid */ binNextIdx; /* next bin of points to be processed, we're done when binNextIdx == binNum */ unsigned int *tmpPointPerm; /* storing points during rebinning */ pullPoint **tmpPointPtr; unsigned int tmpPointNum; airThreadMutex *binMutex; /* mutex around bin, needed because bins are the unit of work for the tasks */ pullTask **task; /* dynamically allocated array of tasks */ airThreadBarrier *iterBarrierA; /* barriers between iterations */ airThreadBarrier *iterBarrierB; /* barriers between iterations */ #if PULL_HINTER Nrrd *nhinter; /* 2-D histogram of (r,s)-space relative locations of interacting particles (NOT thread safe) */ #endif FILE *logAdd; /* text-file record of all the particles that have been added (NOT thread-safe) */ /* OUTPUT ---------------------------- */ double timeIteration, /* time needed for last (single) iter */ timeRun, /* total time spent in pullRun() */ energy; /* final energy of system */ unsigned int addNum, /* # prtls added by PopCntl in last iter */ nixNum, /* # prtls nixed by PopCntl in last iter */ stuckNum, /* # stuck particles in last iter */ pointNum, /* total # particles */ CCNum, /* # connected components */ iter, /* how many iterations were needed (this is updated per iteration) */ /* HEY: this should really be per-task, to be thread-safe!! */ count[PULL_COUNT_MAX+1]; /* all possible kinds of counts */ } pullContext; /* ******** pullTrace */ typedef struct { double seedPos[4]; /* where was the seed point */ /* ------- output ------- */ Nrrd *nvert, /* locations of tract vertices */ *nstrn, /* if non-NULL, 1-D array of strengths */ *nvelo; /* 1-D list of velocities */ unsigned int seedIdx; /* which index in nvert is for seedpoint */ int whyStop[2], /* why backward/forward (0/1) tracing stopped (from pullTraceStop* enum) */ whyNowhere; /* why trace never started (from pullTraceStop*) */ } pullTrace; /* ******** pullTraceMulti */ typedef struct { pullTrace **trace; unsigned int traceNum; airArray *traceArr; } pullTraceMulti; /* ******** pullPtrPtrUnion ** ** deal with "dereferencing type-punned pointer will ** break strict-aliasing rules" */ typedef union { pullPoint ***points; /* address of array of point pointers */ void **v; } pullPtrPtrUnion; /* defaultsPull.c */ PULL_EXPORT const int pullPresent; PULL_EXPORT const int pullPhistEnabled; PULL_EXPORT const char *pullBiffKey; /* initPull.c */ PULL_EXPORT int pullInitRandomSet(pullContext *pctx, unsigned int numInitial); PULL_EXPORT int pullInitHaltonSet(pullContext *pctx, unsigned int numInitial, unsigned int start); PULL_EXPORT int pullInitPointPerVoxelSet(pullContext *pctx, int pointPerVoxel, unsigned int zSlcMin, unsigned int zSlcMax, unsigned int alongScaleNum, double jitter); PULL_EXPORT int pullInitGivenPosSet(pullContext *pctx, const Nrrd *npos); PULL_EXPORT int pullInitLiveThreshUseSet(pullContext *pctx, int liveThreshUse); PULL_EXPORT int pullInitUnequalShapesAllowSet(pullContext *pctx, int allow); /* parmPull.c */ PULL_EXPORT int pullIterParmSet(pullContext *pctx, int which, unsigned int pval); PULL_EXPORT int pullSysParmSet(pullContext *pctx, int which, double pval); PULL_EXPORT int pullFlagSet(pullContext *pctx, int which, int flag); PULL_EXPORT int pullVerboseSet(pullContext *pctx, int verbose); PULL_EXPORT int pullThreadNumSet(pullContext *pctx, unsigned int threadNum); PULL_EXPORT int pullRngSeedSet(pullContext *pctx, unsigned int rngSeed); PULL_EXPORT int pullProgressBinModSet(pullContext *pctx, unsigned int bmod); PULL_EXPORT int pullCallbackSet(pullContext *pctx, void (*iter_cb)(void *data_cb), void *data_cb); PULL_EXPORT int pullInterEnergySet(pullContext *pctx, int interType, const pullEnergySpec *enspR, const pullEnergySpec *enspS, const pullEnergySpec *enspWin); PULL_EXPORT int pullLogAddSet(pullContext *pctx, FILE *log); /* energy.c */ PULL_EXPORT const airEnum *const pullInterType; PULL_EXPORT const airEnum *const pullEnergyType; PULL_EXPORT const pullEnergy *const pullEnergyUnknown; PULL_EXPORT const pullEnergy *const pullEnergySpring; PULL_EXPORT const pullEnergy *const pullEnergyGauss; PULL_EXPORT const pullEnergy *const pullEnergyBspln; PULL_EXPORT const pullEnergy *const pullEnergyButterworth; PULL_EXPORT const pullEnergy *const pullEnergyCotan; PULL_EXPORT const pullEnergy *const pullEnergyCubic; PULL_EXPORT const pullEnergy *const pullEnergyQuartic; PULL_EXPORT const pullEnergy *const pullEnergyCubicWell; PULL_EXPORT const pullEnergy *const pullEnergyBetterCubicWell; PULL_EXPORT const pullEnergy *const pullEnergyQuarticWell; PULL_EXPORT const pullEnergy *const pullEnergyHepticWell; PULL_EXPORT const pullEnergy *const pullEnergyZero; PULL_EXPORT const pullEnergy *const pullEnergyButterworthParabola; PULL_EXPORT const pullEnergy *const pullEnergyAll[PULL_ENERGY_TYPE_MAX+1]; PULL_EXPORT pullEnergySpec *pullEnergySpecNew(void); PULL_EXPORT void pullEnergySpecSet(pullEnergySpec *ensp, const pullEnergy *energy, const double parm[PULL_ENERGY_PARM_NUM]); PULL_EXPORT void pullEnergySpecCopy(pullEnergySpec *esDst, const pullEnergySpec *esSrc); PULL_EXPORT pullEnergySpec *pullEnergySpecNix(pullEnergySpec *ensp); PULL_EXPORT int pullEnergySpecParse(pullEnergySpec *ensp, const char *str); PULL_EXPORT hestCB *pullHestEnergySpec; /* volumePull.c */ PULL_EXPORT pullVolume *pullVolumeNew(void); PULL_EXPORT pullVolume *pullVolumeNix(pullVolume *vol); PULL_EXPORT int pullVolumeSingleAdd(pullContext *pctx, const gageKind *kind, char *name, const Nrrd *nin, const NrrdKernelSpec *ksp00, const NrrdKernelSpec *ksp11, const NrrdKernelSpec *ksp22); PULL_EXPORT int pullVolumeStackAdd(pullContext *pctx, const gageKind *kind, char *name, const Nrrd *nin, const Nrrd *const *ninSS, double *scalePos, unsigned int ninNum, int scaleDerivNorm, double scaleDerivNormBias, const NrrdKernelSpec *ksp00, const NrrdKernelSpec *ksp11, const NrrdKernelSpec *ksp22, const NrrdKernelSpec *kspSS); PULL_EXPORT const pullVolume *pullVolumeLookup(const pullContext *pctx, const char *volName); PULL_EXPORT int pullConstraintScaleRange(pullContext *pctx, double ssrange[2]); /* enumsPull.c */ PULL_EXPORT const airEnum *const pullInfo; PULL_EXPORT const airEnum *const pullSource; PULL_EXPORT const airEnum *const pullProp; PULL_EXPORT const airEnum *const pullProcessMode; PULL_EXPORT const airEnum *const pullTraceStop; PULL_EXPORT const airEnum *const pullCount; PULL_EXPORT const airEnum *const pullConstraintFail; /* infoPull.c */ PULL_EXPORT unsigned int pullPropLen(int prop); PULL_EXPORT unsigned int pullInfoLen(int info); PULL_EXPORT pullInfoSpec *pullInfoSpecNew(void); PULL_EXPORT pullInfoSpec *pullInfoSpecNix(pullInfoSpec *ispec); PULL_EXPORT int pullInfoSpecAdd(pullContext *pctx, pullInfoSpec *ispec); PULL_EXPORT int pullInfoGet(Nrrd *ninfo, int info, pullContext *pctx); PULL_EXPORT int pullInfoSpecSprint(char str[AIR_STRLEN_LARGE], const pullContext *pctx, const pullInfoSpec *ispec); /* contextPull.c */ PULL_EXPORT pullContext *pullContextNew(void); PULL_EXPORT pullContext *pullContextNix(pullContext *pctx); PULL_EXPORT int pullOutputGet(Nrrd *nPosOut, Nrrd *nTenOut, Nrrd *nStrengthOut, const double scaleVec[3], double scaleRad, pullContext *pctx); PULL_EXPORT int pullOutputGetFilter(Nrrd *nPosOut, Nrrd *nTenOut, Nrrd *nStrengthOut, const double scaleVec[3], double scaleRad, pullContext *pctx, unsigned int idtagMin, unsigned int idtagMax); PULL_EXPORT int pullPositionHistoryGet(limnPolyData *pld, pullContext *pctx); PULL_EXPORT int pullPropGet(Nrrd *nprop, int prop, pullContext *pctx); /* pointPull.c */ PULL_EXPORT int pullPointInitializePerVoxel(const pullContext *pctx, const unsigned int pointIdx, pullPoint *point, pullVolume *scaleVol, int *createFailP); PULL_EXPORT int pullPointInitializeRandomOrHalton(pullContext *pctx, const unsigned int pointIdx, pullPoint *point, pullVolume *scaleVol); PULL_EXPORT int pullPointInitializeGivenPos(pullContext *pctx, const double *posData, const unsigned int pointIdx, pullPoint *point, int *createFailP); PULL_EXPORT double pullPointScalar(const pullContext *pctx, const pullPoint *point, int sclInfo, double grad[4], double hess[9]); PULL_EXPORT unsigned int pullPointNumber(const pullContext *pctx); PULL_EXPORT unsigned int pullPointNumberFilter(const pullContext *pctx, unsigned int idtagMin, unsigned int idtagMax); PULL_EXPORT pullPoint *pullPointNew(pullContext *pctx); PULL_EXPORT pullPoint *pullPointNix(pullPoint *pnt); PULL_EXPORT int pullProbe(pullTask *task, pullPoint *point); /* binningPull.c */ PULL_EXPORT int pullBinsPointAdd(pullContext *pctx, pullPoint *point, /* output */ pullBin **binUsed); PULL_EXPORT int pullBinsPointMaybeAdd(pullContext *pctx, pullPoint *point, /* output */ pullBin **binUsed, int *added); /* trace.c */ PULL_EXPORT pullTrace *pullTraceNew(void); PULL_EXPORT pullTrace *pullTraceNix(pullTrace *pts); PULL_EXPORT size_t pullTraceMultiSizeof(const pullTraceMulti *mtrc); PULL_EXPORT int pullTraceSet(pullContext *pctx, pullTrace *trc, int recordStrength, double scaleDelta, double halfScaleWin, double velocityMax, unsigned int arrIncr, const double seedPos[4]); PULL_EXPORT pullTraceMulti *pullTraceMultiNew(void); PULL_EXPORT pullTraceMulti *pullTraceMultiNix(pullTraceMulti *mtrc); PULL_EXPORT int pullTraceMultiAdd(pullTraceMulti *mtrc, pullTrace *trc, int *addedP); PULL_EXPORT int pullTraceMultiFilterConcaveDown(Nrrd *nfilt, const pullTraceMulti *mtrc, double winLenFrac); PULL_EXPORT int pullTraceMultiPlotAdd(Nrrd *nplot, const pullTraceMulti *mtrc, const Nrrd *nfilt, int strengthUse, unsigned int trcIdxMin, unsigned int trcNum); PULL_EXPORT int pullTraceMultiWrite(FILE *file, const pullTraceMulti *mtrc); PULL_EXPORT int pullTraceMultiRead(pullTraceMulti *mtrc, FILE *file); /* actionPull.c */ PULL_EXPORT int pullEnergyPlot(pullContext *pctx, Nrrd *nplot, double xx, double yy, double zz, unsigned int res); PULL_EXPORT int pullBinProcess(pullTask *task, unsigned int myBinIdx); PULL_EXPORT int pullGammaLearn(pullContext *pctx); /* corePull.c */ PULL_EXPORT int pullStart(pullContext *pctx); PULL_EXPORT int pullRun(pullContext *pctx); PULL_EXPORT int pullFinish(pullContext *pctx); /* ccPull.c */ PULL_EXPORT int pullCCFind(pullContext *pctx); PULL_EXPORT int pullCCMeasure(pullContext *pctx, Nrrd *nmeas, int measrInfo, double rho); PULL_EXPORT int pullCCSort(pullContext *pctx, int measrInfo, double rho); #ifdef __cplusplus } #endif #endif /* PULL_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/meet/0000775000175000017500000000000012203513756015332 5ustar domibeldomibelteem-1.11.0~svn6057/src/meet/meetNrrd.c0000664000175000017500000004071212165631065017263 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "meet.h" typedef union { const NrrdKernel ***k; void **v; } _kernu; /* ** ALLOCATES and returns a NULL-terminated array of all the ** NrrdKernels in Teem */ const NrrdKernel ** meetNrrdKernelAll(void) { airArray *arr; const NrrdKernel **kern; unsigned int ii; int di, ci, ai, dmax, cmax, amax; _kernu ku; ku.k = &kern; arr = airArrayNew(ku.v, NULL, sizeof(NrrdKernel *), 2); /* kernel.c */ ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelZero; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBox; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBoxSupportDebug; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCatmullRomSupportDebug; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCatmullRomSupportDebugD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCatmullRomSupportDebugDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCos4SupportDebug; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCos4SupportDebugD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCos4SupportDebugDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCos4SupportDebugDDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCheap; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelHermiteScaleSpaceFlag; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelTent; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelForwDiff; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCentDiff; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBCCubic; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBCCubicD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBCCubicDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCatmullRom; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCatmullRomD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelCatmullRomDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelAQuartic; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelAQuarticD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelAQuarticDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC3Quintic; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC3QuinticD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC3QuinticDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC4Hexic; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC4HexicD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC4HexicDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC4HexicDDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC4HexicApproxInverse; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC5Septic; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC5SepticD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC5SepticDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC5SepticDDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelC5SepticApproxInverse; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelGaussian; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelGaussianD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelGaussianDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelDiscreteGaussian; /* winKernel.c */ ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelHann; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelHannD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelHannDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBlackman; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBlackmanD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBlackmanDD; /* bsplKernel.c */ ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline1; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline1D; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline2; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline2D; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline2DD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline3; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline3D; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline3DD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline3DDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline3ApproxInverse; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline4; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline4D; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline4DD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline4DDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline5; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline5D; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline5DD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline5DDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline5ApproxInverse; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline6; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline6D; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline6DD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline6DDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline7; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline7D; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline7DD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline7DDD; ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelBSpline7ApproxInverse; /* tmfKernel.c nrrdKernelTMF[D+1][C+1][A] is d_c_ef: Dth-derivative, C-order continuous ("smooth"), A-order accurate (for D and C, index 0 accesses the function for -1) NRRD_EXPORT NrrdKernel *const nrrdKernelTMF[4][5][5]; */ dmax = AIR_CAST(int, nrrdKernelTMF_maxD); cmax = AIR_CAST(int, nrrdKernelTMF_maxC); amax = AIR_CAST(int, nrrdKernelTMF_maxA); for (di=-1; di<=dmax; di++) { for (ci=-1; ci<=cmax; ci++) { for (ai=1; ai<=amax; ai++) { ii = airArrayLenIncr(arr, 1); kern[ii] = nrrdKernelTMF[di+1][ci+1][ai]; } } } /* NULL-terminate the list */ ii = airArrayLenIncr(arr, 1); kern[ii] = NULL; /* nix, not nuke the airArray */ airArrayNix(arr); return kern; } /* the knowledge here about what is a derivative of what is something that will be built into kernels in a future Teem version */ static const NrrdKernel * kintegral(const NrrdKernel *kd) { const NrrdKernel *ret=NULL; /* the statement "INTGL(K)" is saying that K has a derivative with the usual name K##D. This is made more convenient by the consistent use of the "D" suffix for indicating a derivative */ #define INTGL(K) if (K##D == kd) { ret = K; } INTGL(nrrdKernelHann); INTGL(nrrdKernelHannD); INTGL(nrrdKernelBlackman); INTGL(nrrdKernelBlackmanD); INTGL(nrrdKernelBSpline1); INTGL(nrrdKernelBSpline2); INTGL(nrrdKernelBSpline2D); INTGL(nrrdKernelBSpline3); INTGL(nrrdKernelBSpline3D); INTGL(nrrdKernelBSpline3DD); INTGL(nrrdKernelBSpline4); INTGL(nrrdKernelBSpline4D); INTGL(nrrdKernelBSpline4DD); INTGL(nrrdKernelBSpline5); INTGL(nrrdKernelBSpline5D); INTGL(nrrdKernelBSpline5DD); INTGL(nrrdKernelBSpline6); INTGL(nrrdKernelBSpline6D); INTGL(nrrdKernelBSpline6DD); INTGL(nrrdKernelBSpline7); INTGL(nrrdKernelBSpline7D); INTGL(nrrdKernelBSpline7DD); INTGL(nrrdKernelCos4SupportDebug); INTGL(nrrdKernelCos4SupportDebugD); INTGL(nrrdKernelCos4SupportDebugDD); INTGL(nrrdKernelCatmullRomSupportDebug); INTGL(nrrdKernelCatmullRomSupportDebugD); INTGL(nrrdKernelBCCubic); INTGL(nrrdKernelBCCubicD); INTGL(nrrdKernelCatmullRom); INTGL(nrrdKernelCatmullRomD); INTGL(nrrdKernelAQuartic); INTGL(nrrdKernelAQuarticD); INTGL(nrrdKernelC3Quintic); INTGL(nrrdKernelC3QuinticD); INTGL(nrrdKernelC4Hexic); INTGL(nrrdKernelC4HexicD); INTGL(nrrdKernelC4HexicDD); INTGL(nrrdKernelC5Septic); INTGL(nrrdKernelC5SepticD); INTGL(nrrdKernelC5SepticDD); INTGL(nrrdKernelGaussian); INTGL(nrrdKernelGaussianD); #undef INTGL return ret; } /* ** Does more than call nrrdKernelCheck on all kernels: ** makes sure that all kernels have unique names ** makes sure that derivative relationships are correct ** Also, simply calling nrrdKernelCheck requires some knowledge ** of the kernel's needed parameters ** ** HEY: its problematic that because the various kernels have different ** parameter epsilon requirements, they usually end up having to be ** enumerated in some of the if/else statements below; it would be much ** better if new kernels didn't need to be so explicitly added! */ int meetNrrdKernelAllCheck(void) { static const char me[]="meetNrrdKernelAllCheck"; const NrrdKernel **kern, *kk, *ll; unsigned int ki, kj, pnum; airArray *mop; double epsl, XX, YY, parm0[NRRD_KERNEL_PARMS_NUM], parm1_1[NRRD_KERNEL_PARMS_NUM], parm1_X[NRRD_KERNEL_PARMS_NUM], parm[NRRD_KERNEL_PARMS_NUM]; size_t evalNum; int EE; mop = airMopNew(); kern = meetNrrdKernelAll(); airMopAdd(mop, AIR_CAST(void*, kern), airFree, airMopAlways); evalNum = 120000; /* success of kernel integral test is surprisingly dependent on this, likely due to the naive way the integral is numerically computed; the current value here represents some experimentation */ epsl = 0.9e-5; XX = 7.0/3.0; /* 2.333.. */ YY = 43.0/9.0; /* 4.777.. */ parm0[0] = AIR_NAN; /* shouldn't be read */ parm1_1[0] = 1.0; parm1_X[0] = XX; ki = 0; while ((kk = kern[ki])) { kj = 0; while (kj < ki) { ll = kern[kj]; if (kk == ll) { biffAddf(MEET, "%s: kern[%u] and [%u] were identical (%s)", me, kj, ki, kk->name); airMopError(mop); return 1; } if (!airStrcmp(kk->name, ll->name)) { biffAddf(MEET, "%s: kern[%u] and [%u] have same name (%s)", me, kj, ki, kk->name); airMopError(mop); return 1; } kj++; } pnum = kk->numParm; EE = 0; /* the second argument to CHECK is how much to scale up the permissible error in kernel evaluations (between float and double) The kernels for which this is higher should be targets for re-coding with an eye towards numerical accuracy */ #define CHECK(P, S, N) \ if (!EE) EE |= nrrdKernelCheck(kk, (P), evalNum, epsl*(S), \ N, N, \ kintegral(kk), (P)); if (nrrdKernelBCCubic == kk || nrrdKernelBCCubicD == kk || nrrdKernelBCCubicDD == kk) { /* try a few settings of the 3 parms */ ELL_3V_SET(parm, 1.0, 0.0, 0.0); CHECK(parm, 1, 2); ELL_3V_SET(parm, XX, 0.0, 0.0); CHECK(parm, 1, 2); ELL_3V_SET(parm, 1.0, 1.0/3.0, 1.0/3.0); CHECK(parm, 1, 2); ELL_3V_SET(parm, XX, 1.0/3.0, 1.0/3.0); CHECK(parm, 1, 2); ELL_3V_SET(parm, 1.0, 0.0, 1.0); CHECK(parm, 1, 2); ELL_3V_SET(parm, XX, 0.0, 1.0); CHECK(parm, 1, 2); ELL_3V_SET(parm, 1.0, 0.5, 0.0); CHECK(parm, 1, 2); ELL_3V_SET(parm, XX, 0.5, 0.0); CHECK(parm, 1, 2); } else if (2 == pnum) { if (nrrdKernelAQuartic == kk || nrrdKernelAQuarticD == kk || nrrdKernelAQuarticDD == kk) { ELL_2V_SET(parm, 1.0, 0.0); CHECK(parm, 10, 2); ELL_2V_SET(parm, 1.0, 0.5); CHECK(parm, 10, 2); ELL_2V_SET(parm, XX, 0.0); CHECK(parm, 10, 2); ELL_2V_SET(parm, XX, 0.5); CHECK(parm, 10, 2); } else if (nrrdKernelGaussian == kk || nrrdKernelGaussianD == kk || nrrdKernelGaussianDD == kk) { ELL_2V_SET(parm, 0.1, XX); CHECK(parm, 10, 2); ELL_2V_SET(parm, 0.1, YY); CHECK(parm, 10, 2); ELL_2V_SET(parm, 1.0, XX); CHECK(parm, 10, 2); ELL_2V_SET(parm, 1.0, YY); CHECK(parm, 10, 2); ELL_2V_SET(parm, XX, XX); CHECK(parm, 10, 2); ELL_2V_SET(parm, XX, YY); CHECK(parm, 10, 2); } else if (nrrdKernelHann == kk || nrrdKernelHannD == kk || nrrdKernelBlackman == kk) { ELL_2V_SET(parm, 0.5, XX); CHECK(parm, 100, 2); ELL_2V_SET(parm, 0.5, YY); CHECK(parm, 100, 2); ELL_2V_SET(parm, 1.0, XX); CHECK(parm, 100, 2); ELL_2V_SET(parm, 1.0, YY); CHECK(parm, 100, 2); ELL_2V_SET(parm, XX, XX); CHECK(parm, 100, 2); ELL_2V_SET(parm, XX, YY); CHECK(parm, 100, 2); } else if (nrrdKernelHannDD == kk || nrrdKernelBlackmanD == kk || nrrdKernelBlackmanDD == kk) { /* there are apparently bugs in these kernels */ ELL_2V_SET(parm, 0.5, XX); CHECK(parm, 10000000, 2); ELL_2V_SET(parm, 0.5, YY); CHECK(parm, 10000000, 2); ELL_2V_SET(parm, 1.0, XX); CHECK(parm, 1000000, 2); ELL_2V_SET(parm, 1.0, YY); CHECK(parm, 1000000, 2); ELL_2V_SET(parm, XX, XX); CHECK(parm, 100000, 2); ELL_2V_SET(parm, XX, YY); CHECK(parm, 100000, 2); } else if (nrrdKernelDiscreteGaussian == kk) { ELL_2V_SET(parm, 0.1, XX); CHECK(parm, 1, 2); ELL_2V_SET(parm, 0.1, YY); CHECK(parm, 1, 2); ELL_2V_SET(parm, 1.0, XX); CHECK(parm, 1, 2); ELL_2V_SET(parm, 1.0, YY); CHECK(parm, 1, 2); ELL_2V_SET(parm, XX, XX); CHECK(parm, 1, 2); ELL_2V_SET(parm, XX, YY); CHECK(parm, 1, 2); } else { biffAddf(MEET, "%s: sorry, got unexpected 2-parm kernel %s", me, kk->name); airMopError(mop); return 1; } } else if (1 == pnum) { if (strstr(kk->name, "TMF")) { /* these take a single parm, but its not support */ parm[0] = 0.0; CHECK(parm, 10, 2); parm[0] = 1.0/3.0; CHECK(parm, 10, 2); } else { /* zero, box, boxsup, cos4sup{,D,DD,DDD}, cheap, ctmrsup{,D,DD}, tent, fordif, cendif */ /* takes a single support/scale parm[0], try two different values */ if (nrrdKernelCos4SupportDebug == kk || nrrdKernelCos4SupportDebugD == kk || nrrdKernelCos4SupportDebugDD == kk || nrrdKernelCos4SupportDebugDDD == kk || nrrdKernelCatmullRomSupportDebugD == kk || nrrdKernelCatmullRomSupportDebugDD == kk) { CHECK(parm1_1, 10, 4); CHECK(parm1_X, 10, 4); } else { CHECK(parm1_1, 1, 2); CHECK(parm1_X, 1, 2); } } } else if (0 == pnum) { /* C3Quintic{,D,DD,DD}, C4Hexic{,D,DD,DDD}, C5Septic{,D}, hermiteSS, catmull-rom{,D}, bspl{3,5,7}{,D,DD,DDD} */ if (nrrdKernelC3Quintic == kk || nrrdKernelC3QuinticD == kk || nrrdKernelC3QuinticDD == kk || nrrdKernelC4Hexic == kk || nrrdKernelC4HexicD == kk || nrrdKernelC4HexicDD == kk || nrrdKernelC4HexicDDD == kk || nrrdKernelC5Septic == kk || nrrdKernelC5SepticD == kk || nrrdKernelC5SepticDD == kk || nrrdKernelC5SepticDDD == kk ) { CHECK(parm0, 1, 2); CHECK(parm0, 1, 2); } else if (nrrdKernelBSpline5DD == kk || nrrdKernelBSpline5DDD == kk || nrrdKernelBSpline7DD == kk ) { CHECK(parm0, 100, 2); } else { CHECK(parm0, 10, 2); } } else { biffAddf(MEET, "%s: sorry, didn't expect %u parms for %s", me, pnum, kk->name); airMopError(mop); return 1; } #undef CHECK if (EE) { biffMovef(MEET, NRRD, "%s: problem with kern[%u] \"%s\"", me, ki, kk->name ? kk->name : "(NULL name)"); airMopError(mop); return 1; } ki++; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/meet/meetPull.c0000664000175000017500000005676612174673217017317 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "meet.h" meetPullVol * meetPullVolNew(void) { meetPullVol *ret; ret = AIR_CALLOC(1, meetPullVol); if (ret) { ret->kind = NULL; ret->fileName = ret->volName = NULL; ret->derivNormSS = AIR_FALSE; ret->uniformSS = AIR_FALSE; ret->optimSS = AIR_FALSE; ret->leeching = AIR_FALSE; ret->numSS = 0; ret->rangeSS[0] = ret->rangeSS[1] = AIR_NAN; ret->derivNormBiasSS = 0.0; ret->posSS = NULL; ret->nin = NULL; ret->ninSS = NULL; } return ret; } meetPullVol * meetPullVolCopy(const meetPullVol *mpv) { meetPullVol *ret; unsigned int si; ret = meetPullVolNew(); /* HEY: hope this is okay for dynamic kinds */ ret->kind = mpv->kind; ret->fileName = airStrdup(mpv->fileName); ret->volName = airStrdup(mpv->volName); ret->derivNormSS = mpv->derivNormSS; ret->uniformSS = mpv->uniformSS; ret->optimSS = mpv->optimSS; ret->leeching = AIR_FALSE; ret->recomputedSS = AIR_FALSE; ret->numSS = mpv->numSS; ret->rangeSS[0] = mpv->rangeSS[0]; ret->rangeSS[1] = mpv->rangeSS[1]; ret->derivNormBiasSS = mpv->derivNormBiasSS; ret->posSS = AIR_CALLOC(ret->numSS, double); /* HEY: no error checking */ for (si=0; sinumSS; si++) { ret->posSS[si] = mpv->posSS[si]; } if (mpv->numSS) { ret->nin = NULL; ret->ninSS = AIR_CALLOC(ret->numSS, Nrrd *); for (si=0; sinumSS; si++) { ret->ninSS[si] = nrrdNew(); nrrdCopy(ret->ninSS[si], mpv->ninSS[si]); } } else { ret->nin = nrrdNew(); nrrdCopy(ret->nin, mpv->nin); ret->ninSS = NULL; } return ret; } /* ******** meetPullVolParse ** ** parses a string to extract all the information necessary to create ** the pullVolume (this function originated in Deft/src/main-pull.c) */ int meetPullVolParse(meetPullVol *mpv, const char *_str) { static const char me[]="meetPullVolParse"; #define VFMT_SHRT "::" #define SFMT "-<#smp>-[-no|u]" #define VFMT_LONG "::" SFMT ":" char *str, *ctok, *clast=NULL, *dtok, *dlast=NULL; airArray *mop; int wantSS; if (!(mpv && _str)) { biffAddf(MEET, "%s: got NULL pointer", me); return 1; } if (!( str = airStrdup(_str) )) { biffAddf(MEET, "%s: couldn't strdup input", me); return 1; } mop = airMopNew(); airMopAdd(mop, str, airFree, airMopAlways); if (!( 3 == airStrntok(str, ":") || 4 == airStrntok(str, ":") )) { biffAddf(MEET, "%s: didn't get 3 or 4 \":\"-separated tokens in \"%s\"; " "not of form " VFMT_SHRT " or " VFMT_LONG , me, _str); airMopError(mop); return 1; } /* mpv->nin is set elsewhere */ wantSS = (4 == airStrntok(str, ":")); ctok = airStrtok(str, ":", &clast); if (!(mpv->fileName = airStrdup(ctok))) { biffAddf(MEET, "%s: couldn't strdup fileName", me); airMopError(mop); return 1; } airMopAdd(mop, &(mpv->fileName), (airMopper)airSetNull, airMopOnError); airMopAdd(mop, mpv->fileName, airFree, airMopOnError); ctok = airStrtok(NULL, ":", &clast); if (!(mpv->kind = meetConstGageKindParse(ctok))) { biffAddf(MEET, "%s: couldn't parse \"%s\" as kind", me, ctok); airMopError(mop); return 1; } if (wantSS) { int haveFlags; ctok = airStrtok(NULL, ":", &clast); if (!( 3 == airStrntok(ctok, "-") || 4 == airStrntok(ctok, "-") )) { biffAddf(MEET, "%s: didn't get 3 or 4 \"-\"-separated tokens in \"%s\"; " "not of form SFMT" , me, ctok); airMopError(mop); return 1; } haveFlags = (4 == airStrntok(ctok, "-")); dtok = airStrtok(ctok, "-", &dlast); if (1 != sscanf(dtok, "%lg", &(mpv->rangeSS[0]))) { biffAddf(MEET, "%s: couldn't parse \"%s\" as min scale", me, dtok); airMopError(mop); return 1; } dtok = airStrtok(NULL, "-", &dlast); if (1 != sscanf(dtok, "%u", &(mpv->numSS))) { biffAddf(MEET, "%s: couldn't parse \"%s\" as # scale samples", me, dtok); airMopError(mop); return 1; } if (!( mpv->numSS >= 2 )) { biffAddf(MEET, "%s: need # scale samples >= 2 (not %u)", me, mpv->numSS); airMopError(mop); return 1; } dtok = airStrtok(NULL, "-", &dlast); if (1 != sscanf(dtok, "%lg", &(mpv->rangeSS[1]))) { biffAddf(MEET, "%s: couldn't parse \"%s\" as max scale", me, dtok); airMopError(mop); return 1; } /* initialize things as if there were no flags */ mpv->derivNormSS = AIR_FALSE; mpv->uniformSS = AIR_FALSE; mpv->optimSS = AIR_FALSE; mpv->derivNormBiasSS = 0.0; if (haveFlags) { char *flags, *bias; /* look for various things in flags */ flags = airToLower(airStrdup(airStrtok(NULL, "-", &dlast))); airMopAdd(mop, flags, airFree, airMopAlways); if (strchr(flags, 'n')) { mpv->derivNormSS = AIR_TRUE; } if (strchr(flags, 'u')) { mpv->uniformSS = AIR_TRUE; } if (strchr(flags, 'o')) { mpv->optimSS = AIR_TRUE; } if (mpv->optimSS && mpv->uniformSS) { biffAddf(MEET, "%s: can't have both optimal ('o') and uniform ('u') " "flags set in \"%s\"", me, flags); airMopError(mop); return 1; } if ((bias = strchr(flags, '+'))) { /* indicating a bias, unfortunately only a positive one is possible here, because of the way that other fields are tokenized by '-' */ bias++; if (1 != sscanf(bias, "%lf", &(mpv->derivNormBiasSS))) { biffAddf(MEET, "%s: couldn't parse bias \"%s\"", me, bias); airMopError(mop); return 1; } } } /* mpv->ninSS and mpv->posSS are allocated and filled elsewhere */ mpv->ninSS = NULL; mpv->posSS = NULL; /* we don't actually create nrrds nor load the volumes here, because we don't know cachePath, and because we might want different pullVolumes to share the same underlying Nrrds */ } else { /* no scale-space stuff wanted */ mpv->numSS = 0; mpv->rangeSS[0] = mpv->rangeSS[1] = AIR_NAN; mpv->ninSS = NULL; mpv->posSS = NULL; } ctok = airStrtok(NULL, ":", &clast); if (!(mpv->volName = airStrdup(ctok))) { biffAddf(MEET, "%s: couldn't strdup volName", me); airMopError(mop); return 1; } airMopAdd(mop, &(mpv->volName), (airMopper)airSetNull, airMopOnError); airMopAdd(mop, mpv->volName, airFree, airMopOnError); if (strchr(ctok, '-')) { biffAddf(MEET, "%s: you probably don't want \"-\" in volume name \"%s\"; " "forgot last \":\" in scale sampling specification?", me, ctok); airMopError(mop); return 1; } airMopOkay(mop); return 0; } int meetHestPullVolParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { static const char me[]="meetHestPullVolParse"; meetPullVol *mpv, **mpvP; airArray *mop; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); mpvP = AIR_CAST(meetPullVol **, ptr); *mpvP = mpv = meetPullVolNew(); airMopAdd(mop, mpvP, (airMopper)airSetNull, airMopOnError); airMopAdd(mop, mpv, (airMopper)meetPullVolNix, airMopOnError); if (meetPullVolParse(mpv, str)) { char *ler; airMopAdd(mop, ler = biffGetDone(MEET), airFree, airMopOnError); airStrcpy(err, AIR_STRLEN_HUGE, ler); airMopError(mop); return 1; } airMopOkay(mop); return 0; } /* ******** meetPullVolNix ** ** this frees stuff allocated meetPullVolParse and meetPullVolLoadMulti */ meetPullVol * meetPullVolNix(meetPullVol *mpv) { if (mpv) { if (!mpv->leeching && mpv->nin) { nrrdNuke(mpv->nin); } if (mpv->numSS) { unsigned int ssi; if (mpv->ninSS) { /* need this check because the mpv may not have benefitted from meetPullVolLoadMulti, so it might be incomplete */ for (ssi=0; ssinumSS; ssi++) { if (!mpv->leeching) { nrrdNuke(mpv->ninSS[ssi]); } } airFree(mpv->ninSS); } airFree(mpv->posSS); } airFree(mpv->fileName); airFree(mpv->volName); airFree(mpv); } return NULL; } hestCB _meetHestPullVol = { sizeof(meetPullVol *), "meetPullVol", meetHestPullVolParse, (airMopper)meetPullVolNix, }; hestCB * meetHestPullVol = &_meetHestPullVol; /* ******** meetPullVolLeechable ** ** indicates whether lchr can leech from orig */ int meetPullVolLeechable(const meetPullVol *lchr, const meetPullVol *orig) { static const char me[]="meetPullVolLeechable"; int can, verbose; verbose = 0; can = !!strcmp(orig->fileName, "-"); /* can, if not reading from stdin */ if (verbose && !can) { fprintf(stderr, "%s: no: from stdin\n", me); } can &= !strcmp(orig->fileName, lchr->fileName); /* come from same file */ if (verbose && !can) { fprintf(stderr, "%s: no: not from same file\n", me); } can &= (orig->kind == lchr->kind); /* same kind */ if (verbose && !can) { fprintf(stderr, "%s: no: not same kind\n", me); } /* need to have different volname */ can &= (orig->numSS == lchr->numSS); /* same scale space */ if (verbose && !can) { fprintf(stderr, "%s: no: not same scale space\n", me); } if (lchr->numSS) { /* DO allow difference in derivNormSS (the main reason for leeching) */ /* same SS sampling strategy */ can &= (orig->uniformSS == lchr->uniformSS); if (verbose && !can) { fprintf(stderr, "%s: no: not same uniformSS\n", me); } can &= (orig->optimSS == lchr->optimSS); if (verbose && !can) { fprintf(stderr, "%s: no: not same optimSS\n", me); } /* same SS range */ can &= (orig->rangeSS[0] == lchr->rangeSS[0]); can &= (orig->rangeSS[1] == lchr->rangeSS[1]); if (verbose && !can) { fprintf(stderr, "%s: no: not same rangeSS\n", me); } } return can; } void meetPullVolLeech(meetPullVol *vol, const meetPullVol *volPrev) { if (vol && volPrev) { vol->nin = volPrev->nin; if (vol->numSS) { unsigned int ni; /* have to allocate ninSS here; in volPrev it was probably allocated by gageStackBlurManage */ vol->ninSS = AIR_CALLOC(vol->numSS, Nrrd *); /* have to allocate posSS here; in volPrev is was probably allocated by meetPullVolLoadMulti */ vol->posSS = AIR_CALLOC(vol->numSS, double); for (ni=0; ninumSS; ni++) { vol->ninSS[ni] = volPrev->ninSS[ni]; vol->posSS[ni] = volPrev->posSS[ni]; } } vol->leeching = AIR_TRUE; } return; } /* ******** meetPullVolLoadMulti ** ** at this point the per-pullVolume information required for ** loading/creating the volumes, which is NOT in the meetPullVol, is ** the cachePath and the fields we have to set in the ** gageStackBlurParm, so these have to be passed explicitly. ** ** The passed sbparm is only for communication boundary, padValue, ** verbose, etc: the various little parameters for stack blurring, ** which are themselves changing with experimentation. This is ** cleaner than passing them as separate arguments to ** meetPullVolLoadMulti. This change was prompted by the addition ** of sbparm->oneDim. sbparm is only needed if there are ** scale-space volumes being loaded. */ int meetPullVolLoadMulti(meetPullVol **mpv, unsigned int mpvNum, char *cachePath, NrrdKernelSpec *kSSblur, const gageStackBlurParm *sbparm, int verbose) { static const char me[]="meetPullVolLoadMulti"; char formatSS[AIR_STRLEN_LARGE]; unsigned int mpvIdx; gageStackBlurParm *sbp; airArray *mop; meetPullVol *vol; if (!( mpv && cachePath && kSSblur)) { biffAddf(MEET, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); /* this can be re-used for different volumes */ sbp = gageStackBlurParmNew(); airMopAdd(mop, sbp, (airMopper)gageStackBlurParmNix, airMopAlways); if (sbparm) { if (gageStackBlurParmVerboseSet(sbp, sbparm->verbose) || gageStackBlurParmOneDimSet(sbp, sbparm->oneDim) || gageStackBlurParmKernelSet(sbp, kSSblur, AIR_TRUE) || gageStackBlurParmBoundarySet(sbp, sbparm->boundary, sbparm->padValue)) { biffMovef(MEET, GAGE, "%s: trouble with stack blur parms", me); airMopError(mop); return 1; } } for (mpvIdx=0; mpvIdxvolName, pvi, mpv[pvi]->volName); } continue; } /* else we're not leeching */ vol->leeching = AIR_FALSE; if (verbose) { fprintf(stderr, "%s: vspec[%u] (%s) cannot leech\n", me, mpvIdx, vol->volName); } /* if this is the pull we only have to learn the scale samples, but these might even be used */ vol->nin = nrrdNew(); airMopAdd(mop, &(vol->nin), (airMopper)airSetNull, airMopOnError); airMopAdd(mop, vol->nin, (airMopper)nrrdNuke, airMopOnError); if (nrrdLoad(vol->nin, vol->fileName, NULL)) { biffMovef(MEET, NRRD, "%s: trouble loading mpv[%u]->nin (\"%s\")", me, mpvIdx, vol->volName); airMopError(mop); return 1; } if (vol->numSS) { sprintf(formatSS, "%s/%s-%%03u-%03u.nrrd", cachePath, vol->volName, vol->numSS); if (verbose) { fprintf(stderr, "%s: managing %s ... \n", me, formatSS); } if (gageStackBlurParmScaleSet(sbp, vol->numSS, vol->rangeSS[0], vol->rangeSS[1], vol->uniformSS, vol->optimSS) || gageStackBlurManage(&(vol->ninSS), &(vol->recomputedSS), sbp, formatSS, AIR_TRUE, NULL, vol->nin, vol->kind)) { biffMovef(MEET, GAGE, "%s: trouble getting volume stack (\"%s\")", me, formatSS); airMopError(mop); return 1; } if (verbose) { fprintf(stderr, "%s: ... done\n", me); } } /* allocate and set vol->posSS from sbp-scale regardless of kind */ if (vol->numSS) { vol->posSS = AIR_CALLOC(sbp->num, double); for (ssi=0; ssinum; ssi++) { vol->posSS[ssi] = sbp->scale[ssi]; } } } airMopOkay(mop); return 0; } /* ******** meetPullVolAddMulti ** ** the spatial (k00, k11, k22) and scale (kSSrecon) reconstruction ** kernels are not part of the meetPullVol, so have to be passed in here */ int meetPullVolAddMulti(pullContext *pctx, meetPullVol **mpv, unsigned int mpvNum, const NrrdKernelSpec *k00, const NrrdKernelSpec *k11, const NrrdKernelSpec *k22, const NrrdKernelSpec *kSSrecon) { static const char me[]="meetPullVolAddMulti"; unsigned int mpvIdx; if (!( pctx && mpv )) { biffAddf(MEET, "%s: got NULL pointer", me); return 1; } for (mpvIdx=0; mpvIdxnumSS) { E = pullVolumeSingleAdd(pctx, vol->kind, vol->volName, vol->nin, k00, k11, k22); } else { E = pullVolumeStackAdd(pctx, vol->kind, vol->volName, vol->nin, AIR_CAST(const Nrrd *const *, vol->ninSS), vol->posSS, vol->numSS, vol->derivNormSS, vol->derivNormBiasSS, k00, k11, k22, kSSrecon); } if (E) { biffMovef(MEET, PULL, "%s: trouble adding volume %u/%u (\"%s\")", me, mpvIdx, mpvNum, vol->volName); return 1; } } return 0; } meetPullInfo * meetPullInfoNew(void) { meetPullInfo *ret; ret = AIR_CALLOC(1, meetPullInfo); if (ret) { ret->info = 0; ret->source = pullSourceUnknown; ret->prop = pullPropUnknown; ret->constraint = AIR_FALSE; ret->volName = ret->itemStr = NULL; ret->zero = ret->scale = AIR_NAN; } return ret; } meetPullInfo * meetPullInfoNix(meetPullInfo *minf) { if (minf) { airFree(minf->volName); airFree(minf->itemStr); free(minf); } return NULL; } static int zeroScaleSet(meetPullInfo *minf, int haveZS, char **lastP) { static const char me[]="_zeroScaleSet"; char *tok; if (haveZS) { tok = airStrtok(NULL, ":", lastP); if (1 != sscanf(tok, "%lf", &(minf->zero))) { biffAddf(MEET, "%s: couldn't parse %s as zero (double)", me, tok); return 1; } tok = airStrtok(NULL, ":", lastP); if (1 != sscanf(tok, "%lf", &(minf->scale))) { biffAddf(MEET, "%s: couldn't parse %s as scale (double)", me, tok); return 1; } } else { minf->zero = minf->scale = AIR_NAN; } return 0; } int meetPullInfoParse(meetPullInfo *minf, const char *_str) { static const char me[]="meetPullInfoParse"; #define IFMT_GAGE "[-c]::[::]" #define IFMT_PROP ":prop=[::]" #define PROP_PREFIX "prop=" /* has to end with = */ char *str, *tok, *last=NULL, *iflags; airArray *mop; int haveZS, source; if (!(minf && _str)) { biffAddf(MEET, "%s: got NULL pointer", me); return 1; } if ( (3 == airStrntok(_str, ":") || 5 == airStrntok(_str, ":")) && 1 == airStrntok(_str, "=") ) { source = pullSourceGage; haveZS = (5 == airStrntok(_str, ":")); } else if ( (2 == airStrntok(_str, ":") || 4 == airStrntok(_str, ":")) && 2 == airStrntok(_str, "=") ) { source = pullSourceProp; haveZS = (4 == airStrntok(_str, ":")); } else { biffAddf(MEET, "%s: \"%s\" not of form " IFMT_GAGE " or " IFMT_PROP, me, _str); return 1; } mop = airMopNew(); if (!( str = airStrdup(_str) )) { biffAddf(MEET, "%s: couldn't strdup input", me); return 1; } airMopAdd(mop, str, airFree, airMopAlways); minf->source = source; if (pullSourceGage == source) { tok = airStrtok(str, ":", &last); iflags = strchr(tok, '-'); if (iflags) { *iflags = '\0'; iflags++; } if (!(minf->info = airEnumVal(pullInfo, tok))) { biffAddf(MEET, "%s: couldn't parse \"%s\" as %s", me, tok, pullInfo->name); airMopError(mop); return 1; } if (iflags) { if (strchr(iflags, 'c')) { minf->constraint = AIR_TRUE; } } tok = airStrtok(NULL, ":", &last); airFree(minf->volName); minf->volName = airStrdup(tok); airMopAdd(mop, minf->volName, airFree, airMopOnError); tok = airStrtok(NULL, ":", &last); airFree(minf->itemStr); minf->itemStr = airStrdup(tok); airMopAdd(mop, minf->itemStr, airFree, airMopOnError); if (zeroScaleSet(minf, haveZS, &last)) { biffAddf(MEET, "%s: couldn't parse zero or scale", me); airMopError(mop); return 1; } } else if (pullSourceProp == source) { /* ":prop=[::]" */ tok = airStrtok(str, ":", &last); if (!(minf->info = airEnumVal(pullInfo, tok))) { biffAddf(MEET, "%s: couldn't parse \"%s\" as %s", me, tok, pullInfo->name); airMopError(mop); return 1; } tok = airStrtok(NULL, ":", &last); if (strncmp(PROP_PREFIX, tok, strlen(PROP_PREFIX))) { biffAddf(MEET, "%s: property info didn't start with %s", me, PROP_PREFIX); } tok += strlen(PROP_PREFIX); if (!(minf->prop = airEnumVal(pullProp, tok))) { biffAddf(MEET, "%s: couldn't parse \"%s\" as %s", me, tok, pullProp->name); airMopError(mop); return 1; } if (zeroScaleSet(minf, haveZS, &last)) { biffAddf(MEET, "%s: couldn't parse zero or scale", me); airMopError(mop); return 1; } } else { biffAddf(MEET, "%s: sorry, source %s not handled", me, airEnumStr(pullSource, source)); airMopError(mop); return 1; } airMopOkay(mop); return 0; } int meetHestPullInfoParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { static const char me[]="meetHestPullInfoParse"; airArray *mop; meetPullInfo **minfP, *minf; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); minfP = AIR_CAST(meetPullInfo **, ptr); *minfP = minf = meetPullInfoNew(); airMopAdd(mop, minfP, (airMopper)airSetNull, airMopOnError); airMopAdd(mop, minf, (airMopper)meetPullInfoNix, airMopOnError); if (meetPullInfoParse(minf, str)) { char *ler; airMopAdd(mop, ler = biffGetDone(MEET), airFree, airMopOnError); airStrcpy(err, AIR_STRLEN_HUGE, ler); airMopError(mop); return 1; } airMopOkay(mop); return 0; } hestCB _meetHestPullInfo = { sizeof(meetPullInfo *), "meetPullInfo", meetHestPullInfoParse, (airMopper)meetPullInfoNix }; hestCB * meetHestPullInfo = &_meetHestPullInfo; int meetPullInfoAddMulti(pullContext *pctx, meetPullInfo **minf, unsigned int minfNum) { static const char me[]="meetPullInfoAddMulti"; const pullVolume *vol; unsigned int ii; airArray *mop; if (!( pctx && minf )) { biffAddf(MEET, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); for (ii=0; iivolName = airStrdup(minf[ii]->volName); ispec->source = minf[ii]->source; ispec->info = minf[ii]->info; ispec->prop = minf[ii]->prop; ispec->zero = minf[ii]->zero; ispec->scale = minf[ii]->scale; ispec->constraint = minf[ii]->constraint; /* the item is the one thing that takes some work to recover; we need to find the volume and find the item from its kind->enm */ if (pullSourceGage == ispec->source) { if (!( vol = pullVolumeLookup(pctx, minf[ii]->volName) )) { biffMovef(MEET, PULL, "%s: can't find volName \"%s\" for minf[%u]", me, minf[ii]->volName, ii); airMopError(mop); return 1; } if (!( ispec->item = airEnumVal(vol->kind->enm, minf[ii]->itemStr))) { biffAddf(MEET, "%s: can't parse \"%s\" as item of %s kind (minf[%u])\n", me, minf[ii]->itemStr, vol->kind->name, ii); airMopError(mop); return 1; } } if (pullInfoSpecAdd(pctx, ispec)) { biffMovef(MEET, PULL, "%s: trouble adding ispec from minf[%u]", me, ii); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/meet/GNUmakefile0000664000175000017500000000363412165631065017413 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := meet #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### TEEM_LIB_LIST (except meet) $(L).NEED = air hest biff nrrd ell unrrdu alan moss tijk gage dye bane limn echo hoover seek ten elf pull coil push mite $(L).PUBLIC_HEADERS = meet.h $(L).OBJS = enumall.o meetNrrd.o meetGage.o meetPull.o $(L).TESTS = test/strace test/tenums #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/meet/meet.h0000664000175000017500000001635212174673217016452 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MEET_HAS_BEEN_INCLUDED #define MEET_HAS_BEEN_INCLUDED #include #include /* TEEM_LIB_LIST */ #include #include #include #include #include #include #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) # include #endif #include #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) # include #endif #include #include #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) # include #endif #include #include #include #include #include #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) # include #endif #include #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) # include # include #endif #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(meet_EXPORTS) || defined(teem_EXPORTS) # define MEET_EXPORT extern __declspec(dllexport) # else # define MEET_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define MEET_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define MEET meetBiffKey /* enumall.c: (not really a descriptive filename) */ MEET_EXPORT const int meetPresent; MEET_EXPORT const char *meetBiffKey; MEET_EXPORT const airEnum **meetAirEnumAll(void); MEET_EXPORT void meetAirEnumAllPrint(FILE *file); MEET_EXPORT int meetAirEnumAllCheck(void); MEET_EXPORT const char *const meetTeemLibs[]; /* meetNrrd.c */ MEET_EXPORT const NrrdKernel **meetNrrdKernelAll(void); MEET_EXPORT int meetNrrdKernelAllCheck(void); /* meetGage.c */ MEET_EXPORT gageKind *meetGageKindParse(const char *str); MEET_EXPORT const gageKind *meetConstGageKindParse(const char *str); MEET_EXPORT hestCB *meetHestGageKind; MEET_EXPORT hestCB *meetHestConstGageKind; /* ******** meetPullVol ** ** this is information that somehow precedes the full pullVolume; it ** is more likely to have been by the user, and is not everything that ** a pullVolume is ("Vol" vs "Volume"). Does provide a way of parsing ** a string to specify all this info (useful for command-line). Note ** that the nrrds inside are owned by this, in contrast to the ** pullVolume. ** ** One constraint that motivates putting this in meet (instead of ** pull) is that this has to be general WRT gageKind; and will ** ultimately rely on meetConstGageKindParse. So, either ** meetConstGageKindParse gets passed to pull as a callback, or this ** functionality moves up to meet. Also, given the weird leeching ** tricks that are being enabled here, perhaps it is better for this ** to be properly outside of pull. */ typedef struct { const gageKind *kind; char *fileName, *volName; int derivNormSS, /* normalize derivatives based on scale */ uniformSS, /* uniform sampling along scale */ optimSS, /* optimal (non-uniform) sampling of scale */ leeching, /* non-zero iff using the same nin and ninSS as another meetPullVol (so as to avoid redundant copies in memory) */ recomputedSS; /* (OUTPUT) non-zero if meetPullVolLoadMulti had to recompute these, versus being read from disk */ unsigned int numSS; double rangeSS[2], derivNormBiasSS, *posSS; Nrrd *nin; /* we DO own */ Nrrd **ninSS; /* we DO own */ } meetPullVol; /* ******** meetPullInfo ** ** information that helps define a pullInfoSpec. ** ** The original reason that the pullInfo struct itself isn't being ** used here is that we want a way of parsing and storing this ** information from some source (e.g. command-line via hest), at a ** point where we do not yet know, or not have access to all the ** volumes and their gageKinds. So, the gageItem information is ** stored here only as a string, rather than the real integral item ** number. */ typedef struct { int info, /* which pullInfo is being defined */ source, /* the source (from pullSource* enum) */ prop, /* which property (if pullSourceProp) */ constraint; /* this info should be a constraint */ char *volName, /* name of volume from which info is measured */ *itemStr; /* which item in that volume gives the info */ double zero, scale; /* affine mapping of scalar info */ } meetPullInfo; /* meetPull.c */ MEET_EXPORT meetPullVol *meetPullVolNew(void); MEET_EXPORT meetPullVol *meetPullVolCopy(const meetPullVol *mpv); MEET_EXPORT int meetPullVolParse(meetPullVol *mpv, const char *str); MEET_EXPORT int meetPullVolLeechable(const meetPullVol *orig, const meetPullVol *lchr); MEET_EXPORT meetPullVol *meetPullVolNix(meetPullVol *pvol); MEET_EXPORT hestCB *meetHestPullVol; MEET_EXPORT int meetPullVolLoadMulti(meetPullVol **mpv, unsigned int mpvNum, char *cachePath, NrrdKernelSpec *kSSblur, const gageStackBlurParm *sbparm, int verbose); MEET_EXPORT int meetPullVolAddMulti(pullContext *pctx, meetPullVol **mpv, unsigned int mpvNum, const NrrdKernelSpec *k00, const NrrdKernelSpec *k11, const NrrdKernelSpec *k22, const NrrdKernelSpec *kSSrecon); MEET_EXPORT meetPullInfo *meetPullInfoNew(void); MEET_EXPORT meetPullInfo *meetPullInfoNix(meetPullInfo *minf); MEET_EXPORT int meetPullInfoParse(meetPullInfo *minf, const char *str); MEET_EXPORT hestCB *meetHestPullInfo; MEET_EXPORT int meetPullInfoAddMulti(pullContext *pctx, meetPullInfo **minf, unsigned int minfNum); #ifdef __cplusplus } #endif #endif /* MEET_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/meet/sources.cmake0000664000175000017500000000033212017054576020020 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(MEET_SOURCES enumall.c meetNrrd.c meetGage.c meetPull.c meet.h ) ADD_TEEM_LIBRARY(meet ${MEET_SOURCES}) teem-1.11.0~svn6057/src/meet/enumall.c0000664000175000017500000002000712165631065017133 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "meet.h" const int meetPresent = 42; const char * meetBiffKey = "meet"; typedef union { const airEnum ***enm; void **v; } foobarUnion; /* ******** meetAirEnumAll ** ** ALLOCATES and returns a NULL-terminated array of ** pointers to all the airEnums in Teem ** ** It would be better if this array could be created at compile-time, ** but efforts at doing this resulted in lots of "initializer is not const" ** errors. ** ** NOTE: the order here reflects the library ordering of the LIBS ** variable in teem/src/GNUMakefile, which is the canonical dependency ** ordering of the libraries. Can manually check completeness by: ** (TEEM_LIB_LIST) grep "airEnum *" {air,hest,biff,nrrd,ell,unrrdu,alan,moss,tijk,gage,dye,bane,limn,echo,hoover,seek,ten,elf,pull,coil,push,mite}/?*.h | grep EXPORT | more ** (with the ? in "}/?*.h" to stop warnings about / * inside comment) ** We could grep specifically for "const airEnum *const", but its good to ** use this occasion to also make sure that all public airEnums are ** indeed const airEnum *const */ const airEnum ** meetAirEnumAll() { airArray *arr; const airEnum **enm; unsigned int ii; foobarUnion fbu; arr = airArrayNew((fbu.enm = &enm, fbu.v), NULL, sizeof(airEnum *), 2); /* air */ ii = airArrayLenIncr(arr, 1); enm[ii] = airEndian; ii = airArrayLenIncr(arr, 1); enm[ii] = airBool; /* hest: no airEnums */ /* biff: no airEnums */ /* nrrd */ ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdFormatType; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdType; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdEncodingType; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdCenter; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdKind; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdField; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdSpace; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdSpacingStatus; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdBoundary; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdMeasure; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdUnaryOp; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdBinaryOp; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdTernaryOp; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdFFTWPlanRigor; ii = airArrayLenIncr(arr, 1); enm[ii] = nrrdResampleNonExistent; /* ell */ ii = airArrayLenIncr(arr, 1); enm[ii] = ell_cubic_root; /* unrrdu: no airEnums */ #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) /* alan */ ii = airArrayLenIncr(arr, 1); enm[ii] = alanStop; #endif /* moss: no airEnums */ #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) ii = airArrayLenIncr(arr, 1); enm[ii] = tijk_class; #endif /* gage */ ii = airArrayLenIncr(arr, 1); enm[ii] = gageErr; ii = airArrayLenIncr(arr, 1); enm[ii] = gageKernel; ii = airArrayLenIncr(arr, 1); enm[ii] = gageItemPackPart; ii = airArrayLenIncr(arr, 1); enm[ii] = gageScl; ii = airArrayLenIncr(arr, 1); enm[ii] = gageVec; /* dye: no airEnums */ #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) /* bane */ ii = airArrayLenIncr(arr, 1); enm[ii] = baneGkmsMeasr; #endif /* limn */ ii = airArrayLenIncr(arr, 1); enm[ii] = limnSpace; ii = airArrayLenIncr(arr, 1); enm[ii] = limnPolyDataInfo; ii = airArrayLenIncr(arr, 1); enm[ii] = limnCameraPathTrack; ii = airArrayLenIncr(arr, 1); enm[ii] = limnPrimitive; ii = airArrayLenIncr(arr, 1); enm[ii] = limnSplineType; ii = airArrayLenIncr(arr, 1); enm[ii] = limnSplineInfo; /* echo */ ii = airArrayLenIncr(arr, 1); enm[ii] = echoJitter; ii = airArrayLenIncr(arr, 1); enm[ii] = echoType; ii = airArrayLenIncr(arr, 1); enm[ii] = echoMatter; /* hoover */ ii = airArrayLenIncr(arr, 1); enm[ii] = hooverErr; /* seek */ ii = airArrayLenIncr(arr, 1); enm[ii] = seekType; /* ten */ ii = airArrayLenIncr(arr, 1); enm[ii] = tenAniso; ii = airArrayLenIncr(arr, 1); enm[ii] = tenInterpType; ii = airArrayLenIncr(arr, 1); enm[ii] = tenGage; ii = airArrayLenIncr(arr, 1); enm[ii] = tenFiberType; ii = airArrayLenIncr(arr, 1); enm[ii] = tenDwiFiberType; ii = airArrayLenIncr(arr, 1); enm[ii] = tenFiberStop; ii = airArrayLenIncr(arr, 1); enm[ii] = tenFiberIntg; ii = airArrayLenIncr(arr, 1); enm[ii] = tenGlyphType; ii = airArrayLenIncr(arr, 1); enm[ii] = tenEstimate1Method; ii = airArrayLenIncr(arr, 1); enm[ii] = tenEstimate2Method; ii = airArrayLenIncr(arr, 1); enm[ii] = tenTripleType; ii = airArrayLenIncr(arr, 1); enm[ii] = tenDwiGage; #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) /* elf: no airEnums */ #endif /* pull */ ii = airArrayLenIncr(arr, 1); enm[ii] = pullInterType; ii = airArrayLenIncr(arr, 1); enm[ii] = pullEnergyType; ii = airArrayLenIncr(arr, 1); enm[ii] = pullInfo; ii = airArrayLenIncr(arr, 1); enm[ii] = pullSource; ii = airArrayLenIncr(arr, 1); enm[ii] = pullProp; ii = airArrayLenIncr(arr, 1); enm[ii] = pullProcessMode; ii = airArrayLenIncr(arr, 1); enm[ii] = pullTraceStop; ii = airArrayLenIncr(arr, 1); enm[ii] = pullCount; ii = airArrayLenIncr(arr, 1); enm[ii] = pullConstraintFail; #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) /* coil */ ii = airArrayLenIncr(arr, 1); enm[ii] = coilMethodType; ii = airArrayLenIncr(arr, 1); enm[ii] = coilKindType; /* push */ ii = airArrayLenIncr(arr, 1); enm[ii] = pushEnergyType; #endif /* mite */ ii = airArrayLenIncr(arr, 1); enm[ii] = miteVal; ii = airArrayLenIncr(arr, 1); enm[ii] = miteStageOp; /* meet: no new airEnums of its own */ /* NULL-terminate the list */ ii = airArrayLenIncr(arr, 1); enm[ii] = NULL; /* nix, not nuke the airArray */ airArrayNix(arr); return enm; } void meetAirEnumAllPrint(FILE *file) { const airEnum **enm, *ee; unsigned int ei; if (!file) { return; } enm = meetAirEnumAll(); ei = 0; while ((ee = enm[ei])) { airEnumPrint(file, ee); fprintf(file, "\n"); ei++; } free(AIR_CAST(void *, enm)); return; } int meetAirEnumAllCheck(void) { static const char me[]="meetAirEnumAllCheck"; const airEnum **enm, *ee; char err[AIR_STRLEN_LARGE]; unsigned int ei; airArray *mop; mop = airMopNew(); enm = meetAirEnumAll(); airMopAdd(mop, (void*)enm, airFree, airMopAlways); ei = 0; while ((ee = enm[ei])) { /* fprintf(stderr, "!%s: %u %s\n", me, ei, ee->name); */ if (airEnumCheck(err, ee)) { biffAddf(MEET, "%s: problem with enum %u", me, ei); biffAddf(MEET, "%s", err); /* kind of a hack */ airMopError(mop); return 1; } ei++; } airMopOkay(mop); return 0; } const char *const meetTeemLibs[] = { /* TEEM_LIB_LIST */ "air", "hest", "biff", "nrrd", "ell", "unrrdu", #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) "alan", #endif "moss", #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) "tijk", #endif "gage", "dye", #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) "bane", #endif "limn", "echo", "hoover", "seek", "ten", #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) "elf", #endif "pull", #if defined(TEEM_BUILD_EXPERIMENTAL_LIBS) "coil", "push", #endif "mite", "meet", NULL }; teem-1.11.0~svn6057/src/meet/meetGage.c0000664000175000017500000000760712165631065017227 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "meet.h" gageKind * /*Teem: error if (!ret) */ _meetGageKindParse(const char *_str, int constOnly) { char *str; gageKind *ret; if (!_str) { return NULL; } str = airToLower(airStrdup(_str)); if (!str) { return NULL; } if (!strcmp(gageKindScl->name, str)) { ret = gageKindScl; } else if (!strcmp(gageKindVec->name, str)) { ret = gageKindVec; } else if (!strcmp(tenGageKind->name, str)) { ret = tenGageKind; } else if (!constOnly && !strcmp(TEN_DWI_GAGE_KIND_NAME, str)) { ret = tenDwiGageKindNew(); } else { ret = NULL; } airFree(str); return ret; } gageKind * /*Teem: error if (!ret) */ meetGageKindParse(const char *_str) { return _meetGageKindParse(_str, AIR_FALSE); } const gageKind * /*Teem: error if (!ret) */ meetConstGageKindParse(const char *_str) { return _meetGageKindParse(_str, AIR_TRUE); } /* ** same as _meetHestGageKindParse below but without the DWI kind, ** which isn't const */ int _meetHestConstGageKindParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[] = "_meetHestGageConstKindParse"; const gageKind **kindP; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } /* of course, the const correctness goes out the window with all the casting that's necessary with hest ... */ kindP = (const gageKind **)ptr; *kindP = meetConstGageKindParse(str); if (!*kindP) { sprintf(err, "%s: \"%s\" not \"%s\", \"%s\", or \"%s\"", me, str, gageKindScl->name, gageKindVec->name, tenGageKind->name); return 1; } return 0; } int _meetHestGageKindParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[] = "_meetHestGageKindParse"; gageKind **kindP; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } kindP = (gageKind **)ptr; *kindP = meetGageKindParse(str); if (!*kindP) { sprintf(err, "%s: \"%s\" not \"%s\", \"%s\", \"%s\", or \"%s\"", me, str, gageKindScl->name, gageKindVec->name, tenGageKind->name, TEN_DWI_GAGE_KIND_NAME); return 1; } return 0; } void * _meetHestGageKindDestroy(void *ptr) { gageKind *kind; if (ptr) { kind = AIR_CAST(gageKind *, ptr); if (!strcmp(TEN_DWI_GAGE_KIND_NAME, kind->name)) { tenDwiGageKindNix(kind); } } return NULL; } static hestCB _meetHestGageKind = { sizeof(gageKind *), "gageKind", _meetHestGageKindParse, _meetHestGageKindDestroy }; static hestCB _meetHestConstGageKind = { sizeof(gageKind *), "gageKind", _meetHestConstGageKindParse, NULL }; /* ******** meetHestGageKind ** ** This provides a uniform way to parse gageKinds from the command-line */ hestCB * meetHestGageKind = &_meetHestGageKind; hestCB * meetHestConstGageKind = &_meetHestConstGageKind; teem-1.11.0~svn6057/src/meet/test/0000775000175000017500000000000012203513756016311 5ustar domibeldomibelteem-1.11.0~svn6057/src/meet/test/tenums.c0000664000175000017500000000307212165631065017773 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../meet.h" int main(int argc, const char *argv[]) { const char *me; char *err; airArray *mop; AIR_UNUSED(argc); me = argv[0]; mop = airMopNew(); if (meetAirEnumAllCheck()) { airMopAdd(mop, err=biffGetDone(MEET), airFree, airMopAlways); fprintf(stderr, "%s: problem:\n%s", me, err); airMopError(mop); return 1; } /* else no problems */ meetAirEnumAllPrint(stdout); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/meet/test/strace.c0000664000175000017500000011765212174667353017764 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "../meet.h" /* gcc -g -W -Wall -arch x86_64 strace.c -o strace -Wl,-prebind \ -I/Users/gk/teem-install/include \ -L/Users/gk/teem-install/lib/ \ -lteem -lpng -lz -lpthread -lfftw3 -lm */ int pullScaleTracePlotAdd(pullContext *pctx, Nrrd *nwild, Nrrd *nccd, Nrrd *nmask, double mth, airArray *insideArr, double velHalf, pullTrace *pts) { static const char me[]="pullScaleTracePlotAdd"; double ssr[2], *pos, *velo, *wild, *ccd, *mask; unsigned int pnum, pidx, sizeS, sizeV; if (!(pctx && nwild && nccd && pts)) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (!nrrdSameSize(nwild, nccd, AIR_TRUE)) { biffMovef(PULL, NRRD, "%s: nwild not same size as nccd", me); return 1; } if (nmask || insideArr) { if (!insideArr) { biffAddf(PULL, "%s: got nmask but not insideArr", me); return 1; } if (!nmask) { biffAddf(PULL, "%s: got insideArr but not nmask", me); return 1; } if (nrrdTypeDouble != nmask->type) { biffAddf(PULL, "%s: nmask has type %s but want %s", me, airEnumStr(nrrdType, nmask->type), airEnumStr(nrrdType, nrrdTypeDouble)); return 1; } if (!nrrdSameSize(nwild, nmask, AIR_TRUE)) { biffMovef(PULL, NRRD, "%s: nwild not same size as nmask", me); return 1; } if (!nrrdSameSize(nccd, nmask, AIR_TRUE)) { biffMovef(PULL, NRRD, "%s: nccd not same size as nmask", me); return 1; } if (!AIR_EXISTS(mth)) { biffAddf(PULL, "%s: got non-existent mask thresh %g", me, mth); return 1; } } ssr[0] = nwild->axis[0].min; ssr[1] = nwild->axis[0].max; sizeS = AIR_CAST(unsigned int, nwild->axis[0].size); sizeV = AIR_CAST(unsigned int, nwild->axis[1].size); wild = AIR_CAST(double *, nwild->data); ccd = AIR_CAST(double *, nccd->data); if (nmask) { mask = AIR_CAST(double *, nmask->data); } else { mask = NULL; } pnum = AIR_CAST(unsigned int, pts->nvert->axis[1].size); pos = AIR_CAST(double *, pts->nvert->data); velo = AIR_CAST(double *, pts->nvelo->data); for (pidx=0; pidxcalstop */) { ccd[sidx + sizeS*vidx] += 1; } else { wild[sidx + sizeS*vidx] += 1; } if (nmask && mask[sidx + sizeS*vidx] >= mth) { unsigned int ii; double *inside; ii = airArrayLenIncr(insideArr, 1); inside = AIR_CAST(double *, insideArr->data); ELL_4V_COPY(inside + 4*ii, pp); } } return 0; } #if 0 if (pullScaleTracePlotAdd(pctx, nwild, nccd, nmask, 0.5 /* mask thresh */, insideArr, shalf, pts)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble on point %u:\n%s", me, pidx, err); airMopError(mop); return 1; } #endif int findAndTraceMorePoints(Nrrd *nplot, pullContext *pctx, pullVolume *scaleVol, int strengthUse, double scaleStep, double scaleHalfLen, double speedLimit, unsigned int traceArrIncr, pullTraceMulti *mtrc, unsigned int pointNum) { static const char me[]="findAndTraceMorePoints"; unsigned int pointsSoFar, idtagBase, pidx, addedNum; pullTrace *trace; pullPoint *point; Nrrd *nPosOut; airArray *mop; double *pos; char doneStr[AIR_STRLEN_SMALL]; mop = airMopNew(); pointsSoFar = pullPointNumber(pctx); idtagBase = pctx->idtagNext; point = NULL; printf("%s: adding %u new points (to %u; %s) . . . ", me, pointNum, pointsSoFar, airPrettySprintSize_t(doneStr, pullTraceMultiSizeof(mtrc))); for (pidx=0; pidxidtag); airMopError(mop); return 1; } if (pullBinsPointAdd(pctx, point, NULL)) { biffAddf(PULL, "%s: trouble binning point %u", me, point->idtag); airMopError(mop); return 1; } point = NULL; } printf("%s\n", airDoneStr(0, pidx, pointNum, doneStr)); if (point) { /* we created a new test point, but it was never placed in the volume */ /* so, HACK: undo pullPointNew . . . */ point = pullPointNix(point); pctx->idtagNext -= 1; } nPosOut = nrrdNew(); airMopAdd(mop, nPosOut, (airMopper)nrrdNuke, airMopAlways); if (pullOutputGetFilter(nPosOut, NULL, NULL, NULL, 0.0, pctx, idtagBase, 0)) { biffAddf(PULL, "%s: trouble", me); airMopError(mop); return 1; } pos = AIR_CAST(double *, nPosOut->data); addedNum = nPosOut->axis[1].size; printf("%s: tracing . . . ", me); for (pidx=0; pidxdata); nn = AIR_CAST(unsigned int, nrrdElementNumber(nprob)); for (ii=0; iidata); qq = AIR_CAST(double *, nqq->data); for (ii=0; iirespFileEnable = AIR_TRUE; me = argv[0]; hestOptAdd(&hopt, "int", "int", airTypeEnum, 1, 1, &interType, "justr", "inter-particle energy type", NULL, pullInterType); hestOptAdd(&hopt, "enr", "spec", airTypeOther, 1, 1, &enspR, "cotan", "inter-particle energy, radial component", NULL, NULL, pullHestEnergySpec); hestOptAdd(&hopt, "ens", "spec", airTypeOther, 1, 1, &enspS, "zero", "inter-particle energy, scale component", NULL, NULL, pullHestEnergySpec); hestOptAdd(&hopt, "enw", "spec", airTypeOther, 1, 1, &enspWin, "butter:16,0.8", "windowing to create locality with additive " "scale-space interaction (\"-int add\")", NULL, NULL, pullHestEnergySpec); hestOptAdd(&hopt, "zz", "bool", airTypeBool, 1, 1, &zeroZ, "false", "always constrain Z=0, to process 2D images"); hestOptAdd(&hopt, "su", "bool", airTypeBool, 1, 1, &strnUse, "false", "weigh contributions to traces with strength"); hestOptAdd(&hopt, "efs", "bool", airTypeBool, 1, 1, &energyFromStrength, "false", "whether or not strength contributes to particle-image energy"); hestOptAdd(&hopt, "nave", "bool", airTypeBool, 1, 1, &nixAtVolumeEdgeSpace, "false", "whether or not to nix points at edge of volume, where gage had " "to invent values for kernel support"); hestOptAdd(&hopt, "cbst", "bool", airTypeBool, 1, 1, &constraintBeforeSeedThresh, "false", "during initialization, try constraint satisfaction before " "testing seedThresh"); hestOptAdd(&hopt, "noadd", NULL, airTypeBool, 0, 0, &noAdd, NULL, "turn off adding during population control"); hestOptAdd(&hopt, "usa", "bool", airTypeBool, 1, 1, &unequalShapesAllow, "false", "allow volumes to have different shapes (false is safe as " "different volume sizes are often accidental)"); hestOptAdd(&hopt, "nobin", NULL, airTypeBool, 0, 0, &binSingle, NULL, "turn off spatial binning (which prevents multi-threading " "from being useful), for debugging or speed-up measurement"); hestOptAdd(&hopt, "lti", "bool", airTypeBool, 1, 1, &liveThresholdOnInit, "true", "impose liveThresh on initialization"); hestOptAdd(&hopt, "por", "bool", airTypeBool, 1, 1, &permuteOnRebin, "true", "permute points during rebinning"); hestOptAdd(&hopt, "svec", "vec", airTypeDouble, 3, 3, scaleVec, "0 0 0", "if non-zero (length), vector to use for displaying scale " "in 3-space"); hestOptAdd(&hopt, "gssr", "rad", airTypeDouble, 1, 1, &glyphScaleRad, "0.0", "if non-zero (length), scaling of scale to cylindrical tensors"); hestOptAdd(&hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1", "verbosity level"); hestOptAdd(&hopt, "vol", "vol0 vol1", airTypeOther, 1, -1, &vspec, NULL, "input volumes, in format ::", &vspecNum, NULL, meetHestPullVol); hestOptAdd(&hopt, "info", "info0 info1", airTypeOther, 1, -1, &idef, NULL, "info definitions, in format " "[-c]::[::]", &idefNum, NULL, meetHestPullInfo); hestOptAdd(&hopt, "k00", "kern00", airTypeOther, 1, 1, &k00, "cubic:1,0", "kernel for gageKernel00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kern11", airTypeOther, 1, 1, &k11, "cubicd:1,0", "kernel for gageKernel11", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kern22", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "kernel for gageKernel22", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "sscp", "path", airTypeString, 1, 1, &cachePathSS, "./", "path (without trailing /) for where to read/write " "pre-blurred volumes for scale-space"); hestOptAdd(&hopt, "kssb", "kernel", airTypeOther, 1, 1, &kSSblur, "ds:1,5", "blurring kernel, to sample scale space", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "kssr", "kernel", airTypeOther, 1, 1, &kSSrecon, "hermite", "kernel for reconstructing from scale space samples", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "nss", "# scl smpls", airTypeUInt, 1, 1, &samplesAlongScaleNum, "1", "if using \"-ppv\", number of samples along scale axis " "for each spatial position"); hestOptAdd(&hopt, "np", "# points", airTypeUInt, 1, 1, &pointNumInitial, "1000", "number of points to start in simulation"); useHalton=AIR_TRUE; /* hestOptAdd(&hopt, "halton", NULL, airTypeInt, 0, 0, &useHalton, NULL, "use Halton sequence initialization instead of " "uniform random"); */ hestOptAdd(&hopt, "tnp", "# points", airTypeUInt, 1, 1, &tracePointNum, "1000", "number of points to add in each iteration of " "estimation of plot"); hestOptAdd(&hopt, "pnm", "# passes", airTypeUInt, 1, 1, &passNumMax, "10", "max number of passes in plot estimation"); hestOptAdd(&hopt, "tpdt", "thresh", airTypeDouble, 1, 1, &tpdThresh, "1.0", "KL-distance threshold"); hestOptAdd(&hopt, "ti", "fname", airTypeString, 1, 1, &tracesInS, "", "input file of pre-computed traces"); hestOptAdd(&hopt, "to", "fname", airTypeString, 1, 1, &tracesOutS, "", "file for saving *computed* traces"); hestOptAdd(&hopt, "ppv", "# pnts/vox", airTypeUInt, 1, 1, &pointPerVoxel, "0", "number of points per voxel to start in simulation " "(need to have a seed thresh vol, overrides \"-np\")"); hestOptAdd(&hopt, "ppvzr", "z range", airTypeUInt, 2, 2, ppvZRange, "1 0", "range of Z slices (1st num < 2nd num) to do ppv in, or, " "\"1 0\" for whole volume"); hestOptAdd(&hopt, "jit", "jitter", airTypeDouble, 1, 1, &jitter, "0", "amount of jittering to do with ppv"); hestOptAdd(&hopt, "pi", "npos", airTypeOther, 1, 1, &nPosIn, "", "4-by-N array of positions to start at (overrides \"-np\")", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "step", "step", airTypeDouble, 1, 1, &stepInitial, "1", "initial step size for gradient descent"); hestOptAdd(&hopt, "csm", "step", airTypeDouble, 1, 1, &constraintStepMin, "0.0001", "convergence criterion for constraint satisfaction"); hestOptAdd(&hopt, "snap", "# iters", airTypeUInt, 1, 1, &snap, "0", "if non-zero, # iters between saved snapshots"); hestOptAdd(&hopt, "stim", "# iters", airTypeUInt, 1, 1, &stuckIterMax, "5", "if non-zero, max # iterations to allow a particle " " to be stuck before nixing"); hestOptAdd(&hopt, "maxci", "# iters", airTypeUInt, 1, 1, &constraintIterMax, "15", "if non-zero, max # iterations for contraint enforcement"); hestOptAdd(&hopt, "irad", "scale", airTypeDouble, 1, 1, &radiusSpace, "1", "particle radius in spatial domain"); hestOptAdd(&hopt, "srad", "scale", airTypeDouble, 1, 1, &radiusScale, "1", "particle radius in scale domain"); hestOptAdd(&hopt, "bws", "bin width", airTypeDouble, 1, 1, &binWidthSpace, "1.001", "spatial bin width as multiple of spatial radius"); hestOptAdd(&hopt, "alpha", "alpha", airTypeDouble, 1, 1, &alpha, "0.5", "blend between particle-image (alpha=0) and " "inter-particle (alpha=1) energies"); hestOptAdd(&hopt, "beta", "beta", airTypeDouble, 1, 1, &beta, "1.0", "when using Phi2 energy, blend between pure " "space repulsion (beta=0) and " "scale attraction (beta=1)"); hestOptAdd(&hopt, "gamma", "gamma", airTypeDouble, 1, 1, &gamma, "1.0", "scaling factor on energy from strength"); hestOptAdd(&hopt, "theta", "theta", airTypeDouble, 1, 1, &theta, "0.0", "slope of increasing livethresh wrt scale"); hestOptAdd(&hopt, "ess", "scl", airTypeDouble, 1, 1, &backStepScale, "0.5", "when energy goes up instead of down, scale step " "size by this"); hestOptAdd(&hopt, "oss", "scl", airTypeDouble, 1, 1, &opporStepScale, "1.0", "opportunistic scaling (hopefully up, >1) of step size " "on every iteration"); hestOptAdd(&hopt, "edmin", "frac", airTypeDouble, 1, 1, &energyDecreaseMin, "0.0001", "convergence threshold: stop when fractional improvement " "(decrease) in energy dips below this"); hestOptAdd(&hopt, "iad", "# iters", airTypeUInt, 1, 1, &addDescent, "10", "# iters to run descent on tentative new points during PC"); hestOptAdd(&hopt, "icb", "# iters", airTypeUInt, 1, 1, &iterCallback, "1", "periodicity of calling rendering callback"); hestOptAdd(&hopt, "ac3c", "ac3c", airTypeBool, 1, 1, &allowCodimension3Constraints, "false", "allow codimensions 3 constraints"); hestOptAdd(&hopt, "sit", "sit", airTypeBool, 1, 1, &scaleIsTau, "false", "scale is tau"); hestOptAdd(&hopt, "rng", "seed", airTypeUInt, 1, 1, &rngSeed, "42", "base seed value for RNGs"); hestOptAdd(&hopt, "pbm", "mod", airTypeUInt, 1, 1, &progressBinMod, "50", "progress bin mod"); hestOptAdd(&hopt, "nt", "# threads", airTypeInt, 1, 1, &threadNum, "1", (airThreadCapable ? "number of threads hoover should use" : "if threads where enabled in this Teem build, this is how " "you would control the number of threads to use")); hestOptAdd(&hopt, "addlog", "fname", airTypeString, 1, 1, &addLogS, "", "name of file in which to log all particle additions"); hestOptAdd(&hopt, "po", "nout", airTypeString, 1, 1, &posOutS, "pos.nrrd", "position output"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "trace.nrrd", "trace volume"); hestOptAdd(&hopt, "eob", "base", airTypeString, 1, 1, &extraOutBaseS, "", "save extra info (besides position), and use this string as " "the base of the filenames. Not using this means the extra " "info is not saved."); hestOptAdd(&hopt, "ss", "sstep", airTypeDouble, 1, 1, &sstep, "0.0003", "fraction of SS range used as step along scale for tracing"); hestOptAdd(&hopt, "sw", "swin", airTypeDouble, 1, 1, &sswin, "0.1", "fraction of SS range that caps length of trace along scale"); hestOptAdd(&hopt, "sh", "shalf", airTypeDouble, 1, 1, &shalf, "5.0", "velocity that will be half-way down vertical axis of plot"); hestOptAdd(&hopt, "sl", "sslim", airTypeDouble, 1, 1, &sslim, "50.0", "velocity at which we give up tracking"); hestOptAdd(&hopt, "pr", "sx sy", airTypeUInt, 2, 2, pres, "1000 420", "resolution of the 2D plot"); hestOptAdd(&hopt, "tlo", "fname", airTypeString, 1, 1, &trcListOutS, "", "output filename of list of all points in all traces"); hestOptAdd(&hopt, "tvo", "fname", airTypeString, 1, 1, &trcVolOutS, "", "output filename for rasterized trace of scale-space volume"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); /* airEnumPrint(stderr, gageScl); exit(0); */ if (airStrlen(addLogS)) { if (!(addLog = airFopen(addLogS, stdout, "w"))) { fprintf(stderr, "%s: couldn't open %s for writing\n", me, addLogS); airMopError(mop); return 1; } airMopAdd(mop, addLog, (airMopper)airFclose, airMopAlways); } else { addLog = NULL; } pctx = pullContextNew(); airMopAdd(mop, pctx, (airMopper)pullContextNix, airMopAlways); if (pullVerboseSet(pctx, verbose) || pullFlagSet(pctx, pullFlagZeroZ, zeroZ) || pullFlagSet(pctx, pullFlagEnergyFromStrength, energyFromStrength) || pullFlagSet(pctx, pullFlagNixAtVolumeEdgeSpace, nixAtVolumeEdgeSpace) || pullFlagSet(pctx, pullFlagConstraintBeforeSeedThresh, constraintBeforeSeedThresh) || pullFlagSet(pctx, pullFlagBinSingle, binSingle) || pullFlagSet(pctx, pullFlagNoAdd, noAdd) || pullFlagSet(pctx, pullFlagPermuteOnRebin, permuteOnRebin) /* want this to be true; tracing is different than regular particles */ || pullFlagSet(pctx, pullFlagRestrictiveAddToBins, AIR_TRUE) || pullFlagSet(pctx, pullFlagAllowCodimension3Constraints, allowCodimension3Constraints) || pullFlagSet(pctx, pullFlagScaleIsTau, scaleIsTau) || pullInitUnequalShapesAllowSet(pctx, unequalShapesAllow) || pullIterParmSet(pctx, pullIterParmSnap, snap) || pullIterParmSet(pctx, pullIterParmStuckMax, stuckIterMax) || pullIterParmSet(pctx, pullIterParmConstraintMax, constraintIterMax) || pullIterParmSet(pctx, pullIterParmAddDescent, addDescent) || pullIterParmSet(pctx, pullIterParmCallback, iterCallback) || pullSysParmSet(pctx, pullSysParmStepInitial, stepInitial) || pullSysParmSet(pctx, pullSysParmConstraintStepMin, constraintStepMin) || pullSysParmSet(pctx, pullSysParmRadiusSpace, radiusSpace) || pullSysParmSet(pctx, pullSysParmRadiusScale, radiusScale) || pullSysParmSet(pctx, pullSysParmBinWidthSpace, binWidthSpace) || pullSysParmSet(pctx, pullSysParmAlpha, alpha) || pullSysParmSet(pctx, pullSysParmBeta, beta) || pullSysParmSet(pctx, pullSysParmGamma, gamma) || pullSysParmSet(pctx, pullSysParmTheta, theta) || pullSysParmSet(pctx, pullSysParmEnergyDecreaseMin, energyDecreaseMin) || pullSysParmSet(pctx, pullSysParmBackStepScale, backStepScale) || pullSysParmSet(pctx, pullSysParmOpporStepScale, opporStepScale) || pullRngSeedSet(pctx, rngSeed) || pullProgressBinModSet(pctx, progressBinMod) || pullThreadNumSet(pctx, threadNum) || pullInterEnergySet(pctx, interType, enspR, enspS, enspWin) || pullInitLiveThreshUseSet(pctx, liveThresholdOnInit) || pullLogAddSet(pctx, addLog)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble with flags:\n%s", me, err); airMopError(mop); return 1; } if (useHalton) { E = pullInitHaltonSet(pctx, pointNumInitial, 0); } else if (nPosIn) { E = pullInitGivenPosSet(pctx, nPosIn); } else if (pointPerVoxel) { E = pullInitPointPerVoxelSet(pctx, pointPerVoxel, ppvZRange[0], ppvZRange[1], samplesAlongScaleNum, jitter); } else { E = pullInitRandomSet(pctx, pointNumInitial); } if (E) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble with flags:\n%s", me, err); airMopError(mop); return 1; } sbp = gageStackBlurParmNew(); airMopAdd(mop, sbp, (airMopper)gageStackBlurParmNix, airMopAlways); if (gageStackBlurParmBoundarySet(sbp, nrrdBoundaryWrap, AIR_NAN) /* though this verbosity could in principle be different */ || gageStackBlurParmVerboseSet(sbp, verbose)) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble with stack blur parms:\n%s", me, err); airMopError(mop); return 1; } if (meetPullVolLoadMulti(vspec, vspecNum, cachePathSS, kSSblur, sbp, verbose) || meetPullVolAddMulti(pctx, vspec, vspecNum, k00, k11, k22, kSSrecon) || meetPullInfoAddMulti(pctx, idef, idefNum)) { airMopAdd(mop, err = biffGetDone(MEET), airFree, airMopAlways); fprintf(stderr, "%s: trouble with volumes or infos:\n%s", me, err); airMopError(mop); return 1; } if (airStrlen(tracesInS)) { /* don't need to initialize points if we're reading a trace, but annoyingly we do need the rest of the pull set up, *JUST* so that we can read off the scale-space range associated with the constraint */ pullFlagSet(pctx, pullFlagStartSkipsPoints, AIR_TRUE); } if (pullStart(pctx)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble starting system:\n%s", me, err); airMopError(mop); return 1; } if (nrrdMaybeAlloc_va(nplotA, nrrdTypeDouble, 2, AIR_CAST(size_t, pres[0]), AIR_CAST(size_t, pres[1]))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble creating output:\n%s", me, err); airMopError(mop); return 1; } if (pullConstraintScaleRange(pctx, ssrange)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "!%s: ================== ssrange %g %g\n", me, ssrange[0], ssrange[1]); fprintf(stderr, "!%s: ======== mvol %u %g %g\n", me, vspec[0]->numSS, vspec[0]->rangeSS[0], vspec[0]->rangeSS[1]); nplotA->axis[0].min = ssrange[0]; nplotA->axis[0].max = ssrange[1]; nplotA->axis[1].min = 0.0; nplotA->axis[1].max = 2*shalf; nrrdCopy(nplotB, nplotA); if (airStrlen(tracesInS)) { if (!(tracesFile = airFopen(tracesInS, stdin, "rb"))) { fprintf(stderr, "%s: couldn't open %s for reading\n", me, tracesInS); airMopError(mop); return 1; } if (pullTraceMultiRead(mtrc, tracesFile)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble reading:\n%s", me, err); airMopError(mop); return 1; } airFclose(tracesFile); goto plotting; } /* else */ if (!pctx->constraint) { fprintf(stderr, "%s: this programs requires a constraint\n", me); airMopError(mop); return 1; } scaleVol = NULL; { unsigned int ii; for (ii=0; iivolNum; ii++) { if (pctx->vol[ii]->ninScale) { scaleVol = pctx->vol[ii]; break; } } } if (!scaleVol) { fprintf(stderr, "%s: this program requires scale-space\n", me); airMopError(mop); return 1; } if (pullOutputGet(nPosOut, NULL, NULL, NULL, 0.0, pctx)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble 3.1:\n%s", me, err); airMopError(mop); return 1; } nrrdSave(posOutS, nPosOut, NULL); { double *pos, seedPos[4], scaleWin, scaleStep, dist=0; unsigned int pidx, pnum, passIdx; pullTrace *pts; Nrrd *nsplot, *nprogA, *nprogB, *nlsplot; char doneStr[AIR_STRLEN_SMALL]; pos = AIR_CAST(double *, nPosOut->data); nlsplot = nrrdNew(); airMopAdd(mop, nlsplot, (airMopper)nrrdNuke, airMopAlways); nsplot = nrrdNew(); airMopAdd(mop, nsplot, (airMopper)nrrdNuke, airMopAlways); nprogA = nrrdNew(); airMopAdd(mop, nprogA, (airMopper)nrrdNuke, airMopAlways); nprogB = nrrdNew(); airMopAdd(mop, nprogB, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nprogA, nplotA) || nrrdCopy(nprogB, nplotA)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble creating nprogs:\n%s", me, err); airMopError(mop); return 1; } scaleWin = sswin*(ssrange[1]-ssrange[0]); scaleStep = sstep*(ssrange[1]-ssrange[0]); pnum = nPosOut->axis[1].size; printf("!%s: tracing initial %u points . . . ", me, pnum); for (pidx=0; pidx dist = %g (%s %g)\n", me, dd, dist, dist < tpdThresh ? "<" : ">=", tpdThresh); nrrdCopy(nlsplot, nsplot); if (dist < tpdThresh) { fprintf(stderr, "%s: converged: dist %g < thresh %g\n", me, dist, tpdThresh); break; } } if (dist >= tpdThresh) { fprintf(stderr, "%s: WARNING did NOT converge: dist %g >= thresh %g\n", me, dist, tpdThresh); } if (airStrlen(tracesOutS) && !airStrlen(tracesInS)) { tracesFile = airFopen(tracesOutS, stdout, "wb"); if (pullTraceMultiWrite(tracesFile, mtrc)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s", me, err); airMopError(mop); return 1; } fclose(tracesFile); } plotting: if (airStrlen(trcListOutS)) { /* format: ** trcIdx isSeed X Y Z S f(velo) strn qual ** 0 1 2 3 4 5 6 7 8 (9) */ Nrrd *ntlo; double *tlo; size_t sx=9, totn=0, toti=0; pullTrace *trc; pullPoint *lpnt; unsigned int ti; for (ti=0; titraceNum; ti++) { trc = mtrc->trace[ti]; totn += trc->nvelo->axis[0].size; } ntlo = nrrdNew(); lpnt = pullPointNew(pctx); if (nrrdMaybeAlloc_va(ntlo, nrrdTypeDouble, 2, sx, totn)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't alloc output:\n%s", me, err); airMopError(mop); return 1; } airMopAdd(mop, ntlo, (airMopper)nrrdNuke, airMopAlways); tlo = AIR_CAST(double *, ntlo->data); for (ti=0; titraceNum; ti++) { unsigned int vi, vn; double *vert, *velo, *strn, qual; trc = mtrc->trace[ti]; vn = AIR_CAST(unsigned int, trc->nvelo->axis[0].size); vert = AIR_CAST(double *, trc->nvert->data); velo = AIR_CAST(double *, trc->nvelo->data); strn = AIR_CAST(double *, (trc->nstrn ? trc->nstrn->data : NULL)); for (vi=0; viseedIdx); ELL_4V_COPY(tlo + sx*toti + 2, vert + 4*vi); ELL_4V_COPY(lpnt->pos, vert + 4*vi); if (pctx->ispec[pullInfoQuality]) { pullProbe(pctx->task[0], lpnt); qual = pullPointScalar(pctx, lpnt, pullInfoQuality, NULL, NULL); } else { qual = 0.0; } tlo[sx*toti + 6] = atan(velo[vi]/shalf)/(AIR_PI/2); tlo[sx*toti + 7] = strn ? strn[vi] : 0.0; tlo[sx*toti + 8] = qual; toti++; } } if (nrrdSave(trcListOutS, ntlo, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s", me, err); airMopError(mop); return 1; } } if (airStrlen(trcVolOutS)) { /* HEY: copy and paste from above */ Nrrd *nout; const gagePoint *pnt; meetPullVol *mpv; pullTrace *trc; pullPoint *lpnt; unsigned int size[4], idx[4], iii, ti, si; double idxd[4], val, (*lup)(const void *v, size_t I), (*ins)(void *v, size_t I, double d); mpv = meetPullVolCopy(vspec[0]); airMopAdd(mop, mpv, (airMopper)meetPullVolNix, airMopAlways); size[0] = AIR_CAST(unsigned int, mpv->ninSS[0]->axis[0].size); size[1] = AIR_CAST(unsigned int, mpv->ninSS[0]->axis[1].size); size[2] = AIR_CAST(unsigned int, mpv->ninSS[0]->axis[2].size); size[3] = mpv->numSS; printf("!%s: size = (%u,%u,%u,%u)\n", me, size[0], size[1], size[2], size[3]); lpnt = pullPointNew(pctx); airMopAdd(mop, lpnt, (airMopper)pullPointNix, airMopAlways); for (si=0; sininSS[si]); } lup = nrrdDLookup[mpv->ninSS[0]->type]; ins = nrrdDInsert[mpv->ninSS[0]->type]; pnt = &(pctx->task[0]->vol[0]->gctx->point); for (ti=0; titraceNum; ti++) { unsigned int vi, vn; double *vert, *velo, *strn, wght; trc = mtrc->trace[ti]; vn = AIR_CAST(unsigned int, trc->nvelo->axis[0].size); vert = AIR_CAST(double *, trc->nvert->data); velo = AIR_CAST(double *, trc->nvelo->data); strn = AIR_CAST(double *, (strnUse && trc->nstrn ? trc->nstrn->data : NULL)); for (vi=0; vipos, vert + 4*vi); if (zeroZ && lpnt->pos[2] != 0) { fprintf(stderr, "%s: zeroZ violated\n", me); airMopError(mop); return 1; } wght = 1 - atan(velo[vi]/shalf)/(AIR_PI/2); if (strn) { wght *= strn[vi]; } /* probe just to get the transform to idx-space from gage */ if (pullProbe(pctx->task[0], lpnt)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: couldn't probe:\n%s", me, err); airMopError(mop); return 1; } ELL_4V_ADD2(idxd, pnt->frac, pnt->idx); /* because of gage subtlety that gagePoint->idx is index of upper, not lower, corner, idxd is too big by 1 */ idx[0] = airIndexClamp(-0.5, idxd[0]-1, size[0]-0.5, size[0]); idx[1] = airIndexClamp(-0.5, idxd[1]-1, size[1]-0.5, size[1]); idx[2] = airIndexClamp(-0.5, idxd[2]-1, size[2]-0.5, size[2]); idx[3] = airIndexClamp(0, idxd[3], size[3]-1, size[3]); iii = idx[0] + size[0]*(idx[1] + size[1]*idx[2]); val = lup(mpv->ninSS[idx[3]]->data, iii); ins(mpv->ninSS[idx[3]]->data, iii, wght + val); /* printf("!%s: (%g,%g,%g,%g) -> (%g,%g,%g,%g) -> (%u,%u,%u,%u) -> %u: %g\n", me, lpnt->pos[0], lpnt->pos[1], lpnt->pos[2], lpnt->pos[3], idxd[0], idxd[1], idxd[2], idxd[3], idx[0], idx[1], idx[2], idx[3], iii, val); */ } } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdJoin(nout, AIR_CAST(const Nrrd *const *, mpv->ninSS), size[3], 3, AIR_FALSE) || nrrdSave(trcVolOutS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't join or save SS output:\n%s", me, err); airMopError(mop); return 1; } } if (pullTraceMultiPlotAdd(nplotA, mtrc, NULL, strnUse, 0, 0) || pullTraceMultiFilterConcaveDown(nfilt, mtrc, 0.05) || pullTraceMultiPlotAdd(nplotB, mtrc, nfilt, strnUse, 0, 0)) { airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways); fprintf(stderr, "%s: trouble plotting:\n%s", me, err); airMopError(mop); return 1; } { const Nrrd *nin[2]; nin[0] = nplotA; nin[1] = nplotB; if (nrrdJoin(nplot, nin, 2, 0, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s", me, err); airMopError(mop); return 1; } } if (nrrdSave(outS, nplot, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s", me, err); airMopError(mop); return 1; } } pullFinish(pctx); airMopOkay(mop); return ret; } teem-1.11.0~svn6057/src/moss/0000775000175000017500000000000012203513753015356 5ustar domibeldomibelteem-1.11.0~svn6057/src/moss/privateMoss.h0000664000175000017500000000330212165631065020045 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define MOSS_MAT_SET(mat, a, b, x, c, d, y) \ (mat)[0]=(a); (mat)[1]=(b); (mat)[2]=(x); \ (mat)[3]=(c); (mat)[4]=(d); (mat)[5]=(y) #define MOSS_MAT_COPY(m2, m1) \ (m2)[0] = (m1)[0]; (m2)[1] = (m1)[1]; (m2)[2] = (m1)[2]; \ (m2)[3] = (m1)[3]; (m2)[4] = (m1)[4]; (m2)[5] = (m1)[5] #define MOSS_MAT_6TO9(m2, m1) \ ELL_3V_COPY((m2)+0, (m1)+0); \ ELL_3V_COPY((m2)+3, (m1)+3); \ ELL_3V_SET((m2)+6, 0, 0, 1) #define MOSS_MAT_9TO6(m2, m1) \ MOSS_MAT_SET(m2, (m1)[0], (m1)[1], (m1)[2], (m1)[3], (m1)[4], (m1)[5]) /* methodsMoss.c */ extern int _mossCenter(int center); teem-1.11.0~svn6057/src/moss/xform.c0000664000175000017500000001442212165631065016664 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "moss.h" #include "privateMoss.h" /* 0 1 2 3 4 5 6 7 8 a c tx b d ty 0 0 1 0 1 2 3 4 5 */ void mossMatPrint (FILE *f, double *mat) { fprintf(f, "% 15.7f % 15.7f % 15.7f\n", (float)mat[0], (float)mat[1], (float)mat[2]); fprintf(f, "% 15.7f % 15.7f % 15.7f\n", (float)mat[3], (float)mat[4], (float)mat[5]); } double * mossMatRightMultiply (double *_mat, double *_x) { double mat[9], x[9]; MOSS_MAT_6TO9(x, _x); MOSS_MAT_6TO9(mat, _mat); ell_3m_pre_mul_d(mat, x); MOSS_MAT_9TO6(_mat, mat); return _mat; } double * mossMatLeftMultiply (double *_mat, double *_x) { double mat[9], x[9]; MOSS_MAT_6TO9(x, _x); MOSS_MAT_6TO9(mat, _mat); ell_3m_post_mul_d(mat, x); MOSS_MAT_9TO6(_mat, mat); return _mat; } double * mossMatInvert (double *inv, double *mat) { double inv9[9], mat9[9]; MOSS_MAT_6TO9(mat9, mat); ell_3m_inv_d(inv9, mat9); MOSS_MAT_9TO6(inv, inv9); return inv; } double * mossMatIdentitySet (double *mat) { MOSS_MAT_SET(mat, 1, 0, 0, 0, 1, 0); return mat; } double * mossMatTranslateSet (double *mat, double tx, double ty) { MOSS_MAT_SET(mat, 1, 0, tx, 0, 1, ty); return mat; } double * mossMatRotateSet (double *mat, double angle) { angle *= AIR_PI/180.0; MOSS_MAT_SET(mat, cos(angle), -sin(angle), 0, sin(angle), cos(angle), 0); return mat; } double * mossMatFlipSet (double *mat, double angle) { double rot[6], flip[6]; MOSS_MAT_SET(flip, -1, 0, 0, 0, 1, 0); mossMatIdentitySet(mat); mossMatLeftMultiply(mat, mossMatRotateSet(rot, -angle)); mossMatLeftMultiply(mat, flip); mossMatLeftMultiply(mat, mossMatRotateSet(rot, angle)); return mat; } double * mossMatShearSet (double *mat, double angleFixed, double amount) { double rot[6], shear[6]; MOSS_MAT_SET(shear, 1, amount, 0, 0, 1, 0); mossMatIdentitySet(mat); mossMatLeftMultiply(mat, mossMatRotateSet(rot, -angleFixed)); mossMatLeftMultiply(mat, shear); mossMatLeftMultiply(mat, mossMatRotateSet(rot, angleFixed)); return mat; } double * mossMatScaleSet (double *mat, double sx, double sy) { MOSS_MAT_SET(mat, sx, 0, 0, 0, sy, 0); return mat; } void mossMatApply (double *ox, double *oy, double *mat, double ix, double iy) { *ox = mat[0]*ix + mat[1]*iy + mat[2]; *oy = mat[3]*ix + mat[4]*iy + mat[5]; } int mossLinearTransform (Nrrd *nout, Nrrd *nin, float *bg, double *mat, mossSampler *msp, double xMin, double xMax, double yMin, double yMax, int xSize, int ySize) { static const char me[]="mossLinearTransform"; int ncol, xi, yi, ci, ax0, xCent, yCent; float *val, (*ins)(void *v, size_t I, float f), (*clamp)(float val); double inv[6], xInPos, xOutPos, yInPos, yOutPos; if (!(nout && nin && mat && msp && !mossImageCheck(nin))) { biffAddf(MOSS, "%s: got NULL pointer or bad image", me); return 1; } if (mossSamplerImageSet(msp, nin, bg) || mossSamplerUpdate(msp)) { biffAddf(MOSS, "%s: trouble with sampler", me); return 1; } if (!( xMin != xMax && yMin != yMax && xSize > 1 && ySize > 1 )) { biffAddf(MOSS, "%s: bad args: {x,y}Min == {x,y}Max or {x,y}Size <= 1", me); return 1; } ax0 = MOSS_AXIS0(nin); if (!( AIR_EXISTS(nin->axis[ax0+0].min) && AIR_EXISTS(nin->axis[ax0+0].max) && AIR_EXISTS(nin->axis[ax0+1].min) && AIR_EXISTS(nin->axis[ax0+1].max) )) { biffAddf(MOSS, "%s: input axis min,max not set on axes %d and %d", me, ax0+0, ax0+1); return 1; } ncol = MOSS_NCOL(nin); if (mossImageAlloc(nout, nin->type, xSize, ySize, ncol)) { biffAddf(MOSS, "%s: ", me); return 1; } val = (float*)calloc(ncol, sizeof(float)); if (nrrdCenterUnknown == nout->axis[ax0+0].center) nout->axis[ax0+0].center = _mossCenter(nin->axis[ax0+0].center); xCent = nout->axis[ax0+0].center; if (nrrdCenterUnknown == nout->axis[ax0+1].center) nout->axis[ax0+1].center = _mossCenter(nin->axis[ax0+1].center); yCent = nout->axis[ax0+1].center; nout->axis[ax0+0].min = xMin; nout->axis[ax0+0].max = xMax; nout->axis[ax0+1].min = yMin; nout->axis[ax0+1].max = yMax; ins = nrrdFInsert[nin->type]; clamp = nrrdFClamp[nin->type]; if (mossSamplerSample(val, msp, 0, 0)) { biffAddf(MOSS, "%s: trouble in sampler", me); free(val); return 1; } mossMatInvert(inv, mat); for (yi=0; yiaxis[ax0+0].min, nin->axis[ax0+0].max, nin->axis[ax0+0].size, xInPos); yInPos = NRRD_IDX(yCent, nin->axis[ax0+1].min, nin->axis[ax0+1].max, nin->axis[ax0+1].size, yInPos); mossSamplerSample(val, msp, xInPos, yInPos); for (ci=0; cidata, ci + ncol*(xi + xSize*yi), clamp(val[ci])); } } } free(val); return 0; } teem-1.11.0~svn6057/src/moss/GNUmakefile0000664000175000017500000000352212165631065017436 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := moss #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### $(L).NEED = ell nrrd biff hest air $(L).PUBLIC_HEADERS = moss.h $(L).PRIVATE_HEADERS = privateMoss.h $(L).OBJS = defaultsMoss.o methodsMoss.o sampler.o xform.o hestMoss.o $(L).TESTS = test/invert #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/moss/sources.cmake0000664000175000017500000000037311113047450020041 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(MOSS_SOURCES defaultsMoss.c hestMoss.c methodsMoss.c moss.h privateMoss.h sampler.c xform.c ) ADD_TEEM_LIBRARY(moss ${MOSS_SOURCES}) teem-1.11.0~svn6057/src/moss/methodsMoss.c0000664000175000017500000001065512165631065020042 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "moss.h" #include "privateMoss.h" const int mossPresent = 42; /* ******** mossSamplerNew() ** */ mossSampler * mossSamplerNew (void) { mossSampler *smplr; int i; smplr = (mossSampler *)calloc(1, sizeof(mossSampler)); if (smplr) { smplr->image = NULL; smplr->kernel = NULL; for (i=0; ikparm[i] = AIR_NAN; smplr->ivc = NULL; smplr->xFslw = smplr->yFslw = NULL; smplr->xIdx = smplr->yIdx = NULL; smplr->bg = NULL; smplr->fdiam = smplr->ncol = 0; smplr->boundary = mossDefBoundary; for (i=0; iflag[i] = AIR_FALSE; } return smplr; } int mossSamplerFill (mossSampler *smplr, int fdiam, int ncol) { static const char me[]="_mossSamplerFill"; if (!(smplr)) { biffAddf(MOSS, "%s: got NULL pointer", me); return 1; } smplr->ivc = (float*)calloc(fdiam*fdiam*ncol, sizeof(float)); smplr->xFslw = (double*)calloc(fdiam, sizeof(double)); smplr->yFslw = (double*)calloc(fdiam, sizeof(double)); smplr->xIdx = (int*)calloc(fdiam, sizeof(int)); smplr->yIdx = (int*)calloc(fdiam, sizeof(int)); if (!( smplr->ivc && smplr->xFslw && smplr->yFslw && smplr->xIdx && smplr->yIdx )) { biffAddf(MOSS, "%s: couldn't allocate buffers", me); return 1; } smplr->fdiam = fdiam; smplr->ncol = ncol; return 0; } void mossSamplerEmpty (mossSampler *smplr) { if (smplr) { smplr->ivc = (float *)airFree(smplr->ivc); smplr->xFslw = (double *)airFree(smplr->xFslw); smplr->yFslw = (double *)airFree(smplr->yFslw); smplr->xIdx = (int *)airFree(smplr->xIdx); smplr->yIdx = (int *)airFree(smplr->yIdx); smplr->fdiam = 0; smplr->ncol = 0; } return; } mossSampler * mossSamplerNix (mossSampler *smplr) { if (smplr) { mossSamplerEmpty(smplr); smplr->bg = (float *)airFree(smplr->bg); free(smplr); } return NULL; } int mossImageCheck (Nrrd *image) { static const char me[]="mossImageCheck"; if (nrrdCheck(image)) { biffMovef(MOSS, NRRD, "%s: given nrrd invalid", me); return 1; } if (!( (2 == image->dim || 3 == image->dim) && nrrdTypeBlock != image->type )) { biffAddf(MOSS, "%s: image has invalid dimension (%d) or type (%s)", me, image->dim, airEnumStr(nrrdType, image->type)); return 1; } return 0; } int mossImageAlloc (Nrrd *image, int type, int sx, int sy, int ncol) { static const char me[]="mossImageAlloc"; int ret; if (!(image && AIR_IN_OP(nrrdTypeUnknown, type, nrrdTypeBlock) && sx > 0 && sy > 0 && ncol > 0)) { biffAddf(MOSS, "%s: got NULL pointer or bad args", me); return 1; } if (1 == ncol) { ret = nrrdMaybeAlloc_va(image, type, 2, AIR_CAST(size_t, sx), AIR_CAST(size_t, sy)); } else { ret = nrrdMaybeAlloc_va(image, type, 3, AIR_CAST(size_t, ncol), AIR_CAST(size_t, sx), AIR_CAST(size_t, sy)); } if (ret) { biffMovef(MOSS, NRRD, "%s: couldn't allocate image", me); return 1; } return 0; } int _mossCenter(int center) { center = (nrrdCenterUnknown == center ? mossDefCenter : center); center = AIR_CLAMP(nrrdCenterUnknown+1, center, nrrdCenterLast-1); return center; } teem-1.11.0~svn6057/src/moss/sampler.c0000664000175000017500000001510412165631065017172 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "moss.h" #include "privateMoss.h" int mossSamplerImageSet (mossSampler *smplr, Nrrd *image, float *bg) { static const char me[]="mossSamplerImageSet"; int ci, ncol; if (!(smplr && image)) { biffAddf(MOSS, "%s: got NULL pointer", me); return 1; } if (mossImageCheck(image)) { biffAddf(MOSS, "%s: ", me); return 1; } smplr->image = image; smplr->flag[mossFlagImage] = AIR_TRUE; ncol = MOSS_NCOL(image); smplr->bg = (float *)airFree(smplr->bg); if (bg) { smplr->bg = (float*)calloc(ncol, sizeof(float)); for (ci=0; cibg[ci] = bg[ci]; } } return 0; } int mossSamplerKernelSet (mossSampler *smplr, const NrrdKernel *kernel, double *kparm) { static const char me[]="mossSamplerKernelSet"; unsigned int ki; if (!(smplr && kernel && kparm)) { biffAddf(MOSS, "%s: got NULL pointer", me); return 1; } smplr->kernel = kernel; for (ki=0; kinumParm; ki++) { smplr->kparm[ki] = kparm[ki]; } smplr->flag[mossFlagKernel] = AIR_TRUE; return 0; } int mossSamplerUpdate (mossSampler *smplr) { static const char me[]="mossSamplerUpdate"; int ncol=0, fdiam=0; if (!(smplr)) { biffAddf(MOSS, "%s: got NULL pointer", me); return 1; } if (smplr->flag[mossFlagImage]) { ncol = MOSS_NCOL(smplr->image); if (ncol != smplr->ncol) { mossSamplerEmpty(smplr); smplr->ncol = ncol; } } if (smplr->flag[mossFlagKernel]) { fdiam = 2*AIR_ROUNDUP(smplr->kernel->support(smplr->kparm)); if (fdiam != smplr->fdiam) { mossSamplerEmpty(smplr); smplr->fdiam = fdiam; } } if (!(smplr->ivc)) { if (mossSamplerFill(smplr, fdiam, ncol)) { biffAddf(MOSS, "%s: ", me); return 1; } } if (nrrdBoundaryPad == smplr->boundary && !smplr->bg) { biffAddf(MOSS, "%s: want %s boundary behavior, but bg vector is NULL", me, airEnumStr(nrrdBoundary, nrrdBoundaryPad)); return 1; } return 0; } int mossSamplerSample (float *val, mossSampler *smplr, double xPos, double yPos) { static const char me[]="mossSamplerSample"; int i, xi, yi, ci, sx, sy, fdiam, frad, ncol; double xf, yf, tmp; float (*lup)(const void *v, size_t I); if (!(val && smplr)) { biffAddf(MOSS, "%s: got NULL pointer", me); return 1; } if (!(smplr->ivc)) { biffAddf(MOSS, "%s: given sampler not ready (no caches)", me); return 1; } /* set {x,y}Idx, set {x,y}Fslw to sample locations */ if (mossVerbose) { fprintf(stderr, "%s: pos = %g %g\n", me, xPos, yPos); } sx = MOSS_SX(smplr->image); sy = MOSS_SY(smplr->image); xi = (int)floor(xPos); xf = xPos - xi; yi = (int)floor(yPos); yf = yPos - yi; fdiam = smplr->fdiam; frad = fdiam/2; for (i=0; ixIdx[i] = xi + i - frad + 1; smplr->yIdx[i] = yi + i - frad + 1; smplr->xFslw[i] = xf - i + frad - 1; smplr->yFslw[i] = yf - i + frad - 1; } if (mossVerbose) { fprintf(stderr, " --> xIdx: %d %d ; xFsl %g %g\n", smplr->xIdx[0], smplr->xIdx[1], smplr->xFslw[0], smplr->xFslw[1]); fprintf(stderr, " yIdx: %d %d ; yFsl %g %g\n", smplr->yIdx[0], smplr->yIdx[1], smplr->yFslw[0], smplr->yFslw[1]); } switch(smplr->boundary) { case nrrdBoundaryBleed: for (i=0; ixIdx[i] = AIR_CLAMP(0, smplr->xIdx[i], sx-1); smplr->yIdx[i] = AIR_CLAMP(0, smplr->yIdx[i], sy-1); } break; case nrrdBoundaryWrap: for (i=0; ixIdx[i] = AIR_MOD(smplr->xIdx[i], sx); smplr->yIdx[i] = AIR_MOD(smplr->yIdx[i], sy); } break; case nrrdBoundaryPad: /* this is handled later */ break; default: biffAddf(MOSS, "%s: sorry, %s boundary not implemented", me, airEnumStr(nrrdBoundary, smplr->boundary)); return 1; } if (mossVerbose) { fprintf(stderr, " --> xIdx: %d %d ; xFsl %g %g\n", smplr->xIdx[0], smplr->xIdx[1], smplr->xFslw[0], smplr->xFslw[1]); } /* copy values to ivc, set {x,y}Fslw to filter sample weights */ lup = nrrdFLookup[smplr->image->type]; ncol = smplr->ncol; if (nrrdBoundaryPad == smplr->boundary) { for (yi=0; yixIdx[xi], sx-1) && AIR_IN_CL(0, smplr->yIdx[yi], sy-1)) { for (ci=0; ciivc[xi + fdiam*(yi + fdiam*ci)] = lup(smplr->image->data, ci + ncol*(smplr->xIdx[xi] + sx*smplr->yIdx[yi])); } } else { for (ci=0; ciivc[xi + fdiam*(yi + fdiam*ci)] = smplr->bg[ci]; } } } } } else { for (yi=0; yiivc[xi + fdiam*(yi + fdiam*ci)] = lup(smplr->image->data, ci + ncol*(smplr->xIdx[xi] + sx*smplr->yIdx[yi])); } } } } smplr->kernel->evalN_d(smplr->xFslw, smplr->xFslw, fdiam, smplr->kparm); smplr->kernel->evalN_d(smplr->yFslw, smplr->yFslw, fdiam, smplr->kparm); /* do convolution */ memset(val, 0, ncol*sizeof(float)); for (ci=0; cixFslw[xi]*smplr->ivc[xi + fdiam*(yi + fdiam*ci)]; } val[ci] += AIR_CAST(float, smplr->yFslw[yi]*tmp); } } return 0; } teem-1.11.0~svn6057/src/moss/hestMoss.c0000664000175000017500000000764112165631065017343 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "moss.h" #include "privateMoss.h" int _mossHestTransformParse (void *ptr, char *_str, char err[AIR_STRLEN_HUGE]) { char me[]="_mossHestTransformParse", *str; double **matP, tx, ty, sx, sy, angle, mat[6], shf, sha; airArray *mop; if (!(ptr && _str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } matP = (double **)ptr; mop = airMopNew(); *matP = (double *)calloc(6, sizeof(double)); airMopMem(mop, matP, airMopOnError); str = airToLower(airStrdup(_str)); airMopMem(mop, &str, airMopAlways); if (!strcmp("identity", str)) { mossMatIdentitySet(*matP); } else if (1 == sscanf(str, "flip:%lf", &angle)) { mossMatFlipSet(*matP, angle); } else if (2 == sscanf(str, "translate:%lf,%lf", &tx, &ty)) { mossMatTranslateSet(*matP, tx, ty); } else if (2 == sscanf(str, "t:%lf,%lf", &tx, &ty)) { mossMatTranslateSet(*matP, tx, ty); } else if (1 == sscanf(str, "rotate:%lf", &angle)) { mossMatRotateSet(*matP, angle); } else if (1 == sscanf(str, "r:%lf", &angle)) { mossMatRotateSet(*matP, angle); } else if (2 == sscanf(str, "scale:%lf,%lf", &sx, &sy)) { mossMatScaleSet(*matP, sx, sy); } else if (2 == sscanf(str, "s:%lf,%lf", &sx, &sy)) { mossMatScaleSet(*matP, sx, sy); } else if (2 == sscanf(str, "shear:%lf,%lf", &shf, &sha)) { mossMatShearSet(*matP, shf, sha); } else if (6 == sscanf(str, "%lf,%lf,%lf,%lf,%lf,%lf", mat+0, mat+1, mat+2, mat+3, mat+4, mat+5)) { MOSS_MAT_COPY(*matP, mat); } else { sprintf(err, "%s: couldn't parse \"%s\" as a transform", me, _str); airMopError(mop); return 1; } airMopOkay(mop); return 0; } hestCB _mossHestTransform = { sizeof(double*), "2D transform", _mossHestTransformParse, airFree }; hestCB * mossHestTransform = &_mossHestTransform; /* ----------------------------------------------------------------- */ /* ** _mossHestOriginParse() ** ** parse an origin specification ** p(x,y): absolute pixel position --> val[3] = (0,x,y) ** u(x,y): position in unit box [0,1]x[0,1] --> val[3] = (1,x,y) */ int _mossHestOriginParse (void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[]="_mossHestOriginParse"; double **valP; airArray *mop; valP = (double **)ptr; mop = airMopNew(); *valP = (double *)calloc(3, sizeof(double)); airMopMem(mop, valP, airMopOnError); if (2 == sscanf(str, "p:%lf,%lf", *valP + 1, *valP + 2)) { (*valP)[0] = 0; } else if (2 == sscanf(str, "u:%lf,%lf", *valP + 1, *valP + 2)) { (*valP)[0] = 1; } else { sprintf(err, "%s: couldn't parse \"%s\" as origin", me, str); airMopError(mop); return 1; } airMopOkay(mop); return 0; } hestCB _mossHestOrigin = { sizeof(double*), "origin specification", _mossHestOriginParse, airFree }; hestCB * mossHestOrigin = &_mossHestOrigin; teem-1.11.0~svn6057/src/moss/defaultsMoss.c0000664000175000017500000000246212165631065020203 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "moss.h" #include "privateMoss.h" const char * mossBiffKey = "moss"; int mossDefBoundary = nrrdBoundaryBleed; int mossDefCenter = nrrdCenterCell; int mossVerbose = 0; teem-1.11.0~svn6057/src/moss/moss.h0000664000175000017500000001335312165631065016521 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MOSS_HAS_BEEN_INCLUDED #define MOSS_HAS_BEEN_INCLUDED /* NOTE: this library has not undergone the changes as other Teem libraries in order to make sure that array lengths and indices are stored in unsigned types */ #include #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(moss_EXPORTS) || defined(teem_EXPORTS) # define MOSS_EXPORT extern __declspec(dllexport) # else # define MOSS_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define MOSS_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define MOSS mossBiffKey #define MOSS_NCOL(img) (3 == (img)->dim ? (img)->axis[0].size : 1) #define MOSS_AXIS0(img) (3 == (img)->dim ? 1 : 0) #define MOSS_SX(img) (3 == (img)->dim \ ? (img)->axis[1].size \ : (img)->axis[0].size ) #define MOSS_SY(img) (3 == (img)->dim \ ? (img)->axis[2].size \ : (img)->axis[1].size ) enum { mossFlagUnknown=-1, /* -1: nobody knows */ mossFlagImage, /* 0: image being sampled */ mossFlagKernel, /* 1: kernel(s) used for sampling */ mossFlagLast }; #define MOSS_FLAG_NUM 2 typedef struct { Nrrd *image; /* the image to sample */ const NrrdKernel *kernel; /* which kernel to use on both axes */ double kparm[NRRD_KERNEL_PARMS_NUM]; /* kernel arguments */ float *ivc; /* intermediate value cache */ double *xFslw, *yFslw; /* filter sample locations->weights */ int fdiam, ncol; /* filter diameter; ivc is allocated for (fdiam+1) x (fdiam+1) x ncol doubles, with that axis ordering */ int *xIdx, *yIdx; /* arrays for x and y coordinates, both allocated for fdiam */ float *bg; /* background color */ int boundary; /* from nrrdBoundary* enum */ int flag[MOSS_FLAG_NUM]; /* I'm a flag-waving struct */ } mossSampler; /* defaultsMoss.c */ MOSS_EXPORT const char *mossBiffKey; MOSS_EXPORT int mossDefBoundary; MOSS_EXPORT int mossDefCenter; MOSS_EXPORT int mossVerbose; /* methodsMoss.c */ MOSS_EXPORT const int mossPresent; MOSS_EXPORT mossSampler *mossSamplerNew(void); MOSS_EXPORT int mossSamplerFill(mossSampler *smplr, int fdiam, int ncol); MOSS_EXPORT void mossSamplerEmpty(mossSampler *smplr); MOSS_EXPORT mossSampler *mossSamplerNix(mossSampler *smplr); MOSS_EXPORT int mossImageCheck(Nrrd *image); MOSS_EXPORT int mossImageAlloc(Nrrd *image, int type, int sx, int sy, int ncol); /* sampler.c */ MOSS_EXPORT int mossSamplerImageSet(mossSampler *smplr, Nrrd *image, float *bg); MOSS_EXPORT int mossSamplerKernelSet(mossSampler *smplr, const NrrdKernel *kernel, double *kparm); MOSS_EXPORT int mossSamplerUpdate(mossSampler *smplr); MOSS_EXPORT int mossSamplerSample(float *val, mossSampler *smplr, double xPos, double yPos); /* hestMoss.c */ MOSS_EXPORT hestCB *mossHestTransform; MOSS_EXPORT hestCB *mossHestOrigin; /* xform.c */ MOSS_EXPORT void mossMatPrint(FILE *f, double *mat); MOSS_EXPORT double *mossMatRightMultiply(double *mat, double *x); MOSS_EXPORT double *mossMatLeftMultiply (double *mat, double *x); MOSS_EXPORT double *mossMatInvert(double *inv, double *mat); MOSS_EXPORT double *mossMatIdentitySet(double *mat); MOSS_EXPORT double *mossMatTranslateSet(double *mat, double tx, double ty); MOSS_EXPORT double *mossMatRotateSet(double *mat, double angle); MOSS_EXPORT double *mossMatFlipSet(double *mat, double angle); MOSS_EXPORT double *mossMatShearSet(double *mat, double angleFixed, double amount); MOSS_EXPORT double *mossMatScaleSet(double *mat, double sx, double sy); MOSS_EXPORT void mossMatApply(double *ox, double *oy, double *mat, double ix, double iy); MOSS_EXPORT int mossLinearTransform(Nrrd *nout, Nrrd *nin, float *bg, double *mat, mossSampler *msp, double xMin, double xMax, double yMin, double yMax, int sx, int sy); #ifdef __cplusplus } #endif #endif /* MOSS_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/moss/test/0000775000175000017500000000000012203513753016335 5ustar domibeldomibelteem-1.11.0~svn6057/src/moss/test/invert.c0000664000175000017500000000327112042324052020004 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../moss.h" int main(int argc, const char *argv[]) { const char *me, *info="inverts a moss transform"; hestOpt *hopt=NULL; double *mat, inv[6]; me = argv[0]; hestOptAdd(&hopt, "t", "transform", airTypeOther, 1, 1, &mat, "identity", "transform(s) to apply to image", NULL, NULL, mossHestTransform); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); fprintf(stderr, "%s: got transform:\n", me); mossMatPrint(stderr, mat); mossMatInvert(inv, mat); fprintf(stderr, "\n%s: inverse:\n", me); mossMatPrint(stderr, inv); exit(0); } teem-1.11.0~svn6057/src/moss/TODO.txt0000664000175000017500000000032107722615035016666 0ustar domibeldomibel- nix all the mossMat stuff- and change over to using a 9-element matrix, since this is needed to represent the general perspective transforms which underlying doing mosiacs - add mosaic stuff from old moss teem-1.11.0~svn6057/src/push/0000775000175000017500000000000012203513755015356 5ustar domibeldomibelteem-1.11.0~svn6057/src/push/methodsPush.c0000664000175000017500000001137312165631065020034 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "push.h" #include "privatePush.h" /* ** the reason to take the pushContext *pctx argument is to allow ** doling out the ttaagg ID */ pushPoint * pushPointNew(pushContext *pctx) { pushPoint *pnt; pushPtrPtrUnion pppu; if (pctx) { pnt = AIR_CAST(pushPoint *, calloc(1, sizeof(pushPoint))); if (pnt) { pnt->ttaagg = pctx->ttaagg++; ELL_3V_SET(pnt->pos, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(pnt->frc, AIR_NAN, AIR_NAN, AIR_NAN); TEN_T_SET(pnt->ten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); TEN_T_SET(pnt->inv, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(pnt->cnt, AIR_NAN, AIR_NAN, AIR_NAN); pnt->grav = AIR_NAN; ELL_3V_SET(pnt->gravGrad, AIR_NAN, AIR_NAN, AIR_NAN); pnt->seedThresh = AIR_NAN; pnt->enr = DBL_MAX; /* any finite quantity will be less than this */ pnt->neighArr = airArrayNew((pppu.point = &(pnt->neigh), pppu.v), &(pnt->neighNum), sizeof(pushPoint *), 10); } } else { pnt = NULL; } return pnt; } pushPoint * pushPointNix(pushPoint *pnt) { airFree(pnt); return NULL; } pushContext * pushContextNew(void) { pushContext *pctx; pctx = (pushContext *)calloc(1, sizeof(pushContext)); if (pctx) { pctx->pointNum = 0; pctx->nin = NULL; pctx->npos = NULL; pctx->stepInitial = 1; pctx->scale = 0.2; pctx->wall = 0.1; pctx->cntScl = 0.0; pctx->deltaLimit = 0.3; pctx->deltaFracMin = 0.2; pctx->energyStepFrac = 0.9; pctx->deltaFracStepFrac = 0.5; pctx->neighborTrueProb = 0.3; pctx->probeProb = 0.5; pctx->energyImprovMin = 0.01; pctx->detReject = AIR_FALSE; pctx->midPntSmp = AIR_FALSE; pctx->verbose = 0; pctx->seedRNG = 42; pctx->threadNum = 1; pctx->maxIter = 0; pctx->snap = 0; pctx->gravItem = tenGageUnknown; pctx->gravGradItem = tenGageUnknown; pctx->gravScl = AIR_NAN; pctx->gravZero = AIR_NAN; pctx->seedThreshItem = tenGageUnknown; pctx->seedThreshSign = +1; pctx->seedThresh = 0.0; pctx->ensp = pushEnergySpecNew(); pctx->binSingle = AIR_FALSE; pctx->binIncr = 512; pctx->ksp00 = nrrdKernelSpecNew(); pctx->ksp11 = nrrdKernelSpecNew(); pctx->ksp22 = nrrdKernelSpecNew(); pctx->ttaagg = 0; pctx->nten = NULL; pctx->ninv = NULL; pctx->nmask = NULL; pctx->gctx = NULL; pctx->tpvl = NULL; pctx->ipvl = NULL; pctx->finished = AIR_FALSE; pctx->dimIn = 0; pctx->sliceAxis = 42; /* an invalid value */ pctx->bin = NULL; ELL_3V_SET(pctx->binsEdge, 0, 0, 0); pctx->binNum = 0; pctx->binIdx = 0; pctx->binMutex = NULL; pctx->step = AIR_NAN; pctx->maxDist = AIR_NAN; pctx->maxEval = AIR_NAN; pctx->meanEval = AIR_NAN; pctx->maxDet = AIR_NAN; pctx->energySum = 0; pctx->task = NULL; pctx->iterBarrierA = NULL; pctx->iterBarrierB = NULL; pctx->deltaFrac = AIR_NAN; pctx->timeIteration = 0; pctx->timeRun = 0; pctx->iter = 0; pctx->noutPos = nrrdNew(); pctx->noutTen = nrrdNew(); } return pctx; } /* ** this should only nix things created by pushContextNew */ pushContext * pushContextNix(pushContext *pctx) { if (pctx) { pctx->ensp = pushEnergySpecNix(pctx->ensp); pctx->ksp00 = nrrdKernelSpecNix(pctx->ksp00); pctx->ksp11 = nrrdKernelSpecNix(pctx->ksp11); pctx->ksp22 = nrrdKernelSpecNix(pctx->ksp22); pctx->noutPos = nrrdNuke(pctx->noutPos); pctx->noutTen = nrrdNuke(pctx->noutTen); airFree(pctx); } return NULL; } teem-1.11.0~svn6057/src/push/push.h0000664000175000017500000003362212165631065016516 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PUSH_HAS_BEEN_INCLUDED #define PUSH_HAS_BEEN_INCLUDED #include #include #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(push_EXPORTS) || defined(teem_EXPORTS) # define PUSH_EXPORT extern __declspec(dllexport) # else # define PUSH_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define PUSH_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define PUSH pushBiffKey #define PUSH_THREAD_MAXNUM 512 /* ******** pushPoint ** ** information about a point in the simulation. There are really two ** kinds of information here: "pos", "enr", "frc" pertain to the ** simulation of point dynamics, while "ten", "inv", "cnt", "grav", ** "gravGrad", "seedThresh" are properties of the field sampled at the ** point. */ typedef struct pushPoint_t { unsigned int ttaagg; double pos[3], /* position in world space */ enr, /* energy accumulator (current iteration) */ frc[3], /* force accumulator (current iteration) */ ten[7], /* tensor here */ inv[7], /* inverse of tensor */ cnt[3], /* mask's containment gradient */ grav, gravGrad[3], /* gravity stuff */ seedThresh; /* seed thresh */ /* per-point list of active neighbors- which is updated only periodically. In addition to spatial binning, this greatly reduces the number of pair-wise interactions computed (based on idea from Meyer et al.) */ struct pushPoint_t **neigh; unsigned int neighNum; airArray *neighArr; } pushPoint; /* ******** pushBin ** ** the data structure for doing spatial binning. ** ** in tractlet-less push, bins do own the points they contain */ typedef struct pushBin_t { unsigned int pointNum; /* # of points in this bin */ pushPoint **point; /* dyn. alloc. array of point pointers */ airArray *pointArr; /* airArray around point and pointNum */ struct pushBin_t **neighbor; /* pre-computed NULL-terminated list of all neighboring bins, including myself */ } pushBin; /* ******** pushTask ** ** The information specific for a thread. */ typedef struct pushTask_t { struct pushContext_t *pctx; /* parent's context */ gageContext *gctx; /* result of gageContextCopy(pctx->gctx) */ const double *tenAns, /* results of gage probing */ *invAns, *cntAns, *gravAns, *gravGradAns, *seedThreshAns; airThread *thread; /* my thread */ unsigned int threadIdx, /* which thread am I */ pointNum; /* # points I let live this iteration */ double energySum, /* sum of energies of points I processed */ deltaFracSum; /* contribution to pctx->deltaFrac */ airRandMTState *rng; /* state for my RNG */ void *returnPtr; /* for airThreadJoin */ } pushTask; /* ******** pushEnergyType* enum ** ** the different shapes of potential energy profiles that can be used */ enum { pushEnergyTypeUnknown, /* 0 */ pushEnergyTypeSpring, /* 1 */ pushEnergyTypeGauss, /* 2 */ pushEnergyTypeCoulomb, /* 3 */ pushEnergyTypeCotan, /* 4 */ pushEnergyTypeZero, /* 5 */ pushEnergyTypeLast }; #define PUSH_ENERGY_TYPE_MAX 5 #define PUSH_ENERGY_PARM_NUM 3 /* ******** pushEnergy ** ** the functions which determine inter-point forces ** ** NOTE: the eval() function probably does NOT check to see it was passed ** non-NULL pointers into which to store energy and force */ typedef struct { char name[AIR_STRLEN_SMALL]; unsigned int parmNum; void (*eval)(double *energy, double *force, double dist, const double parm[PUSH_ENERGY_PARM_NUM]); double (*support)(const double parm[PUSH_ENERGY_PARM_NUM]); } pushEnergy; typedef struct { const pushEnergy *energy; double parm[PUSH_ENERGY_PARM_NUM]; } pushEnergySpec; /* ******** pushContext ** ** everything for doing one simulation computation ** */ typedef struct pushContext_t { /* INPUT ----------------------------- */ unsigned int pointNum; /* number points to start simulation w/ */ Nrrd *nin, /* 3D image of 3D masked tensors, though it may only be a single slice */ *npos; /* positions to start with (overrides pointNum) */ double stepInitial, /* initial time step in integration (which will be reduced as the system converges) */ scale, /* scaling from tensor to glyph size */ wall, /* spring constant of walls */ cntScl, /* magnitude of containment gradient */ deltaLimit, /* speed limit on particles' motion, as a fraction of glyph radius along direction of motion */ deltaFracMin, /* lowest value of deltaFrac (see below) that is allowed without decreasing step size */ energyStepFrac, /* when energy goes up instead of down, the fraction by which to scale step size */ deltaFracStepFrac, /* when deltaFrac goes below deltaFracMin, fraction by which to scale step size */ neighborTrueProb, /* probability that we find the true neighbors of the particle, as opposed to using a cached list */ probeProb, /* probability that we gageProbe() to find the local tensor value, instead of re-using last value */ energyImprovMin; /* convergence threshold: stop when fracional improvement (decrease) in energy dips below this */ int detReject, /* determinant-based rejection at init */ midPntSmp, /* sample midpoint btw part.s for physics */ verbose; /* blah blah blah */ unsigned int seedRNG, /* seed value for random number generator */ threadNum, /* number of threads to use */ maxIter, /* if non-zero, max number of iterations */ snap; /* if non-zero, interval between iterations at which output snapshots are saved */ int gravItem, /* tenGage item (scalar) for "height" potential energy associated w/ gravity */ gravGradItem; /* tenGage item (vector) for gravity */ double gravScl, /* sign and magnitude of gravity's effect: when this is positive, higher values of gravItem have higher potential energy */ gravZero; /* the height that corresponds to zero potential energy from gravity */ int seedThreshItem, /* item for constraining random seeding */ seedThreshSign; /* +1: need val > thresh; -1: opposite */ double seedThresh; /* threshold for seed constraint */ pushEnergySpec *ensp; /* potential energy function to use */ int binSingle; /* disable binning (for debugging) */ unsigned int binIncr; /* increment for per-bin airArray */ NrrdKernelSpec *ksp00, /* for sampling tensor field */ *ksp11, /* for gradient of mask, other 1st derivs */ *ksp22; /* for 2nd derivatives */ /* INTERNAL -------------------------- */ unsigned int ttaagg; /* next value for per-point ID */ Nrrd *nten, /* 3D image of 3D masked tensors */ *ninv, /* pre-computed inverse of nten */ *nmask; /* mask image from nten */ gageContext *gctx; /* gage context around nten, ninv, nmask */ gagePerVolume *tpvl, *ipvl; /* gage pervolumes around nten and ninv */ int finished; /* used to signal all threads to return */ unsigned int dimIn, /* dim (2 or 3) of input, meaning whether it was a single slice or a full volume */ sliceAxis; /* got a single 3-D slice, which axis had only a single sample */ pushBin *bin; /* volume of bins (see binsEdge, binNum) */ unsigned int binsEdge[3], /* # bins along each volume edge, determined by maxEval and scale */ binNum, /* total # bins in grid */ binIdx; /* *next* bin of points needing to be processed. Stage is done when binIdx == binNum */ airThreadMutex *binMutex; /* mutex around bin */ double step, /* current working step size */ maxDist, /* max distance btween interacting points */ maxEval, meanEval, /* max and mean principal eval in field */ maxDet, energySum; /* potential energy of entire particles */ pushTask **task; /* dynamically allocated array of tasks */ airThreadBarrier *iterBarrierA; /* barriers between iterations */ airThreadBarrier *iterBarrierB; /* barriers between iterations */ double deltaFrac; /* mean (over all particles in last iteration) of fraction of distance actually travelled to distance that it wanted to travel (due to speed limit) */ /* OUTPUT ---------------------------- */ double timeIteration, /* time needed for last (single) iter */ timeRun; /* total time spent in computation */ unsigned int iter; /* how many iterations were needed */ Nrrd *noutPos, /* list of 2D or 3D positions */ *noutTen; /* list of 2D or 3D masked tensors */ } pushContext; typedef union { pushPoint ***point; void **v; } pushPtrPtrUnion; /* defaultsPush.c */ PUSH_EXPORT const int pushPresent; PUSH_EXPORT const char *pushBiffKey; /* methodsPush.c */ PUSH_EXPORT pushPoint *pushPointNew(pushContext *pctx); PUSH_EXPORT pushPoint *pushPointNix(pushPoint *pnt); PUSH_EXPORT pushContext *pushContextNew(void); PUSH_EXPORT pushContext *pushContextNix(pushContext *pctx); /* forces.c (legacy name for info about (derivatives of) energy functions) */ PUSH_EXPORT const airEnum *const pushEnergyType; PUSH_EXPORT const pushEnergy *const pushEnergyUnknown; PUSH_EXPORT const pushEnergy *const pushEnergySpring; PUSH_EXPORT const pushEnergy *const pushEnergyGauss; PUSH_EXPORT const pushEnergy *const pushEnergyCoulomb; PUSH_EXPORT const pushEnergy *const pushEnergyCotan; PUSH_EXPORT const pushEnergy *const pushEnergyZero; PUSH_EXPORT const pushEnergy *const pushEnergyAll[PUSH_ENERGY_TYPE_MAX+1]; PUSH_EXPORT pushEnergySpec *pushEnergySpecNew(void); PUSH_EXPORT void pushEnergySpecSet(pushEnergySpec *ensp, const pushEnergy *energy, const double parm[PUSH_ENERGY_PARM_NUM]); PUSH_EXPORT pushEnergySpec *pushEnergySpecNix(pushEnergySpec *ensp); PUSH_EXPORT int pushEnergySpecParse(pushEnergySpec *ensp, const char *str); PUSH_EXPORT hestCB *pushHestEnergySpec; /* corePush.c */ PUSH_EXPORT int pushStart(pushContext *pctx); PUSH_EXPORT int pushIterate(pushContext *pctx); PUSH_EXPORT int pushRun(pushContext *pctx); PUSH_EXPORT int pushFinish(pushContext *pctx); /* binning.c */ PUSH_EXPORT void pushBinInit(pushBin *bin, unsigned int incr); PUSH_EXPORT void pushBinDone(pushBin *bin); PUSH_EXPORT int pushBinPointAdd(pushContext *pctx, pushPoint *point); PUSH_EXPORT void pushBinAllNeighborSet(pushContext *pctx); PUSH_EXPORT int pushRebin(pushContext *pctx); /* action.c */ PUSH_EXPORT int pushBinProcess(pushTask *task, unsigned int myBinIdx); PUSH_EXPORT int pushOutputGet(Nrrd *nPos, Nrrd *nTen, Nrrd *nEnr, pushContext *pctx); #ifdef __cplusplus } #endif #endif /* PUSH_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/push/defaultsPush.c0000664000175000017500000000240112165631065020170 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "push.h" #include "privatePush.h" const int pushPresent = 42; const char * pushBiffKey = "push"; int _pushVerbose = 0; teem-1.11.0~svn6057/src/push/action.c0000664000175000017500000003274112165631065017010 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "push.h" #include "privatePush.h" unsigned int _pushPointTotal(pushContext *pctx) { unsigned int binIdx, pointNum; pushBin *bin; pointNum = 0; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; pointNum += bin->pointNum; } return pointNum; } int _pushProbe(pushTask *task, pushPoint *point) { static const char me[]="_pushProbe"; double posWorld[4], posIdx[4]; ELL_3V_COPY(posWorld, point->pos); posWorld[3] = 1.0; ELL_4MV_MUL(posIdx, task->gctx->shape->WtoI, posWorld); ELL_4V_HOMOG(posIdx, posIdx); posIdx[0] = AIR_CLAMP(-0.5, posIdx[0], task->gctx->shape->size[0]-0.5); posIdx[1] = AIR_CLAMP(-0.5, posIdx[1], task->gctx->shape->size[1]-0.5); posIdx[2] = AIR_CLAMP(-0.5, posIdx[2], task->gctx->shape->size[2]-0.5); if (gageProbe(task->gctx, posIdx[0], posIdx[1], posIdx[2])) { biffAddf(PUSH, "%s: gageProbe failed:\n (%d) %s\n", me, task->gctx->errNum, task->gctx->errStr); return 1; } TEN_T_COPY(point->ten, task->tenAns); TEN_T_COPY(point->inv, task->invAns); ELL_3V_COPY(point->cnt, task->cntAns); if (tenGageUnknown != task->pctx->gravItem) { point->grav = task->gravAns[0]; ELL_3V_COPY(point->gravGrad, task->gravGradAns); } if (tenGageUnknown != task->pctx->seedThreshItem) { point->seedThresh = task->seedThreshAns[0]; } return 0; } int pushOutputGet(Nrrd *nPosOut, Nrrd *nTenOut, Nrrd *nEnrOut, pushContext *pctx) { static const char me[]="pushOutputGet"; unsigned int binIdx, pointRun, pointNum, pointIdx; int E; float *posOut, *tenOut, *enrOut; pushBin *bin; pushPoint *point; pointNum = _pushPointTotal(pctx); E = AIR_FALSE; if (nPosOut) { E |= nrrdMaybeAlloc_va(nPosOut, nrrdTypeFloat, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, pointNum)); } if (nTenOut) { E |= nrrdMaybeAlloc_va(nTenOut, nrrdTypeFloat, 2, AIR_CAST(size_t, 7), AIR_CAST(size_t, pointNum)); } if (nEnrOut) { E |= nrrdMaybeAlloc_va(nEnrOut, nrrdTypeFloat, 1, AIR_CAST(size_t, pointNum)); } if (E) { biffMovef(PUSH, NRRD, "%s: trouble allocating outputs", me); return 1; } posOut = nPosOut ? (float*)(nPosOut->data) : NULL; tenOut = nTenOut ? (float*)(nTenOut->data) : NULL; enrOut = nEnrOut ? (float*)(nEnrOut->data) : NULL; pointRun = 0; for (binIdx=0; binIdxbinNum; binIdx++) { bin = pctx->bin + binIdx; for (pointIdx=0; pointIdxpointNum; pointIdx++) { point = bin->point[pointIdx]; if (posOut) { ELL_3V_SET_TT(posOut + 3*pointRun, float, point->pos[0], point->pos[1], point->pos[2]); } if (tenOut) { TEN_T_COPY_TT(tenOut + 7*pointRun, float, point->ten); } if (enrOut) { enrOut[pointRun] = AIR_CAST(float, point->enr); } pointRun++; } } return 0; } int _pushPairwiseEnergy(pushTask *task, double *enrP, double frc[3], pushEnergySpec *ensp, pushPoint *myPoint, pushPoint *herPoint, double YY[3], double iscl) { static const char me[]="_pushPairwiseEnergy"; double inv[7], XX[3], nXX[3], rr, mag, WW[3]; if (task->pctx->midPntSmp) { pushPoint _tmpPoint; double det; ELL_3V_SCALE_ADD2(_tmpPoint.pos, 0.5, myPoint->pos, 0.5, herPoint->pos); if (_pushProbe(task, &_tmpPoint)) { biffAddf(PUSH, "%s: at midpoint of %u and %u", me, myPoint->ttaagg, herPoint->ttaagg); *enrP = AIR_NAN; return 1; } TEN_T_INV(inv, _tmpPoint.ten, det); } else { TEN_T_SCALE_ADD2(inv, 0.5, myPoint->inv, 0.5, herPoint->inv); } TEN_TV_MUL(XX, inv, YY); ELL_3V_NORM(nXX, XX, rr); ensp->energy->eval(enrP, &mag, rr*iscl, ensp->parm); if (mag) { mag *= iscl; TEN_TV_MUL(WW, inv, nXX); ELL_3V_SCALE(frc, mag, WW); } else { ELL_3V_SET(frc, 0, 0, 0); } return 0; } #define EPS_PER_MAX_DIST 200 #define SEEK_MAX_ITER 30 int pushBinProcess(pushTask *task, unsigned int myBinIdx) { static const char me[]="pushBinProcess"; pushBin *myBin, *herBin, **neighbor; unsigned int myPointIdx, herPointIdx; pushPoint *myPoint, *herPoint; double enr, frc[3], delta[3], deltaLen, deltaNorm[3], warp[3], limit, maxDiffLenSqrd, iscl, diff[3], diffLenSqrd; if (task->pctx->verbose > 2) { fprintf(stderr, "%s(%u): doing bin %u\n", me, task->threadIdx, myBinIdx); } maxDiffLenSqrd = (task->pctx->maxDist)*(task->pctx->maxDist); myBin = task->pctx->bin + myBinIdx; iscl = 1.0/(2*task->pctx->scale); for (myPointIdx=0; myPointIdxpointNum; myPointIdx++) { myPoint = myBin->point[myPointIdx]; myPoint->enr = 0; ELL_3V_SET(myPoint->frc, 0, 0, 0); if (1.0 <= task->pctx->neighborTrueProb || airDrandMT_r(task->rng) <= task->pctx->neighborTrueProb || !myPoint->neighArr->len) { neighbor = myBin->neighbor; if (1.0 > task->pctx->neighborTrueProb) { airArrayLenSet(myPoint->neighArr, 0); } while ((herBin = *neighbor)) { for (herPointIdx=0; herPointIdxpointNum; herPointIdx++) { herPoint = herBin->point[herPointIdx]; if (myPoint == herPoint) { /* can't interact with myself */ continue; } ELL_3V_SUB(diff, herPoint->pos, myPoint->pos); diffLenSqrd = ELL_3V_DOT(diff, diff); if (diffLenSqrd > maxDiffLenSqrd) { /* too far away to interact */ continue; } if (_pushPairwiseEnergy(task, &enr, frc, task->pctx->ensp, myPoint, herPoint, diff, iscl)) { biffAddf(PUSH, "%s: between points %u and %u, A", me, myPoint->ttaagg, herPoint->ttaagg); return 1; } myPoint->enr += enr/2; if (ELL_3V_DOT(frc, frc)) { ELL_3V_INCR(myPoint->frc, frc); if (1.0 > task->pctx->neighborTrueProb) { unsigned int idx; idx = airArrayLenIncr(myPoint->neighArr, 1); myPoint->neigh[idx] = herPoint; } } if (!ELL_3V_EXISTS(myPoint->frc)) { biffAddf(PUSH, "%s: bad myPoint->frc (%g,%g,%g) @ bin %p end", me, myPoint->frc[0], myPoint->frc[1], myPoint->frc[2], AIR_VOIDP(herBin)); return 1; } } neighbor++; } } else { /* we are doing neighborhood list optimization, and this is an iteration where we use the list. So the body of this loop has to be the same as the meat of the above loop */ unsigned int neighIdx; for (neighIdx=0; neighIdxneighArr->len; neighIdx++) { herPoint = myPoint->neigh[neighIdx]; ELL_3V_SUB(diff, herPoint->pos, myPoint->pos); if (_pushPairwiseEnergy(task, &enr, frc, task->pctx->ensp, myPoint, herPoint, diff, iscl)) { biffAddf(PUSH, "%s: between points %u and %u, B", me, myPoint->ttaagg, herPoint->ttaagg); return 1; } myPoint->enr += enr/2; ELL_3V_INCR(myPoint->frc, frc); } } if (!ELL_3V_EXISTS(myPoint->frc)) { biffAddf(PUSH, "%s: post-nei myPoint->frc (%g,%g,%g) doesn't exist", me, myPoint->frc[0], myPoint->frc[1], myPoint->frc[2]); return 1; } /* each point sees containment forces */ ELL_3V_SCALE(frc, task->pctx->cntScl, myPoint->cnt); ELL_3V_INCR(myPoint->frc, frc); myPoint->enr += task->pctx->cntScl*(1 - myPoint->ten[0]); /* each point also maybe experiences gravity */ if (tenGageUnknown != task->pctx->gravItem) { ELL_3V_SCALE(frc, -task->pctx->gravScl, myPoint->gravGrad); myPoint->enr += task->pctx->gravScl*(myPoint->grav - task->pctx->gravZero); ELL_3V_INCR(myPoint->frc, frc); } if (!ELL_3V_EXISTS(myPoint->frc)) { biffAddf(PUSH, "%s: post-grav myPoint->frc (%g,%g,%g) doesn't exist", me, myPoint->frc[0], myPoint->frc[1], myPoint->frc[2]); return 1; } /* each point in this thing also maybe experiences wall forces */ if (task->pctx->wall) { /* there's an effort here to get the forces and energies, which are actually computed in index space, to be correctly scaled into world space, but no promises that its right ... */ double enrIdx[4]={0,0,0,0}, enrWorld[4]; unsigned int ci; double posWorld[4], posIdx[4], len, frcIdx[4], frcWorld[4]; ELL_3V_COPY(posWorld, myPoint->pos); posWorld[3] = 1.0; ELL_4MV_MUL(posIdx, task->pctx->gctx->shape->WtoI, posWorld); ELL_4V_HOMOG(posIdx, posIdx); for (ci=0; ci<3; ci++) { if (1 == task->pctx->gctx->shape->size[ci]) { frcIdx[ci] = 0; } else { len = posIdx[ci] - -0.5; if (len < 0) { len *= -1; frcIdx[ci] = task->pctx->wall*len; enrIdx[ci] = task->pctx->wall*len*len/2; } else { len = posIdx[ci] - (task->pctx->gctx->shape->size[ci] - 0.5); if (len > 0) { frcIdx[ci] = -task->pctx->wall*len; enrIdx[ci] = task->pctx->wall*len*len/2; } else { frcIdx[ci] = 0; enrIdx[ci] = 0; } } } } frcIdx[3] = 0.0; enrIdx[3] = 0.0; ELL_4MV_MUL(frcWorld, task->pctx->gctx->shape->ItoW, frcIdx); ELL_4MV_MUL(enrWorld, task->pctx->gctx->shape->ItoW, enrIdx); ELL_3V_INCR(myPoint->frc, frcWorld); myPoint->enr += ELL_3V_LEN(enrWorld); } /* wall */ if (!ELL_3V_EXISTS(myPoint->frc)) { biffAddf(PUSH, "%s: post-wall myPoint->frc (%g,%g,%g) doesn't exist", me, myPoint->frc[0], myPoint->frc[1], myPoint->frc[2]); return 1; } task->energySum += myPoint->enr; /* -------------------------------------------- */ /* force calculation done, now update positions */ /* -------------------------------------------- */ ELL_3V_SCALE(delta, task->pctx->step, myPoint->frc); ELL_3V_NORM(deltaNorm, delta, deltaLen); if (0 == deltaLen) { /* an unforced point, but this isn't an error */ return 0; } if (!(AIR_EXISTS(deltaLen) && ELL_3V_EXISTS(deltaNorm))) { biffAddf(PUSH, "%s: deltaLen %g or deltaNorm (%g,%g,%g) doesn't exist", me, deltaLen, deltaNorm[0], deltaNorm[1], deltaNorm[2]); return 1; } if (deltaLen) { double newDelta; TEN_TV_MUL(warp, myPoint->inv, delta); /* limit is some fraction of glyph radius along direction of delta */ limit = (task->pctx->deltaLimit *task->pctx->scale*deltaLen/(FLT_MIN + ELL_3V_LEN(warp))); newDelta = limit*deltaLen/(limit + deltaLen); /* by definition newDelta <= deltaLen */ task->deltaFracSum += newDelta/deltaLen; ELL_3V_SCALE_INCR(myPoint->pos, newDelta, deltaNorm); if (!ELL_3V_EXISTS(myPoint->pos)) { biffAddf(PUSH, "%s: myPoint->pos %g*(%g,%g,%g) --> (%g,%g,%g) " "doesn't exist", me, newDelta, deltaNorm[0], deltaNorm[1], deltaNorm[2], myPoint->pos[0], myPoint->pos[1], myPoint->pos[2]); return 1; } } if (2 == task->pctx->dimIn) { double posIdx[4], posWorld[4], posOrig[4]; ELL_3V_COPY(posOrig, myPoint->pos); posOrig[3] = 1.0; ELL_4MV_MUL(posIdx, task->pctx->gctx->shape->WtoI, posOrig); ELL_4V_HOMOG(posIdx, posIdx); posIdx[task->pctx->sliceAxis] = 0.0; ELL_4MV_MUL(posWorld, task->pctx->gctx->shape->ItoW, posIdx); ELL_34V_HOMOG(myPoint->pos, posWorld); if (!ELL_3V_EXISTS(myPoint->pos)) { biffAddf(PUSH, "%s: myPoint->pos (%g,%g,%g) -> (%g,%g,%g) " "doesn't exist", me, posOrig[0], posOrig[1], posOrig[2], myPoint->pos[0], myPoint->pos[1], myPoint->pos[2]); return 1; } } if (1.0 <= task->pctx->probeProb || airDrandMT_r(task->rng) <= task->pctx->probeProb) { if (_pushProbe(task, myPoint)) { biffAddf(PUSH, "%s: probing at new field pos", me); return 1; } } /* the point lived, count it */ task->pointNum += 1; } /* for myPointIdx */ return 0; } teem-1.11.0~svn6057/src/push/corePush.c0000664000175000017500000003351312165631065017321 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "push.h" #include "privatePush.h" /* ** this is the core of the worker threads: as long as there are bins ** left to process, get the next one, and process it */ int _pushProcess(pushTask *task) { static const char me[]="_pushProcess"; unsigned int binIdx; while (task->pctx->binIdx < task->pctx->binNum) { /* get the index of the next bin to process */ if (task->pctx->threadNum > 1) { airThreadMutexLock(task->pctx->binMutex); } do { binIdx = task->pctx->binIdx; if (task->pctx->binIdx < task->pctx->binNum) { task->pctx->binIdx++; } } while (binIdx < task->pctx->binNum && 0 == task->pctx->bin[binIdx].pointNum); if (task->pctx->threadNum > 1) { airThreadMutexUnlock(task->pctx->binMutex); } if (binIdx == task->pctx->binNum) { /* no more bins to process! */ break; } if (pushBinProcess(task, binIdx)) { biffAddf(PUSH, "%s(%u): had trouble on bin %u", me, task->threadIdx, binIdx); return 1; } } return 0; } /* the main loop for each worker thread */ void * _pushWorker(void *_task) { static const char me[]="_pushWorker"; pushTask *task; task = (pushTask *)_task; while (1) { if (task->pctx->verbose > 1) { fprintf(stderr, "%s(%u): waiting on barrier A\n", me, task->threadIdx); } /* pushFinish sets finished prior to the barriers */ airThreadBarrierWait(task->pctx->iterBarrierA); if (task->pctx->finished) { if (task->pctx->verbose > 1) { fprintf(stderr, "%s(%u): done!\n", me, task->threadIdx); } break; } /* else there's work to do ... */ if (task->pctx->verbose > 1) { fprintf(stderr, "%s(%u): starting to process\n", me, task->threadIdx); } if (_pushProcess(task)) { /* HEY clearly not threadsafe ... */ biffAddf(PUSH, "%s: thread %u trouble", me, task->threadIdx); task->pctx->finished = AIR_TRUE; } if (task->pctx->verbose > 1) { fprintf(stderr, "%s(%u): waiting on barrier B\n", me, task->threadIdx); } airThreadBarrierWait(task->pctx->iterBarrierB); } return _task; } int _pushContextCheck(pushContext *pctx) { static const char me[]="_pushContextCheck"; unsigned int numSingle; if (!pctx) { biffAddf(PUSH, "%s: got NULL pointer", me); return 1; } if (!( pctx->pointNum >= 1 )) { biffAddf(PUSH, "%s: pctx->pointNum (%d) not >= 1\n", me, pctx->pointNum); return 1; } if (!( AIR_IN_CL(1, pctx->threadNum, PUSH_THREAD_MAXNUM) )) { biffAddf(PUSH, "%s: pctx->threadNum (%d) outside valid range [1,%d]", me, pctx->threadNum, PUSH_THREAD_MAXNUM); return 1; } if (nrrdCheck(pctx->nin)) { biffMovef(PUSH, NRRD, "%s: got a broken input nrrd", me); return 1; } if (!( (4 == pctx->nin->dim && 7 == pctx->nin->axis[0].size) )) { biffAddf(PUSH, "%s: input doesn't look like 3D masked tensor volume", me); return 1; } numSingle = 0; numSingle += (1 == pctx->nin->axis[1].size); numSingle += (1 == pctx->nin->axis[2].size); numSingle += (1 == pctx->nin->axis[3].size); if (numSingle > 1) { biffAddf(PUSH, "%s: can have a single sample along at most one axis", me); return 1; } if (pctx->npos) { if (nrrdCheck(pctx->npos)) { biffMovef(PUSH, NRRD, "%s: got a broken position nrrd", me); return 1; } if (!( 2 == pctx->npos->dim && 3 == pctx->npos->axis[0].size )) { biffAddf(PUSH, "%s: position nrrd not 2-D 3-by-N", me); return 1; } } if (tenGageUnknown != pctx->gravItem) { if (airEnumValCheck(tenGage, pctx->gravItem)) { biffAddf(PUSH, "%s: gravity item %u invalid", me, pctx->gravItem); return 1; } if (1 != tenGageKind->table[pctx->gravItem].answerLength) { biffAddf(PUSH, "%s: answer length of gravity item %s is %u, not 1", me, airEnumStr(tenGage, pctx->gravItem), tenGageKind->table[pctx->gravItem].answerLength); return 1; } if (airEnumValCheck(tenGage, pctx->gravGradItem)) { biffAddf(PUSH, "%s: gravity gradient item %u invalid", me, pctx->gravGradItem); return 1; } if (3 != tenGageKind->table[pctx->gravGradItem].answerLength) { biffAddf(PUSH, "%s: answer length of gravity grad item %s is %u, not 3", me, airEnumStr(tenGage, pctx->gravGradItem), tenGageKind->table[pctx->gravGradItem].answerLength); return 1; } if (!AIR_EXISTS(pctx->gravScl)) { biffAddf(PUSH, "%s: gravity scaling doesn't exist", me); return 1; } if (!AIR_EXISTS(pctx->gravZero)) { biffAddf(PUSH, "%s: gravity zero doesn't exist", me); return 1; } } return 0; } int pushStart(pushContext *pctx) { static const char me[]="pushStart"; unsigned int tidx; if (_pushContextCheck(pctx)) { biffAddf(PUSH, "%s: trouble", me); return 1; } airSrandMT(pctx->seedRNG); /* the ordering of steps below is important: gage context has to be set up before its copied by task setup */ pctx->step = pctx->stepInitial; if (_pushTensorFieldSetup(pctx) || _pushGageSetup(pctx) || _pushTaskSetup(pctx) || _pushBinSetup(pctx) || _pushPointSetup(pctx)) { biffAddf(PUSH, "%s: trouble setting up context", me); return 1; } fprintf(stderr, "!%s: setup done-ish\n", me); if (pctx->threadNum > 1) { pctx->binMutex = airThreadMutexNew(); pctx->iterBarrierA = airThreadBarrierNew(pctx->threadNum); pctx->iterBarrierB = airThreadBarrierNew(pctx->threadNum); /* start threads 1 and up running; they'll all hit iterBarrierA */ for (tidx=1; tidxthreadNum; tidx++) { if (pctx->verbose > 1) { fprintf(stderr, "%s: spawning thread %d\n", me, tidx); } airThreadStart(pctx->task[tidx]->thread, _pushWorker, (void *)(pctx->task[tidx])); } } else { pctx->binMutex = NULL; pctx->iterBarrierA = NULL; pctx->iterBarrierB = NULL; } pctx->iter = 0; return 0; } /* ******** pushIterate ** ** (documentation) ** ** NB: this implements the body of thread 0, the master thread */ int pushIterate(pushContext *pctx) { static const char me[]="pushIterate"; unsigned int ti, pointNum; double time0, time1; int myError; if (!pctx) { biffAddf(PUSH, "%s: got NULL pointer", me); return 1; } if (pctx->verbose) { fprintf(stderr, "%s: starting iterations\n", me); } time0 = airTime(); /* the _pushWorker checks finished after iterBarrierA */ pctx->finished = AIR_FALSE; pctx->binIdx=0; for (ti=0; tithreadNum; ti++) { pctx->task[ti]->pointNum = 0; pctx->task[ti]->energySum = 0; pctx->task[ti]->deltaFracSum = 0; } if (pctx->verbose) { fprintf(stderr, "%s: starting iter %d w/ %u threads\n", me, pctx->iter, pctx->threadNum); } if (pctx->threadNum > 1) { airThreadBarrierWait(pctx->iterBarrierA); } myError = AIR_FALSE; if (_pushProcess(pctx->task[0])) { biffAddf(PUSH, "%s: master thread trouble w/ iter %u", me, pctx->iter); pctx->finished = AIR_TRUE; myError = AIR_TRUE; } if (pctx->threadNum > 1) { airThreadBarrierWait(pctx->iterBarrierB); } if (pctx->finished) { if (!myError) { /* we didn't set finished- one of the workers must have */ biffAddf(PUSH, "%s: worker error on iter %u", me, pctx->iter); } return 1; } pctx->energySum = 0; pctx->deltaFrac = 0; pointNum = 0; for (ti=0; tithreadNum; ti++) { pctx->energySum += pctx->task[ti]->energySum; pctx->deltaFrac += pctx->task[ti]->deltaFracSum; pointNum += pctx->task[ti]->pointNum; } pctx->deltaFrac /= pointNum; if (pushRebin(pctx)) { biffAddf(PUSH, "%s: problem with new point locations", me); return 1; } time1 = airTime(); pctx->timeIteration = time1 - time0; pctx->timeRun += time1 - time0; pctx->iter += 1; return 0; } int pushRun(pushContext *pctx) { static const char me[]="pushRun"; char poutS[AIR_STRLEN_MED], toutS[AIR_STRLEN_MED]; Nrrd *npos, *nten; double time0, time1, enrLast, enrNew=AIR_NAN, enrImprov=AIR_NAN, enrImprovAvg=AIR_NAN; if (pushIterate(pctx)) { biffAddf(PUSH, "%s: trouble on starting iteration", me); return 1; } fprintf(stderr, "!%s: starting pctx->energySum = %g\n", me, pctx->energySum); time0 = airTime(); pctx->iter = 0; do { enrLast = pctx->energySum; if (pushIterate(pctx)) { biffAddf(PUSH, "%s: trouble on iter %d", me, pctx->iter); return 1; } if (pctx->snap && !(pctx->iter % pctx->snap)) { nten = nrrdNew(); npos = nrrdNew(); sprintf(poutS, "snap.%06d.pos.nrrd", pctx->iter); sprintf(toutS, "snap.%06d.ten.nrrd", pctx->iter); if (pushOutputGet(npos, nten, NULL, pctx)) { biffAddf(PUSH, "%s: couldn't get snapshot for iter %d", me, pctx->iter); return 1; } if (nrrdSave(poutS, npos, NULL) || nrrdSave(toutS, nten, NULL)) { biffMovef(PUSH, NRRD, "%s: couldn't save snapshot for iter %d", me, pctx->iter); return 1; } nten = nrrdNuke(nten); npos = nrrdNuke(npos); } enrNew = pctx->energySum; enrImprov = 2*(enrLast - enrNew)/(enrLast + enrNew); fprintf(stderr, "!%s: %u, e=%g, de=%g,%g, df=%g\n", me, pctx->iter, enrNew, enrImprov, enrImprovAvg, pctx->deltaFrac); if (enrImprov < 0 || pctx->deltaFrac < pctx->deltaFracMin) { /* either energy went up instead of down, or particles were hitting their speed limit too much */ double tmp; tmp = pctx->step; if (enrImprov < 0) { pctx->step *= pctx->energyStepFrac; fprintf(stderr, "%s: ***** iter %u e improv = %g; step = %g --> %g\n", me, pctx->iter, enrImprov, tmp, pctx->step); } else { pctx->step *= pctx->deltaFracStepFrac; fprintf(stderr, "%s: ##### iter %u deltaf = %g; step = %g --> %g\n", me, pctx->iter, pctx->deltaFrac, tmp, pctx->step); } /* this forces another iteration */ enrImprovAvg = AIR_NAN; } else { /* there was some improvement; energy went down */ if (!AIR_EXISTS(enrImprovAvg)) { /* either enrImprovAvg has initial NaN setting, or was set to NaN because we had to decrease step size; either way we now re-initialize it to a large-ish value, to delay convergence */ enrImprovAvg = 3*enrImprov; } else { /* we had improvement this iteration and last, do weighted average of the two, so that we are measuring the trend, rather than being sensitive to two iterations that just happen to have the same energy. Thus, when enrImprovAvg gets near user-defined threshold, we really must have converged */ enrImprovAvg = (enrImprovAvg + enrImprov)/2; } } } while ( ((!AIR_EXISTS(enrImprovAvg) || enrImprovAvg > pctx->energyImprovMin) && (0 == pctx->maxIter || pctx->iter < pctx->maxIter)) ); fprintf(stderr, "%s: done after %u iters; enr = %g, enrImprov = %g,%g\n", me, pctx->iter, enrNew, enrImprov, enrImprovAvg); time1 = airTime(); pctx->timeRun = time1 - time0; return 0; } /* ** this is called *after* pushOutputGet ** ** should nix everything created by the many _push*Setup() functions */ int pushFinish(pushContext *pctx) { static const char me[]="pushFinish"; unsigned int ii, tidx; if (!pctx) { biffAddf(PUSH, "%s: got NULL pointer", me); return 1; } pctx->finished = AIR_TRUE; if (pctx->threadNum > 1) { if (pctx->verbose > 1) { fprintf(stderr, "%s: finishing workers\n", me); } airThreadBarrierWait(pctx->iterBarrierA); } /* worker threads now pass barrierA and see that finished is AIR_TRUE, and then bail, so now we collect them */ for (tidx=pctx->threadNum; tidx>0; tidx--) { if (tidx-1) { airThreadJoin(pctx->task[tidx-1]->thread, &(pctx->task[tidx-1]->returnPtr)); } pctx->task[tidx-1]->thread = airThreadNix(pctx->task[tidx-1]->thread); pctx->task[tidx-1] = _pushTaskNix(pctx->task[tidx-1]); } pctx->task = (pushTask **)airFree(pctx->task); pctx->nten = nrrdNuke(pctx->nten); pctx->ninv = nrrdNuke(pctx->ninv); pctx->nmask = nrrdNuke(pctx->nmask); pctx->gctx = gageContextNix(pctx->gctx); for (ii=0; iibinNum; ii++) { pushBinDone(pctx->bin + ii); } pctx->bin = (pushBin *)airFree(pctx->bin); ELL_3V_SET(pctx->binsEdge, 0, 0, 0); pctx->binNum = 0; if (pctx->threadNum > 1) { pctx->binMutex = airThreadMutexNix(pctx->binMutex); pctx->iterBarrierA = airThreadBarrierNix(pctx->iterBarrierA); pctx->iterBarrierB = airThreadBarrierNix(pctx->iterBarrierB); } return 0; } teem-1.11.0~svn6057/src/push/setup.c0000664000175000017500000004037712165631065016677 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "push.h" #include "privatePush.h" /* ** _pushTensorFieldSetup sets: **** pctx->dimIn **** pctx->nten **** pctx->ninv **** pctx->nmask ** and checks mask range */ int _pushTensorFieldSetup(pushContext *pctx) { static const char me[]="_pushTensorFieldSetup"; NrrdRange *nrange; airArray *mop; Nrrd *ntmp; int E; float *_ten, *_inv; double ten[7], inv[7]; unsigned int numSingle; size_t ii, NN; mop = airMopNew(); ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); pctx->nten = nrrdNew(); pctx->ninv = nrrdNew(); pctx->nmask = nrrdNew(); numSingle = 0; numSingle += (1 == pctx->nin->axis[1].size); numSingle += (1 == pctx->nin->axis[2].size); numSingle += (1 == pctx->nin->axis[3].size); if (1 == numSingle) { pctx->dimIn = 2; pctx->sliceAxis = (1 == pctx->nin->axis[1].size ? 0 : (1 == pctx->nin->axis[2].size ? 1 : 2)); fprintf(stderr, "!%s: got 2-D input with sliceAxis %u\n", me, pctx->sliceAxis); } else { pctx->dimIn = 3; pctx->sliceAxis = 52; /* HEY: what the heck is 52 ? */ fprintf(stderr, "!%s: got 3-D input\n", me); } E = 0; if (!E) E |= nrrdConvert(pctx->nten, pctx->nin, nrrdTypeFloat); if (!E) E |= nrrdCopy(pctx->ninv, pctx->nten); if (E) { biffMovef(PUSH, NRRD, "%s: trouble creating 3D tensor input", me); airMopError(mop); return 1; } _ten = (float*)pctx->nten->data; _inv = (float*)pctx->ninv->data; NN = nrrdElementNumber(pctx->nten)/7; for (ii=0; iinmask, pctx->nten, 0, 0); if (E) { biffMovef(PUSH, NRRD, "%s: trouble creating mask", me); airMopError(mop); return 1; } nrange = nrrdRangeNewSet(pctx->nmask, nrrdBlind8BitRangeFalse); airMopAdd(mop, nrange, (airMopper)nrrdRangeNix, airMopAlways); if (AIR_ABS(1.0 - nrange->max) > 0.005) { biffAddf(PUSH, "%s: tensor mask max %g not close 1.0", me, nrange->max); airMopError(mop); return 1; } pctx->nten->axis[1].center = nrrdCenterCell; pctx->nten->axis[2].center = nrrdCenterCell; pctx->nten->axis[3].center = nrrdCenterCell; pctx->ninv->axis[1].center = nrrdCenterCell; pctx->ninv->axis[2].center = nrrdCenterCell; pctx->ninv->axis[3].center = nrrdCenterCell; pctx->nmask->axis[0].center = nrrdCenterCell; pctx->nmask->axis[1].center = nrrdCenterCell; pctx->nmask->axis[2].center = nrrdCenterCell; airMopOkay(mop); return 0; } /* ** _pushGageSetup sets: **** pctx->gctx */ int _pushGageSetup(pushContext *pctx) { static const char me[]="_pushGageSetup"; gagePerVolume *mpvl; int E; pctx->gctx = gageContextNew(); /* gageParmSet(pctx->gctx, gageParmRequireAllSpacings, AIR_TRUE); */ E = AIR_FALSE; /* set up tensor probing */ if (!E) E |= !(pctx->tpvl = gagePerVolumeNew(pctx->gctx, pctx->nten, tenGageKind)); if (!E) E |= gagePerVolumeAttach(pctx->gctx, pctx->tpvl); if (!E) E |= gageKernelSet(pctx->gctx, gageKernel00, pctx->ksp00->kernel, pctx->ksp00->parm); if (!E) E |= gageQueryItemOn(pctx->gctx, pctx->tpvl, tenGageTensor); if (tenGageUnknown != pctx->gravItem) { if (!E) E |= gageQueryItemOn(pctx->gctx, pctx->tpvl, pctx->gravItem); if (!E) E |= gageQueryItemOn(pctx->gctx, pctx->tpvl, pctx->gravGradItem); } /* set up tensor inverse probing */ if (!E) E |= !(pctx->ipvl = gagePerVolumeNew(pctx->gctx, pctx->ninv, tenGageKind)); if (!E) E |= gagePerVolumeAttach(pctx->gctx, pctx->ipvl); if (!E) E |= gageQueryItemOn(pctx->gctx, pctx->ipvl, tenGageTensor); /* set up mask gradient probing */ if (!E) E |= !(mpvl = gagePerVolumeNew(pctx->gctx, pctx->nmask, gageKindScl)); if (!E) E |= gagePerVolumeAttach(pctx->gctx, mpvl); if (!E) E |= gageQueryItemOn(pctx->gctx, mpvl, gageSclGradVec); if (!E) E |= gageKernelSet(pctx->gctx, gageKernel11, pctx->ksp11->kernel, pctx->ksp11->parm); /* (maybe) turn on seed thresholding */ if (tenGageUnknown != pctx->seedThreshItem) { if (!E) E |= gageQueryItemOn(pctx->gctx, pctx->tpvl, pctx->seedThreshItem); } /* HEY: seed threshold item should possibly be turned off later! */ if (!E) E |= gageUpdate(pctx->gctx); if (E) { biffMovef(PUSH, GAGE, "%s: trouble setting up gage", me); return 1; } return 0; } pushTask * _pushTaskNew(pushContext *pctx, int threadIdx) { static const char me[]="_pushTaskNew"; pushTask *task; task = (pushTask *)calloc(1, sizeof(pushTask)); if (task) { task->pctx = pctx; if (!(task->gctx = gageContextCopy(pctx->gctx))) { biffMovef(PUSH, GAGE, "%s: trouble copying main gageContext", me); return NULL; } /* ** HEY: its a limitation in gage that we have to know a priori ** the ordering of per-volumes in the context ... */ task->tenAns = gageAnswerPointer(task->gctx, task->gctx->pvl[0], tenGageTensor); task->invAns = gageAnswerPointer(task->gctx, task->gctx->pvl[1], tenGageTensor); task->cntAns = gageAnswerPointer(task->gctx, task->gctx->pvl[2], gageSclGradVec); if (tenGageUnknown != task->pctx->gravItem) { task->gravAns = gageAnswerPointer(task->gctx, task->gctx->pvl[0], task->pctx->gravItem); task->gravGradAns = gageAnswerPointer(task->gctx, task->gctx->pvl[0], task->pctx->gravGradItem); } else { task->gravAns = NULL; task->gravGradAns = NULL; } if (tenGageUnknown != task->pctx->seedThreshItem) { task->seedThreshAns = gageAnswerPointer(task->gctx, task->gctx->pvl[0], task->pctx->seedThreshItem); } else { task->seedThreshAns = NULL; } if (threadIdx) { task->thread = airThreadNew(); } task->rng = airRandMTStateNew(pctx->seedRNG + threadIdx); task->threadIdx = threadIdx; task->pointNum = 0; task->energySum = 0; task->deltaFracSum = 0; task->returnPtr = NULL; } return task; } pushTask * _pushTaskNix(pushTask *task) { if (task) { task->gctx = gageContextNix(task->gctx); if (task->threadIdx) { task->thread = airThreadNix(task->thread); } task->rng = airRandMTStateNix(task->rng); airFree(task); } return NULL; } /* ** _pushTaskSetup sets: **** pctx->task **** pctx->task[] */ int _pushTaskSetup(pushContext *pctx) { static const char me[]="_pushTaskSetup"; unsigned int tidx; pctx->task = (pushTask **)calloc(pctx->threadNum, sizeof(pushTask *)); if (!(pctx->task)) { biffAddf(PUSH, "%s: couldn't allocate array of tasks", me); return 1; } for (tidx=0; tidxthreadNum; tidx++) { if (pctx->verbose) { fprintf(stderr, "%s: creating task %u/%u\n", me, tidx, pctx->threadNum); } pctx->task[tidx] = _pushTaskNew(pctx, tidx); if (!(pctx->task[tidx])) { biffAddf(PUSH, "%s: couldn't allocate task %d", me, tidx); return 1; } } return 0; } /* ** _pushBinSetup sets: **** pctx->maxDist, pctx->minEval, pctx->maxEval, pctx->maxDet **** pctx->binsEdge[], pctx->binNum **** pctx->bin **** pctx->bin[] */ int _pushBinSetup(pushContext *pctx) { static const char me[]="_pushBinSetup"; float eval[3], *tdata; unsigned int ii, nn, count; double col[3][4], volEdge[3]; /* ------------------------ find maxEval, maxDet, and set up binning */ nn = nrrdElementNumber(pctx->nten)/7; pctx->maxEval = 0; pctx->maxDet = 0; pctx->meanEval = 0; count = 0; tdata = (float*)pctx->nten->data; for (ii=0; ii 0.5) { /* HEY: this limitation may be a bad idea */ count++; pctx->meanEval += eval[0]; pctx->maxEval = AIR_MAX(pctx->maxEval, eval[0]); if (2 == pctx->dimIn) { double det2d; /* HEY! HEY! this assumes not only that the measurement frame has been taken care of, but that the volume is axis-aligned */ det2d = (0 == pctx->sliceAxis ? TEN_T_DET_YZ(tdata) : (1 == pctx->sliceAxis ? TEN_T_DET_XZ(tdata) : TEN_T_DET_XY(tdata))); pctx->maxDet = AIR_MAX(pctx->maxDet, det2d); } else { pctx->maxDet = AIR_MAX(pctx->maxDet, eval[0]*eval[1]*eval[2]); } } tdata += 7; } fprintf(stderr, "!%s: dimIn = %u(%u) --> maxDet = %g\n", me, pctx->dimIn, pctx->sliceAxis, pctx->maxDet); pctx->meanEval /= count; pctx->maxDist = (2*pctx->scale*pctx->maxEval *pctx->ensp->energy->support(pctx->ensp->parm)); if (pctx->binSingle) { pctx->binsEdge[0] = 1; pctx->binsEdge[1] = 1; pctx->binsEdge[2] = 1; pctx->binNum = 1; } else { ELL_4MV_COL0_GET(col[0], pctx->gctx->shape->ItoW); col[0][3] = 0.0; ELL_4MV_COL1_GET(col[1], pctx->gctx->shape->ItoW); col[1][3] = 0.0; ELL_4MV_COL2_GET(col[2], pctx->gctx->shape->ItoW); col[2][3] = 0.0; volEdge[0] = ELL_3V_LEN(col[0])*pctx->gctx->shape->size[0]; volEdge[1] = ELL_3V_LEN(col[1])*pctx->gctx->shape->size[1]; volEdge[2] = ELL_3V_LEN(col[2])*pctx->gctx->shape->size[2]; fprintf(stderr, "!%s: volEdge = %g %g %g\n", me, volEdge[0], volEdge[1], volEdge[2]); pctx->binsEdge[0] = AIR_CAST(unsigned int, floor(volEdge[0]/pctx->maxDist)); pctx->binsEdge[0] = pctx->binsEdge[0] ? pctx->binsEdge[0] : 1; pctx->binsEdge[1] = AIR_CAST(unsigned int, floor(volEdge[1]/pctx->maxDist)); pctx->binsEdge[1] = pctx->binsEdge[1] ? pctx->binsEdge[1] : 1; pctx->binsEdge[2] = AIR_CAST(unsigned int, floor(volEdge[2]/pctx->maxDist)); pctx->binsEdge[2] = pctx->binsEdge[2] ? pctx->binsEdge[2] : 1; if (2 == pctx->dimIn) { pctx->binsEdge[pctx->sliceAxis] = 1; } fprintf(stderr, "!%s: maxEval=%g -> maxDist=%g -> binsEdge=(%u,%u,%u)\n", me, pctx->maxEval, pctx->maxDist, pctx->binsEdge[0], pctx->binsEdge[1], pctx->binsEdge[2]); pctx->binNum = pctx->binsEdge[0]*pctx->binsEdge[1]*pctx->binsEdge[2]; } pctx->bin = (pushBin *)calloc(pctx->binNum, sizeof(pushBin)); if (!( pctx->bin )) { biffAddf(PUSH, "%s: trouble allocating bin arrays", me); return 1; } for (ii=0; iibinNum; ii++) { pushBinInit(pctx->bin + ii, pctx->binIncr); } pushBinAllNeighborSet(pctx); return 0; } /* ** _pushPointSetup sets: **** pctx->pointNum (in case pctx->npos) ** ** This is only called by the master thread ** ** this should set stuff to be like after an update stage and ** just before the rebinning */ int _pushPointSetup(pushContext *pctx) { static const char me[]="_pushPointSetup"; double (*lup)(const void *v, size_t I), maxDet; unsigned int pointIdx; pushPoint *point; /* double posIdxHack[2][4] = { {49.99999, 50, 0, 1}, {50, 50, 0, 1}}; */ pctx->pointNum = (pctx->npos ? pctx->npos->axis[1].size : pctx->pointNum); lup = pctx->npos ? nrrdDLookup[pctx->npos->type] : NULL; fprintf(stderr, "!%s: initilizing/seeding ... \n", me); /* HEY: we end up keeping a local copy of maxDet because convolution can produce a tensor with higher determinant than that of any original sample. However, if this is going into effect, detReject should probably *not* be enabled... */ maxDet = pctx->maxDet; for (pointIdx=0; pointIdxpointNum; pointIdx++) { double detProbe; /* fprintf(stderr, "!%s: pointIdx = %u/%u\n", me, pointIdx, pctx->pointNum); */ point = pushPointNew(pctx); if (pctx->npos) { ELL_3V_SET(point->pos, lup(pctx->npos->data, 0 + 3*pointIdx), lup(pctx->npos->data, 1 + 3*pointIdx), lup(pctx->npos->data, 2 + 3*pointIdx)); if (_pushProbe(pctx->task[0], point)) { biffAddf(PUSH, "%s: probing pointIdx %u of npos", me, pointIdx); return 1; } } else { /* double posWorld[4]; ELL_4MV_MUL(posWorld, pctx->gctx->shape->ItoW, posIdxHack[pointIdx]); ELL_34V_HOMOG(point->pos, posWorld); _pushProbe(pctx->task[0], point); */ do { double posIdx[4], posWorld[4]; posIdx[0] = AIR_AFFINE(0.0, airDrandMT(), 1.0, -0.5, pctx->gctx->shape->size[0]-0.5); posIdx[1] = AIR_AFFINE(0.0, airDrandMT(), 1.0, -0.5, pctx->gctx->shape->size[1]-0.5); posIdx[2] = AIR_AFFINE(0.0, airDrandMT(), 1.0, -0.5, pctx->gctx->shape->size[2]-0.5); posIdx[3] = 1.0; if (2 == pctx->dimIn) { posIdx[pctx->sliceAxis] = 0.0; } ELL_4MV_MUL(posWorld, pctx->gctx->shape->ItoW, posIdx); ELL_34V_HOMOG(point->pos, posWorld); /* fprintf(stderr, "%s: posIdx = %g %g %g --> posWorld = %g %g %g " "--> %g %g %g\n", me, posIdx[0], posIdx[1], posIdx[2], posWorld[0], posWorld[1], posWorld[2], point->pos[0], point->pos[1], point->pos[2]); */ if (_pushProbe(pctx->task[0], point)) { biffAddf(PUSH, "%s: probing pointIdx %u of world", me, pointIdx); return 1; } detProbe = TEN_T_DET(point->ten); if (2 == pctx->dimIn) { /* see above HEY! HEY! */ detProbe = (0 == pctx->sliceAxis ? TEN_T_DET_YZ(point->ten) : (1 == pctx->sliceAxis ? TEN_T_DET_XZ(point->ten) : TEN_T_DET_XY(point->ten))); } else { detProbe = TEN_T_DET(point->ten); } maxDet = AIR_MAX(maxDet, detProbe); /* assuming that we're not using some very blurring kernel, this will eventually succeed, because we previously checked the range of values in the mask */ /* HEY: can't ensure that this will eventually succeed with seedThresh enabled! */ /* fprintf(stderr, "!%s: ten[0] = %g\n", me, point->ten[0]); */ /* we OR together all the tests that would make us REJECT this last sample */ } while (point->ten[0] < 0.5 || (tenGageUnknown != pctx->seedThreshItem && ((pctx->seedThresh - point->seedThresh) *pctx->seedThreshSign > 0) ) || (pctx->detReject && (airDrandMT() < detProbe/maxDet)) ); } if (pushBinPointAdd(pctx, point)) { biffAddf(PUSH, "%s: trouble binning point %u", me, point->ttaagg); return 1; } } fprintf(stderr, "!%s: ... seeding DONE\n", me); return 0; } teem-1.11.0~svn6057/src/push/GNUmakefile0000664000175000017500000000361112165631065017433 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := push #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### tendCalc.o nixed $(L).NEED = ten gage ell nrrd biff air $(L).PUBLIC_HEADERS = push.h $(L).PRIVATE_HEADERS = privatePush.h $(L).OBJS = defaultsPush.o methodsPush.o binning.o \ forces.o corePush.o setup.o action.o $(L).TESTS = test/eparse test/pusher #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/push/sources.cmake0000664000175000017500000000042111113047450020031 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(PUSH_SOURCES action.c binning.c corePush.c defaultsPush.c forces.c methodsPush.c privatePush.h push.h setup.c ) ADD_TEEM_LIBRARY(push ${PUSH_SOURCES}) teem-1.11.0~svn6057/src/push/forces.c0000664000175000017500000002763312165631065017020 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "push.h" #include "privatePush.h" #define SPRING "spring" #define GAUSS "gauss" #define COULOMB "coulomb" #define COTAN "cotan" #define ZERO "zero" const char * _pushEnergyTypeStr[PUSH_ENERGY_TYPE_MAX+1] = { "(unknown_energy)", SPRING, GAUSS, COULOMB, COTAN, ZERO }; const char * _pushEnergyTypeDesc[PUSH_ENERGY_TYPE_MAX+1] = { "unknown_energy", "Hooke's law-based potential, with a tunable region of attraction", "Gaussian potential", "Coulomb electrostatic potential, with tunable cut-off", "Cotangent-based potential (from Meyer et al. SMI '05)", "no energy" }; const airEnum _pushEnergyType = { "energy", PUSH_ENERGY_TYPE_MAX, _pushEnergyTypeStr, NULL, _pushEnergyTypeDesc, NULL, NULL, AIR_FALSE }; const airEnum *const pushEnergyType = &_pushEnergyType; /* ---------------------------------------------------------------- ** ------------------------------ UNKNOWN ------------------------- ** ---------------------------------------------------------------- */ void _pushEnergyUnknownEval(double *enr, double *frc, double dist, const double *parm) { static const char me[]="_pushEnergyUnknownEval"; AIR_UNUSED(dist); AIR_UNUSED(parm); *enr = AIR_NAN; *frc = AIR_NAN; fprintf(stderr, "%s: ERROR- using unknown energy.\n", me); return; } double _pushEnergyUnknownSupport(const double *parm) { static const char me[]="_pushEnergyUnknownSupport"; AIR_UNUSED(parm); fprintf(stderr, "%s: ERROR- using unknown energy.\n", me); return AIR_NAN; } pushEnergy _pushEnergyUnknown = { "unknown", 0, _pushEnergyUnknownEval, _pushEnergyUnknownSupport }; const pushEnergy *const pushEnergyUnknown = &_pushEnergyUnknown; /* ---------------------------------------------------------------- ** ------------------------------ SPRING -------------------------- ** ---------------------------------------------------------------- ** 1 parms: ** parm[0]: width of pull region (beyond 1.0) ** ** learned: "1/2" is not 0.5 !!!!! */ void _pushEnergySpringEval(double *enr, double *frc, double dist, const double *parm) { /* static const char me[]="_pushEnergySpringEval"; */ double xx, pull; pull = parm[0]; xx = dist - 1.0; if (xx > pull) { *enr = 0; *frc = 0; } else if (xx > 0) { *enr = xx*xx*(xx*xx/(4*pull*pull) - 2*xx/(3*pull) + 1.0/2.0); *frc = xx*(xx*xx/(pull*pull) - 2*xx/pull + 1); } else { *enr = xx*xx/2; *frc = xx; } /* if (!AIR_EXISTS(ret)) { fprintf(stderr, "!%s: dist=%g, pull=%g, blah=%d --> ret=%g\n", me, dist, pull, blah, ret); } */ return; } double _pushEnergySpringSupport(const double *parm) { return 1.0 + parm[0]; } const pushEnergy _pushEnergySpring = { SPRING, 1, _pushEnergySpringEval, _pushEnergySpringSupport }; const pushEnergy *const pushEnergySpring = &_pushEnergySpring; /* ---------------------------------------------------------------- ** ------------------------------ GAUSS -------------------------- ** ---------------------------------------------------------------- ** 1 parms: ** (distance to inflection point of force function is always 1.0) ** parm[0]: cut-off (as a multiple of standard dev (which is 1.0)) */ /* HEY: copied from teem/src/nrrd/kernel.c */ #define _GAUSS(x, sig, cut) ( \ x >= sig*cut ? 0 \ : exp(-x*x/(2.0*sig*sig))/(sig*2.50662827463100050241)) #define _DGAUSS(x, sig, cut) ( \ x >= sig*cut ? 0 \ : -exp(-x*x/(2.0*sig*sig))*x/(sig*sig*sig*2.50662827463100050241)) void _pushEnergyGaussEval(double *enr, double *frc, double dist, const double *parm) { double cut; cut = parm[0]; *enr = _GAUSS(dist, 1.0, cut); *frc = _DGAUSS(dist, 1.0, cut); return; } double _pushEnergyGaussSupport(const double *parm) { return parm[0]; } const pushEnergy _pushEnergyGauss = { GAUSS, 1, _pushEnergyGaussEval, _pushEnergyGaussSupport }; const pushEnergy *const pushEnergyGauss = &_pushEnergyGauss; /* ---------------------------------------------------------------- ** ------------------------------ CHARGE -------------------------- ** ---------------------------------------------------------------- ** 1 parms: ** (scale: distance to "1.0" in graph of x^(-2)) ** parm[0]: cut-off (as multiple of "1.0") */ void _pushEnergyCoulombEval(double *enr, double *frc, double dist, const double *parm) { *enr = (dist > parm[0] ? 0 : 1.0/dist); *frc = (dist > parm[0] ? 0 : -1.0/(dist*dist)); return; } double _pushEnergyCoulombSupport(const double *parm) { return parm[0]; } const pushEnergy _pushEnergyCoulomb = { COULOMB, 1, _pushEnergyCoulombEval, _pushEnergyCoulombSupport }; const pushEnergy *const pushEnergyCoulomb = &_pushEnergyCoulomb; /* ---------------------------------------------------------------- ** ------------------------------ COTAN --------------------------- ** ---------------------------------------------------------------- ** 0 parms! */ void _pushEnergyCotanEval(double *enr, double *frc, double dist, const double *parm) { double pot, cc; AIR_UNUSED(parm); pot = AIR_PI/2.0; cc = 1.0/(FLT_MIN + tan(dist*pot)); *enr = dist > 1 ? 0 : cc + dist*pot - pot; *frc = dist > 1 ? 0 : -cc*cc*pot; return; } double _pushEnergyCotanSupport(const double *parm) { AIR_UNUSED(parm); return 1; } const pushEnergy _pushEnergyCotan = { COTAN, 0, _pushEnergyCotanEval, _pushEnergyCotanSupport }; const pushEnergy *const pushEnergyCotan = &_pushEnergyCotan; /* ---------------------------------------------------------------- ** ------------------------------- ZERO --------------------------- ** ---------------------------------------------------------------- ** 0 parms: */ void _pushEnergyZeroEval(double *enr, double *frc, double dist, const double *parm) { AIR_UNUSED(dist); AIR_UNUSED(parm); *enr = 0; *frc = 0; return; } double _pushEnergyZeroSupport(const double *parm) { AIR_UNUSED(parm); return 1.0; } const pushEnergy _pushEnergyZero = { ZERO, 0, _pushEnergyZeroEval, _pushEnergyZeroSupport }; const pushEnergy *const pushEnergyZero = &_pushEnergyZero; /* ---------------------------------------------------------------- ** ---------------------------------------------------------------- ** ---------------------------------------------------------------- */ const pushEnergy *const pushEnergyAll[PUSH_ENERGY_TYPE_MAX+1] = { &_pushEnergyUnknown, /* 0 */ &_pushEnergySpring, /* 1 */ &_pushEnergyGauss, /* 2 */ &_pushEnergyCoulomb, /* 3 */ &_pushEnergyCotan, /* 4 */ &_pushEnergyZero /* 5 */ }; pushEnergySpec * pushEnergySpecNew() { pushEnergySpec *ensp; int pi; ensp = (pushEnergySpec *)calloc(1, sizeof(pushEnergySpec)); if (ensp) { ensp->energy = pushEnergyUnknown; for (pi=0; piparm[pi] = AIR_NAN; } } return ensp; } void pushEnergySpecSet(pushEnergySpec *ensp, const pushEnergy *energy, const double parm[PUSH_ENERGY_PARM_NUM]) { unsigned int pi; if (ensp && energy && parm) { ensp->energy = energy; for (pi=0; piparm[pi] = parm[pi]; } } return; } pushEnergySpec * pushEnergySpecNix(pushEnergySpec *ensp) { airFree(ensp); return NULL; } int pushEnergySpecParse(pushEnergySpec *ensp, const char *_str) { static const char me[]="pushEnergySpecParse"; char *str, *col, *_pstr, *pstr; int etype; unsigned int pi, haveParm; airArray *mop; double pval; if (!( ensp && _str )) { biffAddf(PUSH, "%s: got NULL pointer", me); return 1; } /* see if its the name of something that needs no parameters */ etype = airEnumVal(pushEnergyType, _str); if (pushEnergyTypeUnknown != etype) { /* the string is the name of some energy */ ensp->energy = pushEnergyAll[etype]; if (0 != ensp->energy->parmNum) { biffAddf(PUSH, "%s: need %u parms for %s energy, but got none", me, ensp->energy->parmNum, ensp->energy->name); return 1; } /* the energy needs 0 parameters */ for (pi=0; piparm[pi] = AIR_NAN; } return 0; } /* start parsing parms after ':' */ mop = airMopNew(); str = airStrdup(_str); airMopAdd(mop, str, (airMopper)airFree, airMopAlways); col = strchr(str, ':'); if (!col) { biffAddf(PUSH, "%s: \"%s\" isn't a parameter-free energy, but it has no " "\":\" separator to indicate parameters", me, str); airMopError(mop); return 1; } *col = '\0'; etype = airEnumVal(pushEnergyType, str); if (pushEnergyTypeUnknown == etype) { biffAddf(PUSH, "%s: didn't recognize \"%s\" as a %s", me, str, pushEnergyType->name); airMopError(mop); return 1; } ensp->energy = pushEnergyAll[etype]; if (0 == ensp->energy->parmNum) { biffAddf(PUSH, "%s: \"%s\" energy has no parms, but got something", me, ensp->energy->name); return 1; } _pstr = pstr = col+1; /* code lifted from teem/src/nrrd/kernel.c, should probably refactor... */ for (haveParm=0; haveParmenergy->parmNum; haveParm++) { if (!pstr) { break; } if (1 != sscanf(pstr, "%lg", &pval)) { biffAddf(PUSH, "%s: trouble parsing \"%s\" as double (in \"%s\")", me, _pstr, _str); airMopError(mop); return 1; } ensp->parm[haveParm] = pval; if ((pstr = strchr(pstr, ','))) { pstr++; if (!*pstr) { biffAddf(PUSH, "%s: nothing after last comma in \"%s\" (in \"%s\")", me, _pstr, _str); airMopError(mop); return 1; } } } /* haveParm is now the number of parameters that were parsed. */ if (haveParm < ensp->energy->parmNum) { biffAddf(PUSH, "%s: parsed only %u of %u required parms (for %s energy)" "from \"%s\" (in \"%s\")", me, haveParm, ensp->energy->parmNum, ensp->energy->name, _pstr, _str); airMopError(mop); return 1; } else { if (pstr) { biffAddf(PUSH, "%s: \"%s\" (in \"%s\") has more than %u doubles", me, _pstr, _str, ensp->energy->parmNum); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } int _pushHestEnergyParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { pushEnergySpec **enspP; static const char me[]="_pushHestForceParse"; char *perr; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } enspP = (pushEnergySpec **)ptr; *enspP = pushEnergySpecNew(); if (pushEnergySpecParse(*enspP, str)) { perr = biffGetDone(PUSH); airStrcpy(err, AIR_STRLEN_HUGE, perr); free(perr); return 1; } return 0; } hestCB _pushHestEnergySpec = { sizeof(pushEnergySpec*), "energy specification", _pushHestEnergyParse, (airMopper)pushEnergySpecNix }; hestCB * pushHestEnergySpec = &_pushHestEnergySpec; teem-1.11.0~svn6057/src/push/binning.c0000664000175000017500000001535312165631065017157 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "push.h" #include "privatePush.h" /* ** because the pushContext keeps an array of bins (not pointers to them) ** we have Init and Done functions (not New and Nix) */ void pushBinInit(pushBin *bin, unsigned int incr) { pushPtrPtrUnion pppu; bin->pointNum = 0; bin->point = NULL; bin->pointArr = airArrayNew((pppu.point = &(bin->point), pppu.v), &(bin->pointNum), sizeof(pushPoint *), incr); bin->neighbor = NULL; return; } /* ** bins own the points they contain- so this frees them */ void pushBinDone(pushBin *bin) { unsigned int idx; for (idx=0; idxpointNum; idx++) { bin->point[idx] = pushPointNix(bin->point[idx]); } bin->pointArr = airArrayNuke(bin->pointArr); bin->neighbor = (pushBin **)airFree(bin->neighbor); return; } /* ** bins on boundary now extend to infinity; so the only time this ** returns NULL (indicating error) is for non-existent positions */ pushBin * _pushBinLocate(pushContext *pctx, double *_posWorld) { static const char me[]="_pushBinLocate"; double posWorld[4], posIdx[4]; unsigned int axi, eidx[3], binIdx; if (!ELL_3V_EXISTS(_posWorld)) { biffAddf(PUSH, "%s: non-existent position (%g,%g,%g)", me, _posWorld[0], _posWorld[1], _posWorld[2]); return NULL; } if (pctx->binSingle) { binIdx = 0; } else { ELL_3V_COPY(posWorld, _posWorld); posWorld[3] = 1.0; ELL_4MV_MUL(posIdx, pctx->gctx->shape->WtoI, posWorld); ELL_34V_HOMOG(posIdx, posIdx); for (axi=0; axi<3; axi++) { eidx[axi] = airIndexClamp(-0.5, posIdx[axi], pctx->gctx->shape->size[axi]-0.5, pctx->binsEdge[axi]); } binIdx = (eidx[0] + pctx->binsEdge[0]*(eidx[1] + pctx->binsEdge[1]*eidx[2])); } /* fprintf(stderr, "!%s: bin(%g,%g,%g) = %u\n", me, _posWorld[0], _posWorld[1], _posWorld[2], binIdx); */ return pctx->bin + binIdx; } /* ** this makes the bin the owner of the point */ void _pushBinPointAdd(pushContext *pctx, pushBin *bin, pushPoint *point) { int pntI; AIR_UNUSED(pctx); pntI = airArrayLenIncr(bin->pointArr, 1); bin->point[pntI] = point; return; } /* ** the bin loses track of the point, caller responsible for ownership */ void _pushBinPointRemove(pushContext *pctx, pushBin *bin, int loseIdx) { AIR_UNUSED(pctx); bin->point[loseIdx] = bin->point[bin->pointNum-1]; airArrayLenIncr(bin->pointArr, -1); return; } void _pushBinNeighborSet(pushBin *bin, pushBin **nei, unsigned int num) { unsigned int neiI; bin->neighbor = (pushBin **)airFree(bin->neighbor); bin->neighbor = (pushBin **)calloc(1+num, sizeof(pushBin *)); for (neiI=0; neiIneighbor[neiI] = nei[neiI]; } bin->neighbor[neiI] = NULL; return; } void pushBinAllNeighborSet(pushContext *pctx) { /* static const char me[]="pushBinAllNeighborSet"; */ pushBin *nei[3*3*3]; unsigned int neiNum, xi, yi, zi, xx, yy, zz, xmax, ymax, zmax, binIdx; int xmin, ymin, zmin; if (pctx->binSingle) { neiNum = 0; nei[neiNum++] = pctx->bin + 0; _pushBinNeighborSet(pctx->bin + 0, nei, neiNum); } else { for (zi=0; zibinsEdge[2]; zi++) { zmin = AIR_MAX(0, (int)zi-1); zmax = AIR_MIN(zi+1, pctx->binsEdge[2]-1); for (yi=0; yibinsEdge[1]; yi++) { ymin = AIR_MAX(0, (int)yi-1); ymax = AIR_MIN(yi+1, pctx->binsEdge[1]-1); for (xi=0; xibinsEdge[0]; xi++) { xmin = AIR_MAX(0, (int)xi-1); xmax = AIR_MIN(xi+1, pctx->binsEdge[0]-1); neiNum = 0; for (zz=zmin; zz<=zmax; zz++) { for (yy=ymin; yy<=ymax; yy++) { for (xx=xmin; xx<=xmax; xx++) { binIdx = xx + pctx->binsEdge[0]*(yy + pctx->binsEdge[1]*zz); /* fprintf(stderr, "!%s: nei[%u](%u,%u,%u) = %u\n", me, neiNum, xi, yi, zi, binIdx); */ nei[neiNum++] = pctx->bin + binIdx; } } } _pushBinNeighborSet(pctx->bin + xi + pctx->binsEdge[0] *(yi + pctx->binsEdge[1]*zi), nei, neiNum); } } } } return; } int pushBinPointAdd(pushContext *pctx, pushPoint *point) { static const char me[]="pushBinPointAdd"; pushBin *bin; if (!( bin = _pushBinLocate(pctx, point->pos) )) { biffAddf(PUSH, "%s: can't locate point %p %u", me, AIR_CAST(void*, point), point->ttaagg); return 1; } _pushBinPointAdd(pctx, bin, point); return 0; } /* ** This function is only called by the master thread, this ** does *not* have to be thread-safe in any way */ int pushRebin(pushContext *pctx) { static const char me[]="pushRebin"; unsigned int oldBinIdx, pointIdx; pushBin *oldBin, *newBin; pushPoint *point; if (!pctx->binSingle) { for (oldBinIdx=0; oldBinIdxbinNum; oldBinIdx++) { oldBin = pctx->bin + oldBinIdx; for (pointIdx=0; pointIdxpointNum; /* nope! */) { point = oldBin->point[pointIdx]; newBin = _pushBinLocate(pctx, point->pos); if (!newBin) { biffAddf(PUSH, "%s: can't locate point %p %u", me, AIR_CAST(void*, point), point->ttaagg); return 1; } if (oldBin != newBin) { _pushBinPointRemove(pctx, oldBin, pointIdx); _pushBinPointAdd(pctx, newBin, point); } else { /* its in the right bin, move on */ pointIdx++; } } /* for pointIdx */ } /* for oldBinIdx */ } return 0; } teem-1.11.0~svn6057/src/push/privatePush.h0000664000175000017500000000354512165631065020052 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __cplusplus extern "C" { #endif /* defaultsPush.c */ extern int _pushVerbose; /* binning.c */ extern pushBin *_pushBinLocate(pushContext *pctx, double *pos); extern void _pushBinPointAdd(pushContext *pctx, pushBin *bin, pushPoint *point); /* setup.c */ extern pushTask *_pushTaskNew(pushContext *pctx, int threadIdx); extern pushTask *_pushTaskNix(pushTask *task); extern int _pushTensorFieldSetup(pushContext *pctx); extern int _pushGageSetup(pushContext *pctx); extern int _pushTaskSetup(pushContext *pctx); extern int _pushBinSetup(pushContext *pctx); extern int _pushPointSetup(pushContext *pctx); /* action.c */ extern int _pushProbe(pushTask *task, pushPoint *point); #ifdef __cplusplus } #endif teem-1.11.0~svn6057/src/push/test/0000775000175000017500000000000012203513755016335 5ustar domibeldomibelteem-1.11.0~svn6057/src/push/test/eparse.c0000664000175000017500000000502012042367142017753 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../push.h" char *info = ("Tests parsing of energy, and its methods."); int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt=NULL; airArray *mop; pushEnergySpec *ensp; unsigned int pi, xi, nn; double xx, supp, del; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "energy", "spec", airTypeOther, 1, 1, &ensp, NULL, "specification of force function to use", NULL, NULL, pushHestEnergySpec); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); fprintf(stderr, "%s: parsed energy \"%s\", with %u parms.\n", me, ensp->energy->name, ensp->energy->parmNum); for (pi=0; pienergy->parmNum; pi++) { fprintf(stderr, "%u: %g\n", pi, ensp->parm[pi]); } fprintf(stderr, "\n"); nn = 600; supp = ensp->energy->support(ensp->parm); del = AIR_DELTA(0, 2, nn, 0, supp); for (xi=1; xienergy->eval(&e1, &dummy, x1, ensp->parm); ensp->energy->eval(&e0, &dummy, x0, ensp->parm); ensp->energy->eval(&ee, &ff, xx, ensp->parm); printf("%g %g %g %g\n", xx, ee, ff, (e1 - e0)/del); } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/push/test/pusher.c0000664000175000017500000002307012042367142020007 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../push.h" char *info = ("Test program for push library."); int main(int argc, const char *argv[]) { const char *me; char *err; hestOpt *hopt=NULL; airArray *mop; char *outS[3]; char *gravStr, *gravGradStr, *seedStr; pushContext *pctx; Nrrd *_nin, *nin, *nPosIn, *nPosOut, *nTenOut, *nEnrOut; NrrdKernelSpec *ksp00, *ksp11, *ksp22; pushEnergySpec *ensp; int E; me = argv[0]; mop = airMopNew(); pctx = pushContextNew(); airMopAdd(mop, pctx, (airMopper)pushContextNix, airMopAlways); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &_nin, NULL, "input volume to filter", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "np", "# points", airTypeUInt, 1, 1, &(pctx->pointNum), "1000", "number of points to use in simulation"); hestOptAdd(&hopt, "pi", "npos", airTypeOther, 1, 1, &nPosIn, "", "positions to start at (overrides \"-np\")", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "step", "step", airTypeDouble, 1, 1, &(pctx->stepInitial), "1", "step size for gradient descent"); hestOptAdd(&hopt, "scl", "scale", airTypeDouble, 1, 1, &(pctx->scale), "1500", "scaling from tensor size to glyph size"); hestOptAdd(&hopt, "wall", "wall", airTypeDouble, 1, 1, &(pctx->wall), "0.0", "spring constant of containing walls"); hestOptAdd(&hopt, "cnts", "scale", airTypeDouble, 1, 1, &(pctx->cntScl), "0.0", "scaling of containment force"); hestOptAdd(&hopt, "limit", "frac", airTypeDouble, 1, 1, &(pctx->deltaLimit), "0.3", "speed limit on particles' motion"); hestOptAdd(&hopt, "dfmin", "frac", airTypeDouble, 1, 1, &(pctx->deltaFracMin), "0.2", "decrease step size if deltaFrac goes below this"); hestOptAdd(&hopt, "esf", "frac", airTypeDouble, 1, 1, &(pctx->energyStepFrac), "0.9", "when energy goes up instead of down, fraction by " "which to scale step size"); hestOptAdd(&hopt, "dfsf", "frac", airTypeDouble, 1, 1, &(pctx->deltaFracStepFrac), "0.5", "when deltaFrac goes below deltaFracMin, fraction by " "which to scale step size"); hestOptAdd(&hopt, "eimin", "frac", airTypeDouble, 1, 1, &(pctx->energyImprovMin), "0.01", "convergence threshold: stop when fracional improvement " "(decrease) in energy dips below this"); hestOptAdd(&hopt, "detr", NULL, airTypeBool, 0, 0, &(pctx->detReject), NULL, "do determinant-based rejection of initial sample locations"); hestOptAdd(&hopt, "rng", "seed", airTypeUInt, 1, 1, &(pctx->seedRNG), "42", "seed value for RNG which determines initial point locations"); hestOptAdd(&hopt, "nt", "# threads", airTypeUInt, 1, 1, &(pctx->threadNum), "1", "number of threads to run"); hestOptAdd(&hopt, "nprob", "# iters", airTypeDouble, 1, 1, &(pctx->neighborTrueProb), "1.0", "do full neighbor traversal with this probability"); hestOptAdd(&hopt, "pprob", "# iters", airTypeDouble, 1, 1, &(pctx->probeProb), "1.0", "do field probing with this probability"); hestOptAdd(&hopt, "maxi", "# iters", airTypeUInt, 1, 1, &(pctx->maxIter), "0", "if non-zero, max # iterations to run"); hestOptAdd(&hopt, "snap", "iters", airTypeUInt, 1, 1, &(pctx->snap), "0", "if non-zero, # iterations between which a snapshot " "is saved"); hestOptAdd(&hopt, "grv", "item", airTypeString, 1, 1, &gravStr, "none", "item to act as gravity"); hestOptAdd(&hopt, "grvgv", "item", airTypeString, 1, 1, &gravGradStr, "none", "item to act as gravity gradient"); hestOptAdd(&hopt, "grvs", "scale", airTypeDouble, 1, 1, &(pctx->gravScl), "nan", "magnitude and scaling of gravity vector"); hestOptAdd(&hopt, "grvz", "scale", airTypeDouble, 1, 1, &(pctx->gravZero), "nan", "height (WRT gravity) of zero potential energy"); hestOptAdd(&hopt, "seed", "item", airTypeString, 1, 1, &seedStr, "none", "item to act as seed threshold"); hestOptAdd(&hopt, "seedth", "thresh", airTypeDouble, 1, 1, &(pctx->seedThresh), "nan", "seed threshold threshold"); hestOptAdd(&hopt, "energy", "spec", airTypeOther, 1, 1, &ensp, "cotan", "specification of energy function to use", NULL, NULL, pushHestEnergySpec); hestOptAdd(&hopt, "nobin", NULL, airTypeBool, 0, 0, &(pctx->binSingle), NULL, "turn off spatial binning (which prevents multi-threading " "from being useful), for debugging or speed-up measurement"); hestOptAdd(&hopt, "k00", "kernel", airTypeOther, 1, 1, &ksp00, "tent", "kernel for tensor field sampling", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kernel", airTypeOther, 1, 1, &ksp11, "fordif", "kernel for finding containment gradient from mask", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kernel", airTypeOther, 1, 1, &ksp22, "cubicdd:1,0", "kernel for 2nd derivatives", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "o", "nout", airTypeString, 3, 3, outS, "p.nrrd t.nrrd e.nrrd", "output files to save position and tensor info into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nPosOut = nrrdNew(); airMopAdd(mop, nPosOut, (airMopper)nrrdNuke, airMopAlways); nTenOut = nrrdNew(); airMopAdd(mop, nTenOut, (airMopper)nrrdNuke, airMopAlways); nEnrOut = nrrdNew(); airMopAdd(mop, nEnrOut, (airMopper)nrrdNuke, airMopAlways); if (3 == _nin->spaceDim && AIR_EXISTS(_nin->measurementFrame[0][0])) { nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); if (tenMeasurementFrameReduce(nin, _nin)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble undoing measurement frame:\n%s", me, err); airMopError(mop); exit(1); } } else { nin = _nin; } pctx->nin = nin; pctx->npos = nPosIn; pctx->verbose = 0; pctx->binIncr = 84; /* random small-ish value */ pushEnergySpecSet(pctx->ensp, ensp->energy, ensp->parm); nrrdKernelSpecSet(pctx->ksp00, ksp00->kernel, ksp00->parm); nrrdKernelSpecSet(pctx->ksp11, ksp11->kernel, ksp11->parm); nrrdKernelSpecSet(pctx->ksp22, ksp22->kernel, ksp22->parm); if (strcmp("none", gravStr)) { pctx->gravItem = airEnumVal(tenGage, gravStr); if (tenGageUnknown == pctx->gravItem) { fprintf(stderr, "%s: couldn't parse \"%s\" as a %s (gravity)\n", me, gravStr, tenGage->name); airMopError(mop); return 1; } pctx->gravGradItem = airEnumVal(tenGage, gravGradStr); if (tenGageUnknown == pctx->gravGradItem) { fprintf(stderr, "%s: couldn't parse \"%s\" as a %s (gravity grad)\n", me, gravGradStr, tenGage->name); airMopError(mop); return 1; } } else { pctx->gravItem = tenGageUnknown; pctx->gravGradItem = tenGageUnknown; pctx->gravZero = AIR_NAN; pctx->gravScl = AIR_NAN; } if (strcmp("none", seedStr)) { pctx->seedThreshItem = airEnumVal(tenGage, seedStr); if (tenGageUnknown == pctx->seedThreshItem) { fprintf(stderr, "%s: couldn't parse \"%s\" as a %s (seedthresh)\n", me, seedStr, tenGage->name); airMopError(mop); return 1; } } else { pctx->seedThreshItem = 0; pctx->seedThresh = AIR_NAN; } E = 0; if (!E) E |= pushStart(pctx); if (!E) E |= pushRun(pctx); if (!E) E |= pushOutputGet(nPosOut, nTenOut, nEnrOut, pctx); if (!E) E |= pushFinish(pctx); if (E) { airMopAdd(mop, err = biffGetDone(PUSH), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } fprintf(stderr, "%s: time for %d iterations= %g secs\n", me, pctx->iter, pctx->timeRun); if (nrrdSave(outS[0], nPosOut, NULL) || nrrdSave(outS[1], nTenOut, NULL) || nrrdSave(outS[2], nEnrOut, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/GNUmakefile0000664000175000017500000003274712165631065016470 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### #### top-level GNUmakefile: Master makefile for teem #### ## ".SUFFIXES :" speeds debugging with make -d (and probably make in ## general) by eliminating the list of suffixes checked by implicit ## pattern rules. The rest of the rules are to tell make to forget ## about trying to automatically update the files that we "include" ## .SUFFIXES : % : %,v % : RCS/%,v % : RCS/% % : s.% % : SCCS/s.% %.c : %.w # this doesn't seem to work, unfortunately ## TEEM_ROOT: a relative path to the directory which contains the ## "src", "include", and all the architecture-specific directories ## (which in turn contain "bin", "lib", and "obj"). Whether ## make started on this makefile or on an individual library's ## makefile determines who gets to set TEEM_ROOT first. Same for ## TEEM_SRC, a relative path to the "src" directory ## TEEM_ROOT ?= .. TEEM_SRC ?= . ## When making Windows project files, set TEEM_ARCH to cygwin ifeq (project, $(MAKECMDGOALS)) TEEM_ARCH ?= cygwin endif ## read in the check* functions, and check on TEEM_ARCH ## include $(TEEM_SRC)/make/errorCheck.mk $(checkArchSet) $(checkArchLinux) $(checkArchLinux64) $(checkArchNetbsd64) $(checkArchDarwin) $(checkArchValid) $(checkTeemDest) ## the architecture name may have two parts, ARCH and SUBARCH, ## seperated by one period ## ARCH = $(basename $(TEEM_ARCH)) SUBARCH = $(patsubst .%,%,$(suffix $(TEEM_ARCH))) ## Before we read in the architecture-dependent stuff, take a stab at ## defining the various programs we'll need, and some of their flags. ## If these are not over-written, we assume that they'll work. ## CC ?= cc LD = ld AR = ar ARFLAGS = ru RM = rm -f CP = cp CHMOD = chmod SLEEP = sleep ## Enstate the architecture-dependent settings by reading through the ## file specific to the chosen architecture, then check the things that ## are set there. ## include $(TEEM_SRC)/make/$(ARCH).mk $(checkShext) ## information about optional external ("xtern") libraries to link with ## include $(TEEM_SRC)/make/externals.mk ## LIBS: all the teem libraries we'll try to build ## NUMS: "numbering" of all the libraries in dependency order ## RNUMS: in link order, and "top-dep" order needed by bin/GNUmakefile ## {R}NUM/LIBS: used for craziness below ## (TEEM_LIB_LIST) ## LIBS = air hest biff nrrd ell unrrdu alan moss tijk gage dye bane limn echo hoover seek ten elf pull coil push mite meet NUMS = 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M RNUMS = M L K J I H G F E D C B A 9 8 7 6 5 4 3 2 1 0 NUM/LIBS = $(join $(NUMS),$(LIBS:%=/%)) RNUM/LIBS = $(join $(RNUMS),$(LIBS:%=/%)) ## MODES: the different kinds of builds that we support. Declaring ## these modes (for all libraries) as phony saves make the effort of ## trying implicit rules to update them. ## MODES = install dev clean clobber .PHONY : $(foreach LIB,teem $(LIBS),$(MODES:%=$(LIB)/%)) teem.dsp ## Top-level/default rules. If make started with this file, then ## these will be the first rules that make sees, so that "make" ## defaults to "make X/install" for *every* library X. If make ## started in a library subdirectory, these won't be seen at all. ## ifeq (,$(DEF_TARGETS)) install : teem/install dev : teem/dev clean : teem/clean clobber : teem/clobber DEF_TARGETS := true endif ## Top-level rules, available regardless of where make started ## teem/install : $(addsuffix /install,$(LIBS) bin) megalibs teem/dev : $(addsuffix /dev,$(LIBS) bin) teem/clean : $(addsuffix /clean,$(LIBS) bin) $(if $(TEEM_LITTER),$(RM) $(TEEM_ROOT)/src/$(TEEM_LITTER)) teem/clobber : teem/clean $(addsuffix /clobber,$(LIBS) bin) unmegalibs nothing : ; ## if.missing(stuff): returns $stuff if one or more of the files ## listed in stuff don't exist ## if.missing = $(if $(strip $(foreach x,$(1),$(if $(wildcard $(x)),,no))),$(1)) ## create.if.missing(dir): creates directory dir if it doesn't exist, ## and gives a warning to that effect ## create.if.missing = \ $(if $(call if.missing,$(1)),\ $(warning *** WARNING ***: Creating needed directory $(1)) \ $(shell mkdir $(1))) ## Set directory-related variables: where to install things, as well ## as the directories used in conjunction with the -I and -L path ## flags for cc and ld ## ifeq (undefined,$(origin TEEM_DEST)) IDEST = $(TEEM_ROOT)/include LDEST = $(TEEM_ROOT)/arch/$(TEEM_ARCH)/lib BDEST = $(TEEM_ROOT)/arch/$(TEEM_ARCH)/bin else IDEST = $(TEEM_DEST)/include LDEST = $(TEEM_DEST)/lib BDEST = $(TEEM_DEST)/bin $(foreach dir,$(IDEST) $(LDEST) $(BDEST),\ $(call create.if.missing,$(dir))) endif ## create IDEST/teem if necessary $(call create.if.missing,$(IDEST)/teem) ODEST = $(TEEM_ROOT)/arch/$(TEEM_ARCH)/obj IPATH += -I$(IDEST) LPATH += -L$(LDEST) ifneq (undefined,$(origin TEEM_DEST)) # we still need this for the teem*.h headers (such as teemEndian.h) IPATH += -I$(TEEM_ROOT)/include endif ## By giving a list of library extensions we care about, LIBEXTS determines ## what kinds of libraries are built (just static, or both static and ## shared) ## ifdef TEEM_SHEXT LIBEXTS = $(TEEM_SHEXT) endif LIBEXTS += a ####################################### ## Flags ## ifneq (undefined,$(origin TEEM_LINK_SHARED)) # If we ever have absolutify-this-path working, then having an # absolute TEEM_DEST path is no longer a pre-requisite BIN_CFLAGS += $(SHARED_CFLAG) $(if $(TEEM_DEST),$(if $(SHARED_RPATH),$(SHARED_RPATH)$(LDEST),),) else BIN_CFLAGS += $(STATIC_CFLAG) endif ## The -DTEEM_BUILD_EXPERIMENTAL_LIBS mimics something that ## would otherwise come from CMake files CFLAGS += -DTEEM_BUILD_EXPERIMENTAL_LIBS $(OPT_CFLAG) $(ARCH_CFLAG) LDFLAGS += $(ARCH_LDFLAG) $(SHARED_LDFLAG) ## SGI's C pre-processor errors aren't fatal by default ## CFLAGS += $(TEEM_CPP_ERROR_DIE) ####################################### ## "Functions" ## The bread and butter of how template.mk works. ## Each of these can be $(call)ed with a library name, as the one ## and only argument, in order to get a list of files or flags related to ## library. ## ## {libs,hdrs}.inst(L): installed libs and headers for library L ## hdrs.dev(L): the local (original) copies of public and private headers ## tests.dev(L): library L tests for debugging, built in-place ## {libs,objs}.dev(L): object files for library L, and the libs used ## when compiling the tests ## libs.inst = $(foreach ext,$(LIBEXTS),$(LDEST)/lib$(1).$(ext)) hdrs.inst = $(addprefix $(IDEST)/teem/,$($(1).PUBLIC_HEADERS)) hdrs.dev = $(addprefix $(TEEM_SRC)/$(1)/,\ $($(1).PUBLIC_HEADERS) $($(1).PRIVATE_HEADERS)) tests.dev = $(addprefix $(TEEM_SRC)/$(1)/,$($(1).TESTS)) libs.dev = $(foreach ext,$(LIBEXTS),$(ODEST)/lib$(1).$(ext)) objs.dev = $(addprefix $(ODEST)/,$($(1).OBJS)) hdrs.dev = $(addprefix $(TEEM_SRC)/$(1)/,\ $($(1).PUBLIC_HEADERS) $($(1).PRIVATE_HEADERS)) ## need(L): pseudo-recursive expansion of all the libraries which ## library L depends on, either directly or indirectly. Because make ## doesn't allow recursive functions, and because I can't figure out ## how to do fixed-point determination in gmake, I'm only doing a few ## levels of prerequisite expansion. Each level is done by "dmnl" ## (discover more needed libs). The number of levels is more than ## enough for the current teem; and adding more levels later is ## trivial. ## ## Since we rely on $(sort) to remove redundancies, we need a way of ## putting the libraries back in dependency order (!= lexical order). ## So, we prefix the needed library names with [0..D] (via $(join)) to ## create NUM/LIBS (above), re-sort them, and then lose the prefix ## with $(notdir). This is done by "deporder". On the link line, ## however, the ordering needs to be reversed, this is done by ## "linkorder". ## dmnl = $(sort $(foreach LIB,$(1),$(LIB) $($(LIB).NEED))) deporder = $(notdir $(sort $(foreach LIB,$(1),\ $(filter %/$(LIB),$(NUM/LIBS))))) linkorder = $(notdir $(sort $(foreach LIB,$(1),\ $(filter %/$(LIB),$(RNUM/LIBS))))) need = $(call deporder,\ $(call dmnl,\ $(call dmnl,\ $(call dmnl,\ $(foreach LIB,$(1),$($(LIB).NEED)))))) meneed = $(call deporder,$(1) $(call need,$(1))) ## link(libs): "-lLIB" for all LIBs in libs, in correct link order ## link = $(foreach LIB,$(call linkorder,$(1)),-l$(LIB)) ## For XXX in: "ENDIAN", "QNANHIBIT", "DIO" ## ## TEEM_XXX is set in the architecture-specific makefile, and ## $(L).NEED_XXX is set in the Makefile for the library which needs ## that info. Meanwhile, teemXxx.h in teem's top-level include ## directory contains C-preprocessor code to make sure that the ## variable has been set, and set to something ## reasonable. more.cflags(L) is the list of -D flags for all required ## XXX. ## more.cflags = $(if $($(1).NEED_DIO),-DTEEM_DIO=$(TEEM_DIO))\ $(if $($(1).NEED_QNANHIBIT),-DTEEM_QNANHIBIT=$(TEEM_QNANHIBIT)) \ ## banner(L) is progress indication for compiling library L; see ## template.mk. The double colon rules previously used to print this ## fatally confused parallel builds ## banner = echo "--------------------" $(1) "--------------------" #src2lib = $(notdir $(patsubst %/,%,$(dir $(1:.c=)))) #HDR2LIB = $(notdir $(patsubst %/,%,$(dir $(1:.h=)))) #STL2LIB = $(patsubst lib%,%,$(notdir $(1:.a=))) #SHL2LIB = $(patsubst lib%,%,$(notdir $(1:.$(TEEM_SHEXT)=))) #used.hdrs = $(foreach lib,$(1),$(call hdrs.inst,$(lib))) #used.libs = $(foreach lib,$(1),$(call libs.inst,$(lib))) #used = $(call used.libs,$(1)) $(call used.hdrs,$(1)) ## related to external optional libraries. Called "xtern"s to avoid ## similarities to library filename extensions ("ext"s) above. ## ## wants.xtern(XTERN): returns XTERN if TEEM_ has been set, ## otherwise nothing ## xterns: list of all wanted XTERNs (a subset of $(XTERNS)) ## for.xtern(libs,patt): for all xtern in xterns, for all libraries ## in $(libs) that benefit from $(xtern), ## replace XXX in $(patt) with $(xtern) ## evallist(list): evaluates each element of list as a variable ## xtern.Ipath(LIB): the external-specific -I flags needed for compiling ## objects in library LIB ## xtern.Dflag(LIB): the -D flags turned on for library LIB in order to ## enable the desired externals ## xtern.Lpath(libs): paths -Lblah for any wanted xterns whose base ## teem dependency is within libs. libs should probably be $($(L).meneed) ## for some library L ## xtern.link(libs): -l link flags for any wanted externals whose base ## teem dependency is within libs. ## wants.xtern = $(if $(findstring undefined,$(origin TEEM_$(1))),,$(1)) xterns = $(foreach xtern,$(XTERNS),$(call wants.xtern,$(xtern))) for.xtern = $(strip $(foreach xtern,$(xterns),$(foreach lib,$(1),\ $(if $(filter $(xtern),$($(lib).XTERN)),\ $(subst XXX,$(xtern),$(2)))))) evallist = $(foreach var,$(1),$($(var))) xtern.Ipath = $(call evallist,$(call for.xtern,$(1),TEEM_XXX_IPATH)) xtern.Dflag = $(call for.xtern,$(1),-DTEEM_XXX=1) xtern.Lpath = $(call evallist,$(call for.xtern,$(1),TEEM_XXX_LPATH)) xtern.link = $(call evallist,$(call for.xtern,$(1),XXX.LINK)) ####################################### ## Read in the makefiles for all the libraries, and the bins. Run-away ## recursive inclusion is prevented by having set DEF_TARGETS. ## Note: "include" is a directive, not a function, which eliminates ## the possibility of iterating through the libraries, reading the ## make file, and then setting variables based on what was just read. ## ## Lsave is used to preserve the value of L, in case we're being included ## from a lower-level makefile which set a value for L. If we didn't put ## L back the way it was, L would have to be set twice in the lower level ## makefiles ## INCLUDED = true ifneq (,$(L)) ifeq (undefined,$(origin Lsave)) Lsave := $(L) endif endif include $(foreach LIB,$(LIBS),$(TEEM_SRC)/$(LIB)/GNUmakefile) ifneq (,$(Lsave)) L := $(Lsave) INCLUDED = else # Lsave wasn't set, which means that make was invoked on this file, # and since the library make files do not include the bin makefile, # we do so now include $(TEEM_SRC)/bin/GNUmakefile endif ## megalibs: libteem.a and libteem.$(TEEM_SHEXT) ## megalibs : $(foreach ext,$(LIBEXTS),$(LDEST)/libteem.$(ext)) $(LDEST)/libteem.a : $(foreach lib,$(LIBS),$(call objs.dev,$(lib))) $(AR) $(ARFLAGS) $@ $^ ifdef TEEM_SHEXT $(LDEST)/libteem.$(TEEM_SHEXT) : $(foreach lib,$(LIBS),$(call objs.dev,$(lib))) $(LD) -o $@ \ $(if $(TEEM_DEST),$(if $(SHARED_LINK_NAME),$(SHARED_LINK_NAME)$@,),) \ $(LDFLAGS) $(LPATH) $^ \ $(foreach X,$(xterns),$(TEEM_$(X)_LPATH)) \ $(foreach X,$(xterns),$($(X).LINK)) endif unmegalibs: $(RM) $(foreach ext,$(LIBEXTS),$(LDEST)/libteem.$(ext)) include $(TEEM_SRC)/make/win32.mk teem-1.11.0~svn6057/src/ten/0000775000175000017500000000000012203513756015166 5ustar domibeldomibelteem-1.11.0~svn6057/src/ten/tendSten.c0000664000175000017500000000633212165631065017123 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Calculate structure tensors from a scalar field" static const char *_tend_stenInfoL = (INFO ". Not a diffusion tensor, but it is symmetric and positive-definate."); int tend_stenMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int iScale, dScale, dsmp; Nrrd *nin, *nout; char *outS; hestOptAdd(&hopt, "ds", "diff. scale", airTypeInt, 1, 1, &dScale, "1", "differentiation scale, in pixels: the radius of the " "kernel used for differentation to compute gradient vectors"); hestOptAdd(&hopt, "is", "int. scale", airTypeInt, 1, 1, &iScale, "2", "integration scale, in pixels: the radius of the " "kernel used for blurring outer products of gradients " "in order compute structure tensors"); hestOptAdd(&hopt, "df", "downsample factor", airTypeInt, 1, 1, &dsmp, "1", "the factor by which to downsample when creating volume of " "structure tensors"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input scalar volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output filename"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_stenInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (gageStructureTensor(nout, nin, dScale, iScale, dsmp)) { airMopAdd(mop, err=biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble calculating structure tensors:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(sten, INFO); teem-1.11.0~svn6057/src/ten/triple.c0000664000175000017500000003350312165631065016636 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" typedef void (*tenTripleConverter)(double dst[3], const double src[3]); #define SQRT6 2.449489742783178098197284 #define SQRT2 1.414213562373095048801689 #define SQRT3 1.732050807568877293527446 #define J1 (j[0]) #define J2 (j[1]) #define J3 (j[2]) #define K1 (k[0]) #define K2 (k[1]) #define K3 (k[2]) #define R1 (r[0]) #define R2 (r[1]) #define R3 (r[2]) #define MU1 (mu[0]) #define MU2 (mu[1]) #define MU3 (mu[2]) static void _iden(double dst[3], const double src[3]) { ELL_3V_COPY(dst, src); return; } /* in the function names below, the format is __() */ static void _mu_ev(double mu[3], const double ev[3]) { double mm; mm = mu[0] = (ev[0] + ev[1] + ev[2])/3; mu[1] = ((ev[0] - mm)*(ev[0] - mm) + (ev[1] - mm)*(ev[1] - mm) + (ev[2] - mm)*(ev[2] - mm))/3; mu[2] = ((ev[0] - mm)*(ev[0] - mm)*(ev[0] - mm) + (ev[1] - mm)*(ev[1] - mm)*(ev[1] - mm) + (ev[2] - mm)*(ev[2] - mm)*(ev[2] - mm))/3; } static double _xyzmat[] = {2/SQRT6, -1/SQRT6, -1/SQRT6, 0, 1/SQRT2, -1/SQRT2, 1/SQRT3, 1/SQRT3, 1/SQRT3}; static void _xyz_ev(double xyz[3], const double _ev[3]) { double ev[3], tmp; ELL_3V_COPY(ev, _ev); ELL_SORT3(ev[0], ev[1], ev[2], tmp); ELL_3MV_MUL(xyz, _xyzmat, ev); } static void _ev_xyz(double ev[3], const double xyz[3]) { ELL_3MV_TMUL(ev, _xyzmat, xyz); } static void _j_ev(double j[3], const double ev[3]) { J1 = ev[0] + ev[1] + ev[2]; J2 = ev[0]*ev[1] + ev[0]*ev[2] + ev[1]*ev[2]; J3 = ev[0]*ev[1]*ev[2]; } static void _k_mu(double k[3], const double mu[3]) { double stdv; K1 = 3*MU1; stdv = sqrt(MU2); K2 = SQRT3*stdv; K3 = stdv ? SQRT2*MU3/(stdv*stdv*stdv) : 0; } static void _r_ev(double r[3], const double ev[3]) { double mu[3], stdv; _mu_ev(mu, ev); R1 = sqrt(ev[0]*ev[0] + ev[1]*ev[1] + ev[2]*ev[2]); stdv = sqrt(MU2); R2 = R1 ? (3/SQRT2)*stdv/R1 : 0; R3 = stdv ? SQRT2*MU3/(stdv*stdv*stdv) : 0; } static void _r_mu(double r[3], const double mu[3]) { double stdv; R1 = sqrt(3*(MU1*MU1 + MU2)); stdv = sqrt(MU2); R2 = R1 ? (3/SQRT2)*stdv/R1 : 0; R3 = stdv ? SQRT2*MU3/(stdv*stdv*stdv) : 0; } static void _ev_wp(double ev[3], const double wp[3]) { ev[0] = wp[0] + wp[1]*cos(wp[2]); ev[1] = wp[0] + wp[1]*cos(wp[2] - 2*AIR_PI/3); ev[2] = wp[0] + wp[1]*cos(wp[2] + 2*AIR_PI/3); } static void _wp_mu(double wp[3], const double mu[3]) { double stdv, mode; wp[0] = MU1; stdv = sqrt(MU2); wp[1] = SQRT2*stdv; mode = stdv ? SQRT2*MU3/(stdv*stdv*stdv) : 0; mode = AIR_CLAMP(-1, mode, 1); wp[2] = acos(AIR_CLAMP(-1, mode, 1))/3; } static void _mu_j(double mu[3], const double j[3]) { MU1 = J1/3; MU2 = 2*(J1*J1 - 3*J2)/9; MU3 = 2*J1*J1*J1/27 - J1*J2/3 + J3; } static void _r_j(double r[3], const double j[3]) { double mu[3], stdv; R1 = sqrt(J1*J1 - 2*J2); R2 = sqrt(J1*J1 - 3*J2)/R1; _mu_j(mu, j); stdv = sqrt(MU2); R3 = stdv ? SQRT2*MU3/(stdv*stdv*stdv) : 0; } static void _k_r(double k[3], const double r[3]) { K1 = R1*sqrt(3 - 2*R2*R2); K2 = (SQRT2/SQRT3)*R1*R2; K3 = R3; } /* _j_r(double j[3], const double r[3]) { double ss, nmu3; J1 = R1*sqrt(3 - 2*R2*R2); J2 = R1*R1*(1 - R2*R2); ss = R1*R2; nmu3 = 2*R3*ss*ss*ss; J3 = } */ static void _wp_k(double wp[3], const double k[3]) { wp[0] = K1/3; wp[1] = (SQRT2/SQRT3)*K2; wp[2] = acos(AIR_CLAMP(-1, K3, 1))/3; } static void _k_wp(double k[3], const double wp[3]) { K1 = 3*wp[0]; K2 = (SQRT3/SQRT2)*wp[1]; K3 = cos(3*wp[2]); } static void _rtz_xyz(double rThZ[3], const double XYZ[3]) { rThZ[0] = sqrt(XYZ[0]*XYZ[0] + XYZ[1]*XYZ[1]); rThZ[1] = atan2(XYZ[1], XYZ[0]); rThZ[2] = XYZ[2]; } static void _rtp_xyz(double RThPh[3], const double XYZ[3]) { RThPh[0] = sqrt(XYZ[0]*XYZ[0] + XYZ[1]*XYZ[1] + XYZ[2]*XYZ[2]); RThPh[1] = atan2(XYZ[1], XYZ[0]); RThPh[2] = atan2(sqrt(XYZ[0]*XYZ[0] + XYZ[1]*XYZ[1]), XYZ[2]); } static void _xyz_rtz(double XYZ[3], const double rThZ[3]) { XYZ[0] = rThZ[0]*cos(rThZ[1]); XYZ[1] = rThZ[0]*sin(rThZ[1]); XYZ[2] = rThZ[2]; } static void _xyz_rtp(double XYZ[3], const double RThPh[3]) { XYZ[0] = RThPh[0]*cos(RThPh[1])*sin(RThPh[2]); XYZ[1] = RThPh[0]*sin(RThPh[1])*sin(RThPh[2]); XYZ[2] = RThPh[0]*cos(RThPh[2]); } static void _rtz_k(double rThZ[3], const double k[3]) { rThZ[0] = K2; rThZ[1] = acos(AIR_CLAMP(-1, K3, 1))/3; rThZ[2] = K1/SQRT3; } static void _k_rtz(double k[3], const double rThZ[3]) { K1 = SQRT3*rThZ[2]; K2 = rThZ[0]; K3 = cos(3*rThZ[1]); } static void _rtp_r(double RThPh[3], const double r[3]) { RThPh[0] = R1; RThPh[1] = acos(AIR_CLAMP(-1, R3, 1))/3; RThPh[2] = asin(AIR_CLAMP(-1, (SQRT2/SQRT3)*R2, 1)); } static void _r_rtp(double r[3], const double RThPh[3]) { R1 = RThPh[0]; R2 = sin(RThPh[2])*SQRT3/SQRT2; R3 = cos(3*RThPh[1]); } static void _wp_rtz(double wp[3], const double rThZ[3]) { wp[0] = rThZ[2]/SQRT3; wp[1] = (SQRT2/SQRT3)*rThZ[0]; wp[2] = rThZ[1]; } static void _rtz_wp(double rThZ[3], const double wp[3]) { rThZ[0] = (SQRT3/SQRT2)*wp[1]; rThZ[1] = wp[2]; rThZ[2] = SQRT3*wp[0]; } #define CONVERT1(dst, mid, src) \ static void \ _##dst##_##src(double dst[3], const double src[3]) { \ double mid[3]; \ _##mid##_##src(mid, src); \ _##dst##_##mid(dst, mid); \ } #define CONVERT2(dst, mdB, mdA, src) \ static void \ _##dst##_##src(double dst[3], const double src[3]) { \ double mdA[3], mdB[3]; \ _##mdA##_##src(mdA, src); \ _##mdB##_##mdA(mdB, mdA); \ _##dst##_##mdB(dst, mdB); \ } CONVERT1(ev, xyz, rtz) /* _ev_rtz */ CONVERT1(rtz, xyz, ev) /* _rtz_ev */ CONVERT1(ev, xyz, rtp) /* _ev_rtp */ CONVERT1(rtp, xyz, ev) /* _rtp_ev */ CONVERT1(k, mu, ev) /* _k_ev */ CONVERT1(wp, mu, ev) /* _wp_ev */ CONVERT1(ev, wp, mu) /* _ev_mu */ CONVERT2(ev, wp, mu, j) /* _ev_j */ CONVERT1(ev, wp, k) /* _ev_k */ CONVERT2(ev, xyz, rtp, r) /* _ev_r */ static tenTripleConverter _convert[TEN_TRIPLE_TYPE_MAX+1][TEN_TRIPLE_TYPE_MAX+1] = { {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, /* DEST: SRC: ev mu xyz rtz rtp J K R WP */ /* ev */ {NULL, _iden, _ev_mu, _ev_xyz, _ev_rtz, _ev_rtp, _ev_j, _ev_k, _ev_r, _ev_wp}, /* mu */ {NULL, _mu_ev, _iden, NULL, NULL, NULL, _mu_j, NULL, NULL, NULL}, /* xyz */ {NULL, _xyz_ev, NULL, _iden, _xyz_rtz, _xyz_rtp, NULL, NULL, NULL, NULL}, /* rtz */ {NULL, _rtz_ev, NULL, _rtz_xyz, _iden, NULL, NULL, _rtz_k, NULL, _rtz_wp}, /* rtp */ {NULL, _rtp_ev, NULL, _rtp_xyz, NULL, _iden, NULL, NULL, _rtp_r, NULL}, /* J */ {NULL, _j_ev, NULL, NULL, NULL, NULL, _iden, NULL, NULL, NULL}, /* K */ {NULL, _k_ev, _k_mu, NULL, _k_rtz, NULL, NULL, _iden, _k_r, _k_wp}, /* R */ {NULL, _r_ev, _r_mu, NULL, NULL, _r_rtp, _r_j, NULL, _iden, NULL}, /* WP */ {NULL, _wp_ev, _wp_mu, NULL, _wp_rtz, NULL, NULL, _wp_k, NULL, _iden}}; void tenTripleConvertSingle_d(double dst[3], int dstType, const double src[3], const int srcType) { static const char me[]="tenTripleConvertSingle_d"; int direct; if (airEnumValCheck(tenTripleType, dstType) || airEnumValCheck(tenTripleType, srcType)) { /* got invalid source or destination type */ ELL_3V_SET(dst, AIR_NAN, AIR_NAN, AIR_NAN); return; } if (_convert[dstType][srcType]) { /* we have a direct converter */ _convert[dstType][srcType](dst, src); direct = AIR_TRUE; } else { double eval[3]; /* else, for lack of anything clever, we convert via evals */ _convert[tenTripleTypeEigenvalue][srcType](eval, src); _convert[dstType][tenTripleTypeEigenvalue](dst, eval); direct = AIR_FALSE; } /* warn if conversion created non-existent values from existent input */ if (ELL_3V_EXISTS(src) && !ELL_3V_EXISTS(dst)) { fprintf(stderr, "%s: problem? (%s) %g %g %g <-%s- (%s) %g %g %g\n", me, airEnumStr(tenTripleType, dstType), dst[0], dst[1], dst[2], direct ? "-" : "...", airEnumStr(tenTripleType, srcType), src[0], src[1], src[2]); } return; } void tenTripleConvertSingle_f(float _dst[3], int dstType, const float _src[3], const int srcType) { double dst[3], src[3]; ELL_3V_COPY(src, _src); tenTripleConvertSingle_d(dst, dstType, src, srcType); ELL_3V_COPY_TT(_dst, float, dst); } void tenTripleCalcSingle_d(double dst[3], int ttype, double ten[7]) { double eval[3]; /* in time this can become more efficient ... */ switch (ttype) { case tenTripleTypeEigenvalue: tenEigensolve_d(dst, NULL, ten); break; case tenTripleTypeMoment: case tenTripleTypeXYZ: case tenTripleTypeRThetaZ: case tenTripleTypeRThetaPhi: case tenTripleTypeK: case tenTripleTypeJ: case tenTripleTypeWheelParm: tenEigensolve_d(eval, NULL, ten); tenTripleConvertSingle_d(dst, ttype, eval, tenTripleTypeEigenvalue); break; case tenTripleTypeR: dst[0] = sqrt(_tenAnisoTen_d[tenAniso_S](ten)); dst[1] = _tenAnisoTen_d[tenAniso_FA](ten); dst[2] = _tenAnisoTen_d[tenAniso_Mode](ten); break; default: /* what on earth? */ ELL_3V_SET(dst, AIR_NAN, AIR_NAN, AIR_NAN); } return; } void tenTripleCalcSingle_f(float dst[3], int ttype, float ten[7]) { double dst_d[3], ten_d[7]; TEN_T_COPY(ten_d, ten); tenTripleCalcSingle_d(dst_d, ttype, ten_d); ELL_3V_COPY_TT(dst, float, dst_d); return; } int tenTripleCalc(Nrrd *nout, int ttype, const Nrrd *nten) { static const char me[]="tenTripleCalc"; size_t II, NN, size[NRRD_DIM_MAX]; double (*ins)(void *, size_t, double), (*lup)(const void *, size_t); if (!( nout && nten )) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(tenTripleType, ttype)) { biffAddf(TEN, "%s: got invalid %s (%d)", me, tenTripleType->name, ttype); return 1; } if (tenTensorCheck(nten, nrrdTypeDefault, AIR_FALSE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a valid DT array", me); return 1; } if (!( nrrdTypeFloat == nten->type || nrrdTypeDouble == nten->type )) { biffAddf(TEN, "%s: need input type %s or %s, not %s\n", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nten->type)); } nrrdAxisInfoGet_nva(nten, nrrdAxisInfoSize, size); size[0] = 3; if (nrrdMaybeAlloc_nva(nout, nten->type, nten->dim, size)) { biffMovef(TEN, NRRD, "%s: couldn't alloc output", me); return 1; } NN = nrrdElementNumber(nten)/7; lup = nrrdDLookup[nten->type]; ins = nrrdDInsert[nten->type]; for (II=0; IIdata, vv + 7*II); } tenTripleCalcSingle_d(trip, ttype, ten); for (vv=0; vv<3; vv++) { ins(nout->data, vv + 3*II, trip[vv]); } } if (nrrdAxisInfoCopy(nout, nten, NULL, (NRRD_AXIS_INFO_SIZE_BIT))) { biffMovef(TEN, NRRD, "%s: couldn't copy axis info", me); return 1; } nout->axis[0].kind = nrrdKindUnknown; if (nrrdBasicInfoCopy(nout, nten, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { biffAddf(TEN, "%s:", me); return 1; } return 0; } int tenTripleConvert(Nrrd *nout, int dstType, const Nrrd *nin, int srcType) { static const char me[]="tenTripleConvert"; size_t II, NN; double (*ins)(void *, size_t, double), (*lup)(const void *, size_t); if (!( nout && nin )) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if ( airEnumValCheck(tenTripleType, dstType) || airEnumValCheck(tenTripleType, srcType) ) { biffAddf(TEN, "%s: got invalid %s dst (%d) or src (%d)", me, tenTripleType->name, dstType, srcType); return 1; } if (3 != nin->axis[0].size) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: need axis[0].size 3, not %s", me, airSprintSize_t(stmp, nin->axis[0].size)); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(TEN, "%s: input has non-scalar %s type", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (nrrdCopy(nout, nin)) { biffMovef(TEN, NRRD, "%s: couldn't initialize output", me); return 1; } lup = nrrdDLookup[nin->type]; ins = nrrdDInsert[nout->type]; NN = nrrdElementNumber(nin)/3; for (II=0; IIdata, 0 + 3*II); src[1] = lup(nin->data, 1 + 3*II); src[2] = lup(nin->data, 2 + 3*II); tenTripleConvertSingle_d(dst, dstType, src, srcType); ins(nout->data, 0 + 3*II, dst[0]); ins(nout->data, 1 + 3*II, dst[1]); ins(nout->data, 2 + 3*II, dst[2]); } return 0; } teem-1.11.0~svn6057/src/ten/bimod.c0000664000175000017500000003244012165631065016430 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" tenEMBimodalParm* tenEMBimodalParmNew() { tenEMBimodalParm *biparm; biparm = (tenEMBimodalParm*)calloc(1, sizeof(tenEMBimodalParm)); if (biparm) { biparm->minProb = 0.0001; biparm->minProb2 = 0.0001; biparm->minDelta = 0.00001; biparm->minFraction = 0.05; /* 5% */ biparm->minConfidence = 0.7; biparm->maxIteration = 200; biparm->verbose = AIR_FALSE; biparm->histo = NULL; biparm->pp1 = biparm->pp2 = NULL; biparm->vmin = biparm->vmax = AIR_NAN; biparm->N = 0; } return biparm; } tenEMBimodalParm* tenEMBimodalParmNix(tenEMBimodalParm *biparm) { if (biparm) { biparm->histo = (double *)airFree(biparm->histo); biparm->pp1 = (double *)airFree(biparm->pp1); biparm->pp2 = (double *)airFree(biparm->pp2); } airFree(biparm); return NULL; } int _tenEMBimodalInit(tenEMBimodalParm *biparm, const Nrrd *_nhisto) { static const char me[]="_tenEMBimodalInit"; int i, median; Nrrd *nhisto; double medianD, sum; airArray *mop; if (!( biparm->maxIteration > 5 )) { biffAddf(TEN, "%s: biparm->maxIteration = %d too small", me, biparm->maxIteration); return 1; } mop = airMopNew(); nhisto = nrrdNew(); airMopAdd(mop, nhisto, (airMopper)nrrdNuke, airMopOnError); airMopAdd(mop, nhisto, (airMopper)nrrdNix, airMopOnOkay); if (nrrdConvert(nhisto, _nhisto, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: trouble converting histogram to double", me); airMopError(mop); return 1; } biparm->N = nhisto->axis[0].size; biparm->histo = (double*)(nhisto->data); biparm->vmin = (AIR_EXISTS(nhisto->axis[0].min) ? nhisto->axis[0].min : -0.5); biparm->vmax = (AIR_EXISTS(nhisto->axis[0].max) ? nhisto->axis[0].max : biparm->N - 0.5); (nrrdMeasureLine[nrrdMeasureHistoMedian]) (&medianD, nrrdTypeDouble, biparm->histo, nrrdTypeDouble, biparm->N, AIR_NAN, AIR_NAN); (nrrdMeasureLine[nrrdMeasureSum]) (&sum, nrrdTypeDouble, biparm->histo, nrrdTypeDouble, biparm->N, AIR_NAN, AIR_NAN); for (i=0; iN; i++) { biparm->histo[i] /= sum; } if (!AIR_EXISTS(medianD)) { biffMovef(TEN, NRRD, "%s: got empty histogram? (median calculation failed)", me); airMopError(mop); return 1; } median = (int)medianD; biparm->pp1 = (double*)calloc(biparm->N, sizeof(double)); biparm->pp2 = (double*)calloc(biparm->N, sizeof(double)); if (!( biparm->pp1 && biparm->pp2 )) { biffAddf(TEN, "%s: couldn't allocate posterior prob. buffers", me); airMopError(mop); return 1; } /* get mean and stdv of bins below median */ (nrrdMeasureLine[nrrdMeasureHistoMean]) (&(biparm->mean1), nrrdTypeDouble, biparm->histo, nrrdTypeDouble, median, AIR_NAN, AIR_NAN); (nrrdMeasureLine[nrrdMeasureHistoSD]) (&(biparm->stdv1), nrrdTypeDouble, biparm->histo, nrrdTypeDouble, median, AIR_NAN, AIR_NAN); /* get mean (shift upwards by median) and stdv of bins above median */ (nrrdMeasureLine[nrrdMeasureHistoMean]) (&(biparm->mean2), nrrdTypeDouble, biparm->histo + median, nrrdTypeDouble, biparm->N - median, AIR_NAN, AIR_NAN); (nrrdMeasureLine[nrrdMeasureHistoSD]) (&(biparm->stdv2), nrrdTypeDouble, biparm->histo + median, nrrdTypeDouble, biparm->N - median, AIR_NAN, AIR_NAN); biparm->mean2 += median; biparm->fraction1 = 0.5; if (biparm->verbose) { fprintf(stderr, "%s: median = %d\n", me, median); fprintf(stderr, "%s: m1, s1 = %g, %g; m2, s2 = %g, %g\n", me, biparm->mean1, biparm->stdv1, biparm->mean2, biparm->stdv2); } airMopOkay(mop); return 0; } void _tenEMBimodalBoost(double *pp1P, double *pp2P, double b) { double p1, p2, tmp; int sw=AIR_FALSE; if (*pp1P < *pp2P) { ELL_SWAP2(*pp1P, *pp2P, tmp); sw = AIR_TRUE; } p1 = 1 - pow(1 - *pp1P, b); p2 = 1 - p1; if (sw) { *pp1P = p2; *pp2P = p1; } else { *pp1P = p1; *pp2P = p2; } } /* ** what is posterior probability that measured value x comes from ** material 1 and 2, stored in pp1 and pp2 */ void _tenEMBimodalPP(tenEMBimodalParm *biparm) { int i; double g1, g2, pp1, pp2, f1, min; min = (1 == biparm->stage ? biparm->minProb : biparm->minProb2); f1 = biparm->fraction1; for (i=0; iN; i++) { g1 = airGaussian(i, biparm->mean1, biparm->stdv1); g2 = airGaussian(i, biparm->mean2, biparm->stdv2); if (g1 <= min && g2 <= min) { pp1 = pp2 = 0; } else { pp1 = f1*g1 / (f1*g1 + (1-f1)*g2); pp2 = 1 - pp1; } biparm->pp1[i] = pp1; biparm->pp2[i] = pp2; } if (biparm->verbose > 1) { Nrrd *ntmp = nrrdNew(); nrrdWrap_va(ntmp, biparm->pp1, nrrdTypeDouble, 1, AIR_CAST(size_t, biparm->N)); nrrdSave("pp1.nrrd", ntmp, NULL); nrrdWrap_va(ntmp, biparm->pp2, nrrdTypeDouble, 1, AIR_CAST(size_t, biparm->N)); nrrdSave("pp2.nrrd", ntmp, NULL); nrrdNix(ntmp); } return; } double _tenEMBimodalNewFraction1(tenEMBimodalParm *biparm) { int i; double pp1, pp2, h, sum1, sum2; sum1 = sum2 = 0.0; for (i=0; iN; i++) { pp1 = biparm->pp1[i]; pp2 = biparm->pp2[i]; h = biparm->histo[i]; sum1 += pp1*h; sum2 += pp2*h; } return sum1/(sum1 + sum2); } void _tenEMBimodalNewMean(double *m1P, double *m2P, tenEMBimodalParm *biparm) { int i; double pp1, pp2, h, sum1, isum1, sum2, isum2; sum1 = isum1 = sum2 = isum2 = 0.0; for (i=0; iN; i++) { pp1 = biparm->pp1[i]; pp2 = biparm->pp2[i]; h = biparm->histo[i]; isum1 += i*pp1*h; isum2 += i*pp2*h; sum1 += pp1*h; sum2 += pp2*h; } *m1P = isum1/sum1; *m2P = isum2/sum2; } void _tenEMBimodalNewSigma(double *s1P, double *s2P, double m1, double m2, tenEMBimodalParm *biparm) { int i; double pp1, pp2, h, sum1, isum1, sum2, isum2; sum1 = isum1 = sum2 = isum2 = 0.0; for (i=0; iN; i++) { pp1 = biparm->pp1[i]; pp2 = biparm->pp2[i]; h = biparm->histo[i]; isum1 += (i-m1)*(i-m1)*pp1*h; isum2 += (i-m2)*(i-m2)*pp2*h; sum1 += pp1*h; sum2 += pp2*h; } *s1P = sqrt(isum1/sum1); *s2P = sqrt(isum2/sum2); } void _tenEMBimodalSaveImage(tenEMBimodalParm *biparm) { char name[AIR_STRLEN_MED]; Nrrd *nh, *nm, *nhi, *nmi, *ni; NrrdRange *range; const Nrrd *nhmhi[3]; double *m, max; int i; nh = nrrdNew(); nm = nrrdNew(); nhi = nrrdNew(); nmi = nrrdNew(); ni = nrrdNew(); nrrdWrap_va(nh, biparm->histo, nrrdTypeDouble, 1, AIR_CAST(size_t, biparm->N)); range = nrrdRangeNewSet(nh, nrrdBlind8BitRangeFalse); max = range->max*1.1; nrrdRangeNix(range); nrrdCopy(nm, nh); m = (double*)(nm->data); for (i=0; iN; i++) { m[i] = biparm->fraction1*airGaussian(i, biparm->mean1, biparm->stdv1); m[i] += (1-biparm->fraction1)*airGaussian(i, biparm->mean2, biparm->stdv2); } nrrdHistoDraw(nmi, nm, 400, AIR_FALSE, max); nrrdHistoDraw(nhi, nh, 400, AIR_FALSE, max); ELL_3V_SET(nhmhi, nhi, nmi, nhi); nrrdJoin(ni, nhmhi, 3, 0, AIR_TRUE); sprintf(name, "%04d-%d.png", biparm->iteration, biparm->stage); nrrdSave(name, ni, NULL); nh = nrrdNix(nh); nm = nrrdNuke(nm); nhi = nrrdNuke(nhi); nmi = nrrdNuke(nmi); ni = nrrdNuke(ni); return; } int _tenEMBimodalIterate(tenEMBimodalParm *biparm) { static const char me[]="_tenEMBimodalIterate"; double om1, os1, om2, os2, of1, m1, s1, m2, s2, f1; /* copy old values */ om1 = biparm->mean1; os1 = biparm->stdv1; of1 = biparm->fraction1; om2 = biparm->mean2; os2 = biparm->stdv2; /* find new values, and calculate delta */ _tenEMBimodalPP(biparm); f1 = _tenEMBimodalNewFraction1(biparm); /* if (1 == biparm->stage) { */ _tenEMBimodalNewMean(&m1, &m2, biparm); /* } */ _tenEMBimodalNewSigma(&s1, &s2, m1, m2, biparm); biparm->delta = ((fabs(m1 - om1) + fabs(m2 - om2) + fabs(s1 - os1) + fabs(s2 - os2))/biparm->N + fabs(f1 - of1)); /* set new values */ biparm->mean1 = m1; biparm->stdv1 = s1; biparm->fraction1 = f1; biparm->mean2 = m2; biparm->stdv2 = s2; if (biparm->verbose) { fprintf(stderr, "%s(%d:%d):\n", me, biparm->stage, biparm->iteration); fprintf(stderr, " m1, s1 = %g, %g\n", m1, s1); fprintf(stderr, " m2, s2 = %g, %g\n", m2, s2); fprintf(stderr, " f1 = %g ; delta = %g\n", f1, biparm->delta); } if (biparm->verbose > 1) { _tenEMBimodalSaveImage(biparm); } return 0; } int _tenEMBimodalConfThresh(tenEMBimodalParm *biparm) { static const char me[]="_tenEMBimodalConfThresh"; double m1, s1, m2, s2, f1, f2, A, B, C, D, t1, t2; biparm->confidence = ((biparm->mean2 - biparm->mean1) / (biparm->stdv1 + biparm->stdv2)); m1 = biparm->mean1; s1 = biparm->stdv1; f1 = biparm->fraction1; m2 = biparm->mean2; s2 = biparm->stdv2; f2 = 1 - f1; A = s1*s1 - s2*s2; B = 2*(m1*s2*s2 - m2*s1*s1); C = s1*s1*m2*m2 - s2*s2*m1*m1 + 4*s1*s1*s2*s2*log(s2*f1/(s1*f2)); D = B*B - 4*A*C; if (D < 0) { biffAddf(TEN, "%s: threshold descriminant went negative (%g)", me, D); return 1; } t1 = (-B + sqrt(D))/(2*A); if (AIR_IN_OP(m1, t1, m2)) { biparm->threshold = t1; } else { t2 = (-B - sqrt(D))/(2*A); if (AIR_IN_OP(m1, t2, m2)) { biparm->threshold = t2; } else { biffAddf(TEN, "%s: neither computed threshold %g,%g inside open interval " "between means (%g,%g)", me, t1, t2, m1, m2); return 1; } } if (biparm->verbose) { fprintf(stderr, "%s: conf = %g, thresh = %g\n", me, biparm->confidence, biparm->threshold); } return 0; } int _tenEMBimodalCheck(tenEMBimodalParm *biparm) { static const char me[]="_tenEMBimodalCheck"; if (!( biparm->confidence > biparm->minConfidence )) { biffAddf(TEN, "%s: confidence %g went below threshold %g", me, biparm->confidence, biparm->minConfidence); return 1; } if (!( biparm->stdv1 > 0 && biparm->stdv2 > 0 )) { biffAddf(TEN, "%s: stdv of material 1 (%g) or 2 (%g) went negative", me, biparm->stdv1, biparm->stdv2); return 1; } if (!( biparm->mean1 > 0 && biparm->mean1 < biparm->N-1 && biparm->mean2 > 0 && biparm->mean2 < biparm->N-1 )) { biffAddf(TEN, "%s: mean of material 1 (%g) or 2 (%g) went outside " "given histogram range [0 .. %d]", me, biparm->mean1, biparm->mean2, biparm->N-1); return 1; } if (biparm->fraction1 < biparm->minFraction) { biffAddf(TEN, "%s: material 1 fraction (%g) fell below threshold %g", me, biparm->fraction1, biparm->minFraction); return 1; } if (1 - biparm->fraction1 < biparm->minFraction) { biffAddf(TEN, "%s: material 2 fraction (%g) fell below threshold %g", me, 1 - biparm->fraction1, biparm->minFraction); return 1; } return 0; } int tenEMBimodal(tenEMBimodalParm *biparm, const Nrrd *_nhisto) { static const char me[]="tenEMBimodal"; int done, _iter; if (!(biparm && _nhisto)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( 1 == _nhisto->dim )) { biffAddf(TEN, "%s: histogram must be 1-D, not %d-D", me, _nhisto->dim); return 1; } if (_tenEMBimodalInit(biparm, _nhisto)) { biffAddf(TEN, "%s: trouble initializing parameters", me); return 1; } done = AIR_FALSE; biparm->iteration = 0; for (biparm->stage = 1; biparm->stage <= (biparm->twoStage ? 2 : 1); biparm->stage++) { for (_iter=0; biparm->iteration <= biparm->maxIteration; biparm->iteration++, _iter++) { if (_tenEMBimodalIterate(biparm) /* sets delta */ || _tenEMBimodalConfThresh(biparm) || _tenEMBimodalCheck(biparm)) { biffAddf(TEN, "%s: problem with fitting (iter=%d)", me, biparm->iteration); return 1; } if (biparm->delta < biparm->minDelta && (!biparm->twoStage || 1 == biparm->stage || _iter > 10) ) { done = AIR_TRUE; break; } } } if (!done) { biffAddf(TEN, "%s: didn't converge after %d iterations", me, biparm->maxIteration); return 1; } return 0; } teem-1.11.0~svn6057/src/ten/tendGrads.c0000664000175000017500000001554612165631065017261 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Calculate balanced gradient directions for DWI acquisition" static const char *_tend_gradsInfoL = (INFO ", based on a simulation of anti-podal point pairs repelling each other " "on the unit sphere surface. This can either distribute more uniformly " "a given set of gradients, or it can make a new distribution from scratch. " "A more clever implementation could decrease drag with time, as the " "solution converges, to get closer to the minimum energy configuration " "faster. In the mean time, you can run a second pass on the output of " "the first pass, using lower drag. A second phase of the algorithm " "tries sign changes in gradient directions in trying to find an optimally " "balanced set of directions. This uses a randomized search, so if it " "doesn't seem to be finishing in a reasonable amount of time, try " "restarting with a different \"-seed\"."); int tend_gradsMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int num, E; Nrrd *nin, *nout; char *outS; tenGradientParm *tgparm; unsigned int seed; mop = airMopNew(); tgparm = tenGradientParmNew(); airMopAdd(mop, tgparm, (airMopper)tenGradientParmNix, airMopAlways); hestOptAdd(&hopt, "n", "# dir", airTypeInt, 1, 1, &num, "6", "desired number of diffusion gradient directions"); hestOptAdd(&hopt, "i", "grads", airTypeOther, 1, 1, &nin, "", "initial gradient directions to start with, instead " "of default random initial directions (overrides \"-n\")", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "seed", "value", airTypeUInt, 1, 1, &seed, "42", "seed value to used with airSrandMT()"); hestOptAdd(&hopt, "step", "step", airTypeDouble, 1, 1, &(tgparm->initStep), "1.0", "time increment in solver"); hestOptAdd(&hopt, "single", NULL, airTypeInt, 0, 0, &(tgparm->single), NULL, "instead of the default behavior of tracking a pair of " "antipodal points (appropriate for determining DWI gradients), " "use only single points (appropriate for who knows what)."); hestOptAdd(&hopt, "snap", "interval", airTypeInt, 1, 1, &(tgparm->snap), "0", "specifies an interval between which snapshots of the point " "positions should be saved out. By default (not using this " "option), there is no such snapshot behavior"); hestOptAdd(&hopt, "jitter", "jitter", airTypeDouble, 1, 1, &(tgparm->jitter), "0.1", "amount by which to perturb points when given an input nrrd"); hestOptAdd(&hopt, "miniter", "# iters", airTypeInt, 1, 1, &(tgparm->minIteration), "0", "max number of iterations for which to run the simulation"); hestOptAdd(&hopt, "maxiter", "# iters", airTypeInt, 1, 1, &(tgparm->maxIteration), "1000000", "max number of iterations for which to run the simulation"); hestOptAdd(&hopt, "minvelo", "vel", airTypeDouble, 1, 1, &(tgparm->minVelocity), "0.00001", "low threshold on mean velocity of repelling points, " "at which point repulsion phase of algorithm terminates."); hestOptAdd(&hopt, "exp", "exponent", airTypeDouble, 1, 1, &(tgparm->expo_d), "1", "the exponent n that determines the potential energy 1/r^n."); hestOptAdd(&hopt, "dp", "potential change", airTypeDouble, 1, 1, &(tgparm->minPotentialChange), "0.000000001", "low threshold on fractional change of potential at " "which point repulsion phase of algorithm terminates."); hestOptAdd(&hopt, "minimprov", "delta", airTypeDouble, 1, 1, &(tgparm->minMeanImprovement), "0.00005", "in the second phase of the algorithm, " "when stochastically balancing the sign of the gradients, " "the (small) improvement in length of mean gradient " "which triggers termination (as further improvements " "are unlikely."); hestOptAdd(&hopt, "minmean", "len", airTypeDouble, 1, 1, &(tgparm->minMean), "0.0001", "if length of mean gradient falls below this, finish " "the balancing phase"); hestOptAdd(&hopt, "izv", "insert", airTypeBool, 1, 1, &(tgparm->insertZeroVec), "false", "adding zero vector at beginning of grads"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "file to write output nrrd to"); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_gradsInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); /* see if it was an integral exponent */ tgparm->expo = AIR_CAST(unsigned int, tgparm->expo_d); if (tgparm->expo == tgparm->expo_d) { /* ooo, it was */ tgparm->expo_d = 0; } else { /* no, its non-integral, indicate this as follows */ tgparm->expo = 0; } tgparm->seed = seed; if (tgparm->snap) { tgparm->report = tgparm->snap; } E = (nin ? tenGradientDistribute(nout, nin, tgparm) : tenGradientGenerate(nout, num, tgparm)); if (E) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making distribution:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(grads, INFO); teem-1.11.0~svn6057/src/ten/defaultsTen.c0000664000175000017500000000310612165631065017611 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" const int tenPresent = 42; const char * tenBiffKey = "ten"; const char tenDefFiberKernel[] = "cubic:0,0.5"; double tenDefFiberStepSize = 0.01; int tenDefFiberUseIndexSpace = AIR_FALSE; int tenDefFiberMaxNumSteps = 0; double tenDefFiberMaxHalfLen = 3; int tenDefFiberAnisoStopType = tenAniso_Cl2; double tenDefFiberAnisoThresh = 0.5; int tenDefFiberIntg = tenFiberIntgEuler; double tenDefFiberWPunct = 0; teem-1.11.0~svn6057/src/ten/modelB0.c0000664000175000017500000000367712165631065016632 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define PARM_NUM 1 static const tenModelParmDesc parmDesc[] = { /* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0}, }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; double b0; b0 = parm[0]; for (ii=0; iiimgNum; ii++) { dwiSim[ii] = b0; } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { sprintf(str, "(%g)", parm[0]); return str; } _TEN_PARM_ALLOC _TEN_PARM_RAND _TEN_PARM_STEP _TEN_PARM_DIST _TEN_PARM_COPY _TEN_PARM_CONVERT_NOOP _TEN_SQE _TEN_SQE_GRAD_CENTDIFF _TEN_SQE_FIT(tenModelB0) _TEN_NLL _TEN_NLL_GRAD_STUB _TEN_NLL_FIT_STUB tenModel _tenModelB0 = { TEN_MODEL_STR_B0, _TEN_MODEL_FIELDS }; const tenModel *const tenModelB0 = &_tenModelB0; teem-1.11.0~svn6057/src/ten/modelBall.c0000664000175000017500000000442612165631065017234 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define PARM_NUM 2 static const tenModelParmDesc parmDesc[] = { /* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 1 */ {"diffusivity", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0} }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; double b0, diff; b0 = parm[0]; diff = parm[1]; for (ii=0; iiimgNum; ii++) { dwiSim[ii] = b0*exp(-espec->bval[ii]*diff); } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { sprintf(str, "(%g) %g", parm[0], parm[1]); return str; } _TEN_PARM_ALLOC _TEN_PARM_RAND _TEN_PARM_STEP _TEN_PARM_DIST _TEN_PARM_COPY _TEN_PARM_CONVERT_NOOP _TEN_SQE _TEN_SQE_GRAD_CENTDIFF _TEN_SQE_FIT(tenModelBall) _TEN_NLL _TEN_NLL_GRAD_STUB _TEN_NLL_FIT_STUB tenModel _tenModelBall = { /* TEN_MODEL_STR_BALL, _TEN_MODEL_FIELDS */ "ball", 2, parmDesc, simulate, parmSprint, parmAlloc, parmRand, parmStep, parmDist, parmCopy, parmConvert, sqe, sqeGrad, sqeFit, nll, nllGrad, nllFit }; const tenModel *const tenModelBall = &_tenModelBall; teem-1.11.0~svn6057/src/ten/tendGlyph.c0000664000175000017500000006151312165631065017277 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Generate postscript or ray-traced renderings of 3D glyphs" static const char *_tend_glyphInfoL = (INFO ". Whether the output is postscript or a ray-traced image is controlled " "by the initial \"rt\" flag (by default, the output is postscript). " "Because this is doing viz/graphics, many parameters need to be set. " "Use a response file to simplify giving the command-line options which " "aren't changing between invocations. " "The postscript output is an EPS file, suitable for including as a figure " "in LaTeX, or viewing with ghostview, or distilling into PDF. " "The ray-traced output is a 5 channel (R,G,B,A,T) float nrrd, suitable for " "\"unu crop -min 0 0 0 -max 2 M M \" followed by " "\"unu gamma\" and/or \"unu quantize -b 8\"."); #define _LIMNMAGIC "LIMN0000" int _tendGlyphReadCams(int imgSize[2], limnCamera **camP, unsigned int *numCamsP, FILE *fin) { static const char me[]="_tendGlyphReadCams"; char line[AIR_STRLEN_HUGE]; int ki; double di, dn, df, fr[3], at[3], up[3], va, dwell; airArray *mop, *camA; if (!( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE) && !strcmp(_LIMNMAGIC, line) )) { biffAddf(TEN, "%s: couldn't read first line or it wasn't \"%s\"", me, _LIMNMAGIC); return 1; } if (!( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE) && 2 == (airStrtrans(airStrtrans(line, '{', ' '), '}', ' '), sscanf(line, "imgSize %d %d", imgSize+0, imgSize+1)) )) { biffAddf(TEN, "%s: couldn't read second line or it wasn't " "\"imgSize \"", me); return 1; } mop = airMopNew(); camA = airArrayNew((void **)camP, numCamsP, sizeof(limnCamera), 1); airMopAdd(mop, camA, (airMopper)airArrayNix, airMopAlways); while ( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE) ) { airStrtrans(airStrtrans(line, '{', ' '), '}', ' '); ki = airArrayLenIncr(camA, 1); if (14 != sscanf(line, "cam.di %lg cam.at %lg %lg %lg " "cam.up %lg %lg %lg cam.dn %lg cam.df %lg cam.va %lg " "relDwell %lg cam.fr %lg %lg %lg", &di, at+0, at+1, at+2, up+0, up+1, up+2, &dn, &df, &va, &dwell, fr+0, fr+1, fr+2)) { biffAddf(TEN, "%s: trouble parsing line %d: \"%s\"", me, ki, line); airMopError(mop); return 1; } (*camP)[ki].neer = dn; (*camP)[ki].faar = df; (*camP)[ki].dist = di; ELL_3V_COPY((*camP)[ki].from, fr); ELL_3V_COPY((*camP)[ki].at, at); ELL_3V_COPY((*camP)[ki].up, up); (*camP)[ki].fov = va; (*camP)[ki].aspect = (double)imgSize[0]/imgSize[1]; (*camP)[ki].atRelative = AIR_FALSE; (*camP)[ki].orthographic = AIR_FALSE; (*camP)[ki].rightHanded = AIR_TRUE; } airMopOkay(mop); return 0; } int tend_glyphMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret, doRT = AIR_FALSE; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *emap, *nraw, *npos, *nslc; char *outS; limnCamera *cam, *hackcams; limnObject *glyph; limnWindow *win; echoObject *rect=NULL; echoScene *scene; echoRTParm *eparm; echoGlobalState *gstate; tenGlyphParm *gparm; float bg[3], edgeColor[3], buvne[5], shadow, creaseAngle; int ires[2], slice[2], nobg, ambocc, concave; unsigned int hackci, hacknumcam; size_t hackmin[3]={0,0,0}, hackmax[3]={2,0,0}; char *hackFN, hackoutFN[AIR_STRLEN_SMALL]; FILE *hackF; Nrrd *hacknpng, *hacknrgb; NrrdRange *hackrange; double v2w[9], ldir[3], edir[3], fdir[3], corn[3], len; /* so that command-line options can be read from file */ hparm->respFileEnable = AIR_TRUE; hparm->elideSingleEmptyStringDefault = AIR_TRUE; mop = airMopNew(); cam = limnCameraNew(); airMopAdd(mop, cam, (airMopper)limnCameraNix, airMopAlways); glyph = limnObjectNew(1000, AIR_TRUE); airMopAdd(mop, glyph, (airMopper)limnObjectNix, airMopAlways); scene = echoSceneNew(); airMopAdd(mop, scene, (airMopper)echoSceneNix, airMopAlways); win = limnWindowNew(limnDevicePS); airMopAdd(mop, win, (airMopper)limnWindowNix, airMopAlways); gparm = tenGlyphParmNew(); airMopAdd(mop, gparm, (airMopper)tenGlyphParmNix, airMopAlways); eparm = echoRTParmNew(); airMopAdd(mop, eparm, (airMopper)echoRTParmNix, airMopAlways); /* do postscript or ray-traced? */ hestOptAdd(&hopt, "rt", NULL, airTypeFloat, 0, 0, &doRT, NULL, "generate ray-traced output. By default (not using this " "option), postscript output is generated."); hestOptAdd(&hopt, "v", "level", airTypeInt, 1, 1, &(gparm->verbose), "0", "verbosity level"); /* which points will rendered */ hestOptAdd(&hopt, "ctr", "conf thresh", airTypeFloat, 1, 1, &(gparm->confThresh), "0.5", "Glyphs will be drawn only for tensors with confidence " "values greater than this threshold"); hestOptAdd(&hopt, "a", "aniso", airTypeEnum, 1, 1, &(gparm->anisoType), "fa", "Which anisotropy metric to use for thresholding the data " "points to be drawn", NULL, tenAniso); hestOptAdd(&hopt, "atr", "aniso thresh", airTypeFloat, 1, 1, &(gparm->anisoThresh), "0.5", "Glyphs will be drawn only for tensors with anisotropy " "greater than this threshold"); hestOptAdd(&hopt, "p", "pos array", airTypeOther, 1, 1, &npos, "", "Instead of being on a grid, tensors are at arbitrary locations, " "as defined by this 3-by-N array of floats. Doing this makes " "various other options moot", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "m", "mask vol", airTypeOther, 1, 1, &(gparm->nmask), "", "Scalar volume (if any) for masking region in which glyphs are " "drawn, in conjunction with \"mtr\" flag. ", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "mtr", "mask thresh", airTypeFloat, 1, 1, &(gparm->maskThresh), "0.5", "Glyphs will be drawn only for tensors with mask " "value greater than this threshold"); /* how glyphs will be shaped */ hestOptAdd(&hopt, "g", "glyph shape", airTypeEnum, 1, 1, &(gparm->glyphType), "box", "shape of glyph to use for display. Possibilities " "include \"box\", \"sphere\", \"cylinder\", and " "\"superquad\"", NULL, tenGlyphType); hestOptAdd(&hopt, "sh", "sharpness", airTypeFloat, 1, 1, &(gparm->sqdSharp), "3.0", "for superquadric glyphs, how much to sharp edges form as a " "function of differences between eigenvalues. Higher values " "mean that edges form more easily"); hestOptAdd(&hopt, "gsc", "scale", airTypeFloat, 1, 1, &(gparm->glyphScale), "0.01", "over-all glyph size in world-space"); /* how glyphs will be colored */ hestOptAdd(&hopt, "c", "evector #", airTypeInt, 1, 1, &(gparm->colEvec), "0", "which eigenvector should determine coloring. " "(formally \"v\") " "\"0\", \"1\", \"2\" are principal, medium, and minor"); hestOptAdd(&hopt, "sat", "saturation", airTypeFloat, 1, 1, &(gparm->colMaxSat), "1.0", "maximal saturation to use on glyph colors (use 0.0 to " "create a black and white image)"); hestOptAdd(&hopt, "ga", "aniso", airTypeEnum, 1, 1, &(gparm->colAnisoType), "fa", "Which anisotropy metric to use for modulating the " "saturation of the glyph color", NULL, tenAniso); hestOptAdd(&hopt, "am", "aniso mod", airTypeFloat, 1, 1, &(gparm->colAnisoModulate), "0.0", "How much to modulate glyph color saturation by " "anisotropy (as chosen by \"-ga\"). " "If 1.0, then glyphs for zero anisotropy " "data points will have no hue. "); hestOptAdd(&hopt, "gg", "gray", airTypeFloat, 1, 1, &(gparm->colIsoGray), "1.0", "desaturating glyph color due to low anisotropy " "tends towards this gray level"); hestOptAdd(&hopt, "gam", "gamma", airTypeFloat, 1, 1, &(gparm->colGamma), "0.7", "gamma to use on color components (after saturation)"); hestOptAdd(&hopt, "emap", "env map", airTypeOther, 1, 1, &emap, "", "environment map to use for shading glyphs. By default, " "there is no shading", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "adsp", "phong", airTypeFloat, 4, 4, &(gparm->ADSP), "0 1 0 30", "phong ambient, diffuse, specular components, " "and specular power"); hestOptAdd(&hopt, "bg", "background", airTypeFloat, 3, 3, bg, "1 1 1", "background RGB color; each component in range [0.0,1.0]"); hestOptAdd(&hopt, "ec", "edge rgb", airTypeFloat, 3, 3, edgeColor, "0 0 0", "edge RGB color; each component in range [0.0,1.0]"); /* parameters for showing a dataset slice */ hestOptAdd(&hopt, "slc", "axis pos", airTypeInt, 2, 2, slice, "-1 -1", "For showing a gray-scale slice of anisotropy: the axis " "and position along which to slice. Use \"-1 -1\" to signify " "that no slice should be shown"); hestOptAdd(&hopt, "si", "slice image", airTypeOther, 1, 1, &nslc, "", "Instead of showing a slice of the anisotropy used to cull " "glyphs, show something else. ", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "off", "slice offset", airTypeFloat, 1, 1, &(gparm->sliceOffset), "0.0", "Offset from slice position to render slice at (so that it " "doesn't occlude glyphs)."); hestOptAdd(&hopt, "sg", "slice gamma", airTypeFloat, 1, 1, &(gparm->sliceGamma), "1.7", "Gamma to apply to values on slice."); hestOptAdd(&hopt, "sb", "slice bias", airTypeFloat, 1, 1, &(gparm->sliceBias), "0.05", "amount by which to bump up slice gray values prior to gamma."); /* camera */ hestOptAdd(&hopt, "fr", "from point", airTypeDouble, 3, 3, cam->from, NULL, "position of camera, used to determine view vector"); hestOptAdd(&hopt, "at", "at point", airTypeDouble, 3, 3, cam->at, "0 0 0", "camera look-at point, used to determine view vector"); hestOptAdd(&hopt, "up", "up vector", airTypeDouble, 3, 3, cam->up, "0 0 1", "camera pseudo-up vector, used to determine view coordinates"); hestOptAdd(&hopt, "rh", NULL, airTypeInt, 0, 0, &(cam->rightHanded), NULL, "use a right-handed UVN frame (V points down)"); hestOptAdd(&hopt, "dn", "near clip", airTypeDouble, 1, 1, &(cam->neer), "-2", "position of near clipping plane, relative to look-at point"); hestOptAdd(&hopt, "df", "far clip", airTypeDouble, 1, 1, &(cam->faar), "2", "position of far clipping plane, relative to look-at point"); hestOptAdd(&hopt, "or", NULL, airTypeInt, 0, 0, &(cam->orthographic), NULL, "use orthogonal projection"); hestOptAdd(&hopt, "ur", "uMin uMax", airTypeDouble, 2, 2, cam->uRange, "-1 1", "range in U direction of image plane"); hestOptAdd(&hopt, "vr", "vMin vMax", airTypeDouble, 2, 2, cam->vRange, "-1 1", "range in V direction of image plane"); hestOptAdd(&hopt, "fv", "fov", airTypeDouble, 1, 1, &(cam->fov), "nan", "if not NaN, vertical field-of-view, in degrees"); /* postscript-specific options */ hestOptAdd(&hopt, "gr", "glyph res", airTypeInt, 1, 1, &(gparm->facetRes), "10", "(* postscript only *) " "resolution of polygonalization of glyphs (all glyphs " "other than the default box)"); hestOptAdd(&hopt, "wd", "3 widths", airTypeFloat, 3, 3, gparm->edgeWidth, "0.8 0.4 0.0", "(* postscript only *) " "width of edges drawn for three kinds of glyph " "edges: silohuette, crease, non-crease"); hestOptAdd(&hopt, "psc", "scale", airTypeFloat, 1, 1, &(win->scale), "300", "(* postscript only *) " "scaling from screen space units to postscript units " "(in points)"); hestOptAdd(&hopt, "ca", "angle", airTypeFloat, 1, 1, &creaseAngle, "70", "(* postscript only *) " "minimum crease angle"); hestOptAdd(&hopt, "nobg", NULL, airTypeInt, 0, 0, &nobg, NULL, "(* postscript only *) " "don't initially fill with background color"); hestOptAdd(&hopt, "concave", NULL, airTypeInt, 0, 0, &concave, NULL, "use slightly buggy rendering method suitable for " "concave or self-occluding objects"); /* ray-traced-specific options */ hestOptAdd(&hopt, "is", "nx ny", airTypeInt, 2, 2, ires, "256 256", "(* ray-traced only *) " "image size (resolution) to render"); hestOptAdd(&hopt, "ns", "# samp", airTypeInt, 1, 1, &(eparm->numSamples),"4", "(* ray-traced only *) " "number of samples per pixel (must be a square number)"); if (airThreadCapable) { hestOptAdd(&hopt, "nt", "# threads", airTypeInt, 1, 1, &(eparm->numThreads), "1", "(* ray-traced only *) " "number of threads to be used for rendering"); } hestOptAdd(&hopt, "al", "B U V N E", airTypeFloat, 5, 5, buvne, "0 -1 -1 -4 0.7", "(* ray-traced only *) " "brightness (B), view-space location (U V N), " "and length of edge (E) " "of a square area light source, for getting soft shadows. " "Requires lots more samples \"-ns\" to converge. Use " "brightness 0 (the default) to turn this off, and use " "environment map-based shading (\"-emap\") instead. "); hestOptAdd(&hopt, "ao", NULL, airTypeInt, 0, 0, &ambocc, NULL, "set up 6 area lights in a box to approximate " "ambient occlusion"); hestOptAdd(&hopt, "shadow", "s", airTypeFloat, 1, 1, &shadow, "1.0", "the extent to which shadowing occurs"); hestOptAdd(&hopt, "hack", "hack", airTypeString, 1, 1, &hackFN, "", "don't mind me"); /* input/output */ hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output file"); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_glyphInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); /* set up slicing stuff */ if (!( -1 == slice[0] && -1 == slice[1] )) { gparm->doSlice = AIR_TRUE; gparm->sliceAxis = slice[0]; gparm->slicePos = slice[1]; gparm->sliceAnisoType = gparm->anisoType; /* gparm->sliceOffset set by hest */ } if (npos) { fprintf(stderr, "!%s: have npos --> turning off onlyPositive \n", me); gparm->onlyPositive = AIR_FALSE; } if (gparm->verbose) { fprintf(stderr, "%s: verbose = %d\n", me, gparm->verbose); } if (tenGlyphGen(doRT ? NULL : glyph, doRT ? scene : NULL, gparm, nin, npos, nslc)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble generating glyphs:\n%s\n", me, err); airMopError(mop); return 1; } if (AIR_EXISTS(cam->fov)) { if (limnCameraAspectSet(cam, ires[0], ires[1], nrrdCenterCell)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble with camera:\n%s\n", me, err); airMopError(mop); return 1; } } cam->dist = 0; cam->atRelative = AIR_TRUE; if (limnCameraUpdate(cam)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble with camera:\n%s\n", me, err); airMopError(mop); return 1; } if (doRT) { nraw = nrrdNew(); airMopAdd(mop, nraw, (airMopper)nrrdNuke, airMopAlways); gstate = echoGlobalStateNew(); airMopAdd(mop, gstate, (airMopper)echoGlobalStateNix, airMopAlways); eparm->shadow = shadow; if (buvne[0] > 0) { ELL_34M_EXTRACT(v2w, cam->V2W); ELL_3MV_MUL(ldir, v2w, buvne+1); ell_3v_perp_d(edir, ldir); ELL_3V_NORM(edir, edir, len); ELL_3V_CROSS(fdir, ldir, edir); ELL_3V_NORM(fdir, fdir, len); ELL_3V_SCALE(edir, buvne[4]/2, edir); ELL_3V_SCALE(fdir, buvne[4]/2, fdir); ELL_3V_ADD4(corn, cam->at, ldir, edir, fdir); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, corn[0], corn[1], corn[2], -edir[0]*2, -edir[1]*2, -edir[2]*2, -fdir[0]*2, -fdir[1]*2, -fdir[2]*2); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, buvne[0], 0); echoObjectAdd(scene, rect); } if (ambocc) { double eye[3], llen; ELL_3V_SUB(eye, cam->from, cam->at); llen = 4*ELL_3V_LEN(eye); ELL_3V_COPY(corn, cam->at); corn[0] -= llen/2; corn[1] -= llen/2; corn[2] -= llen/2; rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, corn[0], corn[1], corn[2], llen, 0, 0, 0, llen, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen)); echoObjectAdd(scene, rect); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, corn[0], corn[1], corn[2], 0, 0, llen, llen, 0, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen)); echoObjectAdd(scene, rect); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, corn[0], corn[1], corn[2], 0, llen, 0, 0, 0, llen); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen)); echoObjectAdd(scene, rect); corn[0] += llen/2; corn[1] += llen/2; corn[2] += llen/2; rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, corn[0], corn[1], corn[2], 0, -llen, 0, -llen, 0, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen)); echoObjectAdd(scene, rect); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, corn[0], corn[1], corn[2], -llen, 0, 0, 0, 0, -llen); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen)); echoObjectAdd(scene, rect); rect = echoObjectNew(scene, echoTypeRectangle); echoRectangleSet(rect, corn[0], corn[1], corn[2], 0, 0, -llen, 0, -llen, 0); echoColorSet(rect, 1, 1, 1, 1); echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen)); echoObjectAdd(scene, rect); } eparm->imgResU = ires[0]; eparm->imgResV = ires[1]; eparm->jitterType = (eparm->numSamples > 1 ? echoJitterJitter : echoJitterNone); eparm->aperture = 0; eparm->renderBoxes = AIR_FALSE; eparm->seedRand = AIR_FALSE; eparm->renderLights = AIR_FALSE; ELL_3V_COPY(scene->bkgr, bg); scene->envmap = emap; if (!airStrlen(hackFN)) { /* normal operation: one ray-tracing for one invocation */ if (echoRTRender(nraw, cam, scene, eparm, gstate)) { airMopAdd(mop, err = biffGetDone(ECHO), airFree, airMopAlways); fprintf(stderr, "%s: trouble ray-tracing %s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nraw, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving ray-tracing output %s\n", me, err); airMopError(mop); return 1; } } else { /* hack: multiple renderings per invocation */ if (!(hackF = airFopen(hackFN, stdin, "rb"))) { fprintf(stderr, "%s: couldn't fopen(\"%s\",\"rb\"): %s\n", me, hackFN, strerror(errno)); airMopError(mop); return 1; } if (_tendGlyphReadCams(ires, &hackcams, &hacknumcam, hackF)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble reading frames %s\n", me, err); airMopError(mop); return 1; } eparm->imgResU = ires[0]; eparm->imgResV = ires[1]; hackmax[1] = ires[0]-1; hackmax[2] = ires[1]-1; hacknrgb = nrrdNew(); hacknpng = nrrdNew(); airMopAdd(mop, hacknrgb, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, hacknpng, (airMopper)nrrdNuke, airMopAlways); hackrange = nrrdRangeNew(0.0, 1.0); airMopAdd(mop, hackrange, (airMopper)nrrdRangeNix, airMopAlways); for (hackci=0; hackciV2W); ELL_3MV_MUL(ldir, v2w, buvne+1); ell_3v_perp_d(edir, ldir); ELL_3V_NORM(edir, edir, len); ELL_3V_CROSS(fdir, ldir, edir); ELL_3V_NORM(fdir, fdir, len); ELL_3V_SCALE(edir, buvne[4]/2, edir); ELL_3V_SCALE(fdir, buvne[4]/2, fdir); ELL_3V_ADD4(corn, cam->at, ldir, edir, fdir); echoRectangleSet(rect, corn[0], corn[1], corn[2], edir[0]*2, edir[1]*2, edir[2]*2, fdir[0]*2, fdir[1]*2, fdir[2]*2); } if (echoRTRender(nraw, cam, scene, eparm, gstate)) { airMopAdd(mop, err = biffGetDone(ECHO), airFree, airMopAlways); fprintf(stderr, "%s: trouble ray-tracing %s\n", me, err); airMopError(mop); return 1; } sprintf(hackoutFN, "%04d.png", hackci); if (nrrdCrop(hacknrgb, nraw, hackmin, hackmax) || nrrdQuantize(hacknpng, hacknrgb, hackrange, 8) || nrrdSave(hackoutFN, hacknpng, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving output %s\n", me, err); airMopError(mop); return 1; } } } } else { if (!(win->file = airFopen(outS, stdout, "wb"))) { fprintf(stderr, "%s: couldn't fopen(\"%s\",\"wb\"): %s\n", me, outS, strerror(errno)); airMopError(mop); return 1; } airMopAdd(mop, win->file, (airMopper)airFclose, airMopAlways); cam->neer = -0.000000001; cam->faar = 0.0000000001; win->ps.lineWidth[limnEdgeTypeBackFacet] = 0; win->ps.lineWidth[limnEdgeTypeBackCrease] = 0; win->ps.lineWidth[limnEdgeTypeContour] = gparm->edgeWidth[0]; win->ps.lineWidth[limnEdgeTypeFrontCrease] = gparm->edgeWidth[1]; win->ps.lineWidth[limnEdgeTypeFrontFacet] = gparm->edgeWidth[2]; win->ps.lineWidth[limnEdgeTypeBorder] = 0; /* win->ps.lineWidth[limnEdgeTypeFrontCrease]; */ win->ps.creaseAngle = creaseAngle; win->ps.noBackground = nobg; ELL_3V_COPY(win->ps.bg, bg); ELL_3V_COPY(win->ps.edgeColor, edgeColor); if (limnObjectRender(glyph, cam, win) || (concave ? limnObjectPSDrawConcave(glyph, cam, emap, win) : limnObjectPSDraw(glyph, cam, emap, win))) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble drawing glyphs:\n%s\n", me, err); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } TEND_CMD(glyph, INFO); teem-1.11.0~svn6057/src/ten/tendAnscale.c0000664000175000017500000000645212165631065017563 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Scale the anisotropic component of the tensors" static const char *_tend_anscaleInfoL = (INFO ". This maintains the isotropic component of the tensor, and fixes " "either the trace or determinant, " "while scaling up (or down) the \"deviatoric\" component " "of the tensor. Good for exaggerating the shape of nearly isotropic " "tensors."); int tend_anscaleMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS; float scale; int fixDet, makePositive; hestOptAdd(&hopt, "s", "scale", airTypeFloat, 1, 1, &scale, NULL, "Amount by which to scale deviatoric component of tensor."); hestOptAdd(&hopt, "fd", NULL, airTypeInt, 0, 0, &fixDet, NULL, "instead of fixing the per-sample trace (the default), fix the " "determinant (ellipsoid volume)"); hestOptAdd(&hopt, "mp", NULL, airTypeInt, 0, 0, &makePositive, NULL, "after changing the eigenvalues of the tensor, enforce their " "non-negative-ness. By default, no such constraint is imposed."); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_anscaleInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenAnisoScale(nout, nin, scale, fixDet, makePositive)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(anscale, INFO); teem-1.11.0~svn6057/src/ten/tendBmat.c0000664000175000017500000000610112165631065017067 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Calculate B-matrix given gradient directions" static const char *_tend_bmatInfoL = (INFO ", assuming no diffusion weighting from the other imaging gradients. " "The input is a 3-by-N array of floats or doubles, each row being " "one of the gradient directions used for diffusion-weighted imaging. " "A plain text file with one gradient per line, no punctuation, is an " "easy way to specify this information. " "The gradient vector coefficients are used as is, without normalization " "(since different gradient strengths are sometimes desired). " "The output has one row of the B-matrix per line, with coefficient " "ordering Bxx, Bxy, Bxz, Byy, Byz, Bzz, and with the off-diagonal " "elements NOT pre-multiplied by 2."); int tend_bmatMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *ngrad, *nout; char *outS; hestOptAdd(&hopt, "i", "grads", airTypeOther, 1, 1, &ngrad, NULL, "array of gradient directions", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output B matrix"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_bmatInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenBMatrixCalc(nout, ngrad)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making B matrix:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(bmat, INFO); teem-1.11.0~svn6057/src/ten/estimate.c0000664000175000017500000016723512174653566017176 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" /* ** learned: when it looks like good-old LLS estimation is producing ** nothing but zero tensors, see if your tec->valueMin is larger ** than (what are problably) floating-point DWIs */ /* http://www.mathworks.com/access/helpdesk/help/toolbox/curvefit/ch_fitt5.html#40515 */ /* ---------------------------------------------- */ int _tenGaussian(double *retP, double m, double t, double s) { static const char me[]="_tenGaussian"; double diff, earg, den; if (!retP) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } diff = (m-t)/2; earg = -diff*diff/2; den = s*sqrt(2*AIR_PI); *retP = exp(earg)/den; if (!AIR_EXISTS(*retP)) { biffAddf(TEN, "%s: m=%g, t=%g, s=%g", me, m, t, s); biffAddf(TEN, "%s: diff=%g, earg=%g, den=%g", me, diff, earg, den); biffAddf(TEN, "%s: failed with ret = exp(%g)/%g = %g/%g = %g", me, earg, den, exp(earg), den, *retP); *retP = AIR_NAN; return 1; } return 0; } int _tenRicianTrue(double *retP, double m /* measured */, double t /* truth */, double s /* sigma */) { static const char me[]="_tenRicianTrue"; double mos, moss, mos2, tos2, tos, ss, earg, barg; if (!retP) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } mos = m/s; moss = mos/s; tos = t/s; ss = s*s; mos2 = mos*mos; tos2 = tos*tos; earg = -(mos2 + tos2)/2; barg = mos*tos; *retP = exp(earg)*airBesselI0(barg)*moss; if (!AIR_EXISTS(*retP)) { biffAddf(TEN, "%s: m=%g, t=%g, s=%g", me, m, t, s); biffAddf(TEN, "%s: mos=%g, moss=%g, tos=%g, ss=%g", me, mos, moss, tos, ss); biffAddf(TEN, "%s: mos2=%g, tos2=%g, earg=%g, barg=%g", me, mos2, tos2, earg, barg); biffAddf(TEN, "%s: failed: ret=exp(%g)*bessi0(%g)*%g = %g * %g * %g = %g", me, earg, barg, moss, exp(earg), airBesselI0(barg), moss, *retP); *retP = AIR_NAN; return 1; } return 0; } int _tenRicianSafe(double *retP, double m, double t, double s) { static const char me[]="_tenRicianSafe"; double diff, ric, gau, neer=10, faar=20; int E; if (!retP) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } diff = AIR_ABS(m-t)/s; E = 0; if (diff < neer) { if (!E) E |= _tenRicianTrue(retP, m, t, s); } else if (diff < faar) { if (!E) E |= _tenRicianTrue(&ric, m, t, s); if (!E) E |= _tenGaussian(&gau, m, t, s); if (!E) *retP = AIR_AFFINE(neer, diff, faar, ric, gau); } else { if (!E) E |= _tenGaussian(retP, m, t, s); } if (E) { biffAddf(TEN, "%s: failed with m=%g, t=%g, s=%g -> diff=%g", me, m, t, s, diff); *retP = AIR_NAN; return 1; } return 0; } int _tenRician(double *retP, double m /* measured */, double t /* truth */, double s /* sigma */) { static const char me[]="_tenRician"; double tos, ric, gau, loSignal=4.0, hiSignal=8.0; int E; if (!retP) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( m >= 0 && t >= 0 && s > 0 )) { biffAddf(TEN, "%s: got bad args: m=%g t=%g s=%g", me, m, t, s); *retP = AIR_NAN; return 1; } tos = t/s; E = 0; if (tos < loSignal) { if (!E) E |= _tenRicianSafe(retP, m, t, s); } else if (tos < hiSignal) { if (!E) E |= _tenRicianSafe(&ric, m, t, s); if (!E) E |= _tenGaussian(&gau, m, t, s); if (!E) *retP = AIR_AFFINE(loSignal, tos, hiSignal, ric, gau); } else { if (!E) E |= _tenGaussian(retP, m, t, s); } if (E) { biffAddf(TEN, "%s: failed with m=%g, t=%g, s=%g -> tos=%g", me, m, t, s, tos); *retP = AIR_NAN; return 1; } return 0; } enum { flagUnknown, flagEstimateMethod, flagBInfo, flagAllNum, flagDwiNum, flagAllAlloc, flagDwiAlloc, flagAllSet, flagDwiSet, flagSkipSet, flagWght, flagEmat, flagLast }; void _tenEstimateOutputInit(tenEstimateContext *tec) { tec->estimatedB0 = AIR_NAN; TEN_T_SET(tec->ten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); tec->conf = AIR_NAN; tec->mdwi = AIR_NAN; tec->time = AIR_NAN; tec->errorDwi = AIR_NAN; tec->errorLogDwi = AIR_NAN; tec->likelihoodDwi = AIR_NAN; } tenEstimateContext * tenEstimateContextNew() { tenEstimateContext *tec; unsigned int fi; airPtrPtrUnion appu; tec = AIR_CAST(tenEstimateContext *, malloc(sizeof(tenEstimateContext))); if (tec) { tec->bValue = AIR_NAN; tec->valueMin = AIR_NAN; tec->sigma = AIR_NAN; tec->dwiConfThresh = AIR_NAN; tec->dwiConfSoft = AIR_NAN; tec->_ngrad = NULL; tec->_nbmat = NULL; tec->skipList = NULL; appu.ui = &(tec->skipList); tec->skipListArr = airArrayNew(appu.v, NULL, 2*sizeof(unsigned int), 128); tec->skipListArr->noReallocWhenSmaller = AIR_TRUE; tec->all_f = NULL; tec->all_d = NULL; tec->simulate = AIR_FALSE; tec->estimate1Method = tenEstimate1MethodUnknown; tec->estimateB0 = AIR_TRUE; tec->recordTime = AIR_FALSE; tec->recordErrorDwi = AIR_FALSE; tec->recordErrorLogDwi = AIR_FALSE; tec->recordLikelihoodDwi = AIR_FALSE; tec->verbose = 0; tec->progress = AIR_FALSE; tec->WLSIterNum = 3; for (fi=flagUnknown+1; fiflag[fi] = AIR_FALSE; } tec->allNum = 0; tec->dwiNum = 0; tec->nbmat = nrrdNew(); tec->nwght = nrrdNew(); tec->nemat = nrrdNew(); tec->knownB0 = AIR_NAN; tec->all = NULL; tec->bnorm = NULL; tec->allTmp = NULL; tec->dwiTmp = NULL; tec->dwi = NULL; tec->skipLut = NULL; _tenEstimateOutputInit(tec); } return tec; } tenEstimateContext * tenEstimateContextNix(tenEstimateContext *tec) { if (tec) { nrrdNuke(tec->nbmat); nrrdNuke(tec->nwght); nrrdNuke(tec->nemat); airArrayNuke(tec->skipListArr); airFree(tec->all); airFree(tec->bnorm); airFree(tec->allTmp); airFree(tec->dwiTmp); airFree(tec->dwi); airFree(tec->skipLut); airFree(tec); } return NULL; } void tenEstimateVerboseSet(tenEstimateContext *tec, int verbose) { if (tec) { tec->verbose = verbose; } return; } void tenEstimateNegEvalShiftSet(tenEstimateContext *tec, int doit) { if (tec) { tec->negEvalShift = !!doit; } return; } int tenEstimateMethodSet(tenEstimateContext *tec, int estimateMethod) { static const char me[]="tenEstimateMethodSet"; if (!tec) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(tenEstimate1Method, estimateMethod)) { biffAddf(TEN, "%s: estimateMethod %d not a valid %s", me, estimateMethod, tenEstimate1Method->name); return 1; } if (tec->estimate1Method != estimateMethod) { tec->estimate1Method = estimateMethod; tec->flag[flagEstimateMethod] = AIR_TRUE; } return 0; } int tenEstimateSigmaSet(tenEstimateContext *tec, double sigma) { static const char me[]="tenEstimateSigmaSet"; if (!tec) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!(AIR_EXISTS(sigma) && sigma >= 0.0)) { biffAddf(TEN, "%s: given sigma (%g) not existent and >= 0.0", me, sigma); return 1; } tec->sigma = sigma; return 0; } int tenEstimateValueMinSet(tenEstimateContext *tec, double valueMin) { static const char me[]="tenEstimateValueMinSet"; if (!tec) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!(AIR_EXISTS(valueMin) && valueMin > 0.0)) { biffAddf(TEN, "%s: given valueMin (%g) not existent and > 0.0", me, valueMin); return 1; } tec->valueMin = valueMin; return 0; } int tenEstimateGradientsSet(tenEstimateContext *tec, const Nrrd *ngrad, double bValue, int estimateB0) { static const char me[]="tenEstimateGradientsSet"; if (!(tec && ngrad)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!AIR_EXISTS(bValue)) { biffAddf(TEN, "%s: given b value doesn't exist", me); return 1; } if (tenGradientCheck(ngrad, nrrdTypeDefault, 7)) { biffAddf(TEN, "%s: problem with gradient list", me); return 1; } tec->bValue = bValue; tec->_ngrad = ngrad; tec->_nbmat = NULL; tec->estimateB0 = estimateB0; tec->flag[flagBInfo] = AIR_TRUE; return 0; } int tenEstimateBMatricesSet(tenEstimateContext *tec, const Nrrd *nbmat, double bValue, int estimateB0) { static const char me[]="tenEstimateBMatricesSet"; if (!(tec && nbmat)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!AIR_EXISTS(bValue)) { biffAddf(TEN, "%s: given b value doesn't exist", me); return 1; } if (tenBMatrixCheck(nbmat, nrrdTypeDefault, 7)) { biffAddf(TEN, "%s: problem with b-matrix list", me); return 1; } tec->bValue = bValue; tec->_ngrad = NULL; tec->_nbmat = nbmat; tec->estimateB0 = estimateB0; tec->flag[flagBInfo] = AIR_TRUE; return 0; } int tenEstimateSkipSet(tenEstimateContext *tec, unsigned int valIdx, int doSkip) { static const char me[]="tenEstimateSkipSet"; unsigned int skipIdx; if (!tec) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } skipIdx = airArrayLenIncr(tec->skipListArr, 1); tec->skipList[0 + 2*skipIdx] = valIdx; tec->skipList[1 + 2*skipIdx] = !!doSkip; tec->flag[flagSkipSet] = AIR_TRUE; return 0; } int tenEstimateSkipReset(tenEstimateContext *tec) { static const char me[]="tenEstimateSkipReset"; if (!tec) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } airArrayLenSet(tec->skipListArr, 0); tec->flag[flagSkipSet] = AIR_TRUE; return 0; } int tenEstimateThresholdSet(tenEstimateContext *tec, double thresh, double soft) { static const char me[]="tenEstimateThresholdSet"; if (!tec) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!(AIR_EXISTS(thresh) && AIR_EXISTS(soft))) { biffAddf(TEN, "%s: not both threshold (%g) and softness (%g) exist", me, thresh, soft); return 1; } tec->dwiConfThresh = thresh; tec->dwiConfSoft = soft; return 0; } int _tenEstimateCheck(tenEstimateContext *tec) { static const char me[]="_tenEstimateCheck"; if (!tec) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( AIR_EXISTS(tec->valueMin) && tec->valueMin > 0.0)) { biffAddf(TEN, "%s: need a positive valueMin set (not %g)", me, tec->valueMin); return 1; } if (!tec->simulate) { if (!AIR_EXISTS(tec->bValue)) { biffAddf(TEN, "%s: b-value not set", me); return 1; } if (airEnumValCheck(tenEstimate1Method, tec->estimate1Method)) { biffAddf(TEN, "%s: estimation method not set", me); return 1; } if (tenEstimate1MethodMLE == tec->estimate1Method && !(AIR_EXISTS(tec->sigma) && (tec->sigma >= 0.0)) ) { biffAddf(TEN, "%s: can't do %s estim w/out non-negative sigma set", me, airEnumStr(tenEstimate1Method, tenEstimate1MethodMLE)); return 1; } if (!(AIR_EXISTS(tec->dwiConfThresh) && AIR_EXISTS(tec->dwiConfSoft))) { biffAddf(TEN, "%s: not both threshold (%g) and softness (%g) exist", me, tec->dwiConfThresh, tec->dwiConfSoft); return 1; } } if (!( tec->_ngrad || tec->_nbmat )) { biffAddf(TEN, "%s: need to set either gradients or B-matrices", me); return 1; } return 0; } /* ** allNum includes the skipped images ** dwiNum does not include the skipped images */ int _tenEstimateNumUpdate(tenEstimateContext *tec) { static const char me[]="_tenEstimateNumUpdate"; unsigned int newAllNum, newDwiNum, allIdx, skipListIdx, skipIdx, skipDo, skipNotNum; double (*lup)(const void *, size_t), gg[3], bb[6]; if (tec->flag[flagBInfo] || tec->flag[flagSkipSet]) { if (tec->_ngrad) { newAllNum = AIR_CAST(unsigned int, tec->_ngrad->axis[1].size); lup = nrrdDLookup[tec->_ngrad->type]; } else { newAllNum = AIR_CAST(unsigned int, tec->_nbmat->axis[1].size); lup = nrrdDLookup[tec->_nbmat->type]; } if (tec->allNum != newAllNum) { tec->allNum = newAllNum; tec->flag[flagAllNum] = AIR_TRUE; } /* HEY: this should probably be its own update function, but its very convenient to allocate these allNum-length arrays here, immediately */ airFree(tec->skipLut); tec->skipLut = AIR_CAST(unsigned char *, calloc(tec->allNum, sizeof(unsigned char))); airFree(tec->bnorm); tec->bnorm = AIR_CAST(double *, calloc(tec->allNum, sizeof(double))); if (!(tec->skipLut && tec->bnorm)) { biffAddf(TEN, "%s: couldn't allocate skipLut, bnorm vectors length %u\n", me, tec->allNum); return 1; } for (skipListIdx=0; skipListIdxskipListArr->len; skipListIdx++) { skipIdx = tec->skipList[0 + 2*skipListIdx]; skipDo = tec->skipList[1 + 2*skipListIdx]; if (!(skipIdx < tec->allNum)) { biffAddf(TEN, "%s: skipList entry %u value index %u not < # vals %u", me, skipListIdx, skipIdx, tec->allNum); return 1; } tec->skipLut[skipIdx] = skipDo; } skipNotNum = 0; for (skipIdx=0; skipIdxallNum; skipIdx++) { skipNotNum += !tec->skipLut[skipIdx]; } if (!(skipNotNum >= 7 )) { biffAddf(TEN, "%s: number of not-skipped (used) values %u < minimum 7", me, skipNotNum); return 1; } newDwiNum = 0; for (allIdx=0; allIdxallNum; allIdx++) { if (tec->skipLut[allIdx]) { tec->bnorm[allIdx] = AIR_NAN; } else { if (tec->_ngrad) { gg[0] = lup(tec->_ngrad->data, 0 + 3*allIdx); gg[1] = lup(tec->_ngrad->data, 1 + 3*allIdx); gg[2] = lup(tec->_ngrad->data, 2 + 3*allIdx); bb[0] = gg[0]*gg[0]; bb[1] = gg[1]*gg[0]; bb[2] = gg[2]*gg[0]; bb[3] = gg[1]*gg[1]; bb[4] = gg[2]*gg[1]; bb[5] = gg[2]*gg[2]; } else { bb[0] = lup(tec->_nbmat->data, 0 + 6*allIdx); bb[1] = lup(tec->_nbmat->data, 1 + 6*allIdx); bb[2] = lup(tec->_nbmat->data, 2 + 6*allIdx); bb[3] = lup(tec->_nbmat->data, 3 + 6*allIdx); bb[4] = lup(tec->_nbmat->data, 4 + 6*allIdx); bb[5] = lup(tec->_nbmat->data, 5 + 6*allIdx); } tec->bnorm[allIdx] = sqrt(bb[0]*bb[0] + 2*bb[1]*bb[1] + 2*bb[2]*bb[2] + bb[3]*bb[3] + 2*bb[4]*bb[4] + bb[5]*bb[5]); if (tec->estimateB0) { ++newDwiNum; } else { newDwiNum += (0.0 != tec->bnorm[allIdx]); } } } if (tec->dwiNum != newDwiNum) { tec->dwiNum = newDwiNum; tec->flag[flagDwiNum] = AIR_TRUE; } if (!tec->estimateB0 && (tec->allNum == tec->dwiNum)) { biffAddf(TEN, "%s: don't want to estimate B0, but all values are DW", me); return 1; } } return 0; } int _tenEstimateAllAllocUpdate(tenEstimateContext *tec) { static const char me[]="_tenEstimateAllAllocUpdate"; if (tec->flag[flagAllNum]) { airFree(tec->all); airFree(tec->allTmp); tec->all = AIR_CAST(double *, calloc(tec->allNum, sizeof(double))); tec->allTmp = AIR_CAST(double *, calloc(tec->allNum, sizeof(double))); if (!( tec->all && tec->allTmp )) { biffAddf(TEN, "%s: couldn't allocate \"all\" arrays (length %u)", me, tec->allNum); return 1; } tec->flag[flagAllAlloc] = AIR_TRUE; } return 0; } int _tenEstimateDwiAllocUpdate(tenEstimateContext *tec) { static const char me[]="_tenEstimateDwiAllocUpdate"; size_t size[2]; int E; if (tec->flag[flagDwiNum]) { airFree(tec->dwi); airFree(tec->dwiTmp); tec->dwi = AIR_CAST(double *, calloc(tec->dwiNum, sizeof(double))); tec->dwiTmp = AIR_CAST(double *, calloc(tec->dwiNum, sizeof(double))); if (!(tec->dwi && tec->dwiTmp)) { biffAddf(TEN, "%s: couldn't allocate DWI arrays (length %u)", me, tec->dwiNum); return 1; } E = 0; if (!E) size[0] = (tec->estimateB0 ? 7 : 6); if (!E) size[1] = tec->dwiNum; if (!E) E |= nrrdMaybeAlloc_nva(tec->nbmat, nrrdTypeDouble, 2, size); if (!E) size[0] = tec->dwiNum; if (!E) size[1] = tec->dwiNum; if (!E) E |= nrrdMaybeAlloc_nva(tec->nwght, nrrdTypeDouble, 2, size); if (E) { biffMovef(TEN, NRRD, "%s: couldn't allocate dwi nrrds", me); return 1; } /* nrrdSave("0-nbmat.txt", tec->nbmat, NULL); */ tec->flag[flagDwiAlloc] = AIR_TRUE; } return 0; } int _tenEstimateAllSetUpdate(tenEstimateContext *tec) { /* static const char me[]="_tenEstimateAllSetUpdate"; */ /* unsigned int skipListIdx, skipIdx, skip, dwiIdx */; if (tec->flag[flagAllAlloc] || tec->flag[flagDwiNum]) { } return 0; } int _tenEstimateDwiSetUpdate(tenEstimateContext *tec) { /* static const char me[]="_tenEstimateDwiSetUpdate"; */ double (*lup)(const void *, size_t I), gg[3], *bmat; unsigned int allIdx, dwiIdx, bmIdx; if (tec->flag[flagAllNum] || tec->flag[flagDwiAlloc]) { if (tec->_ngrad) { lup = nrrdDLookup[tec->_ngrad->type]; } else { lup = nrrdDLookup[tec->_nbmat->type]; } dwiIdx = 0; bmat = AIR_CAST(double*, tec->nbmat->data); for (allIdx=0; allIdxallNum; allIdx++) { if (!tec->skipLut[allIdx] && (tec->estimateB0 || tec->bnorm[allIdx])) { if (tec->_ngrad) { gg[0] = lup(tec->_ngrad->data, 0 + 3*allIdx); gg[1] = lup(tec->_ngrad->data, 1 + 3*allIdx); gg[2] = lup(tec->_ngrad->data, 2 + 3*allIdx); bmat[0] = gg[0]*gg[0]; bmat[1] = gg[1]*gg[0]; bmat[2] = gg[2]*gg[0]; bmat[3] = gg[1]*gg[1]; bmat[4] = gg[2]*gg[1]; bmat[5] = gg[2]*gg[2]; } else { for (bmIdx=0; bmIdx<6; bmIdx++) { bmat[bmIdx] = lup(tec->_nbmat->data, bmIdx + 6*allIdx); } } bmat[1] *= 2.0; bmat[2] *= 2.0; bmat[4] *= 2.0; if (tec->estimateB0) { bmat[6] = -1; } bmat += tec->nbmat->axis[0].size; dwiIdx++; } } } return 0; } int _tenEstimateWghtUpdate(tenEstimateContext *tec) { /* static const char me[]="_tenEstimateWghtUpdate"; */ unsigned int dwiIdx; double *wght; wght = AIR_CAST(double *, tec->nwght->data); if (tec->flag[flagDwiAlloc] || tec->flag[flagEstimateMethod]) { /* HEY: this is only useful for linear LS, no? */ for (dwiIdx=0; dwiIdxdwiNum; dwiIdx++) { wght[dwiIdx + tec->dwiNum*dwiIdx] = 1.0; } tec->flag[flagEstimateMethod] = AIR_FALSE; tec->flag[flagWght] = AIR_TRUE; } return 0; } int _tenEstimateEmatUpdate(tenEstimateContext *tec) { static const char me[]="tenEstimateEmatUpdate"; if (tec->flag[flagDwiSet] || tec->flag[flagWght]) { if (!tec->simulate) { /* HEY: ignores weights! */ if (ell_Nm_pseudo_inv(tec->nemat, tec->nbmat)) { biffMovef(TEN, ELL, "%s: trouble pseudo-inverting %ux%u B-matrix", me, AIR_CAST(unsigned int, tec->nbmat->axis[1].size), AIR_CAST(unsigned int, tec->nbmat->axis[0].size)); return 1; } } tec->flag[flagDwiSet] = AIR_FALSE; tec->flag[flagWght] = AIR_FALSE; } return 0; } int tenEstimateUpdate(tenEstimateContext *tec) { static const char me[]="tenEstimateUpdate"; int EE; EE = 0; if (!EE) EE |= _tenEstimateCheck(tec); if (!EE) EE |= _tenEstimateNumUpdate(tec); if (!EE) EE |= _tenEstimateAllAllocUpdate(tec); if (!EE) EE |= _tenEstimateDwiAllocUpdate(tec); if (!EE) EE |= _tenEstimateAllSetUpdate(tec); if (!EE) EE |= _tenEstimateDwiSetUpdate(tec); if (!EE) EE |= _tenEstimateWghtUpdate(tec); if (!EE) EE |= _tenEstimateEmatUpdate(tec); if (EE) { biffAddf(TEN, "%s: problem updating", me); return 1; } return 0; } /* ** from given tec->all_f or tec->all_d (whichever is non-NULL), sets: ** tec->all[], ** tec->dwi[] ** tec->knownB0, if !tec->estimateB0, ** tec->mdwi, ** tec->conf (from tec->mdwi) */ void _tenEstimateValuesSet(tenEstimateContext *tec) { unsigned int allIdx, dwiIdx, B0Num; double normSum; if (!tec->estimateB0) { tec->knownB0 = 0; } else { tec->knownB0 = AIR_NAN; } normSum = 0; tec->mdwi = 0; B0Num = 0; dwiIdx = 0; for (allIdx=0; allIdxallNum; allIdx++) { if (!tec->skipLut[allIdx]) { tec->all[allIdx] = (tec->all_f ? tec->all_f[allIdx] : tec->all_d[allIdx]); tec->mdwi += tec->bnorm[allIdx]*tec->all[allIdx]; normSum += tec->bnorm[allIdx]; if (tec->estimateB0 || tec->bnorm[allIdx]) { tec->dwi[dwiIdx++] = tec->all[allIdx]; } else { tec->knownB0 += tec->all[allIdx]; B0Num += 1; } } } if (!tec->estimateB0) { tec->knownB0 /= B0Num; } tec->mdwi /= normSum; if (tec->dwiConfSoft > 0) { tec->conf = AIR_AFFINE(-1, airErf((tec->mdwi - tec->dwiConfThresh) /tec->dwiConfSoft), 1, 0, 1); } else { tec->conf = tec->mdwi > tec->dwiConfThresh; } return; } /* ** ASSUMES THAT dwiTmp[] has been stuff with all values simulated from model */ double _tenEstimateErrorDwi(tenEstimateContext *tec) { unsigned int dwiIdx; double err, diff; err = 0; for (dwiIdx=0; dwiIdxdwiNum; dwiIdx++) { diff = tec->dwi[dwiIdx] - tec->dwiTmp[dwiIdx]; /* avg = (tec->dwi[dwiIdx] + tec->dwiTmp[dwiIdx])/2; avg = AIR_ABS(avg); if (avg) { err += diff*diff/(avg*avg); } */ err += diff*diff; } err /= tec->dwiNum; return sqrt(err); } double _tenEstimateErrorLogDwi(tenEstimateContext *tec) { unsigned int dwiIdx; double err, diff; err = 0; for (dwiIdx=0; dwiIdxdwiNum; dwiIdx++) { diff = (log(AIR_MAX(tec->valueMin, tec->dwi[dwiIdx])) - log(AIR_MAX(tec->valueMin, tec->dwiTmp[dwiIdx]))); err += diff*diff; } err /= tec->dwiNum; return sqrt(err); } /* ** sets: ** tec->dwiTmp[] ** and sets of all of them, regardless of estimateB0 */ int _tenEstimate1TensorSimulateSingle(tenEstimateContext *tec, double sigma, double bValue, double B0, const double ten[7]) { static const char me[]="_tenEstimate1TensorSimulateSingle"; unsigned int dwiIdx, jj; double nr, ni, vv; const double *bmat; if (!( ten && ten )) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( AIR_EXISTS(sigma) && sigma >= 0 && AIR_EXISTS(bValue) && AIR_EXISTS(B0) )) { biffAddf(TEN, "%s: got bad args: sigma %g, bValue %g, B0 %g\n", me, sigma, bValue, B0); return 1; } bmat = AIR_CAST(const double *, tec->nbmat->data); for (dwiIdx=0; dwiIdxdwiNum; dwiIdx++) { vv = 0; for (jj=0; jj<6; jj++) { vv += bmat[jj]*ten[1+jj]; } /* fprintf(stderr, "!%s: sigma = %g, bValue = %g, B0 = %g\n", me, sigma, bValue, B0); fprintf(stderr, "!%s[%u]: bmat=(%g %g %g %g %g %g)." "ten=(%g %g %g %g %g %g)\n", me, dwiIdx, bmat[0], bmat[1], bmat[2], bmat[3], bmat[4], bmat[5], ten[1], ten[2], ten[3], ten[4], ten[5], ten[6]); fprintf(stderr, "!%s: %g * exp(- %g * %g) = %g * exp(%g) = " "%g * %g = ... \n", me, B0, bValue, vv, B0, -bValue*vv, B0, exp(-bValue*vv)); */ /* need AIR_MAX(0, vv) because B:D might be negative */ vv = B0*exp(-bValue*AIR_MAX(0, vv)); /* fprintf(stderr, "!%s: vv = %g\n", me, vv); */ if (sigma > 0) { airNormalRand(&nr, &ni); nr *= sigma; ni *= sigma; vv = sqrt((vv+nr)*(vv+nr) + ni*ni); } tec->dwiTmp[dwiIdx] = vv; if (!AIR_EXISTS(tec->dwiTmp[dwiIdx])) { fprintf(stderr, "**********************************\n"); } /* if (tec->verbose) { fprintf(stderr, "%s: dwi[%u] = %g\n", me, dwiIdx, tec->dwiTmp[dwiIdx]); } */ bmat += tec->nbmat->axis[0].size; } return 0; } int tenEstimate1TensorSimulateSingle_f(tenEstimateContext *tec, float *simval, float sigma, float bValue, float B0, const float _ten[7]) { static const char me[]="tenEstimate1TensorSimulateSingle_f"; unsigned int allIdx, dwiIdx; double ten[7]; if (!(tec && simval && _ten)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } TEN_T_COPY(ten, _ten); if (_tenEstimate1TensorSimulateSingle(tec, sigma, bValue, B0, ten)) { biffAddf(TEN, "%s: ", me); return 1; } dwiIdx = 0; for (allIdx=0; allIdxallNum; allIdx++) { if (tec->estimateB0 || tec->bnorm[allIdx]) { simval[allIdx] = AIR_CAST(float, tec->dwiTmp[dwiIdx++]); } else { simval[allIdx] = B0; } } return 0; } int tenEstimate1TensorSimulateSingle_d(tenEstimateContext *tec, double *simval, double sigma, double bValue, double B0, const double ten[7]) { static const char me[]="tenEstimate1TensorSimulateSingle_d"; unsigned int allIdx, dwiIdx; if (!(tec && simval && ten)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( AIR_EXISTS(sigma) && sigma >= 0 && AIR_EXISTS(bValue) && AIR_EXISTS(B0) )) { biffAddf(TEN, "%s: got bad bargs sigma %g, bValue %g, B0 %g\n", me, sigma, bValue, B0); return 1; } if (_tenEstimate1TensorSimulateSingle(tec, sigma, bValue, B0, ten)) { biffAddf(TEN, "%s: ", me); return 1; } dwiIdx = 0; for (allIdx=0; allIdxallNum; allIdx++) { if (tec->estimateB0 || tec->bnorm[allIdx]) { simval[allIdx] = tec->dwiTmp[dwiIdx++]; } else { simval[allIdx] = B0; } } return 0; } int tenEstimate1TensorSimulateVolume(tenEstimateContext *tec, Nrrd *ndwi, double sigma, double bValue, const Nrrd *nB0, const Nrrd *nten, int outType, int keyValueSet) { static const char me[]="tenEstimate1TensorSimulateVolume"; size_t sizeTen, sizeX, sizeY, sizeZ, NN, II; double (*tlup)(const void *, size_t), (*blup)(const void *, size_t), (*lup)(const void *, size_t), ten_d[7], *dwi_d, B0; float *dwi_f, ten_f[7]; unsigned int tt, allIdx; int axmap[4], E; airArray *mop; char stmp[3][AIR_STRLEN_SMALL]; if (!(tec && ndwi && nB0 && nten)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } /* this should have been done by update(), but why not */ if (_tenEstimateCheck(tec)) { biffAddf(TEN, "%s: problem in given context", me); return 1; } if (!(AIR_EXISTS(sigma) && sigma >= 0.0 && AIR_EXISTS(bValue) && bValue >= 0.0)) { biffAddf(TEN, "%s: got invalid sigma (%g) or bValue (%g)\n", me, sigma, bValue); return 1; } if (airEnumValCheck(nrrdType, outType)) { biffAddf(TEN, "%s: requested output type %d not valid", me, outType); return 1; } if (!( nrrdTypeFloat == outType || nrrdTypeDouble == outType )) { biffAddf(TEN, "%s: requested output type (%s) not %s or %s", me, airEnumStr(nrrdType, outType), airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeDouble)); return 1; } mop = airMopNew(); sizeTen = nrrdKindSize(nrrdKind3DMaskedSymMatrix); sizeX = nten->axis[1].size; sizeY = nten->axis[2].size; sizeZ = nten->axis[3].size; if (!(3 == nB0->dim && sizeX == nB0->axis[0].size && sizeY == nB0->axis[1].size && sizeZ == nB0->axis[2].size)) { biffAddf(TEN, "%s: given B0 (%u-D) volume not 3-D %sx%sx%s", me, nB0->dim, airSprintSize_t(stmp[0], sizeX), airSprintSize_t(stmp[1], sizeY), airSprintSize_t(stmp[2], sizeZ)); return 1; } if (nrrdMaybeAlloc_va(ndwi, outType, 4, AIR_CAST(size_t, tec->allNum), sizeX, sizeY, sizeZ)) { biffMovef(TEN, NRRD, "%s: couldn't allocate DWI output", me); airMopError(mop); return 1; } NN = sizeX * sizeY * sizeZ; tlup = nrrdDLookup[nten->type]; blup = nrrdDLookup[nB0->type]; dwi_d = AIR_CAST(double *, ndwi->data); dwi_f = AIR_CAST(float *, ndwi->data); E = 0; for (II=0; !E && IIdata, II); if (nrrdTypeDouble == outType) { for (tt=0; tt<7; tt++) { ten_d[tt] = tlup(nten->data, tt + sizeTen*II); } E = tenEstimate1TensorSimulateSingle_d(tec, dwi_d, sigma, bValue, B0, ten_d); dwi_d += tec->allNum; } else { for (tt=0; tt<7; tt++) { ten_f[tt] = AIR_CAST(float, tlup(nten->data, tt + sizeTen*II)); } E = tenEstimate1TensorSimulateSingle_f(tec, dwi_f, AIR_CAST(float, sigma), AIR_CAST(float, bValue), AIR_CAST(float, B0), ten_f); dwi_f += tec->allNum; } if (E) { biffAddf(TEN, "%s: failed at sample %s", me, airSprintSize_t(stmp[0], II)); airMopError(mop); return 1; } } ELL_4V_SET(axmap, -1, 1, 2, 3); nrrdAxisInfoCopy(ndwi, nten, axmap, NRRD_AXIS_INFO_NONE); ndwi->axis[0].kind = nrrdKindList; if (nrrdBasicInfoCopy(ndwi, nten, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { biffMovef(TEN, NRRD, "%s:", me); airMopError(mop); return 1; } if (keyValueSet) { char keystr[AIR_STRLEN_MED], valstr[AIR_STRLEN_MED]; nrrdKeyValueAdd(ndwi, tenDWMRIModalityKey, tenDWMRIModalityVal); sprintf(valstr, "%g", bValue); nrrdKeyValueAdd(ndwi, tenDWMRIBValueKey, valstr); if (tec->_ngrad) { lup = nrrdDLookup[tec->_ngrad->type]; for (allIdx=0; allIdxallNum; allIdx++) { sprintf(keystr, tenDWMRIGradKeyFmt, allIdx); sprintf(valstr, "%g %g %g", lup(tec->_ngrad->data, 0 + 3*allIdx), lup(tec->_ngrad->data, 1 + 3*allIdx), lup(tec->_ngrad->data, 2 + 3*allIdx)); nrrdKeyValueAdd(ndwi, keystr, valstr); } } else { lup = nrrdDLookup[tec->_nbmat->type]; for (allIdx=0; allIdxallNum; allIdx++) { sprintf(keystr, tenDWMRIBmatKeyFmt, allIdx); sprintf(valstr, "%g %g %g %g %g %g", lup(tec->_nbmat->data, 0 + 6*allIdx), lup(tec->_nbmat->data, 1 + 6*allIdx), lup(tec->_nbmat->data, 2 + 6*allIdx), lup(tec->_nbmat->data, 3 + 6*allIdx), lup(tec->_nbmat->data, 4 + 6*allIdx), lup(tec->_nbmat->data, 5 + 6*allIdx)); nrrdKeyValueAdd(ndwi, keystr, valstr); } } } airMopOkay(mop); return 0; } /* ** sets: ** tec->ten[1..6] ** tec->B0, if tec->estimateB0 */ int _tenEstimate1Tensor_LLS(tenEstimateContext *tec) { static const char me[]="_tenEstimate1Tensor_LLS"; double *emat, tmp, logB0; unsigned int ii, jj; emat = AIR_CAST(double *, tec->nemat->data); if (tec->verbose) { fprintf(stderr, "!%s: estimateB0 = %d\n", me, tec->estimateB0); } if (tec->estimateB0) { for (ii=0; iiallNum; ii++) { tmp = AIR_MAX(tec->valueMin, tec->all[ii]); tec->allTmp[ii] = -log(tmp)/(tec->bValue); } for (jj=0; jj<7; jj++) { tmp = 0; for (ii=0; iiallNum; ii++) { tmp += emat[ii + tec->allNum*jj]*tec->allTmp[ii]; } if (jj < 6) { tec->ten[1+jj] = tmp; if (!AIR_EXISTS(tmp)) { biffAddf(TEN, "%s: estimated non-existent tensor coef (%u) %g", me, jj, tmp); return 1; } } else { /* we're on seventh row, for finding B0 */ tec->estimatedB0 = exp(tec->bValue*tmp); tec->estimatedB0 = AIR_MIN(FLT_MAX, tec->estimatedB0); if (!AIR_EXISTS(tec->estimatedB0)) { biffAddf(TEN, "%s: estimated non-existent B0 %g (b=%g, tmp=%g)", me, tec->estimatedB0, tec->bValue, tmp); return 1; } } } } else { logB0 = log(AIR_MAX(tec->valueMin, tec->knownB0)); for (ii=0; iidwiNum; ii++) { tmp = AIR_MAX(tec->valueMin, tec->dwi[ii]); tec->dwiTmp[ii] = (logB0 - log(tmp))/(tec->bValue); } for (jj=0; jj<6; jj++) { tmp = 0; for (ii=0; iidwiNum; ii++) { tmp += emat[ii + tec->dwiNum*jj]*tec->dwiTmp[ii]; if (tec->verbose > 5) { fprintf(stderr, "%s: emat[(%u,%u)=%u]*dwi[%u] = %g*%g --> %g\n", me, ii, jj, ii + tec->dwiNum*jj, ii, emat[ii + tec->dwiNum*jj], tec->dwiTmp[ii], tmp); } } tec->ten[1+jj] = tmp; } } return 0; } int _tenEstimate1Tensor_WLS(tenEstimateContext *tec) { static const char me[]="_tenEstimate1Tensor_WLS"; unsigned int dwiIdx, iter; double *wght, dwi, sum; if (!tec) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } wght = AIR_CAST(double *, tec->nwght->data); sum = 0; for (dwiIdx=0; dwiIdxdwiNum; dwiIdx++) { dwi = tec->dwi[dwiIdx]; dwi = AIR_MAX(tec->valueMin, dwi); sum += dwi*dwi; } for (dwiIdx=0; dwiIdxdwiNum; dwiIdx++) { dwi = tec->dwi[dwiIdx]; dwi = AIR_MAX(tec->valueMin, dwi); wght[dwiIdx + tec->dwiNum*dwiIdx] = dwi*dwi/sum; } if (ell_Nm_wght_pseudo_inv(tec->nemat, tec->nbmat, tec->nwght)) { biffMovef(TEN, ELL, "%s(1): trouble wght-pseudo-inverting %ux%u B-matrix", me, AIR_CAST(unsigned int, tec->nbmat->axis[1].size), AIR_CAST(unsigned int, tec->nbmat->axis[0].size)); return 1; } /* nrrdSave("wght.txt", tec->nwght, NULL); nrrdSave("bmat.txt", tec->nbmat, NULL); nrrdSave("emat.txt", tec->nemat, NULL); */ if (_tenEstimate1Tensor_LLS(tec)) { biffAddf(TEN, "%s: initial weighted LLS failed", me); return 1; } for (iter=0; iterWLSIterNum; iter++) { /* fprintf(stderr, "!%s: bValue = %g, B0 = %g, ten = %g %g %g %g %g %g\n", me, tec->bValue, (tec->estimateB0 ? tec->estimatedB0 : tec->knownB0), tec->ten[1], tec->ten[2], tec->ten[3], tec->ten[4], tec->ten[5], tec->ten[6]); */ if (_tenEstimate1TensorSimulateSingle(tec, 0.0, tec->bValue, (tec->estimateB0 ? tec->estimatedB0 : tec->knownB0), tec->ten)) { biffAddf(TEN, "%s: iter %u", me, iter); return 1; } for (dwiIdx=0; dwiIdxdwiNum; dwiIdx++) { dwi = tec->dwiTmp[dwiIdx]; if (!AIR_EXISTS(dwi)) { biffAddf(TEN, "%s: bad simulated dwi[%u] == %g (iter %u)", me, dwiIdx, dwi, iter); return 1; } wght[dwiIdx + tec->dwiNum*dwiIdx] = AIR_MAX(FLT_MIN, dwi*dwi); } if (ell_Nm_wght_pseudo_inv(tec->nemat, tec->nbmat, tec->nwght)) { biffMovef(TEN, ELL, "%s(2): trouble w/ %ux%u B-matrix (iter %u)", me, AIR_CAST(unsigned int, tec->nbmat->axis[1].size), AIR_CAST(unsigned int, tec->nbmat->axis[0].size), iter); return 1; } _tenEstimate1Tensor_LLS(tec); } return 0; } int _tenEstimate1TensorGradient(tenEstimateContext *tec, double *gradB0P, double gradTen[7], double B0, double ten[7], double epsilon, int (*gradientCB)(tenEstimateContext *tec, double *gradB0P, double gTen[7], double B0, double ten[7]), int (*badnessCB)(tenEstimateContext *tec, double *badP, double B0, double ten[7])) { static const char me[]="_tenEstimate1TensorGradper"; double forwTen[7], backTen[7], forwBad, backBad; unsigned int ti; if (!( tec && gradB0P && gradTen && badnessCB && ten)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (gradientCB) { if (gradientCB(tec, gradB0P, gradTen, B0, ten)) { biffAddf(TEN, "%s: problem with grad callback", me); return 1; } } else { /* we find gradient manually */ gradTen[0] = 0; for (ti=0; ti<6; ti++) { TEN_T_COPY(forwTen, ten); TEN_T_COPY(backTen, ten); forwTen[ti+1] += epsilon; backTen[ti+1] -= epsilon; if (badnessCB(tec, &forwBad, B0, forwTen) || badnessCB(tec, &backBad, B0, backTen)) { biffAddf(TEN, "%s: trouble at ti=%u", me, ti); return 1; } gradTen[ti+1] = (forwBad - backBad)/(2*epsilon); } } return 0; } int _tenEstimate1TensorDescent(tenEstimateContext *tec, int (*gradientCB)(tenEstimateContext *tec, double *gradB0, double gradTen[7], double B0, double ten[7]), int (*badnessCB)(tenEstimateContext *tec, double *badP, double B0, double ten[7])) { static const char me[]="_tenEstimate1TensorDescent"; double currB0, lastB0, currTen[7], lastTen[7], gradB0=AIR_NAN, gradTen[7], epsilon, stepSize, badInit, bad, badDelta, stepSizeMin = 0.00000000001, badLast; unsigned int iter, iterMax = 100000; /* start with WLS fit since its probably close */ _tenEstimate1Tensor_WLS(tec); if (tec->verbose) { fprintf(stderr, "%s: WLS gave %g %g %g %g %g %g\n", me, tec->ten[1], tec->ten[2], tec->ten[3], tec->ten[4], tec->ten[5], tec->ten[6]); } if (badnessCB(tec, &badInit, (tec->estimateB0 ? tec->estimatedB0 : tec->knownB0), tec->ten) || !AIR_EXISTS(badInit)) { biffAddf(TEN, "%s: problem getting initial bad", me); return 1; } if (tec->verbose) { fprintf(stderr, "\n%s: ________________________________________\n", me); fprintf(stderr, "%s: start: badInit = %g ---------------\n", me, badInit); } epsilon = 0.0000001; newepsilon: if (_tenEstimate1TensorGradient(tec, &gradB0, gradTen, (tec->estimateB0 ? tec->estimatedB0 : tec->knownB0), tec->ten, epsilon, gradientCB, badnessCB)) { biffAddf(TEN, "%s: problem getting initial gradient", me); return 1; } if (!( AIR_EXISTS(gradB0) || 0 <= TEN_T_NORM(gradTen) )) { biffAddf(TEN, "%s: got bad gradB0 %g or zero-norm tensor grad", me, gradB0); return 1; } if (tec->verbose) { fprintf(stderr, "%s: gradTen (%s) = %g %g %g %g %g %g\n", me, gradientCB ? "analytic" : "cent-diff", gradTen[1], gradTen[2], gradTen[3], gradTen[4], gradTen[5], gradTen[6]); } stepSize = 0.1; do { stepSize /= 10; TEN_T_SCALE_ADD2(currTen, 1.0, tec->ten, -stepSize, gradTen); if (tec->estimateB0) { currB0 = tec->estimatedB0 + -stepSize*gradB0; } else { currB0 = tec->knownB0; } if (badnessCB(tec, &bad, currB0, currTen) || !AIR_EXISTS(bad)) { biffAddf(TEN, "%s: problem getting badness for stepSize", me); return 1; } if (tec->verbose) { fprintf(stderr, "%s: ************ stepSize = %g --> bad = %g\n", me, stepSize, bad); } } while (bad > badInit && stepSize > stepSizeMin); if (stepSize <= stepSizeMin) { if (epsilon > FLT_MIN) { epsilon /= 10; fprintf(stderr, "%s: re-trying initial step w/ eps %g\n", me, epsilon); goto newepsilon; } else { biffAddf(TEN, "%s: never found a usable step size", me); return 1; } } else if (tec->verbose) { biffAddf(TEN, "%s: using step size %g\n", me, stepSize); } iter = 0; badLast = bad; do { iter++; TEN_T_COPY(lastTen, currTen); lastB0 = currB0; if (0 == (iter % 3)) { if (_tenEstimate1TensorGradient(tec, &gradB0, gradTen, currB0, currTen, stepSize/5, gradientCB, badnessCB) || !AIR_EXISTS(gradB0)) { biffAddf(TEN, "%s[%u]: problem getting iter grad", me, iter); return 1; } } TEN_T_SCALE_INCR(currTen, -stepSize, gradTen); if (tec->estimateB0) { currB0 -= stepSize*gradB0; } if (badnessCB(tec, &bad, currB0, currTen) || !AIR_EXISTS(bad)) { biffAddf(TEN, "%s[%u]: problem getting badness during grad", me, iter); return 1; } if (tec->verbose) { fprintf(stderr, "%s: %u bad = %g\n", me, iter, bad); } badDelta = bad - badLast; badLast = bad; if (badDelta > 0) { stepSize /= 10; if (tec->verbose) { fprintf(stderr, "%s: badDelta %g > 0 ---> stepSize = %g\n", me, badDelta, stepSize); } badDelta = -1; /* bogus improvement for loop continuation */ TEN_T_COPY(currTen, lastTen); currB0 = lastB0; } } while (iter < iterMax && (iter < 2 || badDelta < -0.00005)); if (iter >= iterMax) { biffAddf(TEN, "%s: didn't converge after %u iterations", me, iter); return 1; } if (tec->verbose) { fprintf(stderr, "%s: finished\n", me); } ELL_6V_COPY(tec->ten+1, currTen+1); tec->estimatedB0 = currB0; return 0; } int _tenEstimate1Tensor_GradientNLS(tenEstimateContext *tec, double *gradB0P, double gradTen[7], double currB0, double currTen[7]) { static const char me[]="_tenEstimate1Tensor_GradientNLS"; double *bmat, dot, tmp, diff, scl; unsigned int dwiIdx; if (!(tec && gradB0P && gradTen && currTen)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } *gradB0P = 0; TEN_T_SET(gradTen, 0, 0, 0, 0, 0, 0, 0); bmat = AIR_CAST(double *, tec->nbmat->data); for (dwiIdx=0; dwiIdxdwiNum; dwiIdx++) { dot = ELL_6V_DOT(bmat, currTen+1); tmp = currB0*exp(-(tec->bValue)*dot); diff = tec->dwi[dwiIdx] - tmp; scl = 2*diff*tmp*(tec->bValue); ELL_6V_SCALE_INCR(gradTen+1, scl, bmat); bmat += tec->nbmat->axis[0].size; /* HEY: increment *gradB0P */ } ELL_6V_SCALE_INCR(gradTen+1, 1.0/tec->dwiNum, gradTen+1); return 0; } int _tenEstimate1Tensor_BadnessNLS(tenEstimateContext *tec, double *retP, double currB0, double currTen[7]) { static const char me[]="_tenEstimate1Tensor_BadnessNLS"; if (!(retP && tec)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (_tenEstimate1TensorSimulateSingle(tec, 0.0, tec->bValue, currB0, currTen)) { biffAddf(TEN, "%s: ", me); return 1; } if (tec->verbose > 2) { unsigned int di; fprintf(stderr, "%s: simdwi =", me); for (di=0; didwiNum; di++) { fprintf(stderr, " %g", tec->dwiTmp[di]); } fprintf(stderr, "\n"); } *retP = _tenEstimateErrorDwi(tec); if (tec->verbose > 2) { fprintf(stderr, "!%s: badness(%g, (%g) %g %g %g %g %g %g) = %g\n", me, currB0, currTen[0], currTen[1], currTen[2], currTen[3], currTen[4], currTen[5], currTen[6], *retP); } return 0; } int _tenEstimate1Tensor_NLS(tenEstimateContext *tec) { static const char me[]="_tenEstimate1Tensor_NLS"; if (_tenEstimate1TensorDescent(tec, NULL /* _tenEstimate1Tensor_GradientNLS */ , _tenEstimate1Tensor_BadnessNLS)) { biffAddf(TEN, "%s: ", me); return 1; } return 0; } int _tenEstimate1Tensor_GradientMLE(tenEstimateContext *tec, double *gradB0P, double gradTen[7], double currB0, double currTen[7]) { static const char me[]="_tenEstimate1Tensor_GradientMLE"; double *bmat, dot, barg, tmp, scl, dwi, sigma, bval; unsigned int dwiIdx; if (!(tec && gradB0P && gradTen && currTen)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tec->verbose) { fprintf(stderr, "%s grad (currTen = %g %g %g %g %g %g)\n", me, currTen[1], currTen[2], currTen[3], currTen[4], currTen[5], currTen[6]); } TEN_T_SET(gradTen, 0, 0, 0, 0, 0, 0, 0); *gradB0P = 0; sigma = tec->sigma; bval = tec->bValue; bmat = AIR_CAST(double *, tec->nbmat->data); for (dwiIdx=0; dwiIdxdwiNum; dwiIdx++) { dwi = tec->dwi[dwiIdx]; dot = ELL_6V_DOT(bmat, currTen+1); barg = exp(-bval*dot)*(dwi/sigma)*(currB0/sigma); tmp = (exp(bval*dot)/sigma)*dwi/airBesselI0(barg); if (tec->verbose) { fprintf(stderr, "%s[%u]: dot = %g, barg = %g, tmp = %g\n", me, dwiIdx, dot, barg, tmp); } if (tmp > DBL_MIN) { tmp = currB0/sigma - tmp*airBesselI1(barg); } else { tmp = currB0/sigma; } if (tec->verbose) { fprintf(stderr, " ---- tmp = %g\n", tmp); } scl = tmp*exp(-2*bval*dot)*bval*currB0/sigma; ELL_6V_SCALE_INCR(gradTen+1, scl, bmat); if (tec->verbose) { fprintf(stderr, "%s[%u]: bmat = %g %g %g %g %g %g\n", me, dwiIdx, bmat[0], bmat[1], bmat[2], bmat[3], bmat[4], bmat[5]); fprintf(stderr, "%s[%u]: scl = %g -> gradTen = %g %g %g %g %g %g\n", me, dwiIdx, scl, gradTen[1], gradTen[2], gradTen[3], gradTen[4], gradTen[5], gradTen[6]); } if (!AIR_EXISTS(scl)) { TEN_T_SET(gradTen, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); *gradB0P = AIR_NAN; biffAddf(TEN, "%s: scl = %g, very sorry", me, scl); return 1; } bmat += tec->nbmat->axis[0].size; /* HEY: increment gradB0 */ } ELL_6V_SCALE_INCR(gradTen+1, 1.0/tec->dwiNum, gradTen+1); if (tec->verbose) { fprintf(stderr, "%s: final gradTen = %g %g %g %g %g %g\n", me, gradTen[1], gradTen[2], gradTen[3], gradTen[4], gradTen[5], gradTen[6]); } return 0; } int _tenEstimate1Tensor_BadnessMLE(tenEstimateContext *tec, double *retP, double currB0, double curt[7]) { static const char me[]="_tenEstimate1Tensor_BadnessMLE"; unsigned int dwiIdx; double *bmat, sum, rice, logrice=0, mesdwi=0, simdwi=0, dot=0; int E; E = 0; sum = 0; bmat = AIR_CAST(double *, tec->nbmat->data); for (dwiIdx=0; !E && dwiIdxdwiNum; dwiIdx++) { dot = ELL_6V_DOT(bmat, curt+1); simdwi = currB0*exp(-(tec->bValue)*dot); mesdwi = tec->dwi[dwiIdx]; if (!E) E |= _tenRician(&rice, mesdwi, simdwi, tec->sigma); if (!E) E |= !AIR_EXISTS(rice); if (!E) logrice = log(rice + DBL_MIN); if (!E) sum += logrice; if (!E) E |= !AIR_EXISTS(sum); if (!E) bmat += tec->nbmat->axis[0].size; } if (E) { biffAddf(TEN, "%s[%u]: dot = (%g %g %g %g %g %g).(%g %g %g %g %g %g) = %g", me, dwiIdx, bmat[0], bmat[1], bmat[2], bmat[3], bmat[4], bmat[5], curt[1], curt[2], curt[3], curt[4], curt[5], curt[6], dot); biffAddf(TEN, "%s[%u]: simdwi = %g * exp(-%g * %g) = %g * exp(%g) " "= %g * %g = %g", me, dwiIdx, currB0, tec->bValue, dot, currB0, -(tec->bValue)*dot, currB0, exp(-(tec->bValue)*dot), currB0*exp(-(tec->bValue)*dot)); biffAddf(TEN, "%s[%u]: mesdwi = %g, simdwi = %g, sigma = %g", me, dwiIdx, mesdwi, simdwi, tec->sigma); biffAddf(TEN, "%s[%u]: rice = %g, logrice = %g, sum = %g", me, dwiIdx, rice, logrice, sum); *retP = AIR_NAN; return 1; } *retP = -sum/tec->dwiNum; return 0; } int _tenEstimate1Tensor_MLE(tenEstimateContext *tec) { static const char me[]="_tenEstimate1Tensor_MLE"; if (_tenEstimate1TensorDescent(tec, NULL, _tenEstimate1Tensor_BadnessMLE)) { biffAddf(TEN, "%s: ", me); return 1; } return 0; } /* ** sets: ** tec->ten[0] (from tec->conf) ** tec->time, if tec->recordTime ** tec->errorDwi, if tec->recordErrorDwi ** tec->errorLogDwi, if tec->recordErrorLogDwi ** tec->likelihoodDwi, if tec->recordLikelihoodDwi */ int _tenEstimate1TensorSingle(tenEstimateContext *tec) { static const char me[]="_tenEstimate1TensorSingle"; double time0, B0; int E; _tenEstimateOutputInit(tec); time0 = tec->recordTime ? airTime() : 0; _tenEstimateValuesSet(tec); tec->ten[0] = tec->conf; switch(tec->estimate1Method) { case tenEstimate1MethodLLS: E = _tenEstimate1Tensor_LLS(tec); break; case tenEstimate1MethodWLS: E = _tenEstimate1Tensor_WLS(tec); break; case tenEstimate1MethodNLS: E = _tenEstimate1Tensor_NLS(tec); break; case tenEstimate1MethodMLE: E = _tenEstimate1Tensor_MLE(tec); break; default: biffAddf(TEN, "%s: estimation method %d unimplemented", me, tec->estimate1Method); return 1; } tec->time = tec->recordTime ? airTime() - time0 : 0; if (tec->negEvalShift) { double eval[3]; tenEigensolve_d(eval, NULL, tec->ten); if (eval[2] < 0) { tec->ten[1] += -eval[2]; tec->ten[4] += -eval[2]; tec->ten[6] += -eval[2]; } } if (E) { TEN_T_SET(tec->ten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); if (tec->estimateB0) { tec->estimatedB0 = AIR_NAN; } biffAddf(TEN, "%s: estimation failed", me); return 1; } if (tec->recordErrorDwi || tec->recordErrorLogDwi) { B0 = tec->estimateB0 ? tec->estimatedB0 : tec->knownB0; if (_tenEstimate1TensorSimulateSingle(tec, 0.0, tec->bValue, B0, tec->ten)) { biffAddf(TEN, "%s: simulation failed", me); return 1; } if (tec->recordErrorDwi) { tec->errorDwi = _tenEstimateErrorDwi(tec); } if (tec->recordErrorLogDwi) { tec->errorLogDwi = _tenEstimateErrorLogDwi(tec); } } /* HEY: record likelihood! */ return 0; } int tenEstimate1TensorSingle_f(tenEstimateContext *tec, float ten[7], const float *all) { static const char me[]="tenEstimate1TensorSingle_f"; if (!(tec && ten && all)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } tec->all_f = all; tec->all_d = NULL; /* fprintf(stderr, "!%s(%u): B0 = %g,%g\n", me, __LINE__, tec->knownB0, tec->estimatedB0); */ if (_tenEstimate1TensorSingle(tec)) { biffAddf(TEN, "%s: ", me); return 1; } /* fprintf(stderr, "!%s(%u): B0 = %g,%g\n", me, __LINE__, tec->knownB0, tec->estimatedB0); */ TEN_T_COPY_TT(ten, float, tec->ten); return 0; } int tenEstimate1TensorSingle_d(tenEstimateContext *tec, double ten[7], const double *all) { static const char me[]="tenEstimate1TensorSingle_d"; unsigned int ii; if (!(tec && ten && all)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } tec->all_f = NULL; tec->all_d = all; if (tec->verbose) { for (ii=0; iiallNum; ii++) { fprintf(stderr, "%s: dwi[%u] = %g\n", me, ii, tec->all_d ? tec->all_d[ii] : tec->all_f[ii]); } fprintf(stderr, "%s: will estimate by %d (%s) \n" " estimateB0 %d; valueMin %g\n", me, tec->estimate1Method, airEnumStr(tenEstimate1Method, tec->estimate1Method), tec->estimateB0, tec->valueMin); } if (_tenEstimate1TensorSingle(tec)) { biffAddf(TEN, "%s: ", me); return 1; } if (tec->verbose) { fprintf(stderr, "%s: ten = %g %g %g %g %g %g %g\n", me, tec->ten[0], tec->ten[1], tec->ten[2], tec->ten[3], tec->ten[4], tec->ten[5], tec->ten[6]); } TEN_T_COPY(ten, tec->ten); return 0; } int tenEstimate1TensorVolume4D(tenEstimateContext *tec, Nrrd *nten, Nrrd **nB0P, Nrrd **nterrP, const Nrrd *ndwi, int outType) { static const char me[]="tenEstimate1TensorVolume4D"; char doneStr[20]; size_t sizeTen, sizeX, sizeY, sizeZ, NN, II, tick; double *all, ten[7], (*lup)(const void *, size_t), (*ins)(void *v, size_t I, double d); unsigned int dd; airArray *mop; int axmap[4]; char stmp[AIR_STRLEN_SMALL]; #if 0 #define NUM 800 double val[NUM], minVal=0, maxVal=10, arg; unsigned int valIdx; Nrrd *nval; for (valIdx=0; valIdxdim && 7 <= ndwi->axis[0].size )) { biffAddf(TEN, "%s: DWI volume should be 4-D with axis 0 size >= 7", me); return 1; } if (tec->allNum != ndwi->axis[0].size) { biffAddf(TEN, "%s: from %s info, expected %u values per sample, " "but have %s in volume", me, tec->_ngrad ? "gradient" : "B-matrix", tec->allNum, airSprintSize_t(stmp, ndwi->axis[0].size)); return 1; } if (nrrdTypeBlock == ndwi->type) { biffAddf(TEN, "%s: DWI volume has non-scalar type %s", me, airEnumStr(nrrdType, ndwi->type)); return 1; } if (airEnumValCheck(nrrdType, outType)) { biffAddf(TEN, "%s: requested output type %d not valid", me, outType); return 1; } if (!( nrrdTypeFloat == outType || nrrdTypeDouble == outType )) { biffAddf(TEN, "%s: requested output type (%s) not %s or %s", me, airEnumStr(nrrdType, outType), airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeDouble)); return 1; } if (nterrP) { int recE, recEL, recLK; recE = !!(tec->recordErrorDwi); recEL = !!(tec->recordErrorLogDwi); recLK = !!(tec->recordLikelihoodDwi); if (1 != recE + recEL + recLK) { biffAddf(TEN, "%s: requested error volume but need exactly one of " "recordErrorDwi, recordErrorLogDwi, recordLikelihoodDwi " "to be set", me); return 1; } } mop = airMopNew(); sizeTen = nrrdKindSize(nrrdKind3DMaskedSymMatrix); sizeX = ndwi->axis[1].size; sizeY = ndwi->axis[2].size; sizeZ = ndwi->axis[3].size; all = AIR_CAST(double *, calloc(tec->allNum, sizeof(double))); if (!all) { biffAddf(TEN, "%s: couldn't allocate length %u array", me, tec->allNum); airMopError(mop); return 1; } airMopAdd(mop, all, airFree, airMopAlways); if (nrrdMaybeAlloc_va(nten, outType, 4, sizeTen, sizeX, sizeY, sizeZ)) { biffMovef(TEN, NRRD, "%s: couldn't allocate tensor output", me); airMopError(mop); return 1; } if (nB0P) { *nB0P = nrrdNew(); if (nrrdMaybeAlloc_va(*nB0P, outType, 3, sizeX, sizeY, sizeZ)) { biffMovef(TEN, NRRD, "%s: couldn't allocate B0 output", me); airMopError(mop); return 1; } airMopAdd(mop, *nB0P, (airMopper)nrrdNuke, airMopOnError); airMopAdd(mop, nB0P, (airMopper)airSetNull, airMopOnError); } if (nterrP) { *nterrP = nrrdNew(); if (nrrdMaybeAlloc_va(*nterrP, outType, 3, sizeX, sizeY, sizeZ) || nrrdBasicInfoCopy(*nterrP, ndwi, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_MEASUREMENTFRAME_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffMovef(TEN, NRRD, "%s: couldn't creatting fitting error output", me); airMopError(mop); return 1; } ELL_3V_SET(axmap, 1, 2, 3); nrrdAxisInfoCopy(*nterrP, ndwi, axmap, NRRD_AXIS_INFO_NONE); airMopAdd(mop, *nterrP, (airMopper)nrrdNuke, airMopOnError); airMopAdd(mop, nterrP, (airMopper)airSetNull, airMopOnError); } NN = sizeX * sizeY * sizeZ; lup = nrrdDLookup[ndwi->type]; ins = nrrdDInsert[outType]; if (tec->progress) { fprintf(stderr, "%s: ", me); } fflush(stderr); tick = NN / 200; tick = AIR_MAX(1, tick); for (II=0; IIprogress && 0 == II%tick) { fprintf(stderr, "%s", airDoneStr(0, II, NN-1, doneStr)); } for (dd=0; ddallNum; dd++) { all[dd] = lup(ndwi->data, dd + tec->allNum*II); } /* tec->verbose = 10*(II == 42509); */ if (tec->verbose) { fprintf(stderr, "!%s: hello; II=%u\n", me, AIR_CAST(unsigned int, II)); } if (tenEstimate1TensorSingle_d(tec, ten, all)) { biffAddf(TEN, "%s: failed at sample %s", me, airSprintSize_t(stmp, II)); airMopError(mop); return 1; } ins(nten->data, 0 + sizeTen*II, ten[0]); ins(nten->data, 1 + sizeTen*II, ten[1]); ins(nten->data, 2 + sizeTen*II, ten[2]); ins(nten->data, 3 + sizeTen*II, ten[3]); ins(nten->data, 4 + sizeTen*II, ten[4]); ins(nten->data, 5 + sizeTen*II, ten[5]); ins(nten->data, 6 + sizeTen*II, ten[6]); if (nB0P) { ins((*nB0P)->data, II, (tec->estimateB0 ? tec->estimatedB0 : tec->knownB0)); } if (nterrP) { /* this works because above we checked that only one of the tec->record* flags is set */ if (tec->recordErrorDwi) { ins((*nterrP)->data, II, tec->errorDwi); } else if (tec->recordErrorLogDwi) { ins((*nterrP)->data, II, tec->errorLogDwi); } else if (tec->recordLikelihoodDwi) { ins((*nterrP)->data, II, tec->likelihoodDwi); } } } if (tec->progress) { fprintf(stderr, "%s\n", airDoneStr(0, II, NN-1, doneStr)); } ELL_4V_SET(axmap, -1, 1, 2, 3); nrrdAxisInfoCopy(nten, ndwi, axmap, NRRD_AXIS_INFO_NONE); nten->axis[0].kind = nrrdKind3DMaskedSymMatrix; if (nrrdBasicInfoCopy(nten, ndwi, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { biffAddf(NRRD, "%s:", me); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/tendEvq.c0000664000175000017500000000732012165631065016743 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Quantize directions of diffusion" static const char *_tend_evqInfoL = (INFO ". Because VTK doesn't do multi-dimensional colormaps, we have to " "quantize directions of diffusion (usually just the principal eigenvector) " "in order to create the usual XYZ<->RGB coloring. Because " "eigenvector directions are poorly defined in regions of low " "anisotropy, the length of the vector (pre-quantization) is modulated " "by anisotropy, requiring the selection of some anisotropy metric."); int tend_evqMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int which, aniso, dontScaleByAniso; Nrrd *nin, *nout; char *outS; hestOptAdd(&hopt, "c", "evec index", airTypeInt, 1, 1, &which, "0", "Which eigenvector should be quantized: \"0\" for the " "direction of fastest diffusion (eigenvector associated " "with largest eigenvalue), \"1\" or \"2\" for other two " "eigenvectors (associated with middle and smallest eigenvalue)"); hestOptAdd(&hopt, "a", "aniso", airTypeEnum, 1, 1, &aniso, NULL, "Which anisotropy metric to scale the eigenvector " "with. " TEN_ANISO_DESC, NULL, tenAniso); hestOptAdd(&hopt, "ns", NULL, airTypeInt, 0, 0, &dontScaleByAniso, NULL, "Don't attenuate the color by anisotropy. By default (not " "using this option), regions with low or no anisotropy are " "very dark colors or black"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_evqInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenEvqVolume(nout, nin, which, aniso, !dontScaleByAniso)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble quantizing eigenvectors:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(evq, INFO); teem-1.11.0~svn6057/src/ten/tendExpand.c0000664000175000017500000001143612165631065017432 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Converts masked non-redundant tensor images to redundant" static const char *_tend_expandInfoL = (INFO ". For images of 3D tensors, this converts from a 7-value tensor " "starting with the confidence/mask value " "(conf, Dxx, Dxy, Dxz, Dyy, Dyz, Dzz) to " "a 9-value tensor with the full matrix " "(Dxx, Dxy, Dxz, Dxy, Dyy, Dyz, Dxz, Dyz, Dzz). " "This is set to all zeros when the confidence is below the given " "threshold. For images of 2D tensors, the conversion is from " "(conf, Dxx, Dxy, Dyy) to (Dxx, Dxy, Dxy, Dyy). " ); int tend_expandMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS; int orientRed, orientRedWithOrigin, mfRed; float scale, thresh; hestOptAdd(&hopt, "t", "thresh", airTypeFloat, 1, 1, &thresh, "0.5", "confidence level to threshold output tensors at. Should " "be between 0.0 and 1.0."); hestOptAdd(&hopt, "s", "scale", airTypeFloat, 1, 1, &scale, "1.0", "how to scale values before saving as 9-value tensor. Useful " "for visualization tools which assume certain characteristic " "ranges of eigenvalues"); hestOptAdd(&hopt, "unmf", NULL, airTypeInt, 0, 0, &mfRed, NULL, "apply and remove the measurement frame, if it exists"); hestOptAdd(&hopt, "ro", NULL, airTypeInt, 0, 0, &orientRed, NULL, "reduce general image orientation to axis-aligned spacings"); hestOptAdd(&hopt, "roo", NULL, airTypeInt, 0, 0, &orientRedWithOrigin, NULL, "reduce general image orientation to axis-aligned spacings, " "while also making some effort to set axis mins from " "space origin"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume, with 7 values per sample", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL, "output tensor volume, with the 9 matrix components per sample"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_expandInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (mfRed && 3 == nin->spaceDim && AIR_EXISTS(nin->measurementFrame[0][0])) { if (tenMeasurementFrameReduce(nin, nin)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble with measurement frame:\n%s\n", me, err); airMopError(mop); return 1; } } if (4 == nin->axis[0].size ? tenExpand2D(nout, nin, scale, thresh) : tenExpand(nout, nin, scale, thresh)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble expanding tensors:\n%s\n", me, err); airMopError(mop); return 1; } if (orientRedWithOrigin || orientRed) { if (nrrdOrientationReduce(nout, nout, orientRedWithOrigin ? AIR_TRUE : AIR_FALSE)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble unorienting:\n%s\n", me, err); airMopError(mop); return 1; } } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(expand, INFO); teem-1.11.0~svn6057/src/ten/aniso.c0000664000175000017500000011407512165631065016454 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" /* ** learned: don't take sqrt(FLT_EPSILON) and expect it to still be ** negligible */ /* ******** !!!! NOTE NOTE NOTE NOTE NOTE !!!! ******** ******** THIS CODE IS NOT REALLY MEANT TO BE EDITED BY HUMANS ******** (only GLK :) ******** ******** It is the worst possible example of the dangers of cut-and-paste ******** ******** !!!! NOTE NOTE NOTE NOTE NOTE !!!! */ float _tenAnisoEval_Conf_f(const float eval[3]) { AIR_UNUSED(eval); return 1.0; } double _tenAnisoEval_Conf_d(const double eval[3]) { AIR_UNUSED(eval); return 1.0; return 1.0; } float _tenAnisoTen_Conf_f(const float ten[7]) { return ten[0]; } double _tenAnisoTen_Conf_d(const double ten[7]) { return ten[0]; } float _tenAnisoEval_Cl1_f(const float eval[3]) { float sum = eval[0] + eval[1] + eval[2]; sum = AIR_MAX(0, sum); return sum ? (eval[0] - eval[1])/sum : 0.0f; } double _tenAnisoEval_Cl1_d(const double eval[3]) { double sum = eval[0] + eval[1] + eval[2]; sum = AIR_MAX(0, sum); return sum ? (eval[0] - eval[1])/sum : 0.0; } float _tenAnisoTen_Cl1_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Cl1_f(eval); } double _tenAnisoTen_Cl1_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Cl1_d(eval); } float _tenAnisoEval_Cp1_f(const float eval[3]) { float sum = eval[0] + eval[1] + eval[2]; sum = AIR_MAX(0, sum); return sum ? 2*(eval[1] - eval[2])/sum : 0.0f; } double _tenAnisoEval_Cp1_d(const double eval[3]) { double sum = eval[0] + eval[1] + eval[2]; sum = AIR_MAX(0, sum); return sum ? 2*(eval[1] - eval[2])/sum : 0.0; } float _tenAnisoTen_Cp1_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Cp1_f(eval); } double _tenAnisoTen_Cp1_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Cp1_d(eval); } float _tenAnisoEval_Ca1_f(const float eval[3]) { float sum = eval[0] + eval[1] + eval[2]; sum = AIR_MAX(0, sum); return sum ? (eval[0] + eval[1] - 2*eval[2])/sum : 0.0f; } double _tenAnisoEval_Ca1_d(const double eval[3]) { double sum = eval[0] + eval[1] + eval[2]; sum = AIR_MAX(0, sum); return sum ? (eval[0] + eval[1] - 2*eval[2])/sum : 0.0; } float _tenAnisoTen_Ca1_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Ca1_f(eval); } double _tenAnisoTen_Ca1_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Ca1_d(eval); } float _tenAnisoEval_Clpmin1_f(const float eval[3]) { float cl, cp, sum = eval[0] + eval[1] + eval[2]; sum = AIR_MAX(0, sum); cl = sum ? (eval[0] - eval[1])/sum : 0.0f; cp = sum ? 2*(eval[1] - eval[2])/sum : 0.0f; return AIR_MIN(cl, cp); } double _tenAnisoEval_Clpmin1_d(const double eval[3]) { double cl, cp, sum = eval[0] + eval[1] + eval[2]; sum = AIR_MAX(0, sum); cl = sum ? (eval[0] - eval[1])/sum : 0.0; cp = sum ? 2*(eval[1] - eval[2])/sum : 0.0; return AIR_MIN(cl, cp); } float _tenAnisoTen_Clpmin1_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Clpmin1_f(eval); } double _tenAnisoTen_Clpmin1_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Clpmin1_d(eval); } float _tenAnisoEval_Cs1_f(const float eval[3]) { float sum = eval[0] + eval[1] + eval[2]; sum = AIR_MAX(0, sum); return sum ? 3*eval[2]/sum : 0.0f; } double _tenAnisoEval_Cs1_d(const double eval[3]) { double sum = eval[0] + eval[1] + eval[2]; sum = AIR_MAX(0, sum); return sum ? 3*eval[2]/sum : 0.0; } float _tenAnisoTen_Cs1_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Cs1_f(eval); } double _tenAnisoTen_Cs1_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Cs1_d(eval); } float _tenAnisoEval_Ct1_f(const float _eval[3]) { float dem, mn, eval[3]; mn = (_eval[0] + _eval[1] + _eval[2])/3; ELL_3V_SET(eval, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn); dem = eval[0] + eval[1] - 2*eval[2]; return dem ? 2*(eval[1] - eval[2])/dem : 0.0f; } double _tenAnisoEval_Ct1_d(const double _eval[3]) { double dem, mn, eval[3]; mn = (_eval[0] + _eval[1] + _eval[2])/3; ELL_3V_SET(eval, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn); dem = eval[0] + eval[1] - 2*eval[2]; return dem ? 2*(eval[1] - eval[2])/dem : 0.0; } float _tenAnisoTen_Ct1_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Ct1_f(eval); } double _tenAnisoTen_Ct1_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Ct1_d(eval); } float _tenAnisoEval_Cl2_f(const float eval[3]) { float eval0 = AIR_MAX(0, eval[0]); return eval0 ? (eval[0] - eval[1])/eval0 : 0.0f; } double _tenAnisoEval_Cl2_d(const double eval[3]) { double eval0 = AIR_MAX(0, eval[0]); return eval0 ? (eval[0] - eval[1])/eval0 : 0.0; } float _tenAnisoTen_Cl2_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Cl2_f(eval); } double _tenAnisoTen_Cl2_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Cl2_d(eval); } float _tenAnisoEval_Cp2_f(const float eval[3]) { float eval0 = AIR_MAX(0, eval[0]); return eval0 ? (eval[1] - eval[2])/eval0 : 0.0f; } double _tenAnisoEval_Cp2_d(const double eval[3]) { double eval0 = AIR_MAX(0, eval[0]); return eval0 ? (eval[1] - eval[2])/eval0 : 0.0; } float _tenAnisoTen_Cp2_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Cp2_f(eval); } double _tenAnisoTen_Cp2_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Cp2_d(eval); } float _tenAnisoEval_Ca2_f(const float eval[3]) { float eval0 = AIR_MAX(0, eval[0]); return eval0 ? (eval[0] - eval[2])/eval0 : 0.0f; } double _tenAnisoEval_Ca2_d(const double eval[3]) { double eval0 = AIR_MAX(0, eval[0]); return eval0 ? (eval[0] - eval[2])/eval0 : 0.0; } float _tenAnisoTen_Ca2_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Ca2_f(eval); } double _tenAnisoTen_Ca2_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Ca2_d(eval); } float _tenAnisoEval_Clpmin2_f(const float eval[3]) { float cl, cp, eval0 = AIR_MAX(0, eval[0]); cl = eval0 ? (eval[0] - eval[1])/eval0 : 0.0f; cp = eval0 ? (eval[1] - eval[2])/eval0 : 0.0f; return AIR_MIN(cl, cp); } double _tenAnisoEval_Clpmin2_d(const double eval[3]) { double cl, cp, eval0 = AIR_MAX(0, eval[0]); cl = eval0 ? (eval[0] - eval[1])/eval0 : 0.0; cp = eval0 ? (eval[1] - eval[2])/eval0 : 0.0; return AIR_MIN(cl, cp); } float _tenAnisoTen_Clpmin2_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Clpmin2_f(eval); } double _tenAnisoTen_Clpmin2_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Clpmin2_d(eval); } float _tenAnisoEval_Cs2_f(const float eval[3]) { float eval0 = AIR_MAX(0, eval[0]); return eval0 ? eval[2]/eval0 : 0.0f; } double _tenAnisoEval_Cs2_d(const double eval[3]) { double eval0 = AIR_MAX(0, eval[0]); return eval0 ? eval[2]/eval0 : 0.0; } float _tenAnisoTen_Cs2_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Cs2_f(eval); } double _tenAnisoTen_Cs2_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Cs2_d(eval); } float _tenAnisoEval_Ct2_f(const float eval[3]) { float denom; denom = eval[0] - eval[2]; return denom ? (eval[1] - eval[2])/denom : 0.0f; } double _tenAnisoEval_Ct2_d(const double eval[3]) { double denom; denom = eval[0] - eval[2]; return denom ? (eval[1] - eval[2])/denom : 0.0; } float _tenAnisoTen_Ct2_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return _tenAnisoEval_Ct2_f(eval); } double _tenAnisoTen_Ct2_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return _tenAnisoEval_Ct2_d(eval); } #define SQRT6 2.44948974278317809819 float _tenAnisoEval_RA_f(const float eval[3]) { float mean, stdv; mean = (eval[0] + eval[1] + eval[2])/3; stdv = AIR_CAST(float, sqrt((mean-eval[0])*(mean-eval[0]) /* not exactly stdv */ + (mean-eval[1])*(mean-eval[1]) + (mean-eval[2])*(mean-eval[2]))); return mean ? AIR_CAST(float, stdv/(mean*SQRT6)) : 0.0f; } double _tenAnisoEval_RA_d(const double eval[3]) { double mean, stdv; mean = (eval[0] + eval[1] + eval[2])/3; stdv = sqrt((mean-eval[0])*(mean-eval[0]) /* not exactly standard dev */ + (mean-eval[1])*(mean-eval[1]) + (mean-eval[2])*(mean-eval[2])); return mean ? stdv/(mean*SQRT6) : 0.0; } float _tenAnisoTen_RA_f(const float tt[7]) { float mn, stdv, dev[7]; mn = TEN_T_TRACE(tt)/3; TEN_T_SET(dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn); stdv = AIR_CAST(float, sqrt(TEN_T_DOT(dev, dev))); return mn ? AIR_CAST(float, stdv/(mn*SQRT6)) : 0.0f; } double _tenAnisoTen_RA_d(const double tt[7]) { double mn, stdv, dev[7]; mn = TEN_T_TRACE(tt)/3; TEN_T_SET(dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn); stdv = sqrt(TEN_T_DOT(dev, dev)); return mn ? stdv/(mn*SQRT6) : 0.0; } float _tenAnisoEval_FA_f(const float eval[3]) { float denom, mean, stdv; denom = 2.0f*(eval[0]*eval[0] + eval[1]*eval[1] + eval[2]*eval[2]); mean = (eval[0] + eval[1] + eval[2])/3; stdv = AIR_CAST(float, (mean-eval[0])*(mean-eval[0]) /* not exactly stdv */ + (mean-eval[1])*(mean-eval[1]) + (mean-eval[2])*(mean-eval[2])); return denom ? AIR_CAST(float, sqrt(3.0*stdv/denom)) : 0.0f; } double _tenAnisoEval_FA_d(const double eval[3]) { double denom, mean, stdv; denom = 2.0*(eval[0]*eval[0] + eval[1]*eval[1] + eval[2]*eval[2]); mean = (eval[0] + eval[1] + eval[2])/3; stdv = ((mean-eval[0])*(mean-eval[0]) /* not exactly standard dev */ + (mean-eval[1])*(mean-eval[1]) + (mean-eval[2])*(mean-eval[2])); return denom ? sqrt(3.0*stdv/denom) : 0.0; } float _tenAnisoTen_FA_f(const float tt[7]) { float denom, mn, stdv, dev[7]; denom = AIR_CAST(float, 2.0*TEN_T_DOT(tt, tt)); mn = TEN_T_TRACE(tt)/3; TEN_T_SET(dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn); stdv = TEN_T_DOT(dev, dev); return denom ? AIR_CAST(float, sqrt(3.0*stdv/denom)) : 0.0f; } double _tenAnisoTen_FA_d(const double tt[7]) { double denom, mn, stdv, dev[7]; denom = 2.0*TEN_T_DOT(tt, tt); mn = TEN_T_TRACE(tt)/3; TEN_T_SET(dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn); stdv = TEN_T_DOT(dev, dev); return denom ? AIR_CAST(float, sqrt(3.0*stdv/denom)) : 0.0; } float _tenAnisoEval_VF_f(const float eval[3]) { float mean; mean = (eval[0] + eval[1] + eval[2])/3.0f; mean = mean*mean*mean; return 1.0f - (mean ? eval[0]*eval[1]*eval[2]/mean : 0.0f); } double _tenAnisoEval_VF_d(const double eval[3]) { double mean; mean = (eval[0] + eval[1] + eval[2])/3; mean = mean*mean*mean; return 1.0 - (mean ? eval[0]*eval[1]*eval[2]/mean : 0.0); } float _tenAnisoTen_VF_f(const float ten[7]) { float mean; mean = TEN_T_TRACE(ten)/3.0f; mean = mean*mean*mean; return 1.0f - (mean ? TEN_T_DET(ten)/mean : 0.0f); } double _tenAnisoTen_VF_d(const double ten[7]) { double mean; mean = TEN_T_TRACE(ten)/3.0; mean = mean*mean*mean; return 1.0 - (mean ? TEN_T_DET(ten)/mean : 0.0); } float _tenAnisoEval_B_f(const float eval[3]) { return eval[0]*eval[1] + eval[0]*eval[2] + eval[1]*eval[2]; } double _tenAnisoEval_B_d(const double eval[3]) { return eval[0]*eval[1] + eval[0]*eval[2] + eval[1]*eval[2]; } float _tenAnisoTen_B_f(const float ten[7]) { return (ten[1]*ten[4] + ten[1]*ten[6] + ten[4]*ten[6] - ten[2]*ten[2] - ten[3]*ten[3] - ten[5]*ten[5]); } double _tenAnisoTen_B_d(const double ten[7]) { return (ten[1]*ten[4] + ten[1]*ten[6] + ten[4]*ten[6] - ten[2]*ten[2] - ten[3]*ten[3] - ten[5]*ten[5]); } float _tenAnisoEval_Q_f(const float eval[3]) { float A, B; A = -(eval[0] + eval[1] + eval[2]); B = _tenAnisoEval_B_f(eval); A = (A*A - 3.0f*B)/9.0f; return AIR_MAX(0, A); } double _tenAnisoEval_Q_d(const double eval[3]) { double A, B; A = -(eval[0] + eval[1] + eval[2]); B = _tenAnisoEval_B_d(eval); A = (A*A - 3.0*B)/9.0; return AIR_MAX(0, A); } float _tenAnisoTen_Q_f(const float ten[7]) { float A, B; A = -TEN_T_TRACE(ten); B = _tenAnisoTen_B_f(ten); A = (A*A - 3.0f*B)/9.0f; return AIR_MAX(0, A); } double _tenAnisoTen_Q_d(const double ten[7]) { double A, B; A = -TEN_T_TRACE(ten); B = _tenAnisoTen_B_d(ten); A = (A*A - 3.0*B)/9.0; return AIR_MAX(0, A); } float _tenAnisoEval_R_f(const float eval[3]) { float A, B, C; A = -(eval[0] + eval[1] + eval[2]); B = _tenAnisoEval_B_f(eval); C = -eval[0]*eval[1]*eval[2]; return (-2*A*A*A + 9*A*B - 27*C)/54; } double _tenAnisoEval_R_d(const double eval[3]) { double A, B, C; A = -(eval[0] + eval[1] + eval[2]); B = _tenAnisoEval_B_d(eval); C = -eval[0]*eval[1]*eval[2]; return (-2*A*A*A + 9*A*B - 27*C)/54; } float _tenAnisoTen_R_f(const float ten[7]) { float A, B, C; A = -TEN_T_TRACE(ten); B = _tenAnisoTen_B_f(ten); C = -TEN_T_DET(ten); return (-2*A*A*A + 9*A*B - 27*C)/54; } double _tenAnisoTen_R_d(const double ten[7]) { double A, B, C; A = -TEN_T_TRACE(ten); B = _tenAnisoTen_B_d(ten); C = -TEN_T_DET(ten); return (-2*A*A*A + 9*A*B - 27*C)/54; } float _tenAnisoEval_S_f(const float eval[3]) { return eval[0]*eval[0] + eval[1]*eval[1] + eval[2]*eval[2]; } double _tenAnisoEval_S_d(const double eval[3]) { return eval[0]*eval[0] + eval[1]*eval[1] + eval[2]*eval[2]; } float _tenAnisoTen_S_f(const float ten[7]) { return TEN_T_DOT(ten, ten); } double _tenAnisoTen_S_d(const double ten[7]) { return TEN_T_DOT(ten, ten); } #define OOSQRT2 0.70710678118654752440 float _tenAnisoEval_Skew_f(const float _eval[3]) { float Q, num, dnm, ret, mn, eval[3]; mn = (_eval[0] + _eval[1] + _eval[2])/3; ELL_3V_SET(eval, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn); Q = _tenAnisoEval_Q_f(eval); num = _tenAnisoEval_R_f(eval); dnm = AIR_CAST(float, Q*sqrt(2*Q)); ret = dnm ? AIR_CAST(float, num/dnm) : 0.0f; return AIR_CAST(float, AIR_CLAMP(-OOSQRT2, ret, OOSQRT2)); } double _tenAnisoEval_Skew_d(const double _eval[3]) { double Q, num, dnm, ret, mn, eval[3]; mn = (_eval[0] + _eval[1] + _eval[2])/3; ELL_3V_SET(eval, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn); Q = _tenAnisoEval_Q_d(eval); num = _tenAnisoEval_R_d(eval); dnm = Q*sqrt(2*Q); ret = dnm ? num/dnm : 0.0; return AIR_CLAMP(-OOSQRT2, ret, OOSQRT2); } float _tenAnisoTen_Skew_f(const float _t[7]) { float Q, num, dnm, ret, mn, ten[7]; mn = TEN_T_TRACE(_t)/3; TEN_T_SET(ten, _t[0], _t[1]-mn, _t[2], _t[3], _t[4]-mn, _t[5], _t[6]-mn); Q = _tenAnisoTen_Q_f(ten); num = _tenAnisoTen_R_f(ten); dnm = AIR_CAST(float, Q*sqrt(2*Q)); ret = dnm ? AIR_CAST(float, num/dnm) : 0.0f; return AIR_CAST(float, AIR_CLAMP(-OOSQRT2, ret, OOSQRT2)); } double _tenAnisoTen_Skew_d(const double _t[7]) { double Q, num, dnm, ret, mn, ten[7]; mn = TEN_T_TRACE(_t)/3; TEN_T_SET(ten, _t[0], _t[1]-mn, _t[2], _t[3], _t[4]-mn, _t[5], _t[6]-mn); Q = _tenAnisoTen_Q_d(ten); num = _tenAnisoTen_R_d(ten); dnm = Q*sqrt(2*Q); ret = dnm ? num/dnm : 0.0; return AIR_CLAMP(-OOSQRT2, ret, OOSQRT2); } float _tenAnisoEval_Mode_f(const float _eval[3]) { float n, d, mn, e[3], ret; mn = (_eval[0] + _eval[1] + _eval[2])/3; ELL_3V_SET(e, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn); n = (e[0] + e[1] - 2*e[2])*(2*e[0] - e[1] - e[2])*(e[0] - 2*e[1] + e[2]); d = (e[0]*e[0] + e[1]*e[1] + e[2]*e[2] - e[0]*e[1] - e[1]*e[2] - e[0]*e[2]); d = AIR_CAST(float, sqrt(AIR_MAX(0, d))); d = 2*d*d*d; ret = d ? AIR_CAST(float, n/d) : 0.0f; return AIR_CLAMP(-1, ret, 1); } double _tenAnisoEval_Mode_d(const double _eval[3]) { double n, d, mn, e[3], ret; mn = (_eval[0] + _eval[1] + _eval[2])/3; ELL_3V_SET(e, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn); n = (e[0] + e[1] - 2*e[2])*(2*e[0] - e[1] - e[2])*(e[0] - 2*e[1] + e[2]); d = (e[0]*e[0] + e[1]*e[1] + e[2]*e[2] - e[0]*e[1] - e[1]*e[2] - e[0]*e[2]); d = sqrt(AIR_MAX(0, d)); d = 2*d*d*d; ret = d ? n/d : 0.0; return AIR_CLAMP(-1, ret, 1); } float _tenAnisoTen_Mode_f(const float tt[7]) { float mn, dev[7], tmp, ret; mn = TEN_T_TRACE(tt)/3.0f; TEN_T_SET(dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn); tmp = AIR_CAST(float, TEN_T_NORM(dev)); tmp = tmp ? 1.0f/tmp : 0.0f; TEN_T_SCALE(dev, tmp, dev); ret = AIR_CAST(float, 3*SQRT6*TEN_T_DET(dev)); return AIR_CLAMP(-1, ret, 1); } double _tenAnisoTen_Mode_d(const double tt[7]) { double mn, dev[7], tmp, ret; mn = TEN_T_TRACE(tt)/3.0; TEN_T_SET(dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn); tmp = TEN_T_NORM(dev); tmp = tmp ? 1.0/tmp : 0.0; TEN_T_SCALE(dev, tmp, dev); ret = 3*SQRT6*TEN_T_DET(dev); return AIR_CLAMP(-1, ret, 1); } /* NOTE: yes, the AIR_CLAMPs here are needed even though ** the _Skew_ functions clamp their output */ #define SQRT2 1.41421356237309504880 float _tenAnisoEval_Th_f(const float eval[3]) { float tmp; tmp = AIR_CAST(float, SQRT2*_tenAnisoEval_Skew_f(eval)); return AIR_CAST(float, acos(AIR_CLAMP(-1, tmp, 1))/3); } double _tenAnisoEval_Th_d(const double eval[3]) { double tmp; tmp = SQRT2*_tenAnisoEval_Skew_d(eval); return acos(AIR_CLAMP(-1, tmp, 1))/3; } float _tenAnisoTen_Th_f(const float ten[7]) { float tmp; tmp = AIR_CAST(float, SQRT2*_tenAnisoTen_Skew_f(ten)); return AIR_CAST(float, acos(AIR_CLAMP(-1, tmp, 1))/3); } double _tenAnisoTen_Th_d(const double ten[7]) { double tmp; tmp = SQRT2*_tenAnisoTen_Skew_d(ten); return acos(AIR_CLAMP(-1, tmp, 1))/3; } float _tenAnisoEval_Omega_f(const float eval[3]) { return _tenAnisoEval_FA_f(eval)*(1.0f+_tenAnisoEval_Mode_f(eval))/2.0f; } double _tenAnisoEval_Omega_d(const double eval[3]) { return _tenAnisoEval_FA_d(eval)*(1.0f+_tenAnisoEval_Mode_d(eval))/2.0f; } float _tenAnisoTen_Omega_f(const float ten[7]) { return _tenAnisoTen_FA_f(ten)*(1.0f+_tenAnisoTen_Mode_f(ten))/2.0f; } double _tenAnisoTen_Omega_d(const double ten[7]) { return _tenAnisoTen_FA_d(ten)*(1.0f+_tenAnisoTen_Mode_d(ten))/2.0f; } float _tenAnisoEval_Det_f(const float eval[3]) { return eval[0]*eval[1]*eval[2]; } double _tenAnisoEval_Det_d(const double eval[3]) { return eval[0]*eval[1]*eval[2]; } float _tenAnisoTen_Det_f(const float ten[7]) { return TEN_T_DET(ten); } double _tenAnisoTen_Det_d(const double ten[7]) { return TEN_T_DET(ten); } float _tenAnisoEval_Tr_f(const float eval[3]) { return eval[0] + eval[1] + eval[2]; } double _tenAnisoEval_Tr_d(const double eval[3]) { return eval[0] + eval[1] + eval[2]; } float _tenAnisoTen_Tr_f(const float ten[7]) { return TEN_T_TRACE(ten); } double _tenAnisoTen_Tr_d(const double ten[7]) { return TEN_T_TRACE(ten); } float _tenAnisoEval_eval0_f(const float eval[3]) { return eval[0]; } double _tenAnisoEval_eval0_d(const double eval[3]) { return eval[0]; } float _tenAnisoTen_eval0_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return eval[0]; } double _tenAnisoTen_eval0_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return eval[0]; } float _tenAnisoEval_eval1_f(const float eval[3]) { return eval[1]; } double _tenAnisoEval_eval1_d(const double eval[3]) { return eval[1]; } float _tenAnisoTen_eval1_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return eval[1]; } double _tenAnisoTen_eval1_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return eval[1]; } float _tenAnisoEval_eval2_f(const float eval[3]) { return eval[2]; } double _tenAnisoEval_eval2_d(const double eval[3]) { return eval[2]; } float _tenAnisoTen_eval2_f(const float ten[7]) { float eval[3]; tenEigensolve_f(eval, NULL, ten); return eval[2]; } double _tenAnisoTen_eval2_d(const double ten[7]) { double eval[3]; tenEigensolve_d(eval, NULL, ten); return eval[2]; } float (*_tenAnisoEval_f[TEN_ANISO_MAX+1])(const float eval[3]) = { NULL, _tenAnisoEval_Conf_f, _tenAnisoEval_Cl1_f, _tenAnisoEval_Cp1_f, _tenAnisoEval_Ca1_f, _tenAnisoEval_Clpmin1_f, _tenAnisoEval_Cs1_f, _tenAnisoEval_Ct1_f, _tenAnisoEval_Cl2_f, _tenAnisoEval_Cp2_f, _tenAnisoEval_Ca2_f, _tenAnisoEval_Clpmin2_f, _tenAnisoEval_Cs2_f, _tenAnisoEval_Ct2_f, _tenAnisoEval_RA_f, _tenAnisoEval_FA_f, _tenAnisoEval_VF_f, _tenAnisoEval_B_f, _tenAnisoEval_Q_f, _tenAnisoEval_R_f, _tenAnisoEval_S_f, _tenAnisoEval_Skew_f, _tenAnisoEval_Mode_f, _tenAnisoEval_Th_f, _tenAnisoEval_Omega_f, _tenAnisoEval_Det_f, _tenAnisoEval_Tr_f, _tenAnisoEval_eval0_f, _tenAnisoEval_eval1_f, _tenAnisoEval_eval2_f }; double (*_tenAnisoEval_d[TEN_ANISO_MAX+1])(const double eval[3]) = { NULL, _tenAnisoEval_Conf_d, _tenAnisoEval_Cl1_d, _tenAnisoEval_Cp1_d, _tenAnisoEval_Ca1_d, _tenAnisoEval_Clpmin1_d, _tenAnisoEval_Cs1_d, _tenAnisoEval_Ct1_d, _tenAnisoEval_Cl2_d, _tenAnisoEval_Cp2_d, _tenAnisoEval_Ca2_d, _tenAnisoEval_Clpmin2_d, _tenAnisoEval_Cs2_d, _tenAnisoEval_Ct2_d, _tenAnisoEval_RA_d, _tenAnisoEval_FA_d, _tenAnisoEval_VF_d, _tenAnisoEval_B_d, _tenAnisoEval_Q_d, _tenAnisoEval_R_d, _tenAnisoEval_S_d, _tenAnisoEval_Skew_d, _tenAnisoEval_Mode_d, _tenAnisoEval_Th_d, _tenAnisoEval_Omega_d, _tenAnisoEval_Det_d, _tenAnisoEval_Tr_d, _tenAnisoEval_eval0_d, _tenAnisoEval_eval1_d, _tenAnisoEval_eval2_d }; float (*_tenAnisoTen_f[TEN_ANISO_MAX+1])(const float ten[7]) = { NULL, _tenAnisoTen_Conf_f, _tenAnisoTen_Cl1_f, _tenAnisoTen_Cp1_f, _tenAnisoTen_Ca1_f, _tenAnisoTen_Clpmin1_f, _tenAnisoTen_Cs1_f, _tenAnisoTen_Ct1_f, _tenAnisoTen_Cl2_f, _tenAnisoTen_Cp2_f, _tenAnisoTen_Ca2_f, _tenAnisoTen_Clpmin2_f, _tenAnisoTen_Cs2_f, _tenAnisoTen_Ct2_f, _tenAnisoTen_RA_f, _tenAnisoTen_FA_f, _tenAnisoTen_VF_f, _tenAnisoTen_B_f, _tenAnisoTen_Q_f, _tenAnisoTen_R_f, _tenAnisoTen_S_f, _tenAnisoTen_Skew_f, _tenAnisoTen_Mode_f, _tenAnisoTen_Th_f, _tenAnisoTen_Omega_f, _tenAnisoTen_Det_f, _tenAnisoTen_Tr_f, _tenAnisoTen_eval0_f, _tenAnisoTen_eval1_f, _tenAnisoTen_eval2_f }; double (*_tenAnisoTen_d[TEN_ANISO_MAX+1])(const double ten[7]) = { NULL, _tenAnisoTen_Conf_d, _tenAnisoTen_Cl1_d, _tenAnisoTen_Cp1_d, _tenAnisoTen_Ca1_d, _tenAnisoTen_Clpmin1_d, _tenAnisoTen_Cs1_d, _tenAnisoTen_Ct1_d, _tenAnisoTen_Cl2_d, _tenAnisoTen_Cp2_d, _tenAnisoTen_Ca2_d, _tenAnisoTen_Clpmin2_d, _tenAnisoTen_Cs2_d, _tenAnisoTen_Ct2_d, _tenAnisoTen_RA_d, _tenAnisoTen_FA_d, _tenAnisoTen_VF_d, _tenAnisoTen_B_d, _tenAnisoTen_Q_d, _tenAnisoTen_R_d, _tenAnisoTen_S_d, _tenAnisoTen_Skew_d, _tenAnisoTen_Mode_d, _tenAnisoTen_Th_d, _tenAnisoTen_Omega_d, _tenAnisoTen_Det_d, _tenAnisoTen_Tr_d, _tenAnisoTen_eval0_d, _tenAnisoTen_eval1_d, _tenAnisoTen_eval2_d }; float tenAnisoEval_f(const float eval[3], int aniso) { return (AIR_IN_OP(tenAnisoUnknown, aniso, tenAnisoLast) ? _tenAnisoEval_f[aniso](eval) : 0); } double tenAnisoEval_d(const double eval[3], int aniso) { return (AIR_IN_OP(tenAnisoUnknown, aniso, tenAnisoLast) ? _tenAnisoEval_d[aniso](eval) : 0); } float tenAnisoTen_f(const float ten[7], int aniso) { return (AIR_IN_OP(tenAnisoUnknown, aniso, tenAnisoLast) ? _tenAnisoTen_f[aniso](ten) : 0); } double tenAnisoTen_d(const double ten[7], int aniso) { return (AIR_IN_OP(tenAnisoUnknown, aniso, tenAnisoLast) ? _tenAnisoTen_d[aniso](ten) : 0); } #if 0 /* ******** tenAnisoCalc_f ** ** !!! THIS FUNCTION HAS BEEN MADE OBSOLETE BY THE NEW ** !!! tenAnisoEval_{f,d} AND tenAnisoTen_{f,d} FUNCTIONS ** !!! THIS WILL LIKELY BE REMOVED FROM FUTURE RELEASES ** ** Because this function does not subtract out the eigenvalue mean ** when computing quantities like Skew and Mode, it has really lousy ** accuracy on those measures compared to tenAnisoEval_{f,d}. ** ** given an array of three SORTED (descending) eigenvalues "e", ** calculates the anisotropy coefficients of Westin et al., ** as well as various others. ** ** NOTE: with time, so many metrics have ended up here that under ** no cases should this be used in any kind of time-critical operations ** ** This does NOT use biff. */ void tenAnisoCalc_f(float c[TEN_ANISO_MAX+1], const float e[3]) { float e0, e1, e2, stdv, mean, sum, cl, cp, ca, ra, fa, vf, denom; float A, B, C, R, Q, N, D; if (!( e[0] >= e[1] && e[1] >= e[2] )) { fprintf(stderr, "tenAnisoCalc_f: eigen values not sorted: " "%g %g %g (%d %d)\n", e[0], e[1], e[2], e[0] >= e[1], e[1] >= e[2]); } if ((tenVerbose > 1) && !( e[0] >= 0 && e[1] >= 0 && e[2] >= 0 )) { fprintf(stderr, "tenAnisoCalc_f: eigen values not all >= 0: %g %g %g\n", e[0], e[1], e[2]); } e0 = AIR_MAX(e[0], 0); e1 = AIR_MAX(e[1], 0); e2 = AIR_MAX(e[2], 0); sum = e0 + e1 + e2; /* first version of cl, cp, cs */ cl = sum ? (e0 - e1)/sum : 0.0f; c[tenAniso_Cl1] = cl; cp = sum ? 2*(e1 - e2)/sum : 0.0f; c[tenAniso_Cp1] = cp; ca = cl + cp; c[tenAniso_Ca1] = ca; c[tenAniso_Clpmin1] = AIR_MIN(cl, cp); /* extra logic here for equality with expressions above */ c[tenAniso_Cs1] = sum ? 1 - ca : 0.0f; c[tenAniso_Ct1] = ca ? cp/ca : 0; /* second version of cl, cp, cs */ cl = e0 ? (e0 - e1)/e0 : 0.0f; c[tenAniso_Cl2] = cl; cp = e0 ? (e1 - e2)/e0 : 0.0f; c[tenAniso_Cp2] = cp; ca = cl + cp; c[tenAniso_Ca2] = ca; c[tenAniso_Clpmin2] = AIR_MIN(cl, cp); /* extra logic here for equality with expressions above */ c[tenAniso_Cs2] = e0 ? 1 - ca : 0.0f; c[tenAniso_Ct2] = ca ? cp/ca : 0.0f; /* non-westin anisos */ mean = sum/3.0f; stdv = AIR_CAST(float, sqrt((mean-e0)*(mean-e0) /* okay, not exactly standard dev */ + (mean-e1)*(mean-e1) + (mean-e2)*(mean-e2))); ra = mean ? AIR_CAST(float, stdv/(mean*SQRT6)) : 0.0f; ra = AIR_CLAMP(0.0f, ra, 1.0f); c[tenAniso_RA] = ra; denom = 2.0f*(e0*e0 + e1*e1 + e2*e2); if (denom) { fa = AIR_CAST(float, stdv*sqrt(3.0/denom)); fa = AIR_CLAMP(0.0f, fa, 1.0f); } else { fa = 0.0f; } c[tenAniso_FA] = fa; vf = 1 - (mean ? e0*e1*e2/(mean*mean*mean) : 0.0f); vf = AIR_CLAMP(0.0f, vf, 1.0f); c[tenAniso_VF] = vf; A = (-e0 - e1 - e2); B = c[tenAniso_B] = e0*e1 + e0*e2 + e1*e2; C = -e0*e1*e2; Q = c[tenAniso_Q] = (A*A - 3*B)/9; R = c[tenAniso_R] = (-2*A*A*A + 9*A*B - 27*C)/54; c[tenAniso_S] = e0*e0 + e1*e1 + e2*e2; c[tenAniso_Skew] = Q ? AIR_CAST(float, R/(Q*sqrt(2*Q))) : 0.0f; c[tenAniso_Skew] = AIR_CLAMP(-OOSQRT2, c[tenAniso_Skew], OOSQRT2); N = (e0 + e1 - 2*e2)*(2*e0 - e1 - e2)*(e0 - 2*e1 + e2); D = AIR_CAST(float, sqrt(e0*e0+e1*e1+e2*e2 - e0*e1-e1*e2-e0*e2)); c[tenAniso_Mode] = D ? N/(2*D*D*D) : 0.0f; c[tenAniso_Mode] = AIR_CLAMP(-1, c[tenAniso_Mode], 1); c[tenAniso_Th] = AIR_CAST(float, acos(AIR_CLAMP(-1, sqrt(2)*c[tenAniso_Skew], 1))/3); c[tenAniso_Omega] = c[tenAniso_FA]*(1+c[tenAniso_Mode])/2; c[tenAniso_Det] = e0*e1*e2; c[tenAniso_Tr] = e0 + e1 + e2; c[tenAniso_eval0] = e0; c[tenAniso_eval1] = e1; c[tenAniso_eval2] = e2; return; } #endif int tenAnisoPlot(Nrrd *nout, int aniso, unsigned int res, int hflip, int whole, int nanout) { static const char me[]="tenAnisoMap"; float *out, tmp; unsigned int x, y; float m0[3], m1[3], m2[3], c0, c1, c2, e[3]; float S = 1/3.0f, L = 1.0f, P = 1/2.0f; /* these make Westin's original (cl,cp,cs) align with the barycentric coordinates */ if (airEnumValCheck(tenAniso, aniso)) { biffAddf(TEN, "%s: invalid aniso (%d)", me, aniso); return 1; } if (!(res > 2)) { biffAddf(TEN, "%s: resolution (%d) invalid", me, res); return 1; } if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 2, AIR_CAST(size_t, res), AIR_CAST(size_t, res))) { biffMovef(TEN, NRRD, "%s: ", me); return 1; } out = (float *)nout->data; if (whole) { ELL_3V_SET(m0, 1, 0, 0); ELL_3V_SET(m1, 0, 1, 0); ELL_3V_SET(m2, 0, 0, 1); } else { ELL_3V_SET(m0, S, S, S); if (hflip) { ELL_3V_SET(m1, P, P, 0); ELL_3V_SET(m2, L, 0, 0); } else { ELL_3V_SET(m1, L, 0, 0); ELL_3V_SET(m2, P, P, 0); } } for (y=0; yaxis[1].size; size[1] = sy = nin->axis[2].size; size[2] = sz = nin->axis[3].size; N = sx*sy*sz; if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 3, sx, sy, sz)) { biffMovef(TEN, NRRD, "%s: trouble", me); return 1; } out = (float *)nout->data; in = (float *)nin->data; for (I=0; I<=N-1; I++) { /* tenVerbose = (I == 911327); */ tensor = in + I*7; if (tenAniso_Conf != aniso && tensor[0] < confThresh) { out[I] = 0.0; continue; } /* no longer used tenEigensolve_f(eval, NULL, tensor); if (!(AIR_EXISTS(eval[0]) && AIR_EXISTS(eval[1]) && AIR_EXISTS(eval[2]))) { NRRD_COORD_GEN(coord, size, 3, I); biffAddf(TEN, "%s: not all eigenvalues exist (%g,%g,%g) at sample " "%d = (%d,%d,%d)", me, eval[0], eval[1], eval[2], (int)I, (int)coord[0], (int)coord[1], (int)coord[2]); return 1; } */ out[I] = tenAnisoTen_f(tensor, aniso); if (!AIR_EXISTS(out[I])) { size_t coord[3]; NRRD_COORD_GEN(coord, size, 3, I); biffAddf(TEN, "%s: generated non-existent aniso %g from tensor " "(%g) %g %g %g %g %g %g at sample %u = (%u,%u,%u)", me, out[I], tensor[0], tensor[1], tensor[2], tensor[3], tensor[4], tensor[5], tensor[6], AIR_CAST(unsigned int, I), AIR_CAST(unsigned int, coord[0]), AIR_CAST(unsigned int, coord[1]), AIR_CAST(unsigned int, coord[2])); return 1; } } ELL_3V_SET(map, 1, 2, 3); if (nrrdAxisInfoCopy(nout, nin, map, NRRD_AXIS_INFO_SIZE_BIT)) { biffMovef(TEN, NRRD, "%s: trouble", me); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } int tenAnisoHistogram(Nrrd *nout, const Nrrd *nin, const Nrrd *nwght, int right, int version, unsigned int res) { static const char me[]="tenAnisoHistogram"; size_t N, I; int csIdx, clIdx, cpIdx; float *tdata, *out, eval[3], cs, cl, cp, (*wlup)(const void *data, size_t idx), weight; unsigned int yres, xi, yi; if (tenTensorCheck(nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a tensor nrrd", me); return 1; } if (nwght) { if (nrrdCheck(nwght)) { biffMovef(TEN, NRRD, "%s: trouble with weighting nrrd", me); return 1; } if (nrrdElementNumber(nwght) != nrrdElementNumber(nin)/nrrdKindSize(nrrdKind3DMaskedSymMatrix) ) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; size_t numten; numten = nrrdElementNumber(nin)/nrrdKindSize(nrrdKind3DMaskedSymMatrix); biffAddf(TEN, "%s: # elements in weight nrrd (%s) != # tensors (%s)", me, airSprintSize_t(stmp1, nrrdElementNumber(nwght)), airSprintSize_t(stmp2, numten)); return 1; } } if (!( 1 == version || 2 == version )) { biffAddf(TEN, "%s: version (%d) wasn't 1 or 2", me, version); return 1; } if (!(res > 10)) { biffAddf(TEN, "%s: resolution (%d) invalid", me, res); return 1; } if (right) { yres = AIR_CAST(unsigned int, AIR_CAST(double, res)/sqrt(3)); } else { yres = res; } if (nwght) { wlup = nrrdFLookup[nwght->type]; } else { wlup = NULL; } if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 2, AIR_CAST(size_t, res), AIR_CAST(size_t, yres))) { biffMovef(TEN, NRRD, "%s: ", me); return 1; } out = (float *)nout->data; tdata = (float *)nin->data; if (right || 1 == version) { clIdx = tenAniso_Cl1; cpIdx = tenAniso_Cp1; csIdx = tenAniso_Cs1; } else { clIdx = tenAniso_Cl2; cpIdx = tenAniso_Cp2; csIdx = tenAniso_Cs2; } N = nrrdElementNumber(nin)/nrrdKindSize(nrrdKind3DMaskedSymMatrix); for (I=0; Idata, I) : 1.0f; if (xi < res && yi < yres-1) { out[xi + res*yi] += tdata[0]*weight; } tdata += nrrdKindSize(nrrdKind3DMaskedSymMatrix); } return 0; } tenEvecRGBParm * tenEvecRGBParmNew() { tenEvecRGBParm *rgbp; rgbp = AIR_CAST(tenEvecRGBParm *, calloc(1, sizeof(tenEvecRGBParm))); if (rgbp) { rgbp->which = 0; rgbp->aniso = tenAniso_Cl2; rgbp->confThresh = 0.5; rgbp->anisoGamma = 1.0; rgbp->gamma = 1.0; rgbp->bgGray = 0.0; rgbp->isoGray = 0.0; rgbp->maxSat = 1.0; rgbp->typeOut = nrrdTypeFloat; rgbp->genAlpha = AIR_FALSE; } return rgbp; } tenEvecRGBParm * tenEvecRGBParmNix(tenEvecRGBParm *rgbp) { if (rgbp) { airFree(rgbp); } return NULL; } int tenEvecRGBParmCheck(const tenEvecRGBParm *rgbp) { static const char me[]="tenEvecRGBParmCheck"; if (!rgbp) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( rgbp->which <= 2 )) { biffAddf(TEN, "%s: which must be 0, 1, or 2 (not %u)", me, rgbp->which); return 1; } if (airEnumValCheck(tenAniso, rgbp->aniso)) { biffAddf(TEN, "%s: anisotropy metric %d not valid", me, rgbp->aniso); return 1; } if (nrrdTypeDefault != rgbp->typeOut && airEnumValCheck(nrrdType, rgbp->typeOut)) { biffAddf(TEN, "%s: output type (%d) not valid", me, rgbp->typeOut); return 1; } return 0; } float _tenEvecRGBComp_f(float conf, float aniso, float comp, const tenEvecRGBParm *rgbp) { double X; X = AIR_ABS(comp); X = pow(X, 1.0/rgbp->gamma); X = AIR_LERP(rgbp->maxSat*aniso, rgbp->isoGray, X); return AIR_CAST(float, conf > rgbp->confThresh ? X : rgbp->bgGray); } double _tenEvecRGBComp_d(double conf, double aniso, double comp, const tenEvecRGBParm *rgbp) { double X; X = AIR_ABS(comp); X = pow(X, 1.0/rgbp->gamma); X = AIR_LERP(rgbp->maxSat*aniso, rgbp->isoGray, X); return conf > rgbp->confThresh ? X : rgbp->bgGray; } void tenEvecRGBSingle_f(float RGB[3], float conf, const float eval[3], const float evec[3], const tenEvecRGBParm *rgbp) { float aniso; if (RGB && eval && rgbp) { aniso = tenAnisoEval_f(eval, rgbp->aniso); aniso = AIR_CAST(float, pow(aniso, 1.0/rgbp->anisoGamma)); ELL_3V_SET(RGB, _tenEvecRGBComp_f(conf, aniso, evec[0], rgbp), _tenEvecRGBComp_f(conf, aniso, evec[1], rgbp), _tenEvecRGBComp_f(conf, aniso, evec[2], rgbp)); } return; } void tenEvecRGBSingle_d(double RGB[3], double conf, const double eval[3], const double evec[3], const tenEvecRGBParm *rgbp) { double aniso; if (RGB && eval && rgbp) { aniso = tenAnisoEval_d(eval, rgbp->aniso); aniso = pow(aniso, 1.0/rgbp->anisoGamma); ELL_3V_SET(RGB, _tenEvecRGBComp_d(conf, aniso, evec[0], rgbp), _tenEvecRGBComp_d(conf, aniso, evec[1], rgbp), _tenEvecRGBComp_d(conf, aniso, evec[2], rgbp)); } return; } teem-1.11.0~svn6057/src/ten/tendFiber.c0000664000175000017500000003171112165631065017240 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Fiber tractography, from one or more seeds" static const char *_tend_fiberInfoL = (INFO ". A fairly complete command-line interface to the tenFiber API."); int tend_fiberMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; char *outS; tenFiberContext *tfx; tenFiberSingle *tfbs; NrrdKernelSpec *ksp; double start[3], step, *_stop, *stop; const airEnum *ftypeEnum; char *ftypeS; int E, intg, useDwi, allPaths, verbose, worldSpace, worldSpaceOut, ftype, ftypeDef; Nrrd *nin, *nseed, *nmat, *_nmat; unsigned int si, stopLen, whichPath; double matx[16]={1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}; tenFiberMulti *tfml; limnPolyData *fiberPld; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "dwi", NULL, airTypeInt, 0, 0, &useDwi, NULL, "input volume is a DWI volume, not a single tensor volume"); hestOptAdd(&hopt, "s", "seed point", airTypeDouble, 3, 3, start, "0 0 0", "seed point for fiber; it will propogate in two opposite " "directions starting from here"); hestOptAdd(&hopt, "ns", "seed nrrd", airTypeOther, 1, 1, &nseed, "", "3-by-N nrrd of seedpoints", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "wsp", NULL, airTypeInt, 0, 0, &worldSpace, NULL, "define seedpoint and output path in worldspace. Otherwise, " "(without using this option) everything is in index space"); hestOptAdd(&hopt, "t", "type", airTypeString, 1, 1, &ftypeS, "", "fiber type; defaults to something"); hestOptAdd(&hopt, "n", "intg", airTypeEnum, 1, 1, &intg, "rk4", "integration method for fiber tracking", NULL, tenFiberIntg); hestOptAdd(&hopt, "k", "kernel", airTypeOther, 1, 1, &ksp, "tent", "kernel for reconstructing tensor field", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "wp", "which", airTypeUInt, 1, 1, &whichPath, "0", "when doing multi-tensor tracking, index of path to follow " "(made moot by \"-ap\")"); hestOptAdd(&hopt, "ap", "allpaths", airTypeInt, 0, 0, &allPaths, NULL, "follow all paths from (all) seedpoint(s), output will be " "polydata, rather than a single 3-by-N nrrd, even if only " "a single path is generated"); hestOptAdd(&hopt, "wspo", NULL, airTypeInt, 0, 0, &worldSpaceOut, NULL, "output should be in worldspace, even if input is not " "(this feature is unstable and/or confusing)"); hestOptAdd(&hopt, "step", "step size", airTypeDouble, 1, 1, &step, "0.01", "stepsize along fiber, in world space"); hestOptAdd(&hopt, "stop", "stop1", airTypeOther, 1, -1, &_stop, NULL, "the conditions that should signify the end of a fiber, or " "when to discard a fiber that is done propagating. " "Multiple stopping criteria are logically OR-ed and tested at " "every point along the fiber. Possibilities include:\n " "\b\bo \"aniso:,\": require anisotropy to be " "above the given threshold. Which anisotropy type is given " "as with \"tend anvol\" (see its usage info)\n " "\b\bo \"len:\": limits the length, in world space, " "of each fiber half\n " "\b\bo \"steps:\": the number of steps in each fiber half " "is capped at N\n " "\b\bo \"conf:\": requires the tensor confidence value " "to be above the given thresh\n " "\b\bo \"radius:\": requires that the radius of " "curvature of the fiber stay above given thr\n " "\b\bo \"frac:\": in multi-tensor tracking, the fraction " "of the tracked component must stay above F\n " "\b\bo \"minlen:\": discard fiber if its final whole " "length is below len (not really a termination criterion)\n " "\b\bo \"minsteps:\": discard fiber if its final number of " "steps is below N (not really a termination criterion)", &stopLen, NULL, tendFiberStopCB); hestOptAdd(&hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "0", "verbosity level"); hestOptAdd(&hopt, "nmat", "transform", airTypeOther, 1, 1, &_nmat, "", "a 4x4 homogenous transform matrix (as a nrrd, or just a text " "file) given with this option will be applied to the output " "tractography vertices just prior to output", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "out", airTypeString, 1, 1, &outS, "-", "output fiber(s)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_fiberInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); tfbs = tenFiberSingleNew(); airMopAdd(mop, tfbs, (airMopper)tenFiberSingleNix, airMopAlways); if (_nmat) { if (!(2 == _nmat->dim && 4 == _nmat->axis[0].size && 4 == _nmat->axis[1].size)) { fprintf(stderr, "%s: transform matrix must be 2-D 4-by-4 array " "not %u-D %u-by-?\n", me, AIR_CAST(unsigned int, _nmat->dim), AIR_CAST(unsigned int, _nmat->axis[0].size)); airMopError(mop); return 1; } nmat = nrrdNew(); airMopAdd(mop, nmat, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nmat, _nmat, nrrdTypeDouble)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem with transform matrix\n%s\n", me, err); airMopError(mop); return 1; } ELL_4M_COPY(matx, AIR_CAST(double *, nmat->data)); fprintf(stderr, "%s: transforming output by:\n", me); ell_4m_print_d(stderr, matx); } if (useDwi) { tfx = tenFiberContextDwiNew(nin, 50, 1, 1, tenEstimate1MethodLLS, tenEstimate2MethodPeled); } else { tfx = tenFiberContextNew(nin); } if (!tfx) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: failed to create the fiber context:\n%s\n", me, err); airMopError(mop); return 1; } airMopAdd(mop, tfx, (airMopper)tenFiberContextNix, airMopAlways); E = 0; for (si=0, stop=_stop; siname); airMopError(mop); return 1; } } else { ftype = ftypeDef; fprintf(stderr, "%s: (defaulting %s to %s)\n", me, ftypeEnum->name, airEnumStr(ftypeEnum, ftype)); } E |= tenFiberTypeSet(tfx, ftype); } if (!E) E |= tenFiberKernelSet(tfx, ksp->kernel, ksp->parm); if (!E) E |= tenFiberIntgSet(tfx, intg); if (!E) E |= tenFiberParmSet(tfx, tenFiberParmStepSize, step); if (!E) E |= tenFiberParmSet(tfx, tenFiberParmUseIndexSpace, worldSpace ? AIR_FALSE: AIR_TRUE); if (!E) E |= tenFiberUpdate(tfx); if (E) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } tenFiberVerboseSet(tfx, verbose); if (!allPaths) { if (tenFiberSingleTrace(tfx, tfbs, start, whichPath)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (tenFiberStopUnknown == tfx->whyNowhere) { fprintf(stderr, "%s: steps[back,forw] = %u,%u; seedIdx = %u\n", me, tfbs->stepNum[0], tfbs->stepNum[1], tfbs->seedIdx); fprintf(stderr, "%s: whyStop[back,forw] = %s,%s\n", me, airEnumStr(tenFiberStop, tfbs->whyStop[0]), airEnumStr(tenFiberStop, tfbs->whyStop[1])); if (worldSpaceOut && !worldSpace) { /* have to convert output to worldspace */ fprintf(stderr, "%s: WARNING!!! output conversion " "to worldspace not done!!!\n", me); } if (_nmat) { fprintf(stderr, "%s: WARNING!!! output transform " "not done!!!\n", me); } if (nrrdSave(outS, tfbs->nvert, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } } else { fprintf(stderr, "%s: fiber failed to start: %s.\n", me, airEnumDesc(tenFiberStop, tfx->whyNowhere)); } } else { if (!nseed) { fprintf(stderr, "%s: didn't get seed nrrd via \"-ns\"\n", me); airMopError(mop); return 1; } tfml = tenFiberMultiNew(); airMopAdd(mop, tfml, (airMopper)tenFiberMultiNix, airMopAlways); /* fiberArr = airArrayNew(AIR_CAST(void **, &fiber), NULL, sizeof(tenFiberSingle), 1024); airArrayStructCB(fiberArr, AIR_CAST(void (*)(void *), tenFiberSingleInit), AIR_CAST(void (*)(void *), tenFiberSingleDone)); airMopAdd(mop, fiberArr, (airMopper)airArrayNuke, airMopAlways); */ fiberPld = limnPolyDataNew(); airMopAdd(mop, fiberPld, (airMopper)limnPolyDataNix, airMopAlways); if (tenFiberMultiTrace(tfx, tfml, nseed) || tenFiberMultiPolyData(tfx, fiberPld, tfml)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (worldSpaceOut && !worldSpace) { /* have to convert output to worldspace */ unsigned int ii; double indx[4], world[3]; for (ii=0; iixyzwNum; ii++) { ELL_4V_COPY(indx, fiberPld->xyzw + 4*ii); ELL_4V_HOMOG(indx, indx); gageShapeItoW(tfx->gtx->shape, world, indx); ELL_3V_COPY_TT(fiberPld->xyzw + 4*ii, float, world); (fiberPld->xyzw + 4*ii)[3] = 1.0; } } if (_nmat) { /* have to further transform output by given matrix */ unsigned int ii; double xxx[4], yyy[4]; for (ii=0; iixyzwNum; ii++) { ELL_4V_COPY(xxx, fiberPld->xyzw + 4*ii); ELL_4MV_MUL(yyy, matx, xxx); ELL_4V_HOMOG(yyy, yyy); ELL_4V_COPY_TT(fiberPld->xyzw + 4*ii, float, yyy); } } if (limnPolyDataSave(outS, fiberPld)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving:\n%s\n", me, err); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } TEND_CMD(fiber, INFO); teem-1.11.0~svn6057/src/ten/modelZero.c0000664000175000017500000000640512165631065017300 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define PARM_NUM 0 static const tenModelParmDesc parmDesc[] = { /* dummy to avoid compiler error */ {"dummy", 0.0, 0.0, AIR_FALSE, AIR_FALSE, 0}, }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; AIR_UNUSED(parm); AIR_UNUSED(espec); for (ii=0; iiimgNum; ii++) { dwiSim[ii] = 0; } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { AIR_UNUSED(parm); sprintf(str, "constant 0"); return str; } static double * parmAlloc(void) { return NULL; } static void parmRand(double *parm, airRandMTState *rng, int knownB0) { AIR_UNUSED(parm); AIR_UNUSED(rng); AIR_UNUSED(knownB0); } static void parmStep(double *parm1, const double scl, const double *grad, const double *parm0) { AIR_UNUSED(parm1); AIR_UNUSED(scl); AIR_UNUSED(grad); AIR_UNUSED(parm0); } static double parmDist(const double *parmA, const double *parmB) { AIR_UNUSED(parmA); AIR_UNUSED(parmB); return 0.0; } static void parmCopy(double *parmA, const double *parmB) { AIR_UNUSED(parmA); AIR_UNUSED(parmB); } static int parmConvert(double *parmDst, const double *parmSrc, const tenModel *modelSrc) { AIR_UNUSED(parmDst); AIR_UNUSED(parmSrc); AIR_UNUSED(modelSrc); return 0; } _TEN_SQE static void sqeGrad(double *grad, const double *parm0, const tenExperSpec *espec, double *dwiBuff, const double *dwiMeas, int knownB0) { AIR_UNUSED(grad); AIR_UNUSED(parm0); AIR_UNUSED(espec); AIR_UNUSED(dwiBuff); AIR_UNUSED(dwiMeas); AIR_UNUSED(knownB0); } _TEN_SQE_FIT(tenModelZero) _TEN_NLL _TEN_NLL_GRAD_STUB static double nllFit(double *parm, const tenExperSpec *espec, const double *dwiMeas, const double *parmInit, int rician, double sigma, int knownB0) { AIR_UNUSED(parm); AIR_UNUSED(espec); AIR_UNUSED(dwiMeas); AIR_UNUSED(parmInit); AIR_UNUSED(rician); AIR_UNUSED(sigma); AIR_UNUSED(knownB0); return AIR_NAN; } tenModel _tenModelZero = { TEN_MODEL_STR_ZERO, _TEN_MODEL_FIELDS }; const tenModel *const tenModelZero = &_tenModelZero; teem-1.11.0~svn6057/src/ten/privateTen.h0000664000175000017500000006132612165631065017471 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TEN_PRIVATE_HAS_BEEN_INCLUDED #define TEN_PRIVATE_HAS_BEEN_INCLUDED #ifdef __cplusplus extern "C" { #endif #define TEND_CMD(name, info) \ unrrduCmd tend_##name##Cmd = { #name, info, tend_##name##Main, AIR_FALSE } /* USAGE, PARSE: both copied verbatim from unrrdu/privateUnrrdu.h, but ** then some hacking was added . . . */ #define USAGE(info) \ if (!argc) { \ hestInfo(stdout, me, (info), hparm); \ hestUsage(stdout, hopt, me, hparm); \ hestGlossary(stdout, hopt, hparm); \ airMopError(mop); \ return 0; \ } /* JUSTPARSE is called by the tend functions that do *not* take an ** input 7-component tensor volume */ #define JUSTPARSE() \ if ((pret=hestParse(hopt, argc, argv, &perr, hparm))) { \ if (1 == pret) { \ fprintf(stderr, "%s: %s\n", me, perr); free(perr); \ hestUsage(stderr, hopt, me, hparm); \ airMopError(mop); \ return 2; \ } else { \ exit(1); \ } \ } /* ** PARSE is called by tend functions that do take a 7-component tensor ** volume, so that as a hack, we can process 6-component volumes as well, ** by padding on the confidence channel (fixed at 1.0) */ #define PARSE() \ JUSTPARSE(); \ if (4 == nin->dim \ && 6 == nin->axis[0].size \ && nrrdTypeBlock != nin->type) { \ ptrdiff_t padmin[4], padmax[4]; \ Nrrd *npadtmp; \ /* create a confidence channel by padding on 1s */ \ padmin[0] = -1; padmin[1] = padmin[2] = padmin[3] = 0; \ padmax[0] = nin->axis[0].size-1; \ padmax[1] = nin->axis[1].size-1; \ padmax[2] = nin->axis[2].size-1; \ padmax[3] = nin->axis[3].size-1; \ npadtmp = nrrdNew(); \ if (nrrdPad_nva(npadtmp, nin, padmin, padmax, nrrdBoundaryPad, 1.0) \ || nrrdCopy(nin, npadtmp)) { \ char *paderr; \ airMopAdd(mop, paderr = biffGetDone(NRRD), airFree, airMopAlways);\ fprintf(stderr, "%s: can't pad 6-comp tensor:\n%s", me, paderr); \ airMopError(mop); \ nrrdNuke(npadtmp); \ return 2; \ } \ nrrdNuke(npadtmp); \ } /* qseg.c: 2-tensor estimation */ extern void _tenQball(const double b, const int gradcount, const double svals[], const double grads[], double qvals[] ); extern void _tenSegsamp2(const int gradcount, const double qvals[], const double grads[], const double qpoints[], unsigned int seg[], double dists[] ); extern void _tenCalcdists(const int centcount, const double centroid[6], const int gradcount, const double qpoints[], double dists[] ); extern void _tenInitcent2(const int gradcount, const double qvals[], const double grads[], double centroids[6] ); extern int _tenCalccent2(const int gradcount, const double qpoints[], const double dists[], double centroid[6], unsigned int seg[] ); extern void _tenSeg2weights(const int gradcount, const int seg[], const int segcount, double weights[] ); extern void _tenQvals2points(const int gradcount, const double qvals[], const double grads[], double qpoints[] ); extern double _tenPldist(const double point[], const double line[] ); /* arishFuncs.c: Arish's implementation of Peled's 2-tensor fit */ #define VEC_SIZE 3 extern void twoTensFunc(double *p, double *x, int m, int n, void *data); extern void formTensor2D(double ten[7], double lam1, double lam3, double phi); /* qglox.c: stuff for quaternion geodesic-loxodromes that has dubious utility for the general public */ extern void tenQGLInterpTwoEvalK(double oeval[3], const double evalA[3], const double evalB[3], const double tt); extern void tenQGLInterpTwoEvalR(double oeval[3], const double evalA[3], const double evalB[3], const double tt); extern void tenQGLInterpTwoEvec(double oevec[9], const double evecA[9], const double evecB[9], double tt); extern void tenQGLInterpTwo(double oten[7], const double tenA[7], const double tenB[7], int ptype, double aa, tenInterpParm *tip); extern int _tenQGLInterpNEval(double evalOut[3], const double *evalIn, /* size 3 -by- NN */ const double *wght, /* size NN */ unsigned int NN, int ptype, tenInterpParm *tip); extern int _tenQGLInterpNEvec(double evecOut[9], const double *evecIn, /* size 9 -by- NN */ const double *wght, /* size NN */ unsigned int NN, tenInterpParm *tip); extern int tenQGLInterpN(double tenOut[7], const double *tenIn, const double *wght, unsigned int NN, int ptype, tenInterpParm *tip); /* experSpec.c */ TEN_EXPORT double _tenExperSpec_sqe(const double *dwiMeas, const double *dwiSim, const tenExperSpec *espec, int knownB0); TEN_EXPORT double _tenExperSpec_nll(const double *dwiMeas, const double *dwiSim, const tenExperSpec *espec, int rician, double sigma, int knownB0); /* tenModel.c */ TEN_EXPORT double _tenModelSqeFitSingle(const tenModel *model, double *testParm, double *grad, double *parm, double *convFrac, unsigned int *itersTaken, const tenExperSpec *espec, double *dwiBuff, const double *dwiMeas, const double *parmInit, int knownB0, unsigned int minIter, unsigned int maxIter, double convEps, int verbose); /* model*.c */ /* ** NOTE: these functions rely on per-model information (like PARM_NUM) ** or functions (like "simulate"), which is why these functions don't ** all use some common underlying function: more information would ** have to be passed (annoying for rapid hacking), and the function ** call overhead might be doubled */ #define _TEN_PARM_ALLOC \ static double * \ parmAlloc(void) { \ \ return AIR_CAST(double *, calloc(PARM_NUM ? PARM_NUM : 1, sizeof(double))); \ } #define _TEN_PARM_RAND \ static void \ parmRand(double *parm, airRandMTState *rng, int knownB0) { \ unsigned int ii; \ \ for (ii=knownB0 ? 1 : 0; ii parmDesc[ii].max) { \ parm1[ii] -= delta; \ } \ while (parm1[ii] < parmDesc[ii].min) { \ parm1[ii] += delta; \ } \ } else { \ parm1[ii] = AIR_CLAMP(parmDesc[ii].min, parm1[ii], parmDesc[ii].max); \ } \ if (parmDesc[ii].vec3 && 2 == parmDesc[ii].vecIdx) { \ double len; \ ELL_3V_NORM(parm1 + ii - 2, parm1 + ii - 2, len); \ } \ } \ } #define _TEN_PARM_DIST \ static double \ parmDist(const double *parmA, const double *parmB) { \ unsigned int ii; \ double dist; \ \ dist = 0; \ for (ii=0; iiaxis[dwiAx].size; bmat = AIR_CAST(double *, nbmat->data); dwiNum = 0; for (slIdx=0; slIdx 0.0; bmat += 6; } if (0 == dwiNum) { biffAddf(TEN, "%s: somehow got zero DWIs", me); airMopError(mop); return 1; } ndwi = AIR_CALLOC(dwiNum, Nrrd *); airMopAdd(mop, ndwi, (airMopper)airFree, airMopAlways); bmat = AIR_CAST(double *, nbmat->data); dwiIdx = -1; for (slIdx=0; slIdx 0.0) { dwiIdx++; ndwi[dwiIdx] = nrrdNew(); airMopAdd(mop, ndwi[dwiIdx], (airMopper)nrrdNuke, airMopAlways); if (nrrdSlice(ndwi[dwiIdx], nin4d, dwiAx, slIdx)) { biffMovef(TEN, NRRD, "%s: trouble slicing DWI at index %u", me, slIdx); airMopError(mop); return 1; } } bmat += 6; } if (_tenEpiRegThresholdFind(threshP, ndwi, dwiNum, AIR_FALSE, 1.5)) { biffAddf(TEN, "%s: trouble finding thresh", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } int tend_estimMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd **nin, *nin4d, *nbmat, *nterr, *nB0, *nout; char *outS, *terrS, *bmatS, *eb0S; float soft, scale, sigma; int dwiax, EE, knownB0, oldstuff, estmeth, verbose, fixneg; unsigned int ninLen, axmap[4], wlsi, *skip, skipNum, skipIdx; double valueMin, thresh; Nrrd *ngradKVP=NULL, *nbmatKVP=NULL; double bKVP, bval; tenEstimateContext *tec; hestOptAdd(&hopt, "old", NULL, airTypeInt, 0, 0, &oldstuff, NULL, "instead of the new tenEstimateContext code, use " "the old tenEstimateLinear code"); hestOptAdd(&hopt, "sigma", "sigma", airTypeFloat, 1, 1, &sigma, "nan", "Rician noise parameter"); hestOptAdd(&hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "0", "verbosity level"); hestOptAdd(&hopt, "est", "estimate method", airTypeEnum, 1, 1, &estmeth, "lls", "estimation method to use. \"lls\": linear-least squares", NULL, tenEstimate1Method); hestOptAdd(&hopt, "wlsi", "WLS iters", airTypeUInt, 1, 1, &wlsi, "1", "when using weighted-least-squares (\"-est wls\"), how " "many iterations to do after the initial weighted fit."); hestOptAdd(&hopt, "fixneg", NULL, airTypeInt, 0, 0, &fixneg, NULL, "after estimating the tensor, ensure that there are no negative " "eigenvalues by adding (to all eigenvalues) the amount by which " "the smallest is negative (corresponding to increasing the " "non-DWI image value)."); hestOptAdd(&hopt, "ee", "filename", airTypeString, 1, 1, &terrS, "", "Giving a filename here allows you to save out the tensor " "estimation error: a value which measures how much error there " "is between the tensor model and the given diffusion weighted " "measurements for each sample. By default, no such error " "calculation is saved."); hestOptAdd(&hopt, "eb", "filename", airTypeString, 1, 1, &eb0S, "", "In those cases where there is no B=0 reference image given " "(\"-knownB0 false\"), " "giving a filename here allows you to save out the B=0 image " "which is estimated from the data. By default, this image value " "is estimated but not saved."); hestOptAdd(&hopt, "t", "thresh", airTypeDouble, 1, 1, &thresh, "nan", "value at which to threshold the mean DWI value per pixel " "in order to generate the \"confidence\" mask. By default, " "the threshold value is calculated automatically, based on " "histogram analysis."); hestOptAdd(&hopt, "soft", "soft", airTypeFloat, 1, 1, &soft, "0", "how fuzzy the confidence boundary should be. By default, " "confidence boundary is perfectly sharp"); hestOptAdd(&hopt, "scale", "scale", airTypeFloat, 1, 1, &scale, "1", "After estimating the tensor, scale all of its elements " "(but not the confidence value) by this amount. Can help with " "downstream numerical precision if values are very large " "or small."); hestOptAdd(&hopt, "mv", "min val", airTypeDouble, 1, 1, &valueMin, "1.0", "minimum plausible value (especially important for linear " "least squares estimation)"); hestOptAdd(&hopt, "B", "B-list", airTypeString, 1, 1, &bmatS, NULL, "6-by-N list of B-matrices characterizing " "the diffusion weighting for each " "image. \"tend bmat\" is one source for such a matrix; see " "its usage info for specifics on how the coefficients of " "the B-matrix are ordered. " "An unadorned plain text file is a great way to " "specify the B-matrix.\n **OR**\n " "Can say just \"-B kvp\" to try to learn B matrices from " "key/value pair information in input images."); hestOptAdd(&hopt, "b", "b", airTypeDouble, 1, 1, &bval, "nan", "\"b\" diffusion-weighting factor (units of sec/mm^2)"); hestOptAdd(&hopt, "knownB0", "bool", airTypeBool, 1, 1, &knownB0, NULL, "Indicates if the B=0 non-diffusion-weighted reference image " "is known, or if it has to be estimated along with the tensor " "elements.\n " "\b\bo if \"true\": in the given list of diffusion gradients or " "B-matrices, there are one or more with zero norm, which are " "simply averaged to find the B=0 reference image value\n " "\b\bo if \"false\": there may or may not be diffusion-weighted " "images among the input; the B=0 image value is going to be " "estimated along with the diffusion model"); hestOptAdd(&hopt, "i", "dwi0 dwi1", airTypeOther, 1, -1, &nin, "-", "all the diffusion-weighted images (DWIs), as separate 3D nrrds, " "**OR**: One 4D nrrd of all DWIs stacked along axis 0", &ninLen, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output tensor volume"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_estimInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); nbmat = nrrdNew(); airMopAdd(mop, nbmat, (airMopper)nrrdNuke, airMopAlways); /* figure out B-matrix */ if (strcmp("kvp", airToLower(bmatS))) { /* its NOT coming from key/value pairs */ if (!AIR_EXISTS(bval)) { fprintf(stderr, "%s: need to specify scalar b-value\n", me); airMopError(mop); return 1; } if (nrrdLoad(nbmat, bmatS, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble loading B-matrix:\n%s\n", me, err); airMopError(mop); return 1; } nin4d = nin[0]; skip = NULL; skipNum = 0; } else { /* it IS coming from key/value pairs */ if (1 != ninLen) { fprintf(stderr, "%s: require a single 4-D DWI volume for " "key/value pair based calculation of B-matrix\n", me); airMopError(mop); return 1; } if (oldstuff) { if (knownB0) { fprintf(stderr, "%s: sorry, key/value-based DWI info not compatible " "with older implementation of knownB0\n", me); airMopError(mop); return 1; } } if (tenDWMRIKeyValueParse(&ngradKVP, &nbmatKVP, &bKVP, &skip, &skipNum, nin[0])) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } if (AIR_EXISTS(bval)) { fprintf(stderr, "%s: WARNING: key/value pair derived b-value %g " "over-riding %g from command-line", me, bKVP, bval); } bval = bKVP; if (ngradKVP) { airMopAdd(mop, ngradKVP, (airMopper)nrrdNuke, airMopAlways); if (tenBMatrixCalc(nbmat, ngradKVP)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble finding B-matrix:\n%s\n", me, err); airMopError(mop); return 1; } } else { airMopAdd(mop, nbmatKVP, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nbmat, nbmatKVP, nrrdTypeDouble)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble converting B-matrix:\n%s\n", me, err); airMopError(mop); return 1; } } /* this will work because of the impositions of tenDWMRIKeyValueParse */ dwiax = ((nrrdKindList == nin[0]->axis[0].kind || nrrdKindVector == nin[0]->axis[0].kind) ? 0 : ((nrrdKindList == nin[0]->axis[1].kind || nrrdKindVector == nin[0]->axis[1].kind) ? 1 : ((nrrdKindList == nin[0]->axis[2].kind || nrrdKindVector == nin[0]->axis[2].kind) ? 2 : 3))); if (0 == dwiax) { nin4d = nin[0]; } else { axmap[0] = dwiax; axmap[1] = 1 > dwiax ? 1 : 0; axmap[2] = 2 > dwiax ? 2 : 1; axmap[3] = 3 > dwiax ? 3 : 2; nin4d = nrrdNew(); airMopAdd(mop, nin4d, (airMopper)nrrdNuke, airMopAlways); if (nrrdAxesPermute(nin4d, nin[0], axmap)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble creating DWI volume:\n%s\n", me, err); airMopError(mop); return 1; } } } nterr = NULL; nB0 = NULL; if (!oldstuff) { if (1 != ninLen) { fprintf(stderr, "%s: sorry, currently need single 4D volume " "for new implementation\n", me); airMopError(mop); return 1; } if (!AIR_EXISTS(thresh)) { if (tend_estimThresholdFind(&thresh, nbmat, nin4d)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble finding threshold:\n%s\n", me, err); airMopError(mop); return 1; } /* HACK to lower threshold a titch */ thresh *= 0.93; fprintf(stderr, "%s: using mean DWI threshold %g\n", me, thresh); } tec = tenEstimateContextNew(); tec->progress = AIR_TRUE; airMopAdd(mop, tec, (airMopper)tenEstimateContextNix, airMopAlways); EE = 0; if (!EE) tenEstimateVerboseSet(tec, verbose); if (!EE) tenEstimateNegEvalShiftSet(tec, fixneg); if (!EE) EE |= tenEstimateMethodSet(tec, estmeth); if (!EE) EE |= tenEstimateBMatricesSet(tec, nbmat, bval, !knownB0); if (!EE) EE |= tenEstimateValueMinSet(tec, valueMin); for (skipIdx=0; skipIdxrecordErrorLogDwi = AIR_TRUE; /* tec->recordErrorDwi = AIR_TRUE; */ } break; case tenEstimate1MethodNLS: if (airStrlen(terrS)) { tec->recordErrorDwi = AIR_TRUE; } break; case tenEstimate1MethodWLS: if (!EE) tec->WLSIterNum = wlsi; if (airStrlen(terrS)) { tec->recordErrorDwi = AIR_TRUE; } break; case tenEstimate1MethodMLE: if (!(AIR_EXISTS(sigma) && sigma > 0.0)) { fprintf(stderr, "%s: can't do %s w/out sigma > 0 (not %g)\n", me, airEnumStr(tenEstimate1Method, tenEstimate1MethodMLE), sigma); airMopError(mop); return 1; } if (!EE) EE |= tenEstimateSigmaSet(tec, sigma); if (airStrlen(terrS)) { tec->recordLikelihoodDwi = AIR_TRUE; } break; } if (!EE) EE |= tenEstimateThresholdSet(tec, thresh, soft); if (!EE) EE |= tenEstimateUpdate(tec); if (EE) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting up estimation:\n%s\n", me, err); airMopError(mop); return 1; } if (tenEstimate1TensorVolume4D(tec, nout, &nB0, airStrlen(terrS) ? &nterr : NULL, nin4d, nrrdTypeFloat)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble doing estimation:\n%s\n", me, err); airMopError(mop); return 1; } if (airStrlen(terrS)) { airMopAdd(mop, nterr, (airMopper)nrrdNuke, airMopAlways); } } else { EE = 0; if (1 == ninLen) { EE = tenEstimateLinear4D(nout, airStrlen(terrS) ? &nterr : NULL, &nB0, nin4d, nbmat, knownB0, thresh, soft, bval); } else { EE = tenEstimateLinear3D(nout, airStrlen(terrS) ? &nterr : NULL, &nB0, (const Nrrd*const*)nin, ninLen, nbmat, knownB0, thresh, soft, bval); } if (EE) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making tensor volume:\n%s\n", me, err); airMopError(mop); return 1; } } if (nterr) { /* it was allocated by tenEstimate*, we have to clean it up */ airMopAdd(mop, nterr, (airMopper)nrrdNuke, airMopAlways); } if (nB0) { /* it was allocated by tenEstimate*, we have to clean it up */ airMopAdd(mop, nB0, (airMopper)nrrdNuke, airMopAlways); } if (1 != scale) { if (tenSizeScale(nout, nout, scale)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble doing scaling:\n%s\n", me, err); airMopError(mop); return 1; } } if (nterr) { if (nrrdSave(terrS, nterr, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing error image:\n%s\n", me, err); airMopError(mop); return 1; } } if (!knownB0 && airStrlen(eb0S)) { if (nrrdSave(eb0S, nB0, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing estimated B=0 image:\n%s\n", me, err); airMopError(mop); return 1; } } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(estim, INFO); teem-1.11.0~svn6057/src/ten/qglox.c0000664000175000017500000003660112165631065016473 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" /* ** computes (r1 - r0)/(log(r1) - log(r0)) */ double _tenQGL_blah(double rr0, double rr1) { double bb, ret; if (rr1 > rr0) { /* the bb calculation below could blow up, so we recurse with flipped order */ ret = _tenQGL_blah(rr1, rr0); } else { /* rr1 <= rr0 --> rr1/rr0 <= 1 --> rr1/rr0 - 1 <= 0 --> bb <= 0 */ /* and rr1 >= 0 --> rr1/rr0 >= 0 --> rr1/rr0 - 1 >= -1 --> bb >= -1 */ bb = rr0 ? (rr1/rr0 - 1) : 0; if (bb > -0.0001) { ret = rr0*(1 + bb*(0.5001249976477329 - bb*(7.0/6 + bb*(1.0/6 - bb/720.0)))); } else { /* had trouble finding a high-quality approximation for b near -1 */ bb = AIR_MAX(bb, -1 + 100*FLT_EPSILON); ret = rr0*bb/log(bb + 1); } } return ret; } #define rr0 (RThZA[0]) #define rr1 (RThZB[0]) #define rr (oRThZ[0]) #define th0 (RThZA[1]) #define th1 (RThZB[1]) #define th (oRThZ[1]) #define zz0 (RThZA[2]) #define zz1 (RThZB[2]) #define zz (oRThZ[2]) void tenQGLInterpTwoEvalK(double oeval[3], const double evalA[3], const double evalB[3], const double tt) { double RThZA[3], RThZB[3], oRThZ[3], bb; tenTripleConvertSingle_d(RThZA, tenTripleTypeRThetaZ, evalA, tenTripleTypeEigenvalue); tenTripleConvertSingle_d(RThZB, tenTripleTypeRThetaZ, evalB, tenTripleTypeEigenvalue); if (rr1 > rr0) { /* the bb calculation below could blow up, so we recurse with flipped order */ tenQGLInterpTwoEvalK(oeval, evalB, evalA, 1-tt); } else { rr = AIR_LERP(tt, rr0, rr1); zz = AIR_LERP(tt, zz0, zz1); bb = rr0 ? (rr1/rr0 - 1) : 0; /* bb can't be positive, because rr1 <= rr0 enforced above, so below is really test for -0.001 < bb <= 0 */ if (bb > -0.0001) { double dth; dth = th1 - th0; /* rr0 and rr1 are similar, use stable approximation */ th = th0 + tt*(dth + (0.5 - tt/2)*dth*bb + (-1.0/12 - tt/4 + tt*tt/3)*dth*bb*bb + (1.0/24 + tt/24 + tt*tt/6 - tt*tt*tt/4)*dth*bb*bb*bb); } else { /* use original formula */ /* have to clamp value of b so that log() values don't explode */ bb = AIR_MAX(bb, -1 + 100*FLT_EPSILON); th = th0 + (th1 - th0)*log(1 + bb*tt)/log(1 + bb); } tenTripleConvertSingle_d(oeval, tenTripleTypeEigenvalue, oRThZ, tenTripleTypeRThetaZ); /* fprintf(stderr, "%s: (b = %g) %g %g %g <-- %g %g %g\n", "blah", bb, oeval[0], oeval[1], oeval[2], oRThZ[0], oRThZ[1], oRThZ[2]); */ } } double _tenQGL_Kdist(const double RThZA[3], const double RThZB[3]) { double dr, dth, dz, bl, dist; dr = rr1 - rr0; bl = _tenQGL_blah(rr0, rr1); dth = th1 - th0; dz = zz1 - zz0; dist = sqrt(dr*dr + bl*bl*dth*dth + dz*dz); return dist; } void _tenQGL_Klog(double klog[3], const double RThZA[3], const double RThZB[3]) { double dr, bl, dth, dz; dr = rr1 - rr0; bl = _tenQGL_blah(rr0, rr1); dth = th1 - th0; dz = zz1 - zz0; ELL_3V_SET(klog, dr, bl*dth, dz); return; } void _tenQGL_Kexp(double RThZB[3], const double RThZA[3], const double klog[3]) { double bl; rr1 = rr0 + klog[0]; bl = _tenQGL_blah(rr0, rr1); th1 = th0 + (bl ? klog[1]/bl : 0); zz1 = zz0 + klog[2]; return; } #undef rr0 #undef rr1 #undef rr #undef th0 #undef th1 #undef th #undef zz0 #undef zz1 #undef zz /* ** stable computation of (ph1 - ph0)/(log(tan(ph1/2)) - log(tan(ph0/2))) */ double _tenQGL_fooo(double ph1, double ph0) { double ret; if (ph0 > ph1) { ret = _tenQGL_fooo(ph0, ph1); } else if (0 == ph0/2) { ret = 0; } else { /* ph1 >= ph0 > 0 */ if (ph1 - ph0 < 0.0001) { double dph, ss, cc; dph = ph1 - ph0; ss = sin(ph1); cc = cos(ph1); ret = (ss + cc*dph/2 + ((cos(2*ph1) - 3)/ss)*dph*dph/24 + (cc/(ss*ss))*dph*dph*dph/24); } else { ret = (ph1 - ph0)/(log(tan(ph1/2)) - log(tan(ph0/2))); } } return ret; } #define rr0 (RThPhA[0]) #define rr1 (RThPhB[0]) #define rr (oRThPh[0]) #define th0 (RThPhA[1]) #define th1 (RThPhB[1]) #define th (oRThPh[1]) #define ph0 (RThPhA[2]) #define ph1 (RThPhB[2]) #define ph (oRThPh[2]) void _tenQGL_Rlog(double rlog[3], const double RThPhA[3], const double RThPhB[3]) { double dr, dth, dph, bl, fo; dr = rr1 - rr0; dth = th1 - th0; dph = ph1 - ph0; bl = _tenQGL_blah(rr0, rr1); fo = _tenQGL_fooo(ph0, ph1); /* rlog[0] rlog[1] rlog[2] */ ELL_3V_SET(rlog, dr, bl*dth*fo, dph*bl); } void _tenQGL_Rexp(double RThPhB[3], const double RThPhA[3], const double rlog[3]) { double bl, fo; rr1 = rr0 + rlog[0]; bl = _tenQGL_blah(rr0, rr1); ph1 = ph0 + (bl ? rlog[2]/bl : 0); fo = _tenQGL_fooo(ph0, ph1); th1 = th0 + (bl*fo ? rlog[1]/(bl*fo) : 0); return; } /* unlike with the K stuff, with the R stuff I seemed to have more luck implementing pair-wise interpolation in terms of log and exp */ void tenQGLInterpTwoEvalR(double oeval[3], const double evalA[3], const double evalB[3], const double tt) { double RThPhA[3], RThPhB[3], rlog[3], oRThPh[3]; tenTripleConvertSingle_d(RThPhA, tenTripleTypeRThetaPhi, evalA, tenTripleTypeEigenvalue); tenTripleConvertSingle_d(RThPhB, tenTripleTypeRThetaPhi, evalB, tenTripleTypeEigenvalue); _tenQGL_Rlog(rlog, RThPhA, RThPhB); ELL_3V_SCALE(rlog, tt, rlog); _tenQGL_Rexp(oRThPh, RThPhA, rlog); tenTripleConvertSingle_d(oeval, tenTripleTypeEigenvalue, oRThPh, tenTripleTypeRThetaPhi); return; } double _tenQGL_Rdist(const double RThPhA[3], const double RThPhB[3]) { double dr, dth, dph, bl, fo; dr = rr1 - rr0; dth = th1 - th0; dph = ph1 - ph0; bl = _tenQGL_blah(rr0, rr1); fo = _tenQGL_fooo(ph0, ph1); return sqrt(dr*dr + bl*bl*(dth*dth*fo*fo + dph*dph)); } #undef rr0 #undef rr1 #undef rr #undef th0 #undef th1 #undef th #undef ph0 #undef ph1 #undef ph /* returns the index into unitq[] of the quaternion that led to the right alignment. If it was already aligned, this will be 0, because unitq[0] is the identity quaternion */ int _tenQGL_q_align(double qOut[4], const double qRef[4], const double qIn[4]) { unsigned int ii, maxDotIdx; double unitq[8][4] = {{+1, 0, 0, 0}, {-1, 0, 0, 0}, {0, +1, 0, 0}, {0, -1, 0, 0}, {0, 0, +1, 0}, {0, 0, -1, 0}, {0, 0, 0, +1}, {0, 0, 0, -1}}; double dot[8], qInMul[8][4], maxDot; for (ii=0; ii<8; ii++) { ell_q_mul_d(qInMul[ii], qIn, unitq[ii]); dot[ii] = ELL_4V_DOT(qRef, qInMul[ii]); } maxDotIdx = 0; maxDot = dot[maxDotIdx]; for (ii=1; ii<8; ii++) { if (dot[ii] > maxDot) { maxDotIdx = ii; maxDot = dot[maxDotIdx]; } } ELL_4V_COPY(qOut, qInMul[maxDotIdx]); return maxDotIdx; } void tenQGLInterpTwoEvec(double oevec[9], const double evecA[9], const double evecB[9], double tt) { double rotA[9], rotB[9], orot[9], oq[4], qA[4], qB[4], _qB[4], qdiv[4], angle, axis[3], qq[4]; ELL_3M_TRANSPOSE(rotA, evecA); ELL_3M_TRANSPOSE(rotB, evecB); ell_3m_to_q_d(qA, rotA); ell_3m_to_q_d(_qB, rotB); _tenQGL_q_align(qB, qA, _qB); /* there's probably a faster way to do this slerp qA --> qB */ ell_q_div_d(qdiv, qA, qB); /* div = A^-1 * B */ angle = ell_q_to_aa_d(axis, qdiv); ell_aa_to_q_d(qq, angle*tt, axis); ell_q_mul_d(oq, qA, qq); ell_q_to_3m_d(orot, oq); ELL_3M_TRANSPOSE(oevec, orot); } void tenQGLInterpTwo(double oten[7], const double tenA[7], const double tenB[7], int ptype, double tt, tenInterpParm *tip) { double oeval[3], evalA[3], evalB[3], oevec[9], evecA[9], evecB[9], cc; AIR_UNUSED(tip); tenEigensolve_d(evalA, evecA, tenA); tenEigensolve_d(evalB, evecB, tenB); cc = AIR_LERP(tt, tenA[0], tenB[0]); if (tenInterpTypeQuatGeoLoxK == ptype) { tenQGLInterpTwoEvalK(oeval, evalA, evalB, tt); } else { tenQGLInterpTwoEvalR(oeval, evalA, evalB, tt); } tenQGLInterpTwoEvec(oevec, evecA, evecB, tt); tenMakeSingle_d(oten, cc, oeval, oevec); return; } /* ** This does (non-optionally) use biff, to report convergence failures ** ** we do in fact require non-NULL tip, because it holds the buffers we need */ int _tenQGLInterpNEval(double evalOut[3], const double *evalIn, /* size 3 -by- NN */ const double *wght, /* size NN */ unsigned int NN, int ptype, tenInterpParm *tip) { static const char me[]="_tenQGLInterpNEval"; double RTh_Out[3], elen; unsigned int ii, iter; int rttype; void (*llog)(double lg[3], const double RTh_A[3], const double RTh_B[3]); void (*lexp)(double RTh_B[3], const double RTh_A[3], const double lg[3]); if (!(evalOut && evalIn && tip)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } /* convert to (R,Th,_) and initialize RTh_Out */ if (tenInterpTypeQuatGeoLoxK == ptype) { rttype = tenTripleTypeRThetaZ; llog = _tenQGL_Klog; lexp = _tenQGL_Kexp; } else { rttype = tenTripleTypeRThetaPhi; llog = _tenQGL_Rlog; lexp = _tenQGL_Rexp; } ELL_3V_SET(RTh_Out, 0, 0, 0); for (ii=0; iirtIn + 3*ii, rttype, evalIn + 3*ii, tenTripleTypeEigenvalue); ww = wght ? wght[ii] : 1.0/NN; ELL_3V_SCALE_INCR(RTh_Out, ww, tip->rtIn + 3*ii); } /* compute iterated weighted mean, stored in RTh_Out */ iter = 0; do { double logavg[3]; /* take log of everyone */ for (ii=0; iirtLog + 3*ii, RTh_Out, tip->rtIn + 3*ii); } /* average, and find length */ ELL_3V_SET(logavg, 0, 0, 0); for (ii=0; iirtLog + 3*ii); } elen = ELL_3V_LEN(logavg); lexp(RTh_Out, RTh_Out, logavg); iter++; } while ((!tip->maxIter || iter < tip->maxIter) && elen > tip->convEps); if (elen > tip->convEps) { ELL_3V_SET(evalOut, AIR_NAN, AIR_NAN, AIR_NAN); biffAddf(TEN, "%s: still have error %g (> eps %g) after max %d iters", me, elen, tip->convEps, tip->maxIter); return 1; } /* finish, convert to eval */ tenTripleConvertSingle_d(evalOut, tenTripleTypeEigenvalue, RTh_Out, rttype); return 0; } double _tenQGL_q_interdot(unsigned int *centerIdxP, double *qq, double *inter, unsigned int NN) { unsigned int ii, jj; double sum, dot, max; for (jj=0; jj max) { *centerIdxP = jj; max = inter[0 + NN*(*centerIdxP)]; } } return sum; } /* ** This does (non-optionally) use biff, to report convergence failures ** ** we do in fact require non-NULL tip, because it holds the buffers we need */ int _tenQGLInterpNEvec(double evecOut[9], const double *evecIn, /* size 9 -by- NN */ const double *wght, /* size NN */ unsigned int NN, tenInterpParm *tip) { static const char me[]="_tenQGLInterpNEvec"; double qOut[4], maxWght, len, /* odsum, */ dsum, rot[9]; unsigned int ii, centerIdx=0, fix, qiter; if (!( evecOut && evecIn && tip )) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } /* convert to quaternions */ for (ii=0; iiqIn + 4*ii, rot); } /* HEY: what should this be used for? variable odsum set but not used */ /* odsum = _tenQGL_q_interdot(¢erIdx, tip->qIn, tip->qInter, NN); */ /* find quaternion with maximal weight, use it as is (decree that its the right representative), and then align rest with that. This is actually principled; symmetry allows it */ centerIdx = 0; if (wght) { maxWght = wght[centerIdx]; for (ii=1; ii maxWght) { centerIdx = ii; maxWght = wght[centerIdx]; } } } for (ii=0; iiqIn + 4*ii, tip->qIn + 4*centerIdx, tip->qIn + 4*ii); } dsum = _tenQGL_q_interdot(¢erIdx, tip->qIn, tip->qInter, NN); /* try to settle on tightest set of representatives */ qiter = 0; do { fix = 0; for (ii=0; iiqIn + 4*ii, tip->qIn + 4*centerIdx, tip->qIn + 4*ii); fix = AIR_MAX(fix, ff); } dsum = _tenQGL_q_interdot(¢erIdx, tip->qIn, tip->qInter, NN); if (tip->maxIter && qiter > tip->maxIter) { biffAddf(TEN, "%s: q tightening unconverged after %u iters; " "interdot = %g -> maxfix = %u; center = %u\n", me, tip->maxIter, dsum, fix, centerIdx); return 1; } qiter++; } while (fix); /* fprintf(stderr, "!%s: dsum %g --%u--> %g\n", me, odsum, qiter, dsum); */ /* make sure they're normalized */ for (ii=0; iiqIn + 4*ii, tip->qIn + 4*ii, len); } /* compute iterated weighted mean, stored in qOut */ if (ell_q_avgN_d(qOut, &qiter, tip->qIn, tip->qBuff, wght, NN, tip->convEps, tip->maxIter)) { biffMovef(TEN, ELL, "%s: problem doing quaternion mean", me); return 1; } /* fprintf(stderr, "!%s: q avg converged in %u\n", me, qiter); */ /* finish, convert back to evec */ ell_q_to_3m_d(rot, qOut); ELL_3M_TRANSPOSE(evecOut, rot); return 0; } teem-1.11.0~svn6057/src/ten/tendEpireg.c0000664000175000017500000002263512165631065017431 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Register diffusion-weighted echo-planar images" static const char *_tend_epiregInfoL = (INFO ". This registration corrects the shear, scale, and translate along " "the phase encoding direction (assumed to be the Y (second) axis of " "the image) caused by eddy currents from the diffusion-encoding " "gradients with echo-planar imaging. The method is based on calculating " "moments of segmented images, where the segmentation is a simple " "procedure based on blurring (optional), thresholding and " "connected component analysis. " "The registered DWIs are resampled with the " "chosen kernel, with the separate DWIs stacked along axis 0."); int tend_epiregMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret, rret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; char *outS, *buff; char *gradS; NrrdKernelSpec *ksp; Nrrd **nin, **nout3D, *nout4D, *ngrad, *ngradKVP, *nbmatKVP; unsigned int ni, ninLen, *skip, skipNum; int ref, noverbose, progress, nocc, baseNum; float bw[2], thr, fitFrac; double bvalue; hestOptAdd(&hopt, "i", "dwi0 dwi1", airTypeOther, 1, -1, &nin, NULL, "all the diffusion-weighted images (DWIs), as separate 3D nrrds, " "**OR**: one 4D nrrd of all DWIs stacked along axis 0", &ninLen, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "g", "grads", airTypeString, 1, 1, &gradS, NULL, "array of gradient directions, in the same order as the " "associated DWIs were given to \"-i\", " "**OR** \"-g kvp\" signifies that gradient directions should " "be read from the key/value pairs of the DWI", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "r", "reference", airTypeInt, 1, 1, &ref, "-1", "which of the DW volumes (zero-based numbering) should be used " "as the standard, to which all other images are transformed. " "Using -1 (the default) means that 9 intrinsic parameters " "governing the relationship between the gradient direction " "and the resulting distortion are estimated and fitted, " "ensuring good registration with the non-diffusion-weighted " "T2 image (which is never explicitly used in registration). " "Otherwise, by picking a specific DWI, no distortion parameter " "estimation is done. "); hestOptAdd(&hopt, "nv", NULL, airTypeInt, 0, 0, &noverbose, NULL, "turn OFF verbose mode, and " "have no idea what stage processing is at."); hestOptAdd(&hopt, "p", NULL, airTypeInt, 0, 0, &progress, NULL, "save out intermediate steps of processing"); hestOptAdd(&hopt, "bw", "x,y blur", airTypeFloat, 2, 2, bw, "1.0 2.0", "standard devs in X and Y directions of gaussian filter used " "to blur the DWIs prior to doing segmentation. This blurring " "does not effect the final resampling of registered DWIs. " "Use \"0.0 0.0\" to say \"no blurring\""); hestOptAdd(&hopt, "t", "DWI thresh", airTypeFloat, 1, 1, &thr, "nan", "Threshold value to use on DWIs, " "to do initial separation of brain and non-brain. By default, " "the threshold is determined automatically by histogram " "analysis. "); hestOptAdd(&hopt, "ncc", NULL, airTypeInt, 0, 0, &nocc, NULL, "do *NOT* do connected component (CC) analysis, after " "thresholding and before moment calculation. Doing CC analysis " "usually gives better results because it converts the " "thresholding output into something much closer to a " "real segmentation"); hestOptAdd(&hopt, "f", "fit frac", airTypeFloat, 1, 1, &fitFrac, "0.70", "(only meaningful with \"-r -1\") When doing linear fitting " "of the intrinsic distortion parameters, it is good " "to ignore the slices for which the segmentation was poor. A " "heuristic is used to rank the slices according to segmentation " "quality. This option controls how many of the (best) slices " "contribute to the fitting. Use \"0\" to disable distortion " "parameter fitting. "); hestOptAdd(&hopt, "k", "kernel", airTypeOther, 1, 1, &ksp, "cubic:0,0.5", "kernel for resampling DWIs along the phase-encoding " "direction during final registration stage", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "s", "start #", airTypeInt, 1, 1, &baseNum, "1", "first number to use in numbered sequence of output files."); hestOptAdd(&hopt, "o", "output/prefix", airTypeString, 1, 1, &outS, "-", "For separate 3D DWI volume inputs: prefix for output filenames; " "will save out one (registered) " "DWI for each input DWI, using the same type as the input. " "**OR**: For single 4D DWI input: output file name. "); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_epiregInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (strcmp("kvp", gradS)) { /* they're NOT coming from key/value pairs */ if (nrrdLoad(ngrad=nrrdNew(), gradS, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble loading gradient list:\n%s\n", me, err); airMopError(mop); return 1; } } else { if (1 != ninLen) { fprintf(stderr, "%s: can do key/value pairs only from single nrrd", me); airMopError(mop); return 1; } /* they are coming from key/value pairs */ if (tenDWMRIKeyValueParse(&ngradKVP, &nbmatKVP, &bvalue, &skip, &skipNum, nin[0])) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing gradient list:\n%s\n", me, err); airMopError(mop); return 1; } if (nbmatKVP) { fprintf(stderr, "%s: sorry, can only use gradients, not b-matrices", me); airMopError(mop); return 1; } ngrad = ngradKVP; } airMopAdd(mop, ngrad, (airMopper)nrrdNuke, airMopAlways); nout3D = AIR_CALLOC(ninLen, Nrrd *); airMopAdd(mop, nout3D, airFree, airMopAlways); nout4D = nrrdNew(); airMopAdd(mop, nout4D, (airMopper)nrrdNuke, airMopAlways); buff = AIR_CALLOC(airStrlen(outS) + 10, char); airMopAdd(mop, buff, airFree, airMopAlways); if (!( nout3D && nout4D && buff )) { fprintf(stderr, "%s: couldn't allocate buffers", me); airMopError(mop); return 1; } for (ni=0; nikernel, ksp->parm, progress, !noverbose); } else { rret = tenEpiRegister3D(nout3D, nin, ninLen, ngrad, ref, bw[0], bw[1], fitFrac, thr, !nocc, ksp->kernel, ksp->parm, progress, !noverbose); } if (rret) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble doing epireg:\n%s\n", me, err); airMopError(mop); return 1; } if (1 == ninLen) { if (nrrdSave(outS, nout4D, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing \"%s\":\n%s\n", me, outS, err); airMopError(mop); return 1; } } else { for (ni=0; ni 99) { sprintf(buff, "%s%05d.nrrd", outS, ni+baseNum); } else if (ninLen+baseNum > 9) { sprintf(buff, "%s%02d.nrrd", outS, ni+baseNum); } else { sprintf(buff, "%s%d.nrrd", outS, ni+baseNum); } if (nrrdSave(buff, nout3D[ni], NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing \"%s\":\n%s\n", me, buff, err); airMopError(mop); return 1; } } } airMopOkay(mop); return 0; } TEND_CMD(epireg, INFO); teem-1.11.0~svn6057/src/ten/tendAnplot.c0000664000175000017500000000730612165631065017451 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Graph anisotropy metric in barycentric coords" static const char *_tend_anplotInfoL = (INFO ". The metrics all vary from 0.0 to 1.0, and will be sampled " "in the lower right half of the image. The plane on which they are " "sampled is a surface of constant trace. You may want to use " "\"unu resample -s = x0.57735 -k tent\" to transform the triangle into " "a 30-60-90 triangle, and \"ilk -t 1,-0.5,0,0,0.866,0 -k tent " "-0 u:0,1 -b pad -bg 0\" (possibly followed by " "teem/src/limntest/triimg) to transform the domain into an equilateral " "triangle."); int tend_anplotMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int res, aniso, whole, nanout, hflip; Nrrd *nout; char *outS; hestOptAdd(&hopt, "r", "res", airTypeInt, 1, 1, &res, "256", "resolution of anisotropy plot"); hestOptAdd(&hopt, "w", NULL, airTypeInt, 0, 0, &whole, NULL, "sample the whole triangle of constant trace, " "instead of just the " "sixth of it in which the eigenvalues have the " "traditional sorted order. "); hestOptAdd(&hopt, "hflip", NULL, airTypeInt, 0, 0, &hflip, NULL, "flip the two bottom corners (swapping the place of " "linear and planar)"); hestOptAdd(&hopt, "nan", NULL, airTypeInt, 0, 0, &nanout, NULL, "set the pixel values outside the triangle to be NaN, " "instead of 0"); hestOptAdd(&hopt, "a", "aniso", airTypeEnum, 1, 1, &aniso, NULL, "Which anisotropy metric to plot. " TEN_ANISO_DESC, NULL, tenAniso); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_anplotInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenAnisoPlot(nout, aniso, res, hflip, whole, nanout)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making plot:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(anplot, INFO); teem-1.11.0~svn6057/src/ten/tendEval.c0000664000175000017500000001151112165631065017074 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Calculate one or more eigenvalues in a DT volume" static const char *_tend_evalInfoL = (INFO ". "); int tend_evalMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret, map[4]; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int ret, *comp, compLen, cc; Nrrd *nin, *nout; char *outS; float thresh, *edata, *tdata, eval[3], evec[9]; size_t N, I, sx, sy, sz; hestOptAdd(&hopt, "c", "c0 ", airTypeInt, 1, 3, &comp, NULL, "which eigenvalues should be saved out. \"0\" for the " "largest, \"1\" for the middle, \"2\" for the smallest, " "\"0 1\", \"1 2\", \"0 1 2\" or similar for more than one", &compLen); hestOptAdd(&hopt, "t", "thresh", airTypeFloat, 1, 1, &thresh, "0.5", "confidence threshold"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_evalInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); for (cc=0; ccaxis[1].size; sy = nin->axis[2].size; sz = nin->axis[3].size; nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (1 == compLen) { ret = nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 3, sx, sy, sz); } else { ret = nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 4, AIR_CAST(size_t, compLen), sx, sy, sz); } if (ret) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble allocating output:\n%s\n", me, err); airMopError(mop); return 1; } N = sx*sy*sz; edata = (float *)nout->data; tdata = (float *)nin->data; if (1 == compLen) { ELL_3V_SET(map, 1, 2, 3); for (I=0; I= thresh)*eval[comp[0]]; tdata += 7; } } else { ELL_4V_SET(map, 0, 1, 2, 3); for (I=0; I= thresh)*eval[comp[cc]]; edata += compLen; tdata += 7; } } if (nrrdAxisInfoCopy(nout, nin, map, NRRD_AXIS_INFO_SIZE_BIT)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (1 != compLen) { nout->axis[0].label = (char *)airFree(nout->axis[0].label); nout->axis[0].kind = nrrdKindUnknown; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(eval, INFO); teem-1.11.0~svn6057/src/ten/tensor.c0000664000175000017500000010345212165631065016652 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" int tenVerbose = 0; void tenRotateSingle_f(float tenOut[7], const float rot[9], const float tenIn[7]) { float rotT[9], matIn[9], tmp[9], matOut[9]; ELL_3M_TRANSPOSE(rotT, rot); TEN_T2M(matIn, tenIn); ELL_3M_MUL(tmp, matIn, rotT); ELL_3M_MUL(matOut, rot, tmp); TEN_M2T_TT(tenOut, float, matOut); tenOut[0] = tenIn[0]; return; } /* ******** tenTensorCheck() ** ** describes if the given nrrd could be a diffusion tensor dataset, ** either the measured DWI data or the calculated tensor data. ** ** We've been using 7 floats for BOTH kinds of tensor data- both the ** measured DWI and the calculated tensor matrices. The measured data ** comes as one anatomical image and 6 DWIs. For the calculated tensors, ** in addition to the 6 matrix components, we keep a "threshold" value ** which is based on the sum of all the DWIs, which describes if the ** calculated tensor means anything or not. ** ** useBiff controls if biff is used to describe the problem */ int tenTensorCheck(const Nrrd *nin, int wantType, int want4D, int useBiff) { static const char me[]="tenTensorCheck"; if (!nin) { if (useBiff) biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (wantType) { if (nin->type != wantType) { if (useBiff) biffAddf(TEN, "%s: wanted type %s, got type %s", me, airEnumStr(nrrdType, wantType), airEnumStr(nrrdType, nin->type)); return 1; } } else { if (!(nin->type == nrrdTypeFloat || nin->type == nrrdTypeShort)) { if (useBiff) biffAddf(TEN, "%s: need data of type float or short", me); return 1; } } if (want4D && !(4 == nin->dim)) { if (useBiff) biffAddf(TEN, "%s: given dimension is %d, not 4", me, nin->dim); return 1; } if (!(7 == nin->axis[0].size)) { if (useBiff) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: axis 0 has size %s, not 7", me, airSprintSize_t(stmp, nin->axis[0].size)); } return 1; } return 0; } int tenMeasurementFrameReduce(Nrrd *nout, const Nrrd *nin) { static const char me[]="tenMeasurementFrameReduce"; double MF[9], MFT[9], tenMeasr[9], tenWorld[9]; float *tdata; size_t ii, nn; unsigned int si, sj; if (!(nout && nin)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tenTensorCheck(nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE)) { biffAddf(TEN, "%s: ", me); return 1; } if (3 != nin->spaceDim) { biffAddf(TEN, "%s: input nrrd needs 3-D (not %u-D) space dimension", me, nin->spaceDim); return 1; } /* [0] [1] [2] [0][0] [1][0] [2][0] [3] [4] [5] = [0][1] [1][1] [2][1] [6] [7] [8] [0][2] [1][2] [2][2] */ MF[0] = nin->measurementFrame[0][0]; MF[1] = nin->measurementFrame[1][0]; MF[2] = nin->measurementFrame[2][0]; MF[3] = nin->measurementFrame[0][1]; MF[4] = nin->measurementFrame[1][1]; MF[5] = nin->measurementFrame[2][1]; MF[6] = nin->measurementFrame[0][2]; MF[7] = nin->measurementFrame[1][2]; MF[8] = nin->measurementFrame[2][2]; if (!ELL_3M_EXISTS(MF)) { biffAddf(TEN, "%s: 3x3 measurement frame doesn't exist", me); return 1; } ELL_3M_TRANSPOSE(MFT, MF); if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(TEN, "%s: trouble with initial copy", me); return 1; } } nn = nrrdElementNumber(nout)/nout->axis[0].size; tdata = (float*)(nout->data); for (ii=0; iimeasurementFrame[si][sj] = AIR_NAN; } } for (si=0; si<3; si++) { for (sj=0; sj<3; sj++) { nout->measurementFrame[si][sj] = (si == sj); } } return 0; } int tenExpand2D(Nrrd *nout, const Nrrd *nin, double scale, double thresh) { static const char me[]="tenExpand2D"; size_t N, I, sx, sy; float *masked, *redund; if (!( nout && nin && AIR_EXISTS(thresh) )) { biffAddf(TEN, "%s: got NULL pointer or non-existent threshold", me); return 1; } if (nout == nin) { biffAddf(TEN, "%s: sorry, need different nrrds for input and output", me); return 1; } /* HEY copy and paste and tweak of tenTensorCheck */ if (!nin) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (nin->type != nrrdTypeFloat) { biffAddf(TEN, "%s: wanted type %s, got type %s", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nin->type)); return 1; } else { if (!(nin->type == nrrdTypeFloat || nin->type == nrrdTypeShort)) { biffAddf(TEN, "%s: need data of type float or short", me); return 1; } } if (3 != nin->dim) { biffAddf(TEN, "%s: given dimension is %u, not 3", me, nin->dim); return 1; } if (!(4 == nin->axis[0].size)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: axis 0 has size %s, not 4", me, airSprintSize_t(stmp, nin->axis[0].size)); return 1; } sx = nin->axis[1].size; sy = nin->axis[2].size; N = sx*sy; if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 3, AIR_CAST(size_t, 4), sx, sy)) { biffMovef(TEN, NRRD, "%s: trouble", me); return 1; } for (I=0; I<=N-1; I++) { masked = (float*)(nin->data) + I*4; redund = (float*)(nout->data) + I*4; if (masked[0] < thresh) { ELL_4V_ZERO_SET(redund); continue; } redund[0] = masked[1]; redund[1] = masked[2]; redund[2] = masked[2]; redund[3] = masked[3]; ELL_4V_SCALE(redund, AIR_CAST(float, scale), redund); } if (nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_SIZE_BIT)) { biffMovef(TEN, NRRD, "%s: trouble", me); return 1; } /* by call above we just copied axis-0 kind, which might be wrong; we actually know the output kind now, so we might as well set it */ nout->axis[0].kind = nrrdKind2DMatrix; if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { biffAddf(TEN, "%s:", me); return 1; } return 0; } int tenExpand(Nrrd *nout, const Nrrd *nin, double scale, double thresh) { static const char me[]="tenExpand"; size_t N, I, sx, sy, sz; float *seven, *nine; if (!( nout && nin && AIR_EXISTS(thresh) )) { biffAddf(TEN, "%s: got NULL pointer or non-existent threshold", me); return 1; } if (nout == nin) { biffAddf(TEN, "%s: sorry, need different nrrds for input and output", me); return 1; } if (tenTensorCheck(nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE)) { biffAddf(TEN, "%s: ", me); return 1; } sx = nin->axis[1].size; sy = nin->axis[2].size; sz = nin->axis[3].size; N = sx*sy*sz; if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 4, AIR_CAST(size_t, 9), sx, sy, sz)) { biffMovef(TEN, NRRD, "%s: trouble", me); return 1; } for (I=0; I<=N-1; I++) { seven = (float*)(nin->data) + I*7; nine = (float*)(nout->data) + I*9; if (seven[0] < thresh) { ELL_3M_ZERO_SET(nine); continue; } TEN_T2M(nine, seven); ELL_3M_SCALE(nine, AIR_CAST(float, scale), nine); } if (nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_SIZE_BIT)) { biffMovef(TEN, NRRD, "%s: trouble", me); return 1; } /* by call above we just copied axis-0 kind, which might be wrong; we actually know the output kind now, so we might as well set it */ nout->axis[0].kind = nrrdKind3DMatrix; if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { biffAddf(TEN, "%s:", me); return 1; } /* Tue Sep 13 18:36:45 EDT 2005: why did I do this? nout->axis[0].label = (char *)airFree(nout->axis[0].label); nout->axis[0].label = airStrdup("matrix"); */ return 0; } int tenShrink(Nrrd *tseven, const Nrrd *nconf, const Nrrd *tnine) { static const char me[]="tenShrink"; size_t I, N, sx, sy, sz; float *seven, *conf, *nine; if (!(tseven && tnine)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tseven == tnine) { biffAddf(TEN, "%s: sorry, need different nrrds for input and output", me); return 1; } if (!( nrrdTypeFloat == tnine->type && 4 == tnine->dim && 9 == tnine->axis[0].size )) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: type not %s (was %s) or dim not 4 (was %d) " "or first axis size not 9 (was %s)", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, tnine->type), tnine->dim, airSprintSize_t(stmp, tnine->axis[0].size)); return 1; } sx = tnine->axis[1].size; sy = tnine->axis[2].size; sz = tnine->axis[3].size; if (nconf) { if (!( nrrdTypeFloat == nconf->type && 3 == nconf->dim && sx == nconf->axis[0].size && sy == nconf->axis[1].size && sz == nconf->axis[2].size )) { biffAddf(TEN, "%s: confidence type not %s (was %s) or dim not 3 (was %d) " "or dimensions didn't match tensor volume", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nconf->type), nconf->dim); return 1; } } if (nrrdMaybeAlloc_va(tseven, nrrdTypeFloat, 4, AIR_CAST(size_t, 7), sx, sy, sz)) { biffMovef(TEN, NRRD, "%s: trouble allocating output", me); return 1; } seven = (float *)tseven->data; conf = nconf ? (float *)nconf->data : NULL; nine = (float *)tnine->data; N = sx*sy*sz; for (I=0; Iaxis[0].kind = nrrdKind3DMaskedSymMatrix; if (nrrdBasicInfoCopy(tseven, tnine, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { biffAddf(TEN, "%s:", me); return 1; } /* Wed Dec 3 11:22:32 EST 2008: no real need to set label string */ return 0; } /* ******** tenEigensolve_f ** ** uses ell_3m_eigensolve_d to get the eigensystem of a single tensor ** disregards the confidence value t[0] ** ** return is same as ell_3m_eigensolve_d, which is same as ell_cubic ** ** NOTE: Even in the post-Teem-1.7 switch from column-major to ** row-major- its still the case that the eigenvectors are at ** evec+0, evec+3, evec+6: this means that they USED to be the ** "columns" of the matrix, and NOW they're the rows. ** ** This does NOT use biff */ int tenEigensolve_f(float _eval[3], float _evec[9], const float t[7]) { double m[9], eval[3], evec[9], trc, iso[9]; int ret; TEN_T2M(m, t); trc = ELL_3M_TRACE(m)/3.0; ELL_3M_IDENTITY_SET(iso); ELL_3M_SCALE_SET(iso, -trc, -trc, -trc); ELL_3M_ADD2(m, m, iso); if (_evec) { ret = ell_3m_eigensolve_d(eval, evec, m, AIR_TRUE); if (tenVerbose > 4) { fprintf(stderr, "---- cubic ret = %d\n", ret); fprintf(stderr, "tensor = {\n"); fprintf(stderr, " % 15.7f,\n", t[1]); fprintf(stderr, " % 15.7f,\n", t[2]); fprintf(stderr, " % 15.7f,\n", t[3]); fprintf(stderr, " % 15.7f,\n", t[4]); fprintf(stderr, " % 15.7f,\n", t[5]); fprintf(stderr, " % 15.7f}\n", t[6]); fprintf(stderr, "roots = %d:\n", ret); fprintf(stderr, " % 31.15f\n", trc + eval[0]); fprintf(stderr, " % 31.15f\n", trc + eval[1]); fprintf(stderr, " % 31.15f\n", trc + eval[2]); } ELL_3V_SET_TT(_eval, float, eval[0] + trc, eval[1] + trc, eval[2] + trc); ELL_3M_COPY_TT(_evec, float, evec); if (ell_cubic_root_single_double == ret) { /* this was added to fix a stupid problem with very nearly isotropic glyphs, used for demonstration figures */ if (eval[0] == eval[1]) { ELL_3V_CROSS(_evec+6, _evec+0, _evec+3); } else { ELL_3V_CROSS(_evec+0, _evec+3, _evec+6); } } if ((tenVerbose > 1) && _eval[2] < 0) { fprintf(stderr, "tenEigensolve_f -------------\n"); fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", t[1], t[2], t[3]); fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", t[2], t[4], t[5]); fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", t[3], t[5], t[6]); fprintf(stderr, " --> % 15.7f % 15.7f % 15.7f\n", _eval[0], _eval[1], _eval[2]); } } else { /* caller only wants eigenvalues */ ret = ell_3m_eigenvalues_d(eval, m, AIR_TRUE); ELL_3V_SET_TT(_eval, float, eval[0] + trc, eval[1] + trc, eval[2] + trc); } return ret; } /* HEY: cut and paste !! */ int tenEigensolve_d(double _eval[3], double evec[9], const double t[7]) { double m[9], eval[3], trc, iso[9]; int ret; TEN_T2M(m, t); trc = ELL_3M_TRACE(m)/3.0; ELL_3M_SCALE_SET(iso, -trc, -trc, -trc); ELL_3M_ADD2(m, m, iso); /* printf("!%s: t = %g %g %g; %g %g; %g\n", "tenEigensolve_f", t[1], t[2], t[3], t[4], t[5], t[6]); printf("!%s: m = %g %g %g; %g %g %g; %g %g %g\n", "tenEigensolve_f", m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); */ if (evec) { ret = ell_3m_eigensolve_d(eval, evec, m, AIR_TRUE); ELL_3V_SET(_eval, eval[0] + trc, eval[1] + trc, eval[2] + trc); if (ell_cubic_root_single_double == ret) { /* this was added to fix a stupid problem with very nearly isotropic glyphs, used for demonstration figures */ if (eval[0] == eval[1]) { ELL_3V_CROSS(evec+6, evec+0, evec+3); } else { ELL_3V_CROSS(evec+0, evec+3, evec+6); } } } else { /* caller only wants eigenvalues */ ret = ell_3m_eigenvalues_d(eval, m, AIR_TRUE); ELL_3V_SET(_eval, eval[0] + trc, eval[1] + trc, eval[2] + trc); } return ret; } /* lop A fprintf(stderr, "################################### I = %d\n", (int)I); tenEigensolve(teval, tevec, out); fprintf(stderr, "evals: (%g %g %g) %g %g %g --> %g %g %g\n", AIR_ABS(eval[0] - teval[0]) + 1, AIR_ABS(eval[1] - teval[1]) + 1, AIR_ABS(eval[2] - teval[2]) + 1, eval[0], eval[1], eval[2], teval[0], teval[1], teval[2]); fprintf(stderr, " tevec lens: %g %g %g\n", ELL_3V_LEN(tevec+3*0), ELL_3V_LEN(tevec+3*1), ELL_3V_LEN(tevec+3*2)); ELL_3V_CROSS(tmp1, evec+3*0, evec+3*1); tmp2[0] = ELL_3V_LEN(tmp1); ELL_3V_CROSS(tmp1, evec+3*0, evec+3*2); tmp2[1] = ELL_3V_LEN(tmp1); ELL_3V_CROSS(tmp1, evec+3*1, evec+3*2); tmp2[2] = ELL_3V_LEN(tmp1); fprintf(stderr, " evec[0] = %g %g %g\n", (evec+3*0)[0], (evec+3*0)[1], (evec+3*0)[2]); fprintf(stderr, " evec[1] = %g %g %g\n", (evec+3*1)[0], (evec+3*1)[1], (evec+3*1)[2]); fprintf(stderr, " evec[2] = %g %g %g\n", (evec+3*2)[0], (evec+3*2)[1], (evec+3*2)[2]); fprintf(stderr, " evec crosses: %g %g %g\n", tmp2[0], tmp2[1], tmp2[2]); ELL_3V_CROSS(tmp1, tevec+3*0, tevec+3*1); tmp2[0] = ELL_3V_LEN(tmp1); ELL_3V_CROSS(tmp1, tevec+3*0, tevec+3*2); tmp2[1] = ELL_3V_LEN(tmp1); ELL_3V_CROSS(tmp1, tevec+3*1, tevec+3*2); tmp2[2] = ELL_3V_LEN(tmp1); fprintf(stderr, " tevec[0] = %g %g %g\n", (tevec+3*0)[0], (tevec+3*0)[1], (tevec+3*0)[2]); fprintf(stderr, " tevec[1] = %g %g %g\n", (tevec+3*1)[0], (tevec+3*1)[1], (tevec+3*1)[2]); fprintf(stderr, " tevec[2] = %g %g %g\n", (tevec+3*2)[0], (tevec+3*2)[1], (tevec+3*2)[2]); fprintf(stderr, " tevec crosses: %g %g %g\n", tmp2[0], tmp2[1], tmp2[2]); if (tmp2[1] < 0.5) { fprintf(stderr, "(panic)\n"); exit(0); } */ void tenMakeSingle_f(float ten[7], float conf, const float eval[3], const float evec[9]) { double tmpMat1[9], tmpMat2[9], diag[9], evecT[9]; ELL_3M_ZERO_SET(diag); ELL_3M_DIAG_SET(diag, eval[0], eval[1], eval[2]); ELL_3M_TRANSPOSE(evecT, evec); ELL_3M_MUL(tmpMat1, diag, evec); ELL_3M_MUL(tmpMat2, evecT, tmpMat1); ten[0] = conf; TEN_M2T_TT(ten, float, tmpMat2); return; } /* HEY: copy and paste! */ void tenMakeSingle_d(double ten[7], double conf, const double eval[3], const double evec[9]) { double tmpMat1[9], tmpMat2[9], diag[9], evecT[9]; ELL_3M_ZERO_SET(diag); ELL_3M_DIAG_SET(diag, eval[0], eval[1], eval[2]); ELL_3M_TRANSPOSE(evecT, evec); ELL_3M_MUL(tmpMat1, diag, evec); ELL_3M_MUL(tmpMat2, evecT, tmpMat1); ten[0] = conf; TEN_M2T_TT(ten, float, tmpMat2); return; } /* ******** tenMake ** ** create a tensor nrrd from nrrds of confidence, eigenvalues, and ** eigenvectors */ int tenMake(Nrrd *nout, const Nrrd *nconf, const Nrrd *neval, const Nrrd *nevec) { static const char me[]="tenTensorMake"; size_t I, N, sx, sy, sz; float *out, *conf, *eval, *evec; int map[4]; /* float teval[3], tevec[9], tmp1[3], tmp2[3]; */ char stmp[7][AIR_STRLEN_SMALL]; if (!(nout && nconf && neval && nevec)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (nrrdCheck(nconf) || nrrdCheck(neval) || nrrdCheck(nevec)) { biffMovef(TEN, NRRD, "%s: didn't get three valid nrrds", me); return 1; } if (!( 3 == nconf->dim && nrrdTypeFloat == nconf->type )) { biffAddf(TEN, "%s: first nrrd not a confidence volume " "(dim = %d, not 3; type = %s, not %s)", me, nconf->dim, airEnumStr(nrrdType, nconf->type), airEnumStr(nrrdType, nrrdTypeFloat)); return 1; } sx = nconf->axis[0].size; sy = nconf->axis[1].size; sz = nconf->axis[2].size; if (!( 4 == neval->dim && 4 == nevec->dim && nrrdTypeFloat == neval->type && nrrdTypeFloat == nevec->type )) { biffAddf(TEN, "%s: second and third nrrd aren't both 4-D (%d and %d) " "and type %s (%s and %s)", me, neval->dim, nevec->dim, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, neval->type), airEnumStr(nrrdType, nevec->type)); return 1; } if (!( 3 == neval->axis[0].size && sx == neval->axis[1].size && sy == neval->axis[2].size && sz == neval->axis[3].size )) { biffAddf(TEN, "%s: second nrrd sizes wrong: " "(%s,%s,%s,%s) not (3,%s,%s,%s)", me, airSprintSize_t(stmp[0], neval->axis[0].size), airSprintSize_t(stmp[1], neval->axis[1].size), airSprintSize_t(stmp[2], neval->axis[2].size), airSprintSize_t(stmp[3], neval->axis[3].size), airSprintSize_t(stmp[4], sx), airSprintSize_t(stmp[5], sy), airSprintSize_t(stmp[6], sz)); return 1; } if (!( 9 == nevec->axis[0].size && sx == nevec->axis[1].size && sy == nevec->axis[2].size && sz == nevec->axis[3].size )) { biffAddf(TEN, "%s: third nrrd sizes wrong: " "(%s,%s,%s,%s) not (9,%s,%s,%s)", me, airSprintSize_t(stmp[0], nevec->axis[0].size), airSprintSize_t(stmp[1], nevec->axis[1].size), airSprintSize_t(stmp[2], nevec->axis[2].size), airSprintSize_t(stmp[3], nevec->axis[3].size), airSprintSize_t(stmp[4], sx), airSprintSize_t(stmp[5], sy), airSprintSize_t(stmp[6], sz)); return 1; } /* finally */ if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 4, AIR_CAST(size_t, 7), sx, sy, sz)) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); return 1; } N = sx*sy*sz; conf = (float *)(nconf->data); eval = (float *)neval->data; evec = (float *)nevec->data; out = (float *)nout->data; for (I=0; Iaxis[0].label = (char *)airFree(nout->axis[0].label); nout->axis[0].label = airStrdup("tensor"); nout->axis[0].kind = nrrdKind3DMaskedSymMatrix; if (nrrdBasicInfoCopy(nout, nconf, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffMovef(TEN, NRRD, "%s:", me); return 1; } return 0; } int tenSlice(Nrrd *nout, const Nrrd *nten, unsigned int axis, size_t pos, unsigned int dim) { static const char me[]="tenSlice"; Nrrd *nslice, **ncoeff=NULL; int ci[4]; airArray *mop; char stmp[2][AIR_STRLEN_SMALL]; if (!(nout && nten)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tenTensorCheck(nten, nrrdTypeDefault, AIR_TRUE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a valid tensor field", me); return 1; } if (!(2 == dim || 3 == dim)) { biffAddf(TEN, "%s: given dim (%d) not 2 or 3", me, dim); return 1; } if (!( axis <= 2 )) { biffAddf(TEN, "%s: axis %u not in valid range [0,1,2]", me, axis); return 1; } if (!( pos < nten->axis[1+axis].size )) { biffAddf(TEN, "%s: slice position %s not in valid range [0..%s]", me, airSprintSize_t(stmp[0], pos), airSprintSize_t(stmp[1], nten->axis[1+axis].size-1)); return 1; } /* ** threshold 0 ** Dxx Dxy Dxz 1 2 3 ** Dxy Dyy Dyz = (2) 4 5 ** Dxz Dyz Dzz (3) (5) 6 */ mop = airMopNew(); airMopAdd(mop, nslice=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (3 == dim) { if (nrrdSlice(nslice, nten, axis+1, pos) || nrrdAxesInsert(nout, nslice, axis+1)) { biffMovef(TEN, NRRD, "%s: trouble making slice", me); airMopError(mop); return 1; } } else { /* HEY: this used to be ncoeff[4], but its passing to nrrdJoin caused "dereferencing type-punned pointer might break strict-aliasing rules" warning; GLK not sure how else to fix it */ ncoeff = AIR_CALLOC(4, Nrrd*); airMopAdd(mop, ncoeff, airFree, airMopAlways); airMopAdd(mop, ncoeff[0]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ncoeff[1]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ncoeff[2]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ncoeff[3]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); switch(axis) { case 0: ELL_4V_SET(ci, 0, 4, 5, 6); break; case 1: ELL_4V_SET(ci, 0, 1, 3, 6); break; case 2: ELL_4V_SET(ci, 0, 1, 2, 4); break; default: biffAddf(TEN, "%s: axis %d bogus", me, axis); airMopError(mop); return 1; break; } if (nrrdSlice(nslice, nten, axis+1, pos) || nrrdSlice(ncoeff[0], nslice, 0, ci[0]) || nrrdSlice(ncoeff[1], nslice, 0, ci[1]) || nrrdSlice(ncoeff[2], nslice, 0, ci[2]) || nrrdSlice(ncoeff[3], nslice, 0, ci[3]) || nrrdJoin(nout, (const Nrrd *const*)ncoeff, 4, 0, AIR_TRUE)) { biffMovef(TEN, NRRD, "%s: trouble collecting coefficients", me); airMopError(mop); return 1; } nout->axis[0].kind = nrrdKind2DMaskedSymMatrix; } airMopOkay(mop); return 0; } #define Txx (ten[1]) #define Txy (ten[2]) #define Txz (ten[3]) #define Tyy (ten[4]) #define Tyz (ten[5]) #define Tzz (ten[6]) #define SQRT_1_OVER_2 0.70710678118654752440 #define SQRT_1_OVER_3 0.57735026918962576450 #define SQRT_2_OVER_3 0.81649658092772603272 #define SQRT_1_OVER_6 0.40824829046386301635 /* ** very special purpose: compute tensor-valued gradient ** of eigenvalue skewness, but have to be given two ** other invariant gradients, NORMALIZED, to which ** eigenvalue skewness should be perpendicular */ void _tenEvalSkewnessGradient_d(double skw[7], const double perp1[7], const double perp2[7], const double ten[7], const double minnorm) { /* static const char me[]="_tenEvalSkewnessGradient_d"; */ double dot, scl, norm; /* start with gradient of determinant */ TEN_T_SET(skw, ten[0], Tyy*Tzz - Tyz*Tyz, Txz*Tyz - Txy*Tzz, Txy*Tyz - Txz*Tyy, Txx*Tzz - Txz*Txz, Txy*Txz - Tyz*Txx, Txx*Tyy - Txy*Txy); /* this normalization is so that minnorm comparison below is meaningful regardless of scale of input */ /* HEY: should have better handling of case where determinant gradient magnitude is near zero */ scl = 1.0/(DBL_EPSILON + TEN_T_NORM(skw)); TEN_T_SCALE(skw, scl, skw); dot = TEN_T_DOT(skw, perp1); TEN_T_SCALE_INCR(skw, -dot, perp1); dot = TEN_T_DOT(skw, perp2); TEN_T_SCALE_INCR(skw, -dot, perp2); norm = TEN_T_NORM(skw); if (norm < minnorm) { /* skw is at an extremum, should diagonalize */ double eval[3], evec[9], matA[9], matB[9], matC[9], mev, third; tenEigensolve_d(eval, evec, ten); mev = (eval[0] + eval[1] + eval[2])/3; eval[0] -= mev; eval[1] -= mev; eval[2] -= mev; third = (eval[0]*eval[0]*eval[0] + eval[1]*eval[1]*eval[1] + eval[2]*eval[2]*eval[2])/3; if (third > 0) { /* skw is positive: linear: eval[1] = eval[2] */ ELL_3MV_OUTER(matA, evec + 1*3, evec + 1*3); ELL_3MV_OUTER(matB, evec + 2*3, evec + 2*3); } else { /* skw is negative: planar: eval[0] = eval[1] */ ELL_3MV_OUTER(matA, evec + 0*3, evec + 0*3); ELL_3MV_OUTER(matB, evec + 1*3, evec + 1*3); } ELL_3M_SCALE_ADD2(matC, SQRT_1_OVER_2, matA, -SQRT_1_OVER_2, matB); TEN_M2T(skw, matC); /* have to make sure that this contrived tensor is indeed orthogonal to perp1 and perp2 */ dot = TEN_T_DOT(skw, perp1); TEN_T_SCALE_INCR(skw, -dot, perp1); dot = TEN_T_DOT(skw, perp2); TEN_T_SCALE_INCR(skw, -dot, perp2); norm = TEN_T_NORM(skw); } TEN_T_SCALE(skw, 1.0/norm, skw); return; } void tenInvariantGradientsK_d(double mu1[7], double mu2[7], double skw[7], const double ten[7], const double minnorm) { double dot, norm; TEN_T_SET(mu1, ten[0], SQRT_1_OVER_3, 0, 0, SQRT_1_OVER_3, 0, SQRT_1_OVER_3); TEN_T_SET(mu2, ten[0], 2*Txx - Tyy - Tzz, 3*Txy, 3*Txz, 2*Tyy - Txx - Tzz, 3*Tyz, 2*Tzz - Txx - Tyy); norm = TEN_T_NORM(mu2); if (norm < minnorm) { /* they gave us a diagonal matrix */ TEN_T_SET(mu2, ten[0], SQRT_2_OVER_3, 0, 0, -SQRT_1_OVER_6, 0, -SQRT_1_OVER_6); } /* next two lines shouldn't really be necessary */ dot = TEN_T_DOT(mu2, mu1); TEN_T_SCALE_INCR(mu2, -dot, mu1); norm = TEN_T_NORM(mu2); TEN_T_SCALE(mu2, 1.0/norm, mu2); _tenEvalSkewnessGradient_d(skw, mu1, mu2, ten, minnorm); return; } void tenInvariantGradientsR_d(double R1[7], double R2[7], double R3[7], const double ten[7], const double minnorm) { double dot, dev[7], norm, tenNorm, devNorm; TEN_T_COPY(R1, ten); tenNorm = norm = TEN_T_NORM(R1); if (norm < minnorm) { TEN_T_SET(R1, ten[0], SQRT_1_OVER_3, 0, 0, SQRT_1_OVER_3, 0, SQRT_1_OVER_3); norm = TEN_T_NORM(R1); } TEN_T_SCALE(R1, 1.0/norm, R1); TEN_T_SET(dev, ten[0], (2*Txx - Tyy - Tzz)/3, Txy, Txz, (2*Tyy - Txx - Tzz)/3, Tyz, (2*Tzz - Txx - Tyy)/3); devNorm = TEN_T_NORM(dev); if (devNorm < minnorm) { /* they gave us a diagonal matrix */ TEN_T_SET(R2, ten[0], SQRT_2_OVER_3, 0, 0, -SQRT_1_OVER_6, 0, -SQRT_1_OVER_6); } else { TEN_T_SCALE_ADD2(R2, tenNorm/devNorm, dev, -devNorm/tenNorm, ten); } /* next two lines shouldn't really be necessary */ dot = TEN_T_DOT(R2, R1); TEN_T_SCALE_INCR(R2, -dot, R1); norm = TEN_T_NORM(R2); if (norm < minnorm) { /* Traceless tensor */ TEN_T_SET(R2, ten[0], SQRT_2_OVER_3, 0, 0, -SQRT_1_OVER_6, 0, -SQRT_1_OVER_6); } else { TEN_T_SCALE(R2, 1.0/norm, R2); } _tenEvalSkewnessGradient_d(R3, R1, R2, ten, minnorm); return; } /* ** evec must be pre-computed (unit-length eigenvectors) and given to us */ void tenRotationTangents_d(double phi1[7], double phi2[7], double phi3[7], const double evec[9]) { double outA[9], outB[9], mat[9]; if (phi1) { phi1[0] = 1.0; ELL_3MV_OUTER(outA, evec + 1*3, evec + 2*3); ELL_3MV_OUTER(outB, evec + 2*3, evec + 1*3); ELL_3M_SCALE_ADD2(mat, SQRT_1_OVER_2, outA, SQRT_1_OVER_2, outB); TEN_M2T(phi1, mat); } if (phi2) { phi2[0] = 1.0; ELL_3MV_OUTER(outA, evec + 0*3, evec + 2*3); ELL_3MV_OUTER(outB, evec + 2*3, evec + 0*3); ELL_3M_SCALE_ADD2(mat, SQRT_1_OVER_2, outA, SQRT_1_OVER_2, outB); TEN_M2T(phi2, mat); } if (phi3) { phi3[0] = 1.0; ELL_3MV_OUTER(outA, evec + 0*3, evec + 1*3); ELL_3MV_OUTER(outB, evec + 1*3, evec + 0*3); ELL_3M_SCALE_ADD2(mat, SQRT_1_OVER_2, outA, SQRT_1_OVER_2, outB); TEN_M2T(phi3, mat); } return; } void tenInv_f(float inv[7], const float ten[7]) { float det; TEN_T_INV(inv, ten, det); } void tenInv_d(double inv[7], const double ten[7]) { double det; TEN_T_INV(inv, ten, det); } void tenLogSingle_d(double logten[7], const double ten[7]) { double eval[3], evec[9]; unsigned int ii; tenEigensolve_d(eval, evec, ten); for (ii=0; ii<3; ii++) { eval[ii] = log(eval[ii]); if (!AIR_EXISTS(eval[ii])) { eval[ii] = -FLT_MAX; /* making stuff up */ } } tenMakeSingle_d(logten, ten[0], eval, evec); } void tenLogSingle_f(float logten[7], const float ten[7]) { float eval[3], evec[9]; unsigned int ii; tenEigensolve_f(eval, evec, ten); for (ii=0; ii<3; ii++) { eval[ii] = AIR_CAST(float, log(eval[ii])); if (!AIR_EXISTS(eval[ii])) { eval[ii] = -FLT_MAX/10; /* still making stuff up */ } } tenMakeSingle_f(logten, ten[0], eval, evec); } void tenExpSingle_d(double expten[7], const double ten[7]) { double eval[3], evec[9]; unsigned int ii; tenEigensolve_d(eval, evec, ten); for (ii=0; ii<3; ii++) { eval[ii] = exp(eval[ii]); } tenMakeSingle_d(expten, ten[0], eval, evec); } void tenExpSingle_f(float expten[7], const float ten[7]) { float eval[3], evec[9]; unsigned int ii; tenEigensolve_f(eval, evec, ten); for (ii=0; ii<3; ii++) { eval[ii] = AIR_CAST(float, exp(eval[ii])); } tenMakeSingle_f(expten, ten[0], eval, evec); } void tenSqrtSingle_d(double sqrtten[7], const double ten[7]) { double eval[3], evec[9]; unsigned int ii; tenEigensolve_d(eval, evec, ten); for (ii=0; ii<3; ii++) { eval[ii] = eval[ii] > 0 ? sqrt(eval[ii]) : 0; } tenMakeSingle_d(sqrtten, ten[0], eval, evec); } void tenSqrtSingle_f(float sqrtten[7], const float ten[7]) { float eval[3], evec[9]; unsigned int ii; tenEigensolve_f(eval, evec, ten); for (ii=0; ii<3; ii++) { eval[ii] = AIR_CAST(float, eval[ii] > 0 ? sqrt(eval[ii]) : 0); } tenMakeSingle_f(sqrtten, ten[0], eval, evec); } void tenPowSingle_d(double powten[7], const double ten[7], double power) { double eval[3], _eval[3], evec[9]; unsigned int ii; tenEigensolve_d(_eval, evec, ten); for (ii=0; ii<3; ii++) { eval[ii] = pow(_eval[ii], power); } tenMakeSingle_d(powten, ten[0], eval, evec); } void tenPowSingle_f(float powten[7], const float ten[7], float power) { float eval[3], evec[9]; unsigned int ii; tenEigensolve_f(eval, evec, ten); for (ii=0; ii<3; ii++) { eval[ii] = AIR_CAST(float, pow(eval[ii], power)); } tenMakeSingle_f(powten, ten[0], eval, evec); } double tenDoubleContract_d(double a[7], double T[21], double b[7]) { double ret; ret = (+ 1*1*T[ 0]*a[1]*b[1] + 1*2*T[ 1]*a[2]*b[1] + 1*2*T[ 2]*a[3]*b[1] + 1*1*T[ 3]*a[4]*b[1] + 1*2*T[ 4]*a[5]*b[1] + 1*1*T[ 5]*a[6]*b[1] + + 2*1*T[ 1]*a[1]*b[2] + 2*2*T[ 6]*a[2]*b[2] + 2*2*T[ 7]*a[3]*b[2] + 2*1*T[ 8]*a[4]*b[2] + 2*2*T[ 9]*a[5]*b[2] + 2*1*T[10]*a[6]*b[2] + + 2*1*T[ 2]*a[1]*b[3] + 2*2*T[ 7]*a[2]*b[3] + 2*2*T[11]*a[3]*b[3] + 2*1*T[12]*a[4]*b[3] + 2*2*T[13]*a[5]*b[3] + 2*1*T[14]*a[6]*b[3] + + 1*1*T[ 3]*a[1]*b[4] + 1*2*T[ 8]*a[2]*b[4] + 1*2*T[12]*a[3]*b[4] + 1*1*T[15]*a[4]*b[4] + 1*2*T[16]*a[5]*b[4] + 1*1*T[17]*a[6]*b[4] + + 2*1*T[ 4]*a[1]*b[5] + 2*2*T[ 9]*a[2]*b[5] + 2*2*T[13]*a[3]*b[5] + 2*1*T[16]*a[4]*b[5] + 2*2*T[18]*a[5]*b[5] + 2*1*T[19]*a[6]*b[5] + + 1*1*T[ 5]*a[1]*b[6] + 1*2*T[10]*a[2]*b[6] + 1*2*T[14]*a[3]*b[6] + 1*1*T[17]*a[4]*b[6] + 1*2*T[19]*a[5]*b[6] + 1*1*T[20]*a[6]*b[6]); return ret; } teem-1.11.0~svn6057/src/ten/tendMfit.c0000664000175000017500000001501212165631065017104 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Estimate models from a set of DW images" static const char *_tend_mfitInfoL = (INFO ". More docs here."); int tend_mfitMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout, *nterr, *nconv, *niter; char *outS, *terrS, *convS, *iterS, *modS; int knownB0, saveB0, verbose, mlfit, typeOut; unsigned int maxIter, minIter, starts; double sigma, eps; const tenModel *model; tenExperSpec *espec; hestOptAdd(&hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "0", "verbosity level"); hestOptAdd(&hopt, "m", "model", airTypeString, 1, 1, &modS, NULL, "which model to fit. Use optional \"b0+\" prefix to " "indicate that the B0 image should also be saved " "(independent of whether it was known or had to be " "estimated, according to \"-knownB0\")."); hestOptAdd(&hopt, "ns", "# starts", airTypeUInt, 1, 1, &starts, "1", "number of random starting points at which to initialize " "fitting"); hestOptAdd(&hopt, "ml", NULL, airTypeInt, 0, 0, &mlfit, NULL, "do ML fitting, rather than least-squares, which also " "requires setting \"-sigma\""); hestOptAdd(&hopt, "sigma", "sigma", airTypeDouble, 1, 1, &sigma, "nan", "Gaussian/Rician noise parameter"); hestOptAdd(&hopt, "eps", "eps", airTypeDouble, 1, 1, &eps, "0.01", "convergence epsilon"); hestOptAdd(&hopt, "mini", "min iters", airTypeUInt, 1, 1, &minIter, "3", "minimum required # iterations for fitting."); hestOptAdd(&hopt, "maxi", "max iters", airTypeUInt, 1, 1, &maxIter, "100", "maximum allowable # iterations for fitting."); hestOptAdd(&hopt, "knownB0", "bool", airTypeBool, 1, 1, &knownB0, NULL, "Indicates if the B=0 non-diffusion-weighted reference image " "is known (\"true\") because it appears one or more times " "amongst the DWIs, or, if it has to be estimated along with " "the other model parameters (\"false\")"); /* (this is now specified as part of the "-m" model description) hestOptAdd(&hopt, "saveB0", "bool", airTypeBool, 1, 1, &saveB0, NULL, "Indicates if the B=0 non-diffusion-weighted value " "should be saved in output, regardless of whether it was " "known or had to be esimated"); */ hestOptAdd(&hopt, "t", "type", airTypeEnum, 1, 1, &typeOut, "float", "output type of model parameters", NULL, nrrdType); hestOptAdd(&hopt, "i", "dwi", airTypeOther, 1, 1, &nin, "-", "all the diffusion-weighted images in one 4D nrrd", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output parameter vector image"); hestOptAdd(&hopt, "eo", "filename", airTypeString, 1, 1, &terrS, "", "Giving a filename here allows you to save out the per-sample " "fitting error. By default, no such error is saved."); hestOptAdd(&hopt, "co", "filename", airTypeString, 1, 1, &convS, "", "Giving a filename here allows you to save out the per-sample " "convergence fraction. By default, no such error is saved."); hestOptAdd(&hopt, "io", "filename", airTypeString, 1, 1, &iterS, "", "Giving a filename here allows you to save out the per-sample " "number of iterations needed for fitting. " "By default, no such error is saved."); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_mfitInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nterr = NULL; nconv = NULL; niter = NULL; espec = tenExperSpecNew(); airMopAdd(mop, espec, (airMopper)tenExperSpecNix, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenModelParse(&model, &saveB0, AIR_FALSE, modS)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing model \"%s\":\n%s\n", me, modS, err); airMopError(mop); return 1; } if (tenExperSpecFromKeyValueSet(espec, nin)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble getting exper from kvp:\n%s\n", me, err); airMopError(mop); return 1; } if (tenModelSqeFit(nout, airStrlen(terrS) ? &nterr : NULL, airStrlen(convS) ? &nconv : NULL, airStrlen(iterS) ? &niter : NULL, model, espec, nin, knownB0, saveB0, typeOut, minIter, maxIter, starts, eps, NULL, verbose)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble fitting:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL) || (airStrlen(terrS) && nrrdSave(terrS, nterr, NULL)) || (airStrlen(convS) && nrrdSave(convS, nconv, NULL)) || (airStrlen(iterS) && nrrdSave(iterS, niter, NULL))) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(mfit, INFO); teem-1.11.0~svn6057/src/ten/tendAvg.c0000664000175000017500000000544112165631065016727 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Average across tensor volumes" static const char *_tend_avgInfoL = (INFO ". The output is the same size as the any one of the inputs. " "The individual tensors may be averaged in various ways."); int tend_avgMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int ninLen, itype; Nrrd **nin, *nout; char *outS; hestOptAdd(&hopt, "i", "nin1 nin2", airTypeOther, 2, -1, &nin, NULL, "list of input diffusion tensor volumes", &ninLen, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "t", "type", airTypeEnum, 1, 1, &itype, "linear", "averaging method", NULL, tenInterpType); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_avgInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenInterpMulti3D(nout, AIR_CAST(const Nrrd*const*, nin), NULL, ninLen, itype, NULL)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(avg, INFO); teem-1.11.0~svn6057/src/ten/tenGage.c0000664000175000017500000032175312165631065016720 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" typedef struct { double *buffTen, *buffWght; tenInterpParm *tip; /* sneakiness: using tip->allocLen to record allocation sizes of buffTen and buffWght, too */ } _tenGagePvlData; gageItemEntry _tenGageTable[TEN_GAGE_ITEM_MAX+1] = { /* enum value len,deriv, prereqs, parent item, parent index, needData */ {tenGageUnknown, 0, 0, {0}, 0, 0, AIR_FALSE}, {tenGageTensor, 7, 0, {0}, 0, 0, AIR_FALSE}, {tenGageConfidence, 1, 0, {tenGageTensor}, tenGageTensor, 0, AIR_FALSE}, {tenGageTrace, 1, 0, {tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageNorm, 1, 0, {tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageB, 1, 0, {tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageDet, 1, 0, {tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageS, 1, 0, {tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageQ, 1, 0, {tenGageS, tenGageB}, 0, 0, AIR_FALSE}, {tenGageFA, 1, 0, {tenGageQ, tenGageS}, 0, 0, AIR_FALSE}, {tenGageR, 1, 0, {tenGageTrace, tenGageB, tenGageDet, tenGageS}, 0, 0, AIR_FALSE}, {tenGageMode, 1, 0, {tenGageR, tenGageQ}, 0, 0, AIR_FALSE}, {tenGageTheta, 1, 0, {tenGageMode}, 0, 0, AIR_FALSE}, {tenGageModeWarp, 1, 0, {tenGageMode}, 0, 0, AIR_FALSE}, {tenGageOmega, 1, 0, {tenGageFA, tenGageMode}, 0, 0, AIR_FALSE}, {tenGageEval, 3, 0, {tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageEval0, 1, 0, {tenGageEval}, tenGageEval, 0, AIR_FALSE}, {tenGageEval1, 1, 0, {tenGageEval}, tenGageEval, 1, AIR_FALSE}, {tenGageEval2, 1, 0, {tenGageEval}, tenGageEval, 2, AIR_FALSE}, {tenGageEvec, 9, 0, {tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageEvec0, 3, 0, {tenGageEvec}, tenGageEvec, 0, AIR_FALSE}, {tenGageEvec1, 3, 0, {tenGageEvec}, tenGageEvec, 3, AIR_FALSE}, {tenGageEvec2, 3, 0, {tenGageEvec}, tenGageEvec, 6, AIR_FALSE}, {tenGageDelNormK2, 7, 0, {tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageDelNormK3, 7, 0, {tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageDelNormR1, 7, 0, {tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageDelNormR2, 7, 0, {tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageDelNormPhi1, 7, 0, {tenGageEvec}, 0, 0, AIR_FALSE}, {tenGageDelNormPhi2, 7, 0, {tenGageEvec}, 0, 0, AIR_FALSE}, {tenGageDelNormPhi3, 7, 0, {tenGageEvec}, 0, 0, AIR_FALSE}, {tenGageTensorGrad, 21, 1, {0}, 0, 0, AIR_FALSE}, {tenGageTensorGradMag, 3, 1, {tenGageTensorGrad}, 0, 0, AIR_FALSE}, {tenGageTensorGradMagMag, 1, 1, {tenGageTensorGradMag}, 0, 0, AIR_FALSE}, {tenGageTraceGradVec, 3, 1, {tenGageTensor, tenGageTensorGrad}, 0, 0, AIR_FALSE}, {tenGageTraceGradMag, 1, 1, {tenGageTraceGradVec}, 0, 0, AIR_FALSE}, {tenGageTraceNormal, 3, 1, {tenGageTraceGradVec, tenGageTraceGradMag}, 0, 0, AIR_FALSE}, {tenGageNormGradVec, 3, 1, {tenGageNorm, tenGageSGradVec}, 0, 0, AIR_FALSE}, {tenGageNormGradMag, 1, 1, {tenGageNormGradVec}, 0, 0, AIR_FALSE}, {tenGageNormNormal, 3, 1, {tenGageNormGradVec, tenGageNormGradMag}, 0, 0, AIR_FALSE}, {tenGageBGradVec, 3, 1, {tenGageTensor, tenGageTensorGrad}, 0, 0, AIR_FALSE}, {tenGageBGradMag, 1, 1, {tenGageBGradVec}, 0, 0, AIR_FALSE}, {tenGageBNormal, 3, 1, {tenGageBGradVec, tenGageBGradMag}, 0, 0, AIR_FALSE}, {tenGageDetGradVec, 3, 1, {tenGageTensor, tenGageTensorGrad}, 0, 0, AIR_FALSE}, {tenGageDetGradMag, 1, 1, {tenGageDetGradVec}, 0, 0, AIR_FALSE}, {tenGageDetNormal, 3, 1, {tenGageDetGradVec, tenGageDetGradMag}, 0, 0, AIR_FALSE}, {tenGageSGradVec, 3, 1, {tenGageTensor, tenGageTensorGrad}, 0, 0, AIR_FALSE}, {tenGageSGradMag, 1, 1, {tenGageSGradVec}, 0, 0, AIR_FALSE}, {tenGageSNormal, 3, 1, {tenGageSGradVec, tenGageSGradMag}, 0, 0, AIR_FALSE}, {tenGageQGradVec, 3, 1, {tenGageSGradVec, tenGageBGradVec}, 0, 0, AIR_FALSE}, {tenGageQGradMag, 1, 1, {tenGageQGradVec}, 0, 0, AIR_FALSE}, {tenGageQNormal, 3, 1, {tenGageQGradVec, tenGageQGradMag}, 0, 0, AIR_FALSE}, {tenGageFAGradVec, 3, 1, {tenGageQGradVec, tenGageSGradVec, tenGageFA}, 0, 0, AIR_FALSE}, {tenGageFAGradMag, 1, 1, {tenGageFAGradVec}, 0, 0, AIR_FALSE}, {tenGageFANormal, 3, 1, {tenGageFAGradVec, tenGageFAGradMag}, 0, 0, AIR_FALSE}, {tenGageRGradVec, 3, 1, {tenGageR, tenGageTraceGradVec, tenGageBGradVec, tenGageDetGradVec, tenGageSGradVec}, 0, 0, AIR_FALSE}, {tenGageRGradMag, 1, 1, {tenGageRGradVec}, 0, 0, AIR_FALSE}, {tenGageRNormal, 3, 1, {tenGageRGradVec, tenGageRGradMag}, 0, 0, AIR_FALSE}, {tenGageModeGradVec, 3, 1, {tenGageRGradVec, tenGageQGradVec, tenGageMode}, 0, 0, AIR_FALSE}, {tenGageModeGradMag, 1, 1, {tenGageModeGradVec}, 0, 0, AIR_FALSE}, {tenGageModeNormal, 3, 1, {tenGageModeGradVec, tenGageModeGradMag}, 0, 0, AIR_FALSE}, {tenGageThetaGradVec, 3, 1, {tenGageRGradVec, tenGageQGradVec, tenGageTheta}, 0, 0, AIR_FALSE}, {tenGageThetaGradMag, 1, 1, {tenGageThetaGradVec}, 0, 0, AIR_FALSE}, {tenGageThetaNormal, 3, 1, {tenGageThetaGradVec, tenGageThetaGradMag}, 0, 0, AIR_FALSE}, {tenGageOmegaGradVec, 3, 1, {tenGageFA, tenGageMode, tenGageFAGradVec, tenGageModeGradVec}, 0, 0, AIR_FALSE}, {tenGageOmegaGradMag, 1, 1, {tenGageOmegaGradVec}, 0, 0, AIR_FALSE}, {tenGageOmegaNormal, 3, 1, {tenGageOmegaGradVec, tenGageOmegaGradMag}, 0, 0, AIR_FALSE}, {tenGageInvarKGrads, 9, 1, {tenGageDelNormK2, tenGageDelNormK3, tenGageTensorGrad}, 0, 0, AIR_FALSE}, {tenGageInvarKGradMags, 3, 1, {tenGageInvarKGrads}, 0, 0, AIR_FALSE}, {tenGageInvarRGrads, 9, 1, {tenGageDelNormR1, tenGageDelNormR2, tenGageDelNormK3, tenGageTensorGrad}, 0, 0, AIR_FALSE}, {tenGageInvarRGradMags, 3, 1, {tenGageInvarRGrads}, 0, 0, AIR_FALSE}, {tenGageRotTans, 9, 1, {tenGageDelNormPhi1, tenGageDelNormPhi2, tenGageDelNormPhi3, tenGageTensorGrad}, 0, 0, AIR_FALSE}, {tenGageRotTanMags, 3, 1, {tenGageRotTans}, 0, 0, AIR_FALSE}, {tenGageEvalGrads, 9, 1, {tenGageTensorGrad, tenGageEval, tenGageEvec}, 0, 0, AIR_FALSE}, {tenGageCl1, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE}, {tenGageCp1, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE}, {tenGageCa1, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE}, {tenGageClpmin1, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE}, {tenGageCl2, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE}, {tenGageCp2, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE}, {tenGageCa2, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE}, {tenGageClpmin2, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE}, {tenGageHessian, 63, 2, {0}, 0, 0, AIR_FALSE}, {tenGageTraceHessian, 9, 2, {tenGageHessian}, 0, 0, AIR_FALSE}, {tenGageTraceHessianEval, 3, 2, {tenGageTraceHessian}, 0, 0, AIR_FALSE}, {tenGageTraceHessianEval0, 1, 2, {tenGageTraceHessianEval}, tenGageTraceHessianEval, 0, AIR_FALSE}, {tenGageTraceHessianEval1, 1, 2, {tenGageTraceHessianEval}, tenGageTraceHessianEval, 1, AIR_FALSE}, {tenGageTraceHessianEval2, 1, 2, {tenGageTraceHessianEval}, tenGageTraceHessianEval, 2, AIR_FALSE}, {tenGageTraceHessianEvec, 9, 2, {tenGageTraceHessian}, 0, 0, AIR_FALSE}, {tenGageTraceHessianEvec0, 3, 2, {tenGageTraceHessianEvec}, tenGageTraceHessianEvec, 0, AIR_FALSE}, {tenGageTraceHessianEvec1, 3, 2, {tenGageTraceHessianEvec}, tenGageTraceHessianEvec, 3, AIR_FALSE}, {tenGageTraceHessianEvec2, 3, 2, {tenGageTraceHessianEvec}, tenGageTraceHessianEvec, 6, AIR_FALSE}, {tenGageTraceHessianFrob, 1, 2, {tenGageTraceHessian}, 0, 0, AIR_FALSE}, {tenGageBHessian, 9, 2, {tenGageTensor, tenGageTensorGrad, tenGageHessian}, 0, 0, AIR_FALSE}, {tenGageDetHessian, 9, 2, {tenGageTensor, tenGageTensorGrad, tenGageHessian}, 0, 0, AIR_FALSE}, {tenGageSHessian, 9, 2, {tenGageTensor, tenGageTensorGrad, tenGageHessian}, 0, 0, AIR_FALSE}, {tenGageQHessian, 9, 2, {tenGageBHessian, tenGageSHessian}, 0, 0, AIR_FALSE}, {tenGageFAHessian, 9, 2, {tenGageSHessian, tenGageQHessian, tenGageSGradVec, tenGageQGradVec, tenGageFA}, 0, 0, AIR_FALSE}, {tenGageFAHessianEval, 3, 2, {tenGageFAHessian}, 0, 0, AIR_FALSE}, {tenGageFAHessianEval0, 1, 2, {tenGageFAHessianEval}, tenGageFAHessianEval, 0, AIR_FALSE}, {tenGageFAHessianEval1, 1, 2, {tenGageFAHessianEval}, tenGageFAHessianEval, 1, AIR_FALSE}, {tenGageFAHessianEval2, 1, 2, {tenGageFAHessianEval}, tenGageFAHessianEval, 2, AIR_FALSE}, {tenGageFAHessianEvec, 9, 2, {tenGageFAHessian}, 0, 0, AIR_FALSE}, {tenGageFAHessianEvec0, 3, 2, {tenGageFAHessianEvec}, tenGageFAHessianEvec, 0, AIR_FALSE}, {tenGageFAHessianEvec1, 3, 2, {tenGageFAHessianEvec}, tenGageFAHessianEvec, 3, AIR_FALSE}, {tenGageFAHessianEvec2, 3, 2, {tenGageFAHessianEvec}, tenGageFAHessianEvec, 6, AIR_FALSE}, {tenGageFAHessianFrob, 1, 2, {tenGageFAHessian}, 0, 0, AIR_FALSE}, {tenGageFARidgeSurfaceStrength, 1, 2, {tenGageConfidence, tenGageFAHessianEval}, 0, 0, AIR_FALSE}, {tenGageFAValleySurfaceStrength, 1, 2, {tenGageConfidence, tenGageFAHessianEval}, 0, 0, AIR_FALSE}, {tenGageFALaplacian, 1, 2, {tenGageFAHessian}, 0, 0, AIR_FALSE}, {tenGageFAHessianEvalMode, 1, 2, {tenGageFAHessianEval}, 0, 0, AIR_FALSE}, {tenGageFARidgeLineAlignment, 1, 2, {tenGageEvec0, tenGageFAHessianEvec0, tenGageFAHessianEvalMode}, 0, 0, AIR_FALSE}, {tenGageFARidgeSurfaceAlignment, 1, 2, {tenGageEvec0, tenGageFAHessianEvec2, tenGageFAHessianEvalMode}, 0, 0, AIR_FALSE}, {tenGageFA2ndDD, 1, 2, {tenGageFAHessian, tenGageFANormal}, 0, 0, AIR_FALSE}, {tenGageFAGeomTens, 9, 2, {tenGageFAHessian, tenGageFAGradMag, tenGageFANormal}, 0, 0, AIR_FALSE}, {tenGageFAKappa1, 1, 2, {tenGageFAGeomTens}, 0, 0, AIR_FALSE}, {tenGageFAKappa2, 1, 2, {tenGageFAGeomTens}, 0, 0, AIR_FALSE}, {tenGageFATotalCurv, 1, 2, {tenGageFAGeomTens}, 0, 0, AIR_FALSE}, {tenGageFAShapeIndex, 1, 2, {tenGageFAKappa1, tenGageFAKappa2}, 0, 0, AIR_FALSE}, {tenGageFAMeanCurv, 1, 2, {tenGageFAKappa1, tenGageFAKappa2}, 0, 0, AIR_FALSE}, {tenGageFAGaussCurv, 1, 2, {tenGageFAKappa1, tenGageFAKappa2}, 0, 0, AIR_FALSE}, {tenGageFACurvDir1, 3, 2, {tenGageFAGeomTens, tenGageFAKappa2}, 0, 0, AIR_FALSE}, {tenGageFACurvDir2, 3, 2, {tenGageFAGeomTens, tenGageFAKappa1}, 0, 0, AIR_FALSE}, {tenGageFAFlowlineCurv, 1, 2, {tenGageFANormal, tenGageFAHessian, tenGageFAGradMag}, 0, 0, AIR_FALSE}, {tenGageRHessian, 9, 2, {tenGageR, tenGageRGradVec, tenGageTraceHessian, tenGageBHessian, tenGageDetHessian, tenGageSHessian}, 0, 0, AIR_FALSE}, {tenGageModeHessian, 9, 2, {tenGageR, tenGageQ, tenGageRGradVec, tenGageQGradVec, tenGageRHessian, tenGageQHessian}, 0, 0, AIR_FALSE}, {tenGageModeHessianEval, 3, 2, {tenGageModeHessian}, 0, 0, AIR_FALSE}, {tenGageModeHessianEval0, 1, 2, {tenGageModeHessianEval}, tenGageModeHessianEval, 0, AIR_FALSE}, {tenGageModeHessianEval1, 1, 2, {tenGageModeHessianEval}, tenGageModeHessianEval, 1, AIR_FALSE}, {tenGageModeHessianEval2, 1, 2, {tenGageModeHessianEval}, tenGageModeHessianEval, 2, AIR_FALSE}, {tenGageModeHessianEvec, 9, 2, {tenGageModeHessian}, 0, 0, AIR_FALSE}, {tenGageModeHessianEvec0, 3, 2, {tenGageModeHessianEvec}, tenGageModeHessianEvec, 0, AIR_FALSE}, {tenGageModeHessianEvec1, 3, 2, {tenGageModeHessianEvec}, tenGageModeHessianEvec, 3, AIR_FALSE}, {tenGageModeHessianEvec2, 3, 2, {tenGageModeHessianEvec}, tenGageModeHessianEvec, 6, AIR_FALSE}, {tenGageModeHessianFrob, 1, 2, {tenGageModeHessian}, 0, 0, AIR_FALSE}, {tenGageOmegaHessian, 9, 2, {tenGageFA, tenGageMode, tenGageFAGradVec, tenGageModeGradVec, tenGageFAHessian, tenGageModeHessian}, 0, 0, AIR_FALSE}, {tenGageOmegaHessianEval, 3, 2, {tenGageOmegaHessian}, 0, 0, AIR_FALSE}, {tenGageOmegaHessianEval0, 1, 2, {tenGageOmegaHessianEval}, tenGageOmegaHessianEval, 0, AIR_FALSE}, {tenGageOmegaHessianEval1, 1, 2, {tenGageOmegaHessianEval}, tenGageOmegaHessianEval, 1, AIR_FALSE}, {tenGageOmegaHessianEval2, 1, 2, {tenGageOmegaHessianEval}, tenGageOmegaHessianEval, 2, AIR_FALSE}, {tenGageOmegaHessianEvec, 9, 2, {tenGageOmegaHessian}, 0, 0, AIR_FALSE}, {tenGageOmegaHessianEvec0, 3, 2, {tenGageOmegaHessianEvec}, tenGageOmegaHessianEvec, 0, AIR_FALSE}, {tenGageOmegaHessianEvec1, 3, 2, {tenGageOmegaHessianEvec}, tenGageOmegaHessianEvec, 3, AIR_FALSE}, {tenGageOmegaHessianEvec2, 3, 2, {tenGageOmegaHessianEvec}, tenGageOmegaHessianEvec, 6, AIR_FALSE}, {tenGageOmegaLaplacian, 1, 2, {tenGageOmegaHessian}, 0, 0, AIR_FALSE}, {tenGageOmega2ndDD, 1, 2, {tenGageOmegaHessian, tenGageOmegaNormal}, 0, 0, AIR_FALSE}, {tenGageOmegaHessianContrTenEvec0, 1, 2, {tenGageOmegaHessian, tenGageEvec0}, 0, 0, AIR_FALSE}, {tenGageOmegaHessianContrTenEvec1, 1, 2, {tenGageOmegaHessian, tenGageEvec1}, 0, 0, AIR_FALSE}, {tenGageOmegaHessianContrTenEvec2, 1, 2, {tenGageOmegaHessian, tenGageEvec2}, 0, 0, AIR_FALSE}, {tenGageTraceGradVecDotEvec0, 1, 1, {tenGageTraceGradVec, tenGageEvec0}, 0, 0, AIR_FALSE}, {tenGageTraceDiffusionAlign, 1, 1, {tenGageTraceNormal, tenGageEvec0}, 0, 0, AIR_FALSE}, {tenGageTraceDiffusionFraction, 1, 1, {tenGageTraceNormal, tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageFAGradVecDotEvec0, 1, 1, {tenGageFAGradVec, tenGageEvec0}, 0, 0, AIR_FALSE}, {tenGageFADiffusionAlign, 1, 1, {tenGageFANormal, tenGageEvec0}, 0, 0, AIR_FALSE}, {tenGageFADiffusionFraction, 1, 1, {tenGageFANormal, tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageOmegaGradVecDotEvec0, 1, 1, {tenGageOmegaGradVec, tenGageEvec0}, 0, 0, AIR_FALSE}, {tenGageOmegaDiffusionAlign, 1, 1, {tenGageOmegaNormal, tenGageEvec0}, 0, 0, AIR_FALSE}, {tenGageOmegaDiffusionFraction, 1, 1, {tenGageOmegaNormal, tenGageTensor}, 0, 0, AIR_FALSE}, /* currently don't have tenGageConfGradVec */ {tenGageConfGradVecDotEvec0, 1, 1, {tenGageTensorGrad, tenGageEvec0}, 0, 0, AIR_FALSE}, {tenGageConfDiffusionAlign, 1, 1, {tenGageTensorGrad, tenGageEvec0}, 0, 0, AIR_FALSE}, {tenGageConfDiffusionFraction, 1, 1, {tenGageTensorGrad, tenGageTensor}, 0, 0, AIR_FALSE}, {tenGageCovariance, 21, 0, {tenGageTensor}, /* and all the values in iv3 */ 0, 0, AIR_FALSE}, {tenGageCovarianceRGRT, 21, 0, {tenGageCovariance, tenGageDelNormR1, tenGageDelNormR2, tenGageDelNormK3, tenGageDelNormPhi1, tenGageDelNormPhi2, tenGageDelNormPhi3}, 0, 0, AIR_FALSE}, {tenGageCovarianceKGRT, 21, 0, {tenGageCovariance, tenGageDelNormK2, tenGageDelNormK3, tenGageDelNormPhi1, tenGageDelNormPhi2, tenGageDelNormPhi3}, 0, 0, AIR_FALSE}, {tenGageTensorLogEuclidean, 7, 0, {0}, 0, 0, AIR_FALSE}, {tenGageTensorQuatGeoLoxK, 7, 0, {0}, 0, 0, AIR_FALSE}, {tenGageTensorQuatGeoLoxR, 7, 0, {0}, 0, 0, AIR_FALSE}, {tenGageTensorRThetaPhiLinear, 7, 0, {0}, 0, 0, AIR_FALSE}, {tenGageCl1GradVec, 3, 1, {tenGageTrace, tenGageEval, tenGageEvalGrads}, 0, 0, AIR_FALSE}, {tenGageCl1GradMag, 1, 1, {tenGageCl1GradVec}, 0, 0, AIR_FALSE}, {tenGageCl1Normal, 3, 1, {tenGageCl1GradVec, tenGageCl1GradMag}, 0, 0, AIR_FALSE}, {tenGageCp1GradVec, 3, 1, {tenGageTrace, tenGageEval, tenGageEvalGrads}, 0, 0, AIR_FALSE}, {tenGageCp1GradMag, 1, 1, {tenGageCp1GradVec}, 0, 0, AIR_FALSE}, {tenGageCp1Normal, 3, 1, {tenGageCp1GradVec, tenGageCp1GradMag}, 0, 0, AIR_FALSE}, {tenGageCa1GradVec, 3, 1, {tenGageTrace, tenGageEval, tenGageEvalGrads}, 0, 0, AIR_FALSE}, {tenGageCa1GradMag, 1, 1, {tenGageCa1GradVec}, 0, 0, AIR_FALSE}, {tenGageCa1Normal, 3, 1, {tenGageCa1GradVec, tenGageCa1GradMag}, 0, 0, AIR_FALSE}, {tenGageTensorGradRotE, 21, 1, {tenGageTensorGrad, tenGageEval, tenGageEvec}, 0, 0, AIR_FALSE}, {tenGageEvalHessian, 27, 2, {tenGageTensorGradRotE, tenGageHessian, tenGageEval}, 0, 0, AIR_FALSE }, {tenGageCl1Hessian, 9, 2, {tenGageTensorGradRotE, tenGageEvalHessian}, 0, 0, AIR_FALSE }, {tenGageCl1HessianEval, 3, 2, {tenGageCl1Hessian}, 0, 0, AIR_FALSE }, {tenGageCl1HessianEval0, 1, 2, {tenGageCl1HessianEval}, tenGageCl1HessianEval, 0, AIR_FALSE}, {tenGageCl1HessianEval1, 1, 2, {tenGageCl1HessianEval}, tenGageCl1HessianEval, 1, AIR_FALSE}, {tenGageCl1HessianEval2, 1, 2, {tenGageCl1HessianEval}, tenGageCl1HessianEval, 2, AIR_FALSE}, {tenGageCl1HessianEvec, 9, 2, {tenGageCl1Hessian}, 0, 0, AIR_FALSE }, {tenGageCl1HessianEvec0, 3, 2, {tenGageCl1HessianEvec}, tenGageCl1HessianEvec, 0, AIR_FALSE}, {tenGageCl1HessianEvec1, 3, 2, {tenGageCl1HessianEvec}, tenGageCl1HessianEvec, 3, AIR_FALSE}, {tenGageCl1HessianEvec2, 3, 2, {tenGageCl1HessianEvec}, tenGageCl1HessianEvec, 6, AIR_FALSE}, {tenGageCp1Hessian, 9, 2, {tenGageTensorGradRotE, tenGageEvalHessian}, 0, 0, AIR_FALSE }, {tenGageCp1HessianEval, 3, 2, {tenGageCp1Hessian}, 0, 0, AIR_FALSE }, {tenGageCp1HessianEval0, 1, 2, {tenGageCp1HessianEval}, tenGageCp1HessianEval, 0, AIR_FALSE}, {tenGageCp1HessianEval1, 1, 2, {tenGageCp1HessianEval}, tenGageCp1HessianEval, 1, AIR_FALSE}, {tenGageCp1HessianEval2, 1, 2, {tenGageCp1HessianEval}, tenGageCp1HessianEval, 2, AIR_FALSE}, {tenGageCp1HessianEvec, 9, 2, {tenGageCp1Hessian}, 0, 0, AIR_FALSE }, {tenGageCp1HessianEvec0, 3, 2, {tenGageCp1HessianEvec}, tenGageCp1HessianEvec, 0, AIR_FALSE}, {tenGageCp1HessianEvec1, 3, 2, {tenGageCp1HessianEvec}, tenGageCp1HessianEvec, 3, AIR_FALSE}, {tenGageCp1HessianEvec2, 3, 2, {tenGageCp1HessianEvec}, tenGageCp1HessianEvec, 6, AIR_FALSE}, {tenGageCa1Hessian, 9, 2, {tenGageTensorGradRotE, tenGageEvalHessian}, 0, 0, AIR_FALSE }, {tenGageCa1HessianEval, 3, 2, {tenGageCa1Hessian}, 0, 0, AIR_FALSE }, {tenGageCa1HessianEval0, 1, 2, {tenGageCa1HessianEval}, tenGageCa1HessianEval, 0, AIR_FALSE}, {tenGageCa1HessianEval1, 1, 2, {tenGageCa1HessianEval}, tenGageCa1HessianEval, 1, AIR_FALSE}, {tenGageCa1HessianEval2, 1, 2, {tenGageCa1HessianEval}, tenGageCa1HessianEval, 2, AIR_FALSE}, {tenGageCa1HessianEvec, 9, 2, {tenGageCa1Hessian}, 0, 0, AIR_FALSE }, {tenGageCa1HessianEvec0, 3, 2, {tenGageCa1HessianEvec}, tenGageCa1HessianEvec, 0, AIR_FALSE}, {tenGageCa1HessianEvec1, 3, 2, {tenGageCa1HessianEvec}, tenGageCa1HessianEvec, 3, AIR_FALSE}, {tenGageCa1HessianEvec2, 3, 2, {tenGageCa1HessianEvec}, tenGageCa1HessianEvec, 6, AIR_FALSE}, {tenGageFiberCurving, 1, 1, {tenGageRotTans, tenGageEvec}, 0, 0, AIR_FALSE }, {tenGageFiberDispersion, 1, 1, {tenGageRotTans, tenGageEvec}, 0, 0, AIR_FALSE }, {tenGageAniso, TEN_ANISO_MAX+1, 0, {tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE} }; void _tenGageIv3Print(FILE *file, gageContext *ctx, gagePerVolume *pvl) { double *iv3; int i, fd; fd = 2*ctx->radius; iv3 = pvl->iv3 + fd*fd*fd; fprintf(file, "iv3[]'s *Dxx* component:\n"); switch(fd) { case 2: fprintf(file, "% 10.4f % 10.4f\n", (float)iv3[6], (float)iv3[7]); fprintf(file, " % 10.4f % 10.4f\n\n", (float)iv3[4], (float)iv3[5]); fprintf(file, "% 10.4f % 10.4f\n", (float)iv3[2], (float)iv3[3]); fprintf(file, " % 10.4f % 10.4f\n", (float)iv3[0], (float)iv3[1]); break; case 4: for (i=3; i>=0; i--) { fprintf(file, "% 10.4f % 10.4f % 10.4f % 10.4f\n", (float)iv3[12+16*i], (float)iv3[13+16*i], (float)iv3[14+16*i], (float)iv3[15+16*i]); fprintf(file, " % 10.4f %c% 10.4f % 10.4f%c % 10.4f\n", (float)iv3[ 8+16*i], (i==1||i==2)?'\\':' ', (float)iv3[ 9+16*i], (float)iv3[10+16*i], (i==1||i==2)?'\\':' ', (float)iv3[11+16*i]); fprintf(file, " % 10.4f %c% 10.4f % 10.4f%c % 10.4f\n", (float)iv3[ 4+16*i], (i==1||i==2)?'\\':' ', (float)iv3[ 5+16*i], (float)iv3[ 6+16*i], (i==1||i==2)?'\\':' ', (float)iv3[ 7+16*i]); fprintf(file, " % 10.4f % 10.4f % 10.4f % 10.4f\n", (float)iv3[ 0+16*i], (float)iv3[ 1+16*i], (float)iv3[ 2+16*i], (float)iv3[ 3+16*i]); if (i) fprintf(file, "\n"); } break; default: for (i=0; iradius; ten = pvl->directAnswer[tenGageTensor]; tgrad = pvl->directAnswer[tenGageTensorGrad]; thess = pvl->directAnswer[tenGageHessian]; if (!ctx->parm.k3pack) { fprintf(stderr, "!%s: sorry, 6pack filtering not implemented\n", me); return; } fw00 = ctx->fw + fd*3*gageKernel00; fw11 = ctx->fw + fd*3*gageKernel11; fw22 = ctx->fw + fd*3*gageKernel22; /* perform the filtering */ /* HEY: we still want trilinear interpolation of confidence, no? */ if (fd <= 8) { for (valIdx=0; valIdx<7; valIdx++) { filter[ctx->radius](ctx->shape, pvl->iv3 + valIdx*fd*fd*fd, pvl->iv2 + valIdx*fd*fd, pvl->iv1 + valIdx*fd, fw00, fw11, fw22, ten + valIdx, tgrad + valIdx*3, thess + valIdx*9, pvl->needD); } } else { for (valIdx=0; valIdx<7; valIdx++) { gageScl3PFilterN(ctx->shape, fd, pvl->iv3 + valIdx*fd*fd*fd, pvl->iv2 + valIdx*fd*fd, pvl->iv1 + valIdx*fd, fw00, fw11, fw22, ten + valIdx, tgrad + valIdx*3, thess + valIdx*9, pvl->needD); } } return; } void _tenGageAnswer(gageContext *ctx, gagePerVolume *pvl) { char me[]="_tenGageAnswer"; double *tenAns, *evalAns, *evecAns, *vecTmp=NULL, *matTmp=NULL, *gradDtA=NULL, *gradDtB=NULL, *gradDtC=NULL, *gradDtD=NULL, *gradDtE=NULL, *gradDtF=NULL, *hessDtA=NULL, *hessDtB=NULL, *hessDtC=NULL, *hessDtD=NULL, *hessDtE=NULL, *hessDtF=NULL, *gradCbS=NULL, *gradCbB=NULL, *gradCbQ=NULL, *gradCbR=NULL, *hessCbS=NULL, *hessCbB=NULL, *hessCbQ=NULL, *hessCbR=NULL, gradDdXYZ[21]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; double tmp0, tmp1, tmp3, magTmp=0, dtA=0, dtB=0, dtC=0, dtD=0, dtE=0, dtF=0, cbQ=0, cbR=0, cbA=0, cbB=0, cbC=0, cbS=0, gradCbA[3]={0,0,0}, gradCbC[3]={0,0,0}; double hessCbA[9]={0,0,0,0,0,0,0,0,0}, hessCbC[9]={0,0,0,0,0,0,0,0,0}; int ci; tenAns = pvl->directAnswer[tenGageTensor]; evalAns = pvl->directAnswer[tenGageEval]; evecAns = pvl->directAnswer[tenGageEvec]; if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensor)) { /* done if doV */ /* HEY: this was prohibiting a Deft-related hack tenAns[0] = AIR_CLAMP(0, tenAns[0], 1); */ /* HEY: and this was botching using 1-conf as potential energy for push tenAns[0] = AIR_MAX(0, tenAns[0]); */ dtA = tenAns[1]; dtB = tenAns[2]; dtC = tenAns[3]; dtD = tenAns[4]; dtE = tenAns[5]; dtF = tenAns[6]; if (ctx->verbose) { fprintf(stderr, "!%s: tensor = (%g) %g %g %g %g %g %g\n", me, tenAns[0], dtA, dtB, dtC, dtD, dtE, dtF); } } /* done if doV if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageConfidence)) { } */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTrace)) { cbA = -(pvl->directAnswer[tenGageTrace][0] = dtA + dtD + dtF); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageNorm)) { pvl->directAnswer[tenGageNorm][0] = sqrt(dtA*dtA + dtD*dtD + dtF*dtF + 2*dtB*dtB + 2*dtC*dtC + 2*dtE*dtE); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageB)) { cbB = pvl->directAnswer[tenGageB][0] = dtA*dtD + dtA*dtF + dtD*dtF - dtB*dtB - dtC*dtC - dtE*dtE; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDet)) { cbC = -(pvl->directAnswer[tenGageDet][0] = 2*dtB*dtC*dtE + dtA*dtD*dtF - dtC*dtC*dtD - dtA*dtE*dtE - dtB*dtB*dtF); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageS)) { cbS = (pvl->directAnswer[tenGageS][0] = dtA*dtA + dtD*dtD + dtF*dtF + 2*dtB*dtB + 2*dtC*dtC + 2*dtE*dtE); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageQ)) { cbQ = pvl->directAnswer[tenGageQ][0] = (cbS - cbB)/9; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFA)) { tmp0 = (cbS ? cbQ/cbS : 0); tmp0 = AIR_MAX(0, tmp0); pvl->directAnswer[tenGageFA][0] = 3*sqrt(tmp0); /* if (!AIR_EXISTS(pvl->directAnswer[tenGageFA][0])) { fprintf(stderr, "!%s: cbS = %g, cbQ = %g, cbQ/(epsilon + cbS) = %g\n" "tmp0 = max(0, cbQ/(epsilon + cbS)) = %g\n" "sqrt(tmp0) = %g --> %g\n", me, cbS, cbQ, cbQ/(epsilon + cbS), tmp0, sqrt(tmp0), pvl->directAnswer[tenGageFA][0]); } */ } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageR)) { cbR = pvl->directAnswer[tenGageR][0] = (5*cbA*cbB - 27*cbC - 2*cbA*cbS)/54; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageMode)) { double cbQQQ; cbQQQ = 2*AIR_MAX(0, cbQ*cbQ*cbQ); tmp0 = 1.41421356237309504880*(cbQQQ ? cbR/(sqrt(cbQQQ)) : 0); pvl->directAnswer[tenGageMode][0] = AIR_CLAMP(-1, tmp0, 1); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmega)) { pvl->directAnswer[tenGageOmega][0] = pvl->directAnswer[tenGageFA][0]*(1+pvl->directAnswer[tenGageMode][0])/2; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTheta)) { pvl->directAnswer[tenGageTheta][0] = acos(-pvl->directAnswer[tenGageMode][0])/AIR_PI; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageModeWarp)) { pvl->directAnswer[tenGageModeWarp][0] = cos((1-pvl->directAnswer[tenGageMode][0])*AIR_PI/2); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageEvec)) { /* we do the longer process to get eigenvectors, and in the process we always find the eigenvalues, whether or not they were asked for */ tenEigensolve_d(evalAns, evecAns, tenAns); } else if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageEval)) { /* else eigenvectors are NOT needed, but eigenvalues ARE needed */ tenEigensolve_d(evalAns, NULL, tenAns); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDelNormK2) || GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDelNormK3)) { double tmp[7]; tenInvariantGradientsK_d(tmp, pvl->directAnswer[tenGageDelNormK2], pvl->directAnswer[tenGageDelNormK3], tenAns, 0.0000001); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDelNormR1) || GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDelNormR2)) { tenInvariantGradientsR_d(pvl->directAnswer[tenGageDelNormR1], pvl->directAnswer[tenGageDelNormR2], pvl->directAnswer[tenGageDelNormK3], tenAns, 0.0000001); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDelNormPhi1) || GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDelNormPhi2) || GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDelNormPhi3)) { tenRotationTangents_d(pvl->directAnswer[tenGageDelNormPhi1], pvl->directAnswer[tenGageDelNormPhi2], pvl->directAnswer[tenGageDelNormPhi3], evecAns); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorGrad)) { /* done if doD1 */ /* still have to set up pointer variables that item answers below will rely on as short-cuts */ vecTmp = pvl->directAnswer[tenGageTensorGrad]; gradDtA = vecTmp + 1*3; gradDtB = vecTmp + 2*3; gradDtC = vecTmp + 3*3; gradDtD = vecTmp + 4*3; gradDtE = vecTmp + 5*3; gradDtF = vecTmp + 6*3; TEN_T_SET(gradDdXYZ + 0*7, tenAns[0], gradDtA[0], gradDtB[0], gradDtC[0], gradDtD[0], gradDtE[0], gradDtF[0]); TEN_T_SET(gradDdXYZ + 1*7, tenAns[0], gradDtA[1], gradDtB[1], gradDtC[1], gradDtD[1], gradDtE[1], gradDtF[1]); TEN_T_SET(gradDdXYZ + 2*7, tenAns[0], gradDtA[2], gradDtB[2], gradDtC[2], gradDtD[2], gradDtE[2], gradDtF[2]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorGradMag)) { vecTmp = pvl->directAnswer[tenGageTensorGradMag]; vecTmp[0] = sqrt(TEN_T_DOT(gradDdXYZ + 0*7, gradDdXYZ + 0*7)); vecTmp[1] = sqrt(TEN_T_DOT(gradDdXYZ + 1*7, gradDdXYZ + 1*7)); vecTmp[2] = sqrt(TEN_T_DOT(gradDdXYZ + 2*7, gradDdXYZ + 2*7)); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorGradMag)) { pvl->directAnswer[tenGageTensorGradMagMag][0] = ELL_3V_LEN(vecTmp); } /* --- Trace --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTraceGradVec)) { vecTmp = pvl->directAnswer[tenGageTraceGradVec]; ELL_3V_ADD3(vecTmp, gradDtA, gradDtD, gradDtF); ELL_3V_SCALE(gradCbA, -1, vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTraceGradMag)) { magTmp = pvl->directAnswer[tenGageTraceGradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTraceNormal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageTraceNormal], magTmp ? 1/magTmp : 0, vecTmp); } /* ---- Norm stuff handled after S */ /* --- B --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageBGradVec)) { gradCbB = vecTmp = pvl->directAnswer[tenGageBGradVec]; ELL_3V_SCALE_ADD6(vecTmp, dtD + dtF, gradDtA, dtA + dtF, gradDtD, dtA + dtD, gradDtF, -2*dtB, gradDtB, -2*dtC, gradDtC, -2*dtE, gradDtE); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageBGradMag)) { magTmp = pvl->directAnswer[tenGageBGradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageBNormal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageBNormal], magTmp ? 1/magTmp : 0, vecTmp); } /* --- Det --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDetGradVec)) { vecTmp = pvl->directAnswer[tenGageDetGradVec]; ELL_3V_SCALE_ADD6(vecTmp, dtD*dtF - dtE*dtE, gradDtA, 2*(dtC*dtE - dtB*dtF), gradDtB, 2*(dtB*dtE - dtC*dtD), gradDtC, dtA*dtF - dtC*dtC, gradDtD, 2*(dtB*dtC - dtA*dtE), gradDtE, dtA*dtD - dtB*dtB, gradDtF); ELL_3V_SCALE(gradCbC, -1, vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDetGradMag)) { magTmp = pvl->directAnswer[tenGageDetGradMag][0] = AIR_CAST(float, ELL_3V_LEN(vecTmp)); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDetNormal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageDetNormal], magTmp ? 1/magTmp : 0, vecTmp); } /* --- S --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageSGradVec)) { gradCbS = vecTmp = pvl->directAnswer[tenGageSGradVec]; ELL_3V_SCALE_ADD6(vecTmp, 2*dtA, gradDtA, 2*dtD, gradDtD, 2*dtF, gradDtF, 4*dtB, gradDtB, 4*dtC, gradDtC, 4*dtE, gradDtE); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageSGradMag)) { magTmp = pvl->directAnswer[tenGageSGradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageSNormal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageSNormal], magTmp ? 1/magTmp : 0, vecTmp); } /* --- Norm --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageNormGradVec)) { double nslc; nslc = pvl->directAnswer[tenGageNorm][0]; nslc = nslc ? 1/(2*nslc) : 0.0; vecTmp = pvl->directAnswer[tenGageNormGradVec]; ELL_3V_SCALE(vecTmp, nslc, pvl->directAnswer[tenGageSGradVec]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageNormGradMag)) { magTmp = pvl->directAnswer[tenGageNormGradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageNormNormal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageNormNormal], magTmp ? 1/magTmp : 0, vecTmp); } /* --- Q --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageQGradVec)) { gradCbQ = vecTmp = pvl->directAnswer[tenGageQGradVec]; ELL_3V_SCALE_ADD2(vecTmp, 1.0/9, gradCbS, -1.0/9, gradCbB); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageQGradMag)) { magTmp = pvl->directAnswer[tenGageQGradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageQNormal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageQNormal], magTmp ? 1/magTmp : 0, vecTmp); } /* --- FA --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAGradVec)) { vecTmp = pvl->directAnswer[tenGageFAGradVec]; tmp3 = AIR_MAX(0, pvl->directAnswer[tenGageFA][0]); tmp0 = cbQ ? tmp3/(2*cbQ) : 0; tmp1 = cbS ? -tmp3/(2*cbS) : 0; ELL_3V_SCALE_ADD2(vecTmp, tmp0, gradCbQ, tmp1, gradCbS); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAGradMag)) { magTmp = pvl->directAnswer[tenGageFAGradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFANormal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageFANormal], magTmp ? 1/magTmp : 0, vecTmp); } /* --- R --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageRGradVec)) { gradCbR = vecTmp = pvl->directAnswer[tenGageRGradVec]; ELL_3V_SCALE_ADD4(vecTmp, (5*cbB - 2*cbS)/54, gradCbA, 5*cbA/54, gradCbB, -0.5, gradCbC, -cbA/27, gradCbS); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageRGradMag)) { magTmp = pvl->directAnswer[tenGageRGradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageRNormal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageRNormal], magTmp ? 1/magTmp : 0, vecTmp); } /* --- Mode --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageModeGradVec)) { vecTmp = pvl->directAnswer[tenGageModeGradVec]; tmp1 = AIR_MAX(0, cbQ*cbQ*cbQ); tmp1 = tmp1 ? sqrt(1/tmp1) : 0; tmp0 = cbQ ? -tmp1*3*cbR/(2*cbQ) : 0; ELL_3V_SCALE_ADD2(vecTmp, tmp0, gradCbQ, tmp1, gradCbR); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageModeGradMag)) { magTmp = pvl->directAnswer[tenGageModeGradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageModeNormal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageModeNormal], magTmp ? 1/magTmp : 0, vecTmp); } /* --- Theta --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageThetaGradVec)) { vecTmp = pvl->directAnswer[tenGageThetaGradVec]; tmp1 = AIR_MAX(0, cbQ*cbQ*cbQ); tmp0 = tmp1 ? cbR*cbR/tmp1 : 0; tmp1 = sqrt(tmp1)*sqrt(1.0 - tmp0); tmp1 = tmp1 ? 1/(AIR_PI*tmp1) : 0.0; tmp0 = cbQ ? -tmp1*3*cbR/(2*cbQ) : 0.0; ELL_3V_SCALE_ADD2(vecTmp, tmp0, gradCbQ, tmp1, gradCbR); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageThetaGradMag)) { magTmp = pvl->directAnswer[tenGageThetaGradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageThetaNormal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageThetaNormal], magTmp ? 1/magTmp : 0, vecTmp); } /* --- Omega --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaGradVec)) { double fa, mode, *faGrad, *modeGrad; vecTmp = pvl->directAnswer[tenGageOmegaGradVec]; fa = pvl->directAnswer[tenGageFA][0]; mode = pvl->directAnswer[tenGageMode][0]; faGrad = pvl->directAnswer[tenGageFAGradVec]; modeGrad = pvl->directAnswer[tenGageModeGradVec]; ELL_3V_SCALE_ADD2(vecTmp, (1+mode)/2, faGrad, fa/2, modeGrad); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaGradMag)) { magTmp = pvl->directAnswer[tenGageOmegaGradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaNormal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageOmegaNormal], magTmp ? 1/magTmp : 0, vecTmp); } #define SQRT_1_OVER_3 0.57735026918962576450 /* --- Invariant gradients + rotation tangents --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageInvarKGrads)) { double mu1Grad[7], *mu2Grad, *skwGrad; TEN_T_SET(mu1Grad, 1, SQRT_1_OVER_3, 0, 0, SQRT_1_OVER_3, 0, SQRT_1_OVER_3); mu2Grad = pvl->directAnswer[tenGageDelNormK2]; skwGrad = pvl->directAnswer[tenGageDelNormK3]; ELL_3V_SET(pvl->directAnswer[tenGageInvarKGrads] + 0*3, TEN_T_DOT(mu1Grad, gradDdXYZ + 0*7), TEN_T_DOT(mu1Grad, gradDdXYZ + 1*7), TEN_T_DOT(mu1Grad, gradDdXYZ + 2*7)); ELL_3V_SET(pvl->directAnswer[tenGageInvarKGrads] + 1*3, TEN_T_DOT(mu2Grad, gradDdXYZ + 0*7), TEN_T_DOT(mu2Grad, gradDdXYZ + 1*7), TEN_T_DOT(mu2Grad, gradDdXYZ + 2*7)); ELL_3V_SET(pvl->directAnswer[tenGageInvarKGrads] + 2*3, TEN_T_DOT(skwGrad, gradDdXYZ + 0*7), TEN_T_DOT(skwGrad, gradDdXYZ + 1*7), TEN_T_DOT(skwGrad, gradDdXYZ + 2*7)); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageInvarKGradMags)) { ELL_3V_SET(pvl->directAnswer[tenGageInvarKGradMags], ELL_3V_LEN(pvl->directAnswer[tenGageInvarKGrads] + 0*3), ELL_3V_LEN(pvl->directAnswer[tenGageInvarKGrads] + 1*3), ELL_3V_LEN(pvl->directAnswer[tenGageInvarKGrads] + 2*3)); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageInvarRGrads)) { double *R1Grad, *R2Grad, *R3Grad; R1Grad = pvl->directAnswer[tenGageDelNormR1]; R2Grad = pvl->directAnswer[tenGageDelNormR2]; R3Grad = pvl->directAnswer[tenGageDelNormK3]; ELL_3V_SET(pvl->directAnswer[tenGageInvarRGrads] + 0*3, TEN_T_DOT(R1Grad, gradDdXYZ + 0*7), TEN_T_DOT(R1Grad, gradDdXYZ + 1*7), TEN_T_DOT(R1Grad, gradDdXYZ + 2*7)); ELL_3V_SET(pvl->directAnswer[tenGageInvarRGrads] + 1*3, TEN_T_DOT(R2Grad, gradDdXYZ + 0*7), TEN_T_DOT(R2Grad, gradDdXYZ + 1*7), TEN_T_DOT(R2Grad, gradDdXYZ + 2*7)); ELL_3V_SET(pvl->directAnswer[tenGageInvarRGrads] + 2*3, TEN_T_DOT(R3Grad, gradDdXYZ + 0*7), TEN_T_DOT(R3Grad, gradDdXYZ + 1*7), TEN_T_DOT(R3Grad, gradDdXYZ + 2*7)); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageInvarRGradMags)) { ELL_3V_SET(pvl->directAnswer[tenGageInvarRGradMags], ELL_3V_LEN(pvl->directAnswer[tenGageInvarRGrads] + 0*3), ELL_3V_LEN(pvl->directAnswer[tenGageInvarRGrads] + 1*3), ELL_3V_LEN(pvl->directAnswer[tenGageInvarRGrads] + 2*3)); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageEvalGrads)) { double matOut[9], tenOut[9], tmpRes[9], rel1, rel2, w1, w2, eps; unsigned int evi; for (evi=0; evi<=2; evi++) { ELL_3MV_OUTER(matOut, evecAns + evi*3, evecAns + evi*3); TEN_M2T(tenOut, matOut); ELL_3V_SET(tmpRes + evi*3, TEN_T_DOT(tenOut, gradDdXYZ + 0*7), TEN_T_DOT(tenOut, gradDdXYZ + 1*7), TEN_T_DOT(tenOut, gradDdXYZ + 2*7)); } /* Added 2008-06-27: In case there are duplicate eigenvalues, * average their derivatives to avoid visible artifacs in edge * maps. Provide a smooth transition to the ill-defined case */ eps=0.05; /* threshold at which we start the transition */ /* interpolation weights from relative eigenvalue distance */ rel1=(evalAns[0]-evalAns[1])/(fabs(evalAns[0])+fabs(evalAns[1])); rel2=(evalAns[1]-evalAns[2])/(fabs(evalAns[1])+fabs(evalAns[2])); w1=rel1/eps-1; w1*=w1; w2=rel2/eps-1; w2*=w2; if (rel1>eps) { ELL_3V_COPY(pvl->directAnswer[tenGageEvalGrads], tmpRes); } else { if (rel2>eps) { ELL_3V_SCALE_ADD2(pvl->directAnswer[tenGageEvalGrads], 1-0.5*w1, tmpRes, 0.5*w1, tmpRes+3); } else { ELL_3V_SCALE_ADD3(pvl->directAnswer[tenGageEvalGrads], 1-0.5*w1-w1*w2/6.0, tmpRes, 0.5*w1-w1*w2/6.0, tmpRes+3, w1*w2/3.0, tmpRes+6); } } if (rel2>eps) { ELL_3V_COPY(pvl->directAnswer[tenGageEvalGrads]+6, tmpRes+6); } else { if (rel1>eps) { ELL_3V_SCALE_ADD2(pvl->directAnswer[tenGageEvalGrads]+6, 1-0.5*w2, tmpRes+6, 0.5*w2, tmpRes+3); } else { ELL_3V_SCALE_ADD3(pvl->directAnswer[tenGageEvalGrads]+6, 1-0.5*w2-w1*w2/6.0, tmpRes+6, 0.5*w2-w1*w2/6.0, tmpRes+3, w1*w2/3.0, tmpRes); } } ELL_3V_ADD3(pvl->directAnswer[tenGageEvalGrads]+3, tmpRes, tmpRes+3, tmpRes+6); ELL_3V_ADD2(tmpRes, pvl->directAnswer[tenGageEvalGrads], pvl->directAnswer[tenGageEvalGrads]+6); ELL_3V_SUB(pvl->directAnswer[tenGageEvalGrads]+3, pvl->directAnswer[tenGageEvalGrads]+3, tmpRes); /* l2 = trace - l1 - l3 */ } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageRotTans)) { double phi1[7], phi2[7], phi3[7]; tenRotationTangents_d(phi1, phi2, phi3, evecAns); ELL_3V_SET(pvl->directAnswer[tenGageRotTans] + 0*3, TEN_T_DOT(phi1, gradDdXYZ + 0*7), TEN_T_DOT(phi1, gradDdXYZ + 1*7), TEN_T_DOT(phi1, gradDdXYZ + 2*7)); ELL_3V_SET(pvl->directAnswer[tenGageRotTans] + 1*3, TEN_T_DOT(phi2, gradDdXYZ + 0*7), TEN_T_DOT(phi2, gradDdXYZ + 1*7), TEN_T_DOT(phi2, gradDdXYZ + 2*7)); ELL_3V_SET(pvl->directAnswer[tenGageRotTans] + 2*3, TEN_T_DOT(phi3, gradDdXYZ + 0*7), TEN_T_DOT(phi3, gradDdXYZ + 1*7), TEN_T_DOT(phi3, gradDdXYZ + 2*7)); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageRotTanMags)) { ELL_3V_SET(pvl->directAnswer[tenGageRotTanMags], ELL_3V_LEN(pvl->directAnswer[tenGageRotTans] + 0*3), ELL_3V_LEN(pvl->directAnswer[tenGageRotTans] + 1*3), ELL_3V_LEN(pvl->directAnswer[tenGageRotTans] + 2*3)); } /* --- C{l,p,a,lpmin}1 --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCl1)) { tmp0 = tenAnisoEval_d(evalAns, tenAniso_Cl1); pvl->directAnswer[tenGageCl1][0] = AIR_CLAMP(0, tmp0, 1); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCp1)) { tmp0 = tenAnisoEval_d(evalAns, tenAniso_Cp1); pvl->directAnswer[tenGageCp1][0] = AIR_CLAMP(0, tmp0, 1); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCa1)) { tmp0 = tenAnisoEval_d(evalAns, tenAniso_Ca1); pvl->directAnswer[tenGageCa1][0] = AIR_CLAMP(0, tmp0, 1); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageClpmin1)) { tmp0 = tenAnisoEval_d(evalAns, tenAniso_Clpmin1); pvl->directAnswer[tenGageClpmin1][0] = AIR_CLAMP(0, tmp0, 1); } /* --- C{l,p,a,lpmin}2 --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCl2)) { tmp0 = tenAnisoEval_d(evalAns, tenAniso_Cl2); pvl->directAnswer[tenGageCl2][0] = AIR_CLAMP(0, tmp0, 1); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCp2)) { tmp0 = tenAnisoEval_d(evalAns, tenAniso_Cp2); pvl->directAnswer[tenGageCp2][0] = AIR_CLAMP(0, tmp0, 1); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCa2)) { tmp0 = tenAnisoEval_d(evalAns, tenAniso_Ca2); pvl->directAnswer[tenGageCa2][0] = AIR_CLAMP(0, tmp0, 1); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageClpmin2)) { tmp0 = tenAnisoEval_d(evalAns, tenAniso_Clpmin2); pvl->directAnswer[tenGageClpmin2][0] = AIR_CLAMP(0, tmp0, 1); } /* --- Hessian madness (the derivative, not the soldier) --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageHessian)) { /* done if doD2; still have to set up pointers */ matTmp = pvl->directAnswer[tenGageHessian]; hessDtA = matTmp + 1*9; hessDtB = matTmp + 2*9; hessDtC = matTmp + 3*9; hessDtD = matTmp + 4*9; hessDtE = matTmp + 5*9; hessDtF = matTmp + 6*9; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTraceHessian)) { ELL_3M_SCALE_ADD3(pvl->directAnswer[tenGageTraceHessian], 1.0, hessDtA, 1.0, hessDtD, 1.0, hessDtF); ELL_3M_SCALE(hessCbA, -1, pvl->directAnswer[tenGageTraceHessian]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTraceHessianEvec)) { ell_3m_eigensolve_d(pvl->directAnswer[tenGageTraceHessianEval], pvl->directAnswer[tenGageTraceHessianEvec], pvl->directAnswer[tenGageTraceHessian], AIR_TRUE); } else if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTraceHessianEval)) { ell_3m_eigenvalues_d(pvl->directAnswer[tenGageTraceHessianEval], pvl->directAnswer[tenGageTraceHessian], AIR_TRUE); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTraceHessianFrob)) { pvl->directAnswer[tenGageTraceHessianFrob][0] = ELL_3M_FROB(pvl->directAnswer[tenGageTraceHessian]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageBHessian)) { hessCbB = matTmp = pvl->directAnswer[tenGageBHessian]; ELL_3M_ZERO_SET(matTmp); ELL_3M_SCALE_INCR(matTmp, dtB, hessDtB); ELL_3M_SCALE_INCR(matTmp, dtC, hessDtC); ELL_3M_SCALE_INCR(matTmp, dtE, hessDtE); ELL_3MV_OUTER_INCR(matTmp, gradDtB, gradDtB); ELL_3MV_OUTER_INCR(matTmp, gradDtC, gradDtC); ELL_3MV_OUTER_INCR(matTmp, gradDtE, gradDtE); ELL_3M_SCALE(matTmp, -2, matTmp); ELL_3MV_OUTER_INCR(matTmp, gradDtD, gradDtA); ELL_3MV_OUTER_INCR(matTmp, gradDtF, gradDtA); ELL_3MV_OUTER_INCR(matTmp, gradDtA, gradDtD); ELL_3MV_OUTER_INCR(matTmp, gradDtF, gradDtD); ELL_3MV_OUTER_INCR(matTmp, gradDtA, gradDtF); ELL_3MV_OUTER_INCR(matTmp, gradDtD, gradDtF); ELL_3M_SCALE_INCR(matTmp, dtD + dtF, hessDtA); ELL_3M_SCALE_INCR(matTmp, dtA + dtF, hessDtD); ELL_3M_SCALE_INCR(matTmp, dtA + dtD, hessDtF); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageDetHessian)) { double tmp[3]; matTmp = pvl->directAnswer[tenGageDetHessian]; ELL_3M_ZERO_SET(matTmp); ELL_3V_SCALE_ADD3(tmp, dtD, gradDtF, dtF, gradDtD, -2*dtE, gradDtE); ELL_3MV_OUTER_INCR(matTmp, tmp, gradDtA); ELL_3M_SCALE_INCR(matTmp, dtD*dtF - dtE*dtE, hessDtA); ELL_3V_SCALE_ADD4(tmp, 2*dtC, gradDtE, 2*dtE, gradDtC, -2*dtB, gradDtF, -2*dtF, gradDtB); ELL_3MV_OUTER_INCR(matTmp, tmp, gradDtB); ELL_3M_SCALE_INCR(matTmp, 2*(dtC*dtE - dtB*dtF), hessDtB); ELL_3V_SCALE_ADD4(tmp, 2*dtB, gradDtE, 2*dtE, gradDtB, -2*dtC, gradDtD, -2*dtD, gradDtC); ELL_3MV_OUTER_INCR(matTmp, tmp, gradDtC); ELL_3M_SCALE_INCR(matTmp, 2*(dtB*dtE - dtC*dtD), hessDtC); ELL_3V_SCALE_ADD3(tmp, dtA, gradDtF, dtF, gradDtA, -2*dtC, gradDtC); ELL_3MV_OUTER_INCR(matTmp, tmp, gradDtD); ELL_3M_SCALE_INCR(matTmp, dtA*dtF - dtC*dtC, hessDtD); ELL_3V_SCALE_ADD4(tmp, 2*dtB, gradDtC, 2*dtC, gradDtB, -2*dtA, gradDtE, -2*dtE, gradDtA); ELL_3MV_OUTER_INCR(matTmp, tmp, gradDtE); ELL_3M_SCALE_INCR(matTmp, 2*(dtB*dtC - dtA*dtE), hessDtE); ELL_3V_SCALE_ADD3(tmp, dtA, gradDtD, dtD, gradDtA, -2*dtB, gradDtB); ELL_3MV_OUTER_INCR(matTmp, tmp, gradDtF); ELL_3M_SCALE_INCR(matTmp, dtA*dtD - dtB*dtB, hessDtF); ELL_3M_SCALE(hessCbC, -1, pvl->directAnswer[tenGageDetHessian]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageSHessian)) { hessCbS = matTmp = pvl->directAnswer[tenGageSHessian]; ELL_3M_ZERO_SET(matTmp); ELL_3M_SCALE_INCR(matTmp, dtB, hessDtB); ELL_3MV_OUTER_INCR(matTmp, gradDtB, gradDtB); ELL_3M_SCALE_INCR(matTmp, dtC, hessDtC); ELL_3MV_OUTER_INCR(matTmp, gradDtC, gradDtC); ELL_3M_SCALE_INCR(matTmp, dtE, hessDtE); ELL_3MV_OUTER_INCR(matTmp, gradDtE, gradDtE); ELL_3M_SCALE(matTmp, 2, matTmp); ELL_3M_SCALE_INCR(matTmp, dtA, hessDtA); ELL_3MV_OUTER_INCR(matTmp, gradDtA, gradDtA); ELL_3M_SCALE_INCR(matTmp, dtD, hessDtD); ELL_3MV_OUTER_INCR(matTmp, gradDtD, gradDtD); ELL_3M_SCALE_INCR(matTmp, dtF, hessDtF); ELL_3MV_OUTER_INCR(matTmp, gradDtF, gradDtF); ELL_3M_SCALE(matTmp, 2, matTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageQHessian)) { hessCbQ = pvl->directAnswer[tenGageQHessian]; ELL_3M_SCALE_ADD2(hessCbQ, 1.0/9, hessCbS, -1.0/9, hessCbB); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAHessian)) { double tmpQ, rQ, orQ, oQ, tmpS, rS, orS, oS; tmpQ = AIR_MAX(0, cbQ); tmpS = AIR_MAX(0, cbS); oQ = tmpQ ? 1/tmpQ : 0; oS = tmpS ? 1/tmpS : 0; rQ = sqrt(tmpQ); rS = sqrt(tmpS); orQ = rQ ? 1/rQ : 0; orS = rS ? 1/rS : 0; matTmp = pvl->directAnswer[tenGageFAHessian]; ELL_3M_ZERO_SET(matTmp); ELL_3M_SCALE_INCR(matTmp, orS*orQ, hessCbQ); ELL_3M_SCALE_INCR(matTmp, -rQ*orS*oS, hessCbS); ELL_3MV_SCALE_OUTER_INCR(matTmp, -orS*orQ*oQ/2, gradCbQ, gradCbQ); ELL_3MV_SCALE_OUTER_INCR(matTmp, 3*rQ*orS*oS*oS/2, gradCbS, gradCbS); ELL_3MV_SCALE_OUTER_INCR(matTmp, -orS*oS*orQ/2, gradCbS, gradCbQ); ELL_3MV_SCALE_OUTER_INCR(matTmp, -orQ*orS*oS/2, gradCbQ, gradCbS); ELL_3M_SCALE(matTmp, 3.0/2, matTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAHessianEvec)) { ell_3m_eigensolve_d(pvl->directAnswer[tenGageFAHessianEval], pvl->directAnswer[tenGageFAHessianEvec], pvl->directAnswer[tenGageFAHessian], AIR_TRUE); } else if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAHessianEval)) { ell_3m_eigenvalues_d(pvl->directAnswer[tenGageFAHessianEval], pvl->directAnswer[tenGageFAHessian], AIR_TRUE); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAHessianFrob)) { pvl->directAnswer[tenGageFAHessianFrob][0] = ELL_3M_FROB(pvl->directAnswer[tenGageFAHessian]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFARidgeSurfaceStrength)) { double ev; ev = -pvl->directAnswer[tenGageFAHessianEval][2]; ev = AIR_MAX(0, ev); pvl->directAnswer[tenGageFARidgeSurfaceStrength][0] = tenAns[0]*ev; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAValleySurfaceStrength)) { double ev; ev = pvl->directAnswer[tenGageFAHessianEval][0]; ev = AIR_MAX(0, ev); pvl->directAnswer[tenGageFAValleySurfaceStrength][0] = tenAns[0]*ev; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFALaplacian)) { double *hess; hess = pvl->directAnswer[tenGageFAHessian]; pvl->directAnswer[tenGageFALaplacian][0] = hess[0] + hess[4] + hess[8]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAHessianEvalMode)) { double *heval; heval = pvl->directAnswer[tenGageFAHessianEval]; pvl->directAnswer[tenGageFAHessianEvalMode][0] = airMode3_d(heval); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFARidgeLineAlignment)) { double *hev0, *dev0, dot, mde; hev0 = pvl->directAnswer[tenGageFAHessianEvec0]; dev0 = pvl->directAnswer[tenGageEvec0]; dot = ELL_3V_DOT(hev0, dev0); mde = pvl->directAnswer[tenGageFAHessianEvalMode][0]; mde = AIR_AFFINE(-1, mde, 1, 0, 1); pvl->directAnswer[tenGageFARidgeLineAlignment][0] = mde*dot*dot; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFARidgeSurfaceAlignment)) { double *hev2, *dev0, dot, mde; hev2 = pvl->directAnswer[tenGageFAHessianEvec2]; dev0 = pvl->directAnswer[tenGageEvec0]; dot = ELL_3V_DOT(hev2, dev0); mde = pvl->directAnswer[tenGageFAHessianEvalMode][0]; mde = AIR_AFFINE(-1, mde, 1, 1, 0); pvl->directAnswer[tenGageFARidgeSurfaceAlignment][0]= mde*(1-dot*dot); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFA2ndDD)) { double *hess, *norm, tmpv[3]; hess = pvl->directAnswer[tenGageFAHessian]; norm = pvl->directAnswer[tenGageFANormal]; ELL_3MV_MUL(tmpv, hess, norm); pvl->directAnswer[tenGageFA2ndDD][0] = ELL_3V_DOT(norm, tmpv); } /* HEY: lots of this is copy/paste from gage/sclanswer.c */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAGeomTens)) { double denom, *fahess, *fagmag, tmpMat[9], *fnorm, nPerp[9], sHess[9]; fahess = pvl->directAnswer[tenGageFAHessian]; fagmag = pvl->directAnswer[tenGageFAGradMag]; fnorm = pvl->directAnswer[tenGageFANormal]; denom = (*fagmag) ? 1.0/(*fagmag) : 0.0; ELL_3M_SCALE(sHess, denom, fahess); ELL_3M_IDENTITY_SET(nPerp); ELL_3MV_SCALE_OUTER_INCR(nPerp, -1, fnorm, fnorm); /* gten = nPerp * sHess * nPerp */ ELL_3M_MUL(tmpMat, sHess, nPerp); ELL_3M_MUL(pvl->directAnswer[tenGageFAGeomTens], nPerp, tmpMat); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFATotalCurv)) { pvl->directAnswer[tenGageFATotalCurv][0] = ELL_3M_FROB(pvl->directAnswer[tenGageFAGeomTens]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAKappa1) || GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAKappa2)) { double *k1, *k2, T, N, D; k1 = pvl->directAnswer[tenGageFAKappa1]; k2 = pvl->directAnswer[tenGageFAKappa2]; T = ELL_3M_TRACE(pvl->directAnswer[tenGageFAGeomTens]); N = pvl->directAnswer[tenGageFATotalCurv][0]; D = 2*N*N - T*T; D = AIR_MAX(D, 0); D = sqrt(D); k1[0] = 0.5*(T + D); k2[0] = 0.5*(T - D); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAMeanCurv)) { double k1, k2; k1 = pvl->directAnswer[tenGageFAKappa1][0]; k2 = pvl->directAnswer[tenGageFAKappa2][0]; pvl->directAnswer[tenGageFAMeanCurv][0] = (k1 + k2)/2; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAShapeIndex)) { double k1, k2; k1 = pvl->directAnswer[tenGageFAKappa1][0]; k2 = pvl->directAnswer[tenGageFAKappa2][0]; pvl->directAnswer[tenGageFAShapeIndex][0] = -(2/AIR_PI)*atan2(k1 + k2, k1 - k2); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAGaussCurv)) { double k1, k2; k1 = pvl->directAnswer[tenGageFAKappa1][0]; k2 = pvl->directAnswer[tenGageFAKappa2][0]; pvl->directAnswer[tenGageFAGaussCurv][0] = k1*k2; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFACurvDir1)) { double kk, tmpMat[9], tmpVec[3]; kk = pvl->directAnswer[tenGageFAKappa2][0]; ELL_3M_COPY(tmpMat, pvl->directAnswer[tenGageFAGeomTens]); tmpMat[0] -= kk; tmpMat[4] -= kk; tmpMat[8] -= kk; ell_3m_1d_nullspace_d(tmpVec, tmpMat); ELL_3V_COPY(pvl->directAnswer[tenGageFACurvDir1], tmpVec); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFACurvDir2)) { double kk, tmpMat[9], tmpVec[3]; kk = pvl->directAnswer[tenGageFAKappa1][0]; ELL_3M_COPY(tmpMat, pvl->directAnswer[tenGageFAGeomTens]); tmpMat[0] -= kk; tmpMat[4] -= kk; tmpMat[8] -= kk; ell_3m_1d_nullspace_d(tmpVec, tmpMat); ELL_3V_COPY(pvl->directAnswer[tenGageFACurvDir1], tmpVec); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAFlowlineCurv)) { double *fahess, *fnorm, *fagmag, denom, nPerp[9], nProj[9], ncTen[9], sHess[9], tmpMat[9]; fnorm = pvl->directAnswer[tenGageFANormal]; fahess = pvl->directAnswer[tenGageFAHessian]; fagmag = pvl->directAnswer[tenGageFAGradMag]; ELL_3MV_OUTER(nProj, fnorm, fnorm); ELL_3M_IDENTITY_SET(nPerp); ELL_3M_SCALE_INCR(nPerp, -1, nProj); denom = (*fagmag) ? 1.0/(*fagmag) : 0.0; ELL_3M_SCALE(sHess, denom, fahess); /* ncTen = nPerp * sHess * nProj */ ELL_3M_MUL(tmpMat, sHess, nProj); ELL_3M_MUL(ncTen, nPerp, tmpMat); pvl->directAnswer[gageSclFlowlineCurv][0] = ELL_3M_FROB(ncTen); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageRHessian)) { hessCbR = matTmp = pvl->directAnswer[tenGageRHessian]; ELL_3M_ZERO_SET(matTmp); ELL_3M_SCALE_INCR(matTmp, 5*cbB - 2*cbS, hessCbA); ELL_3MV_SCALE_OUTER_INCR(matTmp, 5, gradCbB, gradCbA); ELL_3MV_SCALE_OUTER_INCR(matTmp, -2, gradCbS, gradCbA); ELL_3M_SCALE_INCR(matTmp, 5*cbA, hessCbB); ELL_3MV_SCALE_OUTER_INCR(matTmp, 5, gradCbA, gradCbB); ELL_3M_SCALE_INCR(matTmp, -27, hessCbC); ELL_3M_SCALE_INCR(matTmp, -2*cbA, hessCbS); ELL_3MV_SCALE_OUTER_INCR(matTmp, -2, gradCbA, gradCbS); ELL_3M_SCALE(matTmp, 1.0/54, matTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageModeHessian)) { double tmpQ, oQ, rQ; tmpQ = AIR_MAX(0, cbQ); rQ = sqrt(tmpQ); oQ = tmpQ ? 1/tmpQ : 0; matTmp = pvl->directAnswer[tenGageModeHessian]; ELL_3M_ZERO_SET(matTmp); ELL_3M_SCALE_INCR(matTmp, -(3.0/2)*cbR, hessCbQ); ELL_3MV_SCALE_OUTER_INCR(matTmp, (15.0/4)*cbR*oQ, gradCbQ, gradCbQ); ELL_3MV_SCALE_OUTER_INCR(matTmp, -(3.0/2), gradCbR, gradCbQ); ELL_3M_SCALE_INCR(matTmp, cbQ, hessCbR); ELL_3MV_SCALE_OUTER_INCR(matTmp, -(3.0/2), gradCbQ, gradCbR); tmp0 = (tmpQ && rQ) ? 1/(tmpQ*tmpQ*rQ) : 0.0; ELL_3M_SCALE(matTmp, tmp0, matTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageModeHessianEvec)) { ell_3m_eigensolve_d(pvl->directAnswer[tenGageModeHessianEval], pvl->directAnswer[tenGageModeHessianEvec], pvl->directAnswer[tenGageModeHessian], AIR_TRUE); } else if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageModeHessianEval)) { ell_3m_eigenvalues_d(pvl->directAnswer[tenGageModeHessianEval], pvl->directAnswer[tenGageModeHessian], AIR_TRUE); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageModeHessianFrob)) { pvl->directAnswer[tenGageModeHessianFrob][0] = ELL_3M_FROB(pvl->directAnswer[tenGageModeHessian]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaHessian)) { double fa, mode, *modeGrad, *faGrad, *modeHess, *faHess; fa = pvl->directAnswer[tenGageFA][0]; mode = pvl->directAnswer[tenGageMode][0]; faGrad = pvl->directAnswer[tenGageFAGradVec]; modeGrad = pvl->directAnswer[tenGageModeGradVec]; faHess = pvl->directAnswer[tenGageFAHessian]; modeHess = pvl->directAnswer[tenGageModeHessian]; matTmp = pvl->directAnswer[tenGageOmegaHessian]; ELL_3M_ZERO_SET(matTmp); ELL_3M_SCALE_INCR(matTmp, (1+mode)/2, faHess); ELL_3M_SCALE_INCR(matTmp, fa/2, modeHess); ELL_3MV_SCALE_OUTER_INCR(matTmp, 0.5, modeGrad, faGrad); ELL_3MV_SCALE_OUTER_INCR(matTmp, 0.5, faGrad, modeGrad); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaHessianEvec)) { ell_3m_eigensolve_d(pvl->directAnswer[tenGageOmegaHessianEval], pvl->directAnswer[tenGageOmegaHessianEvec], pvl->directAnswer[tenGageOmegaHessian], AIR_TRUE); } else if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaHessianEval)) { ell_3m_eigenvalues_d(pvl->directAnswer[tenGageOmegaHessianEval], pvl->directAnswer[tenGageOmegaHessian], AIR_TRUE); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaLaplacian)) { double *hess; hess = pvl->directAnswer[tenGageOmegaHessian]; pvl->directAnswer[tenGageOmegaLaplacian][0] = hess[0] + hess[4] + hess[8]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmega2ndDD)) { double *hess, *norm, tmpv[3]; hess = pvl->directAnswer[tenGageOmegaHessian]; norm = pvl->directAnswer[tenGageOmegaNormal]; ELL_3MV_MUL(tmpv, hess, norm); pvl->directAnswer[tenGageOmega2ndDD][0] = ELL_3V_DOT(norm, tmpv); } /* the copy-and-paste nature of this is really getting out of control ... */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaHessianContrTenEvec0)) { double *hess, *evec, tmpv[3]; hess = pvl->directAnswer[tenGageOmegaHessian]; evec = pvl->directAnswer[tenGageEvec0]; ELL_3MV_MUL(tmpv, hess, evec); pvl->directAnswer[tenGageOmegaHessianContrTenEvec0][0] = ELL_3V_DOT(evec, tmpv); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaHessianContrTenEvec1)) { double *hess, *evec, tmpv[3]; hess = pvl->directAnswer[tenGageOmegaHessian]; evec = pvl->directAnswer[tenGageEvec1]; ELL_3MV_MUL(tmpv, hess, evec); pvl->directAnswer[tenGageOmegaHessianContrTenEvec1][0] = ELL_3V_DOT(evec, tmpv); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaHessianContrTenEvec2)) { double *hess, *evec, tmpv[3]; hess = pvl->directAnswer[tenGageOmegaHessian]; evec = pvl->directAnswer[tenGageEvec2]; ELL_3MV_MUL(tmpv, hess, evec); pvl->directAnswer[tenGageOmegaHessianContrTenEvec2][0] = ELL_3V_DOT(evec, tmpv); } /* --- evec0 dot products */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTraceGradVecDotEvec0)) { tmp0 = ELL_3V_DOT(evecAns + 0*3, pvl->directAnswer[tenGageTraceGradVec]); pvl->directAnswer[tenGageTraceGradVecDotEvec0][0] = AIR_ABS(tmp0); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTraceDiffusionAlign)) { tmp0 = ELL_3V_DOT(evecAns + 0*3, pvl->directAnswer[tenGageTraceNormal]); tmp0 = 1 - 2*acos(AIR_ABS(tmp0))/AIR_PI; pvl->directAnswer[tenGageTraceDiffusionAlign][0] = tmp0; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTraceDiffusionFraction)) { double tmpv[3]; TEN_TV_MUL(tmpv, tenAns, pvl->directAnswer[tenGageTraceNormal]); tmp0 = ELL_3V_DOT(tmpv, pvl->directAnswer[tenGageTraceNormal]); tmp0 /= TEN_T_TRACE(tenAns) ? TEN_T_TRACE(tenAns) : 1; pvl->directAnswer[tenGageTraceDiffusionFraction][0] = tmp0; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFAGradVecDotEvec0)) { tmp0 = ELL_3V_DOT(evecAns + 0*3, pvl->directAnswer[tenGageFAGradVec]); pvl->directAnswer[tenGageFAGradVecDotEvec0][0] = AIR_ABS(tmp0); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFADiffusionAlign)) { tmp0 = ELL_3V_DOT(evecAns + 0*3, pvl->directAnswer[tenGageFANormal]); tmp0 = 1 - 2*acos(AIR_ABS(tmp0))/AIR_PI; pvl->directAnswer[tenGageFADiffusionAlign][0] = tmp0; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFADiffusionFraction)) { double tmpv[3]; TEN_TV_MUL(tmpv, tenAns, pvl->directAnswer[tenGageFANormal]); tmp0 = ELL_3V_DOT(tmpv, pvl->directAnswer[tenGageFANormal]); tmp0 /= TEN_T_TRACE(tenAns) ? TEN_T_TRACE(tenAns) : 1; pvl->directAnswer[tenGageFADiffusionFraction][0] = tmp0; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaGradVecDotEvec0)) { tmp0 = ELL_3V_DOT(evecAns + 0*3, pvl->directAnswer[tenGageOmegaGradVec]); pvl->directAnswer[tenGageOmegaGradVecDotEvec0][0] = AIR_ABS(tmp0); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaDiffusionAlign)) { tmp0 = ELL_3V_DOT(evecAns + 0*3, pvl->directAnswer[tenGageOmegaNormal]); tmp0 = 1 - 2*acos(AIR_ABS(tmp0))/AIR_PI; pvl->directAnswer[tenGageOmegaDiffusionAlign][0] = tmp0; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageOmegaDiffusionFraction)) { double tmpv[3]; TEN_TV_MUL(tmpv, tenAns, pvl->directAnswer[tenGageOmegaNormal]); tmp0 = ELL_3V_DOT(tmpv, pvl->directAnswer[tenGageOmegaNormal]); tmp0 /= TEN_T_TRACE(tenAns) ? TEN_T_TRACE(tenAns) : 1; pvl->directAnswer[tenGageOmegaDiffusionFraction][0] = tmp0; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageConfGradVecDotEvec0)) { double *confGrad; confGrad = pvl->directAnswer[tenGageTensorGrad]; tmp0 = ELL_3V_DOT(evecAns + 0*3, confGrad); pvl->directAnswer[tenGageConfGradVecDotEvec0][0] = AIR_ABS(tmp0); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageConfDiffusionAlign)) { double *confGrad, confNorm[3], tmp; confGrad = pvl->directAnswer[tenGageTensorGrad]; ELL_3V_NORM(confNorm, confGrad, tmp); tmp0 = ELL_3V_DOT(evecAns + 0*3, confNorm); tmp0 = 1 - 2*acos(AIR_ABS(tmp0))/AIR_PI; pvl->directAnswer[tenGageConfDiffusionAlign][0] = tmp0; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageConfDiffusionFraction)) { double *confGrad, confNorm[3], tmp, tmpv[3]; confGrad = pvl->directAnswer[tenGageTensorGrad]; ELL_3V_NORM(confNorm, confGrad, tmp); TEN_TV_MUL(tmpv, tenAns, confNorm); tmp0 = ELL_3V_DOT(tmpv, confNorm); tmp0 /= TEN_T_TRACE(tenAns) ? TEN_T_TRACE(tenAns) : 1; pvl->directAnswer[tenGageConfDiffusionFraction][0] = tmp0; } /* --- Covariance --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCovariance)) { unsigned int cc, tt, taa, tbb, vijk, vii, vjj, vkk, fd, fddd; double *cov, ww, wxx, wyy, wzz, ten[7]; cov = pvl->directAnswer[tenGageCovariance]; /* HEY: casting because radius signed (shouldn't be) */ fd = AIR_CAST(unsigned int, 2*ctx->radius); fddd = fd*fd*fd; /* reset answer */ for (cc=0; cc<21; cc++) { cov[cc] = 0; } ten[0] = 1; /* never used anyway */ for (vijk=0; vijkfw[vii + fd*(0 + 3*gageKernel00)]; wyy = ctx->fw[vjj + fd*(1 + 3*gageKernel00)]; wzz = ctx->fw[vkk + fd*(2 + 3*gageKernel00)]; ww = wxx*wyy*wzz; for (tt=1; tt<7; tt++) { ten[tt] = ww*(pvl->iv3[vijk + fddd*tt] - tenAns[tt]); } cc = 0; for (taa=0; taa<6; taa++) { for (tbb=taa; tbb<6; tbb++) { /* HEY: do I really mean to have this factor in here? */ /* it probably meant that the units in the IGRT TMI paper were wrong by this factor ... */ cov[cc] += 100000*ten[taa+1]*ten[tbb+1]; cc++; } } } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCovarianceRGRT)) { double *cov, *covr, *igrt[6]; unsigned int taa, tbb, cc; cov = pvl->directAnswer[tenGageCovariance]; covr = pvl->directAnswer[tenGageCovarianceRGRT]; igrt[0] = pvl->directAnswer[tenGageDelNormR1]; igrt[1] = pvl->directAnswer[tenGageDelNormR2]; igrt[2] = pvl->directAnswer[tenGageDelNormK3]; igrt[3] = pvl->directAnswer[tenGageDelNormPhi1]; igrt[4] = pvl->directAnswer[tenGageDelNormPhi2]; igrt[5] = pvl->directAnswer[tenGageDelNormPhi3]; cc = 0; for (taa=0; taa<6; taa++) { for (tbb=taa; tbb<6; tbb++) { covr[cc] = tenDoubleContract_d(igrt[tbb], cov, igrt[taa]); cc++; } } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCovarianceKGRT)) { double *cov, *covk, *igrt[6], delnormk1[7]; unsigned int taa, tbb, cc; cov = pvl->directAnswer[tenGageCovariance]; covk = pvl->directAnswer[tenGageCovarianceKGRT]; TEN_T_SET(delnormk1, 1, 0.57735026, 0, 0, 0.57735026, 0, 0.57735026); igrt[0] = delnormk1; igrt[1] = pvl->directAnswer[tenGageDelNormK2]; igrt[2] = pvl->directAnswer[tenGageDelNormK3]; igrt[3] = pvl->directAnswer[tenGageDelNormPhi1]; igrt[4] = pvl->directAnswer[tenGageDelNormPhi2]; igrt[5] = pvl->directAnswer[tenGageDelNormPhi3]; cc = 0; for (taa=0; taa<6; taa++) { for (tbb=taa; tbb<6; tbb++) { covk[cc] = tenDoubleContract_d(igrt[tbb], cov, igrt[taa]); cc++; } } } /* these are items that somewhat bypass the convolution result (in tenGageTensor) because it has to do something else fancy with the constituent tensors. This is young and hacky code; and it may be that facilitating this kind of processing should be better supported by the gage API ... */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorLogEuclidean) || GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorQuatGeoLoxK) || GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorQuatGeoLoxR) || GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorRThetaPhiLinear)) { unsigned int vijk, vii, vjj, vkk, fd, fddd; _tenGagePvlData *pvlData; double *ans; int qret; pvlData = AIR_CAST(_tenGagePvlData *, pvl->data); /* HEY: casting because radius is signed (shouldn't be) */ fd = AIR_CAST(unsigned int, 2*ctx->radius); fddd = fd*fd*fd; for (vijk=0; vijkfw[vii + fd*(0 + 3*gageKernel00)]; wyy = ctx->fw[vjj + fd*(1 + 3*gageKernel00)]; wzz = ctx->fw[vkk + fd*(2 + 3*gageKernel00)]; pvlData->buffWght[vijk] = wxx*wyy*wzz; for (tt=0; tt<7; tt++) { pvlData->buffTen[tt + 7*vijk] = pvl->iv3[vijk + fddd*tt]; } } qret = 0; if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorLogEuclidean)) { ans = pvl->directAnswer[tenGageTensorLogEuclidean]; qret = tenInterpN_d(ans, pvlData->buffTen, pvlData->buffWght, fddd, tenInterpTypeLogLinear, pvlData->tip); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorQuatGeoLoxK)) { ans = pvl->directAnswer[tenGageTensorQuatGeoLoxK]; qret = tenInterpN_d(ans, pvlData->buffTen, pvlData->buffWght, fddd, tenInterpTypeQuatGeoLoxK, pvlData->tip); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorQuatGeoLoxR)) { ans = pvl->directAnswer[tenGageTensorQuatGeoLoxR]; qret= tenInterpN_d(ans, pvlData->buffTen, pvlData->buffWght, fddd, tenInterpTypeQuatGeoLoxR, pvlData->tip); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorRThetaPhiLinear)) { ans = pvl->directAnswer[tenGageTensorRThetaPhiLinear]; qret= tenInterpN_d(ans, pvlData->buffTen, pvlData->buffWght, fddd, tenInterpTypeRThetaPhiLinear, pvlData->tip); } if (qret) { char *lerr; fprintf(stderr, "!%s: problem!!!\n %s", me, lerr = biffGet(TEN)); free(lerr); } } /* --- cl/cp/ca gradients --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCl1GradVec)) { vecTmp = pvl->directAnswer[tenGageCl1GradVec]; ELL_3V_SET(vecTmp, (evalAns[0]*(-2*pvl->directAnswer[tenGageEvalGrads][3]-pvl->directAnswer[tenGageEvalGrads][6]) +evalAns[1]*(2*pvl->directAnswer[tenGageEvalGrads][0]+pvl->directAnswer[tenGageEvalGrads][6]) +evalAns[2]*(pvl->directAnswer[tenGageEvalGrads][0]-pvl->directAnswer[tenGageEvalGrads][3])) /(pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0]), (evalAns[0]*(-2*pvl->directAnswer[tenGageEvalGrads][4]-pvl->directAnswer[tenGageEvalGrads][7]) +evalAns[1]*(2*pvl->directAnswer[tenGageEvalGrads][1]+pvl->directAnswer[tenGageEvalGrads][7]) +evalAns[2]*(pvl->directAnswer[tenGageEvalGrads][1]-pvl->directAnswer[tenGageEvalGrads][4])) /(pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0]), (evalAns[0]*(-2*pvl->directAnswer[tenGageEvalGrads][5]-pvl->directAnswer[tenGageEvalGrads][8]) +evalAns[1]*(2*pvl->directAnswer[tenGageEvalGrads][2]+pvl->directAnswer[tenGageEvalGrads][8]) +evalAns[2]*(pvl->directAnswer[tenGageEvalGrads][2]-pvl->directAnswer[tenGageEvalGrads][5])) /(pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0])); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCl1GradMag)) { magTmp = pvl->directAnswer[tenGageCl1GradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCl1Normal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageCl1Normal], magTmp ? 1/magTmp : 0, vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCp1GradVec)) { vecTmp = pvl->directAnswer[tenGageCp1GradVec]; ELL_3V_SET(vecTmp, 2*(evalAns[0]*(pvl->directAnswer[tenGageEvalGrads][3]-pvl->directAnswer[tenGageEvalGrads][6]) +evalAns[1]*(-pvl->directAnswer[tenGageEvalGrads][0]-2*pvl->directAnswer[tenGageEvalGrads][6]) +evalAns[2]*(pvl->directAnswer[tenGageEvalGrads][0]+2*pvl->directAnswer[tenGageEvalGrads][3])) /(pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0]), 2*(evalAns[0]*(pvl->directAnswer[tenGageEvalGrads][4]-pvl->directAnswer[tenGageEvalGrads][7]) +evalAns[1]*(-pvl->directAnswer[tenGageEvalGrads][1]-2*pvl->directAnswer[tenGageEvalGrads][7]) +evalAns[2]*(pvl->directAnswer[tenGageEvalGrads][1]+2*pvl->directAnswer[tenGageEvalGrads][4])) /(pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0]), 2*(evalAns[0]*(pvl->directAnswer[tenGageEvalGrads][5]-pvl->directAnswer[tenGageEvalGrads][8]) +evalAns[1]*(-pvl->directAnswer[tenGageEvalGrads][2]-2*pvl->directAnswer[tenGageEvalGrads][8]) +evalAns[2]*(pvl->directAnswer[tenGageEvalGrads][2]+2*pvl->directAnswer[tenGageEvalGrads][5])) /(pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0])); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCp1GradMag)) { magTmp = pvl->directAnswer[tenGageCp1GradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCp1Normal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageCp1Normal], magTmp ? 1/magTmp : 0, vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCa1GradVec)) { vecTmp = pvl->directAnswer[tenGageCa1GradVec]; ELL_3V_SET(vecTmp, -3*((evalAns[0]+evalAns[1])*pvl->directAnswer[tenGageEvalGrads][6] -evalAns[2]*(pvl->directAnswer[tenGageEvalGrads][0]+pvl->directAnswer[tenGageEvalGrads][3])) /(pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0]), -3*((evalAns[0]+evalAns[1])*pvl->directAnswer[tenGageEvalGrads][7] -evalAns[2]*(pvl->directAnswer[tenGageEvalGrads][1]+pvl->directAnswer[tenGageEvalGrads][4])) /(pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0]), -3*((evalAns[0]+evalAns[1])*pvl->directAnswer[tenGageEvalGrads][8] -evalAns[2]*(pvl->directAnswer[tenGageEvalGrads][2]+pvl->directAnswer[tenGageEvalGrads][5])) /(pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0])); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCa1GradMag)) { magTmp = pvl->directAnswer[tenGageCa1GradMag][0] = ELL_3V_LEN(vecTmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCa1Normal)) { ELL_3V_SCALE(pvl->directAnswer[tenGageCa1Normal], magTmp ? 1/magTmp : 0, vecTmp); } /* --- tensor gradient, rotated into eigenframe of the tensor itself --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageTensorGradRotE)) { /* confidence not affected by rotation */ double evecsT[9], evecs[9], tmp[9], tmp2[9]; double diff0, diff1, diff2, diffthresh; double rdiff0, rdiff1, rdiff2; unsigned int evi, tci; ELL_3V_COPY(pvl->directAnswer[tenGageTensorGradRotE], pvl->directAnswer[tenGageTensorGrad]); /* pre-compute relative eval diffs to detect ill-conditioned case */ diff0=(evalAns[0]-evalAns[1])/(fabs(evalAns[0])+fabs(evalAns[1])); diff1=(evalAns[1]-evalAns[2])/(fabs(evalAns[1])+fabs(evalAns[2])); diff2=(evalAns[0]-evalAns[2])/(fabs(evalAns[0])+fabs(evalAns[2])); diffthresh=0.05; if (diff2>diffthresh) rdiff2=1.0; else rdiff2=diff2/diffthresh; if (diff1>diffthresh) rdiff1=1.0; else rdiff1=diff1/diffthresh; if (diff0>diffthresh) rdiff0=1.0; else rdiff0=diff0/diffthresh; ELL_3M_COPY(evecs,evecAns); ELL_3M_TRANSPOSE(evecsT,evecs); for (evi=0; evi<3; evi++) { char sign; /* first, simply rotate derivatives into eigenframe of value */ TEN_T2M(tmp,gradDdXYZ + evi*7); ell_3m_mul_d(tmp2,tmp,evecsT); ell_3m_mul_d(tmp,evecs,tmp2); /* If necessary, perform a number of additional rotations to * distribute eigenvalue derivatives equally in ill-defined * cases. Explanation in Schultz and Seidel, "Using Eigenvalue * Derivatives for Edge Detection in DT-MRI Data", DAGM 2008*/ if (rdiff0<1.0) { /* the goal is to find the smallest angle phi such that * rotation by phi around z will result in tmp[0]=tmp[4], i.e.: * * cos(phi)^2*tmp[0]-2*sin(phi)*cos(phi)*tmp[1]+sin(phi)^2*tmp[4] = * sin(phi)^2*tmp[0]+2*sin(phi)*cos(phi)*tmp[1]+cos(phi)^2*tmp[4] * => * tan(2*phi)=(tmp[0]-tmp[4])/(2*tmp[1]) * * we use atan2 to avoid potential problems with tmp[1]==0, * but manipulate the signs of the arguments s.t. the result * is always in [-pi/2,pi/2] (i.e., the smallest solution of * the above equality) */ /* rotate around z axis */ double phi, R[9], RT[9]; sign = (tmp[0]-tmp[4])*tmp[1]>0?1:-1; phi=0.5*atan2(sign*fabs(tmp[0]-tmp[4]),fabs(2*tmp[1])); ELL_3M_ROTATE_Z_SET(R, (1.0-rdiff0)*phi); ELL_3M_TRANSPOSE(RT,R); ell_3m_mul_d(tmp2,tmp,RT); ell_3m_mul_d(tmp,R,tmp2); } if (rdiff1<1.0) { /* rotate around x axis */ double phi, R[9], RT[9]; sign = (tmp[4]-tmp[8])*tmp[5]>0?1:-1; phi=0.5*atan2(sign*fabs(tmp[4]-tmp[8]),fabs(2*tmp[5])); ELL_3M_ROTATE_X_SET(R, (1.0-rdiff1)*phi); ELL_3M_TRANSPOSE(RT,R); ell_3m_mul_d(tmp2,tmp,RT); ell_3m_mul_d(tmp,R,tmp2); } if (rdiff2<1.0) { double mean, submatrix[3], isoPhi, gamma, beta, A, C, R[9],RT[9]; int axis, midaxis, smallest; mean=(tmp[0]+tmp[4]+tmp[8])/3.0; /* what's the median? */ midaxis=0; if ((tmp[0]>tmp[4] && tmp[4]>tmp[8])|| (tmp[0]tmp[8] && tmp[8]>tmp[0])|| (tmp[4]tmp[4*midaxis]) { smallest=1; } sign=1; if ((smallest && (tmp[0]tmp[4] && tmp[0]>tmp[8]))) { axis=0; submatrix[0]=tmp[4]; submatrix[1]=tmp[5]; submatrix[2]=tmp[8]; if (midaxis!=1) sign=-1; } else if ((smallest && (tmp[4]tmp[0] && tmp[4]>tmp[8]))) { axis=1; submatrix[0]=tmp[8]; submatrix[1]=tmp[2]; submatrix[2]=tmp[0]; if (midaxis!=2) sign=-1; } else { axis=2; submatrix[0]=tmp[0]; submatrix[1]=tmp[1]; submatrix[2]=tmp[4]; if (midaxis!=0) sign=-1; } isoPhi=0.0f; gamma=sign*(submatrix[0]-submatrix[2]); beta=-sign*2*submatrix[1]; A=sqrt(gamma*gamma+beta*beta); C=atan2(gamma,beta); isoPhi=0.5*(asin(2.0/A*(mean-0.5*(submatrix[0]+submatrix[2])))-C); /* make sure we use the minimal rotation */ isoPhi=asin(2.0/A*(mean-0.5*(submatrix[0]+submatrix[2]))); if (isoPhi>0) { if (fabs(AIR_PI-isoPhi-C)0?1:-1; ELL_3M_ROTATE_X_SET(R, (1.0-rdiff2)*0.5*atan2(sign*fabs(tmp[0]-tmp[4]),fabs(2*tmp[1]))); break; case 1: sign = (tmp[8]-tmp[0])*tmp[2]>0?1:-1; ELL_3M_ROTATE_Y_SET(R, (1.0-rdiff2)*0.5*atan2(sign*fabs(tmp[8]-tmp[0]),fabs(2*tmp[2]))); break; case 2: sign = (tmp[4]-tmp[8])*tmp[5]>0?1:-1; ELL_3M_ROTATE_Z_SET(R, (1.0-rdiff2)*0.5*atan2(sign*fabs(tmp[4]-tmp[8]),fabs(2*tmp[5]))); break; } ELL_3M_TRANSPOSE(RT,R); ell_3m_mul_d(tmp2,tmp,RT); ell_3m_mul_d(tmp,R,tmp2); } /* Now, we can set the answer */ TEN_M2T(tmp2,tmp); for (tci=1; tci<7; tci++) { pvl->directAnswer[tenGageTensorGradRotE][3*tci+evi] = tmp2[tci]; } } } /* --- Eigenvalue Hessians: rotate partial second derivatives into * eigenframe and take into account correction factor based on * eigenvector derivatives --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageEvalHessian)) { /* some aliases for convenience: */ double *thess = pvl->directAnswer[tenGageHessian]; double *evhess = pvl->directAnswer[tenGageEvalHessian]; double *tgradE = pvl->directAnswer[tenGageTensorGradRotE]; int dira, dirb; /* directions of first/second partial derivative */ int k; /* number of the eigenvalue */ double evecsT[9], tmp[9]; /* HEY: some copy-paste from tenGageEvalGrads */ double eps=0.05; /* treshold for handling degeneracies */ double rel1, rel2, rel3, w1, w2; /* interpolation weights from relative eigenvalue distance */ rel1=evalAns[0]*evalAns[0]+evalAns[1]*evalAns[1]; if (rel1>1e-10) rel1=(evalAns[0]-evalAns[1])*(evalAns[0]-evalAns[1])/rel1; rel2=evalAns[0]*evalAns[0]+evalAns[2]*evalAns[2]; if (rel2>1e-10) rel2=(evalAns[0]-evalAns[2])*(evalAns[0]-evalAns[2])/rel2; rel3=evalAns[1]*evalAns[1]+evalAns[2]*evalAns[2]; if (rel3>1e-10) rel3=(evalAns[1]-evalAns[2])*(evalAns[1]-evalAns[2])/rel3; w1=rel1/eps-1; w1*=w1; w2=rel2/eps-1; w2*=w2; ELL_3M_TRANSPOSE(evecsT,evecAns); for (dira=0; dira<3; dira++) { for (dirb=0; dirb<=dira; dirb++) { /* exploit symmetry of Hessian */ double rdiff1,rdiff2; double l1res, l2res, l3res; /* collect second partial derivatives in dira,dirb */ double H[9]; ELL_3V_SET(H, thess[9+3*dirb+dira], thess[18+3*dirb+dira], thess[27+3*dirb+dira]); ELL_3V_SET(H+3, thess[18+3*dirb+dira], thess[36+3*dirb+dira], thess[45+3*dirb+dira]); ELL_3V_SET(H+6, thess[27+3*dirb+dira], thess[45+3*dirb+dira], thess[54+3*dirb+dira]); /* rotate into eigenframe of value */ ell_3m_mul_d(tmp,H,evecsT); ell_3m_mul_d(H,evecAns,tmp); /* we have to divide by rdiff=lambda_1-lambda_2; the following * is a heuristic to avoid numerical problems in case rdiff is * near zero */ if (rel1>eps) rdiff1=1.0/(evalAns[0]-evalAns[1]); else if (rel1>1e-10) rdiff1=(rel1/eps)/(evalAns[0]-evalAns[1]); else rdiff1=0; if (rel2>eps) rdiff2=1.0/(evalAns[0]-evalAns[2]); else if (rel2>1e-10) rdiff2=(rel2/eps)/(evalAns[0]-evalAns[2]); else rdiff2=0; l1res=H[0]+2*(tgradE[6+dira]*tgradE[6+dirb]*rdiff1+ tgradE[9+dira]*tgradE[9+dirb]*rdiff2); if (rel1>eps) rdiff1=1.0/(evalAns[1]-evalAns[0]); else if (rel1>1e-10) rdiff1=(rel1/eps)/(evalAns[1]-evalAns[0]); else rdiff1=0; if (rel3>eps) rdiff2=1.0/(evalAns[1]-evalAns[2]); else if (rel3>1e-10) rdiff2=(rel3/eps)/(evalAns[1]-evalAns[2]); else rdiff2=0; l2res=H[4]+2*(tgradE[6+dira]*tgradE[6+dirb]*rdiff1+ tgradE[15+dira]*tgradE[15+dirb]*rdiff2); if (rel2>eps) rdiff1=1.0/(evalAns[2]-evalAns[0]); else if (rel2>1e-10) rdiff1=(rel2/eps)/(evalAns[2]-evalAns[0]); else rdiff1=0; if (rel3>eps) rdiff2=1.0/(evalAns[2]-evalAns[1]); else if (rel3>1e-10) rdiff2=(rel3/eps)/(evalAns[2]-evalAns[1]); else rdiff2=0; l3res=H[8]+2*(tgradE[9+dira]*tgradE[9+dirb]*rdiff1+ tgradE[15+dira]*tgradE[15+dirb]*rdiff2); if (rel1>eps) evhess[3*dirb+dira]=l1res; else { if (rel2>eps) evhess[3*dirb+dira]=(1-0.5*w1)*l1res+0.5*w1*l2res; else evhess[3*dirb+dira]=(1-0.5*w1-w1*w2/6.0)*l1res+ (0.5*w1-w1*w2/6.0)*l2res+ w1*w2/3.0*l3res; } if (rel2>eps) evhess[18+3*dirb+dira]=l3res; else { if (rel1>eps) evhess[18+3*dirb+dira]=(1-0.5*w2)*l3res+0.5*w2*l2res; else evhess[18+3*dirb+dira]=(1-0.5*w2-w1*w2/6.0)*l3res+ (0.5*w2-w1*w2/6.0)*l2res+ w1*w2/3.0*l1res; } evhess[9+3*dirb+dira]=l1res+l2res+l3res - evhess[3*dirb+dira] - evhess[18+3*dirb+dira]; } } for (dira=0; dira<2; dira++) { for (dirb=dira+1; dirb<3; dirb++) { /* copy over symmetric values */ for (k=0; k<3; k++) { evhess[9*k+3*dirb+dira]=evhess[9*k+3*dira+dirb]; } } } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCl1Hessian)) { int dira, dirb; double *cl1hess = pvl->directAnswer[tenGageCl1Hessian]; double *tgradE = pvl->directAnswer[tenGageTensorGradRotE]; double *evhess = pvl->directAnswer[tenGageEvalHessian]; /* A and B come out of the quotient rule; cf. appendix of Schultz * et al., TVCG 2010 for more details */ double B = (evalAns[0]+evalAns[1]+evalAns[2])* (evalAns[0]+evalAns[1]+evalAns[2]); for (dira=0; dira<3; dira++) { for (dirb=0; dirb<=dira; dirb++) { /* again, exploit Hessian symmetry */ double A = evalAns[0]*(-2*tgradE[12+dira]-tgradE[18+dira])+ evalAns[1]*(2*tgradE[3+dira]+tgradE[18+dira])+ evalAns[2]*(tgradE[3+dira]-tgradE[12+dira]); double Ad = tgradE[3+dirb]*(-2*tgradE[12+dira]-tgradE[18+dira])+ evalAns[0]*(-2*evhess[9+3*dirb+dira]-evhess[18+3*dirb+dira])+ tgradE[12+dirb]*(2*tgradE[3+dira]+tgradE[18+dira])+ evalAns[1]*(2*evhess[3*dirb+dira]+evhess[18+3*dirb+dira])+ tgradE[18+dirb]*(tgradE[3+dira]-tgradE[12+dira])+ evalAns[2]*(evhess[3*dirb+dira]-evhess[9+3*dirb+dira]); double Bd = 2*(evalAns[0]+evalAns[1]+evalAns[2])* (tgradE[3+dirb]+tgradE[12+dirb]+tgradE[18+dirb]); cl1hess[3*dirb+dira]=Ad/B-A/B*Bd/B; } } for (dira=0; dira<2; dira++) { for (dirb=dira+1; dirb<3; dirb++) { /* copy over symmetric values */ cl1hess[3*dirb+dira]=cl1hess[3*dira+dirb]; } } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCl1HessianEvec)) { ell_3m_eigensolve_d(pvl->directAnswer[tenGageCl1HessianEval], pvl->directAnswer[tenGageCl1HessianEvec], pvl->directAnswer[tenGageCl1Hessian], AIR_TRUE); } else if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCl1HessianEval)) { ell_3m_eigenvalues_d(pvl->directAnswer[tenGageCl1HessianEval], pvl->directAnswer[tenGageCl1Hessian], AIR_TRUE); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCp1Hessian)) { int dira, dirb; double *cp1hess = pvl->directAnswer[tenGageCp1Hessian]; double *tgradE = pvl->directAnswer[tenGageTensorGradRotE]; double *evhess = pvl->directAnswer[tenGageEvalHessian]; double B = (evalAns[0]+evalAns[1]+evalAns[2])* (evalAns[0]+evalAns[1]+evalAns[2]); for (dira=0; dira<3; dira++) { for (dirb=0; dirb<=dira; dirb++) { /* again, exploit Hessian symmetry */ double A = 2*(evalAns[0]*(tgradE[12+dira]-tgradE[18+dira])+ evalAns[1]*(-tgradE[3+dira]-2*tgradE[18+dira])+ evalAns[2]*(tgradE[3+dira]+2*tgradE[12+dira])); double Ad=2*(evalAns[0]*(evhess[9+3*dirb+dira]-evhess[18+3*dirb+dira])+ evalAns[1]*(-evhess[3*dirb+dira]-2*evhess[18+3*dirb+dira])+ evalAns[2]*(evhess[3*dirb+dira]+2*evhess[9+3*dirb+dira])+ tgradE[3+dirb]*(tgradE[12+dira]-tgradE[18+dira])+ tgradE[12+dirb]*(-tgradE[3+dira]-2*tgradE[18+dira])+ tgradE[18+dirb]*(tgradE[3+dira]+2*tgradE[12+dira])); double Bd = 2*(evalAns[0]+evalAns[1]+evalAns[2])* (tgradE[3+dirb]+tgradE[12+dirb]+tgradE[18+dirb]); cp1hess[3*dirb+dira]=Ad/B-A/B*Bd/B; } } for (dira=0; dira<2; dira++) { for (dirb=dira+1; dirb<3; dirb++) { /* copy over symmetric values */ cp1hess[3*dirb+dira]=cp1hess[3*dira+dirb]; } } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCp1HessianEvec)) { ell_3m_eigensolve_d(pvl->directAnswer[tenGageCp1HessianEval], pvl->directAnswer[tenGageCp1HessianEvec], pvl->directAnswer[tenGageCp1Hessian], AIR_TRUE); } else if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCp1HessianEval)) { ell_3m_eigenvalues_d(pvl->directAnswer[tenGageCp1HessianEval], pvl->directAnswer[tenGageCp1Hessian], AIR_TRUE); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCa1Hessian)) { int dira, dirb; double *ca1hess = pvl->directAnswer[tenGageCa1Hessian]; double *tgradE = pvl->directAnswer[tenGageTensorGradRotE]; double *evhess = pvl->directAnswer[tenGageEvalHessian]; double B = (evalAns[0]+evalAns[1]+evalAns[2])* (evalAns[0]+evalAns[1]+evalAns[2]); for (dira=0; dira<3; dira++) { for (dirb=0; dirb<=dira; dirb++) { /* again, exploit Hessian symmetry */ double A = 3*(evalAns[0]*tgradE[18+dira]+evalAns[1]*tgradE[18+dira]+ evalAns[2]*(-tgradE[3+dira]-tgradE[12+dira])); double Ad = 3*(tgradE[3+dirb]*tgradE[18+dira]+ tgradE[12+dirb]*tgradE[18+dira]+ tgradE[18+dirb]*(-tgradE[3+dira]-tgradE[12+dira])+ evalAns[0]*evhess[18+3*dirb+dira]+ evalAns[1]*evhess[18+3*dirb+dira]+ evalAns[2]*(-evhess[3*dirb+dira]-evhess[9+3*dirb+dira])); double Bd = 2*(evalAns[0]+evalAns[1]+evalAns[2])* (tgradE[3+dirb]+tgradE[12+dirb]+tgradE[18+dirb]); /* above formulas are true for cs, so flip sign here */ ca1hess[3*dirb+dira]=-Ad/B+A/B*Bd/B; } } for (dira=0; dira<2; dira++) { for (dirb=dira+1; dirb<3; dirb++) { /* copy over symmetric values */ ca1hess[3*dirb+dira]=ca1hess[3*dira+dirb]; } } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCa1HessianEvec)) { ell_3m_eigensolve_d(pvl->directAnswer[tenGageCa1HessianEval], pvl->directAnswer[tenGageCa1HessianEvec], pvl->directAnswer[tenGageCa1Hessian], AIR_TRUE); } else if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageCa1HessianEval)) { ell_3m_eigenvalues_d(pvl->directAnswer[tenGageCa1HessianEval], pvl->directAnswer[tenGageCa1Hessian], AIR_TRUE); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFiberCurving) || GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFiberDispersion)) { double rtout[7], evout[7]; TEN_T3V_OUTER(rtout, pvl->directAnswer[tenGageRotTans] + 1*3); TEN_T3V_OUTER_INCR(rtout, pvl->directAnswer[tenGageRotTans] + 2*3); if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFiberCurving)) { TEN_T3V_OUTER(evout, pvl->directAnswer[tenGageEvec0]); pvl->directAnswer[tenGageFiberCurving][0] = TEN_T_DOT(rtout, evout); /* pvl->directAnswer[tenGageFiberCurving][0] *= 100000; */ } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageFiberDispersion)) { TEN_T3V_OUTER(evout, pvl->directAnswer[tenGageEvec1]); TEN_T3V_OUTER_INCR(evout, pvl->directAnswer[tenGageEvec2]); pvl->directAnswer[tenGageFiberDispersion][0] = TEN_T_DOT(rtout, evout); /* pvl->directAnswer[tenGageFiberDispersion][0] *= 100000; */ } } /* --- Aniso --- */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenGageAniso)) { for (ci=tenAnisoUnknown+1; ci<=TEN_ANISO_MAX; ci++) { pvl->directAnswer[tenGageAniso][ci] = tenAnisoEval_d(evalAns, ci); } } return; } void * _tenGagePvlDataNew(const struct gageKind_t *kind) { _tenGagePvlData *pvlData; AIR_UNUSED(kind); pvlData = AIR_CALLOC(1, _tenGagePvlData); if (pvlData) { pvlData->buffTen = NULL; pvlData->buffWght = NULL; pvlData->tip = tenInterpParmNew(); } return pvlData; } void * _tenGagePvlDataCopy(const struct gageKind_t *kind, const void *_pvlDataOld) { _tenGagePvlData *pvlDataNew, *pvlDataOld; unsigned int num; AIR_UNUSED(kind); pvlDataOld = AIR_CAST(_tenGagePvlData *, _pvlDataOld); num = pvlDataOld->tip->allocLen; pvlDataNew = AIR_CALLOC(1, _tenGagePvlData); if (pvlDataNew) { pvlDataNew->buffTen = AIR_CALLOC(7*num, double); pvlDataNew->buffWght = AIR_CALLOC(num, double); pvlDataNew->tip = tenInterpParmCopy(pvlDataOld->tip); } return pvlDataNew; } void * _tenGagePvlDataNix(const struct gageKind_t *kind, void *_pvlData) { _tenGagePvlData *pvlData; AIR_UNUSED(kind); pvlData = AIR_CAST(_tenGagePvlData *, _pvlData); airFree(pvlData->buffTen); airFree(pvlData->buffWght); tenInterpParmNix(pvlData->tip); airFree(pvlData); return NULL; } int _tenGagePvlDataUpdate(const struct gageKind_t *kind, const gageContext *ctx, const gagePerVolume *pvl, const void *_pvlData) { _tenGagePvlData *pvlData; unsigned int fd, num; AIR_UNUSED(kind); AIR_UNUSED(pvl); pvlData = AIR_CAST(_tenGagePvlData *, _pvlData); fd = AIR_CAST(unsigned int, 2*ctx->radius); num = fd*fd*fd; if (num != pvlData->tip->allocLen) { /* HEY: no error checking */ airFree(pvlData->buffTen); pvlData->buffTen = NULL; airFree(pvlData->buffWght); pvlData->buffWght = NULL; pvlData->buffTen = AIR_CALLOC(7*num, double); pvlData->buffWght = AIR_CALLOC(num, double); tenInterpParmBufferAlloc(pvlData->tip, num); } return 0; } gageKind _tenGageKind = { AIR_FALSE, /* statically allocated */ "tensor", &_tenGage, 1, 7, TEN_GAGE_ITEM_MAX, _tenGageTable, _tenGageIv3Print, _tenGageFilter, _tenGageAnswer, _tenGagePvlDataNew, _tenGagePvlDataCopy, _tenGagePvlDataNix, _tenGagePvlDataUpdate, NULL }; gageKind * tenGageKind = &_tenGageKind; teem-1.11.0~svn6057/src/ten/fiber.c0000664000175000017500000012334612165631065016433 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define TEN_FIBER_INCR 512 /* ** _tenFiberProbe ** ** The job here is to probe at (world space) "wPos" and then set: ** tfx->fiberTen ** tfx->fiberEval (all 3 evals) ** tfx->fiberEvec (all 3 eigenvectors) ** if (tfx->stop & (1 << tenFiberStopAniso): tfx->fiberAnisoStop ** ** In the case of non-single-tensor tractography, we do so based on ** ten2Which (when at the seedpoint) or ** ** Note that for performance reasons, a non-zero return value ** (indicating error) and the associated use of biff, is only possible ** if seedProbe is non-zero, the reason being that problems can be ** detected at the seedpoint, and won't arise after the seedpoint. ** ** Errors from gage are indicated by *gageRet, which includes leaving ** the domain of the volume, which is used to terminate fibers. ** ** Our use of tfx->seedEvec (shared with _tenFiberAlign), as well as that ** of tfx->lastDir and tfx->lastDirSet, could stand to have further ** debugging and documentation ... */ int _tenFiberProbe(tenFiberContext *tfx, int *gageRet, double wPos[3], int seedProbe) { static const char me[]="_tenFiberProbe"; double iPos[3]; int ret = 0; double tens2[2][7]; gageShapeWtoI(tfx->gtx->shape, iPos, wPos); *gageRet = gageProbe(tfx->gtx, iPos[0], iPos[1], iPos[2]); if (tfx->verbose > 2) { fprintf(stderr, "%s(%g,%g,%g, %s): hi ----- %s\n", me, iPos[0], iPos[1], iPos[2], seedProbe ? "***TRUE***" : "false", tfx->useDwi ? "using DWIs" : ""); } if (!tfx->useDwi) { /* normal single-tensor tracking */ TEN_T_COPY(tfx->fiberTen, tfx->gageTen); ELL_3V_COPY(tfx->fiberEval, tfx->gageEval); ELL_3M_COPY(tfx->fiberEvec, tfx->gageEvec); if (tfx->stop & (1 << tenFiberStopAniso)) { tfx->fiberAnisoStop = tfx->gageAnisoStop[0]; } if (seedProbe) { ELL_3V_COPY(tfx->seedEvec, tfx->fiberEvec); } } else { /* tracking in DWIs */ if (tfx->verbose > 2 && seedProbe) { fprintf(stderr, "%s: fiber type = %s\n", me, airEnumStr(tenDwiFiberType, tfx->fiberType)); } switch (tfx->fiberType) { double evec[2][9], eval[2][3]; case tenDwiFiberType1Evec0: if (tfx->mframeUse) { double matTmpA[9], matTmpB[9]; TEN_T2M(matTmpA, tfx->gageTen); ELL_3M_MUL(matTmpB, tfx->mframe, matTmpA); ELL_3M_MUL(matTmpA, matTmpB, tfx->mframeT); TEN_M2T(tfx->fiberTen, matTmpA); tfx->fiberTen[0] = tfx->gageTen[0]; } else { TEN_T_COPY(tfx->fiberTen, tfx->gageTen); } tenEigensolve_d(tfx->fiberEval, tfx->fiberEvec, tfx->fiberTen); if (tfx->stop & (1 << tenFiberStopAniso)) { double tmp; tmp = tenAnisoTen_d(tfx->fiberTen, tfx->anisoStopType); tfx->fiberAnisoStop = AIR_CLAMP(0, tmp, 1); } if (seedProbe) { ELL_3V_COPY(tfx->seedEvec, tfx->fiberEvec); } break; case tenDwiFiberType2Evec0: /* Estimate principal diffusion direction of each tensor */ if (tfx->mframeUse) { /* Transform both the tensors */ double matTmpA[9], matTmpB[9]; TEN_T2M(matTmpA, tfx->gageTen2 + 0); ELL_3M_MUL(matTmpB, tfx->mframe, matTmpA); ELL_3M_MUL(matTmpA, matTmpB, tfx->mframeT); TEN_M2T(tens2[0], matTmpA); /* new eigen values and vectors */ tenEigensolve_d(eval[0], evec[0], tens2[0]); TEN_T2M(matTmpA, tfx->gageTen2 + 7); ELL_3M_MUL(matTmpB, tfx->mframe, matTmpA); ELL_3M_MUL(matTmpA, matTmpB, tfx->mframeT); TEN_M2T(tens2[1], matTmpA); tenEigensolve_d(eval[1], evec[1], tens2[1]); } else { tenEigensolve_d(eval[0], evec[0], tfx->gageTen2 + 0); tenEigensolve_d(eval[1], evec[1], tfx->gageTen2 + 7); } /* set ten2Use */ if (seedProbe) { /* we're on the *very* 1st probe per tract, at the seed pt */ ELL_3V_COPY(tfx->seedEvec, evec[tfx->ten2Which]); tfx->ten2Use = tfx->ten2Which; if (tfx->verbose > 2) { fprintf(stderr, "%s: ** ten2Use == ten2Which == %d\n", me, tfx->ten2Use); } } else { double *lastVec, dot[2]; if (!tfx->lastDirSet) { /* we're on some probe of the first step */ lastVec = tfx->seedEvec; } else { /* we're past the first step */ /* Arish says: "Bug len has not been initialized and don't think its needed". The first part is not a problem; "len" is in the *output* argument of ELL_3V_NORM. The second part seems to be true, even though Gordon can't currently see why! */ /* ELL_3V_NORM(tfx->lastDir, tfx->lastDir, len); */ lastVec = tfx->lastDir; } dot[0] = ELL_3V_DOT(lastVec, evec[0]); dot[1] = ELL_3V_DOT(lastVec, evec[1]); if (dot[0] < 0) { dot[0] *= -1; ELL_3M_SCALE(evec[0], -1, evec[0]); } if (dot[1] < 0) { dot[1] *= -1; ELL_3M_SCALE(evec[1], -1, evec[1]); } tfx->ten2Use = (dot[0] > dot[1]) ? 0 : 1; if (tfx->verbose > 2) { fprintf(stderr, "%s(%g,%g,%g): dot[0,1] = %f, %f -> use %u\n", me, wPos[0], wPos[1], wPos[2], dot[0], dot[1], tfx->ten2Use ); } } /* based on ten2Use, set the rest of the needed fields */ if (tfx->mframeUse) { TEN_T_COPY(tfx->fiberTen, tens2[tfx->ten2Use]); } else { TEN_T_COPY(tfx->fiberTen, tfx->gageTen2 + 7*(tfx->ten2Use)); } tfx->fiberTen[0] = tfx->gageTen2[0]; /* copy confidence */ ELL_3V_COPY(tfx->fiberEval, eval[tfx->ten2Use]); ELL_3M_COPY(tfx->fiberEvec, evec[tfx->ten2Use]); if (tfx->stop & (1 << tenFiberStopAniso)) { double tmp; tmp = tenAnisoEval_d(tfx->fiberEval, tfx->anisoStopType); tfx->fiberAnisoStop = AIR_CLAMP(0, tmp, 1); /* HEY: what about speed? */ } else { tfx->fiberAnisoStop = AIR_NAN; } break; default: biffAddf(TEN, "%s: %s %s (%d) unimplemented!", me, tenDwiFiberType->name, airEnumStr(tenDwiFiberType, tfx->fiberType), tfx->fiberType); ret = 1; } /* switch (tfx->fiberType) */ } if (tfx->verbose > 2) { fprintf(stderr, "%s: fiberEvec = %g %g %g\n", me, tfx->fiberEvec[0], tfx->fiberEvec[1], tfx->fiberEvec[2]); } return ret; } int _tenFiberStopCheck(tenFiberContext *tfx) { static const char me[]="_tenFiberStopCheck"; if (tfx->numSteps[tfx->halfIdx] >= TEN_FIBER_NUM_STEPS_MAX) { fprintf(stderr, "%s: numSteps[%d] exceeded sanity check value of %d!!\n", me, tfx->halfIdx, TEN_FIBER_NUM_STEPS_MAX); fprintf(stderr, "%s: Check fiber termination conditions, or recompile " "with a larger value for TEN_FIBER_NUM_STEPS_MAX\n", me); return tenFiberStopNumSteps; } if (tfx->stop & (1 << tenFiberStopConfidence)) { if (tfx->fiberTen[0] < tfx->confThresh) { return tenFiberStopConfidence; } } if (tfx->stop & (1 << tenFiberStopRadius)) { if (tfx->radius < tfx->minRadius) { return tenFiberStopRadius; } } if (tfx->stop & (1 << tenFiberStopAniso)) { if (tfx->fiberAnisoStop < tfx->anisoThresh) { return tenFiberStopAniso; } } if (tfx->stop & (1 << tenFiberStopNumSteps)) { if (tfx->numSteps[tfx->halfIdx] > tfx->maxNumSteps) { return tenFiberStopNumSteps; } } if (tfx->stop & (1 << tenFiberStopLength)) { if (tfx->halfLen[tfx->halfIdx] >= tfx->maxHalfLen) { return tenFiberStopLength; } } if (tfx->useDwi && tfx->stop & (1 << tenFiberStopFraction) && tfx->gageTen2) { /* not all DWI fiber types use gageTen2 */ double fracUse; fracUse = (0 == tfx->ten2Use ? tfx->gageTen2[7] : 1 - tfx->gageTen2[7]); if (fracUse < tfx->minFraction) { return tenFiberStopFraction; } } return 0; } void _tenFiberAlign(tenFiberContext *tfx, double vec[3]) { static const char me[]="_tenFiberAlign"; double scale, dot; if (tfx->verbose > 2) { fprintf(stderr, "%s: hi %s (lds %d):\t%g %g %g\n", me, !tfx->lastDirSet ? "**" : " ", tfx->lastDirSet, vec[0], vec[1], vec[2]); } if (!(tfx->lastDirSet)) { dot = ELL_3V_DOT(tfx->seedEvec, vec); /* this is the first step (or one of the intermediate steps for RK) in this fiber half; 1st half follows the eigenvector determined at seed point, 2nd goes opposite */ if (tfx->verbose > 2) { fprintf(stderr, "!%s: dir=%d, dot=%g\n", me, tfx->halfIdx, dot); } if (!tfx->halfIdx) { /* 1st half */ scale = dot < 0 ? -1 : 1; } else { /* 2nd half */ scale = dot > 0 ? -1 : 1; } } else { dot = ELL_3V_DOT(tfx->lastDir, vec); /* we have some history in this fiber half */ scale = dot < 0 ? -1 : 1; } ELL_3V_SCALE(vec, scale, vec); if (tfx->verbose > 2) { fprintf(stderr, "!%s: scl = %g -> \t%g %g %g\n", me, scale, vec[0], vec[1], vec[2]); } return; } /* ** parm[0]: lerp between 1 and the stuff below ** parm[1]: "t": (parm[1],0) is control point between (0,0) and (1,1) ** parm[2]: "d": parabolic blend between parm[1]-parm[2] and parm[1]+parm[2] */ void _tenFiberAnisoSpeed(double *step, double xx, double parm[3]) { double aa, dd, tt, yy; tt = parm[1]; dd = parm[2]; aa = 1.0/(DBL_EPSILON + 4*dd*(1.0-tt)); yy = xx - tt + dd; xx = (xx < tt - dd ? 0 : (xx < tt + dd ? aa*yy*yy : (xx - tt)/(1 - tt))); xx = AIR_LERP(parm[0], 1, xx); ELL_3V_SCALE(step, xx, step); } /* ** ------------------------------------------------------------------- ** ------------------------------------------------------------------- ** The _tenFiberStep_* routines are responsible for putting a step into ** the given step[] vector. Without anisoStepSize, this should be ** UNIT LENGTH, with anisoStepSize, its scaled by that anisotropy measure */ void _tenFiberStep_Evec(tenFiberContext *tfx, double step[3]) { /* fiberEvec points to the correct gage answer based on fiberType */ ELL_3V_COPY(step, tfx->fiberEvec + 3*0); _tenFiberAlign(tfx, step); if (tfx->anisoSpeedType) { _tenFiberAnisoSpeed(step, tfx->fiberAnisoSpeed, tfx->anisoSpeedFunc); } } void _tenFiberStep_TensorLine(tenFiberContext *tfx, double step[3]) { double cl, evec0[3], vout[3], vin[3], len; ELL_3V_COPY(evec0, tfx->fiberEvec + 3*0); _tenFiberAlign(tfx, evec0); if (tfx->lastDirSet) { ELL_3V_COPY(vin, tfx->lastDir); TEN_T3V_MUL(vout, tfx->fiberTen, tfx->lastDir); ELL_3V_NORM(vout, vout, len); _tenFiberAlign(tfx, vout); /* HEY: is this needed? */ } else { ELL_3V_COPY(vin, evec0); ELL_3V_COPY(vout, evec0); } /* HEY: should be using one of the tenAnisoEval[] functions */ cl = (tfx->fiberEval[0] - tfx->fiberEval[1])/(tfx->fiberEval[0] + 0.00001); ELL_3V_SCALE_ADD3(step, cl, evec0, (1-cl)*(1-tfx->wPunct), vin, (1-cl)*tfx->wPunct, vout); /* _tenFiberAlign(tfx, step); */ ELL_3V_NORM(step, step, len); if (tfx->anisoSpeedType) { _tenFiberAnisoSpeed(step, tfx->fiberAnisoSpeed, tfx->anisoSpeedFunc); } } void _tenFiberStep_PureLine(tenFiberContext *tfx, double step[3]) { static const char me[]="_tenFiberStep_PureLine"; AIR_UNUSED(tfx); AIR_UNUSED(step); fprintf(stderr, "%s: sorry, unimplemented!\n", me); } void _tenFiberStep_Zhukov(tenFiberContext *tfx, double step[3]) { static const char me[]="_tenFiberStep_Zhukov"; AIR_UNUSED(tfx); AIR_UNUSED(step); fprintf(stderr, "%s: sorry, unimplemented!\n", me); } void (* _tenFiberStep[TEN_FIBER_TYPE_MAX+1])(tenFiberContext *, double *) = { NULL, _tenFiberStep_Evec, _tenFiberStep_Evec, _tenFiberStep_Evec, _tenFiberStep_TensorLine, _tenFiberStep_PureLine, _tenFiberStep_Zhukov }; /* ** ------------------------------------------------------------------- ** ------------------------------------------------------------------- ** The _tenFiberIntegrate_* routines must assume that ** _tenFiberProbe(tfx, tfx->wPos, AIR_FALSE) has just been called */ int _tenFiberIntegrate_Euler(tenFiberContext *tfx, double forwDir[3]) { _tenFiberStep[tfx->fiberType](tfx, forwDir); ELL_3V_SCALE(forwDir, tfx->stepSize, forwDir); return 0; } int _tenFiberIntegrate_Midpoint(tenFiberContext *tfx, double forwDir[3]) { double loc[3], half[3]; int gret; _tenFiberStep[tfx->fiberType](tfx, half); ELL_3V_SCALE_ADD2(loc, 1, tfx->wPos, 0.5*tfx->stepSize, half); _tenFiberProbe(tfx, &gret, loc, AIR_FALSE); if (gret) return 1; _tenFiberStep[tfx->fiberType](tfx, forwDir); ELL_3V_SCALE(forwDir, tfx->stepSize, forwDir); return 0; } int _tenFiberIntegrate_RK4(tenFiberContext *tfx, double forwDir[3]) { double loc[3], k1[3], k2[3], k3[3], k4[3], c1, c2, c3, c4, h; int gret; h = tfx->stepSize; c1 = h/6.0; c2 = h/3.0; c3 = h/3.0; c4 = h/6.0; _tenFiberStep[tfx->fiberType](tfx, k1); ELL_3V_SCALE_ADD2(loc, 1, tfx->wPos, 0.5*h, k1); _tenFiberProbe(tfx, &gret, loc, AIR_FALSE); if (gret) return 1; _tenFiberStep[tfx->fiberType](tfx, k2); ELL_3V_SCALE_ADD2(loc, 1, tfx->wPos, 0.5*h, k2); _tenFiberProbe(tfx, &gret, loc, AIR_FALSE); if (gret) return 1; _tenFiberStep[tfx->fiberType](tfx, k3); ELL_3V_SCALE_ADD2(loc, 1, tfx->wPos, h, k3); _tenFiberProbe(tfx, &gret, loc, AIR_FALSE); if (gret) return 1; _tenFiberStep[tfx->fiberType](tfx, k4); ELL_3V_SET(forwDir, c1*k1[0] + c2*k2[0] + c3*k3[0] + c4*k4[0], c1*k1[1] + c2*k2[1] + c3*k3[1] + c4*k4[1], c1*k1[2] + c2*k2[2] + c3*k3[2] + c4*k4[2]); return 0; } int (* _tenFiberIntegrate[TEN_FIBER_INTG_MAX+1])(tenFiberContext *tfx, double *) = { NULL, _tenFiberIntegrate_Euler, _tenFiberIntegrate_Midpoint, _tenFiberIntegrate_RK4 }; /* ** modified body of previous tenFiberTraceSet, in order to ** permit passing the nval for storing desired probed values */ static int _fiberTraceSet(tenFiberContext *tfx, Nrrd *nval, Nrrd *nfiber, double *buff, unsigned int halfBuffLen, unsigned int *startIdxP, unsigned int *endIdxP, double seed[3]) { static const char me[]="_fiberTraceSet"; airArray *fptsArr[2], /* airArrays of backward (0) and forward (1) fiber points */ *pansArr[2]; /* airArrays of backward (0) and forward (1) probed values */ double *fpts[2], /* arrays storing forward and backward fiber points */ *pans[2], /* arrays storing forward and backward probed values */ tmp[3], iPos[3], currPoint[3], forwDir[3], *fiber, /* array of both forward and backward points, when finished */ *valOut; /* same for probed values */ const double *pansP; /* pointer to gage's probed values */ int gret, whyStop, buffIdx, fptsIdx, pansIdx, outIdx, oldStop, keepfiber; unsigned int i, pansLen; airArray *mop; airPtrPtrUnion appu; if (!(tfx)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (nval) { if (!tfx->fiberProbeItem) { biffAddf(TEN, "%s: want to record probed values but no item set", me); return 1; } pansLen = gageAnswerLength(tfx->gtx, tfx->pvl, tfx->fiberProbeItem); pansP = gageAnswerPointer(tfx->gtx, tfx->pvl, tfx->fiberProbeItem); } else { pansLen = 0; pansP = NULL; } /* fprintf(stderr, "!%s: =========================== \n", me); fprintf(stderr, "!%s: \n", me); fprintf(stderr, "!%s: item %d -> pansLen = %u\n", me, tfx->fiberProbeItem, pansLen); fprintf(stderr, "!%s: \n", me); fprintf(stderr, "!%s: =========================== \n", me); */ /* HEY: a hack to preserve the state inside tenFiberContext so that we have fewer side effects (tfx->maxNumSteps may still be set) */ oldStop = tfx->stop; if (!nfiber) { if (!( buff && halfBuffLen > 0 && startIdxP && startIdxP )) { biffAddf(TEN, "%s: need either non-NULL nfiber or fpts buffer info", me); return 1; } if (tenFiberStopSet(tfx, tenFiberStopNumSteps, halfBuffLen)) { biffAddf(TEN, "%s: error setting new fiber stop", me); return 1; } } /* initialize the quantities which describe the fiber halves */ tfx->halfLen[0] = tfx->halfLen[1] = 0.0; tfx->numSteps[0] = tfx->numSteps[1] = 0; tfx->whyStop[0] = tfx->whyStop[1] = tenFiberStopUnknown; /* fprintf(stderr, "!%s: try probing once, at seed %g %g %g\n", me, seed[0], seed[1], seed[2]); */ /* try probing once, at seed point */ if (tfx->useIndexSpace) { gageShapeItoW(tfx->gtx->shape, tmp, seed); } else { ELL_3V_COPY(tmp, seed); } if (_tenFiberProbe(tfx, &gret, tmp, AIR_TRUE)) { biffAddf(TEN, "%s: first _tenFiberProbe failed", me); return 1; } if (gret) { if (gageErrBoundsSpace != tfx->gtx->errNum) { biffAddf(TEN, "%s: gage problem on first _tenFiberProbe: %s (%d)", me, tfx->gtx->errStr, tfx->gtx->errNum); return 1; } else { /* the problem on the first probe was that it was out of bounds, which is not a catastrophe; its handled the same as below */ tfx->whyNowhere = tenFiberStopBounds; if (nval) { nrrdEmpty(nval); } if (nfiber) { nrrdEmpty(nfiber); } else { *startIdxP = *endIdxP = 0; } return 0; } } /* see if we're doomed (tract dies before it gets anywhere) */ /* have to fake out the possible radius check, since at this point there is no radius of curvature; this will always pass */ tfx->radius = DBL_MAX; if ((whyStop = _tenFiberStopCheck(tfx))) { /* stopped immediately at seed point, but that's not an error */ tfx->whyNowhere = whyStop; if (nval) { nrrdEmpty(nval); } if (nfiber) { nrrdEmpty(nfiber); } else { *startIdxP = *endIdxP = 0; } return 0; } else { /* did not immediately halt */ tfx->whyNowhere = tenFiberStopUnknown; } /* airMop{Error,Okay}() can safely be called on NULL */ mop = (nfiber || nval) ? airMopNew() : NULL; for (tfx->halfIdx=0; tfx->halfIdx<=1; tfx->halfIdx++) { if (nval) { appu.d = &(pans[tfx->halfIdx]); pansArr[tfx->halfIdx] = airArrayNew(appu.v, NULL, pansLen*sizeof(double), TEN_FIBER_INCR); airMopAdd(mop, pansArr[tfx->halfIdx], (airMopper)airArrayNuke, airMopAlways); } else { pansArr[tfx->halfIdx] = NULL; } pansIdx = -1; if (nfiber) { appu.d = &(fpts[tfx->halfIdx]); fptsArr[tfx->halfIdx] = airArrayNew(appu.v, NULL, 3*sizeof(double), TEN_FIBER_INCR); airMopAdd(mop, fptsArr[tfx->halfIdx], (airMopper)airArrayNuke, airMopAlways); buffIdx = -1; } else { fptsArr[tfx->halfIdx] = NULL; fpts[tfx->halfIdx] = NULL; buffIdx = halfBuffLen; } fptsIdx = -1; tfx->halfLen[tfx->halfIdx] = 0; if (tfx->useIndexSpace) { ELL_3V_COPY(iPos, seed); gageShapeItoW(tfx->gtx->shape, tfx->wPos, iPos); } else { /* fprintf(stderr, "!%s(A): %p %p %p\n", me, tfx->gtx->shape, iPos, seed); */ gageShapeWtoI(tfx->gtx->shape, iPos, seed); ELL_3V_COPY(tfx->wPos, seed); } /* have to initially pass the possible radius check in _tenFiberStopCheck(); this will always pass */ tfx->radius = DBL_MAX; ELL_3V_SET(tfx->lastDir, 0, 0, 0); tfx->lastDirSet = AIR_FALSE; for (tfx->numSteps[tfx->halfIdx] = 0; AIR_TRUE; tfx->numSteps[tfx->halfIdx]++) { _tenFiberProbe(tfx, &gret, tfx->wPos, AIR_FALSE); if (gret) { /* even if gageProbe had an error OTHER than going out of bounds, we're not going to report it any differently here, alas */ tfx->whyStop[tfx->halfIdx] = tenFiberStopBounds; /* fprintf(stderr, "!%s: A tfx->whyStop[%d] = %s\n", me, tfx->halfIdx, airEnumStr(tenFiberStop, tfx->whyStop[tfx->halfIdx])); */ break; } if ((whyStop = _tenFiberStopCheck(tfx))) { if (tenFiberStopNumSteps == whyStop) { /* we stopped along this direction because tfx->numSteps[tfx->halfIdx] exceeded tfx->maxNumSteps. Okay. But tfx->numSteps[tfx->halfIdx] is supposed to be a record of how steps were (successfully) taken. So we need to decrementing before moving on ... */ tfx->numSteps[tfx->halfIdx]--; } tfx->whyStop[tfx->halfIdx] = whyStop; /* fprintf(stderr, "!%s: B tfx->whyStop[%d] = %s\n", me, tfx->halfIdx, airEnumStr(tenFiberStop, tfx->whyStop[tfx->halfIdx])); */ break; } if (tfx->useIndexSpace) { /* fprintf(stderr, "!%s(B): %p %p %p\n", me, tfx->gtx->shape, iPos, tfx->wPos); */ gageShapeWtoI(tfx->gtx->shape, iPos, tfx->wPos); ELL_3V_COPY(currPoint, iPos); } else { ELL_3V_COPY(currPoint, tfx->wPos); } if (nval) { pansIdx = airArrayLenIncr(pansArr[tfx->halfIdx], 1); /* HEY: speed this up */ memcpy(pans[tfx->halfIdx] + pansLen*pansIdx, pansP, pansLen*sizeof(double)); /* fprintf(stderr, "!%s: (dir %d) saving to %d: %g @ (%g,%g,%g)\n", me, tfx->halfIdx, pansIdx, pansP[0], currPoint[0], currPoint[1], currPoint[2]); */ } if (nfiber) { fptsIdx = airArrayLenIncr(fptsArr[tfx->halfIdx], 1); ELL_3V_COPY(fpts[tfx->halfIdx] + 3*fptsIdx, currPoint); } else { ELL_3V_COPY(buff + 3*buffIdx, currPoint); /* fprintf(stderr, "!%s: (dir %d) saving to %d pnt %g %g %g\n", me, tfx->halfIdx, buffIdx, currPoint[0], currPoint[1], currPoint[2]); */ buffIdx += !tfx->halfIdx ? -1 : 1; } /* forwDir is set by this to point to the next fiber point */ if (_tenFiberIntegrate[tfx->intg](tfx, forwDir)) { tfx->whyStop[tfx->halfIdx] = tenFiberStopBounds; /* fprintf(stderr, "!%s: C tfx->whyStop[%d] = %s\n", me, tfx->halfIdx, airEnumStr(tenFiberStop, tfx->whyStop[tfx->halfIdx])); */ break; } /* fprintf(stderr, "!%s: forwDir = %g %g %g\n", me, forwDir[0], forwDir[1], forwDir[2]); */ if (tfx->stop & (1 << tenFiberStopRadius)) { /* some more work required to compute radius of curvature */ double svec[3], dvec[3], SS, DD, dlen; /* sum,diff length squared */ /* tfx->lastDir and forwDir are not normalized to unit-length */ if (tfx->lastDirSet) { ELL_3V_ADD2(svec, tfx->lastDir, forwDir); ELL_3V_SUB(dvec, tfx->lastDir, forwDir); SS = ELL_3V_DOT(svec, svec); DD = ELL_3V_DOT(dvec, dvec); /* Sun Nov 2 00:04:05 EDT 2008: GLK can't recover how he derived this, and can't see why it would be corrrect, even though it seems to work correctly... tfx->radius = sqrt(SS*(SS+DD)/DD)/4; */ dlen = sqrt(DD); tfx->radius = dlen ? (SS + DD)/(4*dlen) : DBL_MAX; } else { tfx->radius = DBL_MAX; } } /* if (!tfx->lastDirSet) { fprintf(stderr, "!%s: now setting lastDirSet to (%g,%g,%g)\n", me, forwDir[0], forwDir[1], forwDir[2]); } */ ELL_3V_COPY(tfx->lastDir, forwDir); tfx->lastDirSet = AIR_TRUE; ELL_3V_ADD2(tfx->wPos, tfx->wPos, forwDir); tfx->halfLen[tfx->halfIdx] += ELL_3V_LEN(forwDir); } } keepfiber = AIR_TRUE; if ((tfx->stop & (1 << tenFiberStopStub)) && (2 == fptsArr[0]->len + fptsArr[1]->len)) { /* seed point was actually valid, but neither half got anywhere, and the user has set tenFiberStopStub, so we report this as a non-starter, via tfx->whyNowhere. */ tfx->whyNowhere = tenFiberStopStub; keepfiber = AIR_FALSE; } if ((tfx->stop & (1 << tenFiberStopMinNumSteps)) && (fptsArr[0]->len + fptsArr[1]->len < tfx->minNumSteps)) { /* whole fiber didn't have enough steps */ tfx->whyNowhere = tenFiberStopMinNumSteps; keepfiber = AIR_FALSE; } if ((tfx->stop & (1 << tenFiberStopMinLength)) && (tfx->halfLen[0] + tfx->halfLen[1] < tfx->minWholeLen)) { /* whole fiber wasn't long enough */ tfx->whyNowhere = tenFiberStopMinLength; keepfiber = AIR_FALSE; } if (!keepfiber) { /* for the curious, tfx->whyStop[0,1], tfx->numSteps[0,1], and tfx->halfLen[1,2] remain set, from above */ if (nval) { nrrdEmpty(nval); } if (nfiber) { nrrdEmpty(nfiber); } else { *startIdxP = *endIdxP = 0; } } else { if (nval) { if (nrrdMaybeAlloc_va(nval, nrrdTypeDouble, 2, AIR_CAST(size_t, pansLen), AIR_CAST(size_t, (pansArr[0]->len + pansArr[1]->len - 1)))) { biffMovef(TEN, NRRD, "%s: couldn't allocate probed value nrrd", me); airMopError(mop); return 1; } valOut = AIR_CAST(double*, nval->data); outIdx = 0; /* HEY: speed up memcpy */ for (i=pansArr[0]->len-1; i>=1; i--) { memcpy(valOut + pansLen*outIdx, pans[0] + pansLen*i, pansLen*sizeof(double)); outIdx++; } for (i=0; i<=pansArr[1]->len-1; i++) { memcpy(valOut + pansLen*outIdx, pans[1] + pansLen*i, pansLen*sizeof(double)); outIdx++; } } if (nfiber) { if (nrrdMaybeAlloc_va(nfiber, nrrdTypeDouble, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, (fptsArr[0]->len + fptsArr[1]->len - 1)))) { biffMovef(TEN, NRRD, "%s: couldn't allocate fiber nrrd", me); airMopError(mop); return 1; } fiber = AIR_CAST(double*, nfiber->data); outIdx = 0; for (i=fptsArr[0]->len-1; i>=1; i--) { ELL_3V_COPY(fiber + 3*outIdx, fpts[0] + 3*i); outIdx++; } for (i=0; i<=fptsArr[1]->len-1; i++) { ELL_3V_COPY(fiber + 3*outIdx, fpts[1] + 3*i); outIdx++; } } else { *startIdxP = halfBuffLen - tfx->numSteps[0]; *endIdxP = halfBuffLen + tfx->numSteps[1]; } } tfx->stop = oldStop; airMopOkay(mop); return 0; } /* ******** tenFiberTraceSet ** ** slightly more flexible API for fiber tracking than tenFiberTrace ** ** EITHER: pass a non-NULL nfiber, and NULL, 0, NULL, NULL for ** the following arguments, and things are the same as with tenFiberTrace: ** data inside the nfiber is allocated, and the tract vertices are copied ** into it, having been stored in dynamically allocated airArrays ** ** OR: pass a NULL nfiber, and a buff allocated for 3*(2*halfBuffLen + 1) ** (note the "+ 1" !!!) doubles. The fiber tracking on each half will stop ** at halfBuffLen points. The given seedpoint will be stored in ** buff[0,1,2 + 3*halfBuffLen]. The linear (1-D) indices for the end of ** the first tract half, and the end of the second tract half, will be set in ** *startIdxP and *endIdxP respectively (this does not include a multiply ** by 3) ** ** it is worth pointing out here that internally, all tractography is done ** in gage's world space, regardless of tfx->useIndexSpace. The conversion ** from/to index is space (if tfx->useIndexSpace is non-zero) is only done ** for seedpoints and when fiber vertices are saved out, respectively. ** ** As of Sun Aug 1 20:40:55 CDT 2010 this is just a wrapper around ** _fiberTraceSet; this will probably change in Teem 2.0 */ int tenFiberTraceSet(tenFiberContext *tfx, Nrrd *nfiber, double *buff, unsigned int halfBuffLen, unsigned int *startIdxP, unsigned int *endIdxP, double seed[3]) { static const char me[]="tenFiberTraceSet"; if (_fiberTraceSet(tfx, NULL, nfiber, buff, halfBuffLen, startIdxP, endIdxP, seed)) { biffAddf(TEN, "%s: problem", me); return 1; } return 0; } /* ******** tenFiberTrace ** ** takes a starting position in index or world space, depending on the ** value of tfx->useIndexSpace */ int tenFiberTrace(tenFiberContext *tfx, Nrrd *nfiber, double seed[3]) { static const char me[]="tenFiberTrace"; if (_fiberTraceSet(tfx, NULL, nfiber, NULL, 0, NULL, NULL, seed)) { biffAddf(TEN, "%s: problem computing tract", me); return 1; } return 0; } /* ******** tenFiberDirectionNumber ** ** NOTE: for the time being, a return of zero indicates an error, not ** that we're being clever and detect that the seedpoint is in such ** isotropy that no directions are possible (though such cleverness ** will hopefully be implemented soon) */ unsigned int tenFiberDirectionNumber(tenFiberContext *tfx, double seed[3]) { static const char me[]="tenFiberDirectionNumber"; unsigned int ret; if (!(tfx && seed)) { biffAddf(TEN, "%s: got NULL pointer", me); return 0; } /* HEY: eventually this stuff will be specific to the seedpoint ... */ if (tfx->useDwi) { switch (tfx->fiberType) { case tenDwiFiberType1Evec0: ret = 1; break; case tenDwiFiberType2Evec0: ret = 2; break; case tenDwiFiberType12BlendEvec0: biffAddf(TEN, "%s: sorry, type %s not yet implemented", me, airEnumStr(tenDwiFiberType, tenDwiFiberType12BlendEvec0)); ret = 0; break; default: biffAddf(TEN, "%s: type %d unknown!", me, tfx->fiberType); ret = 0; break; } } else { /* not using DWIs */ ret = 1; } return ret; } /* ******** tenFiberSingleTrace ** ** fiber tracing API that uses new tenFiberSingle, as well as being ** aware of multi-direction tractography ** ** NOTE: this will not try any cleverness in setting "num" ** according to whether the seedpoint is a non-starter */ int tenFiberSingleTrace(tenFiberContext *tfx, tenFiberSingle *tfbs, double seed[3], unsigned int which) { static const char me[]="tenFiberSingleTrace"; if (!(tfx && tfbs && seed)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } /* set input fields in tfbs */ ELL_3V_COPY(tfbs->seedPos, seed); tfbs->dirIdx = which; /* not our job to set tfbx->dirNum ... */ /* set tfbs->nvert */ /* no harm in setting this even when there are no multiple fibers */ tfx->ten2Which = which; if (_fiberTraceSet(tfx, (tfx->fiberProbeItem ? tfbs->nval : NULL), tfbs->nvert, NULL, 0, NULL, NULL, seed)) { biffAddf(TEN, "%s: problem computing tract", me); return 1; } /* set other fields based on tfx output */ tfbs->halfLen[0] = tfx->halfLen[0]; tfbs->halfLen[1] = tfx->halfLen[1]; tfbs->seedIdx = tfx->numSteps[0]; tfbs->stepNum[0] = tfx->numSteps[0]; tfbs->stepNum[1] = tfx->numSteps[1]; tfbs->whyStop[0] = tfx->whyStop[0]; tfbs->whyStop[1] = tfx->whyStop[1]; tfbs->whyNowhere = tfx->whyNowhere; return 0; } typedef union { tenFiberSingle **f; void **v; } fiberunion; /* uses biff */ tenFiberMulti * tenFiberMultiNew() { static const char me[]="tenFiberMultiNew"; tenFiberMulti *ret; fiberunion tfu; ret = AIR_CAST(tenFiberMulti *, calloc(1, sizeof(tenFiberMulti))); if (ret) { ret->fiber = NULL; ret->fiberNum = 0; tfu.f = &(ret->fiber); ret->fiberArr = airArrayNew(tfu.v, &(ret->fiberNum), sizeof(tenFiberSingle), 512 /* incr */); if (ret->fiberArr) { airArrayStructCB(ret->fiberArr, AIR_CAST(void (*)(void *), tenFiberSingleInit), AIR_CAST(void (*)(void *), tenFiberSingleDone)); } else { biffAddf(TEN, "%s: couldn't create airArray", me); return NULL; } } else { biffAddf(TEN, "%s: couldn't create tenFiberMulti", me); return NULL; } return ret; } int tenFiberMultiCheck(airArray *arr) { static const char me[]="tenFiberMultiCheck"; if (!arr) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (sizeof(tenFiberSingle) != arr->unit) { biffAddf(TEN, "%s: given airArray cannot be for fibers", me); return 1; } if (!(AIR_CAST(void (*)(void *), tenFiberSingleInit) == arr->initCB && AIR_CAST(void (*)(void *), tenFiberSingleDone) == arr->doneCB)) { biffAddf(TEN, "%s: given airArray not set up with fiber callbacks", me); return 1; } return 0; } tenFiberMulti * tenFiberMultiNix(tenFiberMulti *tfm) { if (tfm) { airArrayNuke(tfm->fiberArr); airFree(tfm); } return NULL; } /* ******** tenFiberMultiTrace ** ** does tractography for a list of seedpoints ** ** tfml has been returned from tenFiberMultiNew() */ int tenFiberMultiTrace(tenFiberContext *tfx, tenFiberMulti *tfml, const Nrrd *_nseed) { static const char me[]="tenFiberMultiTrace"; airArray *mop; const double *seedData; double seed[3]; unsigned int seedNum, seedIdx, fibrNum, dirNum, dirIdx; Nrrd *nseed; if (!(tfx && tfml && _nseed)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tenFiberMultiCheck(tfml->fiberArr)) { biffAddf(TEN, "%s: problem with fiber array", me); return 1; } if (!(2 == _nseed->dim && 3 == _nseed->axis[0].size)) { biffAddf(TEN, "%s: seed list should be a 2-D (not %u-D) " "3-by-X (not %u-by-X) array", me, _nseed->dim, AIR_CAST(unsigned int, _nseed->axis[0].size)); return 1; } mop = airMopNew(); seedNum = _nseed->axis[1].size; if (nrrdTypeDouble == _nseed->type) { seedData = AIR_CAST(const double *, _nseed->data); } else { nseed = nrrdNew(); airMopAdd(mop, nseed, AIR_CAST(airMopper, nrrdNuke), airMopAlways); if (nrrdConvert(nseed, _nseed, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: couldn't convert seed list", me); return 1; } seedData = AIR_CAST(const double *, nseed->data); } /* HEY: the correctness of the use of the airArray here is quite subtle */ fibrNum = 0; for (seedIdx=0; seedIdxverbose > 1) { fprintf(stderr, "%s: dir %u/%u on seed %u/%u; len %u; # %u\n", me, dirIdx, dirNum, seedIdx, seedNum, tfml->fiberArr->len, fibrNum); } /* tfml->fiberArr->len can never be < fibrNum */ if (tfml->fiberArr->len == fibrNum) { airArrayLenIncr(tfml->fiberArr, 1); } ELL_3V_COPY(tfml->fiber[fibrNum].seedPos, seedData + 3*seedIdx); tfml->fiber[fibrNum].dirIdx = dirIdx; tfml->fiber[fibrNum].dirNum = dirNum; ELL_3V_COPY(seed, seedData + 3*seedIdx); if (tenFiberSingleTrace(tfx, &(tfml->fiber[fibrNum]), seed, dirIdx)) { biffAddf(TEN, "%s: trouble on seed (%g,%g,%g) %u/%u, dir %u/%u", me, seed[0], seed[1], seed[2], seedIdx, seedNum, dirIdx, dirNum); return 1; } if (tfx->verbose) { if (tenFiberStopUnknown == tfml->fiber[fibrNum].whyNowhere) { fprintf(stderr, "%s: (%g,%g,%g) ->\n" " steps = %u,%u; len = %g,%g; whyStop = %s,%s\n", me, seed[0], seed[1], seed[2], tfml->fiber[fibrNum].stepNum[0], tfml->fiber[fibrNum].stepNum[1], tfml->fiber[fibrNum].halfLen[0], tfml->fiber[fibrNum].halfLen[1], airEnumStr(tenFiberStop, tfml->fiber[fibrNum].whyStop[0]), airEnumStr(tenFiberStop, tfml->fiber[fibrNum].whyStop[1])); } else { fprintf(stderr, "%s: (%g,%g,%g) -> whyNowhere: %s\n", me, seed[0], seed[1], seed[2], airEnumStr(tenFiberStop, tfml->fiber[fibrNum].whyNowhere)); } } fibrNum++; } } /* if the airArray got to be its length only because of the work above, then the following will be a no-op. Otherwise, via the callbacks, it will clear out the tenFiberSingle's that we didn't create here */ airArrayLenSet(tfml->fiberArr, fibrNum); airMopOkay(mop); return 0; } static int _fiberMultiExtract(tenFiberContext *tfx, Nrrd *nval, limnPolyData *lpld, tenFiberMulti *tfml) { static const char me[]="_fiberMultiExtract"; unsigned int seedIdx, vertTotalNum, fiberNum, fiberIdx, vertTotalIdx, pansLen, pvNum; double *valOut; if (!(tfx && (lpld || nval) && tfml)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tenFiberMultiCheck(tfml->fiberArr)) { biffAddf(TEN, "%s: problem with fiber array", me); return 1; } if (nval) { if (!tfx->fiberProbeItem) { biffAddf(TEN, "%s: want probed values but no item set", me); return 1; } pansLen = gageAnswerLength(tfx->gtx, tfx->pvl, tfx->fiberProbeItem); } else { pansLen = 0; } /* fprintf(stderr, "!%s: =========================== \n", me); fprintf(stderr, "!%s: \n", me); fprintf(stderr, "!%s: item %d -> pansLen = %u\n", me, tfx->fiberProbeItem, pansLen); fprintf(stderr, "!%s: \n", me); fprintf(stderr, "!%s: =========================== \n", me); */ /* we have to count the real fibers that went somewhere, excluding fibers that went nowhere (counted in tfml->fiberNum) */ vertTotalNum = 0; fiberNum = 0; pvNum = 0; for (seedIdx=0; seedIdxfiberArr->len; seedIdx++) { tenFiberSingle *tfs; tfs = tfml->fiber + seedIdx; if (!(tenFiberStopUnknown == tfs->whyNowhere)) { continue; } if (nval) { if (tfs->nval) { if (!(2 == tfs->nval->dim && pansLen == tfs->nval->axis[0].size && tfs->nvert->axis[1].size == tfs->nval->axis[1].size)) { biffAddf(TEN, "%s: fiber[%u]->nval seems wrong", me, seedIdx); return 1; } pvNum++; } } vertTotalNum += tfs->nvert->axis[1].size; fiberNum++; } if (nval && pvNum != fiberNum) { biffAddf(TEN, "%s: pvNum %u != fiberNum %u", me, pvNum, fiberNum); return 1; } if (nval) { if (nrrdMaybeAlloc_va(nval, nrrdTypeDouble, 2, AIR_CAST(size_t, pansLen), AIR_CAST(size_t, vertTotalNum))) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); return 1; } valOut = AIR_CAST(double *, nval->data); } else { valOut = NULL; } if (lpld) { if (limnPolyDataAlloc(lpld, 0, /* no extra per-vertex info */ vertTotalNum, vertTotalNum, fiberNum)) { biffMovef(TEN, LIMN, "%s: couldn't allocate output", me); return 1; } } fiberIdx = 0; vertTotalIdx = 0; for (seedIdx=0; seedIdxfiberArr->len; seedIdx++) { double *vert, *pans; unsigned int vertIdx, vertNum; tenFiberSingle *tfs; tfs = tfml->fiber + seedIdx; if (!(tenFiberStopUnknown == tfs->whyNowhere)) { continue; } vertNum = tfs->nvert->axis[1].size; pans = (nval ? AIR_CAST(double*, tfs->nval->data) : NULL); vert = (lpld ? AIR_CAST(double*, tfs->nvert->data) : NULL); for (vertIdx=0; vertIdxxyzw + 4*vertTotalIdx, float, vert + 3*vertIdx); (lpld->xyzw + 4*vertTotalIdx)[3] = 1.0; lpld->indx[vertTotalIdx] = vertTotalIdx; } if (nval) { /* HEY speed up memcpy */ memcpy(valOut + pansLen*vertTotalIdx, pans + pansLen*vertIdx, pansLen*sizeof(double)); } vertTotalIdx++; } if (lpld) { lpld->type[fiberIdx] = limnPrimitiveLineStrip; lpld->icnt[fiberIdx] = vertNum; } fiberIdx++; } return 0; } /* ******** tenFiberMultiPolyData ** ** converts tenFiberMulti to polydata. ** ** currently the tenFiberContext *tfx arg is not used, but it will ** probably be needed in the future as the way that parameters to the ** polydata creation process are passed. */ int tenFiberMultiPolyData(tenFiberContext *tfx, limnPolyData *lpld, tenFiberMulti *tfml) { static const char me[]="tenFiberMultiPolyData"; if (_fiberMultiExtract(tfx, NULL, lpld, tfml)) { biffAddf(TEN, "%s: problem", me); return 1; } return 0; } int tenFiberMultiProbeVals(tenFiberContext *tfx, Nrrd *nval, tenFiberMulti *tfml) { static const char me[]="tenFiberMultiProbeVals"; if (_fiberMultiExtract(tfx, nval, NULL, tfml)) { biffAddf(TEN, "%s: problem", me); return 1; } return 0; } teem-1.11.0~svn6057/src/ten/tendEvalmult.c0000664000175000017500000000517212165631065020004 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Modify shape by multiplying eigenvalues by a constant" static const char *_tend_evalmultInfoL = (INFO ". The orientation of the tensor is unchanged."); int tend_evalmultMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS; float val; hestOptAdd(&hopt, "v", "value", airTypeFloat, 1, 1, &val, NULL, "Value to multiply eigenvalues by"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_evalmultInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenEigenvalueMultiply(nout, nin, val)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(evalmult, INFO); teem-1.11.0~svn6057/src/ten/tenMacros.h0000664000175000017500000002207112165631065017275 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TENMACROS_HAS_BEEN_INCLUDED #define TENMACROS_HAS_BEEN_INCLUDED #ifdef __cplusplus extern "C" { #endif /* ******** TEN_T2M, TEN_M2T ** ** for going between 7-element list and 9-element matrix ** representations of a symmetric tensor ** ** the ordering of the tensor elements is assumed to be: ** ** threshold 0 ** Dxx Dxy Dxz 1 2 3 ** Dxy Dyy Dyz = (2) 4 5 ** Dxz Dyz Dzz (3) (5) 6 ** ** As in ell, the matrix ordering is given by: ** ** 0 1 2 ** 3 4 5 ** 6 7 8 ** ** Note that TEN_M2T does NOT set the threshold element (index 0), ** and that the threshold value plays no role in TEN_T2M. */ #define TEN_T2M(m, t) ( \ (m)[0] = (t)[1], (m)[1] = (t)[2], (m)[2] = (t)[3], \ (m)[3] = (t)[2], (m)[4] = (t)[4], (m)[5] = (t)[5], \ (m)[6] = (t)[3], (m)[7] = (t)[5], (m)[8] = (t)[6] ) /* Tue Feb 19 16:36:53 GMT 2008: trying averaging the off-diagonal elements to make better sense of matrices which aren't quite symmetric */ #define TEN_M2T(t, m) ( \ (t)[1] = (m)[0], \ (t)[2] = ((m)[1]+(m)[3])/2.0, \ (t)[3] = ((m)[2]+(m)[6])/2.0, \ (t)[4] = (m)[4], \ (t)[5] = ((m)[5]+(m)[7])/2.0, \ (t)[6] = (m)[8] ) #define TEN_M2T_TT(t, TT, m) ( \ (t)[1] = AIR_CAST(TT, (m)[0]), \ (t)[2] = AIR_CAST(TT, ((m)[1]+(m)[3])/2.0), \ (t)[3] = AIR_CAST(TT, ((m)[2]+(m)[6])/2.0), \ (t)[4] = AIR_CAST(TT, (m)[4]), \ (t)[5] = AIR_CAST(TT, ((m)[5]+(m)[7])/2.0), \ (t)[6] = AIR_CAST(TT, (m)[8])) #define TEN_TV_MUL(v2, t, v1) \ ((v2)[0] = (t)[1]*(v1)[0] + (t)[2]*(v1)[1] + (t)[3]*(v1)[2], \ (v2)[1] = (t)[2]*(v1)[0] + (t)[4]*(v1)[1] + (t)[5]*(v1)[2], \ (v2)[2] = (t)[3]*(v1)[0] + (t)[5]*(v1)[1] + (t)[6]*(v1)[2]) #define TEN_T_EXISTS(t) ( \ AIR_EXISTS((t)[0]) \ && AIR_EXISTS((t)[1]) \ && AIR_EXISTS((t)[2]) \ && AIR_EXISTS((t)[3]) \ && AIR_EXISTS((t)[4]) \ && AIR_EXISTS((t)[5]) \ && AIR_EXISTS((t)[6]) ) #define TEN_T_SET(t, conf, a, b, c, d, e, f) ( \ (t)[0] = (conf), \ (t)[1] = (a), (t)[2] = (b), (t)[3] = (c), \ (t)[4] = (d), (t)[5] = (e), \ (t)[6] = (f) ) #define TEN_T_SET_TT(t, TT, conf, a, b, c, d, e, f) ( \ (t)[0] = AIR_CAST(TT, (conf)), \ (t)[1] = AIR_CAST(TT, (a)), \ (t)[2] = AIR_CAST(TT, (b)), \ (t)[3] = AIR_CAST(TT, (c)), \ (t)[4] = AIR_CAST(TT, (d)), \ (t)[5] = AIR_CAST(TT, (e)), \ (t)[6] = AIR_CAST(TT, (f))) #define TEN_T_COPY(d, s) ( \ (d)[0] = (s)[0], \ (d)[1] = (s)[1], \ (d)[2] = (s)[2], \ (d)[3] = (s)[3], \ (d)[4] = (s)[4], \ (d)[5] = (s)[5], \ (d)[6] = (s)[6] ) #define TEN_T_COPY_TT(d, TT, s) ( \ (d)[0] = AIR_CAST(TT, (s)[0]), \ (d)[1] = AIR_CAST(TT, (s)[1]), \ (d)[2] = AIR_CAST(TT, (s)[2]), \ (d)[3] = AIR_CAST(TT, (s)[3]), \ (d)[4] = AIR_CAST(TT, (s)[4]), \ (d)[5] = AIR_CAST(TT, (s)[5]), \ (d)[6] = AIR_CAST(TT, (s)[6]) ) #define TEN_T_DET(t) ( \ (t)[1]*((t)[4]*(t)[6] - (t)[5]*(t)[5]) \ + (t)[2]*((t)[5]*(t)[3] - (t)[2]*(t)[6]) \ + (t)[3]*((t)[2]*(t)[5] - (t)[3]*(t)[4])) #define TEN_T_DET_XY(t) ( (t)[1]*(t)[4] - (t)[2]*(t)[2] ) #define TEN_T_DET_XZ(t) ( (t)[1]*(t)[6] - (t)[3]*(t)[3] ) #define TEN_T_DET_YZ(t) ( (t)[4]*(t)[6] - (t)[5]*(t)[5] ) #define TEN_T_TRACE(t) ((t)[1] + (t)[4] + (t)[6]) #define TEN_T_INV(i, t, det) \ ((det) = TEN_T_DET(t), \ (i)[0] = (t)[0], \ (i)[1] = _ELL_2M_DET((t)[4],(t)[5],(t)[5],(t)[6])/(det), \ (i)[2] = -_ELL_2M_DET((t)[2],(t)[5],(t)[3],(t)[6])/(det), \ (i)[3] = _ELL_2M_DET((t)[2],(t)[4],(t)[3],(t)[5])/(det), \ (i)[4] = _ELL_2M_DET((t)[1],(t)[3],(t)[3],(t)[6])/(det), \ (i)[5] = -_ELL_2M_DET((t)[1],(t)[2],(t)[3],(t)[5])/(det), \ (i)[6] = _ELL_2M_DET((t)[1],(t)[2],(t)[2],(t)[4])/(det)) #define TEN_T_DOT(A, B) ( \ (A)[1]*(B)[1] + 2*(A)[2]*(B)[2] + 2*(A)[3]*(B)[3] \ + (A)[4]*(B)[4] + 2*(A)[5]*(B)[5] \ + (A)[6]*(B)[6] ) #define TEN_T_NORM(A) (sqrt(TEN_T_DOT(A,A))) #define TEN_T_ADD(a, b, c) ( \ (a)[0] = ((b)[0] + (c)[0])/2.0, \ (a)[1] = (b)[1] + (c)[1], \ (a)[2] = (b)[2] + (c)[2], \ (a)[3] = (b)[3] + (c)[3], \ (a)[4] = (b)[4] + (c)[4], \ (a)[5] = (b)[5] + (c)[5], \ (a)[6] = (b)[6] + (c)[6]) #define TEN_T_SUB(a, b, c) ( \ (a)[0] = ((b)[0] + (c)[0])/2.0, \ (a)[1] = (b)[1] - (c)[1], \ (a)[2] = (b)[2] - (c)[2], \ (a)[3] = (b)[3] - (c)[3], \ (a)[4] = (b)[4] - (c)[4], \ (a)[5] = (b)[5] - (c)[5], \ (a)[6] = (b)[6] - (c)[6]) #define TEN_T_AFFINE(C, i, x, I, A, B) ( \ (C)[0] = AIR_AFFINE((i), (x), (I), (A)[0], (B)[0]), \ (C)[1] = AIR_AFFINE((i), (x), (I), (A)[1], (B)[1]), \ (C)[2] = AIR_AFFINE((i), (x), (I), (A)[2], (B)[2]), \ (C)[3] = AIR_AFFINE((i), (x), (I), (A)[3], (B)[3]), \ (C)[4] = AIR_AFFINE((i), (x), (I), (A)[4], (B)[4]), \ (C)[5] = AIR_AFFINE((i), (x), (I), (A)[5], (B)[5]), \ (C)[6] = AIR_AFFINE((i), (x), (I), (A)[6], (B)[6])) #define TEN_T_LERP(c, w, a, b) ( \ (c)[0] = AIR_LERP((w), (a)[0], (b)[0]), \ (c)[1] = AIR_LERP((w), (a)[1], (b)[1]), \ (c)[2] = AIR_LERP((w), (a)[2], (b)[2]), \ (c)[3] = AIR_LERP((w), (a)[3], (b)[3]), \ (c)[4] = AIR_LERP((w), (a)[4], (b)[4]), \ (c)[5] = AIR_LERP((w), (a)[5], (b)[5]), \ (c)[6] = AIR_LERP((w), (a)[6], (b)[6])) #define TEN_T_SCALE(a, s, b) ( \ (a)[0] = (b)[0], \ (a)[1] = (s)*(b)[1], \ (a)[2] = (s)*(b)[2], \ (a)[3] = (s)*(b)[3], \ (a)[4] = (s)*(b)[4], \ (a)[5] = (s)*(b)[5], \ (a)[6] = (s)*(b)[6]) #define TEN_T_INCR(a, b) ( \ (a)[0] = (b)[0], \ (a)[1] += (b)[1], \ (a)[2] += (b)[2], \ (a)[3] += (b)[3], \ (a)[4] += (b)[4], \ (a)[5] += (b)[5], \ (a)[6] += (b)[6]) #define TEN_T_SCALE_INCR(a, s, b) ( \ (a)[0] = (b)[0], \ (a)[1] += (s)*(b)[1], \ (a)[2] += (s)*(b)[2], \ (a)[3] += (s)*(b)[3], \ (a)[4] += (s)*(b)[4], \ (a)[5] += (s)*(b)[5], \ (a)[6] += (s)*(b)[6]) #define TEN_T_SCALE_INCR2(a, s, b, t, c) ( \ (a)[0] = AIR_MIN((b)[0], (c)[0]), \ (a)[1] += (s)*(b)[1] + (t)*(c)[1], \ (a)[2] += (s)*(b)[2] + (t)*(c)[2], \ (a)[3] += (s)*(b)[3] + (t)*(c)[3], \ (a)[4] += (s)*(b)[4] + (t)*(c)[4], \ (a)[5] += (s)*(b)[5] + (t)*(c)[5], \ (a)[6] += (s)*(b)[6] + (t)*(c)[6]) #define TEN_T_SCALE_ADD2(a, s, b, t, c) ( \ (a)[0] = AIR_MIN((b)[0], (c)[0]), \ (a)[1] = (s)*(b)[1] + (t)*(c)[1], \ (a)[2] = (s)*(b)[2] + (t)*(c)[2], \ (a)[3] = (s)*(b)[3] + (t)*(c)[3], \ (a)[4] = (s)*(b)[4] + (t)*(c)[4], \ (a)[5] = (s)*(b)[5] + (t)*(c)[5], \ (a)[6] = (s)*(b)[6] + (t)*(c)[6]) #define TEN_T3V_MUL(b, t, a) ( \ (b)[0] = (t)[1]*(a)[0] + (t)[2]*(a)[1] + (t)[3]*(a)[2], \ (b)[1] = (t)[2]*(a)[0] + (t)[4]*(a)[1] + (t)[5]*(a)[2], \ (b)[2] = (t)[3]*(a)[0] + (t)[5]*(a)[1] + (t)[6]*(a)[2]) #define TEN_T3V_OUTER(t, a) ( \ (t)[0] = 1.0, \ (t)[1] = (a)[0]*(a)[0], (t)[2] = (a)[0]*(a)[1], (t)[3] = (a)[0]*(a)[2], \ (t)[4] = (a)[1]*(a)[1], (t)[5] = (a)[1]*(a)[2], \ (t)[6] = (a)[2]*(a)[2]) #define TEN_T3V_OUTER_INCR(t, a) ( \ (t)[1] += (a)[0]*(a)[0], (t)[2] += (a)[0]*(a)[1], (t)[3] += (a)[0]*(a)[2], \ (t)[4] += (a)[1]*(a)[1], (t)[5] += (a)[1]*(a)[2], \ (t)[6] += (a)[2]*(a)[2]) /* NOTE: never looks at (t)[0] */ #define TEN_T3V_CONTR(t, v) ( \ (v)[0]*(t)[1]*(v)[0] + (v)[0]*(t)[2]*(v)[1] + (v)[0]*(t)[3]*(v)[2] \ + (v)[1]*(t)[2]*(v)[0] + (v)[1]*(t)[4]*(v)[1] + (v)[1]*(t)[5]*(v)[2] \ + (v)[2]*(t)[3]*(v)[0] + (v)[2]*(t)[5]*(v)[1] + (v)[2]*(t)[6]*(v)[2]) #ifdef __cplusplus } #endif #endif /* TENMACROS_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/ten/tendSatin.c0000664000175000017500000003050212165631065017264 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Generate a pretty synthetic DT volume" static const char *_tend_satinInfoL = (INFO ". The surface of a sphere or torus is covered with either linear or " "planar anisotropic tensors, or somewhere in between."); void tend_satinSphereEigen(float *eval, float *evec, float x, float y, float z, float parm, float mina, float maxa, float thick, float bnd, float evsc) { float aniso, bound1, bound2, r, norm, tmp[3]; r = AIR_CAST(float, sqrt(x*x + y*y + z*z)); /* 1 on inside, 0 on outside */ bound1 = AIR_CAST(float, 0.5 - 0.5*airErf((r-0.9)/(bnd + 0.0001))); /* other way around */ bound2 = AIR_CAST(float, 0.5 - 0.5*airErf((0.9-thick-r)/(bnd + 0.0001))); aniso = AIR_CAST(float, AIR_AFFINE(0.0, AIR_MIN(bound1, bound2), 1.0, mina, maxa)); ELL_3V_SET_TT(eval, float, AIR_LERP(aniso, 1.0/3.0, AIR_AFFINE(0.0, parm, 2.0, 1.0, 0.0)), AIR_LERP(aniso, 1.0/3.0, AIR_AFFINE(0.0, parm, 2.0, 0.0, 1.0)), AIR_LERP(aniso, 1.0/3.0, 0)); ELL_3V_SCALE(eval, evsc, eval); /* v0: looking down positive Z, points counter clockwise */ if (x || y) { ELL_3V_SET(evec + 3*0, y, -x, 0); ELL_3V_NORM_TT(evec + 3*0, float, evec + 3*0, norm); /* v1: points towards pole at positive Z */ ELL_3V_SET(tmp, -x, -y, -z); ELL_3V_NORM_TT(tmp, float, tmp, norm); ELL_3V_CROSS(evec + 3*1, tmp, evec + 3*0); /* v2: v0 x v1 */ ELL_3V_CROSS(evec + 3*2, evec + 3*0, evec + 3*1); } else { /* not optimal, but at least it won't show up in glyph visualizations */ ELL_3M_IDENTITY_SET(evec); } return; } void tend_satinTorusEigen(float *eval, float *evec, float x, float y, float z, float parm, float mina, float maxa, float thick, float bnd, float evsc) { float bound, R, r, norm, out[3], up[3], aniso; thick *= 2; R = AIR_CAST(float, sqrt(x*x + y*y)); r = AIR_CAST(float, sqrt((R-1)*(R-1) + z*z)); /* 1 on inside, 0 on outside */ bound = AIR_CAST(float, 0.5 - 0.5*airErf((r-thick)/(bnd + 0.0001))); aniso = AIR_CAST(float, AIR_AFFINE(0, bound, 1, mina, maxa)); ELL_3V_SET_TT(eval, float, AIR_LERP(aniso, 1.0/3.0, AIR_AFFINE(0.0, parm, 2.0, 1.0, 0.0)), AIR_LERP(aniso, 1.0/3.0, AIR_AFFINE(0.0, parm, 2.0, 0.0, 1.0)), AIR_LERP(aniso, 1.0/3.0, 0)); ELL_3V_SCALE(eval, evsc, eval); ELL_3V_SET(up, 0, 0, 1); if (x || y) { /* v0: looking down positive Z, points counter clockwise */ ELL_3V_SET(evec + 3*0, y, -x, 0); ELL_3V_NORM_TT(evec + 3*0, float, evec + 3*0, norm); /* v2: points into core of torus */ /* out: points away from (x,y)=(0,0) */ ELL_3V_SET(out, x, y, 0); ELL_3V_NORM_TT(out, float, out, norm); ELL_3V_SCALE_ADD2(evec + 3*2, -z, up, (1-R), out); ELL_3V_NORM_TT(evec + 3*2, float, evec + 3*2, norm); /* v1: looking at right half of cross-section, points counter clockwise */ ELL_3V_CROSS(evec + 3*1, evec + 3*0, evec + 3*2); } else { /* not optimal, but at least it won't show up in glyph visualizations */ ELL_3M_IDENTITY_SET(evec); } return; } int tend_satinGen(Nrrd *nout, float parm, float mina, float maxa, int wsize, float thick, float scaling, float bnd, float bndRm, float evsc, int torus) { static const char me[]="tend_satinGen"; char buff[AIR_STRLEN_SMALL]; Nrrd *nconf, *neval, *nevec; float *conf, *eval, *evec; size_t xi, yi, zi, size[3]; float x, y, z, min[3], max[3]; if (torus) { ELL_3V_SET(size, 2*wsize, 2*wsize, wsize); ELL_3V_SET(min, -2, -2, -1); ELL_3V_SET(max, 2, 2, 1); } else { ELL_3V_SET(size, wsize, wsize, wsize); ELL_3V_SET(min, -1, -1, -1); ELL_3V_SET(max, 1, 1, 1); } if (nrrdMaybeAlloc_va(nconf=nrrdNew(), nrrdTypeFloat, 3, size[0], size[1], size[2]) || nrrdMaybeAlloc_va(neval=nrrdNew(), nrrdTypeFloat, 4, AIR_CAST(size_t, 3), size[0], size[1], size[2]) || nrrdMaybeAlloc_va(nevec=nrrdNew(), nrrdTypeFloat, 4, AIR_CAST(size_t, 9), size[0], size[1], size[2])) { biffMovef(TEN, NRRD, "%s: trouble allocating temp nrrds", me); return 1; } conf = (float *)nconf->data; eval = (float *)neval->data; evec = (float *)nevec->data; for (zi=0; zicontent = airStrdup(buff); return 0; } int tend_satinMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int wsize, torus; float parm, maxa, mina, thick, scaling, bnd, bndRm, evsc; Nrrd *nout; char *outS; gageShape *shape; double spd[4][4], orig[4]; hestOptAdd(&hopt, "t", "do torus", airTypeInt, 0, 0, &torus, NULL, "generate a torus dataset, instead of the default spherical"); hestOptAdd(&hopt, "p", "aniso parm", airTypeFloat, 1, 1, &parm, NULL, "anisotropy parameter. 0.0 for one direction of linear (along " "the equator for spheres, or along the larger circumference for " "toruses), 1.0 for planar, 2.0 for the other direction of linear " "(from pole to pole for spheres, or along the smaller " "circumference for toruses)"); hestOptAdd(&hopt, "max", "max ca1", airTypeFloat, 1, 1, &maxa, "1.0", "maximum anisotropy in dataset, according to the \"ca1\" " "anisotropy metric. \"1.0\" means " "completely linear or completely planar anisotropy"); hestOptAdd(&hopt, "min", "min ca1", airTypeFloat, 1, 1, &mina, "0.0", "minimum anisotropy in dataset"); hestOptAdd(&hopt, "b", "boundary", airTypeFloat, 1, 1, &bnd, "0.05", "parameter governing how fuzzy the boundary between high and " "low anisotropy is. Use \"-b 0\" for no fuzziness"); hestOptAdd(&hopt, "br", "ramp", airTypeFloat, 1, 1, &bndRm, "0.0", "how much to ramp upeffective \"b\" along Y axis. " "Use \"-b 0\" for no such ramping."); hestOptAdd(&hopt, "th", "thickness", airTypeFloat, 1, 1, &thick, "0.3", "parameter governing how thick region of high anisotropy is"); hestOptAdd(&hopt, "scl", "scaling", airTypeFloat, 1, 1, &scaling, "1.0", "scaling on size of sphere or torus within volume; lowering " "this below default 1.0 produces more background margin"); hestOptAdd(&hopt, "evsc", "eval scale", airTypeFloat, 1, 1, &evsc, "1.0", "scaling of eigenvalues"); hestOptAdd(&hopt, "s", "size", airTypeInt, 1, 1, &wsize, "32", "dimensions of output volume. For size N, the output is " "N\tx\tN\tx\tN for spheres, and 2N\tx\t2N\tx\tN for toruses"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output filename"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_satinInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tend_satinGen(nout, parm, mina, maxa, wsize, thick, scaling, bnd, bndRm, evsc, torus)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making volume:\n%s\n", me, err); airMopError(mop); return 1; } /* use gageShape to determine orientation info */ nrrdAxisInfoSet_va(nout, nrrdAxisInfoCenter, nrrdCenterUnknown, nrrdCenterCell, nrrdCenterCell, nrrdCenterCell); shape = gageShapeNew(); airMopAdd(mop, shape, (airMopper)gageShapeNix, airMopAlways); /* this is a weird mix of new and legacy code. At some point prior to Wed May 27 19:23:55 CDT 2009, it was okay to pass in a volume to gageShapeSet that had absolutely no notion of spacing or orientation. Then gageShapeSet was used to get a plausible set of space directions and space origin. Now, we're setting some spacings, so that gageShapeSet can do its thing, then (below) nan-ing out those spacings so that the nrrd is self-consistent */ nout->axis[1].spacing = 1.0; nout->axis[2].spacing = 1.0; nout->axis[3].spacing = 1.0; if (gageShapeSet(shape, nout, tenGageKind->baseDim)) { airMopAdd(mop, err=biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble doing shape:\n%s\n", me, err); airMopError(mop); return 1; } /* the ItoW is a 4x4 matrix, but we really only care about the first three rows */ ELL_4V_SET(spd[0], AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); ELL_4MV_COL0_GET(spd[1], shape->ItoW); ELL_4V_SCALE(spd[1], 32, spd[1]); ELL_4MV_COL1_GET(spd[2], shape->ItoW); ELL_4V_SCALE(spd[2], 32, spd[2]); ELL_4MV_COL2_GET(spd[3], shape->ItoW); ELL_4V_SCALE(spd[3], 32, spd[3]); ELL_4MV_COL3_GET(orig, shape->ItoW); ELL_4V_SCALE(orig, 32, orig); nrrdSpaceSet(nout, nrrdSpaceRightAnteriorSuperior); nrrdSpaceOriginSet(nout, orig); nrrdAxisInfoSet_va(nout, nrrdAxisInfoSpaceDirection, spd[0], spd[1], spd[2], spd[3]); nout->axis[1].spacing = AIR_NAN; nout->axis[2].spacing = AIR_NAN; nout->axis[3].spacing = AIR_NAN; nrrdAxisInfoSet_va(nout, nrrdAxisInfoKind, nrrdKind3DMaskedSymMatrix, nrrdKindSpace, nrrdKindSpace, nrrdKindSpace); nout->measurementFrame[0][0] = 1; nout->measurementFrame[1][0] = 0; nout->measurementFrame[2][0] = 0; nout->measurementFrame[0][1] = 0; nout->measurementFrame[1][1] = 1; nout->measurementFrame[2][1] = 0; nout->measurementFrame[0][2] = 0; nout->measurementFrame[1][2] = 0; nout->measurementFrame[2][2] = 1; if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } /* TEND_CMD(satin, INFO); */ unrrduCmd tend_satinCmd = { "satin", INFO, tend_satinMain, AIR_FALSE }; teem-1.11.0~svn6057/src/ten/tendHelix.c0000664000175000017500000002604112165631065017262 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Generate twisting helical tensor field" static const char *_tend_helixInfoL = (INFO ". The main utility of such a field is to debug handling of coordinate " "systems in tensor field visualization. The \"space directions\" and " "\"space origin\" fields of the NRRD header determines the mapping from " "coordinates in the index space of the image to coordinates in the " "world space in which the image is " "sampled. The \"measurement frame\" field determines the mapping from " "the coordinates of the tensor itself, to coordinates of the world space. " "When these are correctly handled, the " "region of high anisotropy is a right-handed helix (same as DNA). " "Using differing axes sizes (via \"-s\") helps make sure that the " "raster ordering of axes is correct. In addition, the tensors twist " "relative to the helix, which exposes handling of the measurement frame. " "If you trace paths guided by the principal eigenvector of the tensors, " "along the surface of the helical cylinder, you get another " "right-handed helix, as if the the tensor field is modeling the result " "if twisting a set of fibers into single-stranded helical bundle. "); void tend_helixDoit(Nrrd *nout, double bnd, double orig[3], double i2w[9], double mf[9], double r, double R, double S, double angle, int incrtwist, double ev[3], double bgEval, int verbose) { int sx, sy, sz, xi, yi, zi; double th, t0, t1, t2, t3, v1, v2, wpos[3], vpos[3], mfT[9], W2H[9], H2W[9], H2C[9], C2H[9], fv[3], rv[3], uv[3], mA[9], mB[9], inside, tmp[3], len; float *out; sx = nout->axis[1].size; sy = nout->axis[2].size; sz = nout->axis[3].size; out = (float*)nout->data; ELL_3M_TRANSPOSE(mfT, mf); for (zi=0; zi vpos[2]) { t0 = th - AIR_PI; t3 = th; } else { t0 = th; t3 = th + AIR_PI; } t1 = RR*t0 + CC*t3; t2 = CC*t0 + RR*t3; v1 = VAL(t1); v2 = VAL(t2); while ( t3-t0 > 0.000001*(AIR_ABS(t1)+AIR_ABS(t2)) ) { if (v1 < v2) { SHIFT3(t3, t2, t1, CC*t0 + RR*t2); SHIFT2(v2, v1, VAL(t1)); } else { SHIFT3(t0, t1, t2, RR*t1 + CC*t3); SHIFT2(v1, v2, VAL(t2)); } } /* t1 (and t2) are now the th for which the point on the helix (R*cos(th), R*sin(th), S*(th)/(2*AIR_PI)) is closest to vpos */ WPOS(wpos, t1); ELL_3V_SUB(wpos, vpos, wpos); ELL_3V_SET(fv, -R*sin(t1), R*cos(t1), S/AIR_PI); /* helix tangent */ ELL_3V_NORM(fv, fv, len); ELL_3V_COPY(rv, wpos); ELL_3V_NORM(rv, rv, len); len = ELL_3V_DOT(rv, fv); ELL_3V_SCALE(tmp, -len, fv); ELL_3V_ADD2(rv, rv, tmp); ELL_3V_NORM(rv, rv, len); /* rv now normal to helix, closest to pointing to vpos */ ELL_3V_CROSS(uv, rv, fv); ELL_3V_NORM(uv, uv, len); /* (rv,fv,uv) now right-handed frame */ ELL_3MV_ROW0_SET(W2H, uv); /* as is (uv,rv,fv) */ ELL_3MV_ROW1_SET(W2H, rv); ELL_3MV_ROW2_SET(W2H, fv); ELL_3M_TRANSPOSE(H2W, W2H); inside = 0.5 - 0.5*airErf((ELL_3V_LEN(wpos)-r)/(bnd + 0.0001)); if (incrtwist) { th = angle*ELL_3V_LEN(wpos)/r; } else { th = angle; } ELL_3M_ROTATE_Y_SET(H2C, th); ELL_3M_TRANSPOSE(C2H, H2C); ELL_3M_SCALE_SET(mA, AIR_LERP(inside, bgEval, ev[1]), AIR_LERP(inside, bgEval, ev[2]), AIR_LERP(inside, bgEval, ev[0])); ELL_3M_MUL(mB, mA, H2C); ELL_3M_MUL(mA, mB, W2H); ELL_3M_MUL(mB, mA, mf); ELL_3M_MUL(mA, C2H, mB); ELL_3M_MUL(mB, H2W, mA); ELL_3M_MUL(mA, mfT, mB); TEN_M2T_TT(out, float, mA); out[0] = 1.0; out += 7; } } } return; } int tend_helixMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int size[3], nit, verbose; Nrrd *nout; double R, r, S, bnd, angle, ev[3], ip[3], iq[4], mp[3], mq[4], tmp[9], orig[3], i2w[9], rot[9], mf[9], spd[4][3], bge; char *outS; hestOptAdd(&hopt, "s", "size", airTypeInt, 3, 3, size, NULL, "sizes along fast, medium, and slow axes of the sampled volume, " "often called \"X\", \"Y\", and \"Z\". It is best to use " "slightly different sizes here, to expose errors in interpreting " "axis ordering (e.g. \"-s 39 40 41\")"); hestOptAdd(&hopt, "ip", "image orientation", airTypeDouble, 3, 3, ip, "0 0 0", "quaternion quotient space orientation of image"); hestOptAdd(&hopt, "mp", "measurement orientation", airTypeDouble, 3, 3, mp, "0 0 0", "quaternion quotient space orientation of measurement frame"); hestOptAdd(&hopt, "b", "boundary", airTypeDouble, 1, 1, &bnd, "10", "parameter governing how fuzzy the boundary between high and " "low anisotropy is. Use \"-b 0\" for no fuzziness"); hestOptAdd(&hopt, "r", "little radius", airTypeDouble, 1, 1, &r, "30", "(minor) radius of cylinder tracing helix"); hestOptAdd(&hopt, "R", "big radius", airTypeDouble, 1, 1, &R, "50", "(major) radius of helical turns"); hestOptAdd(&hopt, "S", "spacing", airTypeDouble, 1, 1, &S, "100", "spacing between turns of helix (along its axis)"); hestOptAdd(&hopt, "a", "angle", airTypeDouble, 1, 1, &angle, "60", "maximal angle of twist of tensors along path. There is no " "twist at helical core of path, and twist increases linearly " "with radius around this path. Positive twist angle with " "positive spacing resulting in a right-handed twist around a " "right-handed helix. "); hestOptAdd(&hopt, "nit", NULL, airTypeInt, 0, 0, &nit, NULL, "changes behavior of twist angle as function of distance from " "center of helical core: instead of increasing linearly as " "describe above, be at a constant angle"); hestOptAdd(&hopt, "ev", "eigenvalues", airTypeDouble, 3, 3, ev, "0.006 0.002 0.001", "eigenvalues of tensors (in order) along direction of coil, " "circumferential around coil, and radial around coil. "); hestOptAdd(&hopt, "bg", "background", airTypeDouble, 1, 1, &bge, "0.5", "eigenvalue of isotropic background"); hestOptAdd(&hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "1", "verbose output"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output file"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_helixInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 4, AIR_CAST(size_t, 7), AIR_CAST(size_t, size[0]), AIR_CAST(size_t, size[1]), AIR_CAST(size_t, size[2]))) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble allocating output:\n%s\n", me, err); airMopError(mop); return 1; } ELL_4V_SET(iq, 1.0, ip[0], ip[1], ip[2]); ell_q_to_3m_d(rot, iq); ELL_3V_SET(orig, -2*R + 2*R/size[0], -2*R + 2*R/size[1], -2*R + 2*R/size[2]); ELL_3M_ZERO_SET(i2w); ELL_3M_DIAG_SET(i2w, 4*R/size[0], 4*R/size[1], 4*R/size[2]); ELL_3MV_MUL(tmp, rot, orig); ELL_3V_COPY(orig, tmp); ELL_3M_MUL(tmp, rot, i2w); ELL_3M_COPY(i2w, tmp); ELL_4V_SET(mq, 1.0, mp[0], mp[1], mp[2]); ell_q_to_3m_d(mf, mq); tend_helixDoit(nout, bnd, orig, i2w, mf, r, R, S, angle*AIR_PI/180, !nit, ev, bge, verbose); nrrdSpaceSet(nout, nrrdSpaceRightAnteriorSuperior); nrrdSpaceOriginSet(nout, orig); ELL_3V_SET(spd[0], AIR_NAN, AIR_NAN, AIR_NAN); ELL_3MV_COL0_GET(spd[1], i2w); ELL_3MV_COL1_GET(spd[2], i2w); ELL_3MV_COL2_GET(spd[3], i2w); nrrdAxisInfoSet_va(nout, nrrdAxisInfoSpaceDirection, spd[0], spd[1], spd[2], spd[3]); nrrdAxisInfoSet_va(nout, nrrdAxisInfoCenter, nrrdCenterUnknown, nrrdCenterCell, nrrdCenterCell, nrrdCenterCell); nrrdAxisInfoSet_va(nout, nrrdAxisInfoKind, nrrdKind3DMaskedSymMatrix, nrrdKindSpace, nrrdKindSpace, nrrdKindSpace); nout->measurementFrame[0][0] = mf[0]; nout->measurementFrame[1][0] = mf[1]; nout->measurementFrame[2][0] = mf[2]; nout->measurementFrame[0][1] = mf[3]; nout->measurementFrame[1][1] = mf[4]; nout->measurementFrame[2][1] = mf[5]; nout->measurementFrame[0][2] = mf[6]; nout->measurementFrame[1][2] = mf[7]; nout->measurementFrame[2][2] = mf[8]; if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(helix, INFO); teem-1.11.0~svn6057/src/ten/modelBall1Cylinder.c0000664000175000017500000000606512165631065021010 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define PARM_NUM 8 static const tenModelParmDesc parmDesc[] = { /* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 1 */ {"diffusivity", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 2 */ {"fraction", 0.0, 1.0, AIR_FALSE, AIR_FALSE, 0}, /* 3 */ {"length", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 4 */ {"radius", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 5 */ {"x", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 0}, /* 6 */ {"y", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 1}, /* 7 */ {"z", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 2} }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; double b0, diffBall, frac, length, radius, vec[3], ten[7], ident[7] = {1, 1, 0, 0, 1, 0, 1}; b0 = parm[0]; diffBall = parm[1]; frac = parm[2]; length = parm[3]; radius = parm[4]; vec[0] = parm[5]; vec[1] = parm[6]; vec[2] = parm[7]; TEN_T3V_OUTER(ten, vec); TEN_T_SCALE_ADD2(ten, length - radius, ten, radius, ident); for (ii=0; iiimgNum; ii++) { double diffCyl, dwiBall, dwiCyl, bb; bb = espec->bval[ii]; diffCyl = TEN_T3V_CONTR(ten, espec->grad + 3*ii); dwiCyl = exp(-bb*diffCyl); dwiBall = exp(-bb*diffBall); dwiSim[ii] = b0*AIR_LERP(frac, dwiBall, dwiCyl); } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { sprintf(str, "(%g) [(1-f) %g + (f=%g) %gX%g (%g,%g,%g)]", parm[0], parm[1], parm[2], parm[3], parm[4], parm[5], parm[6], parm[7]); return str; } _TEN_PARM_ALLOC _TEN_PARM_RAND _TEN_PARM_STEP _TEN_PARM_DIST _TEN_PARM_COPY _TEN_PARM_CONVERT_NOOP _TEN_SQE _TEN_SQE_GRAD_CENTDIFF _TEN_SQE_FIT(tenModelBall1Cylinder) _TEN_NLL _TEN_NLL_GRAD_STUB _TEN_NLL_FIT_STUB tenModel _tenModelBall1Cylinder = { TEN_MODEL_STR_BALL1CYLINDER, _TEN_MODEL_FIELDS }; const tenModel *const tenModelBall1Cylinder = &_tenModelBall1Cylinder; teem-1.11.0~svn6057/src/ten/model1Stick.c0000664000175000017500000000466512165631065017525 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define PARM_NUM 5 static const tenModelParmDesc parmDesc[] = { /* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 1 */ {"diffusivity", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 2 */ {"x", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 0}, /* 3 */ {"y", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 1}, /* 4 */ {"z", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 2} }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; double b0, diff, vec[3]; b0 = parm[0]; diff = parm[1]; vec[0] = parm[2]; vec[1] = parm[3]; vec[2] = parm[4]; for (ii=0; iiimgNum; ii++) { double dot; dot = ELL_3V_DOT(vec, espec->grad + 3*ii); dwiSim[ii] = b0*exp(-espec->bval[ii]*diff*dot*dot); } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { sprintf(str, "(%g) %g (%g,%g,%g)", parm[0], parm[1], parm[2], parm[3], parm[4]); return str; } _TEN_PARM_ALLOC _TEN_PARM_RAND _TEN_PARM_STEP _TEN_PARM_DIST _TEN_PARM_COPY _TEN_PARM_CONVERT_NOOP _TEN_SQE _TEN_SQE_GRAD_CENTDIFF _TEN_SQE_FIT(tenModel1Stick) _TEN_NLL _TEN_NLL_GRAD_STUB _TEN_NLL_FIT_STUB tenModel _tenModel1Stick = { TEN_MODEL_STR_1STICK, _TEN_MODEL_FIELDS }; const tenModel *const tenModel1Stick = &_tenModel1Stick; teem-1.11.0~svn6057/src/ten/miscTen.c0000664000175000017500000002522712165631065016745 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" int tenEvecRGB(Nrrd *nout, const Nrrd *nin, const tenEvecRGBParm *rgbp) { static const char me[]="tenEvecRGB"; size_t size[NRRD_DIM_MAX]; float (*lup)(const void *, size_t), (*ins)(void *, size_t, float); float ten[7], eval[3], evec[9], RGB[3]; size_t II, NN; unsigned char *odataUC; unsigned short *odataUS; if (!(nout && nin)) { biffAddf(TEN, "%s: got NULL pointer (%p,%p)", me, AIR_CAST(void *, nout), AIR_CVOIDP(nin)); return 1; } if (tenEvecRGBParmCheck(rgbp)) { biffAddf(TEN, "%s: RGB parm trouble", me); return 1; } if (!(2 <= nin->dim && 7 == nin->axis[0].size)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: need nin->dim >= 2 (not %u), axis[0].size == 7 " "(not %s)", me, nin->dim, airSprintSize_t(stmp, nin->axis[0].size)); return 1; } nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); size[0] = rgbp->genAlpha ? 4 : 3; if (nrrdMaybeAlloc_nva(nout, (nrrdTypeDefault == rgbp->typeOut ? nin->type : rgbp->typeOut), nin->dim, size)) { biffMovef(TEN, NRRD, "%s: couldn't alloc output", me); return 1; } odataUC = AIR_CAST(unsigned char *, nout->data); odataUS = AIR_CAST(unsigned short *, nout->data); NN = nrrdElementNumber(nin)/7; lup = nrrdFLookup[nin->type]; ins = nrrdFInsert[nout->type]; for (II=0; IIdata, 0 + 7*II), lup(nin->data, 1 + 7*II), lup(nin->data, 2 + 7*II), lup(nin->data, 3 + 7*II), lup(nin->data, 4 + 7*II), lup(nin->data, 5 + 7*II), lup(nin->data, 6 + 7*II)); tenEigensolve_f(eval, evec, ten); tenEvecRGBSingle_f(RGB, ten[0], eval, evec + 3*(rgbp->which), rgbp); switch (nout->type) { case nrrdTypeUChar: odataUC[0 + size[0]*II] = airIndexClamp(0.0, RGB[0], 1.0, 256); odataUC[1 + size[0]*II] = airIndexClamp(0.0, RGB[1], 1.0, 256); odataUC[2 + size[0]*II] = airIndexClamp(0.0, RGB[2], 1.0, 256); if (rgbp->genAlpha) { odataUC[3 + size[0]*II] = 255; } break; case nrrdTypeUShort: odataUS[0 + size[0]*II] = airIndexClamp(0.0, RGB[0], 1.0, 65536); odataUS[1 + size[0]*II] = airIndexClamp(0.0, RGB[1], 1.0, 65536); odataUS[2 + size[0]*II] = airIndexClamp(0.0, RGB[2], 1.0, 65536); if (rgbp->genAlpha) { odataUS[3 + size[0]*II] = 65535; } break; default: ins(nout->data, 0 + size[0]*II, RGB[0]); ins(nout->data, 1 + size[0]*II, RGB[1]); ins(nout->data, 2 + size[0]*II, RGB[2]); if (rgbp->genAlpha) { ins(nout->data, 3 + size[0]*II, 1.0); } break; } } if (nrrdAxisInfoCopy(nout, nin, NULL, (NRRD_AXIS_INFO_SIZE_BIT))) { biffMovef(TEN, NRRD, "%s: couldn't copy axis info", me); return 1; } nout->axis[0].kind = nrrdKind3Color; if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { biffAddf(TEN, "%s:", me); return 1; } return 0; } #define SQR(i) ((i)*(i)) short tenEvqSingle(float vec[3], float scl) { static const char me[]="tenEvqSingle"; float tmp, L1; int mi, bins, base, vi, ui; short ret; ELL_3V_NORM_TT(vec, float, vec, tmp); L1 = AIR_ABS(vec[0]) + AIR_ABS(vec[1]) + AIR_ABS(vec[2]); ELL_3V_SCALE(vec, 1/L1, vec); scl = AIR_CLAMP(0.0f, scl, 1.0f); scl = AIR_CAST(float, pow(scl, 0.75)); mi = airIndex(0.0, scl, 1.0, 6); if (mi) { switch (mi) { case 1: bins = 16; base = 1; break; case 2: bins = 32; base = 1+SQR(16); break; case 3: bins = 48; base = 1+SQR(16)+SQR(32); break; case 4: bins = 64; base = 1+SQR(16)+SQR(32)+SQR(48); break; case 5: bins = 80; base = 1+SQR(16)+SQR(32)+SQR(48)+SQR(64); break; default: fprintf(stderr, "%s: PANIC: mi = %d\n", me, mi); exit(0); } vi = airIndex(-1, vec[0]+vec[1], 1, bins); ui = airIndex(-1, vec[0]-vec[1], 1, bins); ret = vi*bins + ui + base; } else { ret = 0; } return ret; } int tenEvqVolume(Nrrd *nout, const Nrrd *nin, int which, int aniso, int scaleByAniso) { static const char me[]="tenEvqVolume"; int map[3]; short *qdata; const float *tdata; float eval[3], evec[9], an; size_t N, I, sx, sy, sz; if (!(nout && nin)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!(AIR_IN_CL(0, which, 2))) { biffAddf(TEN, "%s: eigenvector index %d not in range [0..2]", me, which); return 1; } if (scaleByAniso) { if (airEnumValCheck(tenAniso, aniso)) { biffAddf(TEN, "%s: anisotropy metric %d not valid", me, aniso); return 1; } } if (tenTensorCheck(nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a valid DT volume", me); return 1; } sx = nin->axis[1].size; sy = nin->axis[2].size; sz = nin->axis[3].size; if (nrrdMaybeAlloc_va(nout, nrrdTypeShort, 3, sx, sy, sz)) { biffMovef(TEN, NRRD, "%s: can't allocate output", me); return 1; } N = sx*sy*sz; tdata = (float *)nin->data; qdata = (short *)nout->data; for (I=0; Iaxis[0].size && 2 == nbmat->dim )) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: need a 6xN 2-D array (not a %s x? %d-D array)", me, airSprintSize_t(stmp, nbmat->axis[0].size), nbmat->dim); return 1; } if (nrrdTypeDefault != type && type != nbmat->type) { biffAddf(TEN, "%s: requested type %s but got type %s", me, airEnumStr(nrrdType, type), airEnumStr(nrrdType, nbmat->type)); return 1; } if (nrrdTypeBlock == nbmat->type) { biffAddf(TEN, "%s: sorry, can't use %s type", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!( minnum <= nbmat->axis[1].size )) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: have only %s B-matrices, need at least %d", me, airSprintSize_t(stmp, nbmat->axis[1].size), minnum); return 1; } return 0; } /* ******** _tenFindValley ** ** This is not a general purpose function, and it will take some ** work to make it that way. ** ** the tweak argument implements a cheesy heuristic: threshold should be ** on low side of histogram valley, since stdev for background is much ** narrower then stdev for brain */ int _tenFindValley(double *valP, const Nrrd *nhist, double tweak, int save) { static const char me[]="_tenFindValley"; double gparm[NRRD_KERNEL_PARMS_NUM], dparm[NRRD_KERNEL_PARMS_NUM]; Nrrd *ntmpA, *ntmpB, *nhistD, *nhistDD; float *hist, *histD, *histDD; airArray *mop; size_t bins, maxbb, bb; NrrdRange *range; /* tenEMBimodalParm *biparm; biparm = tenEMBimodalParmNew(); tenEMBimodal(biparm, nhist); biparm = tenEMBimodalParmNix(biparm); */ mop = airMopNew(); airMopAdd(mop, ntmpA=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmpB=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nhistD=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nhistDD=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); bins = nhist->axis[0].size; gparm[0] = bins/128; /* wacky heuristic for gaussian stdev */ gparm[1] = 3; /* how many stdevs to cut-off at */ dparm[0] = 1.0; /* unit spacing */ dparm[1] = 1.0; /* B-Spline kernel */ dparm[2] = 0.0; if (nrrdCheapMedian(ntmpA, nhist, AIR_TRUE, AIR_FALSE, 2, 1.0, 1024) || nrrdSimpleResample(ntmpB, ntmpA, nrrdKernelGaussian, gparm, &bins, NULL) || nrrdSimpleResample(nhistD, ntmpB, nrrdKernelBCCubicD, dparm, &bins, NULL) || nrrdSimpleResample(nhistDD, ntmpB, nrrdKernelBCCubicDD, dparm, &bins, NULL)) { biffMovef(TEN, NRRD, "%s: trouble processing histogram", me); airMopError(mop); return 1; } if (save) { nrrdSave("tmp-histA.nrrd", ntmpA, NULL); nrrdSave("tmp-histB.nrrd", ntmpB, NULL); } hist = (float*)(ntmpB->data); histD = (float*)(nhistD->data); histDD = (float*)(nhistDD->data); range = nrrdRangeNewSet(ntmpB, nrrdBlind8BitRangeState); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); for (bb=0; bbmax) { /* first seek to max in histogram */ break; } } maxbb = bb; for (; bb 0) { /* zero-crossing in 1st deriv, positive 2nd deriv */ break; } } if (bb == bins-1) { biffAddf(TEN, "%s: never saw a satisfactory zero crossing", me); airMopError(mop); return 1; } *valP = nrrdAxisInfoPos(nhist, 0, AIR_AFFINE(0, tweak, 1, maxbb, bb)); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/glyph.c0000664000175000017500000010076512165631065016467 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" tenGlyphParm * tenGlyphParmNew() { tenGlyphParm *parm; parm = (tenGlyphParm *)calloc(1, sizeof(tenGlyphParm)); if (parm) { parm->verbose = 0; parm->nmask = NULL; parm->anisoType = tenAnisoUnknown; parm->onlyPositive = AIR_TRUE; parm->confThresh = AIR_NAN; parm->anisoThresh = AIR_NAN; parm->maskThresh = AIR_NAN; parm->glyphType = tenGlyphTypeUnknown; parm->facetRes = 10; parm->glyphScale = 1.0; parm->sqdSharp = 3.0; ELL_5V_SET(parm->edgeWidth, 0.0f, 0.0f, 0.4f, 0.2f, 0.1f); parm->colEvec = 0; /* first */ parm->colMaxSat = 1; parm->colGamma = 1; parm->colIsoGray = 1; parm->colAnisoType = tenAnisoUnknown; parm->colAnisoModulate = 0; ELL_4V_SET(parm->ADSP, 0, 1, 0, 30); parm->doSlice = AIR_FALSE; parm->sliceAxis = 0; parm->slicePos = 0; parm->sliceAnisoType = tenAnisoUnknown; parm->sliceOffset = 0.0; parm->sliceBias = 0.05f; parm->sliceGamma = 1.0; } return parm; } tenGlyphParm * tenGlyphParmNix(tenGlyphParm *parm) { airFree(parm); return NULL; } int tenGlyphParmCheck(tenGlyphParm *parm, const Nrrd *nten, const Nrrd *npos, const Nrrd *nslc) { static const char me[]="tenGlyphParmCheck"; int duh; size_t tenSize[3]; char stmp[5][AIR_STRLEN_SMALL]; if (!(parm && nten)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(tenAniso, parm->anisoType)) { biffAddf(TEN, "%s: unset (or invalid) anisoType (%d)", me, parm->anisoType); return 1; } if (airEnumValCheck(tenAniso, parm->colAnisoType)) { biffAddf(TEN, "%s: unset (or invalid) colAnisoType (%d)", me, parm->colAnisoType); return 1; } if (!( parm->facetRes >= 3 )) { biffAddf(TEN, "%s: facet resolution %d not >= 3", me, parm->facetRes); return 1; } if (!( AIR_IN_OP(tenGlyphTypeUnknown, parm->glyphType, tenGlyphTypeLast) )) { biffAddf(TEN, "%s: unset (or invalid) glyphType (%d)", me, parm->glyphType); return 1; } if (!( parm->glyphScale > 0)) { biffAddf(TEN, "%s: glyphScale must be > 0 (not %g)", me, parm->glyphScale); return 1; } if (parm->nmask) { if (npos) { biffAddf(TEN, "%s: can't do masking with explicit coordinate list", me); return 1; } if (!( 3 == parm->nmask->dim && parm->nmask->axis[0].size == nten->axis[1].size && parm->nmask->axis[1].size == nten->axis[2].size && parm->nmask->axis[2].size == nten->axis[3].size )) { biffAddf(TEN, "%s: mask isn't 3-D or doesn't have sizes (%s,%s,%s)", me, airSprintSize_t(stmp[0], nten->axis[1].size), airSprintSize_t(stmp[1], nten->axis[2].size), airSprintSize_t(stmp[2], nten->axis[3].size)); return 1; } if (!(AIR_EXISTS(parm->maskThresh))) { biffAddf(TEN, "%s: maskThresh hasn't been set", me); return 1; } } if (!( AIR_EXISTS(parm->anisoThresh) && AIR_EXISTS(parm->confThresh) )) { biffAddf(TEN, "%s: anisoThresh and confThresh haven't both been set", me); return 1; } if (parm->doSlice) { if (npos) { biffAddf(TEN, "%s: can't do slice with explicit coordinate list", me); return 1; } if (!( parm->sliceAxis <=2 )) { biffAddf(TEN, "%s: slice axis %d invalid", me, parm->sliceAxis); return 1; } if (!( parm->slicePos < nten->axis[1+parm->sliceAxis].size )) { biffAddf(TEN, "%s: slice pos %s not in valid range [0..%s]", me, airSprintSize_t(stmp[0], parm->slicePos), airSprintSize_t(stmp[1], nten->axis[1+parm->sliceAxis].size-1)); return 1; } if (nslc) { if (2 != nslc->dim) { biffAddf(TEN, "%s: explicit slice must be 2-D (not %d)", me, nslc->dim); return 1; } tenSize[0] = nten->axis[1].size; tenSize[1] = nten->axis[2].size; tenSize[2] = nten->axis[3].size; for (duh=parm->sliceAxis; duh<2; duh++) { tenSize[duh] = tenSize[duh+1]; } if (!( tenSize[0] == nslc->axis[0].size && tenSize[1] == nslc->axis[1].size )) { biffAddf(TEN, "%s: axis %u slice of %sx%sx%s volume != %sx%s", me, parm->sliceAxis, airSprintSize_t(stmp[0], nten->axis[1].size), airSprintSize_t(stmp[1], nten->axis[2].size), airSprintSize_t(stmp[2], nten->axis[3].size), airSprintSize_t(stmp[3], nslc->axis[0].size), airSprintSize_t(stmp[4], nslc->axis[1].size)); return 1; } } else { if (airEnumValCheck(tenAniso, parm->sliceAnisoType)) { biffAddf(TEN, "%s: unset (or invalid) sliceAnisoType (%d)", me, parm->sliceAnisoType); return 1; } } } return 0; } int tenGlyphGen(limnObject *glyphsLimn, echoScene *glyphsEcho, tenGlyphParm *parm, const Nrrd *nten, const Nrrd *npos, const Nrrd *nslc) { static const char me[]="tenGlyphGen"; gageShape *shape; airArray *mop; float *tdata, eval[3], evec[9], *cvec, rotEvec[9], mA_f[16], absEval[3], glyphScl[3]; double pI[3], pW[3], cl, cp, sRot[16], mA[16], mB[16], msFr[9], tmpvec[3], R, G, B, qA, qB, qC, glyphAniso, sliceGray; unsigned int duh; int slcCoord[3], idx, glyphIdx, axis, numGlyphs, svRGBAfl=AIR_FALSE; limnLook *look; int lookIdx; echoObject *eglyph, *inst, *list=NULL, *split, *esquare; echoPos_t eM[16], originOffset[3], edge0[3], edge1[3]; char stmp[AIR_STRLEN_SMALL]; /* int eret; double tmp1[3], tmp2[3]; */ if (!( (glyphsLimn || glyphsEcho) && nten && parm)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); shape = gageShapeNew(); shape->defaultCenter = nrrdCenterCell; airMopAdd(mop, shape, (airMopper)gageShapeNix, airMopAlways); if (npos) { if (!( 2 == nten->dim && 7 == nten->axis[0].size )) { biffAddf(TEN, "%s: nten isn't 2-D 7-by-N array", me); airMopError(mop); return 1; } if (!( 2 == npos->dim && 3 == npos->axis[0].size && nten->axis[1].size == npos->axis[1].size )) { biffAddf(TEN, "%s: npos isn't 2-D 3-by-%s array", me, airSprintSize_t(stmp, nten->axis[1].size)); airMopError(mop); return 1; } if (!( nrrdTypeFloat == nten->type && nrrdTypeFloat == npos->type )) { biffAddf(TEN, "%s: nten and npos must be %s, not %s and %s", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nten->type), airEnumStr(nrrdType, npos->type)); airMopError(mop); return 1; } } else { if (tenTensorCheck(nten, nrrdTypeFloat, AIR_TRUE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a valid DT volume", me); airMopError(mop); return 1; } } if (tenGlyphParmCheck(parm, nten, npos, nslc)) { biffAddf(TEN, "%s: trouble", me); airMopError(mop); return 1; } if (!npos) { if (gageShapeSet(shape, nten, tenGageKind->baseDim)) { biffMovef(TEN, GAGE, "%s: trouble", me); airMopError(mop); return 1; } } if (parm->doSlice) { ELL_3V_COPY(edge0, shape->spacing); ELL_3V_COPY(edge1, shape->spacing); edge0[parm->sliceAxis] = edge1[parm->sliceAxis] = 0.0; switch(parm->sliceAxis) { case 0: edge0[1] = edge1[2] = 0; ELL_4M_ROTATE_Y_SET(sRot, AIR_PI/2); break; case 1: edge0[0] = edge1[2] = 0; ELL_4M_ROTATE_X_SET(sRot, AIR_PI/2); break; case 2: default: edge0[0] = edge1[1] = 0; ELL_4M_IDENTITY_SET(sRot); break; } ELL_3V_COPY(originOffset, shape->spacing); ELL_3V_SCALE(originOffset, -0.5, originOffset); originOffset[parm->sliceAxis] *= -2*parm->sliceOffset; } if (glyphsLimn) { /* create limnLooks for diffuse and ambient-only shading */ /* ??? */ /* hack: save old value of setVertexRGBAFromLook, and set to true */ svRGBAfl = glyphsLimn->setVertexRGBAFromLook; glyphsLimn->setVertexRGBAFromLook = AIR_TRUE; } if (glyphsEcho) { list = echoObjectNew(glyphsEcho, echoTypeList); } if (npos) { numGlyphs = AIR_UINT(nten->axis[1].size); } else { numGlyphs = shape->size[0] * shape->size[1] * shape->size[2]; } /* find measurement frame transform */ if (3 == nten->spaceDim && AIR_EXISTS(nten->measurementFrame[0][0])) { /* msFr nten->measurementFrame ** 0 1 2 [0][0] [1][0] [2][0] ** 3 4 5 [0][1] [1][1] [2][1] ** 6 7 8 [0][2] [1][2] [2][2] */ msFr[0] = nten->measurementFrame[0][0]; msFr[3] = nten->measurementFrame[0][1]; msFr[6] = nten->measurementFrame[0][2]; msFr[1] = nten->measurementFrame[1][0]; msFr[4] = nten->measurementFrame[1][1]; msFr[7] = nten->measurementFrame[1][2]; msFr[2] = nten->measurementFrame[2][0]; msFr[5] = nten->measurementFrame[2][1]; msFr[8] = nten->measurementFrame[2][2]; } else { ELL_3M_IDENTITY_SET(msFr); } for (idx=0; idxdata) + 7*idx; if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: hello %g %g %g %g %g %g %g\n", me, idx, numGlyphs, tdata[0], tdata[1], tdata[2], tdata[3], tdata[4], tdata[5], tdata[6]); } if (!( TEN_T_EXISTS(tdata) )) { /* there's nothing we can do here */ if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: non-existent data\n", me, idx, numGlyphs); } continue; } if (npos) { ELL_3V_COPY(pW, (float*)(npos->data) + 3*idx); if (!( AIR_EXISTS(pW[0]) && AIR_EXISTS(pW[1]) && AIR_EXISTS(pW[2]) )) { /* position doesn't exist- perhaps because its from the push library, which might kill points by setting coords to nan */ continue; } } else { NRRD_COORD_GEN(pI, shape->size, 3, idx); /* this does take into account full orientation */ gageShapeItoW(shape, pW, pI); if (parm->nmask) { if (!( nrrdFLookup[parm->nmask->type](parm->nmask->data, idx) >= parm->maskThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: doesn't meet mask thresh\n", me, idx, numGlyphs); } continue; } } } tenEigensolve_f(eval, evec, tdata); /* transform eigenvectors by measurement frame */ ELL_3MV_MUL(tmpvec, msFr, evec + 0); ELL_3V_COPY_TT(evec + 0, float, tmpvec); ELL_3MV_MUL(tmpvec, msFr, evec + 3); ELL_3V_COPY_TT(evec + 3, float, tmpvec); ELL_3MV_MUL(tmpvec, msFr, evec + 6); ELL_3V_COPY_TT(evec + 6, float, tmpvec); ELL_3V_CROSS(tmpvec, evec + 0, evec + 3); if (0 > ELL_3V_DOT(tmpvec, evec + 6)) { ELL_3V_SCALE(evec + 6, -1, evec + 6); } ELL_3M_TRANSPOSE(rotEvec, evec); if (parm->doSlice && pI[parm->sliceAxis] == parm->slicePos) { /* set sliceGray */ if (nslc) { /* we aren't masked by confidence, as anisotropy slice is */ for (duh=0; duhsliceAxis; duh++) { slcCoord[duh] = (int)(pI[duh]); } for (duh=duhsliceAxis; duh<2; duh++) { slcCoord[duh] = (int)(pI[duh+1]); } /* HEY: GLK has no idea what's going here */ slcCoord[0] = (int)(pI[0]); slcCoord[1] = (int)(pI[1]); slcCoord[2] = (int)(pI[2]); sliceGray = nrrdFLookup[nslc->type](nslc->data, slcCoord[0] + nslc->axis[0].size*slcCoord[1]); } else { if (!( tdata[0] >= parm->confThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d (slice): conf %g < thresh %g\n", me, idx, numGlyphs, tdata[0], parm->confThresh); } continue; } sliceGray = tenAnisoEval_f(eval, parm->sliceAnisoType); } if (parm->sliceGamma > 0) { sliceGray = AIR_AFFINE(0, sliceGray, 1, parm->sliceBias, 1); sliceGray = pow(sliceGray, 1.0/parm->sliceGamma); } else { sliceGray = AIR_AFFINE(0, sliceGray, 1, 0, 1-parm->sliceBias); sliceGray = 1.0 - pow(sliceGray, -1.0/parm->sliceGamma); } /* make slice contribution */ /* HEY: this is *NOT* aware of shape->fromOrientation */ if (glyphsLimn) { lookIdx = limnObjectLookAdd(glyphsLimn); look = glyphsLimn->look + lookIdx; ELL_4V_SET_TT(look->rgba, float, sliceGray, sliceGray, sliceGray, 1); ELL_3V_SET(look->kads, 1, 0, 0); look->spow = 0; glyphIdx = limnObjectSquareAdd(glyphsLimn, lookIdx); ELL_4M_IDENTITY_SET(mA); ell_4m_post_mul_d(mA, sRot); if (!npos) { ELL_4M_SCALE_SET(mB, shape->spacing[0], shape->spacing[1], shape->spacing[2]); } ell_4m_post_mul_d(mA, mB); ELL_4M_TRANSLATE_SET(mB, pW[0], pW[1], pW[2]); ell_4m_post_mul_d(mA, mB); ELL_4M_TRANSLATE_SET(mB, originOffset[0], originOffset[1], originOffset[2]); ell_4m_post_mul_d(mA, mB); ELL_4M_COPY_TT(mA_f, float, mA); limnObjectPartTransform(glyphsLimn, glyphIdx, mA_f); } if (glyphsEcho) { esquare = echoObjectNew(glyphsEcho,echoTypeRectangle); ELL_3V_ADD2(((echoRectangle*)esquare)->origin, pW, originOffset); ELL_3V_COPY(((echoRectangle*)esquare)->edge0, edge0); ELL_3V_COPY(((echoRectangle*)esquare)->edge1, edge1); echoColorSet(esquare, AIR_CAST(echoCol_t, sliceGray), AIR_CAST(echoCol_t, sliceGray), AIR_CAST(echoCol_t, sliceGray), 1); /* this is pretty arbitrary- but I want shadows to have some effect. Previously, the material was all ambient: (A,D,S) = (1,0,0), which avoided all shadow effects. */ echoMatterPhongSet(glyphsEcho, esquare, 0.4f, 0.6f, 0, 40); echoListAdd(list, esquare); } } if (parm->onlyPositive) { if (eval[2] < 0) { /* didn't have all positive eigenvalues, its outta here */ if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: not all evals %g %g %g > 0\n", me, idx, numGlyphs, eval[0], eval[1], eval[2]); } continue; } } if (!( tdata[0] >= parm->confThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: conf %g < thresh %g\n", me, idx, numGlyphs, tdata[0], parm->confThresh); } continue; } if (!( tenAnisoEval_f(eval, parm->anisoType) >= parm->anisoThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: aniso[%d] %g < thresh %g\n", me, idx, numGlyphs, parm->anisoType, tenAnisoEval_f(eval, parm->anisoType), parm->anisoThresh); } continue; } glyphAniso = tenAnisoEval_f(eval, parm->colAnisoType); /* fprintf(stderr, "%s: eret = %d; evals = %g %g %g\n", me, eret, eval[0], eval[1], eval[2]); ELL_3V_CROSS(tmp1, evec+0, evec+3); tmp2[0] = ELL_3V_LEN(tmp1); ELL_3V_CROSS(tmp1, evec+0, evec+6); tmp2[1] = ELL_3V_LEN(tmp1); ELL_3V_CROSS(tmp1, evec+3, evec+6); tmp2[2] = ELL_3V_LEN(tmp1); fprintf(stderr, "%s: crosses = %g %g %g\n", me, tmp2[0], tmp2[1], tmp2[2]); */ /* set transform (in mA) */ ELL_3V_ABS(absEval, eval); ELL_4M_IDENTITY_SET(mA); /* reset */ ELL_3V_SCALE(glyphScl, parm->glyphScale, absEval); /* scale by evals */ ELL_4M_SCALE_SET(mB, glyphScl[0], glyphScl[1], glyphScl[2]); ell_4m_post_mul_d(mA, mB); ELL_43M_INSET(mB, rotEvec); /* rotate by evecs */ ell_4m_post_mul_d(mA, mB); ELL_4M_TRANSLATE_SET(mB, pW[0], pW[1], pW[2]); /* translate */ ell_4m_post_mul_d(mA, mB); /* set color (in R,G,B) */ cvec = evec + 3*(AIR_CLAMP(0, parm->colEvec, 2)); R = AIR_ABS(cvec[0]); /* standard mapping */ G = AIR_ABS(cvec[1]); B = AIR_ABS(cvec[2]); /* desaturate by colMaxSat */ R = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, R); G = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, G); B = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, B); /* desaturate some by anisotropy */ R = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0, R, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, R)); G = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0, G, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, G)); B = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0, B, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, B)); /* clamp and do gamma */ R = AIR_CLAMP(0.0, R, 1.0); G = AIR_CLAMP(0.0, G, 1.0); B = AIR_CLAMP(0.0, B, 1.0); R = pow(R, parm->colGamma); G = pow(G, parm->colGamma); B = pow(B, parm->colGamma); /* find axis, and superquad exponents qA and qB */ if (eval[2] > 0) { /* all evals positive */ cl = AIR_MIN(0.99, tenAnisoEval_f(eval, tenAniso_Cl1)); cp = AIR_MIN(0.99, tenAnisoEval_f(eval, tenAniso_Cp1)); if (cl > cp) { axis = 0; qA = pow(1-cp, parm->sqdSharp); qB = pow(1-cl, parm->sqdSharp); } else { axis = 2; qA = pow(1-cl, parm->sqdSharp); qB = pow(1-cp, parm->sqdSharp); } qC = qB; } else if (eval[0] < 0) { /* all evals negative */ float aef[3]; aef[0] = absEval[2]; aef[1] = absEval[1]; aef[2] = absEval[0]; cl = AIR_MIN(0.99, tenAnisoEval_f(aef, tenAniso_Cl1)); cp = AIR_MIN(0.99, tenAnisoEval_f(aef, tenAniso_Cp1)); if (cl > cp) { axis = 2; qA = pow(1-cp, parm->sqdSharp); qB = pow(1-cl, parm->sqdSharp); } else { axis = 0; qA = pow(1-cl, parm->sqdSharp); qB = pow(1-cp, parm->sqdSharp); } qC = qB; } else { #define OOSQRT2 0.70710678118654752440 #define OOSQRT3 0.57735026918962576451 /* double poleA[3]={OOSQRT3, OOSQRT3, OOSQRT3}; */ double poleB[3]={1, 0, 0}; double poleC[3]={OOSQRT2, OOSQRT2, 0}; double poleD[3]={OOSQRT3, -OOSQRT3, -OOSQRT3}; double poleE[3]={OOSQRT2, 0, -OOSQRT2}; double poleF[3]={OOSQRT3, OOSQRT3, -OOSQRT3}; double poleG[3]={0, -OOSQRT2, -OOSQRT2}; double poleH[3]={0, 0, -1}; /* double poleI[3]={-OOSQRT3, -OOSQRT3, -OOSQRT3}; */ double funk[3]={0,4,2}, thrn[3]={1,4,4}; double octa[3]={0,2,2}, cone[3]={1,2,2}; double evalN[3], tmp, bary[3]; double qq[3]; ELL_3V_NORM(evalN, eval, tmp); if (eval[1] >= -eval[2]) { /* inside B-F-C */ ell_3v_barycentric_spherical_d(bary, poleB, poleF, poleC, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], octa, bary[1], thrn, bary[2], cone); axis = 2; } else if (eval[0] >= -eval[2]) { /* inside B-D-F */ if (eval[1] >= 0) { /* inside B-E-F */ ell_3v_barycentric_spherical_d(bary, poleB, poleE, poleF, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], octa, bary[1], funk, bary[2], thrn); axis = 2; } else { /* inside B-D-E */ ell_3v_barycentric_spherical_d(bary, poleB, poleD, poleE, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], cone, bary[1], thrn, bary[2], funk); axis = 0; } } else if (eval[0] < -eval[1]) { /* inside D-G-H */ ell_3v_barycentric_spherical_d(bary, poleD, poleG, poleH, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], thrn, bary[1], cone, bary[2], octa); axis = 0; } else if (eval[1] < 0) { /* inside E-D-H */ ell_3v_barycentric_spherical_d(bary, poleE, poleD, poleH, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], funk, bary[1], thrn, bary[2], octa); axis = 0; } else { /* inside F-E-H */ ell_3v_barycentric_spherical_d(bary, poleF, poleE, poleH, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], thrn, bary[1], funk, bary[2], cone); axis = 2; } qA = qq[0]; qB = qq[1]; qC = qq[2]; #undef OOSQRT2 #undef OOSQRT3 } /* add the glyph */ if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: the glyph stays!\n", me, idx, numGlyphs); } if (glyphsLimn) { lookIdx = limnObjectLookAdd(glyphsLimn); look = glyphsLimn->look + lookIdx; ELL_4V_SET_TT(look->rgba, float, R, G, B, 1); ELL_3V_SET(look->kads, parm->ADSP[0], parm->ADSP[1], parm->ADSP[2]); look->spow = 0; switch(parm->glyphType) { case tenGlyphTypeBox: glyphIdx = limnObjectCubeAdd(glyphsLimn, lookIdx); break; case tenGlyphTypeSphere: glyphIdx = limnObjectPolarSphereAdd(glyphsLimn, lookIdx, axis, 2*parm->facetRes, parm->facetRes); break; case tenGlyphTypeCylinder: glyphIdx = limnObjectCylinderAdd(glyphsLimn, lookIdx, axis, parm->facetRes); break; case tenGlyphTypeSuperquad: default: glyphIdx = limnObjectPolarSuperquadFancyAdd(glyphsLimn, lookIdx, axis, AIR_CAST(float, qA), AIR_CAST(float, qB), AIR_CAST(float, qC), 0, 2*parm->facetRes, parm->facetRes); break; } ELL_4M_COPY_TT(mA_f, float, mA); limnObjectPartTransform(glyphsLimn, glyphIdx, mA_f); } if (glyphsEcho) { switch(parm->glyphType) { case tenGlyphTypeBox: eglyph = echoObjectNew(glyphsEcho, echoTypeCube); /* nothing else to set */ break; case tenGlyphTypeSphere: eglyph = echoObjectNew(glyphsEcho, echoTypeSphere); echoSphereSet(eglyph, 0, 0, 0, 1); break; case tenGlyphTypeCylinder: eglyph = echoObjectNew(glyphsEcho, echoTypeCylinder); echoCylinderSet(eglyph, axis); break; case tenGlyphTypeSuperquad: default: eglyph = echoObjectNew(glyphsEcho, echoTypeSuperquad); echoSuperquadSet(eglyph, axis, qA, qB); break; } echoColorSet(eglyph, AIR_CAST(echoCol_t, R), AIR_CAST(echoCol_t, G), AIR_CAST(echoCol_t, B), 1); echoMatterPhongSet(glyphsEcho, eglyph, parm->ADSP[0], parm->ADSP[1], parm->ADSP[2], parm->ADSP[3]); inst = echoObjectNew(glyphsEcho, echoTypeInstance); ELL_4M_COPY(eM, mA); echoInstanceSet(inst, eM, eglyph); echoListAdd(list, inst); } } if (glyphsLimn) { glyphsLimn->setVertexRGBAFromLook = svRGBAfl; } if (glyphsEcho) { split = echoListSplit3(glyphsEcho, list, 10); echoObjectAdd(glyphsEcho, split); } airMopOkay(mop); return 0; } /* ** Zone from Eval */ unsigned int tenGlyphBqdZoneEval(const double eval[3]) { double x, y, z; unsigned int zone; x = eval[0]; y = eval[1]; z = eval[2]; if (y > 0) { /* 0 1 2 3 4 */ if (z > 0) { /* 0 1 */ if (x - y > y - z) { zone = 0; } else { zone = 1; } } else { /* 2 3 4 */ if (y > -z) { zone = 2; } else if (x > -z) { zone = 3; } else { zone = 4; } } } else { /* 5 6 7 8 9 */ if (x > 0) { /* 5 6 7 */ if (x > -z) { zone = 5; } else if (x > -y) { zone = 6; } else { zone = 7; } } else { /* 8 9 */ if (x - y > y - z) { zone = 8; } else { zone = 9; } } } return zone; } /* ** UV from Eval */ void tenGlyphBqdUvEval(double uv[2], const double eval[3]) { double xx, yy, zz, ax, ay, az, mm; ax = AIR_ABS(eval[0]); ay = AIR_ABS(eval[1]); az = AIR_ABS(eval[2]); mm = AIR_MAX(ax, AIR_MAX(ay, az)); if (mm==0) { /* do not divide */ uv[0]=uv[1]=0; return; } xx = eval[0]/mm; yy = eval[1]/mm; zz = eval[2]/mm; uv[0] = AIR_AFFINE(-1, yy, 1, 0, 1); if (xx > -zz) { uv[1] = AIR_AFFINE(-1, zz, 1, 0, 1) - uv[0] + 1; } else { uv[1] = AIR_AFFINE(-1, xx, 1, -1, 0) - uv[0] + 1; } return; } /* ** Eval from UV */ void tenGlyphBqdEvalUv(double eval[3], const double uv[2]) { double xx, yy, zz, ll; yy = AIR_AFFINE(0, uv[0], 1, -1, 1); if (uv[0] + uv[1] > 1) { zz = AIR_AFFINE(0, uv[1], 1, -1, 1) - 1 + yy; xx = 1; } else { xx = AIR_AFFINE(0, uv[1], 1, -1, 1) + yy + 1; zz = -1; } ELL_3V_SET(eval, xx, yy, zz); ELL_3V_NORM(eval, eval, ll); return; } /* ** Zone from UV */ unsigned int tenGlyphBqdZoneUv(const double uv[2]) { /* the use of "volatile" here, as well as additional variables for expressions involving u and v, is based on browsing this summary of the subtleties of IEEE 754: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 In this function, "if (u + v > 0.5)" returned one thing for cygwin, and something else for other platforms. Adding volatile and more variables for expressions brings cygwin back into line with the other platforms */ volatile double u, v, upv, tupv; unsigned int zone; u = uv[0]; v = uv[1]; upv = u + v; tupv = 2*u + v; if (u > 0.5) { /* 0 1 2 3 4 */ if (upv > 1.5) { /* 0 1 */ if (u < v) { zone = 0; } else { zone = 1; } } else { /* 2 3 4 */ if (tupv > 2) { zone = 2; } else if (upv > 1) { zone = 3; } else { zone = 4; } } } else { /* 5 6 7 8 9 */ if (upv > 0.5) { /* 5 6 7 */ if (upv > 1) { zone = 5; } else if (tupv > 1) { zone = 6; } else { zone = 7; } } else { /* 8 9 */ if (u < v) { zone = 8; } else { zone = 9; } } } return zone; } static void baryFind(double bcoord[3], const double uvp[2], const double uv0[2], const double uv1[2], const double uv2[2]) { double mat[9], a, a01, a02, a12; ELL_3M_SET(mat, uv0[0], uv0[1], 1, uv1[0], uv1[1], 1, uvp[0], uvp[1], 1); a01 = ELL_3M_DET(mat); a01 = AIR_ABS(a01); ELL_3M_SET(mat, uv0[0], uv0[1], 1, uv2[0], uv2[1], 1, uvp[0], uvp[1], 1); a02 = ELL_3M_DET(mat); a02 = AIR_ABS(a02); ELL_3M_SET(mat, uv1[0], uv1[1], 1, uv2[0], uv2[1], 1, uvp[0], uvp[1], 1); a12 = ELL_3M_DET(mat); a12 = AIR_ABS(a12); a = a01 + a02 + a12; ELL_3V_SET(bcoord, a12/a, a02/a, a01/a); return; } static void baryBlend(double abc[3], const double co[3], const double abc0[3], const double abc1[3], const double abc2[3]) { unsigned int ii; for (ii=0; ii<3; ii++) { abc[ii] = co[0]*abc0[ii] + co[1]*abc1[ii] + co[2]*abc2[ii]; } return; } void tenGlyphBqdAbcUv(double abc[3], const double uv[2], double betaMax) { static const unsigned int vertsZone[10][3] = {{0, 1, 2}, /* 0 */ {0, 2, 3}, /* 1 */ {1, 3, 4}, /* 2 */ {1, 4, 5}, /* 3 */ {4, 5, 9}, /* 4 */ {1, 5, 6}, /* 5 */ {5, 6, 9}, /* 6 */ {6, 7, 9}, /* 7 */ {7, 8, 10}, /* 8 */ {8, 9, 10}}; /* 9 */ static const double uvVert[11][2] = {{1.00, 1.00}, /* 0 */ {0.50, 1.00}, /* 1 */ {0.75, 0.75}, /* 2 */ {1.00, 0.50}, /* 3 */ {1.00, 0.00}, /* 4 */ {0.50, 0.50}, /* 5 */ {0.00, 1.00}, /* 6 */ {0.00, 0.50}, /* 7 */ {0.25, 0.25}, /* 8 */ {0.50, 0.00}, /* 9 */ {0.00, 0.00}}; /* 10 */ double abcBall[3], abcCyli[3], abcFunk[3], abcThrn[3], abcOcta[3], abcCone[3], abcHalf[3]; /* old compile-time setting const double *abcAll[10][11] = { zone \ vert 0 1 2 3 4 5 6 7 8 9 10 0 {abcBall, abcCyli, abcHalf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, 1 {abcBall, NULL, abcHalf, abcCyli, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, 2 { NULL, abcOcta, NULL, abcCone, abcThrn, NULL, NULL, NULL, NULL, NULL, NULL }, 3 { NULL, abcOcta, NULL, NULL, abcThrn, abcFunk, NULL, NULL, NULL, NULL, NULL }, 4 { NULL, NULL, NULL, NULL, abcThrn, abcFunk, NULL, NULL, NULL, abcCone, NULL }, 5 { NULL, abcCone, NULL, NULL, NULL, abcFunk, abcThrn, NULL, NULL, NULL, NULL }, 6 { NULL, NULL, NULL, NULL, NULL, abcFunk, abcThrn, NULL, NULL, abcOcta, NULL }, 7 { NULL, NULL, NULL, NULL, NULL, NULL, abcThrn, abcCone, NULL, abcOcta, NULL }, 8 { NULL, NULL, NULL, NULL, NULL, NULL, NULL, abcCyli, abcHalf, NULL, abcBall }, 9 { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, abcHalf, abcCyli, abcBall }}; */ const double *abcAll[10][11]; unsigned int pvi[3], zone, vert; double bcoord[3]; ELL_3V_SET(abcBall, 1, 1, 1); ELL_3V_SET(abcCyli, 1, 0, 0); ELL_3V_SET(abcFunk, 0, betaMax, 2); /* only one with c != b */ ELL_3V_SET(abcThrn, 1, betaMax, 3); ELL_3V_SET(abcOcta, 0, 2, 2); ELL_3V_SET(abcCone, 1, 2, 2); ELL_3V_SET(abcHalf, 0.5, 0.5, 0.5); /* alpha is half-way between alpha of octa and cone and beta has to be the same as alpha at for the seam to be shape-continuous */ /* run-time setting of abcAll[][]; compile-time setting (comments above) gives "initializer element is not computable at load time" warnings */ for (zone=0; zone<10; zone++) { for (vert=0; vert<11; vert++) { abcAll[zone][vert]=NULL; } } #define SET(zi, vi0, vi1, vi2, sh0, sh1, sh2) \ abcAll[zi][vi0] = abc##sh0; \ abcAll[zi][vi1] = abc##sh1; \ abcAll[zi][vi2] = abc##sh2 SET(0, 0, 1, 2, Ball, Cyli, Half); SET(1, 0, 2, 3, Ball, Half, Cyli); SET(2, 1, 3, 4, Octa, Cone, Thrn); SET(3, 1, 4, 5, Octa, Thrn, Funk); SET(4, 4, 5, 9, Thrn, Funk, Cone); SET(5, 1, 5, 6, Cone, Funk, Thrn); SET(6, 5, 6, 9, Funk, Thrn, Octa); SET(7, 6, 7, 9, Thrn, Cone, Octa); SET(8, 7, 8,10, Cyli, Half, Ball); SET(9, 8, 9,10, Half, Cyli, Ball); #undef SET zone = tenGlyphBqdZoneUv(uv); ELL_3V_COPY(pvi, vertsZone[zone]); baryFind(bcoord, uv, uvVert[pvi[0]], uvVert[pvi[1]], uvVert[pvi[2]]); baryBlend(abc, bcoord, abcAll[zone][pvi[0]], abcAll[zone][pvi[1]], abcAll[zone][pvi[2]]); return; } teem-1.11.0~svn6057/src/ten/tendTriple.c0000664000175000017500000000517112165631065017451 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Compute volume of shape triples" static const char *_tend_tripleInfoL = (INFO ". The triple can be eignvalues, invariants (J, K, R), " "and lots of other things."); int tend_tripleMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int ttype; Nrrd *nin, *nout; char *outS; hestOptAdd(&hopt, "t", "type", airTypeEnum, 1, 1, &ttype, NULL, "desired output triple type", NULL, tenTripleType); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output triple volume"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_tripleInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenTripleCalc(nout, ttype, nin)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(triple, INFO); teem-1.11.0~svn6057/src/ten/tendEvalpow.c0000664000175000017500000000516412165631065017631 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Modify shape by raising eigenvalues to some power" static const char *_tend_evalpowInfoL = (INFO ". The orientation of the tensor is unchanged."); int tend_evalpowMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS; float expo; hestOptAdd(&hopt, "p", "power", airTypeFloat, 1, 1, &expo, NULL, "Power to which to raise all the eigenvalues."); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output tensor volume"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_evalpowInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenEigenvaluePower(nout, nin, expo)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(evalpow, INFO); teem-1.11.0~svn6057/src/ten/tenDwiGage.c0000664000175000017500000012465312165631065017364 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #if TEEM_LEVMAR #include #endif /* --------------------------------------------------------------------- */ const char * _tenDwiGageStr[] = { "(unknown tenDwiGage)", "all", "b0", "jdwi", "adc", "mdwi", "tlls", "tllserr", "tllserrlog", "tllslike", "twls", "twlserr", "twlserrlog", "twlslike", "tnls", "tnlserr", "tnlserrlog", "tnlslike", "tmle", "tmleerr", "tmleerrlog", "tmlelike", "t", "terr", "terrlog", "tlike", "c", "fa", "adwie", "2qs", "2qserr", "2qsnerr", "2peled", "2pelederr", "2pelednerr", "2peledlminfo", }; const int _tenDwiGageVal[] = { tenDwiGageUnknown, tenDwiGageAll, tenDwiGageB0, tenDwiGageJustDWI, tenDwiGageADC, tenDwiGageMeanDWIValue, tenDwiGageTensorLLS, tenDwiGageTensorLLSError, tenDwiGageTensorLLSErrorLog, tenDwiGageTensorLLSLikelihood, tenDwiGageTensorWLS, tenDwiGageTensorWLSError, tenDwiGageTensorWLSErrorLog, tenDwiGageTensorWLSLikelihood, tenDwiGageTensorNLS, tenDwiGageTensorNLSError, tenDwiGageTensorNLSErrorLog, tenDwiGageTensorNLSLikelihood, tenDwiGageTensorMLE, tenDwiGageTensorMLEError, tenDwiGageTensorMLEErrorLog, tenDwiGageTensorMLELikelihood, tenDwiGageTensor, tenDwiGageTensorError, tenDwiGageTensorErrorLog, tenDwiGageTensorLikelihood, tenDwiGageConfidence, tenDwiGageFA, tenDwiGageTensorAllDWIError, tenDwiGage2TensorQSeg, tenDwiGage2TensorQSegError, tenDwiGage2TensorQSegAndError, tenDwiGage2TensorPeled, tenDwiGage2TensorPeledError, tenDwiGage2TensorPeledAndError, tenDwiGage2TensorPeledLevmarInfo }; const airEnum _tenDwiGage = { "tenDwiGage", TEN_DWI_GAGE_ITEM_MAX, _tenDwiGageStr, _tenDwiGageVal, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const tenDwiGage = &_tenDwiGage; /* --------------------------------------------------------------------- */ gageItemEntry _tenDwiGageTable[TEN_DWI_GAGE_ITEM_MAX+1] = { /* enum value len,deriv, prereqs, parent item, parent index, needData */ {tenDwiGageUnknown, 0, 0, {0}, 0, 0, AIR_TRUE}, /* len == 0 for tenDwiGage{All,JustDWI,ADC} means "learn later at run-time" */ {tenDwiGageAll, 0, 0, {0}, 0, 0, AIR_TRUE}, {tenDwiGageB0, 1, 0, {tenDwiGageAll}, tenDwiGageAll, 0, AIR_TRUE}, {tenDwiGageJustDWI, 0, 0, {tenDwiGageAll}, tenDwiGageAll, 1, AIR_TRUE}, {tenDwiGageADC, 0, 0, {tenDwiGageB0, tenDwiGageJustDWI}, 0, 0, AIR_TRUE}, {tenDwiGageMeanDWIValue, 1, 0, {tenDwiGageAll}, 0, 0, AIR_TRUE}, {tenDwiGageTensorLLS, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE}, {tenDwiGageTensorLLSError, 1, 0, {tenDwiGageTensorLLS}, 0, 0, AIR_TRUE}, {tenDwiGageTensorLLSErrorLog, 1, 0, {tenDwiGageTensorLLS}, 0, 0, AIR_TRUE}, {tenDwiGageTensorLLSLikelihood, 1, 0, {tenDwiGageTensorLLS}, 0, 0, AIR_TRUE}, {tenDwiGageTensorWLS, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE}, {tenDwiGageTensorWLSError, 1, 0, {tenDwiGageTensorWLS}, 0, 0, AIR_TRUE}, {tenDwiGageTensorWLSErrorLog, 1, 0, {tenDwiGageTensorWLS}, 0, 0, AIR_TRUE}, {tenDwiGageTensorWLSLikelihood, 1, 0, {tenDwiGageTensorWLS}, 0, 0, AIR_TRUE}, {tenDwiGageTensorNLS, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE}, {tenDwiGageTensorNLSError, 1, 0, {tenDwiGageTensorNLS}, 0, 0, AIR_TRUE}, {tenDwiGageTensorNLSErrorLog, 1, 0, {tenDwiGageTensorNLS}, 0, 0, AIR_TRUE}, {tenDwiGageTensorNLSLikelihood, 1, 0, {tenDwiGageTensorNLS}, 0, 0, AIR_TRUE}, {tenDwiGageTensorMLE, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE}, {tenDwiGageTensorMLEError, 1, 0, {tenDwiGageTensorMLE}, 0, 0, AIR_TRUE}, {tenDwiGageTensorMLEErrorLog, 1, 0, {tenDwiGageTensorMLE}, 0, 0, AIR_TRUE}, {tenDwiGageTensorMLELikelihood, 1, 0, {tenDwiGageTensorMLE}, 0, 0, AIR_TRUE}, /* these are NOT sub-items: they are copies, as controlled by the kind->data, but not the query: the query can't capture the kind of dependency implemented by having a dynamic kind */ {tenDwiGageTensor, 7, 0, {0}, /* 0 == "learn later at run time" */ 0, 0, AIR_TRUE}, {tenDwiGageTensorError, 1, 0, {0}, 0, 0, AIR_TRUE}, {tenDwiGageTensorErrorLog, 1, 0, {0}, 0, 0, AIR_TRUE}, {tenDwiGageTensorLikelihood, 1, 0, {0}, 0, 0, AIR_TRUE}, /* back to normal non-run-time items */ {tenDwiGageConfidence, 1, 0, {tenDwiGageTensor}, tenDwiGageTensor, 0, AIR_TRUE}, {tenDwiGageFA, 1, 0, {tenDwiGageTensor}, 0, 0, AIR_TRUE}, {tenDwiGageTensorAllDWIError, 0, 0, {tenDwiGageTensor, tenDwiGageJustDWI}, 0, 0, AIR_TRUE}, /* it actually doesn't make sense for tenDwiGage2TensorQSegAndError to be the parent, because of the situations where you want the q-seg result, but don't care about error */ {tenDwiGage2TensorQSeg, 14, 0, {tenDwiGageAll}, 0, 0, AIR_TRUE}, {tenDwiGage2TensorQSegError, 1, 0, {tenDwiGageAll, tenDwiGage2TensorQSeg}, 0, 0, AIR_TRUE}, {tenDwiGage2TensorQSegAndError, 15, 0, {tenDwiGage2TensorQSeg, tenDwiGage2TensorQSegError}, 0, 0, AIR_TRUE}, {tenDwiGage2TensorPeled, 14, 0, {tenDwiGageAll}, 0, 0, AIR_TRUE}, {tenDwiGage2TensorPeledError, 1, 0, {tenDwiGageAll, tenDwiGage2TensorPeled}, 0, 0, AIR_TRUE}, {tenDwiGage2TensorPeledAndError, 15, 0, {tenDwiGage2TensorPeled, tenDwiGage2TensorPeledError}, 0, 0, AIR_TRUE}, {tenDwiGage2TensorPeledLevmarInfo, 5, 0, {tenDwiGage2TensorPeled}, 0, 0, AIR_TRUE} }; void _tenDwiGageIv3Print(FILE *file, gageContext *ctx, gagePerVolume *pvl) { static const char me[]="_tenDwiGageIv3Print"; AIR_UNUSED(ctx); AIR_UNUSED(pvl); fprintf(file, "%s: sorry, unimplemented\n", me); return; } void _tenDwiGageFilter(gageContext *ctx, gagePerVolume *pvl) { static const char me[]="_tenDwiGageFilter"; double *fw00, *fw11, *fw22, *dwi; int fd, needD[3]={AIR_TRUE, AIR_FALSE, AIR_FALSE}; /* tenDwiGageKindData *kindData; */ gageScl3PFilter_t *filter[5] = {NULL, gageScl3PFilter2, gageScl3PFilter4, gageScl3PFilter6, gageScl3PFilter8}; unsigned int J, dwiNum; fd = 2*ctx->radius; dwi = pvl->directAnswer[tenDwiGageAll]; /* kindData = AIR_CAST(tenDwiGageKindData *, pvl->kind->data); */ dwiNum = pvl->kind->valLen; if (!ctx->parm.k3pack) { fprintf(stderr, "%s: sorry, 6pack filtering not implemented\n", me); return; } fw00 = ctx->fw + fd*3*gageKernel00; fw11 = ctx->fw + fd*3*gageKernel11; fw22 = ctx->fw + fd*3*gageKernel22; /* HEY: these will have to be updated if there is ever any use for derivatives in DWIs: can't pass NULL pointers for gradient info. The unusual use of a hard-coded local needD is because there currently isn't allocated space in the tenDwiGage kind (which is unusual for its dynamic allocation) for DWI derivatives */ if (fd <= 8) { for (J=0; Jradius](ctx->shape, pvl->iv3 + J*fd*fd*fd, pvl->iv2 + J*fd*fd, pvl->iv1 + J*fd, fw00, fw11, fw22, dwi + J, NULL, NULL, needD); } } else { for (J=0; Jshape, fd, pvl->iv3 + J*fd*fd*fd, pvl->iv2 + J*fd*fd, pvl->iv1 + J*fd, fw00, fw11, fw22, dwi + J, NULL, NULL, needD); } } return; } /* Returns the Akaike Information Criterion */ /* ** residual: is the variance ** n: number of observations: number of DWI's in our case ** k: number of parameters: number of tensor components in our case */ double _tenComputeAIC(double residual, int n, int k) { double AIC = 0; if (residual == 0) { return 0; } /* AIC, RSS used when doing regression */ AIC = 2*k + n*log(residual); /* Always use bias adjustment */ /* if (n/k < 40) { */ AIC = AIC + ((2*k*(k + 1))/(n - k - 1)); /* } */ return AIC; } /* Form a 2D tensor from the parameters */ void _tenPeledRotate2D(double ten[7], double lam1, double lam3, double phi) { double cc, ss, d3, d1, d2; cc = cos(phi); ss = sin(phi); d1 = cc*cc*lam1 + ss*ss*lam3; d3 = cc*ss*(lam1 - lam3); d2 = ss*ss*lam1 + cc*cc*lam3; TEN_T_SET(ten, 1.0, d1, d3, 0, d2, 0, lam3); return; } /* The main callback function that is iterated during levmar */ /* vector pp of parameters is as follows: ** pp[0]: principal eigenvalue ** pp[1]: fraction of 1st tensor ** pp[2]: phi for 1st tensor ** pp[3]: phi for 2nd tensor */ void _tenLevmarPeledCB(double *pp, double *xx, int mm, int nn, void *_pvlData) { /* static const char me[]="_tenLevmarPeledCB"; */ double tenA[7], tenB[7]; int ii; tenDwiGagePvlData *pvlData; double *egrad; AIR_UNUSED(mm); pvlData = AIR_CAST(tenDwiGagePvlData *, _pvlData); /* Form the tensors using the estimated parms */ _tenPeledRotate2D(tenA, pp[0], pvlData->ten1Eval[2], pp[2]); _tenPeledRotate2D(tenB, pp[0], pvlData->ten1Eval[2], pp[3]); egrad = AIR_CAST(double *, pvlData->nten1EigenGrads->data); /* skip past b0 gradient, HEY: not general purpose */ egrad += 3; for (ii=0; iitec1->bValue*TEN_T3V_CONTR(tenA, egrad + 3*ii); argB = -pvlData->tec1->bValue*TEN_T3V_CONTR(tenB, egrad + 3*ii); if (pvlData->levmarUseFastExp) { sigA = airFastExp(argA); sigB = airFastExp(argB); } else { sigA = exp(argA); sigB = exp(argB); } xx[ii] = pvlData->tec1->knownB0*(pp[1]*sigA + (1-pp[1])*sigB); } return; } void _tenDwiGageAnswer(gageContext *ctx, gagePerVolume *pvl) { static const char me[]="_tenDwiGageAnswer"; unsigned int dwiIdx; tenDwiGageKindData *kindData; tenDwiGagePvlData *pvlData; double *dwiAll, dwiMean=0, tentmp[7]; kindData = AIR_CAST(tenDwiGageKindData *, pvl->kind->data); pvlData = AIR_CAST(tenDwiGagePvlData *, pvl->data); dwiAll = pvl->directAnswer[tenDwiGageAll]; if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageAll)) { /* done if doV */ if (ctx->verbose) { for (dwiIdx=0; dwiIdxkind->valLen; dwiIdx++) { fprintf(stderr, "%s(%d+%g,%d+%g,%d+%g): dwi[%u] = %g\n", me, ctx->point.idx[0], ctx->point.frac[0], ctx->point.idx[1], ctx->point.frac[1], ctx->point.idx[2], ctx->point.frac[2], dwiIdx, dwiAll[dwiIdx]); } fprintf(stderr, "%s: type(ngrad) = %d = %s\n", me, kindData->ngrad->type, airEnumStr(nrrdType, kindData->ngrad->type)); } } /* if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageB0)) { if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageJustDWI)) { done if doV } */ /* HEY this isn't valid for multiple b-values */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageADC)) { double logdwi, logb0; logb0 = log(AIR_MAX(kindData->valueMin, pvl->directAnswer[tenDwiGageB0][0])); for (dwiIdx=1; dwiIdxkind->valLen; dwiIdx++) { logdwi = log(AIR_MAX(kindData->valueMin, pvl->directAnswer[tenDwiGageJustDWI][dwiIdx-1])); pvl->directAnswer[tenDwiGageADC][dwiIdx-1] = (logb0 - logdwi)/kindData->bval; } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageMeanDWIValue)) { dwiMean = 0; for (dwiIdx=1; dwiIdxkind->valLen; dwiIdx++) { dwiMean += dwiAll[dwiIdx]; } dwiMean /= pvl->kind->valLen; pvl->directAnswer[tenDwiGageMeanDWIValue][0] = dwiMean; } /* note: the gage interface to tenEstimate functionality allows you exactly one kind of tensor estimation (per kind), so the function call to do the estimation is actually repeated over and over again; the copy into the answer buffer is what changes... */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLS)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorLLS], tentmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSError)) { pvl->directAnswer[tenDwiGageTensorLLSError][0] = pvlData->tec1->errorDwi; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSErrorLog)) { pvl->directAnswer[tenDwiGageTensorLLSErrorLog][0] = pvlData->tec1->errorLogDwi; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorWLS)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorWLS], tentmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorNLS)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorNLS], tentmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorMLE)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorMLE], tentmp); } /* HEY: have to implement all the different kinds of errors */ /* BEGIN sneakiness ........ */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensor)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensor; TEN_T_COPY(pvl->directAnswer[tenDwiGageTensor], pvl->directAnswer[item->prereq[0]]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorError)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensorError; pvl->directAnswer[tenDwiGageTensorError][0] = pvl->directAnswer[item->prereq[0]][0]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorErrorLog)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensorErrorLog; pvl->directAnswer[tenDwiGageTensorErrorLog][0] = pvl->directAnswer[item->prereq[0]][0]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLikelihood)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensorLikelihood; pvl->directAnswer[tenDwiGageTensorLikelihood][0] = pvl->directAnswer[item->prereq[0]][0]; } /* END sneakiness ........ */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageFA)) { pvl->directAnswer[tenDwiGageFA][0] = pvl->directAnswer[tenDwiGageTensor][0] * tenAnisoTen_d(pvl->directAnswer[tenDwiGageTensor], tenAniso_FA); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorAllDWIError)) { const double *grads; int gradcount; double *ten, d; int i; /* HEY: should switch to tenEstimate-based DWI simulation */ ten = pvl->directAnswer[tenDwiGageTensor]; gradcount = pvl->kind->valLen -1; /* Dont count b0 */ grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ for( i=0; i < gradcount; i++ ) { d = dwiAll[0]*exp(- pvlData->tec1->bValue * TEN_T3V_CONTR(ten, grads + 3*i)); pvl->directAnswer[tenDwiGageTensorAllDWIError][i] = dwiAll[i+1] - d; } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSeg)) { const double *grads; int gradcount; double *twoten; unsigned int valIdx, E; twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; gradcount = pvl->kind->valLen -1; /* Dont count b0 */ grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ if (dwiAll[0] != 0) { /* S0 = 0 */ _tenQball(pvlData->tec2->bValue, gradcount, dwiAll, grads, pvlData->qvals); _tenQvals2points(gradcount, pvlData->qvals, grads, pvlData->qpoints); _tenSegsamp2(gradcount, pvlData->qvals, grads, pvlData->qpoints, pvlData->wght + 1, pvlData->dists ); } else { /* stupid; should really return right here since data is garbage */ for (valIdx=1; valIdx < AIR_CAST(unsigned int, gradcount+1); valIdx++) { pvlData->wght[valIdx] = valIdx % 2; } } E = 0; for (valIdx=1; valIdxkind->valLen; valIdx++) { if (!E) E |= tenEstimateSkipSet(pvlData->tec2, valIdx, pvlData->wght[valIdx]); } if (!E) E |= tenEstimateUpdate(pvlData->tec2); if (!E) E |= tenEstimate1TensorSingle_d(pvlData->tec2, twoten + 0, dwiAll); for (valIdx=1; valIdxkind->valLen; valIdx++) { if (!E) E |= tenEstimateSkipSet(pvlData->tec2, valIdx, 1 - pvlData->wght[valIdx]); } if (!E) E |= tenEstimateUpdate(pvlData->tec2); if (!E) E |= tenEstimate1TensorSingle_d(pvlData->tec2, twoten + 7, dwiAll); if (E) { char *terr; terr = biffGetDone(TEN); fprintf(stderr, "%s: (trouble) %s\n", me, terr); free(terr); } /* hack: confidence for two-tensor fit */ twoten[0] = (twoten[0] + twoten[7])/2; twoten[7] = 0.5; /* fraction that is the first tensor (initial value) */ /* twoten[1 .. 6] = first tensor */ /* twoten[8 .. 13] = second tensor */ /* Compute fraction between tensors if not garbage in this voxel */ if (twoten[0] > 0.5) { double exp0,exp1,d,e=0,g=0, a=0,b=0; int i; for( i=0; i < gradcount; i++ ) { exp0 = exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 0, grads + 3*i)); exp1 = exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 7, grads + 3*i)); d = dwiAll[i+1] / dwiAll[0]; e = exp0 - exp1; g = d - exp1; a += .5*e*e; b += e*g; } twoten[7] = AIR_CLAMP(0, 0.5*(b/a), 1); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSegError)) { const double *grads; int gradcount; double *twoten, d; int i; /* HEY: should switch to tenEstimate-based DWI simulation */ if (dwiAll[0] != 0) { /* S0 = 0 */ twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; gradcount = pvl->kind->valLen -1; /* Dont count b0 */ grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ pvl->directAnswer[tenDwiGage2TensorQSegError][0] = 0; for( i=0; i < gradcount; i++ ) { d = twoten[7]*exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 0, grads + 3*i)); d += (1 - twoten[7])*exp(-pvlData->tec2->bValue *TEN_T3V_CONTR(twoten + 7, grads + 3*i)); d = dwiAll[i+1]/dwiAll[0] - d; pvl->directAnswer[tenDwiGage2TensorQSegError][0] += d*d; } pvl->directAnswer[tenDwiGage2TensorQSegError][0] = sqrt( pvl->directAnswer[tenDwiGage2TensorQSegError][0] ); } else { /* HEY: COMPLETELY WRONG!! An error is not defined! */ pvl->directAnswer[tenDwiGage2TensorQSegError][0] = 0; } /* printf("%f\n",pvl->directAnswer[tenDwiGage2TensorQSegError][0]); */ } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSegAndError)) { double *twoten, *err, *twotenerr; twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; err = pvl->directAnswer[tenDwiGage2TensorQSegError]; twotenerr = pvl->directAnswer[tenDwiGage2TensorQSegAndError]; TEN_T_COPY(twotenerr + 0, twoten + 0); TEN_T_COPY(twotenerr + 7, twoten + 7); twotenerr[14] = err[0]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorPeled)) { #if TEEM_LEVMAR #define PARAMS 4 double *twoTen, Cp /* , residual, AICSingFit, AICTwoFit */; /* Vars for the NLLS */ double guess[PARAMS], loBnd[PARAMS], upBnd[PARAMS], opts[LM_OPTS_SZ], *grad, *egrad, tenA[7], tenB[7], matA[9], matB[9], matTmp[9], rott[9]; unsigned int gi; int lmret; /* Pointer to the location where the two tensor will be written */ twoTen = pvl->directAnswer[tenDwiGage2TensorPeled]; /* Estimate the DWI error, error is given as standard deviation */ pvlData->tec1->recordErrorDwi = AIR_FALSE; /* Estimate the single tensor */ tenEstimate1TensorSingle_d(pvlData->tec1, pvlData->ten1, dwiAll); /* Get the eigenValues and eigen vectors for this tensor */ tenEigensolve_d(pvlData->ten1Eval, pvlData->ten1Evec, pvlData->ten1); /* Get westins Cp */ Cp = tenAnisoEval_d(pvlData->ten1Eval, tenAniso_Cp1); /* Only do two-tensor fitting if CP is greater or equal to than a user-defined threshold */ if (Cp >= pvlData->levmarMinCp) { /* Calculate the residual, need the variance to sqr it */ /* residual = pvlData->tec1->errorDwi*pvlData->tec1->errorDwi; */ /* Calculate the AIC for single tensor fit */ /* AICSingFit = _tenComputeAIC(residual, pvlData->tec1->dwiNum, 6); */ /* the CP-based test is gone; caller's responsibility */ /* rotate DW gradients by inverse of eigenvector column matrix and place into pvlData->nten1EigenGrads (which has been allocated by _tenDwiGagePvlDataNew()) */ grad = AIR_CAST(double *, kindData->ngrad->data); egrad = AIR_CAST(double *, pvlData->nten1EigenGrads->data); for (gi=0; gingrad->axis[1].size; gi++) { /* yes, this is also transforming some zero-length (B0) gradients; that's harmless */ ELL_3MV_MUL(egrad, pvlData->ten1Evec, grad); grad += 3; egrad += 3; } /* Lower and upper bounds for the NLLS routine */ loBnd[0] = 0.0; loBnd[1] = 0.0; loBnd[2] = -AIR_PI/2; loBnd[3] = -AIR_PI/2; upBnd[0] = pvlData->ten1Eval[0]*5; upBnd[1] = 1.0; upBnd[2] = AIR_PI/2; upBnd[3] = AIR_PI/2; /* Starting point for the NLLS */ guess[0] = pvlData->ten1Eval[0]; guess[1] = 0.5; guess[2] = AIR_PI/4; guess[3] = -AIR_PI/4; /* guess[2] = AIR_AFFINE(0, airDrandMT_r(pvlData->randState), 1, AIR_PI/6, AIR_PI/3); guess[3] = AIR_AFFINE(0, airDrandMT_r(pvlData->randState), 1, -AIR_PI/6, -AIR_PI/3); */ /* Fill in the constraints for the LM optimization, the threshold of error difference */ opts[0] = pvlData->levmarTau; opts[1] = pvlData->levmarEps1; opts[2] = pvlData->levmarEps2; opts[3] = pvlData->levmarEps3; /* Very imp to set this opt, note that only forward differences are used to approx Jacobian */ opts[4] = pvlData->levmarDelta; /* run NLLS, results are stored back into guess[] */ pvlData->levmarUseFastExp = AIR_FALSE; lmret = dlevmar_bc_dif(_tenLevmarPeledCB, guess, pvlData->tec1->dwi, PARAMS, pvlData->tec1->dwiNum, loBnd, upBnd, NULL, pvlData->levmarMaxIter, opts, pvlData->levmarInfo, NULL, NULL, pvlData); if (-1 == lmret) { ctx->errNum = 1; sprintf(ctx->errStr, "%s: dlevmar_bc_dif() failed!", me); } else { /* Get the AIC for the two tensor fit, use the levmarinfo to get the residual */ /* residual = pvlData->levmarInfo[1]/pvlData->tec1->dwiNum; AICTwoFit = _tenComputeAIC(residual, pvlData->tec1->dwiNum, 12); */ /* Form the tensors using the estimated pp, returned in guess */ _tenPeledRotate2D(tenA, guess[0], pvlData->ten1Eval[2], guess[2]); _tenPeledRotate2D(tenB, guess[0], pvlData->ten1Eval[2], guess[3]); TEN_T2M(matA, tenA); TEN_T2M(matB, tenB); ELL_3M_TRANSPOSE(rott, pvlData->ten1Evec); ELL_3M_MUL(matTmp, matA, pvlData->ten1Evec); ELL_3M_MUL(matA, rott, matTmp); ELL_3M_MUL(matTmp, matB, pvlData->ten1Evec); ELL_3M_MUL(matB, rott, matTmp); /* Copy two two tensors */ /* guess[1] is population fraction of first tensor */ if (guess[1] > 0.5) { twoTen[7] = guess[1]; TEN_M2T(twoTen + 0, matA); TEN_M2T(twoTen + 7, matB); } else { twoTen[7] = 1 - guess[1]; TEN_M2T(twoTen + 0, matB); TEN_M2T(twoTen + 7, matA); } twoTen[0] = 1; } } else { /* its too planar- just do single tensor fit */ TEN_T_COPY(twoTen, pvlData->ten1); TEN_T_SET(twoTen + 7, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); } #undef PARAMS #else double *twoTen; twoTen = pvl->directAnswer[tenDwiGage2TensorPeled]; TEN_T_SET(twoTen + 0, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); TEN_T_SET(twoTen + 7, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); fprintf(stderr, "%s: sorry, not compiled with TEEM_LEVMAR\n", me); #endif } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorPeledError)) { double *info; info = pvlData->levmarInfo; pvl->directAnswer[tenDwiGage2TensorPeledError][0] = 0; if (info[1] > 0) { /* Returning the standard deviation */ pvl->directAnswer[tenDwiGage2TensorPeledError][0] = sqrt(info[1]/pvlData->tec1->dwiNum); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorPeledAndError)) { double *twoten, *err, *twotenerr; /* HEY cut and paste */ twoten = pvl->directAnswer[tenDwiGage2TensorPeled]; err = pvl->directAnswer[tenDwiGage2TensorPeledError]; twotenerr = pvl->directAnswer[tenDwiGage2TensorPeledAndError]; TEN_T_COPY(twotenerr + 0, twoten + 0); TEN_T_COPY(twotenerr + 7, twoten + 7); twotenerr[14] = err[0]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorPeledLevmarInfo)) { double *info; unsigned int ii, alen; alen = gageKindAnswerLength(pvl->kind, tenDwiGage2TensorPeledLevmarInfo); info = pvl->directAnswer[tenDwiGage2TensorPeledLevmarInfo]; for (ii=0; iilevmarInfo[ii]; } } return; } /* --------------------- pvlData */ /* note use of the GAGE biff key */ void * _tenDwiGagePvlDataNew(const gageKind *kind) { static const char me[]="_tenDwiGagePvlDataNew"; tenDwiGagePvlData *pvlData; tenDwiGageKindData *kindData; const int segcount = 2; unsigned int num; int E; if (tenDwiGageKindCheck(kind)) { biffMovef(GAGE, TEN, "%s: kindData not ready for use", me); return NULL; } kindData = AIR_CAST(tenDwiGageKindData *, kind->data); pvlData = AIR_CALLOC(1, tenDwiGagePvlData); if (!pvlData) { biffAddf(GAGE, "%s: couldn't allocate pvl data!", me); return NULL; } pvlData->tec1 = tenEstimateContextNew(); pvlData->tec2 = tenEstimateContextNew(); for (num=1; num<=2; num++) { tenEstimateContext *tec; tec = (1 == num ? pvlData->tec1 : pvlData->tec2); E = 0; if (!E) tenEstimateVerboseSet(tec, 0); if (!E) tenEstimateNegEvalShiftSet(tec, AIR_FALSE); if (!E) E |= tenEstimateMethodSet(tec, 1 == num ? kindData->est1Method : kindData->est2Method); if (!E) E |= tenEstimateValueMinSet(tec, kindData->valueMin); if (kindData->ngrad->data) { if (!E) E |= tenEstimateGradientsSet(tec, kindData->ngrad, kindData->bval, AIR_FALSE); } else { if (!E) E |= tenEstimateBMatricesSet(tec, kindData->nbmat, kindData->bval, AIR_FALSE); } if (!E) E |= tenEstimateThresholdSet(tec, kindData->thresh, kindData->soft); if (!E) E |= tenEstimateUpdate(tec); if (E) { biffMovef(GAGE, TEN, "%s: trouble setting %u estimation", me, num); return NULL; } } pvlData->vbuf = AIR_CALLOC(kind->valLen, double); pvlData->wght = AIR_CALLOC(kind->valLen, unsigned int); /* HEY: this is where we act on the the assumption about having val[0] be T2 baseline and all subsequent val[i] be DWIs */ pvlData->wght[0] = 1; pvlData->qvals = AIR_CALLOC(kind->valLen-1, double); pvlData->qpoints = AIR_CALLOC(3*(kind->valLen-1), double); pvlData->dists = AIR_CALLOC(segcount*(kind->valLen-1), double); pvlData->weights = AIR_CALLOC(segcount*(kind->valLen-1), double); if (kindData->ngrad->data) { pvlData->nten1EigenGrads = nrrdNew(); /* this is for allocation only; values will get over-written */ nrrdCopy(pvlData->nten1EigenGrads, kindData->ngrad); } else { /* HEY: currently don't handle general B-matrices here */ pvlData->nten1EigenGrads = NULL; } pvlData->randSeed = kindData->randSeed; pvlData->randState = airRandMTStateNew(pvlData->randSeed); /* initialize single-tensor info to all NaNs */ TEN_T_SET(pvlData->ten1, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(pvlData->ten1Evec + 0, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(pvlData->ten1Evec + 3, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(pvlData->ten1Evec + 6, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(pvlData->ten1Eval, AIR_NAN, AIR_NAN, AIR_NAN); /* here's an okay spot to check our compile-time assumptions about the levmar library */ #if TEEM_LEVMAR /* this is needed to make sure that the tenDwiGage2TensorPeledLevmarInfo item definition above is valid */ if (5 != LM_OPTS_SZ) { biffAddf(GAGE, "%s: LM_OPTS_SZ (%d) != expected 5\n", me, LM_OPTS_SZ); return NULL; } #endif pvlData->levmarUseFastExp = AIR_FALSE; pvlData->levmarMaxIter = 200; pvlData->levmarTau = 1E-03; /* LM_INIT_MU; */ pvlData->levmarEps1 = 1E-8; pvlData->levmarEps2 = 1E-8; pvlData->levmarEps3 = 1E-8; pvlData->levmarDelta = 1E-8; pvlData->levmarMinCp = 0.1; /* pvlData->levmarInfo[] is output; not initialized */ return AIR_CAST(void *, pvlData); } void * _tenDwiGagePvlDataCopy(const gageKind *kind, const void *_pvlDataOld) { tenDwiGagePvlData *pvlDataOld, *pvlDataNew; pvlDataOld = AIR_CAST(tenDwiGagePvlData *, _pvlDataOld); pvlDataNew = AIR_CAST(tenDwiGagePvlData *, _tenDwiGagePvlDataNew(kind)); /* HEY: no error checking? */ if (pvlDataOld->nten1EigenGrads) { nrrdCopy(pvlDataNew->nten1EigenGrads, pvlDataOld->nten1EigenGrads); } /* need to copy randState or randSeed? */ TEN_T_COPY(pvlDataNew->ten1, pvlDataOld->ten1); ELL_3M_COPY(pvlDataNew->ten1Evec, pvlDataOld->ten1Evec); ELL_3V_COPY(pvlDataNew->ten1Eval, pvlDataOld->ten1Eval); pvlDataNew->levmarUseFastExp = pvlDataOld->levmarUseFastExp; pvlDataNew->levmarMaxIter = pvlDataOld->levmarMaxIter; pvlDataNew->levmarTau = pvlDataOld->levmarTau; pvlDataNew->levmarEps1 = pvlDataOld->levmarEps1; pvlDataNew->levmarEps2 = pvlDataOld->levmarEps2; pvlDataNew->levmarEps3 = pvlDataOld->levmarEps3; pvlDataNew->levmarDelta = pvlDataOld->levmarDelta; pvlDataNew->levmarMinCp = pvlDataOld->levmarMinCp; /* pvlData->levmarInfo[] is output; not copied */ return pvlDataNew; } int _tenDwiGagePvlDataUpdate(const gageKind *kind, const gageContext *ctx, const gagePerVolume *pvl, const void *_pvlData) { /* static const char me[]="_tenDwiGagePvlDataUpdate"; */ tenDwiGagePvlData *pvlData; AIR_UNUSED(ctx); pvlData = AIR_CAST(tenDwiGagePvlData *, _pvlData); AIR_UNUSED(kind); if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSError) || GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorWLSError) || GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorNLSError) || GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorMLEError)) { pvlData->tec1->recordErrorDwi = AIR_TRUE; } else { pvlData->tec1->recordErrorDwi = AIR_FALSE; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSErrorLog) || GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorWLSErrorLog) || GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorNLSErrorLog) || GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorMLEErrorLog)) { pvlData->tec1->recordErrorLogDwi = AIR_TRUE; } else { pvlData->tec1->recordErrorLogDwi = AIR_FALSE; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSLikelihood) || GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorWLSLikelihood) || GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorNLSLikelihood) || GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorMLELikelihood)) { pvlData->tec1->recordLikelihoodDwi = AIR_TRUE; } else { pvlData->tec1->recordLikelihoodDwi = AIR_FALSE; } /* fprintf(stderr, "%s: record %d %d %d\n", me, pvlData->tec1->recordErrorDwi, pvlData->tec1->recordErrorLogDwi, pvlData->tec1->recordLikelihoodDwi); */ return 0; } void * _tenDwiGagePvlDataNix(const gageKind *kind, void *_pvlData) { tenDwiGagePvlData *pvlData; AIR_UNUSED(kind); pvlData = AIR_CAST(tenDwiGagePvlData *, _pvlData); if (pvlData) { tenEstimateContextNix(pvlData->tec1); tenEstimateContextNix(pvlData->tec2); airFree(pvlData->vbuf); airFree(pvlData->wght); airFree(pvlData->qvals); airFree(pvlData->qpoints); airFree(pvlData->dists); airFree(pvlData->weights); nrrdNuke(pvlData->nten1EigenGrads); airRandMTStateNix(pvlData->randState); airFree(pvlData); } return NULL; } /* --------------------- kindData */ tenDwiGageKindData* tenDwiGageKindDataNew(void) { tenDwiGageKindData *ret; ret = AIR_CALLOC(1, tenDwiGageKindData); if (ret) { /* it may be that only one of these is actually filled */ ret->ngrad = nrrdNew(); ret->nbmat = nrrdNew(); ret->thresh = ret->soft = ret->bval = AIR_NAN; ret->est1Method = tenEstimate1MethodUnknown; ret->est2Method = tenEstimate2MethodUnknown; ret->randSeed = 42; } return ret; } tenDwiGageKindData* tenDwiGageKindDataNix(tenDwiGageKindData *kindData) { if (kindData) { nrrdNuke(kindData->ngrad); nrrdNuke(kindData->nbmat); airFree(kindData); } return NULL; } /* --------------------- dwiKind, and dwiKind->data setting*/ /* ** Because this kind has to be dynamically allocated, ** this is not the kind, but just the template for it ** HEY: having a const public version of this could be a ** nice way of having a way of referring to the dwiKind ** without having to allocate it each time */ gageKind _tenDwiGageKindTmpl = { AIR_TRUE, /* dynamically allocated */ TEN_DWI_GAGE_KIND_NAME, &_tenDwiGage, 1, 0, /* NOT: set later by tenDwiGageKindSet() */ TEN_DWI_GAGE_ITEM_MAX, NULL, /* NOT: modified copy of _tenDwiGageTable, allocated by tenDwiGageKindNew(), and set by _tenDwiGageKindSet() */ _tenDwiGageIv3Print, _tenDwiGageFilter, _tenDwiGageAnswer, _tenDwiGagePvlDataNew, _tenDwiGagePvlDataCopy, _tenDwiGagePvlDataNix, _tenDwiGagePvlDataUpdate, NULL /* NOT: allocated by tenDwiGageKindNew(), insides set by tenDwiGageKindSet() */ }; gageKind * tenDwiGageKindNew() { gageKind *kind; kind = AIR_CALLOC(1, gageKind); if (kind) { memcpy(kind, &_tenDwiGageKindTmpl, sizeof(gageKind)); kind->valLen = 0; /* still has to be set later */ kind->table = AIR_CAST(gageItemEntry *, malloc(sizeof(_tenDwiGageTable))); memcpy(kind->table, _tenDwiGageTable, sizeof(_tenDwiGageTable)); kind->data = AIR_CAST(void *, tenDwiGageKindDataNew()); } return kind; } gageKind * tenDwiGageKindNix(gageKind *kind) { if (kind) { airFree(kind->table); tenDwiGageKindDataNix(AIR_CAST(tenDwiGageKindData *, kind->data)); airFree(kind); } return NULL; } /* ** NOTE: this sets information in both the kind and kindData */ int tenDwiGageKindSet(gageKind *dwiKind, double thresh, double soft, double bval, double valueMin, const Nrrd *ngrad, const Nrrd *nbmat, int e1method, int e2method, unsigned int randSeed) { static const char me[]="tenDwiGageKindSet"; tenDwiGageKindData *kindData; double grad[3], (*lup)(const void *, size_t); unsigned int gi; if (!dwiKind) { biffAddf(TEN, "%s: got NULL pointer", me); return 0; } if (!( !!(ngrad) ^ !!(nbmat) )) { biffAddf(TEN, "%s: need exactly one non-NULL in {ngrad,nbmat}", me); return 1; } if (nbmat) { biffAddf(TEN, "%s: sorry, B-matrices temporarily disabled", me); return 1; } /* (used for detecting errors in losslessly writing/reading a gradient set) { fprintf(stderr, "!%s: saving ngrad.nrrd\n", me); if (ngrad) { nrrdSave("ngrad.nrrd", ngrad, NULL); } } */ if (tenGradientCheck(ngrad, nrrdTypeDefault, 7)) { biffAddf(TEN, "%s: problem with given gradients", me); return 1; } /* make sure that gradient lengths are as expected */ lup = nrrdDLookup[ngrad->type]; grad[0] = lup(ngrad->data, 0); grad[1] = lup(ngrad->data, 1); grad[2] = lup(ngrad->data, 2); if (0.0 != ELL_3V_LEN(grad)) { biffAddf(TEN, "%s: sorry, currently need grad[0] to be len 0 (not %g)", me, ELL_3V_LEN(grad)); return 1; } for (gi=1; giaxis[1].size; gi++) { grad[0] = lup(ngrad->data, 0 + 3*gi); grad[1] = lup(ngrad->data, 1 + 3*gi); grad[2] = lup(ngrad->data, 2 + 3*gi); if (0.0 == ELL_3V_LEN(grad)) { biffAddf(TEN, "%s: sorry, all but first gradient must be non-zero " "(%u is zero)", me, gi); return 1; } } if (airEnumValCheck(tenEstimate1Method, e1method)) { biffAddf(TEN, "%s: e1method %d is not a valid %s", me, e1method, tenEstimate1Method->name); return 1; } if (airEnumValCheck(tenEstimate2Method, e2method)) { biffAddf(TEN, "%s: emethod %d is not a valid %s", me, e2method, tenEstimate2Method->name); return 1; } kindData = AIR_CAST(tenDwiGageKindData *, dwiKind->data); if (nrrdConvert(kindData->ngrad, ngrad, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: trouble converting", me); return 1; } dwiKind->valLen = kindData->ngrad->axis[1].size; /* fixing up the item table ... */ dwiKind->table[tenDwiGageAll].answerLength = dwiKind->valLen; dwiKind->table[tenDwiGageJustDWI].answerLength = dwiKind->valLen - 1; dwiKind->table[tenDwiGageADC].answerLength = dwiKind->valLen - 1; dwiKind->table[tenDwiGageTensorAllDWIError].answerLength = dwiKind->valLen - 1; switch (e1method) { case tenEstimate1MethodLLS: dwiKind->table[tenDwiGageTensor].prereq[0] = tenDwiGageTensorLLS; dwiKind->table[tenDwiGageTensorError].prereq[0] = tenDwiGageTensorLLSError; dwiKind->table[tenDwiGageTensorErrorLog].prereq[0] = tenDwiGageTensorLLSErrorLog; dwiKind->table[tenDwiGageTensorLikelihood].prereq[0] = tenDwiGageTensorLLSLikelihood; break; case tenEstimate1MethodWLS: dwiKind->table[tenDwiGageTensor].prereq[0] = tenDwiGageTensorWLS; dwiKind->table[tenDwiGageTensorError].prereq[0] = tenDwiGageTensorWLSError; dwiKind->table[tenDwiGageTensorErrorLog].prereq[0] = tenDwiGageTensorWLSErrorLog; dwiKind->table[tenDwiGageTensorLikelihood].prereq[0] = tenDwiGageTensorWLSLikelihood; break; case tenEstimate1MethodNLS: dwiKind->table[tenDwiGageTensor].prereq[0] = tenDwiGageTensorNLS; dwiKind->table[tenDwiGageTensorError].prereq[0] = tenDwiGageTensorNLSError; dwiKind->table[tenDwiGageTensorErrorLog].prereq[0] = tenDwiGageTensorNLSErrorLog; dwiKind->table[tenDwiGageTensorLikelihood].prereq[0] = tenDwiGageTensorNLSLikelihood; break; case tenEstimate1MethodMLE: dwiKind->table[tenDwiGageTensor].prereq[0] = tenDwiGageTensorMLE; dwiKind->table[tenDwiGageTensorError].prereq[0] = tenDwiGageTensorMLEError; dwiKind->table[tenDwiGageTensorErrorLog].prereq[0] = tenDwiGageTensorMLEErrorLog; dwiKind->table[tenDwiGageTensorLikelihood].prereq[0] = tenDwiGageTensorMLELikelihood; break; default: biffAddf(TEN, "%s: unimplemented %s: %s (%d)", me, tenEstimate1Method->name, airEnumStr(tenEstimate1Method, e1method), e1method); return 1; break; } kindData->thresh = thresh; kindData->soft = soft; kindData->bval = bval; kindData->valueMin = valueMin; kindData->est1Method = e1method; kindData->est2Method = e2method; kindData->randSeed = randSeed; return 0; } int tenDwiGageKindCheck(const gageKind *kind) { static const char me[]="tenDwiGageKindCheck"; if (!kind) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (strcmp(kind->name, TEN_DWI_GAGE_KIND_NAME)) { biffAddf(TEN, "%s: got \"%s\" kind, not \"%s\"", me, kind->name, TEN_DWI_GAGE_KIND_NAME); return 1; } if (0 == kind->valLen) { biffAddf(TEN, "%s: don't yet know valLen", me); return 1; } if (!kind->data) { biffAddf(TEN, "%s: kind->data is NULL", me); return 1; } return 0; } teem-1.11.0~svn6057/src/ten/tendBfit.c0000664000175000017500000001024412165631065017073 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Non-linear least-squares fitting of b-value curves" static const char *_tend_bfitInfoL = (INFO ". Axis 0 is replaced by three values: amp, dec, err, based on a " "non-linear least-squares fit of amp*exp(-b*dec) to the range of DWI " "values along input axis 0, as a function of changing b values. "); int tend_bfitMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; double *bb, *ww, *_ww, eps; unsigned int ii, bbLen, _wwLen; int iterMax; char *outS; hparm->respFileEnable = AIR_TRUE; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "Input nrrd. List of DWIs from different b-values must " "be along axis 0", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "b", "b1 b2", airTypeDouble, 2, -1, &bb, NULL, "b values across axis 0 of input nrrd", &bbLen); hestOptAdd(&hopt, "w", "w1 w2", airTypeDouble, 2, -1, &_ww, "nan nan", "weights for samples in non-linear fitting", &_wwLen); hestOptAdd(&hopt, "imax", "# iter", airTypeInt, 1, 1, &iterMax, "10", "max number of iterations to use in non-linear fitting, or, " "use 0 to do only initial linear fit"); hestOptAdd(&hopt, "eps", "epsilon", airTypeDouble, 1, 1, &eps, "1", "epsilon convergence threshold for non-linear fitting"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output tensor volume"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_bfitInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( bbLen == nin->axis[0].size )) { char stmp[AIR_STRLEN_SMALL]; fprintf(stderr, "%s: got %d b-values but axis 0 size is %s\n", me, bbLen, airSprintSize_t(stmp, nin->axis[0].size)); airMopError(mop); return 1; } if (AIR_EXISTS(_ww[0])) { if (!( _wwLen == nin->axis[0].size )) { char stmp[AIR_STRLEN_SMALL]; fprintf(stderr, "%s: got %d weights but axis 0 size is %s\n", me, _wwLen, airSprintSize_t(stmp, nin->axis[0].size)); airMopError(mop); return 1; } ww = _ww; } else { /* no explicit weights specified */ ww = (double*)calloc(nin->axis[0].size, sizeof(double)); airMopAdd(mop, ww, airFree, airMopAlways); for (ii=0; iiaxis[0].size; ii++) { ww[ii] = 1.0; } } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenBVecNonLinearFit(nout, nin, bb, ww, iterMax, eps)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(bfit, INFO); teem-1.11.0~svn6057/src/ten/tendUnmf.c0000664000175000017500000000541012165631065017113 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Applies and removes the measurement frame" static const char *_tend_unmfInfoL = (INFO ". When the given tensor volume has a measurement frame associated " "with it, this will apply the measurement frame transform to all " "tensors to convert them into world space, and remove the measurement " "frame from the nrrd."); int tend_unmfMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input diffusion tensor volume " "(sorry, can't use usual default of \"-\" for stdin " "because of hest quirk)", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output tensor volume"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_unmfInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenMeasurementFrameReduce(nout, nin)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(unmf, INFO); teem-1.11.0~svn6057/src/ten/modelBall1StickEMD.c0000664000175000017500000000527012165631065020637 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define PARM_NUM 6 static const tenModelParmDesc parmDesc[] = { /* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 1 */ {"diffusivity", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 2 */ {"fraction", 0.0, 1.0, AIR_FALSE, AIR_FALSE, 0}, /* 3 */ {"x", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 0}, /* 4 */ {"y", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 1}, /* 5 */ {"z", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 2} }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; double b0, diff, frac, vec[3]; b0 = parm[0]; diff = parm[1]; frac = parm[2]; vec[0] = parm[3]; vec[1] = parm[4]; vec[2] = parm[5]; for (ii=0; iiimgNum; ii++) { double dwiBall, dwiStck, dot; dwiBall = exp(-espec->bval[ii]*diff); dot = ELL_3V_DOT(vec, espec->grad + 3*ii); dwiStck = exp(-espec->bval[ii]*diff*dot*dot); dwiSim[ii] = b0*((1-frac)*dwiBall + frac*dwiStck); } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { sprintf(str, "(%g) %g * (%g + %g*(%g,%g,%g))", parm[0], parm[1], 1-parm[2], parm[2], parm[3], parm[4], parm[5]); return str; } _TEN_PARM_ALLOC _TEN_PARM_RAND _TEN_PARM_STEP _TEN_PARM_DIST _TEN_PARM_COPY _TEN_PARM_CONVERT_NOOP _TEN_SQE _TEN_SQE_GRAD_CENTDIFF _TEN_SQE_FIT(tenModelBall1StickEMD) _TEN_NLL _TEN_NLL_GRAD_STUB _TEN_NLL_FIT_STUB tenModel _tenModelBall1StickEMD = { TEN_MODEL_STR_BALL1STICKEMD, _TEN_MODEL_FIELDS }; const tenModel *const tenModelBall1StickEMD = &_tenModelBall1StickEMD; teem-1.11.0~svn6057/src/ten/tendEvec.c0000664000175000017500000001134112165631065017070 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Calculate one or more eigenvectors in a DT volume" static const char *_tend_evecInfoL = (INFO ". "); int tend_evecMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int ret, *comp, compLen, cc; Nrrd *nin, *nout; char *outS; float thresh, *edata, *tdata, eval[3], evec[9], scl; size_t N, I, sx, sy, sz; hestOptAdd(&hopt, "c", "c0 ", airTypeInt, 1, 3, &comp, NULL, "which eigenvalues should be saved out. \"0\" for the " "largest, \"1\" for the middle, \"2\" for the smallest, " "\"0 1\", \"1 2\", \"0 1 2\" or similar for more than one", &compLen); hestOptAdd(&hopt, "t", "thresh", airTypeFloat, 1, 1, &thresh, "0.5", "confidence threshold"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_evecInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); for (cc=0; ccaxis[1].size; sy = nin->axis[2].size; sz = nin->axis[3].size; nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); ret = nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 4, AIR_CAST(size_t, 3*compLen), sx, sy, sz); if (ret) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble allocating output:\n%s\n", me, err); airMopError(mop); return 1; } N = sx*sy*sz; edata = (float *)nout->data; tdata = (float *)nin->data; if (1 == compLen) { for (I=0; I= thresh); ELL_3V_SCALE(edata, scl, evec+3*comp[0]); edata += 3; tdata += 7; } } else { for (I=0; I= thresh); for (cc=0; ccaxis[0].label = (char *)airFree(nout->axis[0].label); nout->axis[0].kind = nrrdKindUnknown; if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(evec, INFO); teem-1.11.0~svn6057/src/ten/fiberMethods.c0000664000175000017500000006505712165631065017763 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" void tenFiberSingleInit(tenFiberSingle *tfbs) { /* char me[]="tenFiberSingleInit"; */ unsigned idx; ELL_3V_SET(tfbs->seedPos, AIR_NAN, AIR_NAN, AIR_NAN); tfbs->dirIdx = tfbs->dirNum = 0; tfbs->nvert = nrrdNew(); tfbs->halfLen[0] = tfbs->halfLen[1] = AIR_NAN; tfbs->seedIdx = tfbs->stepNum[0] = tfbs->stepNum[1] = 0; tfbs->whyStop[0] = tfbs->whyStop[1] = tenFiberStopUnknown; tfbs->whyNowhere = tenFiberStopUnknown; /* actually, the semantics of this field is reversed, so this is not really the way it should be set */ tfbs->nval = nrrdNew(); for (idx=0; idx<=NRRD_MEASURE_MAX; idx++) { tfbs->measr[idx] = AIR_NAN; } return; } void tenFiberSingleDone(tenFiberSingle *tfbs) { tfbs->nvert = nrrdNuke(tfbs->nvert); tfbs->nval = nrrdNuke(tfbs->nval); } tenFiberSingle * tenFiberSingleNew() { tenFiberSingle *ret; ret = AIR_CALLOC(1, tenFiberSingle); if (ret) { tenFiberSingleInit(ret); } return ret; } tenFiberSingle * tenFiberSingleNix(tenFiberSingle *tfbs) { if (tfbs) { tenFiberSingleDone(tfbs); airFree(tfbs); } return NULL; } static tenFiberContext * _tenFiberContextCommonNew(const Nrrd *vol, int useDwi, double thresh, double soft, double valueMin, int ten1method, int ten2method) { static const char me[]="_tenFiberContextCommonNew"; tenFiberContext *tfx; gageKind *kind; airArray *mop; if (!( tfx = AIR_CALLOC(1, tenFiberContext) )) { biffAddf(TEN, "%s: couldn't allocate new context", me); return NULL; } mop = airMopNew(); airMopAdd(mop, tfx, airFree, airMopOnError); if (useDwi) { Nrrd *ngrad=NULL, *nbmat=NULL; double bval=0; unsigned int *skip=NULL, skipNum; tfx->useDwi = AIR_TRUE; /* default fiber type */ tfx->fiberType = tenDwiFiberTypeUnknown; if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &bval, &skip, &skipNum, vol)) { biffAddf(TEN, "%s: trouble parsing DWI info", me ); airMopError(mop); return NULL; } airMopAdd(mop, ngrad, (airMopper)nrrdNuke, airMopOnError); airMopAdd(mop, nbmat, (airMopper)nrrdNuke, airMopOnError); airMopAdd(mop, skip, airFree, airMopOnError); if (skipNum) { biffAddf(TEN, "%s: sorry, can't do DWI skipping here", me); airMopError(mop); return NULL; } kind = tenDwiGageKindNew(); airMopAdd(mop, kind, (airMopper)tenDwiGageKindNix, airMopOnError); if (tenDwiGageKindSet(kind, thresh, soft, bval, valueMin, ngrad, NULL, ten1method, ten2method, 42)) { biffAddf(TEN, "%s: trouble setting DWI kind", me); airMopError(mop); return NULL; } } else { /* it should be a tensor volume */ tfx->useDwi = AIR_FALSE; /* default fiber type */ tfx->fiberType = tenFiberTypeUnknown; if (tenTensorCheck(vol, nrrdTypeUnknown, AIR_TRUE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a tensor volume", me); airMopError(mop); return NULL; } kind = tenGageKind; } tfx->gtx = gageContextNew(); airMopAdd(mop, tfx->gtx, (airMopper)gageContextNix, airMopOnError); tfx->pvl = gagePerVolumeNew(tfx->gtx, vol, kind); airMopAdd(mop, tfx->pvl, (airMopper)gagePerVolumeNix, airMopOnError); if (!( tfx->gtx && tfx->pvl && !gagePerVolumeAttach(tfx->gtx, tfx->pvl) )) { biffMovef(TEN, GAGE, "%s: gage trouble", me); airMopError(mop); return NULL; } tfx->nin = vol; tfx->ksp = nrrdKernelSpecNew(); airMopAdd(mop, tfx->ksp, (airMopper)nrrdKernelSpecNix, airMopOnError); if (nrrdKernelSpecParse(tfx->ksp, tenDefFiberKernel)) { biffMovef(TEN, NRRD, "%s: couldn't parse tenDefFiberKernel \"%s\"", me, tenDefFiberKernel); airMopError(mop); return NULL; } if (tenFiberKernelSet(tfx, tfx->ksp->kernel, tfx->ksp->parm)) { biffAddf(TEN, "%s: couldn't set default kernel", me); airMopError(mop); return NULL; } tfx->fiberProbeItem = 0; /* unknown for any gageKind */ /* looks to GK like GK says that we must set some stop criterion */ tfx->intg = tenDefFiberIntg; tfx->anisoStopType = tenDefFiberAnisoStopType; tfx->anisoSpeedType = tenAnisoUnknown; tfx->stop = 0; tfx->anisoThresh = tenDefFiberAnisoThresh; /* so I'm not using the normal default mechanism, shoot me */ tfx->anisoSpeedFunc[0] = 0; tfx->anisoSpeedFunc[1] = 0; tfx->anisoSpeedFunc[2] = 0; tfx->maxNumSteps = tenDefFiberMaxNumSteps; tfx->minNumSteps = 0; tfx->useIndexSpace = tenDefFiberUseIndexSpace; tfx->verbose = 0; tfx->stepSize = tenDefFiberStepSize; tfx->maxHalfLen = tenDefFiberMaxHalfLen; tfx->minWholeLen = 0.0; tfx->confThresh = 0.5; /* why do I even bother setting these- they'll only get read if the right tenFiberStopSet has been called, in which case they'll be set... */ tfx->minRadius = 1; /* above lament applies here as well */ tfx->minFraction = 0.5; /* and here */ tfx->wPunct = tenDefFiberWPunct; GAGE_QUERY_RESET(tfx->query); tfx->mframe[0] = vol->measurementFrame[0][0]; tfx->mframe[1] = vol->measurementFrame[1][0]; tfx->mframe[2] = vol->measurementFrame[2][0]; tfx->mframe[3] = vol->measurementFrame[0][1]; tfx->mframe[4] = vol->measurementFrame[1][1]; tfx->mframe[5] = vol->measurementFrame[2][1]; tfx->mframe[6] = vol->measurementFrame[0][2]; tfx->mframe[7] = vol->measurementFrame[1][2]; tfx->mframe[8] = vol->measurementFrame[2][2]; if (ELL_3M_EXISTS(tfx->mframe)) { tfx->mframeUse = AIR_TRUE; ELL_3M_TRANSPOSE(tfx->mframeT, tfx->mframe); } else { tfx->mframeUse = AIR_FALSE; } tfx->gageAnisoStop = NULL; tfx->gageAnisoSpeed = NULL; tfx->ten2AnisoStop = AIR_NAN; /* ... don't really see the point of initializing the ten2 stuff here; its properly done in tenFiberTraceSet() ... */ tfx->radius = AIR_NAN; airMopOkay(mop); return tfx; } tenFiberContext * tenFiberContextDwiNew(const Nrrd *dwivol, double thresh, double soft, double valueMin, int ten1method, int ten2method) { static const char me[]="tenFiberContextDwiNew"; tenFiberContext *tfx; if (!( tfx = _tenFiberContextCommonNew(dwivol, AIR_TRUE, thresh, soft, valueMin, ten1method, ten2method) )) { biffAddf(TEN, "%s: couldn't create new context", me); return NULL; } return tfx; } tenFiberContext * tenFiberContextNew(const Nrrd *dtvol) { static const char me[]="tenFiberContextNew"; tenFiberContext *tfx; if (!( tfx = _tenFiberContextCommonNew(dtvol, AIR_FALSE, AIR_NAN, AIR_NAN, AIR_NAN, tenEstimate1MethodUnknown, tenEstimate2MethodUnknown) )) { biffAddf(TEN, "%s: couldn't create new context", me); return NULL; } return tfx; } void tenFiberVerboseSet(tenFiberContext *tfx, int verbose) { if (tfx) { tfx->verbose = verbose; } return; } int tenFiberTypeSet(tenFiberContext *tfx, int ftype) { static const char me[]="tenFiberTypeSet"; if (!tfx) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tfx->useDwi) { fprintf(stderr, "!%s(%d)--- hello\n", me, ftype); switch (ftype) { case tenDwiFiberType1Evec0: GAGE_QUERY_ITEM_ON(tfx->query, tenDwiGageTensorLLS); tfx->gageTen = gageAnswerPointer(tfx->gtx, tfx->pvl, tenDwiGageTensorLLS); tfx->gageTen2 = NULL; break; case tenDwiFiberType2Evec0: GAGE_QUERY_ITEM_ON(tfx->query, tenDwiGage2TensorPeled); tfx->gageTen = NULL; tfx->gageTen2 = gageAnswerPointer(tfx->gtx, tfx->pvl, tenDwiGage2TensorPeled); break; case tenDwiFiberType12BlendEvec0: GAGE_QUERY_ITEM_ON(tfx->query, tenDwiGageTensorLLS); tfx->gageTen = gageAnswerPointer(tfx->gtx, tfx->pvl, tenDwiGageTensorLLS); GAGE_QUERY_ITEM_ON(tfx->query, tenDwiGage2TensorPeled); tfx->gageTen2 = gageAnswerPointer(tfx->gtx, tfx->pvl, tenDwiGage2TensorPeled); break; default: biffAddf(TEN, "%s: unimplemented %s %d", me, tenDwiFiberType->name, ftype); return 1; break; } tfx->gageEval = NULL; tfx->gageEvec = NULL; } else { /* working with tensor volume */ switch(ftype) { case tenFiberTypeEvec0: GAGE_QUERY_ITEM_ON(tfx->query, tenGageEvec0); /* HEY: COPY AND PASTE */ tfx->gageEvec = gageAnswerPointer(tfx->gtx, tfx->pvl, (tenFiberTypeEvec0 == tfx->fiberType ? tenGageEvec0 : (tenFiberTypeEvec1 == tfx->fiberType ? tenGageEvec1 : tenGageEvec2))); break; case tenFiberTypeEvec1: GAGE_QUERY_ITEM_ON(tfx->query, tenGageEvec1); /* HEY: COPY AND PASTE */ tfx->gageEvec = gageAnswerPointer(tfx->gtx, tfx->pvl, (tenFiberTypeEvec0 == tfx->fiberType ? tenGageEvec0 : (tenFiberTypeEvec1 == tfx->fiberType ? tenGageEvec1 : tenGageEvec2))); break; case tenFiberTypeEvec2: GAGE_QUERY_ITEM_ON(tfx->query, tenGageEvec2); /* HEY: COPY AND PASTE */ tfx->gageEvec = gageAnswerPointer(tfx->gtx, tfx->pvl, (tenFiberTypeEvec0 == ftype ? tenGageEvec0 : (tenFiberTypeEvec1 == ftype ? tenGageEvec1 : tenGageEvec2))); break; case tenFiberTypeTensorLine: GAGE_QUERY_ITEM_ON(tfx->query, tenGageTensor); GAGE_QUERY_ITEM_ON(tfx->query, tenGageEval0); GAGE_QUERY_ITEM_ON(tfx->query, tenGageEval1); GAGE_QUERY_ITEM_ON(tfx->query, tenGageEval2); GAGE_QUERY_ITEM_ON(tfx->query, tenGageEvec0); GAGE_QUERY_ITEM_ON(tfx->query, tenGageEvec1); GAGE_QUERY_ITEM_ON(tfx->query, tenGageEvec2); tfx->gageEvec = gageAnswerPointer(tfx->gtx, tfx->pvl, tenGageEvec0); tfx->gageTen = gageAnswerPointer(tfx->gtx, tfx->pvl, tenGageTensor); tfx->gageEval = gageAnswerPointer(tfx->gtx, tfx->pvl, tenGageEval); break; case tenFiberTypePureLine: GAGE_QUERY_ITEM_ON(tfx->query, tenGageTensor); break; case tenFiberTypeZhukov: biffAddf(TEN, "%s: sorry, Zhukov oriented tensors not implemented", me); return 1; break; default: biffAddf(TEN, "%s: fiber type %d not recognized", me, ftype); return 1; break; } /* switch */ if (tenFiberTypeEvec0 == ftype || tenFiberTypeEvec1 == ftype || tenFiberTypeEvec2 == ftype || tenFiberTypeTensorLine == ftype) { tfx->gageTen = gageAnswerPointer(tfx->gtx, tfx->pvl, tenGageTensor); tfx->gageEval = gageAnswerPointer(tfx->gtx, tfx->pvl, tenGageEval0); tfx->gageEvec = gageAnswerPointer(tfx->gtx, tfx->pvl, (tenFiberTypeEvec0 == ftype ? tenGageEvec0 : (tenFiberTypeEvec1 == ftype ? tenGageEvec1 : (tenFiberTypeEvec2 == ftype ? tenGageEvec2 : tenGageEvec)))); tfx->gageTen2 = NULL; } tfx->ten2Which = 0; } tfx->fiberType = ftype; return 0; } /* ******** tenFiberStopSet ** ** how to set stop criteria and their parameters. a little tricky because ** of the use of varargs ** ** valid calls: ** tenFiberStopSet(tfx, tenFiberStopLength, double max) ** tenFiberStopSet(tfx, tenFiberStopMinLength, double min) ** tenFiberStopSet(tfx, tenFiberStopAniso, int anisoType, double anisoThresh) ** tenFiberStopSet(tfx, tenFiberStopNumSteps, unsigned int num) ** tenFiberStopSet(tfx, tenFiberStopMinNumSteps, unsigned int num) ** tenFiberStopSet(tfx, tenFiberStopConfidence, double conf) ** tenFiberStopSet(tfx, tenFiberStopRadius, double radius) ** tenFiberStopSet(tfx, tenFiberStopBounds) ** tenFiberStopSet(tfx, tenFiberStopFraction, double fraction) ** tenFiberStopSet(tfx, tenFiberStopStub) */ int tenFiberStopSet(tenFiberContext *tfx, int stop, ...) { static const char me[]="tenFiberStopSet"; va_list ap; int ret=0; int anisoGage; if (!tfx) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } va_start(ap, stop); switch(stop) { case tenFiberStopAniso: tfx->anisoStopType = va_arg(ap, int); tfx->anisoThresh = va_arg(ap, double); if (!(AIR_IN_OP(tenAnisoUnknown, tfx->anisoStopType, tenAnisoLast))) { biffAddf(TEN, "%s: given aniso stop type %d not valid", me, tfx->anisoStopType); ret = 1; goto end; } if (!(AIR_EXISTS(tfx->anisoThresh))) { biffAddf(TEN, "%s: given aniso threshold doesn't exist", me); ret = 1; goto end; } if (tfx->useDwi) { /* the tensor of which we measure anisotropy can come from lots of places, not just a 1-tensor gage item, so there's no specific item to turn on here... */ tfx->gageAnisoStop = NULL; } else { /* using tensors */ switch(tfx->anisoStopType) { case tenAniso_FA: anisoGage = tenGageFA; break; case tenAniso_Cl1: anisoGage = tenGageCl1; break; case tenAniso_Cp1: anisoGage = tenGageCp1; break; case tenAniso_Ca1: anisoGage = tenGageCa1; break; case tenAniso_Clpmin1: anisoGage = tenGageClpmin1; break; case tenAniso_Cl2: anisoGage = tenGageCl2; break; case tenAniso_Cp2: anisoGage = tenGageCp2; break; case tenAniso_Ca2: anisoGage = tenGageCa2; break; case tenAniso_Clpmin2: anisoGage = tenGageClpmin2; break; default: biffAddf(TEN, "%s: sorry, currently don't have fast %s computation " "via gage", me, airEnumStr(tenAniso, tfx->anisoStopType)); ret = 1; goto end; break; } /* NOTE: we are no longer computing ALL anisotropy measures ... GAGE_QUERY_ITEM_ON(tfx->query, tenGageAniso); */ GAGE_QUERY_ITEM_ON(tfx->query, anisoGage); tfx->gageAnisoStop = gageAnswerPointer(tfx->gtx, tfx->pvl, anisoGage); /* fprintf(stderr, "!%s: stopping on aniso %s < %g\n", me, airEnumStr(tenAniso, tfx->anisoStopType), tfx->anisoThresh); */ } break; case tenFiberStopLength: tfx->maxHalfLen = va_arg(ap, double); if (!( AIR_EXISTS(tfx->maxHalfLen) && tfx->maxHalfLen > 0.0 )) { biffAddf(TEN, "%s: given maxHalfLen %g doesn't exist or isn't > 0.0", me, tfx->maxHalfLen); ret = 1; goto end; } /* no query modifications needed */ break; case tenFiberStopMinLength: tfx->minWholeLen = va_arg(ap, double); if (!( AIR_EXISTS(tfx->minWholeLen) && tfx->minWholeLen >= 0.0 )) { biffAddf(TEN, "%s: given minWholeLen %g doesn't exist or isn't >= 0.0", me, tfx->minWholeLen); ret = 1; goto end; } /* no query modifications needed */ break; case tenFiberStopNumSteps: tfx->maxNumSteps = va_arg(ap, unsigned int); if (!( tfx->maxNumSteps > 0 )) { biffAddf(TEN, "%s: given maxNumSteps isn't > 0.0", me); ret = 1; goto end; } /* no query modifications needed */ break; case tenFiberStopMinNumSteps: tfx->minNumSteps = va_arg(ap, unsigned int); /* no query modifications needed */ break; case tenFiberStopConfidence: tfx->confThresh = va_arg(ap, double); if (!( AIR_EXISTS(tfx->confThresh) )) { biffAddf(TEN, "%s: given confThresh doesn't exist", me); ret = 1; goto end; } GAGE_QUERY_ITEM_ON(tfx->query, tenGageTensor); break; case tenFiberStopRadius: tfx->minRadius = va_arg(ap, double); if (!( AIR_EXISTS(tfx->minRadius) )) { biffAddf(TEN, "%s: given minimum radius doesn't exist", me); ret = 1; goto end; } /* no query modifications needed */ break; case tenFiberStopBounds: /* nothing to set; always used as a stop criterion */ break; case tenFiberStopFraction: if (!tfx->useDwi) { biffAddf(TEN, "%s: can only use %s-based termination in DWI tractography", me, airEnumStr(tenFiberStop, tenFiberStopFraction)); ret = 1; goto end; } tfx->minFraction = va_arg(ap, double); if (!( AIR_EXISTS(tfx->minFraction) )) { biffAddf(TEN, "%s: given minimum fraction doesn't exist", me); ret = 1; goto end; } /* no query modifications needed */ break; case tenFiberStopStub: /* no var-args to grab */ /* no query modifications needed */ break; default: biffAddf(TEN, "%s: stop criterion %d not recognized", me, stop); ret = 1; goto end; } tfx->stop = tfx->stop | (1 << stop); end: va_end(ap); return ret; } /* to avoid var-args */ int tenFiberStopAnisoSet(tenFiberContext *tfx, int anisoType, double anisoThresh) { static const char me[]="tenFiberStopAnisoSet"; if (tenFiberStopSet(tfx, tenFiberStopAniso, anisoType, anisoThresh)) { biffAddf(TEN, "%s: trouble", me); return 1; } return 0; } /* to avoid var-args */ int tenFiberStopDoubleSet(tenFiberContext *tfx, int stop, double val) { static const char me[]="tenFiberStopDoubleSet"; switch (stop) { case tenFiberStopLength: case tenFiberStopMinLength: case tenFiberStopConfidence: case tenFiberStopRadius: case tenFiberStopFraction: if (tenFiberStopSet(tfx, stop, val)) { biffAddf(TEN, "%s: trouble", me); return 1; } break; default: biffAddf(TEN, "%s: given stop criterion %d (%s) isn't a double", me, stop, airEnumStr(tenFiberStop, stop)); return 1; } return 0; } /* to avoid var-args */ int tenFiberStopUIntSet(tenFiberContext *tfx, int stop, unsigned int val) { static const char me[]="tenFiberStopUIntSet"; switch (stop) { case tenFiberStopNumSteps: case tenFiberStopMinNumSteps: if (tenFiberStopSet(tfx, stop, val)) { biffAddf(TEN, "%s: trouble", me); return 1; } break; default: biffAddf(TEN, "%s: given stop criterion %d (%s) isn't an unsigned int", me, stop, airEnumStr(tenFiberStop, stop)); return 1; } return 0; } void tenFiberStopOn(tenFiberContext *tfx, int stop) { if (tfx && !airEnumValCheck(tenFiberStop, stop)) { tfx->stop = tfx->stop | (1 << stop); } return; } void tenFiberStopOff(tenFiberContext *tfx, int stop) { if (tfx && !airEnumValCheck(tenFiberStop, stop)) { tfx->stop = tfx->stop & ~(1 << stop); } return; } void tenFiberStopReset(tenFiberContext *tfx) { if (tfx) { tfx->stop = 0; } return; } int tenFiberAnisoSpeedSet(tenFiberContext *tfx, int aniso, double lerp, double thresh, double soft) { static const char me[]="tenFiberAnisoSpeedSet"; int anisoGage; if (!tfx) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tfx->useDwi) { fprintf(stderr, "!%s: sorry, can't yet work on DWIs; bye.\n", me); exit(1); } if (airEnumValCheck(tenAniso, aniso)) { biffAddf(TEN, "%s: aniso %d not valid", me, aniso); return 1; } switch(aniso) { case tenAniso_FA: anisoGage = tenGageFA; break; case tenAniso_Cl1: anisoGage = tenGageCl1; break; case tenAniso_Cp1: anisoGage = tenGageCp1; break; case tenAniso_Ca1: anisoGage = tenGageCa1; break; case tenAniso_Cl2: anisoGage = tenGageCl2; break; case tenAniso_Cp2: anisoGage = tenGageCp2; break; case tenAniso_Ca2: anisoGage = tenGageCa2; break; default: biffAddf(TEN, "%s: sorry, currently don't have fast %s computation " "via gage", me, airEnumStr(tenAniso, tfx->anisoStopType)); return 1; break; } tfx->anisoSpeedType = aniso; if (tfx->useDwi) { /* actually, finding anisotropy in the context of 2-tensor tracking is not currently done by gage */ } else { GAGE_QUERY_ITEM_ON(tfx->query, anisoGage); tfx->gageAnisoSpeed = gageAnswerPointer(tfx->gtx, tfx->pvl, anisoGage); } tfx->anisoSpeedFunc[0] = lerp; tfx->anisoSpeedFunc[1] = thresh; tfx->anisoSpeedFunc[2] = soft; return 0; } int tenFiberAnisoSpeedReset(tenFiberContext *tfx) { static const char me[]="tenFiberAnisoSpeedReset"; if (!tfx) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } tfx->anisoSpeedType = tenAnisoUnknown; /* HEY: GAGE_QUERY_ITEM_OFF something? */ /* HEY: for both tensor and DWI */ tfx->gageAnisoSpeed = NULL; return 0; } int tenFiberKernelSet(tenFiberContext *tfx, const NrrdKernel *kern, const double parm[NRRD_KERNEL_PARMS_NUM]) { static const char me[]="tenFiberKernelSet"; if (!(tfx && kern)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } nrrdKernelSpecSet(tfx->ksp, kern, parm); if (gageKernelSet(tfx->gtx, gageKernel00, tfx->ksp->kernel, tfx->ksp->parm)) { biffMovef(TEN, GAGE, "%s: problem setting kernel", me); return 1; } return 0; } int tenFiberProbeItemSet(tenFiberContext *tfx, int item) { static const char me[]="tenFiberProbeItemSet"; if (!tfx) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } tfx->fiberProbeItem = item; return 0; } int tenFiberIntgSet(tenFiberContext *tfx, int intg) { static const char me[]="tenFiberIntTypeSet"; if (!(tfx)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( AIR_IN_OP(tenFiberIntgUnknown, intg, tenFiberIntgLast) )) { biffAddf(TEN, "%s: got invalid integration type %d", me, intg); return 1; } tfx->intg = intg; return 0; } int tenFiberParmSet(tenFiberContext *tfx, int parm, double val) { static const char me[]="tenFiberParmSet"; if (tfx) { switch(parm) { case tenFiberParmStepSize: tfx->stepSize = val; break; case tenFiberParmUseIndexSpace: tfx->useIndexSpace = !!val; break; case tenFiberParmWPunct: tfx->wPunct = val; break; case tenFiberParmVerbose: tfx->verbose = AIR_CAST(int, val); break; default: fprintf(stderr, "%s: WARNING!!! tenFiberParm %d not handled\n", me, parm); break; } } return 0; } int tenFiberUpdate(tenFiberContext *tfx) { static const char me[]="tenFiberUpdate"; if (!tfx) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tenFiberTypeUnknown == tfx->fiberType) { biffAddf(TEN, "%s: fiber type not set", me); return 1; } if (!( AIR_IN_OP(tenFiberTypeUnknown, tfx->fiberType, tenFiberTypeLast) )) { biffAddf(TEN, "%s: tfx->fiberType set to bogus value (%d)", me, tfx->fiberType); return 1; } if (tenFiberIntgUnknown == tfx->intg) { biffAddf(TEN, "%s: integration type not set", me); return 1; } if (!( AIR_IN_OP(tenFiberIntgUnknown, tfx->intg, tenFiberIntgLast) )) { biffAddf(TEN, "%s: tfx->intg set to bogus value (%d)", me, tfx->intg); return 1; } if (0 == tfx->stop) { biffAddf(TEN, "%s: no fiber stopping criteria set", me); return 1; } /* HEY there should be a better place for setting this */ if (tfx->fiberProbeItem) { GAGE_QUERY_ITEM_ON(tfx->query, tfx->fiberProbeItem); } if (gageQuerySet(tfx->gtx, tfx->pvl, tfx->query) || gageUpdate(tfx->gtx)) { biffMovef(TEN, GAGE, "%s: trouble with gage", me); return 1; } if (tfx->useDwi) { if (!(0 == tfx->ten2Which || 1 == tfx->ten2Which)) { biffAddf(TEN, "%s: ten2Which must be 0 or 1 (not %u)", me, tfx->ten2Which); return 1; } } return 0; } /* ** exact same precautions about utility of this as with gageContextCopy!!! ** So: only after tenFiberUpdate, and don't touch anything, and don't ** call anything except tenFiberTrace and tenFiberContextNix */ tenFiberContext * tenFiberContextCopy(tenFiberContext *oldTfx) { static const char me[]="tenFiberContextCopy"; tenFiberContext *tfx; if (oldTfx->useDwi) { fprintf(stderr, "!%s: sorry, can't copy DWI contexts; bye.\n", me); exit(1); } tfx = AIR_CALLOC(1, tenFiberContext); memcpy(tfx, oldTfx, sizeof(tenFiberContext)); tfx->ksp = nrrdKernelSpecCopy(oldTfx->ksp); tfx->gtx = gageContextCopy(oldTfx->gtx); tfx->pvl = tfx->gtx->pvl[0]; /* HEY! gage API sucks */ tfx->gageTen = gageAnswerPointer(tfx->gtx, tfx->pvl, tenGageTensor); tfx->gageEval = gageAnswerPointer(tfx->gtx, tfx->pvl, tenGageEval0); /* HEY: COPY AND PASTE */ tfx->gageEvec = gageAnswerPointer(tfx->gtx, tfx->pvl, (tenFiberTypeEvec0 == tfx->fiberType ? tenGageEvec0 : (tenFiberTypeEvec1 == tfx->fiberType ? tenGageEvec1 : tenGageEvec2))); tfx->gageAnisoStop = gageAnswerPointer(tfx->gtx, tfx->pvl, tfx->anisoStopType); tfx->gageAnisoSpeed = (tfx->anisoSpeedType ? gageAnswerPointer(tfx->gtx, tfx->pvl, tfx->anisoSpeedType) : NULL); return tfx; } tenFiberContext * tenFiberContextNix(tenFiberContext *tfx) { if (tfx) { tfx->ksp = nrrdKernelSpecNix(tfx->ksp); tfx->gtx = gageContextNix(tfx->gtx); free(tfx); } return NULL; } teem-1.11.0~svn6057/src/ten/tendLog.c0000664000175000017500000000466312165631065016740 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Calculates logarithm of the tensor" static const char *_tend_logInfoL = (INFO ", which is based on finding the log of the eigenvalues."); int tend_logMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL, "output image"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_logInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenLog(nout, nin)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(log, INFO); teem-1.11.0~svn6057/src/ten/model1Unit2D.c0000664000175000017500000000423412165631065017545 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define PARM_NUM 2 static const tenModelParmDesc parmDesc[] = { /* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 1 */ {"th", 0, 2*AIR_PI, AIR_TRUE, AIR_FALSE, 0} }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; double theta, vec[3]; /* not used: b0 = parm[0]; */ theta = parm[1]; ELL_3V_SET(vec, cos(theta), sin(theta), 0.0); for (ii=0; iiimgNum; ii++) { dwiSim[ii] = ELL_3V_DOT(vec, espec->grad + 3*ii); } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { sprintf(str, "(%g) th=%g", parm[0], parm[1]); return str; } _TEN_PARM_ALLOC _TEN_PARM_RAND _TEN_PARM_STEP _TEN_PARM_DIST _TEN_PARM_COPY _TEN_PARM_CONVERT_NOOP _TEN_SQE _TEN_SQE_GRAD_CENTDIFF _TEN_SQE_FIT(tenModel1Unit2D) _TEN_NLL _TEN_NLL_GRAD_STUB _TEN_NLL_FIT_STUB tenModel _tenModel1Unit2D = { TEN_MODEL_STR_1UNIT2D, _TEN_MODEL_FIELDS }; const tenModel *const tenModel1Unit2D = &_tenModel1Unit2D; teem-1.11.0~svn6057/src/ten/mod.c0000664000175000017500000002512312165631065016115 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" typedef struct { double weight[3], amount, target; /* tenSizeNormalize */ /* amount: tenSizeScale */ double scale; int fixDet; int makePositive; /* tenAnisoScale */ double min, max; /* tenEigenvalueClamp */ double expo; /* tenEigenvaluePower */ double val; /* tenEigenvalueAdd */ } funcParm; enum { funcUnknown, funcSizeNormalize, funcSizeScale, funcAnisoScale, funcEigenvalueClamp, funcEigenvaluePower, funcEigenvalueAdd, funcEigenvalueMultiply, funcLog, funcExp, funcLast }; static int theFunc(Nrrd *nout, const Nrrd *nin, int func, funcParm *parm) { static const char me[]="theFunc"; float *tin, *tout, eval[3], evec[9], weight[3], size, mean; size_t NN, II; unsigned int ri; if (!AIR_IN_OP(funcUnknown, func, funcLast)) { biffAddf(TEN, "%s: given func %d out of range [%d,%d]", me, func, funcUnknown+1, funcLast-1); return 1; } if (!(nout && nin && parm)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tenTensorCheck(nin, nrrdTypeFloat, AIR_FALSE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a tensor nrrd", me); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); return 1; } } tin = (float*)(nin->data); tout = (float*)(nout->data); NN = nrrdElementNumber(nin)/7; switch(func) { case funcSizeNormalize: ELL_3V_COPY_TT(weight, float, parm->weight); size = weight[0] + weight[1] + weight[2]; if (!size) { biffAddf(TEN, "%s: some of eigenvalue weights is zero", me); return 1; } weight[0] /= size; weight[1] /= size; weight[2] /= size; for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); size = (weight[0]*AIR_ABS(eval[0]) + weight[1]*AIR_ABS(eval[1]) + weight[2]*AIR_ABS(eval[2])); ELL_3V_SET_TT(eval, float, AIR_AFFINE(0, parm->amount, 1, eval[0], parm->target*eval[0]/size), AIR_AFFINE(0, parm->amount, 1, eval[1], parm->target*eval[1]/size), AIR_AFFINE(0, parm->amount, 1, eval[2], parm->target*eval[2]/size)); tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; case funcSizeScale: for (II=0; II<=NN-1; II++) { TEN_T_SET_TT(tout, float, tin[0], parm->amount*tin[1], parm->amount*tin[2], parm->amount*tin[3], parm->amount*tin[4], parm->amount*tin[5], parm->amount*tin[6]); tin += 7; tout += 7; } break; case funcAnisoScale: for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); if (parm->fixDet) { eval[0] = AIR_MAX(eval[0], 0.00001f); eval[1] = AIR_MAX(eval[1], 0.00001f); eval[2] = AIR_MAX(eval[2], 0.00001f); ELL_3V_SET_TT(eval, float, log(eval[0]), log(eval[1]), log(eval[2])); } mean = (eval[0] + eval[1] + eval[2])/3.0f; ELL_3V_SET_TT(eval, float, AIR_LERP(parm->scale, mean, eval[0]), AIR_LERP(parm->scale, mean, eval[1]), AIR_LERP(parm->scale, mean, eval[2])); if (parm->fixDet) { ELL_3V_SET_TT(eval, float, exp(eval[0]), exp(eval[1]), exp(eval[2])); } if (eval[2] < 0 && parm->makePositive) { eval[0] = AIR_MAX(eval[0], 0.0f); eval[1] = AIR_MAX(eval[1], 0.0f); eval[2] = AIR_MAX(eval[2], 0.0f); } tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; case funcEigenvalueClamp: for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); if (AIR_EXISTS(parm->min)) { ELL_3V_SET_TT(eval, float, AIR_MAX(eval[0], parm->min), AIR_MAX(eval[1], parm->min), AIR_MAX(eval[2], parm->min)); } if (AIR_EXISTS(parm->max)) { ELL_3V_SET_TT(eval, float, AIR_MIN(eval[0], parm->max), AIR_MIN(eval[1], parm->max), AIR_MIN(eval[2], parm->max)); } tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; case funcEigenvaluePower: for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); ELL_3V_SET_TT(eval, float, pow(eval[0], parm->expo), pow(eval[1], parm->expo), pow(eval[2], parm->expo)); tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; case funcEigenvalueAdd: for (II=0; II<=NN-1; II++) { /* HEY: this doesn't require eigensolve */ tenEigensolve_f(eval, evec, tin); ELL_3V_SET_TT(eval, float, eval[0] + parm->val, eval[1] + parm->val, eval[2] + parm->val); tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; case funcEigenvalueMultiply: for (II=0; II<=NN-1; II++) { /* HEY: this doesn't require eigensolve */ tenEigensolve_f(eval, evec, tin); ELL_3V_SET_TT(eval, float, eval[0]*parm->val, eval[1]*parm->val, eval[2]*parm->val); tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; case funcLog: for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); for (ri=0; ri<3; ri++) { eval[ri] = AIR_CAST(float, log(eval[ri])); eval[ri] = AIR_EXISTS(eval[ri]) ? eval[ri] : -1000000; } tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; case funcExp: for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); for (ri=0; ri<3; ri++) { eval[ri] = AIR_CAST(float, exp(eval[ri])); eval[ri] = AIR_EXISTS(eval[ri]) ? eval[ri] : 0; } tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; } /* basic and per-axis info handled by nrrdCopy above */ return 0; } int tenSizeNormalize(Nrrd *nout, const Nrrd *nin, double _weight[3], double amount, double target) { static const char me[]="tenSizeNormalize"; funcParm parm; ELL_3V_COPY(parm.weight, _weight); parm.amount = amount; parm.target = target; if (theFunc(nout, nin, funcSizeNormalize, &parm)) { biffAddf(TEN, "%s: trouble", me); return 1; } return 0; } int tenSizeScale(Nrrd *nout, const Nrrd *nin, double amount) { static const char me[]="tenSizeScale"; funcParm parm; parm.amount = amount; if (theFunc(nout, nin, funcSizeScale, &parm)) { biffAddf(TEN, "%s: trouble", me); return 1; } return 0; } /* ******** tenAnisoScale ** ** scales the "deviatoric" part of a tensor up or down */ int tenAnisoScale(Nrrd *nout, const Nrrd *nin, double scale, int fixDet, int makePositive) { static const char me[]="tenAnisoScale"; funcParm parm; parm.scale = scale; parm.fixDet = fixDet; parm.makePositive = makePositive; if (theFunc(nout, nin, funcAnisoScale, &parm)) { biffAddf(TEN, "%s: trouble", me); return 1; } return 0; } /* ******** tenEigenvalueClamp ** ** enstates the given value as the lowest eigenvalue */ int tenEigenvalueClamp(Nrrd *nout, const Nrrd *nin, double min, double max) { static const char me[]="tenEigenvalueClamp"; funcParm parm; parm.min = min; parm.max = max; if (theFunc(nout, nin, funcEigenvalueClamp, &parm)) { biffAddf(TEN, "%s: trouble", me); return 1; } return 0; } /* ******** tenEigenvaluePower ** ** raises the eigenvalues to some power */ int tenEigenvaluePower(Nrrd *nout, const Nrrd *nin, double expo) { static const char me[]="tenEigenvaluePower"; funcParm parm; parm.expo = expo; if (theFunc(nout, nin, funcEigenvaluePower, &parm)) { biffAddf(TEN, "%s: trouble", me); return 1; } return 0; } /* ******** tenEigenvalueAdd ** ** adds something to all eigenvalues */ int tenEigenvalueAdd(Nrrd *nout, const Nrrd *nin, double val) { static const char me[]="tenEigenvalueAdd"; funcParm parm; parm.val = val; if (theFunc(nout, nin, funcEigenvalueAdd, &parm)) { biffAddf(TEN, "%s: trouble", me); return 1; } return 0; } /* ******** tenEigenvalueMultiply ** ** multiplies eigenvalues by something */ int tenEigenvalueMultiply(Nrrd *nout, const Nrrd *nin, double val) { static const char me[]="tenEigenvalueMultiply"; funcParm parm; parm.val = val; if (theFunc(nout, nin, funcEigenvalueMultiply, &parm)) { biffAddf(TEN, "%s: trouble", me); return 1; } return 0; } /* ******** tenLog ** ** takes the logarithm (by taking the log of the eigenvalues) */ int tenLog(Nrrd *nout, const Nrrd *nin) { static const char me[]="tenLog"; funcParm parm; if (theFunc(nout, nin, funcLog, &parm)) { biffAddf(TEN, "%s: trouble", me); return 1; } return 0; } /* ******** tenExp ** ** takes the exp() (by taking exp() of the eigenvalues) */ int tenExp(Nrrd *nout, const Nrrd *nin) { static const char me[]="tenExp"; funcParm parm; if (theFunc(nout, nin, funcExp, &parm)) { biffAddf(TEN, "%s: trouble", me); return 1; } return 0; } teem-1.11.0~svn6057/src/ten/tendFlotsam.c0000664000175000017500000001135212165631065017615 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" /* ******** tendCmdList[] ** ** NULL-terminated array of unrrduCmd pointers, as ordered by ** TEN_MAP macro */ unrrduCmd * tendCmdList[] = { TEND_MAP(TEND_LIST) NULL }; const char *tendTitle = "tend: Diffusion Image Processing and Analysis"; /* ******** tendFiberStopParse ** ** for parsing the different ways in which a fiber should be stopped ** For the sake of laziness and uniformity, the stop information is ** stored in an array of 3 (three) doubles: ** info[0]: int value from tenFiberStop* enum ** info[1]: 1st parameter associated with stop method (always used) ** info[2]: 2nd parameter, used occasionally */ int tendFiberStopParse(void *ptr, char *_str, char err[AIR_STRLEN_HUGE]) { char me[]="tenFiberStopParse", *str, *opt, *opt2; double *info; airArray *mop; int integer; if (!(ptr && _str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } info = (double *)ptr; mop = airMopNew(); str = airStrdup(_str); airMopMem(mop, &str, airMopAlways); opt = strchr(str, ':'); if (!opt) { /* couldn't parse string as nrrdEncoding, but there wasn't a colon */ sprintf(err, "%s: didn't see a colon in \"%s\"", me, str); airMopError(mop); return 1; } *opt = '\0'; opt++; info[0] = AIR_CAST(int, airEnumVal(tenFiberStop, str)); if (tenFiberStopUnknown == AIR_CAST(int, info[0])) { sprintf(err, "%s: didn't recognize \"%s\" as %s", me, str, tenFiberStop->name); airMopError(mop); return 1; } switch(AIR_CAST(int, info[0])) { case tenFiberStopAniso: /* , : tenAniso,double */ opt2 = strchr(opt, ','); if (!opt2) { sprintf(err, "%s: didn't see comma between aniso and level in \"%s\"", me, opt); airMopError(mop); return 1; } *opt2 = '\0'; opt2++; info[1] = AIR_CAST(int, airEnumVal(tenAniso, opt)); if (tenAnisoUnknown == AIR_CAST(int, info[1])) { sprintf(err, "%s: didn't recognize \"%s\" as %s", me, opt, tenAniso->name); airMopError(mop); return 1; } if (1 != sscanf(opt2, "%lg", info+2)) { sprintf(err, "%s: couldn't parse aniso level \"%s\" as double", me, opt2); airMopError(mop); return 1; } /* fprintf(stderr, "!%s: parsed aniso:%s,%g\n", me, airEnumStr(tenAniso, AIR_CAST(int, info[1])), info[2]); */ break; case tenFiberStopFraction: case tenFiberStopLength: case tenFiberStopRadius: case tenFiberStopConfidence: case tenFiberStopMinLength: /* all of these take a single double */ if (1 != sscanf(opt, "%lg", info+1)) { sprintf(err, "%s: couldn't parse %s \"%s\" as double", me, airEnumStr(tenFiberStop, AIR_CAST(int, info[0])), opt); airMopError(mop); return 1; } /* fprintf(stderr, "!%s: parse %s:%g\n", me, airEnumStr(tenFiberStop, AIR_CAST(int, info[0])), info[1]); */ break; case tenFiberStopNumSteps: case tenFiberStopMinNumSteps: /* <#steps> : int */ if (1 != sscanf(opt, "%d", &integer)) { sprintf(err, "%s: couldn't parse \"%s\" as int", me, opt); airMopError(mop); return 1; } info[1] = integer; /* fprintf(stderr, "!%s: parse steps:%d\n", me, integer); */ break; case tenFiberStopBounds: /* moron */ break; default: sprintf(err, "%s: stop method %d not supported", me, AIR_CAST(int, info[0])); airMopError(mop); return 1; break; } airMopOkay(mop); return 0; } hestCB _tendFiberStopCB = { 3*sizeof(double), "fiber stop", tendFiberStopParse, NULL }; hestCB * tendFiberStopCB = &_tendFiberStopCB; teem-1.11.0~svn6057/src/ten/GNUmakefile0000664000175000017500000000567212165631065017253 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := ten #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### tendCalc.o nixed $(L).NEED = echo limn gage dye unrrdu ell nrrd biff air $(L).PUBLIC_HEADERS = ten.h tenMacros.h $(L).PRIVATE_HEADERS = privateTen.h $(L).OBJS = tensor.o chan.o aniso.o glyph.o enumsTen.o grads.o miscTen.o \ mod.o estimate.o tenGage.o tenDwiGage.o qseg.o path.o qglox.o \ fiberMethods.o fiber.o epireg.o defaultsTen.o bimod.o bvec.o \ triple.o experSpec.o tenModel.o modelBall.o model1Stick.o \ model1Vector2D.o model1Unit2D.o model2Unit2D.o \ modelBall1Stick.o modelBall1StickEMD.o modelBall1Cylinder.o \ model1Cylinder.o model1Tensor2.o modelZero.o modelB0.o \ tendAbout.o \ tendFlotsam.o tendGrads.o tendAnplot.o tendAnvol.o tendEval.o \ tendEvec.o tendSten.o tendExpand.o tendEvq.o tendPoint.o \ tendTriple.o tendTconv.o tendAvg.o \ tendAnhist.o tendMake.o tendSatin.o tendShrink.o tendGlyph.o \ tendFiber.o tendEpireg.o tendBmat.o tendEstim.o tendSim.o \ tendMsim.o tendMfit.o tendMconv.o \ tendSlice.o tendEllipse.o tendEvecrgb.o tendNorm.o tendAnscale.o \ tendEvalpow.o tendEvalclamp.o tendEvaladd.o tendEvalmult.o \ tendHelix.o tendBfit.o \ tendUnmf.o tendLog.o tendExp.o $(L).TESTS = test/roistat test/tg test/tt test/tem test/rotedge \ test/tsoid test/odf-hist test/to test/tensorDotDat \ test/igrt test/cntr test/geode test/taniso test/csim \ test/ttriple test/tqgl test/teigen #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/ten/tendMake.c0000664000175000017500000000524412165631065017070 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Create DT volume from confidence and eigensystem" static const char *_tend_makeInfoL = (INFO ". The input is in the form of three nrrds, one for confidence " "values (3D), one for eigenvalues (4D, three evals per voxel), and " "one for eigenvectors (4D, nine evec components per voxel)."); int tend_makeMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin[3], *nout; char *outS; hestOptAdd(&hopt, "i", "conf evals evecs", airTypeOther, 3, 3, nin, NULL, "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_makeInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenMake(nout, nin[0], nin[1], nin[2])) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making tensor volume:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(make, INFO); teem-1.11.0~svn6057/src/ten/tendAbout.c0000664000175000017500000001012212174656123017256 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Information about this program and its use" int tend_aboutMain(int argc, const char **argv, const char *me, hestParm *hparm) { char buff[AIR_STRLEN_MED], fmt[AIR_STRLEN_MED]; char par1[] = "\t\t\t\t" "\"tend\" is a command-line interface to much of the functionality " "in \"ten\", a C library for diffusion image processing. Ten is one " "library in the \"Teem\" collection of libraries. More information " "about Teem is at . A checkout of Teem source " "is available via:\n " "svn co http://svn.code.sf.net/p/teem/code/teem/trunk teem\n "; /* "svn co http://teem.svn.sf.net/svnroot/teem/teem/trunk teem\n "; */ char par2[] = "\t\t\t\t" "Long-term maintenance of this software depends on funding, and " "funding depends on being able to document who is using it for what. " "If tend or Ten has helped in your research, including for simple one-off " "experiments or mundane data hacking, the developers of Teem would love " "to know. There are multiple ways of communicating this. " "In your publications, consider adding a line such as this " "in the Acknowledgments: " "\"Data processing performed with the tend tool, " "part of the Teem toolkit available at " "http://teem.sf.net\". " "Alternatively, please email glk@uchicago.edu and briefly describe " "how Teem software has helped in your work. " "Please also consider joining the teem-users mailing list: " ". This is " "the primary forum for feedback, questions, and feature requests.\n "; char par3[] = "\t\t\t\t" "Like \"unu\", another Teem command-line binary, it is often useful " "to chain together invocations of tend with pipes, as in the " "following, which estimates tensors from DWIs, takes a slice of the " "tensor volume, computes the standard RGB colormap of the principal " "eigenvector, and then quantizes it to an 8-bit PNG:\n"; char par4[] = "\ttend estim -i dwi.nhdr -B kvp -knownB0 true \\\n " " | tend slice -a 2 -p 30 \\\n " " | tend evecrgb -c 0 -a cl2 -gam 1.2 \\\n " " | unu quantize -b 8 -min 0 -max 1 -o z30-rgb.png\n"; AIR_UNUSED(argc); AIR_UNUSED(argv); AIR_UNUSED(me); fprintf(stdout, "\n"); sprintf(buff, "--- %s ---", tendTitle); sprintf(fmt, "%%%ds\n", (int)((hparm->columns-strlen(buff))/2 + strlen(buff) - 1)); fprintf(stdout, fmt, buff); sprintf(buff, "(Teem version %s, %s)", airTeemVersion, airTeemReleaseDate); sprintf(fmt, "%%%ds\n", (int)((hparm->columns-strlen(buff))/2 + strlen(buff) - 1)); fprintf(stdout, fmt, buff); fprintf(stdout, "\n"); _hestPrintStr(stdout, 1, 0, 78, par1, AIR_FALSE); _hestPrintStr(stdout, 1, 0, 78, par2, AIR_FALSE); _hestPrintStr(stdout, 1, 0, 78, par3, AIR_FALSE); _hestPrintStr(stdout, 2, 0, 78, par4, AIR_FALSE); return 0; } TEND_CMD(about, INFO); teem-1.11.0~svn6057/src/ten/model2Unit2D.c0000664000175000017500000000502212165631065017542 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define PARM_NUM 4 static const tenModelParmDesc parmDesc[] = { /* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 1 */ {"th0", 0, 2*AIR_PI, AIR_TRUE, AIR_FALSE, 0}, /* 2 */ {"frac", 0, 1, AIR_FALSE, AIR_FALSE, 0}, /* 3 */ {"th1", 0, 2*AIR_PI, AIR_TRUE, AIR_FALSE, 0} }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; double th0, frac, th1, vec0[3], vec1[3]; /* not used: b0 = parm[0]; */ th0 = parm[1]; frac = parm[2]; th1 = parm[3]; ELL_3V_SET(vec0, cos(th0), sin(th0), 0.0); ELL_3V_SET(vec1, cos(th1), sin(th1), 0.0); for (ii=0; iiimgNum; ii++) { double dot0, dot1; dot0 = ELL_3V_DOT(vec0, espec->grad + 3*ii); dot1 = ELL_3V_DOT(vec1, espec->grad + 3*ii); dwiSim[ii] = AIR_LERP(frac, dot0, dot1); } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { sprintf(str, "(%g) (1-f)*th0=%g + (f=%g)*th1=%g", parm[0], parm[1], parm[2], parm[3]); return str; } _TEN_PARM_ALLOC _TEN_PARM_RAND _TEN_PARM_STEP _TEN_PARM_DIST _TEN_PARM_COPY _TEN_PARM_CONVERT_NOOP _TEN_SQE _TEN_SQE_GRAD_CENTDIFF _TEN_SQE_FIT(tenModel2Unit2D) _TEN_NLL _TEN_NLL_GRAD_STUB _TEN_NLL_FIT_STUB tenModel _tenModel2Unit2D = { TEN_MODEL_STR_2UNIT2D, _TEN_MODEL_FIELDS }; const tenModel *const tenModel2Unit2D = &_tenModel2Unit2D; teem-1.11.0~svn6057/src/ten/tendMconv.c0000664000175000017500000000704612165631065017277 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "convert from one model to another" static const char *_tend_mconvInfoL = (INFO ". More docs here."); int tend_mconvMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS, *modelSrcS, *modelDstS; const tenModel *modelDst, *modelSrc; int saveB0; hestOptAdd(&hopt, "mo", "model", airTypeString, 1, 1, &modelDstS, NULL, "which model to convert to"); hestOptAdd(&hopt, "mi", "model", airTypeString, 1, 1, &modelSrcS, "", "model converting from; if not set, will try to determine " "from input nrrd"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input nrrd of model parms", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output nrrd of model parms"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_mconvInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenModelParse(&modelDst, &saveB0, AIR_FALSE, modelDstS)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing model \"%s\":\n%s\n", me, modelDstS, err); airMopError(mop); return 1; } if (saveB0) { printf("%s: warning: saving B0 is determined by input nrrd " "having B0 info.\n", me); } if (airStrlen(modelSrcS)) { if (tenModelParse(&modelSrc, &saveB0, AIR_FALSE, modelSrcS)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing model \"%s\":\n%s\n", me, modelSrcS, err); airMopError(mop); return 1; } } else { modelSrc = NULL; } if (tenModelConvert(nout, NULL, modelDst, nin, modelSrc)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble converting:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(mconv, INFO); teem-1.11.0~svn6057/src/ten/sources.cmake0000664000175000017500000000233511765245200017654 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(TEN_SOURCES aniso.c bimod.c bvec.c chan.c defaultsTen.c enumsTen.c epireg.c estimate.c fiber.c fiberMethods.c glyph.c grads.c miscTen.c mod.c path.c privateTen.h qglox.c qseg.c ten.h tenDwiGage.c tenGage.c tenMacros.h tendAbout.c tendAnhist.c tendAnplot.c tendAnscale.c tendAnvol.c tendAvg.c tendBfit.c tendBmat.c tendEllipse.c tendEpireg.c tendEstim.c tendEval.c tendEvaladd.c tendEvalmult.c tendEvalclamp.c tendEvalpow.c tendEvec.c tendEvecrgb.c tendEvq.c tendExp.c tendExpand.c tendFiber.c tendFlotsam.c tendGlyph.c tendGrads.c tendHelix.c tendLog.c tendMake.c tendNorm.c tendPoint.c tendSatin.c tendShrink.c tendSim.c tendMsim.c tendMfit.c tendMconv.c tendSlice.c tendSten.c tendTconv.c tendTriple.c tendUnmf.c tensor.c triple.c experSpec.c tenModel.c modelZero.c modelB0.c modelBall.c model1Stick.c model1Vector2D.c model1Unit2D.c model2Unit2D.c modelBall1StickEMD.c modelBall1Stick.c modelBall1Cylinder.c model1Cylinder.c model1Tensor2.c ) ADD_TEEM_LIBRARY(ten ${TEN_SOURCES}) teem-1.11.0~svn6057/src/ten/tendExp.c0000664000175000017500000000464512165631065016753 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Calculates exp() of the tensor" static const char *_tend_expInfoL = (INFO ", which is based on exp() of the eigenvalues."); int tend_expMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL, "output image"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_expInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenExp(nout, nin)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(exp, INFO); teem-1.11.0~svn6057/src/ten/model1Cylinder.c0000664000175000017500000000530512165631065020211 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define PARM_NUM 6 static const tenModelParmDesc parmDesc[] = { /* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 1 */ {"length", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 2 */ {"radius", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 3 */ {"x", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 0}, /* 4 */ {"y", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 1}, /* 5 */ {"z", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 2} }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; double b0, length, radius, vec[3], ten[7], ident[7] = {1, 1, 0, 0, 1, 0, 1}; b0 = parm[0]; length = parm[1]; radius = parm[2]; vec[0] = parm[3]; vec[1] = parm[4]; vec[2] = parm[5]; TEN_T3V_OUTER(ten, vec); TEN_T_SCALE_ADD2(ten, length - radius, ten, radius, ident); for (ii=0; iiimgNum; ii++) { double adc, bb; bb = espec->bval[ii]; adc = TEN_T3V_CONTR(ten, espec->grad + 3*ii); dwiSim[ii] = b0*exp(-bb*adc); } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { sprintf(str, "(%g) %gX%g (%g,%g,%g)", parm[0], parm[1], parm[2], parm[3], parm[4], parm[5]); return str; } _TEN_PARM_ALLOC _TEN_PARM_RAND _TEN_PARM_STEP _TEN_PARM_DIST _TEN_PARM_COPY _TEN_PARM_CONVERT_NOOP _TEN_SQE _TEN_SQE_GRAD_CENTDIFF _TEN_SQE_FIT(tenModel1Cylinder) _TEN_NLL _TEN_NLL_GRAD_STUB _TEN_NLL_FIT_STUB tenModel _tenModel1Cylinder = { TEN_MODEL_STR_1CYLINDER, _TEN_MODEL_FIELDS }; const tenModel *const tenModel1Cylinder = &_tenModel1Cylinder; teem-1.11.0~svn6057/src/ten/bvec.c0000664000175000017500000001304312165631065016253 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" double tenBVecNonLinearFit_error(double *bb, double *ss, double *ww, int len, double amp, double dec) { int ii; double err, tmp; err = 0; for (ii=0; iidim >= 2 )) { biffAddf(TEN, "%s: nin->dim (%d) not >= 2", me, nin->dim); return 1; } if (!( nin->axis[0].size < AIR_STRLEN_SMALL )) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: sorry need nin->axis[0].size (%s) < %d", me, airSprintSize_t(stmp, nin->axis[0].size), AIR_STRLEN_SMALL); return 1; } /* allocate/set-up output */ nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); size[0] = 3; if (nrrdMaybeAlloc_nva(nout, nrrdTypeDouble, nin->dim, size)) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); return 1; } for (ii=1; iidim; ii++) { map[ii] = ii; } map[0] = -1; if (nrrdAxisInfoCopy(nout, nin, map, NRRD_AXIS_INFO_NONE)) { biffMovef(TEN, NRRD, "%s: couldn't copy axis info", me); return 1; } /* process all b vectors */ vecSize = nin->axis[0].size*nrrdTypeSize[nin->type]; vecNum = nrrdElementNumber(nin)/nin->axis[0].size; vecLup = nrrdDLookup[nin->type]; vec = (char*)nin->data; out = (double*)nout->data; for (vecI=0; vecIaxis[0].size; ii++) { ss[ii] = vecLup(vec, ii); } /* start with linear fit */ tenBVecNonLinearFit_linear(&, &dec, bb, ss, ww, nin->axis[0].size); error = tenBVecNonLinearFit_error(bb, ss, ww, nin->axis[0].size, amp, dec); /* possibly refine with gauss-newton */ if (iterMax > 0) { iter = 0; do { iter++; tenBVecNonLinearFit_GNstep(&d_amp, &d_dec, bb, ss, ww, nin->axis[0].size, amp, dec); amp += 0.3*d_amp; dec += 0.3*d_dec; diff = d_amp*d_amp + d_dec*d_dec; } while (iter < iterMax && diff > eps); } error = tenBVecNonLinearFit_error(bb, ss, ww, nin->axis[0].size, amp, dec); out[0] = amp; out[1] = dec; out[2] = error; vec += vecSize; out += 3; } return 0; } teem-1.11.0~svn6057/src/ten/tendSlice.c0000664000175000017500000000542412165631065017252 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Slice 3D tensors to get slab/image of 3D/2D tensors" static const char *_tend_sliceInfoL = (INFO ". "); int tend_sliceMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; char *outS; int axis, pos, dim; Nrrd *nin, *nout; hestOptAdd(&hopt, "a", "axis", airTypeInt, 1, 1, &axis, NULL, "axis along which to slice"); hestOptAdd(&hopt, "p", "pos", airTypeInt, 1, 1, &pos, NULL, "position to slice at"); hestOptAdd(&hopt, "d", "dim", airTypeInt, 1, 1, &dim, "3", "dimension of desired tensor output, can be either 2 or 3"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output tensor slice"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_sliceInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); airMopAdd(mop, nout=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (tenSlice(nout, nin, axis, pos, dim)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(slice, INFO); teem-1.11.0~svn6057/src/ten/tendEllipse.c0000664000175000017500000002543212165631065017611 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Generate postscript renderings of 2D glyphs" static const char *_tend_ellipseInfoL = (INFO ". Not much to look at here."); int tend_ellipseDoit(FILE *file, Nrrd *nten, Nrrd *npos, Nrrd *nstn, float min[2], float max[2], float gscale, float dotRad, float lineWidth, float cthresh, int invert) { size_t sx=0, sy=0, ti, nt; int x, y, vi, *sdata; double aspect, minX, minY, maxX, maxY, conf, Dxx, Dxy, Dyy, px, py, spx, spy; float *tdata, *pdata; if (npos) { nt = npos->axis[1].size; aspect = (max[0] - min[0])/(max[1] - min[1]); } else { spx = (AIR_EXISTS(nten->axis[1].spacing) ? nten->axis[1].spacing : 1); spy = (AIR_EXISTS(nten->axis[2].spacing) ? nten->axis[2].spacing : 1); sx = nten->axis[1].size; sy = nten->axis[2].size; nt = sx*sy; aspect = sx*spx/(sy*spy); } if (aspect > 7.5/10) { /* image has a wider aspect ratio than safely printable page area */ minX = 0.5; maxX = 8.0; minY = 5.50 - 7.5/2/aspect; maxY = 5.50 + 7.5/2/aspect; } else { /* image is taller ... */ minX = 4.25 - 10.0/2*aspect; maxX = 4.25 + 10.0/2*aspect; minY = 0.5; maxY = 10.5; } minX *= 72; minY *= 72; maxX *= 72; maxY *= 72; if (npos) { gscale *= AIR_CAST(float, (maxX - minX)/(max[0] - min[0])); dotRad *= AIR_CAST(float, (maxX - minX)/(max[0] - min[0])); lineWidth *= AIR_CAST(float, (maxX - minX)/(max[0] - min[0])); } fprintf(file, "%%!PS-Adobe-3.0 EPSF-3.0\n"); fprintf(file, "%%%%Creator: tend ellipse\n"); fprintf(file, "%%%%Title: blah blah blah\n"); fprintf(file, "%%%%Pages: 1\n"); fprintf(file, "%%%%BoundingBox: %d %d %d %d\n", AIR_CAST(int, floor(minX)), AIR_CAST(int, floor(minY)), AIR_CAST(int, ceil(maxX)), AIR_CAST(int, ceil(maxY))); fprintf(file, "%%%%HiResBoundingBox: %g %g %g %g\n", minX, minY, maxX, maxY); fprintf(file, "%%%%EndComments\n"); fprintf(file, "%%%%BeginProlog\n"); fprintf(file, "%%%%EndProlog\n"); fprintf(file, "%%%%Page: 1 1\n"); fprintf(file, "gsave\n"); if (invert) { fprintf(file, "0 setgray\n"); fprintf(file, "%g %g moveto\n", minX, minY); fprintf(file, "%g %g lineto\n", maxX, minY); fprintf(file, "%g %g lineto\n", maxX, maxY); fprintf(file, "%g %g lineto\n", minX, maxY); fprintf(file, "closepath fill\n"); } fprintf(file, "gsave\n"); fprintf(file, "0.5 setgray\n"); tdata = (float*)nten->data; pdata = npos ? (float*)npos->data : NULL; for (ti=0; ti cthresh) { double eval0, eval1, dd; Dxx = tdata[1]; Dxy = tdata[2]; Dyy = tdata[3]; dd = Dxx - Dyy; eval0 = 0.5*(-Dxx + sqrt(4*Dxy*Dxy + dd*dd) - Dyy); eval1 = 0.5*(-Dxx - sqrt(4*Dxy*Dxy + dd*dd) - Dyy); fprintf(file, "gsave\n"); fprintf(file, "matrix currentmatrix\n"); fprintf(file, "[%g %g %g %g %g %g] concat\n", Dxx, -Dxy, -Dxy, Dyy, px, py); fprintf(file, "0 0 %g 0 360 arc closepath\n", gscale); fprintf(file, "setmatrix\n"); if (eval0 * eval1 < 0) { fprintf(file, "gsave\n"); fprintf(file, "0.15 setgray\n"); fprintf(file, "fill\n"); fprintf(file, "grestore\n"); } else { fprintf(file, "fill\n"); } fprintf(file, "grestore\n"); } tdata += 4; } fprintf(file, "grestore\n"); if (dotRad && !nstn) { fprintf(file, "gsave\n"); tdata = (float*)nten->data; pdata = npos ? (float*)npos->data : NULL; fprintf(file, "%g setgray\n", invert ? 1.0 : 0.0); for (ti=0; ti cthresh) { fprintf(file, "%g %g %g 0 360 arc closepath fill\n", px, py, dotRad); } tdata += 4; } fprintf(file, "grestore\n"); } if ((dotRad || lineWidth) && npos && nstn) { fprintf(file, "gsave\n"); tdata = (float*)nten->data; pdata = npos ? (float*)npos->data : NULL; sdata = nstn ? (int*)nstn->data : NULL; fprintf(file, "%g setlinewidth\n", lineWidth); fprintf(file, "%g setgray\n", invert ? 1.0 : 0.0); fprintf(file, "1 setlinecap\n"); fprintf(file, "1 setlinejoin\n"); for (ti=0; tiaxis[1].size; ti++) { if (1 == sdata[1 + 3*ti]) { vi = sdata[0 + 3*ti]; px = AIR_AFFINE(min[0], pdata[0 + 2*vi], max[0], minX, maxX); py = AIR_AFFINE(min[1], pdata[1 + 2*vi], max[1], maxY, minY); if (tdata[0 + 4*vi] > cthresh) { fprintf(file, "%g %g %g 0 360 arc closepath fill\n", px, py, dotRad); } } else { fprintf(file, "newpath\n"); for (vi = sdata[0 + 3*ti]; vi < sdata[0 + 3*ti] + sdata[1 + 3*ti]; vi++) { px = AIR_AFFINE(min[0], pdata[0 + 2*vi], max[0], minX, maxX); py = AIR_AFFINE(min[1], pdata[1 + 2*vi], max[1], maxY, minY); fprintf(file, "%g %g %s\n", px, py, vi == sdata[0 + 3*ti] ? "moveto" : "lineto"); } fprintf(file, "stroke\n"); vi = sdata[0 + 3*ti] + sdata[2 + 3*ti]; px = AIR_AFFINE(min[0], pdata[0 + 2*vi], max[0], minX, maxX); py = AIR_AFFINE(min[1], pdata[1 + 2*vi], max[1], maxY, minY); fprintf(file, "%g %g %g 0 360 arc closepath fill\n", px, py, dotRad + lineWidth); } } fprintf(file, "grestore\n"); } fprintf(file, "grestore\n"); return 0; } int tend_ellipseMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr; airArray *mop; Nrrd *nten, *npos, *nstn; char *outS; float gscale, dotRad, lineWidth, cthresh, min[2], max[2]; FILE *fout; int invert; mop = airMopNew(); hestOptAdd(&hopt, "ctr", "conf thresh", airTypeFloat, 1, 1, &cthresh, "0.5", "Glyphs will be drawn only for tensors with confidence " "values greater than this threshold"); hestOptAdd(&hopt, "gsc", "scale", airTypeFloat, 1, 1, &gscale, "1", "over-all glyph size"); hestOptAdd(&hopt, "dot", "radius", airTypeFloat, 1, 1, &dotRad, "0.0", "radius of little dot to put in middle of ellipse, or \"0\" " "for no such dot"); hestOptAdd(&hopt, "wid", "width", airTypeFloat, 1, 1, &lineWidth, "0.0", "with of lines for tractlets"); hestOptAdd(&hopt, "inv", NULL, airTypeInt, 0, 0, &invert, NULL, "use white ellipses on black background, instead of reverse"); hestOptAdd(&hopt, "min", "minX minY", airTypeFloat, 2, 2, min, "-1 -1", "when using \"-p\", minimum corner"); hestOptAdd(&hopt, "max", "maxX maxY", airTypeFloat, 2, 2, max, "1 1", "when using \"-p\", maximum corner"); /* input/output */ hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nten, "-", "image of 2D tensors", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "p", "pos array", airTypeOther, 1, 1, &npos, "", "Instead of being on a grid, tensors are at arbitrary locations, " "as defined by this 2-by-N array of floats", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "s", "stn array", airTypeOther, 1, 1, &nstn, "", "Locations given by \"-p\" have this connectivity", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output PostScript file"); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_ellipseInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (npos) { if (!( 2 == nten->dim && 4 == nten->axis[0].size && 2 == npos->dim && 2 == npos->axis[0].size && nten->axis[1].size == npos->axis[1].size )) { fprintf(stderr, "%s: didn't get matching lists of tensors and pos's\n", me); airMopError(mop); return 1; } if (!( nrrdTypeFloat == npos->type )) { fprintf(stderr, "%s: didn't get float type positions\n", me); airMopError(mop); return 1; } } else { if (!(3 == nten->dim && 4 == nten->axis[0].size)) { fprintf(stderr, "%s: didn't get a 3-D 4-by-X-by-Y 2D tensor array\n", me); airMopError(mop); return 1; } } if (!( nrrdTypeFloat == nten->type )) { fprintf(stderr, "%s: didn't get float type tensors\n", me); airMopError(mop); return 1; } if (nstn) { if (!( nrrdTypeUInt == nstn->type && 2 == nstn->dim && 3 == nstn->axis[0].size )) { fprintf(stderr, "%s: connectivity isn't 2-D 3-by-N array of %ss\n", me, airEnumStr(nrrdType, nrrdTypeInt)); airMopError(mop); return 1; } } if (!(fout = airFopen(outS, stdout, "wb"))) { fprintf(stderr, "%s: couldn't open \"%s\" for writing\n", me, outS); airMopError(mop); return 1; } airMopAdd(mop, fout, (airMopper)airFclose, airMopAlways); tend_ellipseDoit(fout, nten, npos, nstn, min, max, gscale, dotRad, lineWidth, cthresh, invert); airMopOkay(mop); return 0; } TEND_CMD(ellipse, INFO); teem-1.11.0~svn6057/src/ten/ten.h0000664000175000017500000025423512165631065016141 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TEN_HAS_BEEN_INCLUDED #define TEN_HAS_BEEN_INCLUDED #include #include #include #include #include #include #include #include #include #include #include "tenMacros.h" #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(ten_EXPORTS) || defined(teem_EXPORTS) # define TEN_EXPORT extern __declspec(dllexport) # else # define TEN_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define TEN_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define TEN tenBiffKey /* ****** tenAniso* enum ** ** the different scalar values that can describe a tensor. Nearly ** all of these are anisotropy metrics, but with time this has become ** a convenient way to present any scalar invariant (such as trace), ** and even the individual eigenvalues ** ** keep in sync: ** aniso.c: _tenAnisoEval_X_f(), _tenAnisoEval_f[] ** aniso.c: _tenAnisoTen_X_d(), _tenAnisoTen_d[] ** aniso.c: tenAnisoCalc_f() ** enumsTen.c: tenAniso */ enum { tenAnisoUnknown, /* 0: nobody knows */ tenAniso_Conf, /* 1: not an anisotropy, but enables some useful hacks */ tenAniso_Cl1, /* 2: Westin's linear (first version) */ tenAniso_Cp1, /* 3: Westin's planar (first version) */ tenAniso_Ca1, /* 4: Westin's linear + planar (first version) */ tenAniso_Clpmin1, /* 5: minimum of Cl and Cp (first version) */ tenAniso_Cs1, /* 6: Westin's spherical (first version) */ tenAniso_Ct1, /* 7: gk's anisotropy type (first version) */ tenAniso_Cl2, /* 8: Westin's linear (second version) */ tenAniso_Cp2, /* 9: Westin's planar (second version) */ tenAniso_Ca2, /* 10: Westin's linear + planar (second version) */ tenAniso_Clpmin2, /* 11: minimum of Cl and Cp (second version) */ tenAniso_Cs2, /* 12: Westin's spherical (second version) */ tenAniso_Ct2, /* 13: gk's anisotropy type (second version) */ tenAniso_RA, /* 14: Bass+Pier's relative anisotropy */ tenAniso_FA, /* 15: (Bass+Pier's fractional anisotropy)/sqrt(2) */ tenAniso_VF, /* 16: volume fraction = 1-(Bass+Pier's volume ratio) */ tenAniso_B, /* 17: linear term in cubic characteristic polynomial */ tenAniso_Q, /* 18: radius of root circle is 2*sqrt(Q) */ tenAniso_R, /* 19: half of third moment of eigenvalues */ tenAniso_S, /* 20: frobenius norm, squared */ tenAniso_Skew, /* 21: R/sqrt(2*Q^3) */ tenAniso_Mode, /* 22: 3*sqrt(6)*det(dev)/norm(dev) = sqrt(2)*skew */ tenAniso_Th, /* 23: acos(sqrt(2)*skew)/3 */ tenAniso_Omega, /* 24: FA*(1+mode)/2 */ tenAniso_Det, /* 25: plain old determinant */ tenAniso_Tr, /* 26: plain old trace */ tenAniso_eval0, /* 27: largest eigenvalue */ tenAniso_eval1, /* 28: middle eigenvalue */ tenAniso_eval2, /* 29: smallest eigenvalue */ tenAnisoLast }; #define TEN_ANISO_MAX 29 /* ******** tenInterpType* enum ** ** different kinds of interpolations paths between tensors */ enum { tenInterpTypeUnknown, /* 0: nobody knows */ tenInterpTypeLinear, /* 1: simple per-coefficient linear */ tenInterpTypeLogLinear, /* 2: linear on logs (Log-Euclidean) */ tenInterpTypeAffineInvariant, /* 3: Riemannian approach of many authors */ tenInterpTypeWang, /* 4: affine-invariant of Z Wang & B Vemuri */ tenInterpTypeGeoLoxK, /* 5: geodesic-loxodrome on K_i invariants */ tenInterpTypeGeoLoxR, /* 6: geodesic-loxodrome on R_i invariants */ tenInterpTypeLoxK, /* 7: total loxodrome on K_i invariants */ tenInterpTypeLoxR, /* 8: total loxodrome on R_i invariants */ tenInterpTypeQuatGeoLoxK, /* 9: geodesic-loxodrome on K_i invariants */ tenInterpTypeQuatGeoLoxR, /* 10: geodesic-loxodrome on R_i invariants */ tenInterpTypeRThetaPhiLinear, /* 11: linear interpolation of (R,theta,phi), oriented by eigensystem of linear avg */ tenInterpTypeLast }; #define TEN_INTERP_TYPE_MAX 11 /* ******** tenGlyphType* enum ** ** the different types of glyphs that may be used for tensor viz */ enum { tenGlyphTypeUnknown, /* 0: nobody knows */ tenGlyphTypeBox, /* 1 */ tenGlyphTypeSphere, /* 2 */ tenGlyphTypeCylinder, /* 3 */ tenGlyphTypeSuperquad, /* 4 */ tenGlyphTypeBetterquad, /* 5: for T Schultz, GL Kindlmann. Superquadric Glyphs for Symmetric Second-Order Tensors. IEEE TVCG Nov/Dec 2010, 16(6):1595-1604 */ tenGlyphTypePolarPlot, /* 6 */ tenGlyphTypeLast }; #define TEN_GLYPH_TYPE_MAX 6 /* ******** tenGlyphParm struct ** ** all input parameters to tenGlyphGen */ typedef struct { int verbose; /* glyphs will be shown at samples that have confidence >= confThresh, and anisotropy anisoType >= anisoThresh, and if nmask is non-NULL, then the corresponding mask value must be >= maskThresh. If onlyPositive, then samples with a non-positive eigenvalue will be skipped, regardless of their purported anisotropy */ Nrrd *nmask; int anisoType, onlyPositive; float confThresh, anisoThresh, maskThresh; /* glyphs have shape glyphType and size glyphScale. Superquadrics are tuned by sqdSharp, and things that must polygonalize do so according to facetRes. Postscript rendering of glyph edges is governed by edgeWidth[] */ int glyphType, facetRes; float glyphScale, sqdSharp; float edgeWidth[5]; /* 0: contour, 1: front crease, 2: front non-crease */ /* glyphs are colored by eigenvector colEvec with the standard XYZ-RGB colormapping, with maximal saturation colMaxSat (use 0.0 to turn off coloring). Saturation is modulated by anisotropy colAnisoType, to a degree controlled by colAnisoModulate (if 0, saturation is not at all modulated by anistropy). Post-saturation, there is a per-channel gamma of colGamma. */ int colEvec, colAnisoType; float colMaxSat, colIsoGray, colGamma, colAnisoModulate, ADSP[4]; /* if doSlice, a slice of anisotropy sliceAnisoType will be depicted in grayscale as a sheet of grayscale squares, one per sample. As with glyphs, these are thresholded by confThresh and maskThresh (but not anisoThresh). Things can be lightened up with a sliceGamma > 1, after the slice's gray values have been mapped from [0,1] to [sliceBias,1]. The squares will be at their corresponding sample locations, but offset by sliceOffset */ unsigned int sliceAxis; size_t slicePos; int doSlice, sliceAnisoType; float sliceOffset, sliceBias, sliceGamma; } tenGlyphParm; #define TEN_ANISO_DESC \ "All the Westin metrics come in two versions. Currently supported:\n " \ "\b\bo \"cl1\", \"cl2\": Westin's linear\n " \ "\b\bo \"cp1\", \"cp2\": Westin's planar\n " \ "\b\bo \"ca1\", \"ca2\": Westin's linear + planar\n " \ "\b\bo \"cs1\", \"cs2\": Westin's spherical (1-ca)\n " \ "\b\bo \"ct1\", \"ct2\": GK's anisotropy type (cp/ca)\n " \ "\b\bo \"ra\": Basser/Pierpaoli relative anisotropy/sqrt(2)\n " \ "\b\bo \"fa\": Basser/Pierpaoli fractional anisotropy\n " \ "\b\bo \"vf\": volume fraction = 1-(Basser/Pierpaoli volume ratio)\n " \ "\b\bo \"tr\": trace" /* ******** tenGage* enum ** ** all the possible queries supported in the tenGage gage kind ** various properties of the quantities below (eigenvalues = v1, v2, v3): ** eigenvalue cubic equation: v^3 + A*v^2 + B*v + C = 0 ** Trace = v1 + v2 + v3 = -A ** B = v1*v2 + v1*v3 + v2*v3 ** Det = v1*v2*v3 = -C ** S = v1*v1 + v2*v2 + v3*v3 ** Q = (S-B)/9 = variance({v1,v2,v3})/2 = (RootRadius/2)^2 ** FA = 3*sqrt(Q/S) ** R = (9*A*B - 2*A^3 - 27*C)/54 = (5*A*B - 2*A*S - 27*C)/54 = thirdmoment({v1,v2,v3})/2 ** P = arccos(R/sqrt(Q)^3)/3 = phase angle of cubic solution ** ** NOTE: currently tenGage knows *nothing* about nrrd->measurementFrame! ** You probably want to call tenMeasurementFrameReduce() first. ** ** Hey- the problem with adding the RGB eigenvector coloring to the ** capability of tenGage is that because this is visualization, you ** can't easily control whether the measurement frame is applied, if ** known- in that sense the RGB info is uniquely different from the ** other vector and tensor items that can be queried . . . so after a ** brief appearance here the RGB evec coloring was removed. The ** gagePerVolume->data field that it motivated has rightly remained. ** ** !!! Changes to this list need to be propogated to: ** !!! tenGage.c: _tenGageTable[], _tenGageAnswer(), ** !!! enumsTen.c: tenGage airEnum. ** */ enum { tenGageUnknown, /* 0: nobody knows */ tenGageTensor, /* 1: "t", the reconstructed tensor: [7] */ tenGageConfidence, /* 2: "c", first of seven tensor values: [1] */ tenGageTrace, /* 3: "tr", trace of tensor: [1] */ tenGageNorm, /* 4: "n", frobenius norm of tensor: [1] */ tenGageB, /* 5: "b": [1] */ tenGageDet, /* 6: "det", determinant of tensor: [1] */ tenGageS, /* 7: "s", square of frobenius norm: [1] */ tenGageQ, /* 8: "q", (S - B)/9: [1] */ tenGageFA, /* 9: "fa", fractional anisotropy: [1] */ tenGageR, /* 10: "r", 9*A*B - 2*A^3 - 27*C: [1] */ tenGageMode, /* 11: "mode", sqrt(2)*R/sqrt(Q^3): [1] */ tenGageTheta, /* 12: "th", arccos(-mode)/AIR_PI: [1] */ tenGageModeWarp, /* 13: "modew", mode warped for better contrast: cos((1-m)*pi/2): [1] */ tenGageOmega, /* 14: "om", fa*(mode+1)/2: [1] */ tenGageEval, /* 15: "eval", all eigenvals of tensor : [3] */ tenGageEval0, /* 16: "eval0", major eigenval of tensor : [1] */ tenGageEval1, /* 17: "eval1", medium eigenval of tensor : [1] */ tenGageEval2, /* 18: "eval2", minor eigenval of tensor : [1] */ tenGageEvec, /* 19: "evec", major eigenvects of tensor: [9] */ tenGageEvec0, /* 20: "evec0", major eigenvect of tensor: [3] */ tenGageEvec1, /* 21: "evec1", medium eigenvect of tensor: [3] */ tenGageEvec2, /* 22: "evec2", minor eigenvect of tensor: [3] */ tenGageDelNormK2, /* 23: "delnk2": normalized gradient tensor of K2 = eval variance: [7] */ tenGageDelNormK3, /* 24: "delnk3": normal gradient tensor of K3 = R3 = eval skewness: [7] */ tenGageDelNormR1, /* 25: "delnr1": normalized gradient tensor of R1 = tensor norm: [7] */ tenGageDelNormR2, /* 26: "delnr2": normalized gradient tensor of R2 = FA: [7] */ tenGageDelNormPhi1, /* 27: "delnphi1": normalized rotation tangent around principal evector: [7] */ tenGageDelNormPhi2, /* 28: "delnphi2": normalized rotation tangent rotation around medium evector: [7] */ tenGageDelNormPhi3, /* 29: "delnphi3": normalized rotation tangent rotation around minor evector: [7] */ tenGageTensorGrad, /* 30: "tg", all tensor component gradients, starting with confidence gradient: [21] */ tenGageTensorGradMag, /* 31: "tgm", actually a 3-vector of tensor gradient norms, one for each axis: [3] */ tenGageTensorGradMagMag, /* 32: "tgmm", single scalar magnitude: [1] */ tenGageTraceGradVec, /* 33: "trgv": gradient (vector) of trace: [3] */ tenGageTraceGradMag, /* 34: "trgm": gradient magnitude of trace: [1] */ tenGageTraceNormal, /* 35: "trn": normal of trace: [3] */ tenGageNormGradVec, /* 36: "ngv", gradient (vector) of norm: [3] */ tenGageNormGradMag, /* 37: "ngm", gradient magnitude of norm: [1] */ tenGageNormNormal, /* 38: "nn", normal of norm: [3] */ tenGageBGradVec, /* 39: "bgv", gradient (vector) of B: [3] */ tenGageBGradMag, /* 40: "bgm", gradient magnitude of B: [1] */ tenGageBNormal, /* 41: "bn", normal of B: [3] */ tenGageDetGradVec, /* 42: "detgv", gradient (vector) of Det: [3] */ tenGageDetGradMag, /* 43: "detgm", gradient magnitude of Det: [1] */ tenGageDetNormal, /* 44: "detn", normal of Det: [3] */ tenGageSGradVec, /* 45: "sgv", gradient (vector) of S: [3] */ tenGageSGradMag, /* 46: "sgm", gradient magnitude of S: [1] */ tenGageSNormal, /* 47: "sn", normal of S: [3] */ tenGageQGradVec, /* 48: "qgv", gradient vector of Q: [3] */ tenGageQGradMag, /* 49: "qgm", gradient magnitude of Q: [1] */ tenGageQNormal, /* 50: "qn", normalized gradient of Q: [3] */ tenGageFAGradVec, /* 51: "fagv", gradient vector of FA: [3] */ tenGageFAGradMag, /* 52: "fagm", gradient magnitude of FA: [1] */ tenGageFANormal, /* 53: "fan", normalized gradient of FA: [3] */ tenGageRGradVec, /* 54: "rgv", gradient vector of Q: [3] */ tenGageRGradMag, /* 55: "rgm", gradient magnitude of Q: [1] */ tenGageRNormal, /* 56: "rn", normalized gradient of Q: [3] */ tenGageModeGradVec, /* 57: "mgv", gradient vector of Mode: [3] */ tenGageModeGradMag, /* 58: "mgm", gradient magnitude of Mode: [1] */ tenGageModeNormal, /* 59: "mn", normalized gradient of Mode: [3] */ tenGageThetaGradVec, /* 60: "thgv", gradient vector of Th: [3] */ tenGageThetaGradMag, /* 61: "thgm", gradient magnitude of Th: [1] */ tenGageThetaNormal, /* 62: "thn", normalized gradient of Th: [3] */ tenGageOmegaGradVec, /* 63: "omgv", gradient vector of Omega: [3] */ tenGageOmegaGradMag, /* 64: "omgm", gradient magnitude of Omega: [1] */ tenGageOmegaNormal, /* 65: "omn", normalized gradient of Omega: [3] */ tenGageInvarKGrads, /* 66: "ikgs", projections of tensor gradient onto the normalized shape gradients: eval mean, variance, skew, in that order: [9] */ tenGageInvarKGradMags, /* 67: "ikgms", vector magnitude of the spatial invariant gradients (above): [3] */ tenGageInvarRGrads, /* 68: "irgs", projections of tensor gradient onto the normalized shape gradients: eval mean, variance, skew, in that order: [9] */ tenGageInvarRGradMags, /* 69: "irgms", vector magnitude of the spatial invariant gradients (above): [3] */ tenGageRotTans, /* 70: "rts", projections of the tensor grad onto *normalized* rotation tangents: [9] */ tenGageRotTanMags, /* 71: "rtms", mags of vectors above: [3] */ tenGageEvalGrads, /* 72: "evgs", projections of tensor gradient onto gradients of eigenvalues: [9] */ tenGageCl1, /* 73: same as tenAniso_Cl1, but faster */ tenGageCp1, /* 74: same as tenAniso_Cp1, but faster */ tenGageCa1, /* 75: same as tenAniso_Ca1, but faster */ tenGageClpmin1, /* 76: min(cl1,cp1) */ tenGageCl2, /* 77: same as tenAniso_Cl2, but faster */ tenGageCp2, /* 78: same as tenAniso_Cp2, but faster */ tenGageCa2, /* 79: same as tenAniso_Ca2, but faster */ tenGageClpmin2, /* 80: min(cl2,cp2) */ tenGageHessian, /* 81: "hess", all hessians of tensor components: [63] */ tenGageTraceHessian, /* 82: "trhess", hessian(trace): [9] */ tenGageTraceHessianEval, /* 83 */ tenGageTraceHessianEval0, /* 84 */ tenGageTraceHessianEval1, /* 85 */ tenGageTraceHessianEval2, /* 86 */ tenGageTraceHessianEvec, /* 87 */ tenGageTraceHessianEvec0, /* 88 */ tenGageTraceHessianEvec1, /* 89 */ tenGageTraceHessianEvec2, /* 90 */ tenGageTraceHessianFrob, /* 91 */ tenGageBHessian, /* 92: "bhess": [9] */ tenGageDetHessian, /* 93: "dethess": [9] */ tenGageSHessian, /* 94: "shess": [9] */ tenGageQHessian, /* 95: "qhess": [9] */ tenGageFAHessian, /* 96: "fahess": [9] */ tenGageFAHessianEval, /* 97: "fahesseval": [3] */ tenGageFAHessianEval0, /* 98: "fahesseval0": [1] */ tenGageFAHessianEval1, /* 99: "fahesseval1": [1] */ tenGageFAHessianEval2, /* 100: "fahesseval2": [1] */ tenGageFAHessianEvec, /* 101: "fahessevec": [9] */ tenGageFAHessianEvec0, /* 102: "fahessevec0": [3] */ tenGageFAHessianEvec1, /* 103: "fahessevec1": [3] */ tenGageFAHessianEvec2, /* 104: "fahessevec2": [3] */ tenGageFAHessianFrob, /* 105 */ tenGageFARidgeSurfaceStrength, /* 106: "farsurfstrn": [1] */ tenGageFAValleySurfaceStrength, /* 107: "favsurfstrn": [1] */ tenGageFALaplacian, /* 108: "falapl": [1] */ tenGageFAHessianEvalMode,/* 109: "fahessevalmode": [1] */ tenGageFARidgeLineAlignment, /* 110: "farlinealn": [1] */ tenGageFARidgeSurfaceAlignment, /* 111: "farsurfaln": [1] */ tenGageFA2ndDD, /* 112: "fa2d": [1] */ tenGageFAGeomTens, /* 113: "fagten", sym. matx w/ evals {0, K1, K2} and evecs {grad, cdir0, cdir1}: [9] */ tenGageFAKappa1, /* 114: "fak1", 1st princ curv: [1] */ tenGageFAKappa2, /* 115: "fak2", 2nd princ curv (k2 <= k1): [1] */ tenGageFATotalCurv, /* 116: "fatc", L2 norm(K1,K2): [1] */ tenGageFAShapeIndex, /* 117: "fasi", Koen.'s shape index, ("S"): [1] */ tenGageFAMeanCurv, /* 118: "famc", mean curvature (K1 + K2)/2: [1] */ tenGageFAGaussCurv, /* 119: "fagc", gaussian curvature K1*K2: [1] */ tenGageFACurvDir1, /* 120: "facdir1", 1st princ curv direction: [3] */ tenGageFACurvDir2, /* 121: "facdir2", 2nd princ curv direction: [3] */ tenGageFAFlowlineCurv, /* 122: "fafc", curv of normal streamline: [1] */ tenGageRHessian, /* 123: "rhess": [9] */ tenGageModeHessian, /* 124: "mhess": [9] */ tenGageModeHessianEval, /* 125: "mhesseval": [3] */ tenGageModeHessianEval0, /* 126: "mhesseval0": [1] */ tenGageModeHessianEval1, /* 127: "mhesseval1": [1] */ tenGageModeHessianEval2, /* 128: "mhesseval2": [1] */ tenGageModeHessianEvec, /* 129: "mhessevec": [9] */ tenGageModeHessianEvec0, /* 130: "mhessevec0": [3] */ tenGageModeHessianEvec1, /* 131: "mhessevec1": [3] */ tenGageModeHessianEvec2, /* 132: "mhessevec2": [3] */ tenGageModeHessianFrob, /* 133 */ tenGageOmegaHessian, /* 134: "omhess": [9] */ tenGageOmegaHessianEval, /* 135: "omhesseval": [3] */ tenGageOmegaHessianEval0,/* 136: "omhesseval0": [1] */ tenGageOmegaHessianEval1,/* 137: "omhesseval1": [1] */ tenGageOmegaHessianEval2,/* 138: "omhesseval2": [1] */ tenGageOmegaHessianEvec, /* 139: "omhessevec": [9] */ tenGageOmegaHessianEvec0,/* 140: "omhessevec0": [3] */ tenGageOmegaHessianEvec1,/* 141: "omhessevec1": [3] */ tenGageOmegaHessianEvec2,/* 142: "omhessevec2": [3] */ tenGageOmegaLaplacian, /* 143: "omlapl": [1] */ tenGageOmega2ndDD, /* 144: "om2d": [1] */ tenGageOmegaHessianContrTenEvec0, /* 145: "omhesscte0": [1] */ tenGageOmegaHessianContrTenEvec1, /* 146: "omhesscte1": [1] */ tenGageOmegaHessianContrTenEvec2, /* 147: "omhesscte2": [1] */ tenGageTraceGradVecDotEvec0, /* 148: "trgvdotevec0": [1] */ tenGageTraceDiffusionAlign, /* 149: "datr": [1] */ tenGageTraceDiffusionFraction, /* 150: "dftr": [1] */ tenGageFAGradVecDotEvec0, /* 151: "fagvdotevec0": [1] */ tenGageFADiffusionAlign, /* 152: "dafa": [1] */ tenGageFADiffusionFraction, /* 153: "dffa": [1] */ tenGageOmegaGradVecDotEvec0, /* 154: "omgvdotevec0": [1] */ tenGageOmegaDiffusionAlign, /* 155: "daom": [1] */ tenGageOmegaDiffusionFraction, /* 156: "dfom": [1] */ tenGageConfGradVecDotEvec0, /* 157: "confgvdotevec0": [1] */ tenGageConfDiffusionAlign, /* 158: "daconf": [1] */ tenGageConfDiffusionFraction, /* 159: "dfconf": [1] */ tenGageCovariance, /* 160: "cov" 4rth order covariance tensor: [21] in order of appearance: 0:xxxx 1:xxxy 2:xxxz 3:xxyy 4:xxyz 5:xxzz 6:xyxy 7:xyxz 8:xyyy 9:xyyz 10:xyzz 11:xzxz 12:xzyy 13:xzyz 14:xzzz 15:yyyy 16:yyyz 17:yyzz 18:yzyz 19:yzzz 20:zzzz */ tenGageCovarianceRGRT, /* 161: "covr" covariance tensor expressed in frame of R invariant gradients and rotation tangents */ tenGageCovarianceKGRT, /* 162: "covk" covariance tensor expressed in frame of K invariant gradients and rotation tangents */ tenGageTensorLogEuclidean, /* 163: "logeuc" log-euclidean interp */ tenGageTensorQuatGeoLoxK, /* 164: "qglk" QGL-K interpolation */ tenGageTensorQuatGeoLoxR, /* 165: "qglr" QGL-R interpolation */ tenGageTensorRThetaPhiLinear, /* 166: "rtpl" RThetaPhiLinear interp */ tenGageCl1GradVec, /* 167: "cl1gv" gradient vector of cl1: [3] */ tenGageCl1GradMag, /* 168: "cl1gm" gradient magnitude of cl1: [1] */ tenGageCl1Normal, /* 169: "cl1gn" normal of cl1: [3] */ tenGageCp1GradVec, /* 170: "cp1gv" gradient vector of cp1: [3] */ tenGageCp1GradMag, /* 171: "cp1gm" gradient magnitude of cp1: [1] */ tenGageCp1Normal, /* 172: "cl1gn" normal of cp1: [3] */ tenGageCa1GradVec, /* 173: "ca1gv" gradient vector of ca1: [3] */ tenGageCa1GradMag, /* 174: "ca1gm" gradient magnitude of ca1: [1] */ tenGageCa1Normal, /* 175: "cl1gn" normal of ca1: [3] */ tenGageTensorGradRotE, /* 176: "tgrote" all tensor component gradients, starting with confidence gradient. Rotated such that eigenvalue derivatives are on the diagonal: [21] */ tenGageEvalHessian, /* 177: "evalhess" Hessian of the eigenvalues: [27] */ tenGageCl1Hessian, /* 178: "cl1hess" Hessian of cl1: [9] */ tenGageCl1HessianEval, /* 179: "cl1hesseval" Hessian evals of cl1: [3] */ tenGageCl1HessianEval0,/* 180: "cl1hesseval0" First Hess eval of cl1: [1] */ tenGageCl1HessianEval1,/* 181: "cl1hesseval1" Second Hess eval of cl1: [1] */ tenGageCl1HessianEval2,/* 182: "cl1hesseval2" Third Hess eval of cl1: [1] */ tenGageCl1HessianEvec, /* 183: "cl1hessevec" Hessian evecs of cl1: [9] */ tenGageCl1HessianEvec0,/* 184: "cl1hessevec0" First Hess evec of cl1: [3] */ tenGageCl1HessianEvec1,/* 185: "cl1hessevec1" Second Hess evec of cl1: [3] */ tenGageCl1HessianEvec2,/* 186: "cl1hessevec2" Third Hess evec of cl1: [3] */ tenGageCp1Hessian, /* 187: "cp1hess" Hessian of cp1: [9] */ tenGageCp1HessianEval, /* 188: "cp1hesseval" Hessian evals of cp1: [3] */ tenGageCp1HessianEval0,/* 189: "cp1hesseval0" First Hess eval of cp1: [1] */ tenGageCp1HessianEval1,/* 190: "cp1hesseval1" Second Hess eval of cp1: [1] */ tenGageCp1HessianEval2,/* 191: "cp1hesseval2" Third Hess eval of cp1: [1] */ tenGageCp1HessianEvec, /* 192: "cp1hessevec" Hessian evecs of cp1: [9] */ tenGageCp1HessianEvec0,/* 193: "cp1hessevec0" First Hess evec of cp1: [3] */ tenGageCp1HessianEvec1,/* 194: "cp1hessevec1" Second Hess evec of cp1: [3] */ tenGageCp1HessianEvec2,/* 195: "cp1hessevec2" Third Hess evec of cp1: [3] */ tenGageCa1Hessian, /* 196: "ca1hess" Hessian of ca1: [9] */ tenGageCa1HessianEval, /* 197: "ca1hesseval" Hessian evals of ca1: [3] */ tenGageCa1HessianEval0,/* 198: "ca1hesseval0" First Hess eval of ca1: [1] */ tenGageCa1HessianEval1,/* 199: "ca1hesseval1" Second Hess eval of ca1: [1] */ tenGageCa1HessianEval2,/* 200: "ca1hesseval2" Third Hess eval of ca1: [1] */ tenGageCa1HessianEvec, /* 201: "ca1hessevec" Hessian evecs of ca1: [9] */ tenGageCa1HessianEvec0,/* 202: "ca1hessevec0" First Hess evec of ca1: [3] */ tenGageCa1HessianEvec1,/* 203: "ca1hessevec1" Second Hess evec of ca1: [3] */ tenGageCa1HessianEvec2,/* 204: "ca1hessevec2" Third Hess evec of ca1: [3] */ tenGageFiberCurving, /* 205: "fibcurv" Savadjiev et al. fiber curving */ tenGageFiberDispersion,/* 206: "fibdisp" Savadjiev et al. fiber dispersion */ tenGageAniso, /* 207: "an", all anisos: [TEN_ANISO_MAX+1] */ tenGageLast }; #define TEN_GAGE_ITEM_MAX 207 /* ******** tenDwiGage* enum ** ** all things that can be measured in the diffusion weighted images that ** underly diffusion tensor imaging */ enum { tenDwiGageUnknown, /* 0: nobody knows */ /* 1: "all", all the measured values, both baseline and diffusion weighted: [N], where N is the number of DWIs */ tenDwiGageAll, /* 2: "b0", the non-Dwi image value, either by direct measurement or by estimation: [1] HEY: currently a hack, because it assumes a single known B0 */ tenDwiGageB0, /* 3: "jdwi", just the DWIs, no B0: [N-1] (HEY same hack) */ tenDwiGageJustDWI, /* 4: "adc", ADCs from the DWIs: [N-1] (HEY same hack) */ tenDwiGageADC, /* 5: "mdwi", the average Dwi image value, which is thresholded to create the confidence mask: [1] */ tenDwiGageMeanDWIValue, /* 6: "tlls": [7], 7: "tllserr": [1], 8: "tllserrlog": [1], 9: "tllslike": [1], linear least squares fit of tensor value to log(Dwi)s */ tenDwiGageTensorLLS, tenDwiGageTensorLLSError, /* RMS error w/ Dwis */ tenDwiGageTensorLLSErrorLog, /* RMS error w/ log(Dwi)s */ tenDwiGageTensorLLSLikelihood, /* 10: "twls": [7], 11: "twlserr": [1], 12: "twlserrlog": [1], 13: "twlslike": [1], weighted least squares fit of tensor value to log(Dwi)s */ tenDwiGageTensorWLS, tenDwiGageTensorWLSError, tenDwiGageTensorWLSErrorLog, tenDwiGageTensorWLSLikelihood, /* 14: "tnls": [7], 15: "tnlserr": [1], 16: "tnlserrlog": [1], 17: "tnlslike": [1], non-linear least squares fit of tensor value to Dwis (not log) */ tenDwiGageTensorNLS, tenDwiGageTensorNLSError, tenDwiGageTensorNLSErrorLog, tenDwiGageTensorNLSLikelihood, /* 18: "tmle": [7], 19: "tmleerr": [1], 20: "tmleerrlog": [1], 21: "tmlelike": [1], maximum-likelihood fit of tensor value to Dwis */ tenDwiGageTensorMLE, tenDwiGageTensorMLEError, tenDwiGageTensorMLEErrorLog, tenDwiGageTensorMLELikelihood, /* 22: "t": [7], 23: "terr": [1], 24: "terrlog": [1], 25: "tlike": [1], one of the above tensors and its errors, depending on settings */ tenDwiGageTensor, tenDwiGageTensorError, tenDwiGageTensorErrorLog, tenDwiGageTensorLikelihood, /* 26: "c", first of seven tensor values: [1] */ tenDwiGageConfidence, /* 27: "fa", FA computed from the single tensor: [1] */ tenDwiGageFA, /* 28: "adwie", all errors between measured and predicted DWIs [N-1] (HEY same hack) */ tenDwiGageTensorAllDWIError, /* 29: "2qserr": [1] 30: "2qs", two tensor fitting by q-ball segmentation: [14] 31: "2qsnerr": [15] */ tenDwiGage2TensorQSeg, tenDwiGage2TensorQSegError, tenDwiGage2TensorQSegAndError, /* 32: "2pelederr": [1] 33: "2peled", two tensor fitting by q-ball segmentation: [14] 34: "2pelednerr": [15] */ tenDwiGage2TensorPeled, tenDwiGage2TensorPeledError, tenDwiGage2TensorPeledAndError, /* 35: "2peledlminfo", levmar output info vector: [9] note: length 9 being correct is checked in _tenDwiGagePvlDataNew() */ tenDwiGage2TensorPeledLevmarInfo, tenDwiGageLast }; #define TEN_DWI_GAGE_ITEM_MAX 35 /* ******** tenEstimate1Method* enum ** ** the different ways of doing single tensor estimation */ enum { tenEstimate1MethodUnknown, /* 0 */ tenEstimate1MethodLLS, /* 1 */ tenEstimate1MethodWLS, /* 2 */ tenEstimate1MethodNLS, /* 3 */ tenEstimate1MethodMLE, /* 4 */ tenEstimate1MethodLast }; #define TEN_ESTIMATE_1_METHOD_MAX 4 /* ******** tenEstimate2Method* enum ** ** the different ways of doing two-tensor estimation */ enum { tenEstimate2MethodUnknown, /* 0 */ tenEstimate2MethodQSegLLS, /* 1 */ tenEstimate2MethodPeled, /* 2 */ tenEstimate2MethodLast }; #define TEN_ESTIMATE_2_METHOD_MAX 2 /* ******** tenEvecRGBParm struct ** ** dumb little bag for the parameters relating to how to do the ** eigenvector -> RGB mapping, since its needed by various things in ** various contexts. Note that you may need two of these, one for ** doing rgb(evec0) (the linear part) and one for doing rgb(evec2) ** (the planar part). This used to have "aniso0" and "aniso2", but ** the associated methods were clumsy and redundant. */ typedef struct { unsigned int which; /* when the eigenvector hasn't already been computed, which eigenvector to map: 0 for linear, 2 or planar, 1 for orthotropic */ int aniso; /* which anisotropy metric modulates saturation */ double confThresh, /* confidence threshold */ anisoGamma, /* gamma on aniso, pre-mapping */ gamma, /* per RGB component gamma, post-mapping */ bgGray, /* gray-value for low confidence samples */ isoGray, /* gray-value for isotropic samples */ maxSat; /* maximum saturation */ int typeOut, /* when output type is flexible, and if this is nrrdTypeUChar or nrrdTypeUShort, then output will be quantized to those types (range [0,255] and [0,65535] respectively); otherwise values are copied directly to output */ genAlpha; /* when output value set is flexible, create RGBA values instead of just RGB */ } tenEvecRGBParm; /* ******** tenFiberType* enum ** ** the different kinds of fiber tractography that we do */ enum { tenFiberTypeUnknown, /* 0: nobody knows */ tenFiberTypeEvec0, /* 1: standard following of principal eigenvector */ tenFiberTypeEvec1, /* 2: following medium eigenvector */ tenFiberTypeEvec2, /* 3: following minor eigenvector */ tenFiberTypeTensorLine, /* 4: Weinstein-Kindlmann tensorlines */ tenFiberTypePureLine, /* 5: "pure" tensorlines- multiplication only */ tenFiberTypeZhukov, /* 6: Zhukov's oriented tensor reconstruction */ tenFiberTypeLast }; #define TEN_FIBER_TYPE_MAX 6 /* ******** tenDwiFiberType* enum ** ** how tractography is done in DWI volumes. This is orthogonal to ** how single- or two-tensor estimation is done; it describes what we ** do with the model(s) once estimated */ enum { tenDwiFiberTypeUnknown, /* 0: nobody knows */ tenDwiFiberType1Evec0, /* 1: like old-fashioned tractography */ tenDwiFiberType2Evec0, /* 2: only using 2-tensor fits */ tenDwiFiberType12BlendEvec0, /* 3: blend between 1- and 2-ten evec0 methods, based on something else */ tenDwiFiberTypeLast }; #define TEN_DWI_FIBER_TYPE_MAX 3 /* ******** tenFiberIntg* enum ** ** the different integration styles supported. Obviously, this is more ** general purpose than fiber tracking, so this will be moved (elsewhere ** in Teem) as needed */ enum { tenFiberIntgUnknown, /* 0: nobody knows */ tenFiberIntgEuler, /* 1: dumb but fast */ tenFiberIntgMidpoint, /* 2: 2nd order Runge-Kutta */ tenFiberIntgRK4, /* 3: 4rth order Runge-Kutta */ tenFiberIntgLast }; #define TEN_FIBER_INTG_MAX 3 /* ******** tenFiberStop* enum ** ** the different reasons why fibers stop going (as stored in ** tenFiberSingle->whyStop[]), or never got started ** (tenFiberSingle->whyNowhere), or never went far enough (also ** tenFiberSingle->whyNowhere). ** ** The addition of tenFiberStopMinLength and tenFiberStopMinNumSteps ** really stretch the meaningfulness of "tenFiberStop", but its the ** only logical place for such constraints to go. ** NOTE: tenFiberStopMinLength and tenFiberStopMinNumSteps only make ** sense as a value for whyNowhere, not whyStop, despite the name. */ enum { tenFiberStopUnknown, /* 0: nobody knows, or, for tfx->whyNowhere: no, actually, we *did* get somewhere with this fiber */ tenFiberStopAniso, /* 1: specified aniso got below specified level */ tenFiberStopLength, /* 2: fiber length in world space got too long */ tenFiberStopNumSteps, /* 3: took too many steps along fiber */ tenFiberStopConfidence, /* 4: tensor "confidence" value went too low */ tenFiberStopRadius, /* 5: radius of curvature got too small */ tenFiberStopBounds, /* 6: fiber position stepped outside volume */ tenFiberStopFraction, /* 7: during multi-tensor tracking, fractional constituency of the tracked tensor got too small */ tenFiberStopStub, /* 8: treat single vertex fibers as non-starters */ tenFiberStopMinLength, /* 9: fibers with total (both halves) small length are discarded */ tenFiberStopMinNumSteps, /* 10: fibers with total (both halves) small # steps are discarded (more general-purpose than tenFiberStopStub) */ tenFiberStopLast }; #define TEN_FIBER_STOP_MAX 10 /* ******** #define TEN_FIBER_NUM_STEPS_MAX ** ** whatever the stop criteria are for fiber tracing, no fiber half can ** have more points than this- a useful sanity check against fibers ** done amok. */ #define TEN_FIBER_NUM_STEPS_MAX 10240 enum { tenFiberParmUnknown, /* 0: nobody knows */ tenFiberParmStepSize, /* 1: base step size */ tenFiberParmUseIndexSpace, /* 2: non-zero iff output of fiber should be seeded in and output in index space, instead of default world */ tenFiberParmWPunct, /* 3: tensor-line parameter */ tenFiberParmVerbose, /* 4: verbosity */ tenFiberParmLast }; #define TEN_FIBER_PARM_MAX 4 enum { tenTripleTypeUnknown, /* 0: nobody knows */ tenTripleTypeEigenvalue, /* 1: eigenvalues sorted in descending order */ tenTripleTypeMoment, /* 2: (mu1,mu2,mu3) */ tenTripleTypeXYZ, /* 3: eval rotation, after Bahn'99 JMR:141(68-77) */ tenTripleTypeRThetaZ, /* 4: cylindrical coords of rotated evals */ tenTripleTypeRThetaPhi, /* 5: spherical coords of rotated evals */ tenTripleTypeJ, /* 6: (J1,J2,J3) principal invariants */ tenTripleTypeK, /* 7: (K1,K2,K3) cylindrical invariants */ tenTripleTypeR, /* 8: (R1,R2,R3) spherical invariants */ tenTripleTypeWheelParm, /* 9: eigenvalue wheel (center,radius,angle) */ tenTripleTypeLast }; #define TEN_TRIPLE_TYPE_MAX 9 /* ******** tenFiberContext ** ** catch-all for input, state, and output of fiber tracing. Luckily, like ** in a gageContext, NOTHING in here is set directly by the user; everything ** should be through the tenFiber* calls */ typedef struct { /* ---- input -------- */ const Nrrd *nin; /* the tensor OR DWI volume being analyzed */ NrrdKernelSpec *ksp; /* reconstruction kernel for tensors or DWIs */ int useDwi, /* we're working in a DWI, not a tensor, volume */ fiberType, /* from tenFiberType* OR tenDwiFiberType* enum */ fiberProbeItem, /* item to probe along fiber and possibly save in tenFiberSingle->nval */ intg, /* from tenFiberIntg* enum */ anisoStopType, /* which aniso we do a threshold on */ anisoSpeedType, /* base step size is function of this anisotropy */ stop, /* BITFLAG for different reasons to stop a fiber */ useIndexSpace, /* output in index space, not world space */ verbose; /* blah blah blah */ double anisoThresh, /* anisotropy threshold */ anisoSpeedFunc[3]; /* parameters of mapping aniso to speed */ unsigned int maxNumSteps, /* max # steps allowed on one fiber *half* */ minNumSteps; /* min signficiant # steps on *whole* fiber */ double stepSize, /* step size in world space */ maxHalfLen, /* longest propagation (forward or backward) allowed from midpoint */ minWholeLen, /* minimum significant length of whole fiber */ confThresh, /* confidence threshold */ minRadius, /* minimum radius of curvature of path */ minFraction; /* minimum fractional constituency in multi-tensor */ double wPunct; /* knob for tensor lines */ unsigned int ten2Which; /* which path to follow in 2-tensor tracking */ /* ---- internal ----- */ gageQuery query; /* query we'll send to gageQuerySet */ int halfIdx, /* current fiber half being computed (0 or 1) */ mframeUse; /* need to use mframe[] and mframeT[] */ double mframe[9], /* measurement frame in normal matrix lay-out */ mframeT[9], /* transpose of mframe[] */ wPos[3], /* current world space location */ wDir[3], /* difference between this and last world space pos */ lastDir[3], /* previous value of wDir */ seedEvec[3]; /* principal eigenvector first found at seed point */ int lastDirSet, /* lastDir[] is usefully set */ lastTenSet; /* lastTen[] is usefully set */ unsigned int ten2Use; /* which of the 2-tensors was last used */ gageContext *gtx; /* wrapped around pvl */ gagePerVolume *pvl; /* wrapped around dtvol */ const double *gageTen, /* gageAnswerPointer(pvl, tenGageTensor) */ *gageEval, /* gageAnswerPointer(pvl, tenGageEval) */ *gageEvec, /* gageAnswerPointer(pvl, tenGageEvec) */ *gageAnisoStop, /* gageAnswerPointer(pvl, tenGage) */ *gageAnisoSpeed, /* gageAnswerPointer(pvl, tenGage) */ *gageTen2; /* gageAnswerPointer(pvl, tenDwiGage..2Tensor..) */ double ten2AnisoStop; double fiberTen[7], fiberEval[3], fiberEvec[9], fiberAnisoStop, fiberAnisoSpeed; double radius; /* current radius of curvature */ /* ---- output ------- */ double halfLen[2]; /* length of each fiber half in world space */ unsigned int numSteps[2]; /* how many samples are used for each fiber half */ int whyStop[2], /* why backward/forward (0/1) tracing stopped (from tenFiberStop* enum) */ whyNowhere; /* why fiber never got started (from tenFiberStop*) */ } tenFiberContext; /* ******** tenFiberSingle ** ** experimental struct for holding results from a single tracing */ typedef struct { /* ------- available for recording for reference, not used by ten */ double seedPos[3]; /* where was the seed point */ unsigned int dirIdx; /* which direction at seedpoint to follow */ unsigned int dirNum; /* how many directions at seedpnt could be followed */ /* ------- output ------- */ Nrrd *nvert; /* locations of tract vertices */ double halfLen[2]; /* (same as in tenFiberContext) */ unsigned int seedIdx, /* which index in nvert is for seedpoint */ stepNum[2]; /* (same as in tenFiberContext) */ int whyStop[2], /* (same as in tenFiberContext) */ whyNowhere; /* (same as in tenFiberContext) */ Nrrd *nval; /* results of probing at vertices */ double measr[NRRD_MEASURE_MAX+1]; /* a controlled mess */ } tenFiberSingle; /* ******** tenFiberMulti ** ** container for multiple fibers */ typedef struct { tenFiberSingle *fiber; unsigned int fiberNum; airArray *fiberArr; } tenFiberMulti; /* ******** struct tenEmBimodalParm ** ** input and output parameters for tenEMBimodal (for fitting two ** gaussians to a histogram). Currently, all fields are directly ** set/read; no API help here. ** ** "fraction" means prior probability ** ** In the output, material #1 is the one with the lower mean */ typedef struct { /* ----- input -------- */ double minProb, /* threshold for negligible posterior prob. values */ minProb2, /* minProb for 2nd stage fitting */ minDelta, /* convergence test for maximization */ minFraction, /* smallest fraction (in 0.0 to 1.0) that material 1 or 2 can legitimately have */ minConfidence, /* smallest confidence value that the model fitting is allowed to have */ twoStage, /* wacky two-stage fitting */ verbose; /* output messages and/or progress images */ unsigned int maxIteration; /* cap on # of non-convergent iters allowed */ /* ----- internal ----- */ double *histo, /* double version of histogram */ *pp1, *pp2, /* pre-computed posterior probabilities for the current iteration */ vmin, vmax, /* value range represented by histogram. This is saved from given histogram, and used to inform final output values, but it is not used for any intermediate histogram calculations, all of which are done entirely in index space */ delta; /* some measure of model change between iters */ int N, /* number of bins in histogram */ stage; /* current stage (1 or 2) */ unsigned int iteration; /* current iteration */ /* ----- output ------- */ double mean1, stdv1, /* material 1 mean and standard dev */ mean2, stdv2, /* same for material 2 */ fraction1, /* fraction of material 1 (== 1 - fraction2) */ confidence, /* (mean2 - mean1)/(stdv1 + stdv2) */ threshold; /* minimum-error threshold */ } tenEMBimodalParm; /* ******** struct tenGradientParm ** ** all parameters for repulsion-based generation of gradient directions ** ** the old physics-based point-repulsion code (RK2 integration of ** equations of motion, with drag force) is gone; this is only the ** fast gradient descent with some strategies for adaptive step size */ typedef struct { /* ----------------------- INPUT */ double initStep, /* initial step size for gradient descent */ jitter, /* amount by which distribution is jittered when starting with a given input set, as a fraction of the ideal edge length */ minVelocity, /* minimum mean gradient velocity that signifies end of first distribution phase */ minPotentialChange, /* minimum change in potential that signifies end of first distribution phase */ minMean, /* mean gradient length that signifies end of secondary balancing phase */ minMeanImprovement; /* magnitude of improvement (reduction) of mean gradient length that signifies end of secondary balancing phase */ int single, /* distribute single points, instead of anti-podal pairs of points */ insertZeroVec, /* when computing output in tenGradientDistribute (and only there, though this is called by tenGradientGenerate), insert the zero vector at the beginning of the list, corresponding to a non-DWI B0 image */ verbose; /* verbosity level; 0 turns off everything */ unsigned int snap, /* interval of interations at which to save snapshats of distribution */ report, /* interval of interations at which to report on progress */ expo, /* the exponent N that defines the potential energy profile 1/r^N (coulomb: N=1) */ seed, /* seed value for random number generator */ maxEdgeShrink, /* max number of times we try to compute an update with smaller edge normalization */ minIteration, /* run for at least this many iterations, which can be useful for high exponents, for which potential measurements can easily go to infinity */ maxIteration; /* bail if we haven't converged by this number of iterations */ double expo_d; /* floating point exponent. If expo is zero, this is the value that matters */ /* ----------------------- INTERNAL */ double step, /* actual current step size (adjusted during the algorithm depending on progress) */ nudge; /* how to increase realDT with each iteration */ /* ----------------------- OUTPUT */ unsigned int itersUsed; /* total number of iterations */ double potential, /* potential, without edge normalization */ potentialNorm, /* potential, with edge normalization */ angle, /* minimum angle */ edge; /* minimum edge length */ } tenGradientParm; /* ******** struct tenEstimateContext ** ** for handling estimation of diffusion models */ typedef struct { /* input ----------- */ double bValue, /* scalar b value */ valueMin, /* smallest sensible for input Dwi value, must be > 0.0 (for taking log) */ sigma, /* noise parameter */ dwiConfThresh, /* mean Dwi threshold for confidence mask */ dwiConfSoft; /* softness in confidence mask */ /* NOTE: for both _ngrad and _nbmat: 1) only one can be non-NULL, and axis[1].size is the total # values, both Dwi and non-Dwi 2) NO additional re-normalization is done on the grads/bmats, UNLIKE the normalization performed by tenDWMRIKeyValueParse(). */ const Nrrd *_ngrad, /* caller's 3-by-allNum gradient list */ *_nbmat; /* caller's 6-by-allNum B-matrix list, off-diagonals are *NOT* pre-multiplied by 2 */ unsigned int *skipList; /* list of value indices that we are to skip */ airArray *skipListArr; /* airArray around skipList */ const float *all_f; /* caller's list of all values (length allNum) */ const double *all_d; /* caller's list of all values (length allNum) */ int simulate, /* if non-zero, we're being used for simulation, not estimation: be tolerant of unset parms */ estimate1Method, /* what kind of single-tensor estimation to do */ estimateB0, /* if non-zero, B0 should be estimated along with rest of model. Otherwise, B0 is found by simply taking average of non-Dwi images */ recordTime, /* if non-zero, record estimation time */ recordErrorDwi, recordErrorLogDwi, recordLikelihoodDwi, verbose, /* blah blah blah */ negEvalShift, /* if non-zero, shift eigenvalues upwards so that smallest one is non-negative */ progress; /* progress indication for volume processing */ unsigned int WLSIterNum; /* number of iterations for WLS */ /* internal -------- */ /* a "dwi" in here is basically any value (diffusion-weighted or not) that varies as a function of the model parameters being estimated */ int flag[128]; /* flags for state management */ unsigned int allNum, /* total number of images (Dwi and non-Dwi) */ dwiNum; /* number of Dwis */ Nrrd *nbmat, /* B-matrices (dwiNum of them) for the Dwis, with off-diagonals (*YES*) pre-multiplied by 2, and with a 7th column of -1.0 if estimateB0 */ *nwght, /* dwiNum x dwiNum matrix of weights */ *nemat; /* pseudo-inverse of nbmat */ double knownB0, /* B0 known from DWI, only if !estimateB0 */ *all, /* (copy of) vector of input values, allocated for allNum */ *bnorm, /* frob norm of B-matrix, allocated for allNum */ *allTmp, *dwiTmp, /* for storing intermediate values, allocated for allNum and dwiNum respectively */ *dwi; /* the Dwi values, allocated for dwiNum */ unsigned char *skipLut; /* skipLut[i] non-zero if we should ignore all[i], allocated for allNum */ /* output ---------- */ double estimatedB0, /* estimated non-Dwi value, only if estimateB0 */ ten[7], /* the estimated single tensor */ conf, /* the "confidence" mask value (i.e. ten[0]) */ mdwi, /* mean Dwi value (used for conf mask calc) */ time, /* time required for estimation */ errorDwi, /* error in Dwi of estimate */ errorLogDwi, /* error in log(Dwi) of estimate */ likelihoodDwi; /* the maximized likelihood */ } tenEstimateContext; /* ******** struct tenDwiGageKindData ** ** the kind data has static info that is set by the user ** and then not altered-- or else it wouldn't really be per-kind, ** and it wouldn't be thread-safe */ typedef struct { Nrrd *ngrad, *nbmat; /* owned by us */ double thresh, soft, bval, valueMin; int est1Method, est2Method; unsigned int randSeed; } tenDwiGageKindData; /* ******** struct tenDwiGagePvlData ** ** the pvl data is the dynamic stuff (buffers, mostly) ** that is potentially modified per query. ** being part of the pvl, such modifications are thread-safe ** ** the reason for having two tenEstimateContexts is that within ** one volume you will sometimes want to measure both one and ** two tensors, and it would be crazy to incur the overhead of ** switching between the two *per-query*. */ typedef struct { tenEstimateContext *tec1, /* for estimating single tensors */ *tec2; /* for estimating two-tensors */ double *vbuf; unsigned int *wght; double *qvals; double *qpoints; double *dists; double *weights; Nrrd *nten1EigenGrads; airRandMTState *randState; unsigned int randSeed; double ten1[7], ten1Evec[9], ten1Eval[3]; int levmarUseFastExp; unsigned int levmarMaxIter; double levmarTau, levmarEps1, levmarEps2, levmarEps3, levmarDelta, levmarMinCp; double levmarInfo[9]; /* output */ } tenDwiGagePvlData; typedef struct { /* input ------------- */ int verbose; double convStep, minNorm, convEps, wghtSumEps; int enableRecurse; unsigned int maxIter, numSteps; int lengthFancy; /* internal ------------ */ unsigned int allocLen; double *eval, *evec, *rtIn, *rtLog, *qIn, *qBuff, *qInter; /* output ------------ */ unsigned int numIter; double convFinal; double lengthShape, lengthOrient; } tenInterpParm; /* the idea is that there should be a uniform way of describing DWI experiments, and as a result of calling one of the Set() functions below, the information is set in tenExperSpec so that combined with a diffusion model spec, DWIs can be simulated */ typedef struct { int set; /* has been set */ unsigned imgNum; /* total number of images, dwi or not */ double *bval, /* all b values: imgNum doubles. NOTE: if dwi[i] is to be considered a "b0" value (for the various functions that have a knownB0 or similarly named flag), bval[i] must be 0.0; regardless of the gradient length */ /* *wght, all weights, but not yet used */ *grad; /* all gradients: 3 x imgNum doubles */ } tenExperSpec; /* description of *one* parameter in the parameter vector that defines one instance of a given model */ typedef struct { char name[AIR_STRLEN_SMALL]; /* name */ double min, max; /* bounds */ int cyclic; /* non-zero if effectively min == max */ int vec3; /* non-zero if this is a coefficient of a UNIT-LENGTH 3-vector */ unsigned int vecIdx; /* *if* this param is part of vector, the index into it, i.e. 0, 1, or 2 */ } tenModelParmDesc; #define TEN_MODEL_B0_MAX 65500 /* HEY: fairly arbitrary, but is set to be a little below the max storable value of the unsigned short in which DWI values might be saved */ #define TEN_MODEL_DIFF_MAX 0.006 /* in units of mm^2/sec; diffusivity of water is about 0.003 mm^2/sec */ #define TEN_MODEL_PARM_GRAD_EPS 0.000005 /* for gradient calculations */ /* ******** struct tenModel ** ** Container for *information* about how DWI values arise (i.e. model ** parameters), with functions that help in fitting and evaluating ** models. A tenModel does not contain any vector of model parameter ** values, it is only a description of those values (especially its ** tenModelParmDesc vector), and utilities for computing with those ** parameter values. ** ** NOTE: the current convention is to *always* have the non-DW T2 ** image value ("B0") be the first value in the parameter vector that ** a tenModel is used to do describe. The B0 value will be found either ** by trivial means (i.e copied from the image data), or by a different ** method than the rest of the model parameters, but it is stored along with ** the parameters because ** (1) its very handy to have in one place all the information that, when ** combined with the tenExperSpec, gives you a DWI value, and ** (2) sometimes B0 does have to be estimated at the same time as the rest ** of the model, so we thereby avoid doubling the number of models ** to be able to support this. ** On the other hand, the B0 value may not be stored along with the rest of ** the parm vec in the case of saving out whole nrrd of parm vecs. ** The "parmNum" field below therefore always includes the B0 value */ typedef struct tenModel_t { char name[AIR_STRLEN_SMALL]; unsigned int parmNum; const tenModelParmDesc *parmDesc; /* noise free simulation */ void (*simulate)(double *dwiSim, const double *parm, const tenExperSpec *espec); /* parameter vector operations */ char *(*sprint)(char str[AIR_STRLEN_MED], const double *parm); double *(*alloc)(void); void (*rand)(double *parm, airRandMTState *rng, int knownB0); void (*step)(double *parm1, const double scl, const double *grad, const double *parm0); double (*dist)(const double *parmA, const double *parmB); void (*copy)(double *parmA, const double *parmB); int (*convert)(double *parmDst, const double *parmSrc, const struct tenModel_t *modelSrc); /* "sqe" == squared error in DWI values */ double (*sqe)(const double *parm, const tenExperSpec *espec, double *dwiBuff, const double *dwiMeas, int knownB0); void (*sqeGrad)(double *grad, const double *parm, const tenExperSpec *espec, double *dwiBuff, const double *dwiMeas, int knownB0); double (*sqeFit)(double *parm, double *convFrac, unsigned int *itersTaken, const tenExperSpec *espec, double *dwiBuff, const double *dwiMeas, const double *parmInit, int knownB0, unsigned int minIter, unsigned int maxIter, double convEps, int verbose); /* "nll" == negative log likelihood */ double (*nll)(const double *parm, const tenExperSpec *espec, double *dwiBuff, const double *dwiMeas, int rician, double sigma, int knownB0); void (*nllGrad)(double *grad, const double *parm, const tenExperSpec *espec, double *dwiBuff, const double *dwiMeas, int rician, double sigma); double (*nllFit)(double *parm, const tenExperSpec *espec, const double *dwiMeas, const double *parmInit, int rician, double sigma, int knownB0); } tenModel; /* defaultsTen.c */ TEN_EXPORT const int tenPresent; TEN_EXPORT const char *tenBiffKey; TEN_EXPORT const char tenDefFiberKernel[]; TEN_EXPORT double tenDefFiberStepSize; TEN_EXPORT int tenDefFiberUseIndexSpace; TEN_EXPORT int tenDefFiberMaxNumSteps; TEN_EXPORT double tenDefFiberMaxHalfLen; TEN_EXPORT int tenDefFiberAnisoStopType; TEN_EXPORT double tenDefFiberAnisoThresh; TEN_EXPORT int tenDefFiberIntg; TEN_EXPORT double tenDefFiberWPunct; /* triple.c */ TEN_EXPORT void tenTripleConvertSingle_d(double dst[3], int dstType, const double src[3], const int srcType); TEN_EXPORT void tenTripleConvertSingle_f(float dst[3], int dstType, const float src[3], const int srcType); TEN_EXPORT void tenTripleCalcSingle_d(double dst[3], int ttype, double ten[7]); TEN_EXPORT void tenTripleCalcSingle_f(float dst[3], int ttype, float ten[7]); TEN_EXPORT int tenTripleCalc(Nrrd *nout, int ttype, const Nrrd *nten); TEN_EXPORT int tenTripleConvert(Nrrd *nout, int dstType, const Nrrd *nin, int srcType); /* grads.c */ TEN_EXPORT tenGradientParm *tenGradientParmNew(void); TEN_EXPORT tenGradientParm *tenGradientParmNix(tenGradientParm *tgparm); TEN_EXPORT int tenGradientCheck(const Nrrd *ngrad, int type, unsigned int minnum); TEN_EXPORT int tenGradientRandom(Nrrd *ngrad, unsigned int num, unsigned int seed); TEN_EXPORT double tenGradientIdealEdge(unsigned int N, int single); TEN_EXPORT int tenGradientJitter(Nrrd *nout, const Nrrd *nin, double dist); TEN_EXPORT int tenGradientBalance(Nrrd *nout, const Nrrd *nin, tenGradientParm *tgparm); TEN_EXPORT void tenGradientMeasure(double *pot, double *minAngle, double *minEdge, const Nrrd *npos, tenGradientParm *tgparm, int edgeNormalize); TEN_EXPORT int tenGradientDistribute(Nrrd *nout, const Nrrd *nin, tenGradientParm *tgparm); TEN_EXPORT int tenGradientGenerate(Nrrd *nout, unsigned int num, tenGradientParm *tgparm); /* enumsTen.c */ TEN_EXPORT const airEnum *const tenAniso; TEN_EXPORT const airEnum *const tenInterpType; TEN_EXPORT const airEnum _tenGage; TEN_EXPORT const airEnum *const tenGage; TEN_EXPORT const airEnum *const tenFiberType; TEN_EXPORT const airEnum *const tenDwiFiberType; TEN_EXPORT const airEnum *const tenFiberStop; TEN_EXPORT const airEnum *const tenFiberIntg; TEN_EXPORT const airEnum *const tenGlyphType; TEN_EXPORT const airEnum *const tenEstimate1Method; TEN_EXPORT const airEnum *const tenEstimate2Method; TEN_EXPORT const airEnum *const tenTripleType; /* path.c */ TEN_EXPORT tenInterpParm *tenInterpParmNew(void); TEN_EXPORT tenInterpParm *tenInterpParmCopy(tenInterpParm *tip); TEN_EXPORT int tenInterpParmBufferAlloc(tenInterpParm *tip, unsigned int num); TEN_EXPORT tenInterpParm *tenInterpParmNix(tenInterpParm *tip); TEN_EXPORT void tenInterpTwo_d(double oten[7], const double tenA[7], const double tenB[7], int ptype, double aa, tenInterpParm *tip); TEN_EXPORT int tenInterpN_d(double tenOut[7], const double *tenIn, const double *wght, unsigned int num, int ptype, tenInterpParm *tip); TEN_EXPORT double tenInterpPathLength(Nrrd *npath, int doubleVerts, int fancy, int shape); TEN_EXPORT int tenInterpTwoDiscrete_d(Nrrd *nout, const double tenA[7], const double tenB[7], int ptype, unsigned int num, tenInterpParm *tip); TEN_EXPORT double tenInterpDistanceTwo_d(const double tenA[7], const double tenB[7], int ptype, tenInterpParm *tip); TEN_EXPORT int tenInterpMulti3D(Nrrd *nout, const Nrrd *const *nin, const double *wght, unsigned int ninNum, int ptype, tenInterpParm *tip); /* glyph.c */ TEN_EXPORT tenGlyphParm *tenGlyphParmNew(void); TEN_EXPORT tenGlyphParm *tenGlyphParmNix(tenGlyphParm *parm); TEN_EXPORT int tenGlyphParmCheck(tenGlyphParm *parm, const Nrrd *nten, const Nrrd *npos, const Nrrd *nslc); TEN_EXPORT int tenGlyphGen(limnObject *glyphs, echoScene *scene, tenGlyphParm *parm, const Nrrd *nten, const Nrrd *npos, const Nrrd *nslc); TEN_EXPORT unsigned int tenGlyphBqdZoneEval(const double eval[3]); TEN_EXPORT void tenGlyphBqdUvEval(double uv[2], const double eval[3]); TEN_EXPORT void tenGlyphBqdEvalUv(double eval[3], const double uv[2]); TEN_EXPORT unsigned int tenGlyphBqdZoneUv(const double uv[2]); TEN_EXPORT void tenGlyphBqdAbcUv(double abc[3], const double uv[2], double betaMax); /* tensor.c */ TEN_EXPORT int tenVerbose; TEN_EXPORT void tenRotateSingle_f(float tenOut[7], const float rot[9], const float tenIn[7]); TEN_EXPORT int tenTensorCheck(const Nrrd *nin, int wantType, int want4D, int useBiff); TEN_EXPORT int tenMeasurementFrameReduce(Nrrd *nout, const Nrrd *nin); TEN_EXPORT int tenExpand2D(Nrrd *nout, const Nrrd *nin, double scale, double thresh); TEN_EXPORT int tenExpand(Nrrd *tnine, const Nrrd *tseven, double scale, double thresh); TEN_EXPORT int tenShrink(Nrrd *tseven, const Nrrd *nconf, const Nrrd *tnine); TEN_EXPORT int tenEigensolve_f(float eval[3], float evec[9], const float ten[7]); TEN_EXPORT int tenEigensolve_d(double eval[3], double evec[9], const double ten[7]); TEN_EXPORT void tenMakeSingle_f(float ten[7], float conf, const float eval[3], const float evec[9]); TEN_EXPORT void tenMakeSingle_d(double ten[7], double conf, const double eval[3], const double evec[9]); TEN_EXPORT int tenMake(Nrrd *nout, const Nrrd *nconf, const Nrrd *neval, const Nrrd *nevec); TEN_EXPORT int tenSlice(Nrrd *nout, const Nrrd *nten, unsigned int axis, size_t pos, unsigned int dim); TEN_EXPORT void tenInvariantGradientsK_d(double K1[7], double K2[7], double K3[7], const double ten[7], const double minnorm); TEN_EXPORT void tenInvariantGradientsR_d(double R1[7], double R2[7], double R3[7], const double ten[7], const double minnorm); TEN_EXPORT void tenRotationTangents_d(double phi1[7], double phi2[7], double phi3[7], const double evec[9]); TEN_EXPORT void tenLogSingle_d(double logten[7], const double ten[7]); TEN_EXPORT void tenLogSingle_f(float logten[7], const float ten[7]); TEN_EXPORT void tenExpSingle_d(double expten[7], const double ten[7]); TEN_EXPORT void tenExpSingle_f(float expten[7], const float ten[7]); TEN_EXPORT void tenSqrtSingle_d(double sqrtten[7], const double ten[7]); TEN_EXPORT void tenSqrtSingle_f(float sqrtten[7], const float ten[7]); TEN_EXPORT void tenPowSingle_d(double powten[7], const double ten[7], double power); TEN_EXPORT void tenPowSingle_f(float powten[7], const float ten[7], float power); /* should rename to tenInvSingle_x */ TEN_EXPORT void tenInv_f(float inv[7], const float ten[7]); TEN_EXPORT void tenInv_d(double inv[7], const double ten[7]); TEN_EXPORT double tenDoubleContract_d(double a[7], double T[21], double b[7]); /* chan.c */ /* old tenCalc* functions superceded/deprecated by new tenEstimate* code */ TEN_EXPORT const char *tenDWMRIModalityKey; TEN_EXPORT const char *tenDWMRIModalityVal; TEN_EXPORT const char *tenDWMRINAVal; TEN_EXPORT const char *tenDWMRIBValueKey; TEN_EXPORT const char *tenDWMRIGradKeyFmt; TEN_EXPORT const char *tenDWMRIBmatKeyFmt; TEN_EXPORT const char *tenDWMRINexKeyFmt; TEN_EXPORT const char *tenDWMRISkipKeyFmt; TEN_EXPORT int tenDWMRIKeyValueParse(Nrrd **ngradP, Nrrd **nbmatP, double *bP, unsigned int **skip, unsigned int *skipNum, const Nrrd *ndwi); TEN_EXPORT int tenBMatrixCalc(Nrrd *nbmat, const Nrrd *ngrad); TEN_EXPORT int tenEMatrixCalc(Nrrd *nemat, const Nrrd *nbmat, int knownB0); TEN_EXPORT void tenEstimateLinearSingle_f(float *ten, float *B0P, const float *dwi, const double *emat, double *vbuf, unsigned int DD, int knownB0, float thresh, float soft, float b); TEN_EXPORT void tenEstimateLinearSingle_d(double *ten, double *B0P, const double *dwi, const double*emat, double *vbuf, unsigned int DD, int knownB0, double thresh, double soft, double b); TEN_EXPORT int tenEstimateLinear3D(Nrrd *nten, Nrrd **nterrP, Nrrd **nB0P, const Nrrd *const *ndwi, unsigned int dwiLen, const Nrrd *nbmat, int knownB0, double thresh, double soft, double b); TEN_EXPORT int tenEstimateLinear4D(Nrrd *nten, Nrrd **nterrP, Nrrd **nB0P, const Nrrd *ndwi, const Nrrd *_nbmat, int knownB0, double thresh, double soft, double b); TEN_EXPORT void tenSimulateSingle_f(float *dwi, float B0, const float *ten, const double *bmat, unsigned int DD, float b); TEN_EXPORT int tenSimulate(Nrrd *ndwi, const Nrrd *nT2, const Nrrd *nten, const Nrrd *nbmat, double b); /* estimate.c */ TEN_EXPORT tenEstimateContext *tenEstimateContextNew(void); TEN_EXPORT void tenEstimateVerboseSet(tenEstimateContext *tec, int verbose); TEN_EXPORT void tenEstimateNegEvalShiftSet(tenEstimateContext *tec, int doit); TEN_EXPORT int tenEstimateMethodSet(tenEstimateContext *tec, int estMethod); TEN_EXPORT int tenEstimateSigmaSet(tenEstimateContext *tec, double sigma); TEN_EXPORT int tenEstimateValueMinSet(tenEstimateContext *tec, double valueMin); TEN_EXPORT int tenEstimateGradientsSet(tenEstimateContext *tec, const Nrrd *ngrad, double bValue, int estimateB0); TEN_EXPORT int tenEstimateBMatricesSet(tenEstimateContext *tec, const Nrrd *nbmat, double bValue, int estimateB0); TEN_EXPORT int tenEstimateSkipSet(tenEstimateContext *tec, unsigned int valIdx, int doSkip); TEN_EXPORT int tenEstimateSkipReset(tenEstimateContext *tec); TEN_EXPORT int tenEstimateThresholdSet(tenEstimateContext *tec, double thresh, double soft); TEN_EXPORT int tenEstimateUpdate(tenEstimateContext *tec); TEN_EXPORT int tenEstimate1TensorSimulateSingle_f(tenEstimateContext *tec, float *simval, float sigma, float bValue, float B0, const float _ten[7]); TEN_EXPORT int tenEstimate1TensorSimulateSingle_d(tenEstimateContext *tec, double *simval, double sigma, double bValue, double B0, const double ten[7]); TEN_EXPORT int tenEstimate1TensorSimulateVolume(tenEstimateContext *tec, Nrrd *ndwi, double sigma, double bValue, const Nrrd *nB0, const Nrrd *nten, int outType, int keyValueSet); TEN_EXPORT int tenEstimate1TensorSingle_f(tenEstimateContext *tec, float ten[7], const float *all); TEN_EXPORT int tenEstimate1TensorSingle_d(tenEstimateContext *tec, double ten[7], const double *all); TEN_EXPORT int tenEstimate1TensorVolume4D(tenEstimateContext *tec, Nrrd *nten, Nrrd **nB0P, Nrrd **nterrP, const Nrrd *ndwi, int outType); TEN_EXPORT tenEstimateContext *tenEstimateContextNix(tenEstimateContext *tec); /* aniso.c */ TEN_EXPORT float (*_tenAnisoEval_f[TEN_ANISO_MAX+1])(const float eval[3]); TEN_EXPORT float tenAnisoEval_f(const float eval[3], int aniso); TEN_EXPORT double (*_tenAnisoEval_d[TEN_ANISO_MAX+1])(const double eval[3]); TEN_EXPORT double tenAnisoEval_d(const double eval[3], int aniso); TEN_EXPORT float (*_tenAnisoTen_f[TEN_ANISO_MAX+1])(const float ten[7]); TEN_EXPORT float tenAnisoTen_f(const float ten[7], int aniso); TEN_EXPORT double (*_tenAnisoTen_d[TEN_ANISO_MAX+1])(const double ten[7]); TEN_EXPORT double tenAnisoTen_d(const double ten[7], int aniso); TEN_EXPORT int tenAnisoPlot(Nrrd *nout, int aniso, unsigned int res, int hflip, int whole, int nanout); TEN_EXPORT int tenAnisoVolume(Nrrd *nout, const Nrrd *nin, int aniso, double confThresh); TEN_EXPORT int tenAnisoHistogram(Nrrd *nout, const Nrrd *nin, const Nrrd *nwght, int right, int version, unsigned int resolution); TEN_EXPORT tenEvecRGBParm *tenEvecRGBParmNew(void); TEN_EXPORT tenEvecRGBParm *tenEvecRGBParmNix(tenEvecRGBParm *rgbp); TEN_EXPORT int tenEvecRGBParmCheck(const tenEvecRGBParm *rgbp); TEN_EXPORT void tenEvecRGBSingle_f(float RGB[3], float conf, const float eval[3], const float evec[3], const tenEvecRGBParm *rgbp); TEN_EXPORT void tenEvecRGBSingle_d(double RGB[3], double conf, const double eval[3], const double evec[3], const tenEvecRGBParm *rgbp); /* miscTen.c */ TEN_EXPORT int tenEvecRGB(Nrrd *nout, const Nrrd *nin, const tenEvecRGBParm *rgbp); TEN_EXPORT short tenEvqSingle_f(float vec[3], float scl); TEN_EXPORT int tenEvqVolume(Nrrd *nout, const Nrrd *nin, int which, int aniso, int scaleByAniso); TEN_EXPORT int tenBMatrixCheck(const Nrrd *nbmat, int type, unsigned int minnum); TEN_EXPORT int _tenFindValley(double *valP, const Nrrd *nhist, double tweak, int save); /* fiberMethods.c */ TEN_EXPORT void tenFiberSingleInit(tenFiberSingle *tfbs); TEN_EXPORT void tenFiberSingleDone(tenFiberSingle *tfbs); TEN_EXPORT tenFiberSingle *tenFiberSingleNew(void); TEN_EXPORT tenFiberSingle *tenFiberSingleNix(tenFiberSingle *tfbs); TEN_EXPORT tenFiberContext *tenFiberContextNew(const Nrrd *dtvol); TEN_EXPORT tenFiberContext *tenFiberContextDwiNew(const Nrrd *dwivol, double thresh, double soft, double valueMin, int ten1method, int ten2method); TEN_EXPORT void tenFiberVerboseSet(tenFiberContext *tfx, int verbose); TEN_EXPORT int tenFiberTypeSet(tenFiberContext *tfx, int type); TEN_EXPORT int tenFiberKernelSet(tenFiberContext *tfx, const NrrdKernel *kern, const double parm[NRRD_KERNEL_PARMS_NUM]); TEN_EXPORT int tenFiberProbeItemSet(tenFiberContext *tfx, int item); TEN_EXPORT int tenFiberIntgSet(tenFiberContext *tfx, int intg); TEN_EXPORT int tenFiberStopSet(tenFiberContext *tfx, int stop, ...); TEN_EXPORT int tenFiberStopAnisoSet(tenFiberContext *tfx, int anisoType, double anisoThresh); TEN_EXPORT int tenFiberStopDoubleSet(tenFiberContext *tfx, int stop, double val); TEN_EXPORT int tenFiberStopUIntSet(tenFiberContext *tfx, int stop, unsigned int val); TEN_EXPORT void tenFiberStopOn(tenFiberContext *tfx, int stop); TEN_EXPORT void tenFiberStopOff(tenFiberContext *tfx, int stop); TEN_EXPORT void tenFiberStopReset(tenFiberContext *tfx); TEN_EXPORT int tenFiberAnisoSpeedSet(tenFiberContext *tfx, int aniso, double lerp, double thresh, double soft); TEN_EXPORT int tenFiberAnisoSpeedReset(tenFiberContext *tfx); TEN_EXPORT int tenFiberParmSet(tenFiberContext *tfx, int parm, double val); TEN_EXPORT int tenFiberUpdate(tenFiberContext *tfx); TEN_EXPORT tenFiberContext *tenFiberContextCopy(tenFiberContext *tfx); TEN_EXPORT tenFiberContext *tenFiberContextNix(tenFiberContext *tfx); /* fiber.c */ TEN_EXPORT int tenFiberTraceSet(tenFiberContext *tfx, Nrrd *nfiber, double *buff, unsigned int halfBuffLen, unsigned int *startIdxP, unsigned int *endIdxP, double seed[3]); TEN_EXPORT int tenFiberTrace(tenFiberContext *tfx, Nrrd *nfiber, double seed[3]); TEN_EXPORT unsigned int tenFiberDirectionNumber(tenFiberContext *tfx, double seed[3]); TEN_EXPORT int tenFiberSingleTrace(tenFiberContext *tfx, tenFiberSingle *tfbs, double seed[3], unsigned int which); TEN_EXPORT tenFiberMulti *tenFiberMultiNew(void); TEN_EXPORT tenFiberMulti *tenFiberMultiNix(tenFiberMulti *tfm); TEN_EXPORT int tenFiberMultiTrace(tenFiberContext *tfx, tenFiberMulti *tfml, const Nrrd *nseed); TEN_EXPORT int tenFiberMultiPolyData(tenFiberContext *tfx, limnPolyData *lpld, tenFiberMulti *tfml); TEN_EXPORT int tenFiberMultiProbeVals(tenFiberContext *tfx, Nrrd *nval, tenFiberMulti *tfml); /* epireg.c */ TEN_EXPORT int _tenEpiRegThresholdFind(double *DWthrP, Nrrd **nin, int ninLen, int save, double expo); TEN_EXPORT int tenEpiRegister3D(Nrrd **nout, Nrrd **ndwi, unsigned int dwiLen, Nrrd *ngrad, int reference, double bwX, double bwY, double fitFrac, double DWthr, int doCC, const NrrdKernel *kern, double *kparm, int progress, int verbose); TEN_EXPORT int tenEpiRegister4D(Nrrd *nout, Nrrd *nin, Nrrd *ngrad, int reference, double bwX, double bwY, double fitFrac, double DWthr, int doCC, const NrrdKernel *kern, double *kparm, int progress, int verbose); /* experSpec.c */ TEN_EXPORT tenExperSpec* tenExperSpecNew(void); TEN_EXPORT int tenExperSpecGradSingleBValSet(tenExperSpec *espec, int insertB0, double bval, const double *grad, unsigned int gradNum); TEN_EXPORT int tenExperSpecGradBValSet(tenExperSpec *espec, int insertB0, const double *bval, const double *grad, unsigned int bgNum); /* TEN_EXPORT int tenExperSpecGradBValWghtSet(tenExperSpec *espec, int insertB0, const double *bval, const double *grad, const double *wght, unsigned int bgwNum); */ TEN_EXPORT int tenExperSpecFromKeyValueSet(tenExperSpec *espec, const Nrrd *ndwi); TEN_EXPORT tenExperSpec* tenExperSpecNix(tenExperSpec *espec); TEN_EXPORT double tenExperSpecKnownB0Get(const tenExperSpec *espec, const double *dwi); TEN_EXPORT double tenExperSpecMaxBGet(const tenExperSpec *espec); TEN_EXPORT int tenDWMRIKeyValueFromExperSpecSet(Nrrd *ndwi, const tenExperSpec *espec); /* tenModel.c */ TEN_EXPORT const char *tenModelPrefixStr; TEN_EXPORT int tenModelParse(const tenModel **model, int *plusB0, int requirePrefix, const char *str); TEN_EXPORT int tenModelFromAxisLearnPossible(const NrrdAxisInfo *axinfo); TEN_EXPORT int tenModelFromAxisLearn(const tenModel **model, int *plusB0, const NrrdAxisInfo *axinfo); TEN_EXPORT int tenModelSimulate(Nrrd *ndwi, int typeOut, tenExperSpec *espec, const tenModel *model, const Nrrd *nB0, /* maybe NULL */ const Nrrd *nparm, int keyValueSet); TEN_EXPORT int tenModelSqeFit(Nrrd *nparm, Nrrd **nsqeP, Nrrd **nconvP, Nrrd **niterP, const tenModel *model, const tenExperSpec *espec, const Nrrd *ndwi, int knownB0, int saveB0, int typeOut, unsigned int minIter, unsigned int maxIter, unsigned int starts, double convEps, airRandMTState *rng, int verbose); TEN_EXPORT int tenModelNllFit(Nrrd *nparm, Nrrd **nnllP, const tenModel *model, const tenExperSpec *espec, const Nrrd *ndwi, int rician, double sigma, int knownB0); TEN_EXPORT int tenModelConvert(Nrrd *nparmDst, int *convRet, const tenModel *modelDst, const Nrrd *nparmSrc, const tenModel *modelSrc); /* ** Have to keep this list of model declarations in sync with: ** * tenModel.c/str2model() ** * all model*.c/parmConvert() (fallen behind here) ** * sources.cmake, GNUmakefile ** ** Note that the TEN_MODEL_STR_* strings should be all lowercase, ** as long as we want the model parsing to be case insensitive, and ** as long as the logic in tenModel.c/str2model() is so simplistic */ /* modelZero.c */ #define TEN_MODEL_STR_ZERO "zero" TEN_EXPORT const tenModel *const tenModelZero; /* modelB0.c */ #define TEN_MODEL_STR_B0 "b0" TEN_EXPORT const tenModel *const tenModelB0; /* modelBall.c */ #define TEN_MODEL_STR_BALL "ball" TEN_EXPORT const tenModel *const tenModelBall; /* model1Vector2D.c */ #define TEN_MODEL_STR_1VECTOR2D "1vector2d" TEN_EXPORT const tenModel *const tenModel1Vector2D; /* model1Unit2D.c */ #define TEN_MODEL_STR_1UNIT2D "1unit2d" TEN_EXPORT const tenModel *const tenModel1Unit2D; /* model2Unit2D.c */ #define TEN_MODEL_STR_2UNIT2D "2unit2d" TEN_EXPORT const tenModel *const tenModel2Unit2D; /* model1Stick.c */ #define TEN_MODEL_STR_1STICK "1stick" TEN_EXPORT const tenModel *const tenModel1Stick; /* modelBall1StickEMD.c */ #define TEN_MODEL_STR_BALL1STICKEMD "ball1stickemd" TEN_EXPORT const tenModel *const tenModelBall1StickEMD; /* modelBall1Stick.c */ #define TEN_MODEL_STR_BALL1STICK "ball1stick" TEN_EXPORT const tenModel *const tenModelBall1Stick; /* modelBall1Cylinder.c */ #define TEN_MODEL_STR_BALL1CYLINDER "ball1cylinder" TEN_EXPORT const tenModel *const tenModelBall1Cylinder; /* model1Cylinder.c */ #define TEN_MODEL_STR_1CYLINDER "1cylinder" TEN_EXPORT const tenModel *const tenModel1Cylinder; /* model1Tensor2.c: 2nd-order tensor (one of them), not two-tensor */ #define TEN_MODEL_STR_1TENSOR2 "1tensor2" TEN_EXPORT const tenModel *const tenModel1Tensor2; /* mod.c */ TEN_EXPORT int tenSizeNormalize(Nrrd *nout, const Nrrd *nin, double weight[3], double amount, double target); TEN_EXPORT int tenSizeScale(Nrrd *nout, const Nrrd *nin, double amount); TEN_EXPORT int tenAnisoScale(Nrrd *nout, const Nrrd *nin, double scale, int fixDet, int makePositive); TEN_EXPORT int tenEigenvaluePower(Nrrd *nout, const Nrrd *nin, double expo); TEN_EXPORT int tenEigenvalueClamp(Nrrd *nout, const Nrrd *nin, double min, double max); TEN_EXPORT int tenEigenvalueAdd(Nrrd *nout, const Nrrd *nin, double val); TEN_EXPORT int tenEigenvalueMultiply(Nrrd *nout, const Nrrd *nin, double val); TEN_EXPORT int tenLog(Nrrd *nout, const Nrrd *nin); TEN_EXPORT int tenExp(Nrrd *nout, const Nrrd *nin); /* bvec.c */ TEN_EXPORT int tenBVecNonLinearFit(Nrrd *nout, const Nrrd *nin, double *bb, double *ww, int iterMax, double eps); /* tenGage.c */ TEN_EXPORT gageKind *tenGageKind; /* tenDwiGage.c */ /* we can't declare or define a tenDwiGageKind->name (analogous to tenGageKind->name or gageSclKind->name) because the DWI kind is dynamically allocated, but at least we can declare a cannonical name (HEY: ugly) */ #define TEN_DWI_GAGE_KIND_NAME "dwi" TEN_EXPORT const airEnum _tenDwiGage; TEN_EXPORT const airEnum *const tenDwiGage; TEN_EXPORT gageKind *tenDwiGageKindNew(void); TEN_EXPORT gageKind *tenDwiGageKindNix(gageKind *dwiKind); /* warning: this function will likely change its arguments in the future */ TEN_EXPORT int tenDwiGageKindSet(gageKind *dwiKind, double thresh, double soft, double bval, double valueMin, const Nrrd *ngrad, const Nrrd *nbmat, int emethod1, int emethod2, unsigned int randSeed); TEN_EXPORT int tenDwiGageKindCheck(const gageKind *kind); /* bimod.c */ TEN_EXPORT tenEMBimodalParm* tenEMBimodalParmNew(void); TEN_EXPORT tenEMBimodalParm* tenEMBimodalParmNix(tenEMBimodalParm *biparm); TEN_EXPORT int tenEMBimodal(tenEMBimodalParm *biparm, const Nrrd *nhisto); /* tend{Flotsam,Anplot,Anvol,Evec,Eval,. . .}.c */ #define TEND_DECLARE(C) TEN_EXPORT unrrduCmd tend_##C##Cmd; #define TEND_LIST(C) &tend_##C##Cmd, /* removed from below (superseded by estim): F(calc) \ */ #define TEND_MAP(F) \ F(about) \ F(grads) \ F(epireg) \ F(bmat) \ F(estim) \ F(sim) \ F(mfit) \ F(mconv) \ F(msim) \ F(make) \ F(avg) \ F(helix) \ F(sten) \ F(glyph) \ F(ellipse) \ F(anplot) \ F(anvol) \ F(anscale) \ F(anhist) \ F(triple) \ F(tconv) \ F(point) \ F(slice) \ F(norm) \ F(fiber) \ F(eval) \ F(evalpow) \ F(evalclamp) \ F(evaladd) \ F(evalmult) \ F(log) \ F(exp) \ F(evec) \ F(evecrgb) \ F(evq) \ F(unmf) \ F(expand) \ F(shrink) \ F(bfit) \ F(satin) TEND_MAP(TEND_DECLARE) TEN_EXPORT unrrduCmd *tendCmdList[]; TEN_EXPORT hestCB *tendFiberStopCB; TEN_EXPORT const char *tendTitle; #ifdef __cplusplus } #endif #endif /* TEN_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/ten/tendTconv.c0000664000175000017500000000530012165631065017275 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Convert between different shape triples" static const char *_tend_tconvInfoL = (INFO ". The triples can be eignvalues, invariants (J, K, R), " "and lots of other things."); int tend_tconvMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int ttype[2]; Nrrd *nin, *nout; char *outS; hestOptAdd(&hopt, "t", "inType outType", airTypeEnum, 2, 2, ttype, NULL, "given input and desired output type of triples", NULL, tenTripleType); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input array of triples", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output array"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_tconvInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenTripleConvert(nout, ttype[1], nin, ttype[0])) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble converting:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(tconv, INFO); teem-1.11.0~svn6057/src/ten/enumsTen.c0000664000175000017500000012665312165631065017146 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" /* -------------------------------------------------------------- */ const char * _tenAnisoStr[TEN_ANISO_MAX+1] = { "(unknown aniso)", "Conf", "Cl1", "Cp1", "Ca1", "Clpmin1", "Cs1", "Ct1", "Cl2", "Cp2", "Ca2", "Clpmin2", "Cs2", "Ct2", "RA", "FA", "VF", "B", "Q", "R", "S", "Skew", "Mode", "Th", "Omega", "Det", "Tr", "eval0", "eval1", "eval2" }; const airEnum _tenAniso = { "anisotropy metric", TEN_ANISO_MAX, _tenAnisoStr, NULL, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const tenAniso = &_tenAniso; /* --------------------------------------------------------------------- */ const char * _tenInterpTypeStr[TEN_INTERP_TYPE_MAX+1] = { "(unknown interp type)", "lin", "loglin", "affinv", "wang", "geoloxk", "geoloxr", "loxk", "loxr", "qgeoloxk", "qgeoloxr", "rtplin" }; const char * _tenInterpTypeStrEqv[] = { "lin", "linear", "lerp", "loglin", "loglinear", "loglerp", "affinv", "wang", "geoloxk", "glk", "geoloxr", "glr", "loxk", "loxr", "qgeoloxk", "qglk", "qgeoloxr", "qglr", "rtplin", "" }; const int _tenInterpTypeValEqv[] = { tenInterpTypeLinear, tenInterpTypeLinear, tenInterpTypeLinear, tenInterpTypeLogLinear, tenInterpTypeLogLinear, tenInterpTypeLogLinear, tenInterpTypeAffineInvariant, tenInterpTypeWang, tenInterpTypeGeoLoxK, tenInterpTypeGeoLoxK, tenInterpTypeGeoLoxR, tenInterpTypeGeoLoxR, tenInterpTypeLoxK, tenInterpTypeLoxR, tenInterpTypeQuatGeoLoxK, tenInterpTypeQuatGeoLoxK, tenInterpTypeQuatGeoLoxR, tenInterpTypeQuatGeoLoxR, tenInterpTypeRThetaPhiLinear }; const airEnum _tenInterpType = { "interp type", TEN_INTERP_TYPE_MAX, _tenInterpTypeStr, NULL, NULL, _tenInterpTypeStrEqv, _tenInterpTypeValEqv, AIR_FALSE }; const airEnum *const tenInterpType = &_tenInterpType; /* --------------------------------------------------------------------- */ const char * _tenGageStr[] = { "(unknown tenGage)", "tensor", "confidence", "trace", "N", "B", "det", "S", "Q", "FA", "R", "mode", "theta", "modew", "omega", "evals", "eval0", "eval1", "eval2", "evecs", "evec0", "evec1", "evec2", "delnk2", "delnk3", "delnr1", "delnr2", "delnphi1", "delnphi2", "delnphi3", "tensor grad", "tensor grad mag", "tensor grad mag mag", "trace grad vec", "trace grad mag", "trace normal", "norm grad vec", "norm grad mag", "norm normal", "B grad vec", "B grad mag", "B normal", "det grad vec", "det grad mag", "det normal", "S grad vec", "S grad mag", "S normal", "Q grad vec", "Q grad mag", "Q normal", "FA grad vec", "FA grad mag", "FA normal", "R grad vec", "R grad mag", "R normal", "mode grad vec", "mode grad mag", "mode normal", "theta grad vec", "theta grad mag", "theta normal", "omega grad vec", "omega grad mag", "omega normal", "invariant K gradients", "invariant K gradient mags", "invariant R gradients", "invariant R gradient mags", "rotation tangents", "rotation tangent mags", "eigenvalue gradients", "Cl1", "Cp1", "Ca1", "Clpmin1", "Cl2", "Cp2", "Ca2", "Clpmin2", "hessian", "trace hessian", "trace hessian evals", "trace hessian eval 0", "trace hessian eval 1", "trace hessian eval 2", "trace hessian evecs", "trace hessian evec 0", "trace hessian evec 1", "trace hessian evec 2", "trace hessian frob", "B hessian", "det hessian", "S hessian", "Q hessian", "FA hessian", "FA hessian evals", "FA hessian eval 0", "FA hessian eval 1", "FA hessian eval 2", "FA hessian evecs", "FA hessian evec 0", "FA hessian evec 1", "FA hessian evec 2", "FA hessian frob", "FA ridge surface strength", "FA valley surface strength", "FA laplacian", "FA hessian eval mode", "FA ridge line alignment", "FA ridge surface alignment", "FA 2nd DD", "FA geometry tensor", "FA kappa1", "FA kappa2", "FA total curv", "FA shape index", "FA mean curv", "FA gauss curv", "FA curv dir1", "FA curv dir2", "FA flowline curv", "R hessian", "mode hessian", "mode hessian evals", "mode hessian eval 0", "mode hessian eval 1", "mode hessian eval 2", "mode hessian evecs", "mode hessian evec 0", "mode hessian evec 1", "mode hessian evec 2", "mode hessian frob", "omega hessian", "omega hessian evals", "omega hessian eval 0", "omega hessian eval 1", "omega hessian eval 2", "omega hessian evecs", "omega hessian evec 0", "omega hessian evec 1", "omega hessian evec 2", "omega laplacian", "omega 2nd DD", "omega hessian contracted with ten evec 0", "omega hessian contracted with ten evec 1", "omega hessian contracted with ten evec 2", "trace gradvec dot evec0", "diffusionAlign(trace)", "diffusionFraction(trace)", "FA gradvec dot evec0", "diffusionAlign(FA)", "diffusionFraction(FA)", "omega gradvec dot evec0", "diffusionAlign(Omega)", "diffusionFraction(Omega)", "conf gradvec dot evec0", "diffusionAlign(conf)", "diffusionFraction(conf)", "cov", "covr", "covk", "logeuclid", "qglk", "qglr", "rtpl", "cl1gv", "cl1gm", "cl1gn", "cp1gv", "cp1gm", "cp1gn", "ca1gv", "ca1gm", "ca1gn", "tgrote", "eval hessian", "cl1 hessian", "cl1 hessian evals", "cl1 hessian eval 0", "cl1 hessian eval 1", "cl1 hessian eval 2", "cl1 hessian evecs", "cl1 hessian evec 0", "cl1 hessian evec 1", "cl1 hessian evec 2", "cp1 hessian", "cp1 hessian evals", "cp1 hessian eval 0", "cp1 hessian eval 1", "cp1 hessian eval 2", "cp1 hessian evecs", "cp1 hessian evec 0", "cp1 hessian evec 1", "cp1 hessian evec 2", "ca1 hessian", "ca1 hessian evals", "ca1 hessian eval 0", "ca1 hessian eval 1", "ca1 hessian eval 2", "ca1 hessian evecs", "ca1 hessian evec 0", "ca1 hessian evec 1", "ca1 hessian evec 2", "fiber curving index", "fiber dispersion index", "anisotropies" }; const char * _tenGageDesc[] = { "(unknown tenGage item)", "tensor", "confidence", "trace", "norm", "B", "determinant", "S", "Q", "FA", "R", "mode", "theta", "warped mode", "omega", "3 eigenvalues", "eigenvalue 0", "eigenvalue 1", "eigenvalue 2", "3 eigenvectors", "eigenvector 0", "eigenvector 1", "eigenvector 2", "delnk2", /* sorry */ "delnk3", /* sorry */ "delnr1", /* sorry */ "delnr2", /* sorry */ "delnphi1", /* sorry */ "delnphi2", /* sorry */ "delnphi3", /* sorry */ "tensor gradients", "tensor gradients magnitudes", "tensor gradients magnitude magnitudes", "trace grad vec", "trace grad mag", "trace normal", "norm grad vec", "norm grad mag", "norm normal", "B grad vec", "B grad mag", "B normal", "determinant grad vec", "determinant grad mag", "determinant normal", "S grad vec", "S grad mag", "S normal", "Q grad vec", "Q grad mag", "Q normal", "FA grad vec", "FA grad mag", "FA normal", "R grad vec", "R grad mag", "R normal", "mode grad vec", "mode grad mag", "mode normal", "theta grad vec", "theta grad mag", "theta normal", "omega grad vec", "omega grad mag", "omega normal", "invariant K gradients", "invariant K gradient mags", "invariant R gradients", "invariant R gradient mags", "rotation tangents", "rotation tangent mags", "eigenvalue gradients", "linear anisotropy (1)", "planar anisotropy (1)", "linear+planar anisotropy (1)", "min(linear,planar) anisotropy (1)", "linear anisotropy (2)", "planar anisotropy (2)", "linear+planar anisotropy (2)", "min(linear,planar) anisotropy (2)", "hessian", "trace hessian", "trace hessian evals", "trace hessian eval 0", "trace hessian eval 1", "trace hessian eval 2", "trace hessian evecs", "trace hessian evec 0", "trace hessian evec 1", "trace hessian evec 2", "trace hessian frob", "B hessian", "det hessian", "S hessian", "Q hessian", "FA hessian", "FA hessian evals", "FA hessian eval 0", "FA hessian eval 1", "FA hessian eval 2", "FA hessian evecs", "FA hessian evec 0", "FA hessian evec 1", "FA hessian evec 2", "FA hessian frob", "FA ridge surface strength", "FA valley surface strength", "FA laplacian", "FA hessian eval mode", "FA ridge line alignment", "FA ridge surface alignment", "FA 2nd DD", "FA geometry tensor", "FA kappa1", "FA kappa2", "FA total curv", "FA shape index", "FA mean curv", "FA gauss curv", "FA curv dir1", "FA curv dir2", "FA flowline curv", "R hessian", "mode hessian", "mode hessian evals", "mode hessian eval 0", "mode hessian eval 1", "mode hessian eval 2", "mode hessian evecs", "mode hessian evec 0", "mode hessian evec 1", "mode hessian evec 2", "mode hessian frob", "omega hessian", "omega hessian evals", "omega hessian eval 0", "omega hessian eval 1", "omega hessian eval 2", "omega hessian evecs", "omega hessian evec 0", "omega hessian evec 1", "omega hessian evec 2", "omega laplacian", "omega 2nd DD", "omega hessian contracted with ten evec 0", "omega hessian contracted with ten evec 1", "omega hessian contracted with ten evec 2", "trace gradvec dot evec0", "diffusion align of trace", "diffusion fraction of trace", "FA gradvec dot evec0", "diffusion align of FA", "diffusion fraction of FA", "omega gradvec dot evec0", "diffusion align of omega", "diffusion fraction of omega", "conf gradvec dot evec0", "diffusion align of conf", "diffusion fraction of conf", "covariance", "covarianceR", "covarianceK", "log-euclidean", "QuatGeoLoxK", "QuatGeoLoxR", "RThetaPhiLinear interp", "gradient vector of cl1", "gradient magnitude of cl1", "normal of cl1", "gradient vector of cp1", "gradient magnitude of cp1", "normal of cp1", "gradient vector of ca1", "gradient magnitude of ca1", "normal of ca1", "all tensor component gradients, starting with confidence gradient, " /* !! CONCAT !! */ "rotated such that eigenvalue derivatives are on the diagonal", "eigenvalue hessians", "cl1 hessian", "cl1 hessian evals", "cl1 hessian eval 0", "cl1 hessian eval 1", "cl1 hessian eval 2", "cl1 hessian evecs", "cl1 hessian evec 0", "cl1 hessian evec 1", "cl1 hessian evec 2", "cp1 hessian", "cp1 hessian evals", "cp1 hessian eval 0", "cp1 hessian eval 1", "cp1 hessian eval 2", "cp1 hessian evecs", "cp1 hessian evec 0", "cp1 hessian evec 1", "cp1 hessian evec 2", "ca1 hessian", "ca1 hessian evals", "ca1 hessian eval 0", "ca1 hessian eval 1", "ca1 hessian eval 2", "ca1 hessian evecs", "ca1 hessian evec 0", "ca1 hessian evec 1", "ca1 hessian evec 2", "fiber curving", "fiber dispersion", "anisotropies" }; const int _tenGageVal[] = { tenGageUnknown, tenGageTensor, /* "t", the reconstructed tensor: GT[7] */ tenGageConfidence, /* "c", first of seven tensor values: GT[1] */ tenGageTrace, /* "tr", trace of tensor: GT[1] */ tenGageNorm, tenGageB, /* "b": GT[1] */ tenGageDet, /* "det", determinant of tensor: GT[1] */ tenGageS, /* "s", square of frobenius norm: GT[1] */ tenGageQ, /* "q", (S - B)/9: GT[1] */ tenGageFA, /* "fa", fractional anisotropy: GT[1] */ tenGageR, /* "r", 9*A*B - 2*A^3 - 27*C: GT[1] */ tenGageMode, /* "mode", sqrt(2)*R/sqrt(Q^3): GT[1] */ tenGageTheta, /* "th", arccos(mode/sqrt(2))/AIR_PI: GT[1] */ tenGageModeWarp, /* */ tenGageOmega, /* */ tenGageEval, /* "eval", all eigenvalues of tensor : GT[3] */ tenGageEval0, /* "eval0", major eigenvalue of tensor : GT[1] */ tenGageEval1, /* "eval1", medium eigenvalue of tensor : GT[1] */ tenGageEval2, /* "eval2", minor eigenvalue of tensor : GT[1] */ tenGageEvec, /* "evec", major eigenvectors of tensor: GT[9] */ tenGageEvec0, /* "evec0", major eigenvectors of tensor: GT[3] */ tenGageEvec1, /* "evec1", medium eigenvectors of tensor: GT[3] */ tenGageEvec2, /* "evec2", minor eigenvectors of tensor: GT[3] */ tenGageDelNormK2, tenGageDelNormK3, tenGageDelNormR1, tenGageDelNormR2, tenGageDelNormPhi1, tenGageDelNormPhi2, tenGageDelNormPhi3, tenGageTensorGrad, /* "tg", all tensor component gradients: GT[21] */ tenGageTensorGradMag, /* "tgm" */ tenGageTensorGradMagMag, /* "tgmm" */ tenGageTraceGradVec, /* "trgv": gradient (vector) of trace: GT[3] */ tenGageTraceGradMag, /* "trgm": gradient magnitude of trace: GT[1] */ tenGageTraceNormal, /* "trn": normal of trace: GT[3] */ tenGageNormGradVec, tenGageNormGradMag, tenGageNormNormal, tenGageBGradVec, /* "bgv", gradient (vector) of B: GT[3] */ tenGageBGradMag, /* "bgm", gradient magnitude of B: GT[1] */ tenGageBNormal, /* "bn", normal of B: GT[3] */ tenGageDetGradVec, /* "detgv", gradient (vector) of Det: GT[3] */ tenGageDetGradMag, /* "detgm", gradient magnitude of Det: GT[1] */ tenGageDetNormal, /* "detn", normal of Det: GT[3] */ tenGageSGradVec, /* "sgv", gradient (vector) of S: GT[3] */ tenGageSGradMag, /* "sgm", gradient magnitude of S: GT[1] */ tenGageSNormal, /* "sn", normal of S: GT[3] */ tenGageQGradVec, /* "qgv", gradient vector of Q: GT[3] */ tenGageQGradMag, /* "qgm", gradient magnitude of Q: GT[1] */ tenGageQNormal, /* "qn", normalized gradient of Q: GT[3] */ tenGageFAGradVec, /* "fagv", gradient vector of FA: GT[3] */ tenGageFAGradMag, /* "fagm", gradient magnitude of FA: GT[1] */ tenGageFANormal, /* "fan", normalized gradient of FA: GT[3] */ tenGageRGradVec, /* "rgv", gradient vector of Q: GT[3] */ tenGageRGradMag, /* "rgm", gradient magnitude of Q: GT[1] */ tenGageRNormal, /* "rn", normalized gradient of Q: GT[3] */ tenGageModeGradVec, /* "mgv", gradient vector of mode: GT[3] */ tenGageModeGradMag, /* "mgm", gradient magnitude of mode: GT[1] */ tenGageModeNormal, /* "mn", normalized gradient of moe: GT[3] */ tenGageThetaGradVec, /* "thgv", gradient vector of theta: GT[3] */ tenGageThetaGradMag, /* "thgm", gradient magnitude of theta: GT[1] */ tenGageThetaNormal, /* "thn", normalized gradient of theta: GT[3] */ tenGageOmegaGradVec, /* */ tenGageOmegaGradMag, /* */ tenGageOmegaNormal, /* */ tenGageInvarKGrads, tenGageInvarKGradMags, tenGageInvarRGrads, tenGageInvarRGradMags, tenGageRotTans, /* "rts" */ tenGageRotTanMags, /* "rtms" */ tenGageEvalGrads, /* "evgs" */ tenGageCl1, tenGageCp1, tenGageCa1, tenGageClpmin1, tenGageCl2, tenGageCp2, tenGageCa2, tenGageClpmin2, tenGageHessian, tenGageTraceHessian, tenGageTraceHessianEval, tenGageTraceHessianEval0, tenGageTraceHessianEval1, tenGageTraceHessianEval2, tenGageTraceHessianEvec, tenGageTraceHessianEvec0, tenGageTraceHessianEvec1, tenGageTraceHessianEvec2, tenGageTraceHessianFrob, tenGageBHessian, tenGageDetHessian, tenGageSHessian, tenGageQHessian, tenGageFAHessian, tenGageFAHessianEval, tenGageFAHessianEval0, tenGageFAHessianEval1, tenGageFAHessianEval2, tenGageFAHessianEvec, tenGageFAHessianEvec0, tenGageFAHessianEvec1, tenGageFAHessianEvec2, tenGageFAHessianFrob, tenGageFARidgeSurfaceStrength, tenGageFAValleySurfaceStrength, tenGageFALaplacian, tenGageFAHessianEvalMode, tenGageFARidgeLineAlignment, tenGageFARidgeSurfaceAlignment, tenGageFA2ndDD, tenGageFAGeomTens, tenGageFAKappa1, tenGageFAKappa2, tenGageFATotalCurv, tenGageFAShapeIndex, tenGageFAMeanCurv, tenGageFAGaussCurv, tenGageFACurvDir1, tenGageFACurvDir2, tenGageFAFlowlineCurv, tenGageRHessian, tenGageModeHessian, tenGageModeHessianEval, tenGageModeHessianEval0, tenGageModeHessianEval1, tenGageModeHessianEval2, tenGageModeHessianEvec, tenGageModeHessianEvec0, tenGageModeHessianEvec1, tenGageModeHessianEvec2, tenGageModeHessianFrob, tenGageOmegaHessian, tenGageOmegaHessianEval, tenGageOmegaHessianEval0, tenGageOmegaHessianEval1, tenGageOmegaHessianEval2, tenGageOmegaHessianEvec, tenGageOmegaHessianEvec0, tenGageOmegaHessianEvec1, tenGageOmegaHessianEvec2, tenGageOmegaLaplacian, tenGageOmega2ndDD, tenGageOmegaHessianContrTenEvec0, tenGageOmegaHessianContrTenEvec1, tenGageOmegaHessianContrTenEvec2, tenGageTraceGradVecDotEvec0, tenGageTraceDiffusionAlign, tenGageTraceDiffusionFraction, tenGageFAGradVecDotEvec0, tenGageFADiffusionAlign, tenGageFADiffusionFraction, tenGageOmegaGradVecDotEvec0, tenGageOmegaDiffusionAlign, tenGageOmegaDiffusionFraction, tenGageConfGradVecDotEvec0, tenGageConfDiffusionAlign, tenGageConfDiffusionFraction, tenGageCovariance, tenGageCovarianceRGRT, tenGageCovarianceKGRT, tenGageTensorLogEuclidean, tenGageTensorQuatGeoLoxK, tenGageTensorQuatGeoLoxR, tenGageTensorRThetaPhiLinear, tenGageCl1GradVec, tenGageCl1GradMag, tenGageCl1Normal, tenGageCp1GradVec, tenGageCp1GradMag, tenGageCp1Normal, tenGageCa1GradVec, tenGageCa1GradMag, tenGageCa1Normal, tenGageTensorGradRotE, tenGageEvalHessian, /* Hessian of the eigenvalues: [27] */ tenGageCl1Hessian, /* Hessian of cl1: [9] */ tenGageCl1HessianEval, /* Hessian eigenvalues of cl1: [3] */ tenGageCl1HessianEval0, /* First Hessian eigenvalue of cl1: [1] */ tenGageCl1HessianEval1, /* Second Hessian eigenvalue of cl1: [1] */ tenGageCl1HessianEval2, /* Third Hessian eigenvalue of cl1: [1] */ tenGageCl1HessianEvec, /* Hessian eigenvectors of cl1: [9] */ tenGageCl1HessianEvec0, /* First Hessian eigenvector of cl1: [3] */ tenGageCl1HessianEvec1, /* Second Hessian eigenvector of cl1: [3] */ tenGageCl1HessianEvec2, /* Third Hessian eigenvector of cl1: [3] */ tenGageCp1Hessian, /* Hessian of cp1: [9] */ tenGageCp1HessianEval, /* Hessian eigenvalues of cp1: [3] */ tenGageCp1HessianEval0, /* First Hessian eigenvalue of cp1: [1] */ tenGageCp1HessianEval1, /* Second Hessian eigenvalue of cp1: [1] */ tenGageCp1HessianEval2, /* Third Hessian eigenvalue of cp1: [1] */ tenGageCp1HessianEvec, /* Hessian eigenvectors of cp1: [9] */ tenGageCp1HessianEvec0, /* First Hessian eigenvector of cp1: [3] */ tenGageCp1HessianEvec1, /* Second Hessian eigenvector of cp1: [3] */ tenGageCp1HessianEvec2, /* Third Hessian eigenvector of cp1: [3] */ tenGageCa1Hessian, /* Hessian of cp1: [9] */ tenGageCa1HessianEval, /* Hessian eigenvalues of cp1: [3] */ tenGageCa1HessianEval0, /* First Hessian eigenvalue of cp1: [1] */ tenGageCa1HessianEval1, /* Second Hessian eigenvalue of cp1: [1] */ tenGageCa1HessianEval2, /* Third Hessian eigenvalue of cp1: [1] */ tenGageCa1HessianEvec, /* Hessian eigenvectors of cp1: [9] */ tenGageCa1HessianEvec0, /* First Hessian eigenvector of cp1: [3] */ tenGageCa1HessianEvec1, /* Second Hessian eigenvector of cp1: [3] */ tenGageCa1HessianEvec2, /* Third Hessian eigenvector of cp1: [3] */ tenGageFiberCurving, tenGageFiberDispersion, tenGageAniso, }; const char * _tenGageStrEqv[] = { "t", "ten", "tensor", "c", "conf", "confidence", "tr", "trace", "n", "norm", "r1", "b", "det", "s", "q", "fa", "r", "mode", "m", "th", "theta", "modew", "mw", "omega", "om", "eval", "evals", "eval0", "eval1", "eval2", "evec", "evecs", "evec0", "evec1", "evec2", "delnk2", "delnk3", "delnr3", "delnr1", "delnr2", "delnphi1", "delnphi2", "delnphi3", "tg", "tensor grad", "tgm", "tensor grad mag", "tgmm", "tensor grad mag mag", "trgv", "tracegv", "trace grad vec", "trgm", "tracegm", "trace grad mag", "trn", "tracen", "trace normal", "ngv", "r1gv", "normgv", "norm grad vec", "ngm", "r1gm", "normgm", "norm grad mag", "nn", "r1n", "normn", "norm normal", "bgv", "b grad vec", "bgm", "b grad mag", "bn", "b normal", "detgv", "det grad vec", "detgm", "det grad mag", "detn", "det normal", "sgv", "s grad vec", "sgm", "s grad mag", "sn", "s normal", "qgv", "q grad vec", "qgm", "q grad mag", "qn", "q normal", "fagv", "fa grad vec", "fagm", "fa grad mag", "fan", "fa normal", "rgv", "r grad vec", "rgm", "r grad mag", "rn", "r normal", "mgv", "mode grad vec", "mgm", "mode grad mag", "mn", "mode normal", "thgv", "th grad vec", "theta grad vec", "thgm", "th grad mag", "theta grad mag", "thn", "th normal", "theta normal", "omgv", "omega grad vec", "omgm", "omega grad mag", "omn", "omega normal", "ikgs", "invariant K gradients", "ikgms", "invariant K gradient mags", "irgs", "invariant R gradients", "irgms", "invariant R gradient mags", "rts", "rotation tangents", "rtms", "rotation tangent mags", "evgs", "eigenvalue gradients", "cl1", "cp1", "ca1", "clpmin1", "cl2", "cp2", "ca2", "clpmin2", "hess", "hessian", "trhess", "trace hessian", "trhesseval", "trace hessian evals", "trhesseval0", "trace hessian eval 0", "trhesseval1", "trace hessian eval 1", "trhesseval2", "trace hessian eval 2", "trhessevec", "trace hessian evecs", "trhessevec0", "trace hessian evec 0", "trhessevec1", "trace hessian evec 1", "trhessevec2", "trace hessian evec 2", "trhessfrob", "trace hessian frob", "bhess", "B hessian", "dethess", "det hessian", "shess", "S hessian", "qhess", "Q hessian", "fahess", "FA hessian", "fahesseval", "FA hessian evals", "fahesseval0", "FA hessian eval 0", "fahesseval1", "FA hessian eval 1", "fahesseval2", "FA hessian eval 2", "fahessevec", "FA hessian evecs", "fahessevec0", "FA hessian evec 0", "fahessevec1", "FA hessian evec 1", "fahessevec2", "FA hessian evec 2", "fahessfrob", "FA hessian frob", "farsurfstrn", "FA ridge surface strength", "favsurfstrn", "FA valley surface strength", "falapl", "FA laplacian", "fahessevalmode", "FA hessian eval mode", "farlinealn", "FA ridge line alignment", "farsurfaln", "FA ridge surface alignment", "fa2d", "fa2dd", "FA 2nd DD", "fagten", "FA geometry tensor", "fak1", "FA kappa1", "fak2", "FA kappa2", "fatc", "FA total curv", "fasi", "FA shape index", "famc", "FA mean curv", "fagc", "FA gauss curv", "facdir1", "FA curv dir1", "facdir2", "FA curv dir2", "fafc", "FA flowline curv", "rhess", "R hessian", "mhess", "mode hessian", "mhesseval", "mode hessian evals", "mhesseval0", "mode hessian eval 0", "mhesseval1", "mode hessian eval 1", "mhesseval2", "mode hessian eval 2", "mhessevec", "mode hessian evecs", "mhessevec0", "mode hessian evec 0", "mhessevec1", "mode hessian evec 1", "mhessevec2", "mode hessian evec 2", "mhessfrob", "mode hessian frob", "omhess", "omega hessian", "omhesseval", "omega hessian evals", "omhesseval0", "omega hessian eval 0", "omhesseval1", "omega hessian eval 1", "omhesseval2", "omega hessian eval 2", "omhessevec", "omega hessian evecs", "omhessevec0", "omega hessian evec 0", "omhessevec1", "omega hessian evec 1", "omhessevec2", "omega hessian evec 2", "omlapl", "omega laplacian", "om2d", "om2dd", "omega 2nd DD", "omhesscte0", "omega hessian contracted with ten evec 0", "omhesscte1", "omega hessian contracted with ten evec 1", "omhesscte2", "omega hessian contracted with ten evec 2", "trgvdotevec0", "trace gradvec dot evec0", "datr", "diffusionAlign(trace)", "dftr", "diffusionFraction(trace)", "fagvdotevec0", "FA gradvec dot evec0", "dafa", "diffusionAlign(FA)", "dffa", "diffusionFraction(FA)", "omgvdotevec0", "omega gradvec dot evec0", "daom", "diffusionAlign(Omega)", "dfom", "diffusionFraction(Omega)", "confgvdotevec0", "conf gradvec dot evec0", "daconf", "diffusionAlign(Conf)", "dfconf", "diffusionFraction(Conf)", "cov", "covr", "covk", "logeuclidean", "logeuc", "logeuclid", "quatgeoloxk", "qglk", "quatgeoloxr", "qglr", "rtpl", "cl1gv", "cl1gm", "cl1gn", "cp1gv", "cp1gm", "cp1gn", "ca1gv", "ca1gm", "ca1gn", "tgrote", "evalhess", "eval hessian", "cl1hess", "cl1 hessian", "cl1hesseval", "cl1 hessian evals", "cl1hesseval0", "cl1 hessian eval 0", "cl1hesseval1", "cl1 hessian eval 1", "cl1hesseval2", "cl1 hessian eval 2", "cl1hessevec", "cl1 hessian evecs", "cl1hessevec0", "cl1 hessian evec 0", "cl1hessevec1", "cl1 hessian evec 1", "cl1hessevec2", "cl1 hessian evec 2", "cp1hess", "cp1 hessian", "cp1hesseval", "cp1 hessian evals", "cp1hesseval0", "cp1 hessian eval 0", "cp1hesseval1", "cp1 hessian eval 1", "cp1hesseval2", "cp1 hessian eval 2", "cp1hessevec", "cp1 hessian evecs", "cp1hessevec0", "cp1 hessian evec 0", "cp1hessevec1", "cp1 hessian evec 1", "cp1hessevec2", "cp1 hessian evec 2", "ca1hess", "ca1 hessian", "ca1hesseval", "ca1 hessian evals", "ca1hesseval0", "ca1 hessian eval 0", "ca1hesseval1", "ca1 hessian eval 1", "ca1hesseval2", "ca1 hessian eval 2", "ca1hessevec", "ca1 hessian evecs", "ca1hessevec0", "ca1 hessian evec 0", "ca1hessevec1", "ca1 hessian evec 1", "ca1hessevec2", "ca1 hessian evec 2", "fcurv", "fibcurv", "fiber curving", "fiber curving index", "fdisp", "fibdisp", "fiber dispersion", "fiber dispersion index", "an", "aniso", "anisotropies", "" }; const int _tenGageValEqv[] = { tenGageTensor, tenGageTensor, tenGageTensor, tenGageConfidence, tenGageConfidence, tenGageConfidence, tenGageTrace, tenGageTrace, tenGageNorm, tenGageNorm, tenGageNorm, tenGageB, tenGageDet, tenGageS, tenGageQ, tenGageFA, tenGageR, tenGageMode, tenGageMode, tenGageTheta, tenGageTheta, tenGageModeWarp, tenGageModeWarp, tenGageOmega, tenGageOmega, tenGageEval, tenGageEval, tenGageEval0, tenGageEval1, tenGageEval2, tenGageEvec, tenGageEvec, tenGageEvec0, tenGageEvec1, tenGageEvec2, tenGageDelNormK2, tenGageDelNormK3, tenGageDelNormK3, tenGageDelNormR1, tenGageDelNormR2, tenGageDelNormPhi1, tenGageDelNormPhi2, tenGageDelNormPhi3, tenGageTensorGrad, tenGageTensorGrad, tenGageTensorGradMag, tenGageTensorGradMag, tenGageTensorGradMagMag, tenGageTensorGradMagMag, tenGageTraceGradVec, tenGageTraceGradVec, tenGageTraceGradVec, tenGageTraceGradMag, tenGageTraceGradMag, tenGageTraceGradMag, tenGageTraceNormal, tenGageTraceNormal, tenGageTraceNormal, tenGageNormGradVec, tenGageNormGradVec, tenGageNormGradVec, tenGageNormGradVec, tenGageNormGradMag, tenGageNormGradMag, tenGageNormGradMag, tenGageNormGradMag, tenGageNormNormal, tenGageNormNormal, tenGageNormNormal, tenGageNormNormal, tenGageBGradVec, tenGageBGradVec, tenGageBGradMag, tenGageBGradMag, tenGageBNormal, tenGageBNormal, tenGageDetGradVec, tenGageDetGradVec, tenGageDetGradMag, tenGageDetGradMag, tenGageDetNormal, tenGageDetNormal, tenGageSGradVec, tenGageSGradVec, tenGageSGradMag, tenGageSGradMag, tenGageSNormal, tenGageSNormal, tenGageQGradVec, tenGageQGradVec, tenGageQGradMag, tenGageQGradMag, tenGageQNormal, tenGageQNormal, tenGageFAGradVec, tenGageFAGradVec, tenGageFAGradMag, tenGageFAGradMag, tenGageFANormal, tenGageFANormal, tenGageRGradVec, tenGageRGradVec, tenGageRGradMag, tenGageRGradMag, tenGageRNormal, tenGageRNormal, tenGageModeGradVec, tenGageModeGradVec, tenGageModeGradMag, tenGageModeGradMag, tenGageModeNormal, tenGageModeNormal, tenGageThetaGradVec, tenGageThetaGradVec, tenGageThetaGradVec, tenGageThetaGradMag, tenGageThetaGradMag, tenGageThetaGradMag, tenGageThetaNormal, tenGageThetaNormal, tenGageThetaNormal, tenGageOmegaGradVec, tenGageOmegaGradVec, tenGageOmegaGradMag, tenGageOmegaGradMag, tenGageOmegaNormal, tenGageOmegaNormal, tenGageInvarKGrads, tenGageInvarKGrads, tenGageInvarKGradMags, tenGageInvarKGradMags, tenGageInvarRGrads, tenGageInvarRGrads, tenGageInvarRGradMags, tenGageInvarRGradMags, tenGageRotTans, tenGageRotTans, tenGageRotTanMags, tenGageRotTanMags, tenGageEvalGrads, tenGageEvalGrads, tenGageCl1, tenGageCp1, tenGageCa1, tenGageClpmin1, tenGageCl2, tenGageCp2, tenGageCa2, tenGageClpmin2, tenGageHessian, tenGageHessian, tenGageTraceHessian, tenGageTraceHessian, tenGageTraceHessianEval, tenGageTraceHessianEval, tenGageTraceHessianEval0, tenGageTraceHessianEval0, tenGageTraceHessianEval1, tenGageTraceHessianEval1, tenGageTraceHessianEval2, tenGageTraceHessianEval2, tenGageTraceHessianEvec, tenGageTraceHessianEvec, tenGageTraceHessianEvec0, tenGageTraceHessianEvec0, tenGageTraceHessianEvec1, tenGageTraceHessianEvec1, tenGageTraceHessianEvec2, tenGageTraceHessianEvec2, tenGageTraceHessianFrob, tenGageTraceHessianFrob, tenGageBHessian, tenGageBHessian, tenGageDetHessian, tenGageDetHessian, tenGageSHessian, tenGageSHessian, tenGageQHessian, tenGageQHessian, tenGageFAHessian, tenGageFAHessian, tenGageFAHessianEval, tenGageFAHessianEval, tenGageFAHessianEval0, tenGageFAHessianEval0, tenGageFAHessianEval1, tenGageFAHessianEval1, tenGageFAHessianEval2, tenGageFAHessianEval2, tenGageFAHessianEvec, tenGageFAHessianEvec, tenGageFAHessianEvec0, tenGageFAHessianEvec0, tenGageFAHessianEvec1, tenGageFAHessianEvec1, tenGageFAHessianEvec2, tenGageFAHessianEvec2, tenGageFAHessianFrob, tenGageFAHessianFrob, tenGageFARidgeSurfaceStrength, tenGageFARidgeSurfaceStrength, tenGageFAValleySurfaceStrength, tenGageFAValleySurfaceStrength, tenGageFALaplacian, tenGageFALaplacian, tenGageFAHessianEvalMode, tenGageFAHessianEvalMode, tenGageFARidgeLineAlignment, tenGageFARidgeLineAlignment, tenGageFARidgeSurfaceAlignment, tenGageFARidgeSurfaceAlignment, tenGageFA2ndDD, tenGageFA2ndDD, tenGageFA2ndDD, tenGageFAGeomTens, tenGageFAGeomTens, tenGageFAKappa1, tenGageFAKappa1, tenGageFAKappa2, tenGageFAKappa2, tenGageFATotalCurv, tenGageFATotalCurv, tenGageFAShapeIndex, tenGageFAShapeIndex, tenGageFAMeanCurv, tenGageFAMeanCurv, tenGageFAGaussCurv, tenGageFAGaussCurv, tenGageFACurvDir1, tenGageFACurvDir1, tenGageFACurvDir2, tenGageFACurvDir2, tenGageFAFlowlineCurv, tenGageFAFlowlineCurv, tenGageRHessian, tenGageRHessian, tenGageModeHessian, tenGageModeHessian, tenGageModeHessianEval, tenGageModeHessianEval, tenGageModeHessianEval0, tenGageModeHessianEval0, tenGageModeHessianEval1, tenGageModeHessianEval1, tenGageModeHessianEval2, tenGageModeHessianEval2, tenGageModeHessianEvec, tenGageModeHessianEvec, tenGageModeHessianEvec0, tenGageModeHessianEvec0, tenGageModeHessianEvec1, tenGageModeHessianEvec1, tenGageModeHessianEvec2, tenGageModeHessianEvec2, tenGageModeHessianFrob, tenGageModeHessianFrob, tenGageOmegaHessian, tenGageOmegaHessian, tenGageOmegaHessianEval, tenGageOmegaHessianEval, tenGageOmegaHessianEval0, tenGageOmegaHessianEval0, tenGageOmegaHessianEval1, tenGageOmegaHessianEval1, tenGageOmegaHessianEval2, tenGageOmegaHessianEval2, tenGageOmegaHessianEvec, tenGageOmegaHessianEvec, tenGageOmegaHessianEvec0, tenGageOmegaHessianEvec0, tenGageOmegaHessianEvec1, tenGageOmegaHessianEvec1, tenGageOmegaHessianEvec2, tenGageOmegaHessianEvec2, tenGageOmegaLaplacian, tenGageOmegaLaplacian, tenGageOmega2ndDD, tenGageOmega2ndDD, tenGageOmega2ndDD, tenGageOmegaHessianContrTenEvec0, tenGageOmegaHessianContrTenEvec0, tenGageOmegaHessianContrTenEvec1, tenGageOmegaHessianContrTenEvec1, tenGageOmegaHessianContrTenEvec2, tenGageOmegaHessianContrTenEvec2, tenGageTraceGradVecDotEvec0, tenGageTraceGradVecDotEvec0, tenGageTraceDiffusionAlign, tenGageTraceDiffusionAlign, tenGageTraceDiffusionFraction, tenGageTraceDiffusionFraction, tenGageFAGradVecDotEvec0, tenGageFAGradVecDotEvec0, tenGageFADiffusionAlign, tenGageFADiffusionAlign, tenGageFADiffusionFraction, tenGageFADiffusionFraction, tenGageOmegaGradVecDotEvec0, tenGageOmegaGradVecDotEvec0, tenGageOmegaDiffusionAlign, tenGageOmegaDiffusionAlign, tenGageOmegaDiffusionFraction, tenGageOmegaDiffusionFraction, tenGageConfGradVecDotEvec0, tenGageConfGradVecDotEvec0, tenGageConfDiffusionAlign, tenGageConfDiffusionAlign, tenGageConfDiffusionFraction, tenGageConfDiffusionFraction, tenGageCovariance, tenGageCovarianceRGRT, tenGageCovarianceKGRT, tenGageTensorLogEuclidean, tenGageTensorLogEuclidean, tenGageTensorLogEuclidean, tenGageTensorQuatGeoLoxK, tenGageTensorQuatGeoLoxK, tenGageTensorQuatGeoLoxR, tenGageTensorQuatGeoLoxR, tenGageTensorRThetaPhiLinear, tenGageCl1GradVec, tenGageCl1GradMag, tenGageCl1Normal, tenGageCp1GradVec, tenGageCp1GradMag, tenGageCp1Normal, tenGageCa1GradVec, tenGageCa1GradMag, tenGageCa1Normal, tenGageTensorGradRotE, tenGageEvalHessian, tenGageEvalHessian, tenGageCl1Hessian, tenGageCl1Hessian, tenGageCl1HessianEval, tenGageCl1HessianEval, tenGageCl1HessianEval0, tenGageCl1HessianEval0, tenGageCl1HessianEval1, tenGageCl1HessianEval1, tenGageCl1HessianEval2, tenGageCl1HessianEval2, tenGageCl1HessianEvec, tenGageCl1HessianEvec, tenGageCl1HessianEvec0, tenGageCl1HessianEvec0, tenGageCl1HessianEvec1, tenGageCl1HessianEvec1, tenGageCl1HessianEvec2, tenGageCl1HessianEvec2, tenGageCp1Hessian, tenGageCp1Hessian, tenGageCp1HessianEval, tenGageCp1HessianEval, tenGageCp1HessianEval0, tenGageCp1HessianEval0, tenGageCp1HessianEval1, tenGageCp1HessianEval1, tenGageCp1HessianEval2, tenGageCp1HessianEval2, tenGageCp1HessianEvec, tenGageCp1HessianEvec, tenGageCp1HessianEvec0, tenGageCp1HessianEvec0, tenGageCp1HessianEvec1, tenGageCp1HessianEvec1, tenGageCp1HessianEvec2, tenGageCp1HessianEvec2, tenGageCa1Hessian, tenGageCa1Hessian, tenGageCa1HessianEval, tenGageCa1HessianEval, tenGageCa1HessianEval0, tenGageCa1HessianEval0, tenGageCa1HessianEval1, tenGageCa1HessianEval1, tenGageCa1HessianEval2, tenGageCa1HessianEval2, tenGageCa1HessianEvec, tenGageCa1HessianEvec, tenGageCa1HessianEvec0, tenGageCa1HessianEvec0, tenGageCa1HessianEvec1, tenGageCa1HessianEvec1, tenGageCa1HessianEvec2, tenGageCa1HessianEvec2, tenGageFiberCurving, tenGageFiberCurving, tenGageFiberCurving, tenGageFiberCurving, tenGageFiberDispersion, tenGageFiberDispersion, tenGageFiberDispersion, tenGageFiberDispersion, tenGageAniso, tenGageAniso, tenGageAniso }; const airEnum _tenGage = { "tenGage", TEN_GAGE_ITEM_MAX, _tenGageStr, _tenGageVal, _tenGageDesc, _tenGageStrEqv, _tenGageValEqv, AIR_FALSE }; const airEnum *const tenGage = &_tenGage; /* --------------------------------------------------------------------- */ const char * _tenFiberTypeStr[] = { "(unknown tenFiberType)", "evec0", "evec1", "evec2", "tensorline", "pureline", "zhukov" }; const char * _tenFiberTypeDesc[] = { "unknown tenFiber type", "simply follow principal eigenvector", "follow medium eigenvector", "follow minor eigenvector", "Weinstein-Kindlmann tensorlines", "based on tensor multiplication only", "Zhukov\'s oriented tensors" }; const char * _tenFiberTypeStrEqv[] = { "ev0", "evec0", "ev1", "evec1", "ev2", "evec2", "tline", "tensorline", "pline", "pureline", "z", "zhukov", "" }; const int _tenFiberTypeValEqv[] = { tenFiberTypeEvec0, tenFiberTypeEvec0, tenFiberTypeEvec1, tenFiberTypeEvec1, tenFiberTypeEvec2, tenFiberTypeEvec2, tenFiberTypeTensorLine, tenFiberTypeTensorLine, tenFiberTypePureLine, tenFiberTypePureLine, tenFiberTypeZhukov, tenFiberTypeZhukov }; const airEnum _tenFiberType = { "tenFiberType", TEN_FIBER_TYPE_MAX, _tenFiberTypeStr, NULL, _tenFiberTypeDesc, _tenFiberTypeStrEqv, _tenFiberTypeValEqv, AIR_FALSE }; const airEnum *const tenFiberType = &_tenFiberType; /* --------------------------------------------------------------------- */ const char * _tenDwiFiberTypeStr[] = { "(unknown tenDwiFiberType)", "1evec0", "2evec0", "12BlendEvec0" }; const char * _tenDwiFiberTypeDesc[] = { "unknown tenDwiFiber type", "single tensor evec0-based", "two-tensor evec0-based", "parameterized blend between 1- and 2-tensor fits" }; const char * _tenDwiFiberTypeStrEqv[] = { "1evec0", "1e0", "2evec0", "2e0", "12BlendEvec0", "12be0", "" }; const int _tenDwiFiberTypeValEqv[] = { tenDwiFiberType1Evec0, tenDwiFiberType1Evec0, tenDwiFiberType2Evec0, tenDwiFiberType2Evec0, tenDwiFiberType12BlendEvec0, tenDwiFiberType12BlendEvec0 }; const airEnum _tenDwiFiberType = { "tenDwiFiberType", TEN_DWI_FIBER_TYPE_MAX, _tenDwiFiberTypeStr, NULL, _tenDwiFiberTypeDesc, _tenDwiFiberTypeStrEqv, _tenDwiFiberTypeValEqv, AIR_FALSE }; const airEnum *const tenDwiFiberType = &_tenDwiFiberType; /* ----------------------------------------------------------------------- */ const char * _tenFiberStopStr[] = { "(unknown tenFiberStop)", "aniso", "length", "steps", "confidence", "radius", "bounds", "fraction", "stub", "minlen", "minsteps", }; const char * _tenFiberStopStrEqv[] = { "aniso", "length", "len", "steps", "confidence", "conf", "c", "radius", "bounds", "fraction", "frac", "f", "stub", "minlen", "minlength", "minsteps", "minnumsteps", "" }; const int _tenFiberStopValEqv[] = { tenFiberStopAniso, tenFiberStopLength, tenFiberStopLength, tenFiberStopNumSteps, tenFiberStopConfidence, tenFiberStopConfidence, tenFiberStopConfidence, tenFiberStopRadius, tenFiberStopBounds, tenFiberStopFraction, tenFiberStopFraction, tenFiberStopFraction, tenFiberStopStub, tenFiberStopMinLength, tenFiberStopMinLength, tenFiberStopMinNumSteps, tenFiberStopMinNumSteps, }; const char * _tenFiberStopDesc[] = { "unknown tenFiber stop", "anisotropy went below threshold", "fiber length exceeded normalcy bounds", "number of steps along fiber too many", "tensor \"confidence\" value too low", "radius of curvature of path got too small", "fiber went outside bounding box", "fractional constituency of tracked tensor got too small", "neither fiber half got anywhere; fiber has single vert", "whole fiber has insufficient length", "whole fiber has too few numbers of steps" }; const airEnum _tenFiberStop = { "fiber stopping criteria", TEN_FIBER_STOP_MAX, _tenFiberStopStr, NULL, _tenFiberStopDesc, _tenFiberStopStrEqv, _tenFiberStopValEqv, AIR_FALSE }; const airEnum *const tenFiberStop = &_tenFiberStop; /* ----------------------------------------------------------------------- */ const char * _tenFiberIntgStr[] = { "(unknown tenFiberIntg)", "euler", "midpoint", "rk4" }; const char * _tenFiberIntgStrEqv[] = { "euler", "midpoint", "rk2", "rk4", "" }; const int _tenFiberIntgValEqv[] = { tenFiberIntgEuler, tenFiberIntgMidpoint, tenFiberIntgMidpoint, tenFiberIntgRK4 }; const char * _tenFiberIntgDesc[] = { "unknown tenFiber intg", "plain Euler", "midpoint method, 2nd order Runge-Kutta", "4rth order Runge-Kutta" }; const airEnum _tenFiberIntg = { "fiber integration method", TEN_FIBER_INTG_MAX, _tenFiberIntgStr, NULL, _tenFiberIntgDesc, _tenFiberIntgStrEqv, _tenFiberIntgValEqv, AIR_FALSE }; const airEnum *const tenFiberIntg = &_tenFiberIntg; /* ----------------------------------------------------------------------- */ const char * _tenGlyphTypeStr[] = { "(unknown tenGlyphType)", "box", "sphere", "cylinder", "superquad", "betterquad", "polarplot" }; #define BOX tenGlyphTypeBox #define SPH tenGlyphTypeSphere #define CYL tenGlyphTypeCylinder #define SQD tenGlyphTypeSuperquad const char * _tenGlyphTypeStrEqv[] = { "b", "box", "s", "sph", "sphere", "c", "cyl", "cylind", "cylinder", "q", "superq", "sqd", "superquad", "superquadric", "bqd", "betterquad", "pplot", "polarplot", "" }; const int _tenGlyphTypeValEqv[] = { BOX, BOX, SPH, SPH, SPH, CYL, CYL, CYL, CYL, SQD, SQD, SQD, SQD, SQD, tenGlyphTypeBetterquad, tenGlyphTypeBetterquad, tenGlyphTypePolarPlot, tenGlyphTypePolarPlot }; const char * _tenGlyphTypeDesc[] = { "unknown tenGlyph type", "box/cube (rectangular prisms)", "sphere (ellipsoids)", "cylinders aligned along major eigenvector", "superquadric (superellipsoids)", "better superquadric", "polar plot", }; const airEnum _tenGlyphType = { "tenGlyphType", TEN_GLYPH_TYPE_MAX, _tenGlyphTypeStr, NULL, _tenGlyphTypeDesc, _tenGlyphTypeStrEqv, _tenGlyphTypeValEqv, AIR_FALSE }; const airEnum *const tenGlyphType = &_tenGlyphType; /* ---------------------------------------------- */ const char * _tenEstimate1MethodStr[] = { "(unknown tenEstimate1Method)", "LLS", "WLS", "NLS", "MLE" }; const char * _tenEstimate1MethodDesc[] = { "unknown tenEstimate1Method", "linear least-squares fit of log(DWI)", "weighted least-squares fit of log(DWI)", "non-linear least-squares fit of DWI", "maximum likelihood estimate from DWI" }; const airEnum _tenEstimate1Method = { "single-tensor-estimation", TEN_ESTIMATE_1_METHOD_MAX, _tenEstimate1MethodStr, NULL, _tenEstimate1MethodDesc, NULL, NULL, AIR_FALSE }; const airEnum *const tenEstimate1Method= &_tenEstimate1Method; /* ---------------------------------------------- */ const char * _tenEstimate2MethodStr[] = { "(unknown tenEstimate2Method)", "QSegLLS", "Peled" }; const char * _tenEstimate2MethodDesc[] = { "unknown tenEstimate2Method", "Q-ball segmentation", "Peled" }; const airEnum _tenEstimate2Method = { "two-tensor-estimation", TEN_ESTIMATE_2_METHOD_MAX, _tenEstimate2MethodStr, NULL, _tenEstimate2MethodDesc, NULL, NULL, AIR_FALSE }; const airEnum *const tenEstimate2Method= &_tenEstimate2Method; /* ---------------------------------------------- */ const char * _tenTripleTypeStr[] = { "(unknown tenTriple)", "eigenvalue", "moment", "XYZ", "RThetaZ", "RThetaPhi", "J", "K", "R", "wheelParms" }; const char * _tenTripleTypeDesc[] = { "unknown tenTriple", "eigenvalues sorted in descending order", "central moments (mu1,mu2,mu3)", "rotation of evals, like Bahn 1999 JMR:141(68-77)", "cylindrical coords of rotated evals", "spherical coords of rotated evals", "principal invariants (J1,J2,J3)", "cylindrical invariants (K1,K2,K3)", "spherical invariants (R1,R2,R3)", "eigenvalue wheel (center,radius,angle)" }; const char * _tenTripleTypeStrEqv[] = { "eigenvalue", "eval", "ev", "moment", "mu", "XYZ", "RThetaZ", "RThZ", "rtz", "RThetaPhi", "RThPh", "rtp", "J", "K", "R", "wheelParm", "wheelParms", "WP", "" }; const int _tenTripleTypeValEqv[] = { tenTripleTypeEigenvalue, tenTripleTypeEigenvalue, tenTripleTypeEigenvalue, tenTripleTypeMoment, tenTripleTypeMoment, tenTripleTypeXYZ, tenTripleTypeRThetaZ, tenTripleTypeRThetaZ, tenTripleTypeRThetaZ, tenTripleTypeRThetaPhi, tenTripleTypeRThetaPhi, tenTripleTypeRThetaPhi, tenTripleTypeJ, tenTripleTypeK, tenTripleTypeR, tenTripleTypeWheelParm, tenTripleTypeWheelParm, tenTripleTypeWheelParm }; const airEnum _tenTripleType = { "tenTripleType", TEN_TRIPLE_TYPE_MAX, _tenTripleTypeStr, NULL, _tenTripleTypeDesc, _tenTripleTypeStrEqv, _tenTripleTypeValEqv, AIR_FALSE }; const airEnum *const tenTripleType = &_tenTripleType; teem-1.11.0~svn6057/src/ten/tendMsim.c0000664000175000017500000001576512165631065017131 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Simulate DW images from an image of models" static const char *_tend_msimInfoL = (INFO ". The output will be in the same form as the input to \"tend estim\". " "The B-matrices (\"-B\") can be the output from \"tend bmat\", or the " "gradients can be given directly (\"-g\"); one of these is required. " "Note that the input tensor image (\"-i\") is the basis of the output " "per-axis fields and image orientation. NOTE: this includes the " "measurement frame used in the input tensor image, which implies that " "the given gradients or B-matrices are already expressed in that " "measurement frame. "); int tend_msimMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; tenExperSpec *espec; const tenModel *model; int E, seed, keyValueSet, outType, plusB0, insertB0; Nrrd *nin, *nT2, *_ngrad, *ngrad, *nout; char *outS, *modS; double bval, sigma; /* maybe this can go in tend.c, but for some reason its explicitly set to AIR_FALSE there */ hparm->elideSingleOtherDefault = AIR_TRUE; hestOptAdd(&hopt, "sigma", "sigma", airTypeDouble, 1, 1, &sigma, "0.0", "Gaussian/Rician noise parameter"); hestOptAdd(&hopt, "seed", "seed", airTypeInt, 1, 1, &seed, "42", "seed value for RNG which creates noise"); hestOptAdd(&hopt, "g", "grad list", airTypeOther, 1, 1, &_ngrad, NULL, "gradient list, one row per diffusion-weighted image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "b0", "b0 image", airTypeOther, 1, 1, &nT2, "", "reference non-diffusion-weighted (\"B0\") image, which " "may be needed if it isn't part of give model param image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "i", "model image", airTypeOther, 1, 1, &nin, "-", "input model image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "m", "model", airTypeString, 1, 1, &modS, NULL, "model with which to simulate DWIs, which must be specified if " "it is not indicated by the first axis in input model image."); hestOptAdd(&hopt, "ib0", "bool", airTypeBool, 1, 1, &insertB0, "false", "insert a non-DW B0 image at the beginning of the experiment " "specification (useful if the given gradient list doesn't " "already have one) and hence also insert a B0 image at the " "beginning of the output simulated DWIs"); hestOptAdd(&hopt, "b", "b", airTypeDouble, 1, 1, &bval, "1000", "b value for simulated scan"); hestOptAdd(&hopt, "kvp", "bool", airTypeBool, 1, 1, &keyValueSet, "true", "generate key/value pairs in the NRRD header corresponding " "to the input b-value and gradients."); hestOptAdd(&hopt, "t", "type", airTypeEnum, 1, 1, &outType, "float", "output type of DWIs", NULL, nrrdType); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output dwis"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_msimInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); espec = tenExperSpecNew(); airMopAdd(mop, espec, (airMopper)tenExperSpecNix, airMopAlways); airSrandMT(seed); if (nrrdTypeDouble == _ngrad->type) { ngrad = _ngrad; } else { ngrad = nrrdNew(); airMopAdd(mop, ngrad, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(ngrad, _ngrad, nrrdTypeDouble)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble converting grads to %s:\n%s\n", me, airEnumStr(nrrdType, nrrdTypeDouble), err); airMopError(mop); return 1; } } plusB0 = AIR_FALSE; if (airStrlen(modS)) { if (tenModelParse(&model, &plusB0, AIR_FALSE, modS)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing model \"%s\":\n%s\n", me, modS, err); airMopError(mop); return 1; } } else if (tenModelFromAxisLearnPossible(nin->axis + 0)) { if (tenModelFromAxisLearn(&model, &plusB0, nin->axis + 0)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing model frmo axis 0 of nin:\n%s\n", me, err); airMopError(mop); return 1; } } else { fprintf(stderr, "%s: need model specified either via \"-m\" or input " "model image axis 0\n", me); airMopError(mop); return 1; } /* we have learned plusB0, but we don't actually need it; either: it describes the given model param image (which is courteous but not necessary since the logic inside tenModeSimulate will see this), or: it is trying to say something about including B0 amongst model parameters (which isn't actually meaningful in the context of simulated DWIs */ E = 0; if (!E) E |= tenGradientCheck(ngrad, nrrdTypeDouble, 1); if (!E) E |= tenExperSpecGradSingleBValSet(espec, insertB0, bval, AIR_CAST(const double *, ngrad->data), ngrad->axis[1].size); if (!E) E |= tenModelSimulate(nout, outType, espec, model, nT2, nin, keyValueSet); if (E) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(msim, INFO); teem-1.11.0~svn6057/src/ten/grads.c0000664000175000017500000005550012165631065016440 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" tenGradientParm * tenGradientParmNew(void) { tenGradientParm *ret; ret = (tenGradientParm *)calloc(1, sizeof(tenGradientParm)); if (ret) { ret->initStep = 1.0; ret->jitter = 0.2; ret->minVelocity = 0.000000001; ret->minPotentialChange = 0.000000001; ret->minMean = 0.0001; ret->minMeanImprovement = 0.00005; ret->single = AIR_FALSE; ret->insertZeroVec = AIR_FALSE; ret->verbose = 1; ret->snap = 0; ret->report = 400; ret->expo = 1; ret->expo_d = 0; ret->seed = 42; ret->maxEdgeShrink = 20; ret->minIteration = 0; ret->maxIteration = 1000000; ret->step = 0; ret->nudge = 0; ret->itersUsed = 0; ret->potential = 0; ret->potentialNorm = 0; ret->angle = 0; ret->edge = 0; } return ret; } tenGradientParm * tenGradientParmNix(tenGradientParm *tgparm) { airFree(tgparm); return NULL; } int tenGradientCheck(const Nrrd *ngrad, int type, unsigned int minnum) { static const char me[]="tenGradientCheck"; if (nrrdCheck(ngrad)) { biffMovef(TEN, NRRD, "%s: basic validity check failed", me); return 1; } if (!( 3 == ngrad->axis[0].size && 2 == ngrad->dim )) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: need a 3xN 2-D array (not a %sx? %u-D array)", me, airSprintSize_t(stmp, ngrad->axis[0].size), ngrad->dim); return 1; } if (nrrdTypeDefault != type && type != ngrad->type) { biffAddf(TEN, "%s: requested type %s but got type %s", me, airEnumStr(nrrdType, type), airEnumStr(nrrdType, ngrad->type)); return 1; } if (nrrdTypeBlock == ngrad->type) { biffAddf(TEN, "%s: sorry, can't use %s type", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!( minnum <= ngrad->axis[1].size )) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: have only %s gradients, need at least %d", me, airSprintSize_t(stmp, ngrad->axis[1].size), minnum); return 1; } return 0; } /* ******** tenGradientRandom ** ** generates num random unit vectors of type double */ int tenGradientRandom(Nrrd *ngrad, unsigned int num, unsigned int seed) { static const char me[]="tenGradientRandom"; double *grad, len; unsigned int gi; if (nrrdMaybeAlloc_va(ngrad, nrrdTypeDouble, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, num))) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); return 1; } airSrandMT(seed); grad = AIR_CAST(double*, ngrad->data); for (gi=0; gi 1 || !len); ELL_3V_SCALE(grad, 1.0/len, grad); grad += 3; } return 0; } /* ******** tenGradientIdealEdge ** ** edge length of delauney triangulation of idealized distribution of ** N gradients (2*N points), but also allowing a boolean "single" flag ** saying that we actually care about N points */ double tenGradientIdealEdge(unsigned int N, int single) { return sqrt((!single ? 4 : 8)*AIR_PI/(sqrt(3)*N)); } /* ******** tenGradientJitter ** ** moves all gradients by amount dist on tangent plane, in a random ** direction, and then renormalizes. The distance is a fraction ** of the ideal edge length (via tenGradientIdealEdge) */ int tenGradientJitter(Nrrd *nout, const Nrrd *nin, double dist) { static const char me[]="tenGradientJitter"; double *grad, perp0[3], perp1[3], len, theta, cc, ss, edge; unsigned int gi, num; if (nrrdConvert(nout, nin, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: trouble converting input to double", me); return 1; } if (tenGradientCheck(nout, nrrdTypeDouble, 3)) { biffAddf(TEN, "%s: didn't get valid gradients", me); return 1; } grad = AIR_CAST(double*, nout->data); num = AIR_UINT(nout->axis[1].size); /* HEY: possible confusion between single and not */ edge = tenGradientIdealEdge(num, AIR_FALSE); for (gi=0; giaxis[1].size); pos = AIR_CAST(double *, npos->data); edge = (edgeNormalize ? tenGradientIdealEdge(num, tgparm->single) : 1.0); *pot = 0; if (minAngle) { *minAngle = AIR_PI; } if (minEdge) { *minEdge = 2; } for (ii=0; iiexpo) { ptmp = airIntPow(edge/len, tgparm->expo); } else { ptmp = pow(edge/len, tgparm->expo_d); } *pot += ptmp; if (minAngle) { atmp = ell_3v_angle_d(pos + 3*ii, pos + 3*jj); *minAngle = AIR_MIN(atmp, *minAngle); } if (!tgparm->single) { *pot += ptmp; ELL_3V_ADD2(diff, pos + 3*ii, pos + 3*jj); len = ELL_3V_LEN(diff); if (minEdge) { *minEdge = AIR_MIN(*minEdge, len); } if (tgparm->expo) { *pot += 2*airIntPow(edge/len, tgparm->expo); } else { *pot += 2*pow(edge/len, tgparm->expo_d); } if (minAngle) { *minAngle = AIR_MIN(AIR_PI-atmp, *minAngle); } } } } return; } /* ** Do asynchronous update of positions in "npos', based on force ** calculations wherein the distances are normalized "edge". Using a ** small "edge" allows forces to either underflow to zero, or be ** finite, instead of exploding to infinity, for high exponents. ** ** The smallest seen edge length is recorded in "*edgeMin", which is ** initialized to the given "edge". This allows, for example, the ** caller to try again with a smaller edge normalization. ** ** The mean velocity of the points through the update is recorded in ** "*meanVel". ** ** Based on the observation that when using large exponents, numerical ** difficulties arise from the (force-based) update of the positions ** of the two (or few) closest particles, this function puts a speed ** limit (variable "limit") on the distance a particle may move during ** update, expressed as a fraction of the normalizing edge length. ** "limit" has been set heuristically, according to the exponent (we ** have to clamp speeds more aggresively with higher exponents), as ** well as (even more heuristically) according to the number of times ** the step size has been decreased. This latter factor has to be ** bounded, so that the update is not unnecessarily bounded when the ** step size gets very small at the last stages of computation. ** Without the step-size-based speed limit, the step size would ** sometimes (e.g. num=200, expo=300) have to reduced to a miniscule ** value, which slows subsequent convergence terribly. ** ** this function is not static, though it could be, so that mac's ** "Sampler" app can profile this */ int _tenGradientUpdate(double *meanVel, double *edgeMin, Nrrd *npos, double edge, tenGradientParm *tgparm) { /* static const char me[]="_tenGradientUpdate"; */ double *pos, newpos[3], grad[3], ngrad[3], dir[3], len, rep, step, diff[3], limit, expo; int num, ii, jj, E; E = 0; pos = AIR_CAST(double *, npos->data); num = AIR_UINT(npos->axis[1].size); *meanVel = 0; *edgeMin = edge; expo = tgparm->expo ? tgparm->expo : tgparm->expo_d; limit = expo*AIR_MIN(sqrt(expo), log(1 + tgparm->initStep/tgparm->step)); for (ii=0; iiexpo) { rep = airIntPow(edge/len, tgparm->expo+1); } else { rep = pow(edge/len, tgparm->expo_d+1); } ELL_3V_SCALE_INCR(grad, rep/num, dir); if (!tgparm->single) { ELL_3V_ADD2(dir, pos + 3*ii, pos + 3*jj); ELL_3V_NORM(dir, dir, len); *edgeMin = AIR_MIN(*edgeMin, len); if (tgparm->expo) { rep = airIntPow(edge/len, tgparm->expo+1); } else { rep = pow(edge/len, tgparm->expo_d+1); } ELL_3V_SCALE_INCR(grad, rep/num, dir); } } ELL_3V_NORM(ngrad, grad, len); if (!( AIR_EXISTS(len) )) { /* things blew up, either in incremental force additions, or in the attempt at normalization */ E = 1; *meanVel = AIR_NAN; break; } if (0 == len) { /* if the length of grad[] underflowed to zero, we can legitimately zero out ngrad[] */ ELL_3V_SET(ngrad, 0, 0, 0); } step = AIR_MIN(len*tgparm->step, edge/limit); ELL_3V_SCALE_ADD2(newpos, 1.0, pos + 3*ii, step, ngrad); ELL_3V_NORM(newpos, newpos, len); ELL_3V_SUB(diff, pos + 3*ii, newpos); *meanVel += ELL_3V_LEN(diff); ELL_3V_COPY(pos + 3*ii, newpos); } *meanVel /= num; return E; } /* ** assign random signs to the vectors and measures the length of their ** mean, as quickly as possible */ static double party(Nrrd *npos, airRandMTState *rstate) { double *pos, mean[3]; unsigned int ii, num, rnd, rndBit; pos = (double *)(npos->data); num = AIR_UINT(npos->axis[1].size); rnd = airUIrandMT_r(rstate); rndBit = 0; ELL_3V_SET(mean, 0, 0, 0); for (ii=0; iiseed); airMopAdd(mop, rstate, (airMopper)airRandMTStateNix, airMopAlways); /* HEY: factor of 100 is an approximate hack */ maxIter = 100*tgparm->maxIteration; lastLen = 1.0; done = AIR_FALSE; do { iter = 0; do { iter++; len = party(nout, rstate); } while (len > lastLen && iter < maxIter); if (iter >= maxIter) { if (tgparm->verbose) { fprintf(stderr, "%s: stopping at max iter %u\n", me, maxIter); } if (nrrdCopy(nout, ncopy)) { biffMovef(TEN, NRRD, "%s: trouble copying", me); airMopError(mop); return 1; } done = AIR_TRUE; } else { if (nrrdCopy(ncopy, nout)) { biffMovef(TEN, NRRD, "%s: trouble copying", me); airMopError(mop); return 1; } improv = lastLen - len; lastLen = len; if (tgparm->verbose) { fprintf(stderr, "%s: (iter %u) improvement: %g (mean length = %g)\n", me, iter, improv, len); } done = (improv <= tgparm->minMeanImprovement || len < tgparm->minMean); } } while (!done); airMopOkay(mop); return 0; } /* ******** tenGradientDistribute ** ** Takes the given list of gradients, normalizes their lengths, ** optionally jitters their positions, does point repulsion, and then ** (optionally) selects a combination of directions with minimum vector sum. ** ** The complicated part of this is the point repulsion, which uses a ** gradient descent with variable set size. The progress of the system ** is measured by decrease in potential (when its measurement doesn't ** overflow to infinity) or an increase in the minimum angle. When a ** step results in negative progress, the step size is halved, and the ** iteration is attempted again. Based on the observation that at ** some points the step size must be made very small to get progress, ** the step size is cautiously increased ("nudged") at every ** iteration, to try to avoid using an overly small step. The amount ** by which the step is nudged is halved everytime the step is halved, ** to avoid endless cycling through step sizes. */ int tenGradientDistribute(Nrrd *nout, const Nrrd *nin, tenGradientParm *tgparm) { static const char me[]="tenGradientDistribute"; char filename[AIR_STRLEN_SMALL]; unsigned int ii, num, iter, oldIdx, newIdx, edgeShrink; airArray *mop; Nrrd *npos[2]; double *pos, len, meanVelocity, pot, potNew, potD, edge, edgeMin, angle, angleNew; int E; if (!nout || tenGradientCheck(nin, nrrdTypeUnknown, 2) || !tgparm) { biffAddf(TEN, "%s: got NULL pointer or invalid input", me); return 1; } num = AIR_UINT(nin->axis[1].size); mop = airMopNew(); npos[0] = nrrdNew(); npos[1] = nrrdNew(); airMopAdd(mop, npos[0], (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, npos[1], (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(npos[0], nin, nrrdTypeDouble) || nrrdConvert(npos[1], nin, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: trouble allocating temp buffers", me); airMopError(mop); return 1; } pos = (double*)(npos[0]->data); for (ii=0; iijitter) { if (tenGradientJitter(npos[0], npos[0], tgparm->jitter)) { biffAddf(TEN, "%s: problem jittering input", me); airMopError(mop); return 1; } } /* initialize things prior to first iteration; have to make sure that loop body tests pass 1st time around */ meanVelocity = 2*tgparm->minVelocity; potD = -2*tgparm->minPotentialChange; oldIdx = 0; newIdx = 1; tgparm->step = tgparm->initStep; tgparm->nudge = 0.1; tenGradientMeasure(&pot, &angle, NULL, npos[oldIdx], tgparm, AIR_TRUE); for (iter = 0; ((!!tgparm->minIteration && iter < tgparm->minIteration) || (iter < tgparm->maxIteration && (!tgparm->minPotentialChange || !AIR_EXISTS(potD) || -potD > tgparm->minPotentialChange) && (!tgparm->minVelocity || meanVelocity > tgparm->minVelocity) && tgparm->step > FLT_MIN)); iter++) { /* copy positions from old to new */ memcpy(npos[newIdx]->data, npos[oldIdx]->data, 3*num*sizeof(double)); edge = tenGradientIdealEdge(num, tgparm->single); edgeShrink = 0; /* try to do a position update, which will fail if repulsion values explode, from having an insufficiently small edge normalization, so retry with smaller edge next time */ do { E = _tenGradientUpdate(&meanVelocity, &edgeMin, npos[newIdx], edge, tgparm); if (E) { if (edgeShrink > tgparm->maxEdgeShrink) { biffAddf(TEN, "%s: %u > %u edge shrinks (%g), update still failed", me, edgeShrink, tgparm->maxEdgeShrink, edge); airMopError(mop); return 1; } edgeShrink++; /* re-initialize positions (HEY ugly code logic) */ memcpy(npos[newIdx]->data, npos[oldIdx]->data, 3*num*sizeof(double)); edge = edgeMin; } } while (E); tenGradientMeasure(&potNew, &angleNew, NULL, npos[newIdx], tgparm, AIR_TRUE); if ((AIR_EXISTS(pot) && AIR_EXISTS(potNew) && potNew <= pot) || angleNew >= angle) { /* there was progress of some kind, either through potential decrease, or angle increase */ potD = 2*(potNew - pot)/(potNew + pot); if (!(iter % tgparm->report) && tgparm->verbose) { fprintf(stderr, "%s(%d): . . . . . . step = %g, edgeShrink = %u\n" " velo = %g<>%g, phi = %g ~ %g<>%g, angle = %g ~ %g\n", me, iter, tgparm->step, edgeShrink, meanVelocity, tgparm->minVelocity, pot, potD, tgparm->minPotentialChange, angle, angleNew - angle); } if (tgparm->snap && !(iter % tgparm->snap)) { sprintf(filename, "%05d.nrrd", iter/tgparm->snap); if (tgparm->verbose) { fprintf(stderr, "%s(%d): . . . . . . saving %s\n", me, iter, filename); } if (nrrdSave(filename, npos[newIdx], NULL)) { char *serr; serr = biffGetDone(NRRD); if (tgparm->verbose) { /* perhaps shouldn't have this check */ fprintf(stderr, "%s: iter=%d, couldn't save snapshot:\n%s" "continuing ...\n", me, iter, serr); } free(serr); } } tgparm->step *= 1 + tgparm->nudge; tgparm->step = AIR_MIN(tgparm->initStep, tgparm->step); pot = potNew; angle = angleNew; /* swap buffers */ newIdx = 1 - newIdx; oldIdx = 1 - oldIdx; } else { /* oops, did not make progress; back off and try again */ if (tgparm->verbose) { fprintf(stderr, "%s(%d): ######## step %g --> %g\n" " phi = %g --> %g ~ %g, angle = %g --> %g\n", me, iter, tgparm->step, tgparm->step/2, pot, potNew, potD, angle, angleNew); } tgparm->step /= 2; tgparm->nudge /= 2; } } /* when the for-loop test fails, we stop before computing the next iteration (which starts with copying from npos[oldIdx] to npos[newIdx]) ==> the final results are in npos[oldIdx] */ if (tgparm->verbose) { fprintf(stderr, "%s: .......................... done distribution:\n" " (%d && %d) || (%d \n" " && (%d || %d || %d) \n" " && (%d || %d) \n" " && %d) is false\n", me, !!tgparm->minIteration, iter < tgparm->minIteration, iter < tgparm->maxIteration, !tgparm->minPotentialChange, !AIR_EXISTS(potD), AIR_ABS(potD) > tgparm->minPotentialChange, !tgparm->minVelocity, meanVelocity > tgparm->minVelocity, tgparm->step > FLT_MIN); fprintf(stderr, " iter=%d, velo = %g<>%g, phi = %g ~ %g<>%g;\n", iter, meanVelocity, tgparm->minVelocity, pot, potD, tgparm->minPotentialChange); fprintf(stderr, " minEdge = %g; idealEdge = %g\n", 2*sin(angle/2), tenGradientIdealEdge(num, tgparm->single)); } tenGradientMeasure(&pot, NULL, NULL, npos[oldIdx], tgparm, AIR_FALSE); tgparm->potential = pot; tenGradientMeasure(&pot, &angle, &edge, npos[oldIdx], tgparm, AIR_TRUE); tgparm->potentialNorm = pot; tgparm->angle = angle; tgparm->edge = edge; tgparm->itersUsed = iter; if ((tgparm->minMeanImprovement || tgparm->minMean) && !tgparm->single) { if (tgparm->verbose) { fprintf(stderr, "%s: optimizing balance:\n", me); } if (tenGradientBalance(nout, npos[oldIdx], tgparm)) { biffAddf(TEN, "%s: failed to minimize vector sum of gradients", me); airMopError(mop); return 1; } if (tgparm->verbose) { fprintf(stderr, "%s: .......................... done balancing.\n", me); } } else { if (tgparm->verbose) { fprintf(stderr, "%s: .......................... (no balancing)\n", me); } if (nrrdConvert(nout, npos[oldIdx], nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: couldn't set output", me); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } /* ** note that if tgparm->insertZeroVec, there will be one sample more ** along axis 1 of nout than the requested #gradients "num" */ int tenGradientGenerate(Nrrd *nout, unsigned int num, tenGradientParm *tgparm) { static const char me[]="tenGradientGenerate"; Nrrd *nin; airArray *mop; if (!(nout && tgparm)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( num >= 3 )) { biffAddf(TEN, "%s: can generate minimum of 3 gradient directions " "(not %d)", me, num); return 1; } mop = airMopNew(); nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); if (tenGradientRandom(nin, num, tgparm->seed) || tenGradientDistribute(nout, nin, tgparm)) { biffAddf(TEN, "%s: trouble", me); airMopError(mop); return 1; } if (tgparm->insertZeroVec) { /* this is potentially confusing: the second axis (axis 1) is going to come back one longer than the requested number of gradients! */ Nrrd *ntmp; ptrdiff_t padMin[2] = {0, -1}, padMax[2]; padMax[0] = AIR_CAST(ptrdiff_t, nout->axis[0].size-1); padMax[1] = AIR_CAST(ptrdiff_t, num-1); ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); if (nrrdPad_nva(ntmp, nout, padMin, padMax, nrrdBoundaryPad, 0.0) || nrrdCopy(nout, ntmp)) { biffMovef(TEN, NRRD, "%s: trouble adding zero vector", me); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/tendAnhist.c0000664000175000017500000000706212165631065017441 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Generate barycentric histograms of anisotropy" static const char *_tend_anhistInfoL = (INFO ". The barycentric space used is either one of Westin's " "triple of spherical, linear, and planar anisotropy. The bin " "counts in the histogram are weighted by the confidence value."); int tend_anhistMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int version, res, right; Nrrd *nin, *nout, *nwght; char *outS; hestOptAdd(&hopt, "v", "westin version", airTypeInt, 1, 1, &version, "1", "Which version of Westin's anisotropy metric triple " "to use, either \"1\" or \"2\""); hestOptAdd(&hopt, "w", "nweight", airTypeOther, 1, 1, &nwght, "", "how to weigh contributions to histogram. By default " "(not using this option), the increment is one bin count per " "sample, but by giving a nrrd, the value in the nrrd at the " "corresponding location will be the bin count increment ", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "r", "res", airTypeInt, 1, 1, &res, NULL, "resolution of anisotropy plot"); hestOptAdd(&hopt, "right", NULL, airTypeInt, 0, 0, &right, NULL, "sample a right-triangle-shaped region, instead of " "a roughly equilateral triangle. "); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_anhistInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenAnisoHistogram(nout, nin, nwght, right, version, res)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making histogram:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(anhist, INFO); teem-1.11.0~svn6057/src/ten/tendEvecrgb.c0000664000175000017500000000725712165631065017576 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Make an RGB volume from an eigenvector and an anisotropy" static const char *_tend_evecrgbInfoL = (INFO ". "); int tend_evecrgbMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; tenEvecRGBParm *rgbp; Nrrd *nin, *nout; char *outS; mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); rgbp = tenEvecRGBParmNew(); airMopAdd(mop, rgbp, AIR_CAST(airMopper, tenEvecRGBParmNix), airMopAlways); hestOptAdd(&hopt, "c", "evec index", airTypeUInt, 1, 1, &(rgbp->which), NULL, "which eigenvector will be colored. \"0\" for the " "principal, \"1\" for the middle, \"2\" for the minor"); hestOptAdd(&hopt, "a", "aniso", airTypeEnum, 1, 1, &(rgbp->aniso), NULL, "Which anisotropy to use for modulating the saturation " "of the colors. " TEN_ANISO_DESC, NULL, tenAniso); hestOptAdd(&hopt, "t", "thresh", airTypeDouble, 1, 1, &(rgbp->confThresh), "0.5", "confidence threshold"); hestOptAdd(&hopt, "bg", "background", airTypeDouble, 1, 1, &(rgbp->bgGray), "0", "gray level to use for voxels who's confidence is zero "); hestOptAdd(&hopt, "gr", "gray", airTypeDouble, 1, 1, &(rgbp->isoGray), "0", "the gray level to desaturate towards as anisotropy " "decreases (while confidence remains 1.0)"); hestOptAdd(&hopt, "gam", "gamma", airTypeDouble, 1, 1, &(rgbp->gamma), "1", "gamma to use on color components"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_evecrgbInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenEvecRGB(nout, nin, rgbp)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble doing colormapping:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(evecrgb, INFO); teem-1.11.0~svn6057/src/ten/tendShrink.c0000664000175000017500000000524012165631065017445 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Converts a 9-value DT volume to a 7-value DT volume" static const char *_tend_shrinkInfoL = (INFO ". The confidence value is set to 1.0 everwhere. You can \"unu splice\" " "or nrrdSplice() something else in its place later."); int tend_shrinkMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume, with 9 matrix components " "per sample", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL, "output tensor volume, with the 7 values per sample"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_shrinkInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenShrink(nout, NULL, nin)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble shrinking tensors:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(shrink, INFO); teem-1.11.0~svn6057/src/ten/path.c0000664000175000017500000010455112165631065016275 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" tenInterpParm * tenInterpParmNew(void) { tenInterpParm *tip; tip = AIR_CAST(tenInterpParm *, malloc(sizeof(tenInterpParm))); if (tip) { tip->verbose = AIR_FALSE; tip->convStep = 0.2; tip->minNorm = 0.0; tip->convEps = 0.0000000001; tip->wghtSumEps = 0.0000001; tip->enableRecurse = AIR_TRUE; tip->maxIter = 20; tip->numSteps = 100; tip->lengthFancy = AIR_FALSE; tip->allocLen = 0; tip->eval = NULL; tip->evec = NULL; tip->rtIn = NULL; tip->rtLog = NULL; tip->qIn = NULL; tip->qBuff = NULL; tip->qInter = NULL; tip->numIter = 0; tip->convFinal = AIR_NAN; tip->lengthShape = AIR_NAN; tip->lengthOrient = AIR_NAN; } return tip; } /* ** handles allocating all the various buffers that are needed for QGL ** interpolation, so that they are repeatedly allocated and freed ** between calls */ int tenInterpParmBufferAlloc(tenInterpParm *tip, unsigned int num) { static const char me[]="tenInterpParmBufferAlloc"; if (0 == num) { /* user wants to free buffers for some reason */ airFree(tip->eval); tip->eval = NULL; airFree(tip->evec); tip->evec = NULL; airFree(tip->rtIn); tip->rtIn = NULL; airFree(tip->rtLog); tip->rtLog = NULL; airFree(tip->qIn); tip->qIn = NULL; airFree(tip->qBuff); tip->qBuff = NULL; airFree(tip->qInter); tip->qInter = NULL; tip->allocLen = 0; } else if (1 == num) { biffAddf(TEN, "%s: need num >= 2 (not %u)", me, num); return 1; } else if (num != tip->allocLen) { airFree(tip->eval); tip->eval = NULL; airFree(tip->evec); tip->evec = NULL; airFree(tip->rtIn); tip->rtIn = NULL; airFree(tip->rtLog); tip->rtLog = NULL; airFree(tip->qIn); tip->qIn = NULL; airFree(tip->qBuff); tip->qBuff = NULL; airFree(tip->qInter); tip->qInter = NULL; tip->eval = AIR_CALLOC(3*num, double); tip->evec = AIR_CALLOC(9*num, double); tip->rtIn = AIR_CALLOC(3*num, double); tip->rtLog = AIR_CALLOC(3*num, double); tip->qIn = AIR_CALLOC(4*num, double); tip->qBuff = AIR_CALLOC(4*num, double); tip->qInter = AIR_CALLOC(num*num, double); if (!(tip->eval && tip->evec && tip->rtIn && tip->rtLog && tip->qIn && tip->qBuff && tip->qInter)) { biffAddf(TEN, "%s: didn't alloc buffers (%p,%p,%p %p %p %p %p)", me, AIR_VOIDP(tip->eval), AIR_VOIDP(tip->evec), AIR_VOIDP(tip->rtIn), AIR_VOIDP(tip->rtLog), AIR_VOIDP(tip->qIn), AIR_VOIDP(tip->qBuff), AIR_VOIDP(tip->qInter)); return 1; } tip->allocLen = num; } return 0; } tenInterpParm * tenInterpParmCopy(tenInterpParm *tip) { static const char me[]="tenInterpParmCopy"; tenInterpParm *newtip; unsigned int num; num = tip->allocLen; newtip = tenInterpParmNew(); if (newtip) { memcpy(newtip, tip, sizeof(tenInterpParm)); /* manually set all pointers */ newtip->allocLen = 0; newtip->eval = NULL; newtip->evec = NULL; newtip->rtIn = NULL; newtip->rtLog = NULL; newtip->qIn = NULL; newtip->qBuff = NULL; newtip->qInter = NULL; if (tenInterpParmBufferAlloc(newtip, num)) { biffAddf(TEN, "%s: trouble allocating output", me); return NULL; } memcpy(newtip->eval, tip->eval, 3*num*sizeof(double)); memcpy(newtip->evec, tip->evec, 9*num*sizeof(double)); memcpy(newtip->rtIn, tip->rtIn, 3*num*sizeof(double)); memcpy(newtip->rtLog, tip->rtLog, 3*num*sizeof(double)); memcpy(newtip->qIn, tip->qIn, 4*num*sizeof(double)); memcpy(newtip->qBuff, tip->qBuff, 4*num*sizeof(double)); memcpy(newtip->qInter, tip->qInter, num*num*sizeof(double)); } return newtip; } tenInterpParm * tenInterpParmNix(tenInterpParm *tip) { if (tip) { airFree(tip->eval); airFree(tip->evec); airFree(tip->rtIn); airFree(tip->rtLog); airFree(tip->qIn); airFree(tip->qBuff); airFree(tip->qInter); free(tip); } return NULL; } /* ******** tenInterpTwo_d ** ** interpolates between two tensors, in various ways ** ** this is really only used for demo purposes; its not useful for ** doing real work in DTI fields. So: its okay that its slow ** (e.g. for tenInterpTypeQuatGeoLox{K,R}, it recomputes the ** eigensystems at the endpoints for every call, even though they are ** apt to be the same between calls. ** ** this */ void tenInterpTwo_d(double oten[7], const double tenA[7], const double tenB[7], int ptype, double aa, tenInterpParm *tip) { static const char me[]="tenInterpTwo_d"; double logA[7], logB[7], tmp1[7], tmp2[7], logMean[7], mat1[9], mat2[9], mat3[9], sqrtA[7], isqrtA[7], mean[7], sqrtB[7], isqrtB[7], oeval[3], evalA[3], evalB[3], oevec[9], evecA[9], evecB[9]; if (!( oten && tenA && tenB )) { /* got NULL pointer, but not using biff */ if (oten) { TEN_T_SET(oten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); } return; } switch (ptype) { case tenInterpTypeLinear: TEN_T_LERP(oten, aa, tenA, tenB); break; case tenInterpTypeLogLinear: tenLogSingle_d(logA, tenA); tenLogSingle_d(logB, tenB); TEN_T_LERP(logMean, aa, logA, logB); tenExpSingle_d(oten, logMean); break; case tenInterpTypeAffineInvariant: tenSqrtSingle_d(sqrtA, tenA); tenInv_d(isqrtA, sqrtA); TEN_T2M(mat1, tenB); TEN_T2M(mat2, isqrtA); ELL_3M_MUL(mat3, mat1, mat2); /* B * is(A) */ ELL_3M_MUL(mat1, mat2, mat3); /* is(A) * B * is(A) */ TEN_M2T(tmp2, mat1); tenPowSingle_d(tmp1, tmp2, aa); /* m = (is(A) * B * is(A))^aa */ TEN_T2M(mat1, tmp1); TEN_T2M(mat2, sqrtA); ELL_3M_MUL(mat3, mat1, mat2); /* m * sqrt(A) */ ELL_3M_MUL(mat1, mat2, mat3); /* sqrt(A) * m * sqrt(A) */ TEN_M2T(oten, mat1); oten[0] = AIR_LERP(aa, tenA[0], tenB[0]); if (tip->verbose) { fprintf(stderr, "%s:\nA= %g %g %g %g %g %g\n" "B = %g %g %g %g %g %g\n" "foo = %g %g %g %g %g %g\n" "bar(%g) = %g %g %g %g %g %g\n", me, tenA[1], tenA[2], tenA[3], tenA[4], tenA[5], tenA[6], tenB[1], tenB[2], tenB[3], tenB[4], tenB[5], tenB[6], tmp1[1], tmp1[2], tmp1[3], tmp1[4], tmp1[5], tmp1[6], aa, oten[1], oten[2], oten[3], oten[4], oten[5], oten[6]); } break; case tenInterpTypeWang: /* HEY: this seems to be broken */ TEN_T_LERP(mean, aa, tenA, tenB); /* "A" = mean */ tenLogSingle_d(logA, tenA); tenLogSingle_d(logB, tenB); TEN_T_LERP(logMean, aa, logA, logB); /* "B" = logMean */ tenSqrtSingle_d(sqrtB, logMean); tenInv_d(isqrtB, sqrtB); TEN_T2M(mat1, mean); TEN_T2M(mat2, isqrtB); ELL_3M_MUL(mat3, mat1, mat2); ELL_3M_MUL(mat1, mat2, mat3); TEN_M2T(tmp1, mat1); tenSqrtSingle_d(oten, tmp1); oten[0] = AIR_LERP(aa, tenA[0], tenB[0]); break; case tenInterpTypeQuatGeoLoxK: case tenInterpTypeQuatGeoLoxR: tenEigensolve_d(evalA, evecA, tenA); tenEigensolve_d(evalB, evecB, tenB); if (tenInterpTypeQuatGeoLoxK == ptype) { tenQGLInterpTwoEvalK(oeval, evalA, evalB, aa); } else { tenQGLInterpTwoEvalR(oeval, evalA, evalB, aa); } tenQGLInterpTwoEvec(oevec, evecA, evecB, aa); tenMakeSingle_d(oten, AIR_LERP(aa, tenA[0], tenB[0]), oeval, oevec); break; case tenInterpTypeGeoLoxK: case tenInterpTypeGeoLoxR: case tenInterpTypeLoxK: case tenInterpTypeLoxR: /* (currently) no closed-form expression for these */ TEN_T_SET(oten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); break; case tenInterpTypeRThetaPhiLinear: if (1) { double rtpA[3], rtpB[3], rtpM[3], eval[3], tenM[7]; tenEigensolve_d(eval, NULL, tenA); tenTripleConvertSingle_d(rtpA, tenTripleTypeRThetaPhi, eval, tenTripleTypeEigenvalue); tenEigensolve_d(eval, NULL, tenB); tenTripleConvertSingle_d(rtpB, tenTripleTypeRThetaPhi, eval, tenTripleTypeEigenvalue); TEN_T_LERP(tenM, aa, tenA, tenB); tenEigensolve_d(eval, oevec, tenM); ELL_3V_LERP(rtpM, aa, rtpA, rtpB); tenTripleConvertSingle_d(oeval, tenTripleTypeEigenvalue, rtpM, tenTripleTypeRThetaPhi); } tenMakeSingle_d(oten, AIR_LERP(aa, tenA[0], tenB[0]), oeval, oevec); break; default: /* error */ TEN_T_SET(oten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); break; } return; } /* ** this NON-optionally uses biff ** ** for simplicity, a pre-allocated tenInterpParm MUST be passed, ** regardless of the interpolation requested */ int tenInterpN_d(double tenOut[7], const double *tenIn, const double *wght, unsigned int num, int ptype, tenInterpParm *tip) { static const char me[]="tenInterpN_d"; unsigned int ii; double ww, cc, tenErr[7], tmpTen[7], wghtSum, eval[3], evec[9], rtp[3]; TEN_T_SET(tenErr, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); /* wght can be NULL ==> equal 1/num weight for all */ if (!(tenOut && tenIn && tip)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( num >= 2 )) { biffAddf(TEN, "%s: need num >= 2 (not %u)", me, num); TEN_T_COPY(tenOut, tenErr); return 1; } if (airEnumValCheck(tenInterpType, ptype)) { biffAddf(TEN, "%s: invalid %s %d", me, tenInterpType->name, ptype); TEN_T_COPY(tenOut, tenErr); return 1; } wghtSum = 0; for (ii=0; iiwghtSumEps, wghtSum, 1 + tip->wghtSumEps) )) { biffAddf(TEN, "%s: wght sum %g not within %g of 1.0", me, wghtSum, tip->wghtSumEps); TEN_T_COPY(tenOut, tenErr); return 1; } switch (ptype) { case tenInterpTypeLinear: TEN_T_SET(tenOut, 0, 0, 0, 0, 0, 0, 0); cc = 0; for (ii=0; iieval + 3*ii, tip->evec + 9*ii, tenIn + 7*ii); ww = wght ? wght[ii] : 1.0/num; cc += ww*(tenIn + 7*ii)[0]; } if (_tenQGLInterpNEval(eval, tip->eval, wght, num, ptype, tip) || _tenQGLInterpNEvec(evec, tip->evec, wght, num, tip)) { biffAddf(TEN, "%s: trouble computing", me); TEN_T_COPY(tenOut, tenErr); return 1; } tenMakeSingle_d(tenOut, cc, eval, evec); } break; case tenInterpTypeRThetaPhiLinear: TEN_T_SET(tmpTen, 0, 0, 0, 0, 0, 0, 0); ELL_3V_SET(rtp, 0, 0, 0); for (ii=0; iiverbose) { fprintf(stderr, "---- %u --> %u %u %u %u %u\n", ii, 2*ii - 2, 2*ii - 1, 2*ii, 2*ii + 1, 2*ii + 2); } tdata = AIR_CAST(double *, ntdata->data); odata = AIR_CAST(double *, nodata->data); tt[0] = tdata + 7*(2*ii - 2); tt[1] = tdata + 7*(2*ii - 1); /* unused */ tt[2] = tdata + 7*(2*ii + 0); tt[3] = tdata + 7*(2*ii + 1); /* unused */ tt[4] = tdata + 7*(2*ii + 2); igrtdata = AIR_CAST(double *, nigrtdata->data); for (jj=0; jj<6; jj++) { igrt[0][jj] = igrtdata + 7*(jj + 6*(2*ii - 2)); /* unused */ igrt[1][jj] = igrtdata + 7*(jj + 6*(2*ii - 1)); igrt[2][jj] = igrtdata + 7*(jj + 6*(2*ii + 0)); igrt[3][jj] = igrtdata + 7*(jj + 6*(2*ii + 1)); igrt[4][jj] = igrtdata + 7*(jj + 6*(2*ii + 2)); /* unused */ } /* re-align [1] and [3] bases relative to [2] */ /* HEY: should I be worrying about aligning the mode normal when it had to be computed from eigenvectors? */ for (jj=3; jj<6; jj++) { if (TEN_T_DOT(igrt[1][jj], igrt[2][jj]) < 0) { TEN_T_SCALE(igrt[1][jj], -1, igrt[1][jj]); } if (TEN_T_DOT(igrt[3][jj], igrt[2][jj]) < 0) { TEN_T_SCALE(igrt[3][jj], -1, igrt[1][jj]); } } TEN_T_SUB(tng, tt[4], tt[0]); tmp = 1.0/TEN_T_NORM(tng); TEN_T_SCALE(tng, tmp, tng); TEN_T_SUB(d02, tt[2], tt[0]); TEN_T_SUB(d24, tt[4], tt[2]); TEN_T_SET(update, 1, 0, 0, 0, 0, 0, 0); for (jj=0; jj<(rotnoop ? 3u : 6u); jj++) { len02 = TEN_T_DOT(igrt[1][jj], d02); len24 = TEN_T_DOT(igrt[3][jj], d24); correct = (len24 - len02)/2; TEN_T_SCALE_INCR(update, correct*scl, igrt[2][jj]); if (tip->verbose) { fprintf(stderr, "igrt[1][%u] = %g %g %g %g %g %g\n", jj, igrt[1][jj][1], igrt[1][jj][2], igrt[1][jj][3], igrt[1][jj][4], igrt[1][jj][5], igrt[1][jj][6]); fprintf(stderr, "igrt[3][%u] = %g %g %g %g %g %g\n", jj, igrt[3][jj][1], igrt[3][jj][2], igrt[3][jj][3], igrt[3][jj][4], igrt[3][jj][5], igrt[3][jj][6]); fprintf(stderr, "(jj=%u) len = %g %g --> (d = %g) " "update = %g %g %g %g %g %g\n", jj, len02, len24, TEN_T_DOT(igrt[2][0], update), update[1], update[2], update[3], update[4], update[5], update[6]); } } if (rotnoop) { double avg[7], diff[7], len; TEN_T_LERP(avg, 0.5, tt[0], tt[4]); TEN_T_SUB(diff, avg, tt[2]); for (jj=0; jj<3; jj++) { len = TEN_T_DOT(igrt[2][jj], diff); TEN_T_SCALE_INCR(diff, -len, igrt[2][jj]); } TEN_T_SCALE_INCR(update, scl*0.2, diff); /* HEY: scaling is a hack */ if (tip->verbose) { fprintf(stderr, "(rotnoop) (d = %g) " "update = %g %g %g %g %g %g\n", TEN_T_DOT(igrt[2][0], update), update[1], update[2], update[3], update[4], update[5], update[6]); } } /* TEN_T_SUB(d02, tt[2], tt[0]); TEN_T_SUB(d24, tt[4], tt[2]); len02 = TEN_T_DOT(tng, d02); len24 = TEN_T_DOT(tng, d24); correct = (len24 - len02); TEN_T_SCALE_INCR(update, scl*correct, tng); */ if (!TEN_T_EXISTS(update)) { biffAddf(TEN, "%s: computed non-existent update (step-size too big?)", me); return 1; } TEN_T_ADD(odata + 7*(2*ii + 0), tt[2], update); return 0; } void _tenInterpGeoLoxIGRT(double *igrt, double *ten, int useK, int rotNoop, double minnorm) { /* static const char me[]="_tenInterpGeoLoxIGRT"; */ double eval[3], evec[9]; if (useK) { tenInvariantGradientsK_d(igrt + 7*0, igrt + 7*1, igrt + 7*2, ten, minnorm); } else { tenInvariantGradientsR_d(igrt + 7*0, igrt + 7*1, igrt + 7*2, ten, minnorm); } if (rotNoop) { /* these shouldn't be used */ TEN_T_SET(igrt + 7*3, 1, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); TEN_T_SET(igrt + 7*4, 1, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); TEN_T_SET(igrt + 7*5, 1, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); } else { tenEigensolve_d(eval, evec, ten); tenRotationTangents_d(igrt + 7*3, igrt + 7*4, igrt + 7*5, evec); } return; } /* ** if "doubling" is non-zero, this assumes that the real ** vertices are on the even-numbered indices: ** (0 1 2 3 4) ** 0 2 4 6 8 --> size=9 --> NN=4 ** 1 3 5 7 */ double tenInterpPathLength(Nrrd *ntt, int doubleVerts, int fancy, int shape) { double *tt, len, diff[7], *tenA, *tenB; unsigned int ii, NN; tt = AIR_CAST(double *, ntt->data); if (doubleVerts) { NN = AIR_CAST(unsigned int, (ntt->axis[1].size-1)/2); } else { NN = AIR_CAST(unsigned int, ntt->axis[1].size-1); } len = 0; for (ii=0; iidata); out = AIR_CAST(double *, nout->data); NN = (nin->axis[1].size-1)/2; lenTotal = tenInterpPathLength(nin, AIR_TRUE, AIR_FALSE, AIR_FALSE); lenStep = lenTotal/NN; /* fprintf(stderr, "!%s: lenTotal/NN = %g/%u = %g = lenStep\n", me, lenTotal, NN, lenStep); */ TEN_T_COPY(out + 7*2*(0 + 0), in + 7*2*(0 + 0)); lenIn = lenRmn = 0; idxOut = 1; for (idxIn=0; idxIn(%s)= %g\n", me, idxIn, lenRmn, lenHere, (lenRmn + lenHere >= lenStep ? "yes" : "no"), lenStep); */ if (lenRmn + lenHere >= lenStep) { len = lenRmn + lenHere; while (len > lenStep) { len -= lenStep; /* fprintf(stderr, "!%s(%u): len = %g -> %g\n", me, idxIn, len + lenStep, len); */ TEN_T_AFFINE(out + 7*(2*idxOut + 0), lenHere, len, 0, tenHere, tenNext); /* fprintf(stderr, "!%s(%u): out[%u] ~ %g\n", me, idxIn, idxOut, AIR_AFFINE(lenHere, len, 0, 0, 1)); */ idxOut++; } lenRmn = len; } else { lenRmn += lenHere; /* fprintf(stderr, "!%s(%u): (==> lenRmn = %g -> %g)\n", me, idxIn, lenRmn - lenHere, lenRmn); */ } /* now lenRmn < lenStep */ lenIn += lenHere; } /* copy very last one in case we didn't get to it somehow */ TEN_T_COPY(out + 7*2*(NN + 0), in + 7*2*(NN + 0)); /* fill in vertex mid-points */ for (idxOut=0; idxOut= 2)) { biffAddf(TEN, "%s: # steps %u too small", me, NN); return 1; } mop = airMopNew(); ntt = nrrdNew(); airMopAdd(mop, ntt, (airMopper)nrrdNuke, airMopAlways); nss = nrrdNew(); airMopAdd(mop, nss, (airMopper)nrrdNuke, airMopAlways); nigrt = nrrdNew(); airMopAdd(mop, nigrt, (airMopper)nrrdNuke, airMopAlways); nsub = nrrdNew(); airMopAdd(mop, nsub, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(ngeod, nrrdTypeDouble, 2, AIR_CAST(size_t, 7), AIR_CAST(size_t, NN+1)) || nrrdMaybeAlloc_va(ntt, nrrdTypeDouble, 2, AIR_CAST(size_t, 7), AIR_CAST(size_t, 2*NN + 1)) || nrrdMaybeAlloc_va(nigrt, nrrdTypeDouble, 3, AIR_CAST(size_t, 7), AIR_CAST(size_t, 6), AIR_CAST(size_t, 2*NN + 1))) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); airMopError(mop); return 1; } geod = AIR_CAST(double *, ngeod->data); tt = AIR_CAST(double *, ntt->data); igrt = AIR_CAST(double *, nigrt->data); *numIter = 0; if (NN > 14 && tip->enableRecurse) { unsigned int subIter; int E; NrrdResampleContext *rsmc; double kparm[3] = {1.0, 0.0, 0.5}; /* recurse and find geodesic with smaller number of vertices */ if (_tenInterpGeoLoxPolyLine(nsub, &subIter, tenA, tenB, NN/2, useK, rotnoop, tip)) { biffAddf(TEN, "%s: problem with recursive call", me); airMopError(mop); return 1; } /* upsample coarse geodesic to higher resolution */ rsmc = nrrdResampleContextNew(); airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways); E = AIR_FALSE; if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, nrrdCenterNode); if (!E) E |= nrrdResampleInputSet(rsmc, nsub); if (!E) E |= nrrdResampleKernelSet(rsmc, 0, NULL, NULL); if (!E) E |= nrrdResampleKernelSet(rsmc, 1, nrrdKernelTent, kparm); if (!E) E |= nrrdResampleSamplesSet(rsmc, 1, 2*NN + 1); if (!E) E |= nrrdResampleRangeFullSet(rsmc, 1); if (!E) E |= nrrdResampleBoundarySet(rsmc, nrrdBoundaryBleed); if (!E) E |= nrrdResampleTypeOutSet(rsmc, nrrdTypeDefault); if (!E) E |= nrrdResampleRenormalizeSet(rsmc, AIR_TRUE); if (!E) E |= nrrdResampleExecute(rsmc, ntt); if (E) { biffMovef(TEN, NRRD, "%s: problem upsampling course solution", me); airMopError(mop); return 1; } *numIter += subIter; } else { /* initialize the path, including all the segment midpoints */ for (ii=0; ii<=2*NN; ii++) { TEN_T_AFFINE(tt + 7*ii, 0, ii, 2*NN, tenA, tenB); } } for (ii=0; ii<=2*NN; ii++) { _tenInterpGeoLoxIGRT(igrt + 7*6*ii, tt + 7*ii, useK, rotnoop, tip->minNorm); } nrrdCopy(nss, ntt); newlen = tenInterpPathLength(ntt, AIR_TRUE, AIR_FALSE, AIR_FALSE); do { unsigned int lo, hi; int dd; len = newlen; if (0 == *numIter % 2) { lo = 1; hi = NN; dd = 1; } else { lo = NN-1; hi = 0; dd = -1; } if (tip->verbose) { fprintf(stderr, "%s: ======= iter = %u (NN=%u)\n", me, *numIter, NN); } for (ii=lo; ii!=hi; ii+=dd) { double sclHack; sclHack = ii*4.0/NN - ii*ii*4.0/NN/NN; if (_tenInterpGeoLoxRelaxOne(nss, ntt, nigrt, ii, rotnoop, sclHack*tip->convStep, tip)) { biffAddf(TEN, "%s: problem on vert %u, iter %u\n", me, ii, *numIter); return 1; } } newlen = _tenPathSpacingEqualize(ntt, nss); /* try doing this less often */ for (ii=0; ii<=2*NN; ii++) { _tenInterpGeoLoxIGRT(igrt + 7*6*ii, tt + 7*ii, useK, rotnoop, tip->minNorm); } *numIter += 1; } while ((0 == tip->maxIter || *numIter < tip->maxIter) && 2*AIR_ABS(newlen - len)/(newlen + len) > tip->convEps); /* copy final result to output */ for (ii=0; ii<=NN; ii++) { TEN_T_COPY(geod + 7*ii, tt + 7*2*ii); } /* values from outer-most recursion will stick */ tip->numIter = *numIter; tip->convFinal = 2*AIR_ABS(newlen - len)/(newlen + len); airMopOkay(mop); return 0; } int tenInterpTwoDiscrete_d(Nrrd *nout, const double tenA[7], const double tenB[7], int ptype, unsigned int num, tenInterpParm *_tip) { static const char me[]="tenInterpTwoDiscrete_d"; double *out; unsigned int ii; airArray *mop; tenInterpParm *tip; if (!nout) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(tenInterpType, ptype)) { biffAddf(TEN, "%s: path type %d not a valid %s", me, ptype, tenInterpType->name); return 1; } mop = airMopNew(); if (_tip) { tip = _tip; } else { tip = tenInterpParmNew(); airMopAdd(mop, tip, (airMopper)tenInterpParmNix, airMopAlways); } if (!( num >= 2 )) { biffAddf(TEN, "%s: need num >= 2 (not %u)", me, num); airMopError(mop); return 1; } if (nrrdMaybeAlloc_va(nout, nrrdTypeDouble, 2, AIR_CAST(size_t, 7), AIR_CAST(size_t, num))) { biffMovef(TEN, NRRD, "%s: trouble allocating output", me); airMopError(mop); return 1; } out = AIR_CAST(double *, nout->data); if (ptype == tenInterpTypeLinear || ptype == tenInterpTypeLogLinear || ptype == tenInterpTypeAffineInvariant || ptype == tenInterpTypeWang || ptype == tenInterpTypeQuatGeoLoxK || ptype == tenInterpTypeQuatGeoLoxR || ptype == tenInterpTypeRThetaPhiLinear) { /* we have fast ways of doing interpolation between two tensors for these path types */ for (ii=0; iinumSteps, tip)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble computing path:\n%s\n", me, err); airMopError(mop); return AIR_NAN; } ret = tenInterpPathLength(npath, AIR_FALSE, AIR_FALSE, AIR_FALSE); if (tip->lengthFancy) { tip->lengthShape = tenInterpPathLength(npath, AIR_FALSE, AIR_TRUE, AIR_TRUE); tip->lengthOrient = tenInterpPathLength(npath, AIR_FALSE, AIR_TRUE, AIR_FALSE); } break; case tenInterpTypeWang: default: fprintf(stderr, "%s: unimplemented %s %d!!!!\n", me, tenInterpType->name, ptype); ret = AIR_NAN; break; } airMopOkay(mop); return ret; } /* ** actually, the input nrrds don't have to be 3D ... */ int tenInterpMulti3D(Nrrd *nout, const Nrrd *const *nin, const double *wght, unsigned int ninLen, int ptype, tenInterpParm *_tip) { static const char me[]="tenInterpMulti3D"; unsigned int ninIdx; size_t II, NN; double (*lup)(const void *, size_t), (*ins)(void *, size_t, double), *tbuff; tenInterpParm *tip; airArray *mop; /* allow NULL wght, to signify equal weighting */ if (!(nout && nin)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( ninLen > 0 )) { biffAddf(TEN, "%s: need at least 1 nin, not 0", me); return 1; } if (airEnumValCheck(tenInterpType, ptype)) { biffAddf(TEN, "%s: invalid %s %d", me, tenInterpType->name, ptype); return 1; } if (tenTensorCheck(nin[0], nrrdTypeDefault, AIR_FALSE, AIR_TRUE)) { biffAddf(TEN, "%s: first nrrd not a tensor array", me); return 1; } if (!( nrrdTypeFloat == nin[0]->type || nrrdTypeDouble == nin[0]->type )) { biffAddf(TEN, "%s: need type %s or %s (not %s) in first nrrd", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeDouble), airEnumStr(nrrdType, nin[0]->type)); return 1; } for (ninIdx=1; ninIdxtype != nin[ninIdx]->type) { biffAddf(TEN, "%s: nin[0] type (%s) != nin[%u] type (%s)", me, airEnumStr(nrrdType, nin[0]->type), ninIdx, airEnumStr(nrrdType, nin[ninIdx]->type)); return 1; } } mop = airMopNew(); if (nrrdCopy(nout, nin[0])) { biffMovef(TEN, NRRD, "%s: couldn't initialize output", me); airMopError(mop); return 1; } if (_tip) { tip = _tip; } else { tip = tenInterpParmNew(); airMopAdd(mop, tip, (airMopper)tenInterpParmNix, airMopAlways); } tbuff = AIR_CAST(double *, calloc(7*ninLen, sizeof(double))); if (!tbuff) { biffAddf(TEN, "%s: couldn't allocate tensor buff", me); airMopError(mop); return 1; } ins = nrrdDInsert[nin[0]->type]; lup = nrrdDLookup[nin[0]->type]; NN = nrrdElementNumber(nin[0])/7; for (II=0; IIdata, tt + 7*II); } } if (tenInterpN_d(tenOut, tbuff, wght, ninLen, ptype, tip)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: trouble on sample %s", me, airSprintSize_t(stmp, II)); airMopError(mop); return 1; } for (tt=0; tt<7; tt++) { ins(nout->data, tt + 7*II, tenOut[tt]); } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/chan.c0000664000175000017500000010651412165631065016253 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" const char * tenDWMRIModalityKey = "modality"; const char * tenDWMRIModalityVal = "DWMRI"; const char * tenDWMRINAVal = "n/a"; const char * tenDWMRIBValueKey = "DWMRI_b-value"; const char * tenDWMRIGradKeyFmt = "DWMRI_gradient_%04u"; const char * tenDWMRIBmatKeyFmt = "DWMRI_B-matrix_%04u"; const char * tenDWMRINexKeyFmt = "DWMRI_NEX_%04u"; const char * tenDWMRISkipKeyFmt = "DWMRI_skip_%04u"; /* ******** tenDWMRIKeyValueParse ** ** Parses through key-value pairs in the NRRD header to determine the ** list of diffusion-sensitizing gradient directions, or B-matrices ** (depending to what was found), according the NAMIC conventions. ** This requires, among other things, that ndwi be have exactly one ** axis with kind nrrdKindList (or nrrdKindVector), which is taken to ** be the DWI axis. ** ** Either *ngradP or *nbmatP is set to a newly- allocated nrrd ** containing this information, and the other one is set to NULL ** The (scalar) b-value is stored in *bP. The image values that are ** to be skipped are stored in the *skipP array (allocated here), ** the length of that array is stored in *skipNumP. Unlike the skip ** array used internally with tenEstimate, this is just a simple 1-D ** array; it is not a list of pairs of (index,skipBool). */ int tenDWMRIKeyValueParse(Nrrd **ngradP, Nrrd **nbmatP, double *bP, unsigned int **skipP, unsigned int *skipNumP, const Nrrd *ndwi) { static const char me[]="tenDWMRIKeyValueParse"; char tmpKey[AIR_STRLEN_MED], key[AIR_STRLEN_MED], *val; const char *keyFmt; int dwiAxis; unsigned int axi, dwiIdx, dwiNum, valNum, valIdx, parsedNum, nexNum, nexIdx, skipIdx, *skipLut; Nrrd *ninfo; double *info, normMax, norm; airArray *mop, *skipArr; if (!( ngradP && nbmatP && skipP && skipNumP && bP && ndwi )) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } /* check modality */ val = nrrdKeyValueGet(ndwi, tenDWMRIModalityKey); if (!val) { biffAddf(TEN, "%s: didn't have \"%s\" key", me, tenDWMRIModalityKey); return 1; } if (strncmp(tenDWMRIModalityVal, val + strspn(val, AIR_WHITESPACE), strlen(tenDWMRIModalityVal))) { biffAddf(TEN, "%s: \"%s\" value was \"%s\", not \"%s\"", me, tenDWMRIModalityKey, val, tenDWMRIModalityVal); return 1; } val = (char *)airFree(val); /* learn b-value */ val = nrrdKeyValueGet(ndwi, tenDWMRIBValueKey); if (!val) { biffAddf(TEN, "%s: didn't have \"%s\" key", me, tenDWMRIBValueKey); return 1; } if (1 != sscanf(val, "%lg", bP)) { biffAddf(TEN, "%s: couldn't parse float from value \"%s\" " "for key \"%s\"", me, val, tenDWMRIBValueKey); return 1; } val = (char *)airFree(val); /* find single DWI axis, set dwiNum to its size */ dwiAxis = -1; for (axi=0; axidim; axi++) { /* the use of nrrdKindVector here is out of deference to how ITK's itkNrrdImageIO.cxx uses nrrdKindVector for VECTOR pixels */ if (nrrdKindList == ndwi->axis[axi].kind || nrrdKindVector == ndwi->axis[axi].kind) { if (-1 != dwiAxis) { biffAddf(TEN, "%s: already saw %s or %s kind on axis %d", me, airEnumStr(nrrdKind, nrrdKindList), airEnumStr(nrrdKind, nrrdKindVector), dwiAxis); return 1; } dwiAxis = axi; } } if (-1 == dwiAxis) { biffAddf(TEN, "%s: did not see \"%s\" kind on any axis", me, airEnumStr(nrrdKind, nrrdKindList)); return 1; } dwiNum = ndwi->axis[dwiAxis].size; /* figure out if we're parsing gradients or b-matrices */ sprintf(tmpKey, tenDWMRIGradKeyFmt, 0); val = nrrdKeyValueGet(ndwi, tmpKey); if (val) { valNum = 3; } else { valNum = 6; sprintf(key, tenDWMRIBmatKeyFmt, 0); val = nrrdKeyValueGet(ndwi, key); if (!val) { biffAddf(TEN, "%s: saw neither \"%s\" nor \"%s\" key", me, tmpKey, key); return 1; } } val = (char *)airFree(val); /* set up parsing and allocate one of output nrrds */ if (3 == valNum) { keyFmt = tenDWMRIGradKeyFmt; ninfo = *ngradP = nrrdNew(); *nbmatP = NULL; } else { keyFmt = tenDWMRIBmatKeyFmt; *ngradP = NULL; ninfo = *nbmatP = nrrdNew(); } if (nrrdMaybeAlloc_va(ninfo, nrrdTypeDouble, 2, AIR_CAST(size_t, valNum), AIR_CAST(size_t, dwiNum))) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); return 1; } info = (double *)(ninfo->data); /* set up skip list recording */ mop = airMopNew(); skipArr = airArrayNew((void**)skipP, skipNumP, sizeof(unsigned int), 16); airMopAdd(mop, skipArr, (airMopper)airArrayNix, airMopAlways); skipLut = AIR_CALLOC(dwiNum, unsigned int); airMopAdd(mop, skipLut, airFree, airMopAlways); if (!skipLut) { biffAddf(TEN, "%s: couldn't allocate skip LUT", me); airMopError(mop); return 1; } /* parse values in ninfo */ for (dwiIdx=0; dwiIdx= 1 )) { biffAddf(TEN, "%s: NEX (%d) for DWI %d not >= 1", me, nexNum, dwiIdx); airMopError(mop); return 1; } if (!( dwiIdx + nexNum - 1 < dwiNum )) { biffAddf(TEN, "%s: NEX %d for DWI %d implies %d DWI > real # DWI %d", me, nexNum, dwiIdx, dwiIdx + nexNum, dwiNum); airMopError(mop); return 1; } for (nexIdx=1; nexIdxdata); for (dwiIdx=0; dwiIdxdata); for (dwiIdx=0; dwiIdxaxis[1].size)) { biffMovef(TEN, NRRD, "%s: trouble", me); airMopError(mop); return 1; } DD = ngrad->axis[1].size; G = (double*)(ngrad->data); bmat = (double*)(nbmat->data); for (dd=0; ddaxis[0].kind = nrrdKind3DSymMatrix; airMopOkay(mop); return 0; } /* ******** tenEMatrixCalc ** */ int tenEMatrixCalc(Nrrd *nemat, const Nrrd *_nbmat, int knownB0) { static const char me[]="tenEMatrixCalc"; Nrrd *nbmat, *ntmp; airArray *mop; ptrdiff_t padmin[2], padmax[2]; unsigned int ri; double *bmat; if (!(nemat && _nbmat)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tenBMatrixCheck(_nbmat, nrrdTypeDefault, 6)) { biffAddf(TEN, "%s: problem with B matrix", me); return 1; } mop = airMopNew(); airMopAdd(mop, nbmat=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (knownB0) { if (nrrdConvert(nbmat, _nbmat, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: couldn't convert given bmat to doubles", me); airMopError(mop); return 1; } } else { airMopAdd(mop, ntmp=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(ntmp, _nbmat, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: couldn't convert given bmat to doubles", me); airMopError(mop); return 1; } ELL_2V_SET(padmin, 0, 0); ELL_2V_SET(padmax, 6, _nbmat->axis[1].size-1); if (nrrdPad_nva(nbmat, ntmp, padmin, padmax, nrrdBoundaryPad, -1)) { biffMovef(TEN, NRRD, "%s: couldn't pad given bmat", me); airMopError(mop); return 1; } } bmat = (double*)(nbmat->data); /* HERE is where the off-diagonal elements get multiplied by 2 */ for (ri=0; riaxis[1].size; ri++) { bmat[1] *= 2; bmat[2] *= 2; bmat[4] *= 2; bmat += nbmat->axis[0].size; } if (ell_Nm_pseudo_inv(nemat, nbmat)) { biffMovef(TEN, ELL, "%s: trouble pseudo-inverting B-matrix", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } /* ******** tenEstimateLinearSingle_d ** ** estimate one single tensor ** ** !! requires being passed a pre-allocated double array "vbuf" which is ** !! used for intermediate calculations (details below) ** ** DD is always the length of the dwi[] array ** ** -------------- IF knownB0 ------------------------- ** input: ** dwi[0] is the B0 image, dwi[1]..dwi[DD-1] are the (DD-1) DWI values, ** emat is the (DD-1)-by-6 estimation matrix, which is the pseudo-inverse ** of the B-matrix (after the off-diagonals have been multiplied by 2). ** vbuf[] is allocated for (at least) DD-1 doubles (DD is fine) ** ** output: ** ten[0]..ten[6] will be the confidence value followed by the tensor ** if B0P, then *B0P is set to the B0 value used in calcs: max(b0,1) ** -------------- IF !knownB0 ------------------------- ** input: ** dwi[0]..dwi[DD-1] are the DD DWI values, emat is the DD-by-7 estimation ** matrix. The 7th column is for estimating the B0 image. ** vbuf[] is allocated for DD doubles ** ** output: ** ten[0]..ten[6] will be the confidence value followed by the tensor ** if B0P, then *B0P is set to estimated B0 value. ** ---------------------------------------------------- */ void tenEstimateLinearSingle_d(double *ten, double *B0P, /* output */ const double *dwi, const double *emat, /* input .. */ double *vbuf, unsigned int DD, int knownB0, double thresh, double soft, double b) { double logB0, tmp, mean; unsigned int ii, jj; /* static const char me[]="tenEstimateLinearSingle_d"; */ if (knownB0) { if (B0P) { /* we save this as a courtesy */ *B0P = AIR_MAX(dwi[0], 1); } logB0 = log(AIR_MAX(dwi[0], 1)); mean = 0; for (ii=1; ii thresh; } for (jj=0; jj<6; jj++) { tmp = 0; for (ii=0; ii thresh; } for (jj=0; jj<7; jj++) { tmp = 0; for (ii=0; ii DWI_NUM_MAX) { fprintf(stderr, "%s: PANIC: sorry, DD=%u > compile-time DWI_NUM_MAX=%u\n", me, DD, DWI_NUM_MAX); exit(1); } for (dwiIdx=0; dwiIdxdim && 7 <= ndwi->axis[0].size )) { biffAddf(TEN, "%s: dwi should be 4-D array with axis 0 size >= 7", me); return 1; } if (tenBMatrixCheck(_nbmat, nrrdTypeDefault, 6)) { biffAddf(TEN, "%s: problem with B matrix", me); return 1; } if (knownB0) { if (!( ndwi->axis[0].size == 1 + _nbmat->axis[1].size )) { biffAddf(TEN, "%s: (knownB0 == true) # input images (%s) " "!= 1 + # B matrix rows (1+%s)", me, airSprintSize_t(stmp[0], ndwi->axis[0].size), airSprintSize_t(stmp[1], _nbmat->axis[1].size)); return 1; } } else { if (!( ndwi->axis[0].size == _nbmat->axis[1].size )) { biffAddf(TEN, "%s: (knownB0 == false) # dwi (%s) " "!= # B matrix rows (%s)", me, airSprintSize_t(stmp[0], ndwi->axis[0].size), airSprintSize_t(stmp[1], _nbmat->axis[1].size)); return 1; } } DD = ndwi->axis[0].size; sx = ndwi->axis[1].size; sy = ndwi->axis[2].size; sz = ndwi->axis[3].size; mop = airMopNew(); airMopAdd(mop, nbmat=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nbmat, _nbmat, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: trouble converting to doubles", me); airMopError(mop); return 1; } airMopAdd(mop, nemat=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (tenEMatrixCalc(nemat, nbmat, knownB0)) { biffAddf(TEN, "%s: trouble computing estimation matrix", me); airMopError(mop); return 1; } vbuf = AIR_CALLOC(knownB0 ? DD-1 : DD, double); dwi1 = AIR_CALLOC(DD, float); dwi2 = AIR_CALLOC(knownB0 ? DD : DD+1, float); airMopAdd(mop, vbuf, airFree, airMopAlways); airMopAdd(mop, dwi1, airFree, airMopAlways); airMopAdd(mop, dwi2, airFree, airMopAlways); if (!(vbuf && dwi1 && dwi2)) { biffAddf(TEN, "%s: couldn't allocate temp buffers", me); airMopError(mop); return 1; } if (!AIR_EXISTS(thresh)) { airMopAdd(mop, ncrop=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nhist=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); ELL_4V_SET(cmin, knownB0 ? 1 : 0, 0, 0, 0); ELL_4V_SET(cmax, DD-1, sx-1, sy-1, sz-1); E = 0; if (!E) E |= nrrdCrop(ncrop, ndwi, cmin, cmax); if (!E) range = nrrdRangeNewSet(ncrop, nrrdBlind8BitRangeState); if (!E) airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); if (!E) E |= nrrdHisto(nhist, ncrop, range, NULL, (int)AIR_MIN(1024, range->max - range->min + 1), nrrdTypeFloat); if (E) { biffMovef(TEN, NRRD, "%s: trouble histograming to find DW threshold", me); airMopError(mop); return 1; } if (_tenFindValley(&thresh, nhist, 0.75, AIR_FALSE)) { biffAddf(TEN, "%s: problem finding DW histogram valley", me); airMopError(mop); return 1; } fprintf(stderr, "%s: using %g for DW confidence threshold\n", me, thresh); } if (nrrdMaybeAlloc_va(nten, nrrdTypeFloat, 4, AIR_CAST(size_t, 7), sx, sy, sz)) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); airMopError(mop); return 1; } if (nterrP) { if (!(*nterrP)) { *nterrP = nrrdNew(); } if (nrrdMaybeAlloc_va(*nterrP, nrrdTypeFloat, 3, sx, sy, sz)) { biffAddf(NRRD, "%s: couldn't allocate error output", me); airMopError(mop); return 1; } airMopAdd(mop, nterrP, (airMopper)airSetNull, airMopOnError); airMopAdd(mop, *nterrP, (airMopper)nrrdNuke, airMopOnError); terr = (float*)((*nterrP)->data); } else { terr = NULL; } if (nB0P) { if (!(*nB0P)) { *nB0P = nrrdNew(); } if (nrrdMaybeAlloc_va(*nB0P, nrrdTypeFloat, 3, sx, sy, sz)) { biffAddf(NRRD, "%s: couldn't allocate error output", me); airMopError(mop); return 1; } airMopAdd(mop, nB0P, (airMopper)airSetNull, airMopOnError); airMopAdd(mop, *nB0P, (airMopper)nrrdNuke, airMopOnError); B0 = (float*)((*nB0P)->data); } else { B0 = NULL; } bmat = (double*)(nbmat->data); emat = (double*)(nemat->data); ten = (float*)(nten->data); lup = nrrdFLookup[ndwi->type]; for (II=0; IIdata, d + DD*II); /* if (tenVerbose) { fprintf(stderr, "%s: input dwi1[%d] = %g\n", me, d, dwi1[d]); } */ } tenEstimateLinearSingle_f(ten, &_B0, dwi1, emat, vbuf, DD, knownB0, AIR_CAST(float, thresh), AIR_CAST(float, soft), AIR_CAST(float, b)); if (nB0P) { *B0 = _B0; } /* if (tenVerbose) { fprintf(stderr, "%s: output ten = (%g) %g,%g,%g %g,%g %g\n", me, ten[0], ten[1], ten[2], ten[3], ten[4], ten[5], ten[6]); } */ if (nterrP) { te = 0; if (knownB0) { tenSimulateSingle_f(dwi2, _B0, ten, bmat, DD, AIR_CAST(float, b)); for (d=1; daxis[0].kind = nrrdKind3DMaskedSymMatrix; if (nrrdBasicInfoCopy(nten, ndwi, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { biffAddf(NRRD, "%s:", me); return 1; } airMopOkay(mop); return 0; } /* ******** tenSimulateSingle_f ** ** given a tensor, simulate the set of diffusion weighted measurements ** represented by the given B matrix ** ** NOTE: the mindset of this function is very much "knownB0==true": ** B0 is required as an argument (and its always copied to dwi[0]), ** and the given bmat is assumed to have DD-1 rows (similar to how ** tenEstimateLinearSingle_f() is set up), and dwi[1] through dwi[DD-1] ** are set to the calculated DWIs. ** ** So: dwi must be allocated for DD values total */ void tenSimulateSingle_f(float *dwi, float B0, const float *ten, const double *bmat, unsigned int DD, float b) { double vv; /* this is how we multiply the off-diagonal entries by 2 */ double matwght[6] = {1, 2, 2, 1, 2, 1}; unsigned int ii, jj; dwi[0] = B0; /* if (tenVerbose) { fprintf(stderr, "ten = %g,%g,%g %g,%g %g\n", ten[1], ten[2], ten[3], ten[4], ten[5], ten[6]); } */ for (ii=0; ii dwi = %g\n", ii, vv, dwi[ii+1]); } */ } return; } int tenSimulate(Nrrd *ndwi, const Nrrd *nT2, const Nrrd *nten, const Nrrd *_nbmat, double b) { static const char me[]="tenSimulate"; size_t II; Nrrd *nbmat; size_t DD, sx, sy, sz; airArray *mop; double *bmat; float *dwi, *ten, (*lup)(const void *, size_t I); char stmp[6][AIR_STRLEN_SMALL]; if (!ndwi || !nT2 || !nten || !_nbmat || tenTensorCheck(nten, nrrdTypeFloat, AIR_TRUE, AIR_TRUE) || tenBMatrixCheck(_nbmat, nrrdTypeDefault, 6)) { biffAddf(TEN, "%s: got NULL pointer or invalid args", me); return 1; } mop = airMopNew(); airMopAdd(mop, nbmat=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nbmat, _nbmat, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: couldn't convert B matrix", me); return 1; } DD = nbmat->axis[1].size+1; sx = nT2->axis[0].size; sy = nT2->axis[1].size; sz = nT2->axis[2].size; if (!(3 == nT2->dim && sx == nten->axis[1].size && sy == nten->axis[2].size && sz == nten->axis[3].size)) { biffAddf(TEN, "%s: dimensions of %u-D T2 volume (%s,%s,%s) " "don't match tensor volume (%s,%s,%s)", me, nT2->dim, airSprintSize_t(stmp[0], sx), airSprintSize_t(stmp[1], sy), airSprintSize_t(stmp[2], sz), airSprintSize_t(stmp[3], nten->axis[1].size), airSprintSize_t(stmp[4], nten->axis[2].size), airSprintSize_t(stmp[5], nten->axis[3].size)); return 1; } if (nrrdMaybeAlloc_va(ndwi, nrrdTypeFloat, 4, AIR_CAST(size_t, DD), sx, sy, sz)) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); return 1; } dwi = (float*)(ndwi->data); ten = (float*)(nten->data); bmat = (double*)(nbmat->data); lup = nrrdFLookup[nT2->type]; for (II=0; II<(size_t)(sx*sy*sz); II++) { /* tenVerbose = (II == 42 + 190*(96 + 196*0)); */ tenSimulateSingle_f(dwi, lup(nT2->data, II), ten, bmat, DD, AIR_CAST(float, b)); dwi += DD; ten += 7; } airMopOkay(mop); return 0; } /* old stuff, prior to tenEstimationMatrix .. */ /* ******** tenCalcOneTensor1 ** ** make one diffusion tensor from the measurements at one voxel, based ** on the gradient directions used by Andy Alexander */ void tenCalcOneTensor1(float tens[7], float chan[7], float thresh, float slope, float b) { double c[7], sum, d1, d2, d3, d4, d5, d6; c[0] = AIR_MAX(chan[0], 1); c[1] = AIR_MAX(chan[1], 1); c[2] = AIR_MAX(chan[2], 1); c[3] = AIR_MAX(chan[3], 1); c[4] = AIR_MAX(chan[4], 1); c[5] = AIR_MAX(chan[5], 1); c[6] = AIR_MAX(chan[6], 1); sum = c[1] + c[2] + c[3] + c[4] + c[5] + c[6]; tens[0] = AIR_CAST(float, (1 + airErf(slope*(sum - thresh)))/2.0); d1 = (log(c[0]) - log(c[1]))/b; d2 = (log(c[0]) - log(c[2]))/b; d3 = (log(c[0]) - log(c[3]))/b; d4 = (log(c[0]) - log(c[4]))/b; d5 = (log(c[0]) - log(c[5]))/b; d6 = (log(c[0]) - log(c[6]))/b; tens[1] = AIR_CAST(float, d1 + d2 - d3 - d4 + d5 + d6); /* Dxx */ tens[2] = AIR_CAST(float, d5 - d6); /* Dxy */ tens[3] = AIR_CAST(float, d1 - d2); /* Dxz */ tens[4] = AIR_CAST(float, -d1 - d2 + d3 + d4 + d5 + d6); /* Dyy */ tens[5] = AIR_CAST(float, d3 - d4); /* Dyz */ tens[6] = AIR_CAST(float, d1 + d2 + d3 + d4 - d5 - d6); /* Dzz */ return; } /* ******** tenCalcOneTensor2 ** ** using gradient directions used by EK */ void tenCalcOneTensor2(float tens[7], float chan[7], float thresh, float slope, float b) { double c[7], sum, d1, d2, d3, d4, d5, d6; c[0] = AIR_MAX(chan[0], 1); c[1] = AIR_MAX(chan[1], 1); c[2] = AIR_MAX(chan[2], 1); c[3] = AIR_MAX(chan[3], 1); c[4] = AIR_MAX(chan[4], 1); c[5] = AIR_MAX(chan[5], 1); c[6] = AIR_MAX(chan[6], 1); sum = c[1] + c[2] + c[3] + c[4] + c[5] + c[6]; tens[0] = AIR_CAST(float, (1 + airErf(slope*(sum - thresh)))/2.0); d1 = (log(c[0]) - log(c[1]))/b; d2 = (log(c[0]) - log(c[2]))/b; d3 = (log(c[0]) - log(c[3]))/b; d4 = (log(c[0]) - log(c[4]))/b; d5 = (log(c[0]) - log(c[5]))/b; d6 = (log(c[0]) - log(c[6]))/b; tens[1] = AIR_CAST(float, d1); /* Dxx */ tens[2] = AIR_CAST(float, d6 - (d1 + d2)/2); /* Dxy */ tens[3] = AIR_CAST(float, d5 - (d1 + d3)/2); /* Dxz */ tens[4] = AIR_CAST(float, d2); /* Dyy */ tens[5] = AIR_CAST(float, d4 - (d2 + d3)/2); /* Dyz */ tens[6] = AIR_CAST(float, d3); /* Dzz */ return; } /* ******** tenCalcTensor ** ** Calculate a volume of tensors from measured data */ int tenCalcTensor(Nrrd *nout, Nrrd *nin, int version, float thresh, float slope, float b) { static const char me[] = "tenCalcTensor"; char cmt[128]; float *out, tens[7], chan[7]; size_t I, sx, sy, sz; void (*calcten)(float tens[7], float chan[7], float thresh, float slope, float b); if (!(nout && nin)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( 1 == version || 2 == version )) { biffAddf(TEN, "%s: version should be 1 or 2, not %d", me, version); return 1; } switch (version) { case 1: calcten = tenCalcOneTensor1; break; case 2: calcten = tenCalcOneTensor2; break; default: biffAddf(TEN, "%s: PANIC, version = %d not handled", me, version); return 1; break; } if (tenTensorCheck(nin, nrrdTypeDefault, AIR_TRUE, AIR_TRUE)) { biffAddf(TEN, "%s: wasn't given valid tensor nrrd", me); return 1; } sx = nin->axis[1].size; sy = nin->axis[2].size; sz = nin->axis[3].size; if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 4, AIR_CAST(size_t, 7), sx, sy, sz)) { biffMovef(TEN, NRRD, "%s: couldn't alloc output", me); return 1; } nout->axis[0].label = airStrdup("c,Dxx,Dxy,Dxz,Dyy,Dyz,Dzz"); nout->axis[1].label = airStrdup("x"); nout->axis[2].label = airStrdup("y"); nout->axis[3].label = airStrdup("z"); nout->axis[0].spacing = AIR_NAN; if (AIR_EXISTS(nin->axis[1].spacing) && AIR_EXISTS(nin->axis[2].spacing) && AIR_EXISTS(nin->axis[3].spacing)) { nout->axis[1].spacing = nin->axis[1].spacing; nout->axis[2].spacing = nin->axis[2].spacing; nout->axis[3].spacing = nin->axis[3].spacing; } else { nout->axis[1].spacing = 1.0; nout->axis[2].spacing = 1.0; nout->axis[3].spacing = 1.0; } sprintf(cmt, "%s: using thresh = %g, slope = %g, b = %g\n", me, thresh, slope, b); nrrdCommentAdd(nout, cmt); out = (float *)nout->data; for (I=0; I<(size_t)(sx*sy*sz); I++) { if (tenVerbose && !(I % (sx*sy))) { fprintf(stderr, "%s: z = %d of %d\n", me, (int)(I/(sx*sy)), (int)sz-1); } chan[0] = nrrdFLookup[nin->type](nin->data, 0 + 7*I); chan[1] = nrrdFLookup[nin->type](nin->data, 1 + 7*I); chan[2] = nrrdFLookup[nin->type](nin->data, 2 + 7*I); chan[3] = nrrdFLookup[nin->type](nin->data, 3 + 7*I); chan[4] = nrrdFLookup[nin->type](nin->data, 4 + 7*I); chan[5] = nrrdFLookup[nin->type](nin->data, 5 + 7*I); chan[6] = nrrdFLookup[nin->type](nin->data, 6 + 7*I); calcten(tens, chan, thresh, slope, b); out[0 + 7*I] = tens[0]; out[1 + 7*I] = tens[1]; out[2 + 7*I] = tens[2]; out[3 + 7*I] = tens[3]; out[4 + 7*I] = tens[4]; out[5 + 7*I] = tens[5]; out[6 + 7*I] = tens[6]; } return 0; } teem-1.11.0~svn6057/src/ten/model1Vector2D.c0000664000175000017500000000440612165631065020071 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define PARM_NUM 3 static const tenModelParmDesc parmDesc[] = { /* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 1 */ {"r", 0.0, 5.0, AIR_FALSE, AIR_FALSE, 0}, /* 2 */ {"th", 0, 2*AIR_PI, AIR_TRUE, AIR_FALSE, 0} }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; double rad, theta, vec[3]; /* not used: b0 = parm[0]; */ rad = parm[1]; theta = parm[2]; ELL_3V_SET(vec, rad*cos(theta), rad*sin(theta), 0.0); for (ii=0; iiimgNum; ii++) { dwiSim[ii] = ELL_3V_DOT(vec, espec->grad + 3*ii); } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { sprintf(str, "(%g) r=%g th=%g", parm[0], parm[1], parm[2]); return str; } _TEN_PARM_ALLOC _TEN_PARM_RAND _TEN_PARM_STEP _TEN_PARM_DIST _TEN_PARM_COPY _TEN_PARM_CONVERT_NOOP _TEN_SQE _TEN_SQE_GRAD_CENTDIFF _TEN_SQE_FIT(tenModel1Vector2D) _TEN_NLL _TEN_NLL_GRAD_STUB _TEN_NLL_FIT_STUB tenModel _tenModel1Vector2D = { TEN_MODEL_STR_1VECTOR2D, _TEN_MODEL_FIELDS }; const tenModel *const tenModel1Vector2D = &_tenModel1Vector2D; teem-1.11.0~svn6057/src/ten/modelBall1Stick.c0000664000175000017500000000546612165631065020320 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define PARM_NUM 7 static const tenModelParmDesc parmDesc[] = { /* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 1 */ {"diff_ball", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 2 */ {"fraction", 0.0, 1.0, AIR_FALSE, AIR_FALSE, 0}, /* 3 */ {"diff_stick", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 4 */ {"x", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 0}, /* 5 */ {"y", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 1}, /* 6 */ {"z", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 2} }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; double b0, diffBall, diffStick, frac, vec[3]; b0 = parm[0]; diffBall = parm[1]; frac = parm[2]; diffStick = parm[3]; vec[0] = parm[4]; vec[1] = parm[5]; vec[2] = parm[6]; for (ii=0; iiimgNum; ii++) { double dwiBall, dwiStick, dot, bb; bb = espec->bval[ii]; dwiBall = exp(-bb*diffBall); dot = ELL_3V_DOT(vec, espec->grad + 3*ii); dwiStick = exp(-bb*diffStick*dot*dot); dwiSim[ii] = b0*(AIR_LERP(frac, dwiBall, dwiStick)); } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { sprintf(str, "(%g) (1-f)*%g + (f=%g)*(%g @ (%g,%g,%g))", parm[0], parm[1], parm[2], parm[3], parm[4], parm[5], parm[6]); return str; } _TEN_PARM_ALLOC _TEN_PARM_RAND _TEN_PARM_STEP _TEN_PARM_DIST _TEN_PARM_COPY _TEN_PARM_CONVERT_NOOP _TEN_SQE _TEN_SQE_GRAD_CENTDIFF _TEN_SQE_FIT(tenModelBall1Stick) _TEN_NLL _TEN_NLL_GRAD_STUB _TEN_NLL_FIT_STUB tenModel _tenModelBall1Stick = { TEN_MODEL_STR_BALL1STICK, _TEN_MODEL_FIELDS }; const tenModel *const tenModelBall1Stick = &_tenModelBall1Stick; teem-1.11.0~svn6057/src/ten/tendAnvol.c0000664000175000017500000000554112165631065017272 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Apply an anisotropy metric to a DT volume" static const char *_tend_anvolInfoL = (INFO ". The anisotropy value will be zero in the locations which " "don't meet the given confidence threshold."); int tend_anvolMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; int aniso; Nrrd *nin, *nout; char *outS; float thresh; hestOptAdd(&hopt, "a", "aniso", airTypeEnum, 1, 1, &aniso, NULL, "Which anisotropy metric to plot. " TEN_ANISO_DESC, NULL, tenAniso); hestOptAdd(&hopt, "t", "thresh", airTypeFloat, 1, 1, &thresh, "0.5", "confidence threshold"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_anvolInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenAnisoVolume(nout, nin, aniso, thresh)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making aniso volume:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(anvol, INFO); teem-1.11.0~svn6057/src/ten/tendEvaladd.c0000664000175000017500000000533412165631065017553 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Modify shape by adding a constant to all eigenvalues" static const char *_tend_evaladdInfoL = (INFO ". The orientation of the tensor is unchanged. Note that unlike " "\"tend anscale\", this operation can completely change the shape " "of the tensor."); int tend_evaladdMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS; float val; hestOptAdd(&hopt, "v", "value", airTypeFloat, 1, 1, &val, NULL, "Value to add to all eigenvalues"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_evaladdInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenEigenvalueAdd(nout, nin, val)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(evaladd, INFO); teem-1.11.0~svn6057/src/ten/tendEvalclamp.c0000664000175000017500000000617612165631065020124 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Modify shape by clamping eigenvalues in some range" static const char *_tend_evalclampInfoL = (INFO ". The orientation of the tensor is unchanged. Note that unlike " "\"tend anscale\", this operation can completely change the shape " "of the tensor."); int tend_evalclampMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS; float min, max; hestOptAdd(&hopt, "min", "min", airTypeFloat, 1, 1, &min, NULL, "Eigenvalues are clamped from below by this (the minimum " "output eigenvalue). Use \"nan\" to signify that no " "minimum clamping should be done."); hestOptAdd(&hopt, "max", "max", airTypeFloat, 1, 1, &max, "nan", "Eigenvalues are clamped from above by this (the maximum " "output eigenvalue). Use \"nan\" to signify that no " "maximum clamping should be done."); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_evalclampInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenEigenvalueClamp(nout, nin, min, max)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(evalclamp, INFO); teem-1.11.0~svn6057/src/ten/tendNorm.c0000664000175000017500000000630312165631065017123 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Normalize tensor size" static const char *_tend_normInfoL = (INFO ". This operates on the eigenvalues of the tensor, and allows " "normalizing some user-defined weighting (\"-w\") of the eigenvalues by " "some user-defined amount (\"-a\")."); int tend_normMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd *nin, *nout; char *outS; float amount, target; double weight[3]; hestOptAdd(&hopt, "w", "w0 w1 w2", airTypeDouble, 3, 3, weight, NULL, "relative weights to put on major, medium, and minor " "eigenvalue when performing normalization (internally " "rescaled to have a 1.0 L1 norm). These weightings determine " "the tensors's \"size\"."); hestOptAdd(&hopt, "a", "amount", airTypeFloat, 1, 1, &amount, "1.0", "how much of the normalization to perform"); hestOptAdd(&hopt, "t", "target", airTypeFloat, 1, 1, &target, "1.0", "target size, post normalization"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_normInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (tenSizeNormalize(nout, nin, weight, amount, target)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(norm, INFO); teem-1.11.0~svn6057/src/ten/test/0000775000175000017500000000000012203513756016145 5ustar domibeldomibelteem-1.11.0~svn6057/src/ten/test/ttriple.c0000664000175000017500000000536212042367142017777 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" char *info = ("tests conversions between info triples"); int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt=NULL; airArray *mop; int *itype, itypeNum, ii; double src[3], last[3], dst[3]; char space[] = " "; me = argv[0]; hestOptAdd(&hopt, NULL, "v1 v2 v3", airTypeDouble, 3, 3, src, NULL, "source triple"); hestOptAdd(&hopt, "t", "type", airTypeEnum, 2, -1, &itype, NULL, "sequence of triple types to convert through", &itypeNum, tenTripleType); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); printf("%s", space + strlen(airEnumStr(tenTripleType, itype[0]))); printf("%s", airEnumStr(tenTripleType, itype[0])); ell_3v_print_d(stdout, src); ELL_3V_COPY(last, src); for (ii=1; ii %s --> %s\n", me, tenTriple->name, airEnumStr(tenTriple, srcType), airEnumStr(tenTriple, dstType), airEnumStr(tenTriple, srcType)); ell_3v_print_d(stdout, src); ell_3v_print_d(stdout, dst); ell_3v_print_d(stdout, tst); */ airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/test/geode.c0000664000175000017500000003047412042367142017401 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" char *info = ("does geodesics"); int main(int argc, const char *argv[]) { const char *me; char *err; hestOpt *hopt=NULL; airArray *mop; char *outS; double _tA[6], tA[7], _tB[6], tB[7], time0, time1, conv, confThresh, pA[3], pB[3], qA[4], qB[4], rA[9], rB[9], mat1[9], mat2[9], tmp, stepSize, minNorm, sclA, sclB; unsigned int NN, maxiter, refIdx[3]; int recurse, ptype, verb; Nrrd *_nin, *nin, *nout; tenInterpParm *tip; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "a", "tensor", airTypeDouble, 6, 6, _tA, "1 0 0 1 0 1", "first tensor"); hestOptAdd(&hopt, "pa", "qq", airTypeDouble, 3, 3, pA, "0 0 0", "rotation of first tensor"); hestOptAdd(&hopt, "sa", "scl", airTypeDouble, 1, 1, &sclA, "1.0", "scaling of first tensor"); hestOptAdd(&hopt, "b", "tensor", airTypeDouble, 6, 6, _tB, "1 0 0 1 0 1", "second tensor"); hestOptAdd(&hopt, "pb", "qq", airTypeDouble, 3, 3, pB, "0 0 0", "rotation of second tensor"); hestOptAdd(&hopt, "sb", "scl", airTypeDouble, 1, 1, &sclB, "1.0", "scaling of second tensor"); hestOptAdd(&hopt, "i", "nten", airTypeOther, 1, 1, &_nin, "", "input tensor volume (makes previous options moot)", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "ri", "x y z", airTypeUInt, 3, 3, refIdx, "0 0 0", "index of reference tensor in input tensor volume"); hestOptAdd(&hopt, "th", "thresh", airTypeDouble, 1, 1, &confThresh, "0.5", "conf mask threshold on \"-i\""); hestOptAdd(&hopt, "n", "# steps", airTypeUInt, 1, 1, &NN, "100", "number of steps in between two tensors"); hestOptAdd(&hopt, "s", "stepsize", airTypeDouble, 1, 1, &stepSize, "1", "step size in update"); hestOptAdd(&hopt, "mn", "minnorm", airTypeDouble, 1, 1, &minNorm, "0.000001", "minnorm of something"); hestOptAdd(&hopt, "c", "conv", airTypeDouble, 1, 1, &conv, "0.0001", "convergence threshold of length fraction"); hestOptAdd(&hopt, "mi", "maxiter", airTypeUInt, 1, 1, &maxiter, "0", "if non-zero, max # iterations for computation"); hestOptAdd(&hopt, "r", "recurse", airTypeInt, 0, 0, &recurse, NULL, "enable recursive solution, when useful"); hestOptAdd(&hopt, "t", "path type", airTypeEnum, 1, 1, &ptype, "lerp", "what type of path to compute", NULL, tenInterpType); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "file to write output nrrd to"); hestOptAdd(&hopt, "v", "verbosity", airTypeInt, 1, 1, &verb, "0", "verbosity"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); tip = tenInterpParmNew(); airMopAdd(mop, tip, (airMopper)tenInterpParmNix, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); tip->verbose = verb; tip->convStep = stepSize; tip->enableRecurse = recurse; tip->minNorm = minNorm; tip->maxIter = maxiter; tip->convEps = conv; if (_nin) { double refTen[7], inTen[7], *in, *out; unsigned int xi, yi, zi, sx, sy, sz, dimOut; int axmap[NRRD_DIM_MAX], numerical; size_t size[NRRD_DIM_MAX]; if (tenTensorCheck(_nin, nrrdTypeDefault, AIR_TRUE, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: input volume not valid:\n%s\n", me, err); airMopError(mop); return 1; } sx = AIR_CAST(unsigned int, _nin->axis[1].size); sy = AIR_CAST(unsigned int, _nin->axis[2].size); sz = AIR_CAST(unsigned int, _nin->axis[3].size); if (!( refIdx[0] < sx && refIdx[1] < sy && refIdx[2] < sz )) { fprintf(stderr, "%s: index (%u,%u,%u) out of bounds (%u,%u,%u)\n", me, refIdx[0], refIdx[1], refIdx[2], sx, sy, sz); airMopError(mop); return 1; } nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); numerical = (ptype == tenInterpTypeGeoLoxK || ptype == tenInterpTypeGeoLoxR || ptype == tenInterpTypeLoxK || ptype == tenInterpTypeLoxR || ptype == tenInterpTypeQuatGeoLoxK || ptype == tenInterpTypeQuatGeoLoxR); if (numerical) { tip->lengthFancy = AIR_TRUE; dimOut = 4; size[0] = 3; size[1] = _nin->axis[1].size; size[2] = _nin->axis[2].size; size[3] = _nin->axis[3].size; axmap[0] = -1; axmap[1] = 1; axmap[2] = 2; axmap[3] = 3; } else { dimOut = 3; size[0] = _nin->axis[1].size; size[1] = _nin->axis[2].size; size[2] = _nin->axis[3].size; axmap[0] = 1; axmap[1] = 2; axmap[2] = 3; } if (nrrdConvert(nin, _nin, nrrdTypeDouble) || nrrdMaybeAlloc_nva(nout, nrrdTypeDouble, dimOut, size) || nrrdAxisInfoCopy(nout, nin, axmap, NRRD_AXIS_INFO_SIZE_BIT) || nrrdBasicInfoCopy(nout, nin, (NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_SAMPLEUNITS_BIT))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } in = AIR_CAST(double *, nin->data); out = AIR_CAST(double *, nout->data); TEN_T_COPY(refTen, in + 7*(refIdx[0] + sx*(refIdx[1] + sy*refIdx[2]))); fprintf(stderr, "!%s: reference tensor = (%g) %g %g %g %g %g %g\n", me, refTen[0], refTen[1], refTen[2], refTen[3], refTen[4], refTen[5], refTen[6]); for (zi=0; ziverbose = 10*(xi == refIdx[0] && yi == refIdx[1] && zi == refIdx[2]); out[0] = tenInterpDistanceTwo_d(inTen, refTen, ptype, tip); out[1] = tip->lengthShape; out[2] = tip->lengthOrient; } out += 3; } else { if (inTen[0] < confThresh) { *out = AIR_NAN; } else { *out = tenInterpDistanceTwo_d(inTen, refTen, ptype, tip); } out += 1; } } if (numerical) { if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving output:\n%s\n", me, err); airMopError(mop); return 1; } } } } } else { /* only doing the path between two specified tensors */ ELL_6V_COPY(tA + 1, _tA); tA[0] = 1.0; TEN_T_SCALE(tA, sclA, tA); ELL_6V_COPY(tB + 1, _tB); tB[0] = 1.0; TEN_T_SCALE(tB, sclB, tB); ELL_4V_SET(qA, 1, pA[0], pA[1], pA[2]); ELL_4V_NORM(qA, qA, tmp); ELL_4V_SET(qB, 1, pB[0], pB[1], pB[2]); ELL_4V_NORM(qB, qB, tmp); ell_q_to_3m_d(rA, qA); ell_q_to_3m_d(rB, qB); TEN_T2M(mat1, tA); ell_3m_mul_d(mat2, rA, mat1); ELL_3M_TRANSPOSE_IP(rA, tmp); ell_3m_mul_d(mat1, mat2, rA); TEN_M2T(tA, mat1); TEN_T2M(mat1, tB); ell_3m_mul_d(mat2, rB, mat1); ELL_3M_TRANSPOSE_IP(rB, tmp); ell_3m_mul_d(mat1, mat2, rB); TEN_M2T(tB, mat1); /* fprintf(stderr, "!%s: tA = (%g) %g %g %g\n %g %g\n %g\n", me, tA[0], tA[1], tA[2], tA[3], tA[4], tA[5], tA[6]); fprintf(stderr, "!%s: tB = (%g) %g %g %g\n %g %g\n %g\n", me, tB[0], tB[1], tB[2], tB[3], tB[4], tB[5], tB[6]); */ time0 = airTime(); if (tenInterpTwoDiscrete_d(nout, tA, tB, ptype, NN, tip)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble computing path:\n%s\n", me, err); airMopError(mop); return 1; } fprintf(stderr, "!%s: ------- # iter = %u, conv = %g\n", me, tip->numIter, tip->convFinal); time1 = airTime(); fprintf(stderr, "%s: path length = %g; time = %g\n", me, tenInterpPathLength(nout, AIR_FALSE, AIR_FALSE, AIR_FALSE), time1 - time0); if (1) { double *geod, eval0[3], eval[3], evec0[9], evec[9], rot[9], diff[7], nrm, tmp, axis[3], angle; unsigned int ii, NN; NN = AIR_CAST(unsigned int, nout->axis[1].size); geod = AIR_CAST(double *, nout->data); geod += 7; for (ii=1; iidim && 21 == _ncov->axis[0].size)) { fprintf(stderr, "%s: didn't get a 4-D 21-by-X volume (got %u-D %u-by-X)\n", me, _ncov->dim, AIR_CAST(unsigned int, _ncov->axis[0].size)); airMopError(mop); return 1; } if (!(nrrdElementNumber(_ncov)/21 == nrrdElementNumber(_nten[0])/7 && nrrdElementNumber(_nten[0])/7 == nrrdElementNumber(_nten[1])/7)) { fprintf(stderr, "%s: number voxels %u %u %u don't all match\n", me, AIR_CAST(unsigned int, nrrdElementNumber(_ncov)/21), AIR_CAST(unsigned int, nrrdElementNumber(_nten[0])/7), AIR_CAST(unsigned int, nrrdElementNumber(_nten[1])/7)); airMopError(mop); return 1; } ncov = nrrdNew(); nten[0] = nrrdNew(); nten[1] = nrrdNew(); nout = nrrdNew(); airMopAdd(mop, ncov, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nten[0], (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nten[1], (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(ncov, _ncov, nrrdTypeDouble) || nrrdConvert(nten[0], _nten[0], nrrdTypeDouble) || nrrdConvert(nten[1], _nten[1], nrrdTypeDouble)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble converting to %s:\n%s\n", me, airEnumStr(nrrdType, nrrdTypeDouble), err); airMopError(mop); return 1; } if (nrrdSlice(nout, nten[0], 0, 0)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble prepping output:\n%s\n", me, err); airMopError(mop); return 1; } cc = AIR_CAST(double *, ncov->data); t0 = AIR_CAST(double *, nten[0]->data); t1 = AIR_CAST(double *, nten[1]->data); out = AIR_CAST(double *, nout->data); nn = nrrdElementNumber(nout); ww[ 0] = 1*1; ww[ 1] = 2*1; ww[ 2] = 2*1; ww[ 3] = 1*1; ww[ 4] = 2*1; ww[ 5] = 1*1; /* */ ww[ 6] = 2*2; ww[ 7] = 2*2; ww[ 8] = 1*2; ww[ 9] = 2*2; ww[10] = 1*2; /* */ ww[11] = 2*2; ww[12] = 1*2; ww[13] = 2*2; ww[14] = 1*2; /* */ ww[15] = 1*1; ww[16] = 2*1; ww[17] = 1*1; /* */ ww[18] = 2*2; ww[19] = 1*2; /* */ ww[20] = 1*1; /* for (ii=0; ii<21; ii++) { ww[ii] = sqrt(ww[ii]); } */ for (ii=0; iidata); for (zi=0; zitype )) { fprintf(stderr, "%s vector type (%s) not %s\n", me, airEnumStr(nrrdType, nvec->type), airEnumStr(nrrdType, nrrdTypeFloat)); airMopError(mop); return 1; } if (!( 2 == nvec->dim && 3 == nvec->axis[0].size )) { fprintf(stderr, "%s: nvec not a 2-D 3-by-N array\n", me); airMopError(mop); return 1; } if (!( _nodf->axis[0].size == nvec->axis[1].size )) { fprintf(stderr, "%s mismatch of _nodf->axis[0].size (%d) vs. " "nvec->axis[1].size (%d)\n", me, (int)_nodf->axis[0].size, (int)nvec->axis[1].size); airMopError(mop); return 1; } nrrdAxisInfoGet_nva(_nodf, nrrdAxisInfoSize, size); size[0] = bins; nhist = nrrdNew(); airMopAdd(mop, nhist, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_nva(nhist, nrrdTypeFloat, _nodf->dim, size)) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble allocating output:\n%s", me, errS); airMopError(mop); return 1; } ncovar = nrrdNew(); airMopAdd(mop, ncovar, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(ncovar, nrrdTypeFloat, 2, AIR_CAST(size_t, bins), AIR_CAST(size_t, bins))) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble allocating covariance output:\n%s", me, errS); airMopError(mop); return 1; } { /* we modify the lengths of the vectors here */ int NN, VV, ii, jj=0, kk, *anglut; float *odf, *hist, *covar, *vec, *vi, *vj, tmp, pvmin; double *mean; Nrrd *nodf, *nanglut; VV = nvec->axis[1].size; NN = nrrdElementNumber(_nodf)/VV; nanglut = nrrdNew(); airMopAdd(mop, nanglut, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(nanglut, nrrdTypeInt, 2, AIR_CAST(size_t, VV), AIR_CAST(size_t, VV))) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble allocating lookup table:\n%s", me, errS); airMopError(mop); return 1; } if (nrrdTypeFloat == _nodf->type) { nodf = _nodf; } else { nodf = nrrdNew(); airMopAdd(mop, nodf, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nodf, _nodf, nrrdTypeFloat)) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble converting input:\n%s", me, errS); airMopError(mop); return 1; } } /* normalize lengths (MODIFIES INPUT) */ vec = (float*)nvec->data; for (ii=0; ii<=jj; ii++) { vi = vec + 3*ii; ELL_3V_NORM(vi, vi, tmp); } /* pre-compute pair-wise angles */ anglut = (int*)nanglut->data; for (jj=0; jjdata; hist = (float*)nhist->data; for (kk=0; kkdata; for (kk=0; kkdata; hist = (float*)nhist->data; for (kk=0; kkdata; for (yi=0; yiaxis[1].spacing = 1.0; nten->axis[2].spacing = 1.0; nten->axis[3].spacing = 1.0; } if (nrrdSave(outS, nten, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/test/teigen.c0000664000175000017500000003250712165631065017574 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009, University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" char *info = ("tests tenEigensolve_d and new stand-alone function."); #define ROOT_TRIPLE 2 /* ell_cubic_root_triple */ #define ROOT_SINGLE_DOUBLE 3 /* ell_cubic_root_single_double */ #define ROOT_THREE 4 /* ell_cubic_root_three */ #define ABS(a) (((a) > 0.0f ? (a) : -(a))) #define VEC_SET(v, a, b, c) \ ((v)[0] = (a), (v)[1] = (b), (v)[2] = (c)) #define VEC_DOT(v1, v2) \ ((v1)[0]*(v2)[0] + (v1)[1]*(v2)[1] + (v1)[2]*(v2)[2]) #define VEC_CROSS(v3, v1, v2) \ ((v3)[0] = (v1)[1]*(v2)[2] - (v1)[2]*(v2)[1], \ (v3)[1] = (v1)[2]*(v2)[0] - (v1)[0]*(v2)[2], \ (v3)[2] = (v1)[0]*(v2)[1] - (v1)[1]*(v2)[0]) #define VEC_ADD(v1, v2) \ ((v1)[0] += (v2)[0], \ (v1)[1] += (v2)[1], \ (v1)[2] += (v2)[2]) #define VEC_SUB(v1, v2) \ ((v1)[0] -= (v2)[0], \ (v1)[1] -= (v2)[1], \ (v1)[2] -= (v2)[2]) #define VEC_SCL(v1, s) \ ((v1)[0] *= (s), \ (v1)[1] *= (s), \ (v1)[2] *= (s)) #define VEC_LEN(v) (sqrt(VEC_DOT(v,v))) #define VEC_NORM(v, len) ((len) = VEC_LEN(v), VEC_SCL(v, 1.0/len)) #define VEC_SCL_SUB(v1, s, v2) \ ((v1)[0] -= (s)*(v2)[0], \ (v1)[1] -= (s)*(v2)[1], \ (v1)[2] -= (s)*(v2)[2]) #define VEC_COPY(v1, v2) \ ((v1)[0] = (v2)[0], \ (v1)[1] = (v2)[1], \ (v1)[2] = (v2)[2]) /* ** All the three given vectors span only a 2D space, and this finds ** the normal to that plane. Simply sums up all the pair-wise ** cross-products to get a good estimate. Trick is getting the cross ** products to line up before summing. */ void nullspace1(double ret[3], const double r0[3], const double r1[3], const double r2[3]) { double crs[3]; /* ret = r0 x r1 */ VEC_CROSS(ret, r0, r1); /* crs = r1 x r2 */ VEC_CROSS(crs, r1, r2); /* ret += crs or ret -= crs; whichever makes ret longer */ if (VEC_DOT(ret, crs) > 0) { VEC_ADD(ret, crs); } else { VEC_SUB(ret, crs); } /* crs = r0 x r2 */ VEC_CROSS(crs, r0, r2); /* ret += crs or ret -= crs; whichever makes ret longer */ if (VEC_DOT(ret, crs) > 0) { VEC_ADD(ret, crs); } else { VEC_SUB(ret, crs); } return; } /* ** All vectors are in the same 1D space, we have to find two ** mutually vectors perpendicular to that span */ void nullspace2(double reta[3], double retb[3], const double r0[3], const double r1[3], const double r2[3]) { double sqr[3], sum[3]; int idx; VEC_COPY(sum, r0); if (VEC_DOT(sum, r1) > 0) { VEC_ADD(sum, r1); } else { VEC_SUB(sum, r1); } if (VEC_DOT(sum, r2) > 0) { VEC_ADD(sum, r2); } else { VEC_SUB(sum, r2); } /* find largest component, to get most stable expression for a perpendicular vector */ sqr[0] = sum[0]*sum[0]; sqr[1] = sum[1]*sum[1]; sqr[2] = sum[2]*sum[2]; idx = 0; if (sqr[0] < sqr[1]) idx = 1; if (sqr[idx] < sqr[2]) idx = 2; /* reta will be perpendicular to sum */ if (0 == idx) { VEC_SET(reta, sum[1] - sum[2], -sum[0], sum[0]); } else if (1 == idx) { VEC_SET(reta, -sum[1], sum[0] - sum[2], sum[1]); } else { VEC_SET(reta, -sum[2], sum[2], sum[0] - sum[1]); } /* and now retb will be perpendicular to both reta and sum */ VEC_CROSS(retb, reta, sum); return; } /* ** Eigensolver for symmetric 3x3 matrix: ** ** M00 M01 M02 ** M01 M11 M12 ** M02 M12 M22 ** ** Must be passed eval[3] vector, and will compute eigenvalues ** only if evec[9] is non-NULL. Computed eigenvectors are at evec+0, ** evec+3, and evec+6. ** ** Return value indicates something about the eigenvalue solution to ** the cubic characteristic equation; see ROOT_ #defines above ** ** Relies on the ABS and VEC_* macros above, as well as math functions ** atan2(), sin(), cos(), sqrt(), and airCbrt(), defined as: double airCbrt(double v) { #if defined(_WIN32) || defined(__STRICT_ANSI__) return (v < 0.0 ? -pow(-v,1.0/3.0) : pow(v,1.0/3.0)); #else return cbrt(v); #endif } ** ** HEY: the numerical precision issues here are very subtle, and ** merit some more scrutiny. With evals (1.000001, 1, 1), for example, ** whether it comes back as a single/double root, vs three distinct roots, ** is determines by the comparison between "D" and "epsilon", and the ** setting of epsilon seems pretty arbitrary at this point... ** */ int evals(double eval[3], const double _M00, const double _M01, const double _M02, const double _M11, const double _M12, const double _M22) { #include "teigen-evals-A.c" #include "teigen-evals-B.c" return roots; } int evals_evecs(double eval[3], double evec[9], const double _M00, const double _M01, const double _M02, const double _M11, const double _M12, const double _M22) { double r0[3], r1[3], r2[3], crs[3], len, dot; double mean, norm, rnorm, Q, R, QQQ, D, theta, M00, M01, M02, M11, M12, M22; double epsilon = 1.0E-12; int roots; /* copy the given matrix elements */ M00 = _M00; M01 = _M01; M02 = _M02; M11 = _M11; M12 = _M12; M22 = _M22; /* ** subtract out the eigenvalue mean (will add back to evals later); ** helps with numerical stability */ mean = (M00 + M11 + M22)/3.0; M00 -= mean; M11 -= mean; M22 -= mean; /* ** divide out L2 norm of eigenvalues (will multiply back later); ** this too seems to help with stability */ norm = sqrt(M00*M00 + 2*M01*M01 + 2*M02*M02 + M11*M11 + 2*M12*M12 + M22*M22); rnorm = norm ? 1.0/norm : 1.0; M00 *= rnorm; M01 *= rnorm; M02 *= rnorm; M11 *= rnorm; M12 *= rnorm; M22 *= rnorm; /* this code is a mix of prior Teem code and ideas from Eberly's "Eigensystems for 3 x 3 Symmetric Matrices (Revisited)" */ Q = (M01*M01 + M02*M02 + M12*M12 - M00*M11 - M00*M22 - M11*M22)/3.0; QQQ = Q*Q*Q; R = (M00*M11*M22 + M02*(2*M01*M12 - M02*M11) - M00*M12*M12 - M01*M01*M22)/2.0; D = QQQ - R*R; if (D > epsilon) { /* three distinct roots- this is the most common case */ double mm, ss, cc; theta = atan2(sqrt(D), R)/3.0; mm = sqrt(Q); ss = sin(theta); cc = cos(theta); eval[0] = 2*mm*cc; eval[1] = mm*(-cc + sqrt(3.0)*ss); eval[2] = mm*(-cc - sqrt(3.0)*ss); roots = ROOT_THREE; /* else D is near enough to zero */ } else if (R < -epsilon || epsilon < R) { double U; /* one double root and one single root */ U = airCbrt(R); /* cube root function */ if (U > 0) { eval[0] = 2*U; eval[1] = -U; eval[2] = -U; } else { eval[0] = -U; eval[1] = -U; eval[2] = 2*U; } roots = ROOT_SINGLE_DOUBLE; } else { /* a triple root! */ eval[0] = eval[1] = eval[2] = 0.0; roots = ROOT_TRIPLE; } /* r0, r1, r2 are the vectors we manipulate to find the nullspaces of M - lambda*I */ VEC_SET(r0, 0.0, M01, M02); VEC_SET(r1, M01, 0.0, M12); VEC_SET(r2, M02, M12, 0.0); if (ROOT_THREE == roots) { r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0]; nullspace1(evec+0, r0, r1, r2); r0[0] = M00 - eval[1]; r1[1] = M11 - eval[1]; r2[2] = M22 - eval[1]; nullspace1(evec+3, r0, r1, r2); r0[0] = M00 - eval[2]; r1[1] = M11 - eval[2]; r2[2] = M22 - eval[2]; nullspace1(evec+6, r0, r1, r2); } else if (ROOT_SINGLE_DOUBLE == roots) { if (eval[1] == eval[2]) { /* one big (eval[0]) , two small (eval[1,2]) */ r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0]; nullspace1(evec+0, r0, r1, r2); r0[0] = M00 - eval[1]; r1[1] = M11 - eval[1]; r2[2] = M22 - eval[1]; nullspace2(evec+3, evec+6, r0, r1, r2); } else { /* two big (eval[0,1]), one small (eval[2]) */ r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0]; nullspace2(evec+0, evec+3, r0, r1, r2); r0[0] = M00 - eval[2]; r1[1] = M11 - eval[2]; r2[2] = M22 - eval[2]; nullspace1(evec+6, r0, r1, r2); } } else { /* ROOT_TRIPLE == roots; use any basis for eigenvectors */ VEC_SET(evec+0, 1, 0, 0); VEC_SET(evec+3, 0, 1, 0); VEC_SET(evec+6, 0, 0, 1); } /* we always make sure its really orthonormal; keeping fixed the eigenvector associated with the largest-magnitude eigenvalue */ if (ABS(eval[0]) > ABS(eval[2])) { /* normalize evec+0 but don't move it */ VEC_NORM(evec+0, len); dot = VEC_DOT(evec+0, evec+3); VEC_SCL_SUB(evec+3, dot, evec+0); VEC_NORM(evec+3, len); dot = VEC_DOT(evec+0, evec+6); VEC_SCL_SUB(evec+6, dot, evec+0); dot = VEC_DOT(evec+3, evec+6); VEC_SCL_SUB(evec+6, dot, evec+3); VEC_NORM(evec+6, len); } else { /* normalize evec+6 but don't move it */ VEC_NORM(evec+6, len); dot = VEC_DOT(evec+6, evec+3); VEC_SCL_SUB(evec+3, dot, evec+6); VEC_NORM(evec+3, len); dot = VEC_DOT(evec+3, evec+0); VEC_SCL_SUB(evec+0, dot, evec+3); dot = VEC_DOT(evec+6, evec+0); VEC_SCL_SUB(evec+0, dot, evec+6); VEC_NORM(evec+0, len); } /* to be nice, make it right-handed */ VEC_CROSS(crs, evec+0, evec+3); if (0 > VEC_DOT(crs, evec+6)) { VEC_SCL(evec+6, -1); } /* multiply back by eigenvalue L2 norm */ eval[0] /= rnorm; eval[1] /= rnorm; eval[2] /= rnorm; /* add back in the eigenvalue mean */ eval[0] += mean; eval[1] += mean; eval[2] += mean; return roots; } void testeigen(double tt[7], double eval[3], double evec[9]) { double mat[9], dot[3], cross[3]; unsigned int ii; TEN_T2M(mat, tt); printf("evals %g %g %g\n", eval[0], eval[1], eval[2]); printf("evec0 (%g) %g %g %g\n", ELL_3V_LEN(evec + 0), evec[0], evec[1], evec[2]); printf("evec1 (%g) %g %g %g\n", ELL_3V_LEN(evec + 3), evec[3], evec[4], evec[5]); printf("evec2 (%g) %g %g %g\n", ELL_3V_LEN(evec + 6), evec[6], evec[7], evec[8]); printf("Mv - lv: (len) X Y Z (should be ~zeros)\n"); for (ii=0; ii<3; ii++) { double uu[3], vv[3], dd[3]; ELL_3MV_MUL(uu, mat, evec + 3*ii); ELL_3V_SCALE(vv, eval[ii], evec + 3*ii); ELL_3V_SUB(dd, uu, vv); printf("%d: (%g) %g %g %g\n", ii, ELL_3V_LEN(dd), dd[0], dd[1], dd[2]); } dot[0] = ELL_3V_DOT(evec + 0, evec + 3); dot[1] = ELL_3V_DOT(evec + 0, evec + 6); dot[2] = ELL_3V_DOT(evec + 3, evec + 6); printf("pairwise dots: (%g) %g %g %g\n", ELL_3V_LEN(dot), dot[0], dot[1], dot[2]); ELL_3V_CROSS(cross, evec+0, evec+3); printf("right-handed: %g\n", ELL_3V_DOT(evec+6, cross)); return; } int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt=NULL; airArray *mop; double _tt[6], tt[7], ss, pp[3], qq[4], rot[9], mat1[9], mat2[9], tmp, evalA[3], evecA[9], evalB[3], evecB[9]; int roots; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, NULL, "m00 m01 m02 m11 m12 m22", airTypeDouble, 6, 6, _tt, NULL, "symmtric matrix coeffs"); hestOptAdd(&hopt, "p", "vec", airTypeDouble, 3, 3, pp, "0 0 0", "rotation as P vector"); hestOptAdd(&hopt, "s", "scl", airTypeDouble, 1, 1, &ss, "1.0", "scaling"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); ELL_6V_COPY(tt + 1, _tt); tt[0] = 1.0; TEN_T_SCALE(tt, ss, tt); ELL_4V_SET(qq, 1, pp[0], pp[1], pp[2]); ELL_4V_NORM(qq, qq, tmp); ell_q_to_3m_d(rot, qq); printf("%s: rot\n", me); printf(" %g %g %g\n", rot[0], rot[1], rot[2]); printf(" %g %g %g\n", rot[3], rot[4], rot[5]); printf(" %g %g %g\n", rot[6], rot[7], rot[8]); TEN_T2M(mat1, tt); ell_3m_mul_d(mat2, rot, mat1); ELL_3M_TRANSPOSE_IP(rot, tmp); ell_3m_mul_d(mat1, mat2, rot); TEN_M2T(tt, mat1); printf("input matrix = \n %g %g %g\n %g %g\n %g\n", tt[1], tt[2], tt[3], tt[4], tt[5], tt[6]); printf("================== tenEigensolve_d ==================\n"); roots = tenEigensolve_d(evalA, evecA, tt); printf("%s roots\n", airEnumStr(ell_cubic_root, roots)); testeigen(tt, evalA, evecA); printf("================== new eigensolve ==================\n"); roots = evals(evalB, tt[1], tt[2], tt[3], tt[4], tt[5], tt[6]); printf("%s roots: %g %g %g\n", airEnumStr(ell_cubic_root, roots), evalB[0], evalB[1], evalB[2]); roots = evals_evecs(evalB, evecB, tt[1], tt[2], tt[3], tt[4], tt[5], tt[6]); printf("%s roots\n", airEnumStr(ell_cubic_root, roots)); testeigen(tt, evalB, evecB); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/test/tensorDotDat.c0000664000175000017500000002411712042367142020725 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" char *info = ("Compute the makings of a new tensor.dat file."); int main(int argc, const char *argv[]) { const char *me; char *err; hestOpt *hopt=NULL; airArray *mop; int E, optimizeEdge, workToDo; unsigned int ii, numRange[2], seedRange[2], seed, seedDone; double *log, minAngle, minEdge, pot, potNorm, time0, time1; char *outStr, logFilename[AIR_STRLEN_MED], gradFilename[AIR_STRLEN_MED], keyStr[AIR_STRLEN_MED], valStr[AIR_STRLEN_MED]; tenGradientParm *tgparm; Nrrd *nlog, *ngrad; size_t size[2]; mop = airMopNew(); tgparm = tenGradientParmNew(); airMopAdd(mop, tgparm, (airMopper)tenGradientParmNix, airMopAlways); tgparm->single = AIR_FALSE; tgparm->snap = 0; tgparm->minMeanImprovement = 0.0; nlog = nrrdNew(); airMopAdd(mop, nlog, (airMopper)nrrdNuke, airMopAlways); ngrad = nrrdNew(); airMopAdd(mop, ngrad, (airMopper)nrrdNuke, airMopAlways); me = argv[0]; hestOptAdd(&hopt, "num", "min max", airTypeUInt, 2, 2, numRange, "6 129", "range of number of gradients to be computed"); hestOptAdd(&hopt, "seed", "min max", airTypeUInt, 2, 2, seedRange, "1 0", "range of seed values to use with the RNG. Using max lower " "than min means that the seed values should be increased " "(and used for computation) without bound"); hestOptAdd(&hopt, "p", "exponent", airTypeUInt, 1, 1, &(tgparm->expo), "1", "the exponent p that defines the 1/r^p potential energy " "(Coulomb is 1)"); hestOptAdd(&hopt, "step", "step", airTypeDouble, 1, 1, &(tgparm->initStep), "1", "time increment in solver"); hestOptAdd(&hopt, "miniter", "# iters", airTypeInt, 1, 1, &(tgparm->minIteration), "0", "required minimum number of simulation iterations"); hestOptAdd(&hopt, "maxiter", "# iters", airTypeInt, 1, 1, &(tgparm->maxIteration), "1000000", "max number of simulations iterations"); hestOptAdd(&hopt, "minvelo", "vel", airTypeDouble, 1, 1, &(tgparm->minVelocity), "0.00000000001", "low threshold on mean velocity of repelling points, " "at which point repulsion phase of algorithm terminates."); hestOptAdd(&hopt, "dp", "potential change", airTypeDouble, 1, 1, &(tgparm->minPotentialChange), "0.00000000001", "low threshold on fractional change of potential at " "which point repulsion phase of algorithm terminates."); hestOptAdd(&hopt, "minimprov", "delta", airTypeDouble, 1, 1, &(tgparm->minMeanImprovement), "0.00005", "in the second phase of the algorithm, " "when stochastically balancing the sign of the gradients, " "the (small) improvement in length of mean gradient " "which triggers termination (as further improvements " "are unlikely. "); hestOptAdd(&hopt, "minmean", "len", airTypeDouble, 1, 1, &(tgparm->minMean), "0.0005", "if length of mean gradient falls below this, finish " "the balancing phase"); hestOptAdd(&hopt, "oe", NULL, airTypeInt, 0, 0, &optimizeEdge, NULL, "optimize for the maximal minimal edge length, " "instead of potential."); hestOptAdd(&hopt, "odir", "out", airTypeString, 1, 1, &outStr, ".", "output directory for all grad files and logs, you should " "leave off the trailing /"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (0 == seedRange[0]) { fprintf(stderr, "%s: sorry, initial seed must be non-zero\n", me); airMopError(mop); return 1; } if (!( numRange[0] <= numRange[1] && numRange[0] >= 6 )) { fprintf(stderr, "%s: number range [%u,%u] invalid\n", me, numRange[0], numRange[1]); airMopError(mop); return 1; } /* in master log (per gradient set): 0: # grads 1: last seed tried 2: seed of best so far 3: best phi, not normalized 4: best phi, normalized 5: best edge min 6: ideal edge 7: iters used 8: time used (in seconds) */ /* see if we can open the log */ sprintf(logFilename, "%s/000-%04u-log.nrrd", outStr, tgparm->expo); if (nrrdLoad(nlog, logFilename, NULL)) { /* no, we couldn't load it, and we don't care why */ free(biffGetDone(NRRD)); /* create a log nrrd of the correct size */ size[0] = 9; size[1] = numRange[1]+1; if (nrrdMaybeAlloc_nva(nlog, nrrdTypeDouble, 2, size)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble making log:\n%s\n", me, err); airMopError(mop); return 1; } } else { /* we could open the log, see if its the right size */ if (!( nrrdTypeDouble == nlog->type && 2 == nlog->dim && 9 == nlog->axis[0].size && numRange[1]+1 == nlog->axis[1].size )) { fprintf(stderr, "%s: given log (%s %u-D %ux%ux?) doesn't match " "desired (%s 2-D 9x%u)\n", me, airEnumStr(nrrdType, nlog->type), nlog->dim, AIR_CAST(unsigned int, nlog->axis[0].size), AIR_CAST(unsigned int, nlog->axis[1].size), airEnumStr(nrrdType, nrrdTypeDouble), numRange[1]+1); airMopError(mop); return 1; } } /* nlog is the right size */ /* initialize log's first column and key/value pairs, and (re)save */ log = AIR_CAST(double *, nlog->data); for (ii=numRange[0]; ii<=numRange[1]; ii++) { log[0 + 9*ii] = ii; } E = 0; if (!E) strcpy(keyStr, "maxiter"); if (!E) sprintf(valStr, "%d", tgparm->maxIteration); if (!E) E |= nrrdKeyValueAdd(nlog, keyStr, valStr); if (!E) strcpy(keyStr, "step"); if (!E) sprintf(valStr, "%g", tgparm->initStep); if (!E) E |= nrrdKeyValueAdd(nlog, keyStr, valStr); if (!E) strcpy(keyStr, "dp"); if (!E) sprintf(valStr, "%g", tgparm->minPotentialChange); if (!E) E |= nrrdKeyValueAdd(nlog, keyStr, valStr); if (!E) strcpy(keyStr, "minvelo"); if (!E) sprintf(valStr, "%g", tgparm->minVelocity); if (!E) E |= nrrdKeyValueAdd(nlog, keyStr, valStr); if (!E) strcpy(keyStr, "minmean"); if (!E) sprintf(valStr, "%g", tgparm->minMean); if (!E) E |= nrrdKeyValueAdd(nlog, keyStr, valStr); if (!E) E |= nrrdSave(logFilename, nlog, NULL); if (E) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing log:\n%s\n", me, err); airMopError(mop); return 1; } /* in master log (per gradient set): 0: # grads 1: last seed for which solution was computed 2: seed of best solution so far 3: best phi, not edge normalized 4: best phi, edge normalized 5: best minimum edge length 6: ideal edge length 7: iters used 8: time used (in seconds) */ workToDo = AIR_FALSE; for (seed=seedRange[0]; seedRange[1] < seedRange[0] || seed <= seedRange[1]; seed++) { for (ii=numRange[0]; ii<=numRange[1]; ii++) { seedDone = AIR_CAST(unsigned int, log[1 + 9*ii]); /* if no seeds have been tried, seedDone will be zero */ if (seedDone >= seed) { /* have already tried this seed, move on */ continue; } workToDo = AIR_TRUE; tgparm->seed = seed; fprintf(stderr, "%s ================ %u %u\n", me, ii, tgparm->seed); time0 = airTime(); if (tenGradientGenerate(ngrad, ii, tgparm)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making distribution:\n%s\n", me, err); airMopError(mop); return 1; } time1 = airTime(); tenGradientMeasure(&potNorm, &minAngle, &minEdge, ngrad, tgparm, AIR_TRUE); if (!seedDone || ((optimizeEdge && minEdge > log[5 + 9*ii]) || potNorm < log[4 + 9*ii])) { /* this gradient set is best so far */ tenGradientMeasure(&pot, NULL, NULL, ngrad, tgparm, AIR_FALSE); log[2 + 9*ii] = tgparm->seed; log[3 + 9*ii] = pot; log[4 + 9*ii] = potNorm; log[5 + 9*ii] = minEdge; log[6 + 9*ii] = tenGradientIdealEdge(ii, AIR_FALSE); log[7 + 9*ii] = tgparm->itersUsed; log[8 + 9*ii] = time1 - time0; sprintf(gradFilename, "%s/%03u-%04u.nrrd", outStr, ii, tgparm->expo); if (nrrdSave(gradFilename, ngrad, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } } log[1 + 9*ii] = tgparm->seed; if (nrrdSave(logFilename, nlog, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing log:\n%s\n", me, err); airMopError(mop); return 1; } } } if (!workToDo) { fprintf(stderr, "%s: apparently finished requested computations.\n", me); } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/test/rgbprobe.c0000664000175000017500000001200312042367142020104 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" int main(int argc, char **argv) { char *me, *err; hestParm *hparm; hestOpt *hopt = NULL; airArray *mop; char *outS; int E, ptsNum, ptsIdx; Nrrd *nin, *nprobe, *nout; float *idata, *odata, scale[3], power, x, y, z, r, g, b, cl; gage_t *evec, *aniso, *tensor; double kparm[NRRD_KERNEL_PARMS_NUM]; gageContext *ctx; gagePerVolume *pvl; mop = airMopNew(); me = argv[0]; hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "p", "nprobe", airTypeOther, 1, 1, &nprobe, NULL, "input list of points to probe at, as 3xN float nrrd", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "sc", "scaling", airTypeFloat, 3, 3, scale, "1.0 1.0 1.0", "scaling that took index space positions (in nin) to " "vertex positions (in nprobe); hopefully just the \"spacings\" " "on the volume that was isosurfaced."); hestOptAdd(&hopt, "pow", "power", airTypeFloat, 1, 1, &power, "0.4", "power to raise cl_2 to to determine saturation of color"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL, "output image (floating point)"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, "secret testing area", AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (tenTensorCheck(nin, nrrdTypeFloat, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: didn't get value tensor volume:\n%s\n", me, err); airMopError(mop); return 1; } if (!( nrrdTypeFloat == nprobe->type && 2 == nprobe->dim && 3 == nprobe->axis[0].size )) { fprintf(stderr, "%s: didn't get valid probe point list\n", me); airMopError(mop); return 1; } ptsNum = nprobe->axis[1].size; ctx = gageContextNew(); airMopAdd(mop, ctx, (airMopper)gageContextNix, airMopAlways); gageSet(ctx, gageParmCheckIntegrals, AIR_TRUE); kparm[0] = 1.0; E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(nin, tenGageKind)); if (!E) E |= gagePerVolumeAttach(ctx, pvl); if (!E) E |= gageKernelSet(ctx, gageKernel00, nrrdKernelTent, kparm); if (!E) E |= gageQuerySet(pvl, ((1 << tenGageEvec) | (1 << tenGageAniso) | (1 << tenGageTensor)) ); if (!E) E |= gageUpdate(ctx); if (E) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } evec = gageAnswerPointer(pvl, tenGageEvec); aniso = gageAnswerPointer(pvl, tenGageAniso); tensor = gageAnswerPointer(pvl, tenGageTensor); if (nrrdAlloc(nout=nrrdNew(), nrrdTypeFloat, 2, 3, ptsNum)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s\n", me, err); airMopError(mop); return 1; } idata = nprobe->data; odata = nout->data; for (ptsIdx=0; ptsIdxaxis[1].size; sy = nten->axis[2].size; x = rad*sin(AIR_PI*angle/180); y = rad*cos(AIR_PI*angle/180); xi = airIndexClamp(0.0, x, sqrt(3.0)/2.0, sx); yi = airIndexClamp(0.0, y, 0.5, sy); ELL_3V_SET(VV, 0, 3, 0); ELL_3V_SET(EE, 1.5, 1.5, 0); ELL_3V_SET(CC, 1, 1, 1); ELL_3V_SUB(YY, EE, CC); ELL_3V_SUB(XX, VV, EE); ELL_3V_NORM(XX, XX, tmp); ELL_3V_NORM(YY, YY, tmp); ELL_3V_SCALE_ADD3(xyz, 1.0, CC, hack*x, XX, hack*y, YY); ELL_3M_IDENTITY_SET(mD); ELL_3M_DIAG_SET(mD, xyz[0], xyz[1], xyz[2]); ELL_3M_IDENTITY_SET(mT); ell_3m_post_mul_d(mT, mRI); ell_3m_post_mul_d(mT, mD); ell_3m_post_mul_d(mT, mRF); tdata = (float*)(nten->data) + 7*(xi + sx*(yi + 1*sy)); tdata[0] = 1.0; TEN_M2T(tdata, mT); } void _cap2xyz(double xyz[3], double ca, double cp, int version, int whole) { double cl, cs, mean; cs = 1 - ca; cl = 1 - cs - cp; mean = (cs + cp + cl)/3; /* xyz[0] = cs*0.333 + cl*1.0 + cp*0.5; xyz[1] = cs*0.333 + cl*0.0 + cp*0.5; xyz[2] = cs*0.333 + cl*0.0 + cp*0.0; xyz[0] = AIR_AFFINE(0, ca, 1, 1.1*xyz[0], 0.86*xyz[0]); xyz[1] = AIR_AFFINE(0, ca, 1, 1.1*xyz[1], 0.86*xyz[1]); xyz[2] = AIR_AFFINE(0, ca, 1, 1.1*xyz[2], 0.86*xyz[2]); */ if (whole) { ELL_3V_SET(xyz, AIR_AFFINE(0.0, 0.9, 1.0, mean, cl), AIR_AFFINE(0.0, 0.9, 1.0, mean, cp), AIR_AFFINE(0.0, 0.9, 1.0, mean, cs)); ELL_3V_SET(xyz, cl, cp, cs); } else { if (1 == version) { ELL_3V_SET(xyz, (3 + 3*cl - cs)/6, (2 - 2*cl + cp)/6, 2*cs/6); } else { ELL_3V_SET(xyz, 1, 1 - cl, cs); } } } void washQtoM3(double m[9], double q[4]) { double p[4], w, x, y, z, len; ELL_4V_COPY(p, q); len = ELL_4V_LEN(p); ELL_4V_SCALE(p, 1.0/len, p); w = p[0]; x = p[1]; y = p[2]; z = p[3]; /* mathematica work implies that we should be setting ROW vectors here */ ELL_3V_SET(m+0, 1 - 2*(y*y + z*z), 2*(x*y - w*z), 2*(x*z + w*y)); ELL_3V_SET(m+3, 2*(x*y + w*z), 1 - 2*(x*x + z*z), 2*(y*z - w*x)); ELL_3V_SET(m+6, 2*(x*z - w*y), 2*(y*z + w*x), 1 - 2*(x*x + y*y)); } int main(int argc, const char *argv[]) { const char *me; char *err, *outS; hestOpt *hopt=NULL; airArray *mop; int sx, sy, xi, yi, samp, version, whole, right; float *tdata; double p[3], xyz[3], q[4], len, hackcp=0, maxca; double ca, cp, mD[9], mRF[9], mRI[9], mT[9], hack; Nrrd *nten; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "n", "# samples", airTypeInt, 1, 1, &samp, "4", "number of glyphs along each edge of triangle"); hestOptAdd(&hopt, "p", "x y z", airTypeDouble, 3, 3, p, NULL, "location in quaternion quotient space"); hestOptAdd(&hopt, "ca", "max ca", airTypeDouble, 1, 1, &maxca, "0.8", "maximum ca to use at bottom edge of triangle"); hestOptAdd(&hopt, "r", NULL, airTypeInt, 0, 0, &right, NULL, "sample a right-triangle-shaped region, instead of " "a roughly equilateral triangle. "); hestOptAdd(&hopt, "w", NULL, airTypeInt, 0, 0, &whole, NULL, "sample the whole triangle of constant trace, " "instead of just the " "sixth of it in which the eigenvalues have the " "traditional sorted order. "); hestOptAdd(&hopt, "hack", "hack", airTypeDouble, 1, 1, &hack, "0.04", "this is a hack"); hestOptAdd(&hopt, "v", "version", airTypeInt, 1, 1, &version, "1", "which version of the Westin metrics to use to parameterize " "triangle; \"1\" for ISMRM 97, \"2\" for MICCAI 99"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output file to save tensors into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nten = nrrdNew(); airMopAdd(mop, nten, (airMopper)nrrdNuke, airMopAlways); if (!( 1 == version || 2 == version )) { fprintf(stderr, "%s: version must be 1 or 2 (not %d)\n", me, version); airMopError(mop); return 1; } if (right) { sx = samp; sy = (int)(1.0*samp/sqrt(3.0)); } else { sx = 2*samp-1; sy = samp; } if (nrrdMaybeAlloc_va(nten, nrrdTypeFloat, 4, AIR_CAST(size_t, 7), AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, 3))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s\n", me, err); airMopError(mop); return 1; } q[0] = 1.0; q[1] = p[0]; q[2] = p[1]; q[3] = p[2]; len = ELL_4V_LEN(q); ELL_4V_SCALE(q, 1.0/len, q); washQtoM3(mRF, q); ELL_3M_TRANSPOSE(mRI, mRF); if (right) { _ra2t(nten, 0.00, 0.0, mRI, mRF, hack); _ra2t(nten, 0.10, 0.0, mRI, mRF, hack); _ra2t(nten, 0.10, 60.0, mRI, mRF, hack); _ra2t(nten, 0.20, 0.0, mRI, mRF, hack); _ra2t(nten, 0.20, 30.0, mRI, mRF, hack); _ra2t(nten, 0.20, 60.0, mRI, mRF, hack); _ra2t(nten, 0.30, 0.0, mRI, mRF, hack); _ra2t(nten, 0.30, 20.0, mRI, mRF, hack); _ra2t(nten, 0.30, 40.0, mRI, mRF, hack); _ra2t(nten, 0.30, 60.0, mRI, mRF, hack); _ra2t(nten, 0.40, 0.0, mRI, mRF, hack); _ra2t(nten, 0.40, 15.0, mRI, mRF, hack); _ra2t(nten, 0.40, 30.0, mRI, mRF, hack); _ra2t(nten, 0.40, 45.0, mRI, mRF, hack); _ra2t(nten, 0.40, 60.0, mRI, mRF, hack); _ra2t(nten, 0.50, 0.0, mRI, mRF, hack); _ra2t(nten, 0.50, 12.0, mRI, mRF, hack); _ra2t(nten, 0.50, 24.0, mRI, mRF, hack); _ra2t(nten, 0.50, 36.0, mRI, mRF, hack); _ra2t(nten, 0.50, 48.0, mRI, mRF, hack); _ra2t(nten, 0.50, 60.0, mRI, mRF, hack); /* _ra2t(nten, 0.60, 30.0, mRI, mRF, hack); */ _ra2t(nten, 0.60, 40.0, mRI, mRF, hack); _ra2t(nten, 0.60, 50.0, mRI, mRF, hack); _ra2t(nten, 0.60, 60.0, mRI, mRF, hack); /* _ra2t(nten, 0.70, 34.3, mRI, mRF, hack); */ /* _ra2t(nten, 0.70, 42.8, mRI, mRF, hack); */ _ra2t(nten, 0.70, 51.4, mRI, mRF, hack); _ra2t(nten, 0.70, 60.0, mRI, mRF, hack); /* _ra2t(nten, 0.80, 45.0, mRI, mRF, hack); */ _ra2t(nten, 0.80, 52.5, mRI, mRF, hack); _ra2t(nten, 0.80, 60.0, mRI, mRF, hack); _ra2t(nten, 0.90, 60.0, mRI, mRF, hack); _ra2t(nten, 1.00, 60.0, mRI, mRF, hack); /* _ra2t(nten, 0.000, 0.0, mRI, mRF, hack); _ra2t(nten, 0.125, 0.0, mRI, mRF, hack); _ra2t(nten, 0.125, 60.0, mRI, mRF, hack); _ra2t(nten, 0.250, 0.0, mRI, mRF, hack); _ra2t(nten, 0.250, 30.0, mRI, mRF, hack); _ra2t(nten, 0.250, 60.0, mRI, mRF, hack); _ra2t(nten, 0.375, 0.0, mRI, mRF, hack); _ra2t(nten, 0.375, 20.0, mRI, mRF, hack); _ra2t(nten, 0.375, 40.0, mRI, mRF, hack); _ra2t(nten, 0.375, 60.0, mRI, mRF, hack); _ra2t(nten, 0.500, 0.0, mRI, mRF, hack); _ra2t(nten, 0.500, 15.0, mRI, mRF, hack); _ra2t(nten, 0.500, 30.0, mRI, mRF, hack); _ra2t(nten, 0.500, 45.0, mRI, mRF, hack); _ra2t(nten, 0.500, 60.0, mRI, mRF, hack); _ra2t(nten, 0.625, 37.0, mRI, mRF, hack); _ra2t(nten, 0.625, 47.5, mRI, mRF, hack); _ra2t(nten, 0.625, 60.0, mRI, mRF, hack); _ra2t(nten, 0.750, 49.2, mRI, mRF, hack); _ra2t(nten, 0.750, 60.0, mRI, mRF, hack); _ra2t(nten, 0.875, 60.0, mRI, mRF, hack); _ra2t(nten, 1.000, 60.0, mRI, mRF, hack); */ nten->axis[1].spacing = 1; nten->axis[2].spacing = (sx-1)/(sqrt(3.0)*(sy-1)); nten->axis[3].spacing = 1; } else { for (yi=0; yi (%g,%g) -> %g %g %g\n", me, yi, xi, ca, cp, xyz[0], xyz[1], xyz[2]); */ ELL_3M_IDENTITY_SET(mD); ELL_3M_DIAG_SET(mD, xyz[0], xyz[1], xyz[2]); ELL_3M_IDENTITY_SET(mT); ell_3m_post_mul_d(mT, mRI); ell_3m_post_mul_d(mT, mD); ell_3m_post_mul_d(mT, mRF); tdata = (float*)nten->data + 7*(2*(samp-1-xi) - (samp-1-yi) + (2*samp-1)*((samp-1-yi) + samp)); tdata[0] = 1.0; TEN_M2T(tdata, mT); } } nten->axis[1].spacing = 1; nten->axis[2].spacing = 1.5; nten->axis[3].spacing = 1; } if (nrrdSave(outS, nten, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/test/igrt.c0000664000175000017500000000757612042367142017272 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" char *info = ("tests invariant grads and rotation tangents."); int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt=NULL; airArray *mop; double _ten[6], ten[7], minnorm, igrt[6][7], eval[3], evec[9], pp[3], qq[4], rot[9], matA[9], matB[9], tmp; int doK, ret, ii, jj; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, NULL, "tensor", airTypeDouble, 6, 6, _ten, NULL, "tensor value"); hestOptAdd(&hopt, "mn", "minnorm", airTypeDouble, 1, 1, &minnorm, "0.00001", "minimum norm before special handling"); hestOptAdd(&hopt, "k", NULL, airTypeInt, 0, 0, &doK, NULL, "Use K invariants, instead of R (the default)"); hestOptAdd(&hopt, "p", "x y z", airTypeDouble, 3, 3, pp, "0 0 0", "location in quaternion quotient space"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); ELL_6V_COPY(ten+1, _ten); ten[0] = 1.0; fprintf(stderr, "input tensor = %f %f %f %f %f %f\n", ten[1], ten[2], ten[3], ten[4], ten[5], ten[6]); ELL_4V_SET(qq, 1, pp[0], pp[1], pp[2]); ELL_4V_NORM(qq, qq, tmp); ell_q_to_3m_d(rot, qq); TEN_T2M(matA, ten); ELL_3M_MUL(matB, rot, matA); ELL_3M_TRANSPOSE_IP(rot, tmp); ELL_3M_MUL(matA, matB, rot); TEN_M2T(ten, matA); fprintf(stderr, "rotated tensor = %f %f %f %f %f %f\n", ten[1], ten[2], ten[3], ten[4], ten[5], ten[6]); ret = tenEigensolve_d(eval, evec, ten); fprintf(stderr, "eigensystem: %s: %g %g %g\n", airEnumDesc(ell_cubic_root, ret), eval[0], eval[1], eval[2]); if (doK) { tenInvariantGradientsK_d(igrt[0], igrt[1], igrt[2], ten, minnorm); } else { tenInvariantGradientsR_d(igrt[0], igrt[1], igrt[2], ten, minnorm); } tenRotationTangents_d(igrt[3], igrt[4], igrt[5], evec); fprintf(stderr, "invariant gradients and rotation tangents:\n"); for (ii=0; ii<=2; ii++) { fprintf(stderr, " %s_%d: (norm=%g) %f %f %f %f %f %f\n", doK ? "K" : "R", ii+1, TEN_T_NORM(igrt[ii]), igrt[ii][1], igrt[ii][2], igrt[ii][3], igrt[ii][4], igrt[ii][5], igrt[ii][6]); } for (ii=3; ii<=5; ii++) { fprintf(stderr, "phi_%d: (norm=%g) %f %f %f %f %f %f\n", ii-2, TEN_T_NORM(igrt[ii]), igrt[ii][1], igrt[ii][2], igrt[ii][3], igrt[ii][4], igrt[ii][5], igrt[ii][6]); } fprintf(stderr, "dot products:\n"); for (ii=0; ii<=5; ii++) { for (jj=ii+1; jj<=5; jj++) { fprintf(stderr, "%d,%d==%f ", ii, jj, TEN_T_DOT(igrt[ii], igrt[jj])); } fprintf(stderr, "\n"); } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/test/taniso.c0000664000175000017500000001011012042367142017574 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" char *info = ("tests anisotropy measures"); int main(int argc, char *argv[]) { char *me; int aa, isot, equi; unsigned int ii, nn; double evalD[3], pp[3], qq[4], rot[9], mat1[9], mat2[9], tenD[7], tmp, mean; double aniso[4]; float tenF[7], evalF[3]; AIR_UNUSED(argc); me = argv[0]; nn = 10000000; for (ii=0; ii 0.0000003) { fprintf(stderr, "\n%s: %u %d (%s) (isot %s, equi %s) tmp=%g\n", me, ii, aa, airEnumStr(tenAniso, aa), isot ? "true" : "false", equi ? "true" : "false", tmp); fprintf(stderr, " %g %g %g %g %g %g;\n", tenD[1], tenD[2], tenD[3], tenD[4], tenD[5], tenD[6]); fprintf(stderr, " %f %f %f (%f %f %f)f --->\n", evalD[0], evalD[1], evalD[2], evalF[0], evalF[1], evalF[2]); fprintf(stderr, " %f %f %f %f\n", aniso[0], aniso[1], aniso[2], aniso[3]); exit(1); } } } fprintf(stderr, "\n"); return 0; } teem-1.11.0~svn6057/src/ten/test/tsoid.c0000664000175000017500000002424612042367142017440 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" char *info = ("Save a single ellipsoid or superquadric into an OFF file."); int soidDoit(limnObject *obj, int look, int sphere, float gamma, int res, float AB[2], float ten[7]) { int partIdx, axis; float cl, cp, qA, qB, eval[3], evec[9], matA[16], matB[16]; if (AB) { qA = AB[0]; qB = AB[1]; axis = 2; } else { tenEigensolve_f(eval, evec, ten); ELL_SORT3(eval[0], eval[1], eval[2], cl); cl = (eval[0] - eval[1])/(eval[0] + eval[1] + eval[2]); cp = 2*(eval[1] - eval[2])/(eval[0] + eval[1] + eval[2]); if (cl > cp) { axis = 0; qA = pow(1-cp, gamma); qB = pow(1-cl, gamma); } else { axis = 2; qA = pow(1-cl, gamma); qB = pow(1-cp, gamma); } fprintf(stderr, "eval = %g %g %g -> cl=%g %s cp=%g -> axis = %d\n", eval[0], eval[1], eval[2], cl, cl > cp ? ">" : "<", cp, axis); } if (sphere) { partIdx = limnObjectPolarSphereAdd(obj, look, 0, 2*res, res); } else { partIdx = limnObjectPolarSuperquadAdd(obj, look, axis, qA, qB, 2*res, res); } ELL_4M_IDENTITY_SET(matA); ELL_4V_SET(matB + 0*4, eval[0], 0, 0, 0); ELL_4V_SET(matB + 1*4, 0, eval[1], 0, 0); ELL_4V_SET(matB + 2*4, 0, 0, eval[2], 0); ELL_4V_SET(matB + 3*4, 0, 0, 0, 1); ELL_4M_SCALE_SET(matB, eval[0], eval[1], eval[2]); ell_4m_post_mul_f(matA, matB); ELL_4V_SET(matB + 0*4, evec[0 + 0*3], evec[0 + 1*3], evec[0 + 2*3], 0); ELL_4V_SET(matB + 1*4, evec[1 + 0*3], evec[1 + 1*3], evec[1 + 2*3], 0); ELL_4V_SET(matB + 2*4, evec[2 + 0*3], evec[2 + 1*3], evec[2 + 2*3], 0); ELL_4V_SET(matB + 3*4, 0, 0, 0, 1); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); return partIdx; } int main(int argc, const char *argv[]) { const char *me; char *err, *outS; double scale[3], matA[9], matB[9], matC[9], sval[3], uu[9], vv[9]; float matAf[9], matBf[16]; float p[3], q[4], mR[9], len, gamma; float os, vs, rad, AB[2], ten[7], view[3]; hestOpt *hopt=NULL; airArray *mop; limnObject *obj; limnLook *look; int lookRod, lookSoid; int partIdx=-1; /* sssh */ int res, sphere; FILE *file; me = argv[0]; hestOptAdd(&hopt, "sc", "scalings", airTypeDouble, 3, 3, scale, "1 1 1", "axis-aligned scaling to do on ellipsoid"); hestOptAdd(&hopt, "AB", "A, B exponents", airTypeFloat, 2, 2, AB, "nan nan", "Directly set the A, B parameters to the superquadric surface, " "over-riding the default behavior of determining them from the " "scalings \"-sc\" as superquadric tensor glyphs"); hestOptAdd(&hopt, "os", "over-all scaling", airTypeFloat, 1, 1, &os, "1", "over-all scaling (multiplied by scalings)"); hestOptAdd(&hopt, "vs", "over-all scaling", airTypeFloat, 1, 1, &vs, "1", "scaling along view-direction (to show off bas-relief " "ambibuity of ellipsoids versus superquads)"); hestOptAdd(&hopt, "fr", "from (eye) point", airTypeFloat, 3, 3, &view, "4 4 4", "eye point, needed for non-unity \"-vs\""); hestOptAdd(&hopt, "gamma", "superquad sharpness", airTypeFloat, 1, 1, &gamma, "0", "how much to sharpen edges as a " "function of differences between eigenvalues"); hestOptAdd(&hopt, "sphere", NULL, airTypeInt, 0, 0, &sphere, NULL, "use a sphere instead of a superquadric"); hestOptAdd(&hopt, "p", "x y z", airTypeFloat, 3, 3, p, "0 0 0", "location in quaternion quotient space"); hestOptAdd(&hopt, "r", "radius", airTypeFloat, 1, 1, &rad, "0.015", "black axis cylinder radius (or 0.0 to not drawn these)"); hestOptAdd(&hopt, "res", "resolution", airTypeInt, 1, 1, &res, "25", "tesselation resolution for both glyph and axis cylinders"); hestOptAdd(&hopt, "o", "output OFF", airTypeString, 1, 1, &outS, "out.off", "output file to save OFF into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); obj = limnObjectNew(1000, AIR_TRUE); airMopAdd(mop, obj, (airMopper)limnObjectNix, airMopAlways); /* create limnLooks for ellipsoid and for rods */ lookSoid = limnObjectLookAdd(obj); look = obj->look + lookSoid; ELL_4V_SET(look->rgba, 1, 1, 1, 1); ELL_3V_SET(look->kads, 0.2, 0.8, 0); look->spow = 0; lookRod = limnObjectLookAdd(obj); look = obj->look + lookRod; ELL_4V_SET(look->rgba, 0, 0, 0, 1); ELL_3V_SET(look->kads, 1, 0, 0); look->spow = 0; ELL_3M_IDENTITY_SET(matA); ELL_3V_SCALE(scale, os, scale); ELL_3M_SCALE_SET(matB, scale[0], scale[1], scale[2]); ell_3m_post_mul_d(matA, matB); if (1 != vs) { ELL_3V_NORM(view, view, len); if (!len) { /* HEY: perhaps do more diplomatic error message here */ fprintf(stderr, "%s: stupido!\n", me); exit(1); } ELL_3MV_OUTER(matB, view, view); ELL_3M_SCALE(matB, vs-1, matB); ELL_3M_IDENTITY_SET(matC); ELL_3M_ADD2(matB, matC, matB); ell_3m_post_mul_d(matA, matB); } ell_3m_svd_d(uu, sval, vv, matA, AIR_TRUE); /* fprintf(stderr, "%s: ____________________________________\n", me); fprintf(stderr, "%s: mat = \n", me); ell_3m_print_d(stderr, matA); fprintf(stderr, "%s: uu = \n", me); ell_3m_print_d(stderr, uu); ELL_3M_TRANSPOSE(matC, uu); ELL_3M_MUL(matB, uu, matC); fprintf(stderr, "%s: uu * uu^T = \n", me); ell_3m_print_d(stderr, matB); fprintf(stderr, "%s: sval = %g %g %g\n", me, sval[0], sval[1], sval[2]); fprintf(stderr, "%s: vv = \n", me); ell_3m_print_d(stderr, vv); ELL_3M_MUL(matB, vv, vv); fprintf(stderr, "%s: vv * vv^T = \n", me); ELL_3M_TRANSPOSE(matC, vv); ELL_3M_MUL(matB, vv, matC); ell_3m_print_d(stderr, matB); ELL_3M_IDENTITY_SET(matA); ell_3m_pre_mul_d(matA, uu); ELL_3M_SCALE_SET(matB, sval[0], sval[1], sval[2]); ell_3m_pre_mul_d(matA, matB); ell_3m_pre_mul_d(matA, vv); fprintf(stderr, "%s: uu * diag(sval) * vv = \n", me); ell_3m_print_d(stderr, matA); fprintf(stderr, "%s: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", me); */ ELL_3M_IDENTITY_SET(matA); ell_3m_pre_mul_d(matA, uu); ELL_3M_SCALE_SET(matB, sval[0], sval[1], sval[2]); ell_3m_pre_mul_d(matA, matB); ELL_3M_TRANSPOSE(matB, uu); ell_3m_pre_mul_d(matA, matB); TEN_M2T(ten, matA); partIdx = soidDoit(obj, lookSoid, sphere, gamma, res, (AIR_EXISTS(AB[0]) && AIR_EXISTS(AB[1])) ? AB : NULL, ten); ELL_4V_SET(q, 1, p[0], p[1], p[2]); ELL_4V_NORM(q, q, len); ell_q_to_3m_f(mR, q); ELL_43M_INSET(matBf, mR); limnObjectPartTransform(obj, partIdx, matBf); if (rad) { partIdx = limnObjectCylinderAdd(obj, lookRod, 0, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, (1-scale[0])/2, rad, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, (1+scale[0])/2, 0.0, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 0, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, (1-scale[0])/2, rad, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, -(1+scale[0])/2, 0.0, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 1, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, (1-scale[1])/2, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, (1+scale[1])/2, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 1, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, (1-scale[1])/2, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, -(1+scale[1])/2, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 2, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, rad, (1-scale[2])/2); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, 0.0, (1+scale[2])/2); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 2, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, rad, (1-scale[2])/2); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, 0.0, -(1+scale[2])/2); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); } file = airFopen(outS, stdout, "w"); airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); if (limnObjectWriteOFF(file, obj)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/test/didthis-shapes.txt0000664000175000017500000000104507740543760021627 0ustar domibeldomibelsetenv CAM "-fr 0 0 -30 -up 0 1 0 -rh" echo "1 1 1 1 3 -4 -3" \ | emap -i - $CAM -amb 0.08 0.08 0.08 -o emap.nrrd test/tt -n 6 -p -0.45 0.2 -0.35 \ | tend glyph -emap emap.nrrd -sat 0 \ $CAM \ -ur -1.2 1.2 -vr -0.9 0.9 -or -atr 0 \ -g s -psc 250 -gsc 0.35 -gr 20 -wd 0.5 0.0 0.0 \ -o demo-spheres.eps test/tt -n 6 -p -0.45 0.2 -0.35 \ | tend glyph -emap emap.nrrd -sat 0 \ $CAM \ -ur -1.2 1.2 -vr -0.9 0.9 -or -atr 0 \ -g q -psc 250 -gsc 0.30 -gr 20 -wd 0.5 0.2 0.0 \ -o demo-quads.eps rm -f emap.nrrd teem-1.11.0~svn6057/src/ten/test/didthis2.txt0000664000175000017500000000276507762053046020437 0ustar domibeldomibelsetenv CAM "-fr 0 0 -30 -up 0 1 0 -rh" echo "1 1 1 1 -1 -1 -3" \ | emap -i - $CAM -o emap.nrrd alias DOIT tend glyph -rt -emap emap.nrrd -bg 0 0 0.15 -sat 0 $CAM \ -ur -1.2 1.2 -vr -0.9 0.9 -is 720 540 -or -atr 0 -ns 16 test/tt -n 7 -p -0.45 0.2 -0.35 \ | DOIT -g b -gsc 0.23 \ | unu crop -min 0 0 0 -max 2 M M \ | unu quantize -b 8 -min 0 -max 1 -o tri-box.png & test/tt -n 7 -p -0.45 0.2 -0.35 \ | DOIT -g s -gsc 0.29 \ | unu crop -min 0 0 0 -max 2 M M \ | unu quantize -b 8 -min 0 -max 1 -o tri-sph.png & test/tt -n 7 -p -0.45 0.2 -0.35 \ | DOIT -g c -gsc 0.25 \ | unu crop -min 0 0 0 -max 2 M M \ | unu quantize -b 8 -min 0 -max 1 -o tri-cyl.png & test/tt -n 7 -p -0.45 0.2 -0.35 \ | DOIT -g q -gsc 0.27 -ns 1 \ | unu crop -min 0 0 0 -max 2 M M \ | unu quantize -b 8 -min 0 -max 1 -o tri-sqd.png echo "1 1 1 1 0 0 -4" \ | emap -i - $CAM -amb 0 0 0 -o tmp0.nrrd unu 2op ^ tmp0.nrrd 2 | unu 2op - 1 - | unu 2op x - 3.14159 | unu 1op cos \ | unu 2op + - 1 | unu 2op / - 2 -o tmp1.nrrd unu 3op lerp 1.0 tmp0.nrrd tmp1.nrrd -o emap.nrrd rm -f tmp0.nrrd tmp1.nrrd alias DOIT tend glyph -rt -emap emap.nrrd -bg 1 1 1 -sat 0 $CAM \ -ur -1.2 1.2 -vr -0.9 0.9 -is 720 540 -or -atr 0 -ns 16 test/tt -n 7 -p -0.45 0.2 -0.35 -ca 0.6 \ | DOIT -g q -sh 4 -gsc 0.27 \ | unu crop -min 0 0 0 -max 2 M M \ | unu quantize -b 8 -min 0 -max 1 -o tri-sqd2.png unu pad -i tri-sqd2.png -min 0 0 -90 -max M M m+719 \ | unu gamma -g 1.4 -max 240 \ | unu quantize -b 8 -min 0 -max 240 -o tri-sqd2-pad.png teem-1.11.0~svn6057/src/ten/test/tem.c0000664000175000017500000000573212042367142017102 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" char *info = ("Test EM bimodal histogram fitting."); int main(int argc, const char *argv[]) { const char *me; char *err; hestOpt *hopt=NULL; airArray *mop; Nrrd *nhisto; tenEMBimodalParm *biparm; double minprob[2]; mop = airMopNew(); me = argv[0]; biparm = tenEMBimodalParmNew(); airMopAdd(mop, biparm, (airMopper)tenEMBimodalParmNix, airMopAlways); hestOptAdd(&hopt, NULL, "histogram", airTypeOther, 1, 1, &nhisto, NULL, "The 1-D histogram to analyize", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "ts", "two stage", airTypeInt, 0, 0, &(biparm->twoStage), NULL, "use two-stage processing"); hestOptAdd(&hopt, "v", "verbose", airTypeInt, 1, 1, &(biparm->verbose), "1", "verbosity level"); hestOptAdd(&hopt, "mp", "minprob 1,2", airTypeDouble, 2, 2, minprob, "0 0", "minimum significant posterior probabilies, for first and " "second stages"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); biparm->minProb = minprob[0]; biparm->minProb2 = minprob[1]; if (tenEMBimodal(biparm, nhisto)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: error doing fitting:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "%s: bimodal histogram fit\n", me); fprintf(stderr, "material 1 (%g%%): mean = %g, stdv = %g\n", 100*(biparm->fraction1), biparm->mean1, biparm->stdv1); fprintf(stderr, "material 2 (%g%%): mean = %g, stdv = %g\n", 100*(1 - biparm->fraction1), biparm->mean2, biparm->stdv2); fprintf(stderr, " ---> optimal threshold = %g (confidence = %g)\n", biparm->threshold, biparm->confidence); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/test/csim.c0000664000175000017500000003053412042367142017246 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" static int csimDo(double tm[7], double tcov[21], double rm[3], double rv[3], Nrrd *ntbuff, tenEstimateContext *tec, double *dwibuff, double sigma, double bvalue, double B0, unsigned int NN, int randrot, double _tenOrig[7]) { char me[]="csimDo"; double *tbuff; unsigned int II, taa, tbb, cc; if (!(ntbuff && ntbuff->data && 2 == ntbuff->dim && 7 == ntbuff->axis[0].size && NN == ntbuff->axis[1].size)) { biffAddf(TEN, "%s: ntbuff not allocated for 2-by-%u array of %s", me, NN, airEnumStr(nrrdType, nrrdTypeDouble)); return 1; } /* find all tensors from simulated DWIs */ tbuff = AIR_CAST(double *, ntbuff->data); for (II=0; II 0.5) { double tdiff[7]; TEN_T_SUB(tdiff, _tenOrig, tbuff); fprintf(stderr, "!%s: %g\n" " (%g) %g,%g,%g %g,%g %g\n" " (%g) %g,%g,%g %g,%g %g\n", me, TEN_T_NORM(tdiff), _tenOrig[0], _tenOrig[1], _tenOrig[2], _tenOrig[3], _tenOrig[4], _tenOrig[5], _tenOrig[6], tbuff[0], tbuff[1], tbuff[2], tbuff[3], tbuff[4], tbuff[5], tbuff[6]); } */ tbuff += 7; } /* find mean tensor, and mean R_i */ tbuff = AIR_CAST(double *, ntbuff->data); TEN_T_SET(tm, 0, 0, 0, 0, 0, 0, 0); ELL_3V_SET(rm, 0, 0, 0); for (II=0; IIdata); for (II=0; IIaxis[1].size; sizeY = _ninTen->axis[2].size; sizeZ = _ninTen->axis[3].size; if (!(3 == _ninB0->dim && sizeX == _ninB0->axis[0].size && sizeY == _ninB0->axis[1].size && sizeZ == _ninB0->axis[2].size)) { char stmp[3][AIR_STRLEN_SMALL]; fprintf(stderr, "%s: given B0 (%u-D) volume not 3-D %sx%sx%s", me, _ninB0->dim, airSprintSize_t(stmp[0], sizeX), airSprintSize_t(stmp[1], sizeY), airSprintSize_t(stmp[2], sizeZ)); airMopError(mop); return 1; } ninTen = nrrdNew(); airMopAdd(mop, ninTen, (airMopper)nrrdNuke, airMopOnError); nmask = nrrdNew(); airMopAdd(mop, nmask, (airMopper)nrrdNuke, airMopOnError); ninB0 = nrrdNew(); airMopAdd(mop, ninB0, (airMopper)nrrdNuke, airMopOnError); noutCovar = nrrdNew(); airMopAdd(mop, noutCovar, (airMopper)nrrdNuke, airMopOnError); noutTen = nrrdNew(); airMopAdd(mop, noutTen, (airMopper)nrrdNuke, airMopOnError); noutRmv = nrrdNew(); airMopAdd(mop, noutRmv, (airMopper)nrrdNuke, airMopOnError); ntbuff = nrrdNew(); airMopAdd(mop, ntbuff, (airMopper)nrrdNuke, airMopOnError); if (nrrdConvert(ninTen, _ninTen, nrrdTypeDouble) || nrrdSlice(nmask, ninTen, 0, 0) || nrrdConvert(ninB0, _ninB0, nrrdTypeDouble) || nrrdMaybeAlloc_va(noutTen, nrrdTypeDouble, 4, AIR_CAST(size_t, 7), sizeX, sizeY, sizeZ) || nrrdMaybeAlloc_va(noutCovar, nrrdTypeDouble, 4, AIR_CAST(size_t, 21), sizeX, sizeY, sizeZ) || nrrdMaybeAlloc_va(noutRmv, nrrdTypeDouble, 4, AIR_CAST(size_t, 6), sizeX, sizeY, sizeZ) || nrrdMaybeAlloc_va(ntbuff, nrrdTypeDouble, 2, AIR_CAST(size_t, 7), NN)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting up tec:\n%s\n", me, err); airMopError(mop); return 1; } tec = tenEstimateContextNew(); airMopAdd(mop, tec, (airMopper)tenEstimateContextNix, airMopAlways); E = 0; if (!E) E |= tenEstimateMethodSet(tec, tenEstimate1MethodLLS); if (!E) E |= tenEstimateValueMinSet(tec, 0.000000001); if (!E) E |= tenEstimateGradientsSet(tec, ngrad, bval, AIR_TRUE); if (!E) E |= tenEstimateThresholdSet(tec, 0, 0); if (!E) E |= tenEstimateUpdate(tec); if (E) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting up tec:\n%s\n", me, err); airMopError(mop); return 1; } airSrandMT(seed); fprintf(stderr, "!%s: randrot = %d\n", me, randrot); if (1) { unsigned int II; unsigned int nsamp; double *inTen, *outTen, *outCovar, *outRmv, *dwibuff, (*lup)(const void *, size_t); char doneStr[AIR_STRLEN_SMALL]; dwibuff = AIR_CAST(double *, calloc(ngrad->axis[1].size, sizeof(double))); airMopAdd(mop, dwibuff, airFree, airMopAlways); nsamp = sizeX*sizeY*sizeZ; inTen = AIR_CAST(double *, ninTen->data); lup = nrrdDLookup[nrrdTypeDouble]; outTen = AIR_CAST(double *, noutTen->data); outCovar = AIR_CAST(double *, noutCovar->data); outRmv = AIR_CAST(double *, noutRmv->data); fprintf(stderr, "!%s: simulating ... ", me); fflush(stderr); for (II=0; IIdata, II), NN, randrot, inTen)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } inTen += 7; outTen += 7; outCovar += 21; outRmv += 6; } fprintf(stderr, "%s\n", airDoneStr(0, II, nsamp, doneStr)); } axmap[0] = -1; axmap[1] = 1; axmap[2] = 2; axmap[3] = 3; if (nrrdSplice(noutTen, noutTen, nmask, 0, 0) || nrrdAxisInfoCopy(noutTen, ninTen, axmap, NRRD_AXIS_INFO_SIZE_BIT) || nrrdAxisInfoCopy(noutCovar, ninTen, axmap, NRRD_AXIS_INFO_SIZE_BIT) || nrrdAxisInfoCopy(noutRmv, ninTen, axmap, NRRD_AXIS_INFO_SIZE_BIT) || nrrdBasicInfoCopy(noutTen, ninTen, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE) || nrrdBasicInfoCopy(noutCovar, ninTen, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE) || nrrdBasicInfoCopy(noutRmv, ninTen, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE) || nrrdSave(outTenS, noutTen, NULL) || nrrdSave(outCovarS, noutCovar, NULL) || nrrdSave(outRmvS, noutRmv, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/test/didthis-helix.txt0000664000175000017500000000133507771323423021453 0ustar domibeldomibeltend helix -s 38 39 40 -r 0.5 -R 1.2 -S 2 -o tmp.nrrd setenv CAM "-fr 3 0 -5 -up 0 0 -1 -rh" tend anvol -i tmp.nrrd -a fa \ | unu 2op - - 0.01 \ | mrender -i - $CAM -dn -1 -di 0 -df 1 -ar \ -ur -1.1 1.1 -vr -1.1 1.1 -is 120 120 -q val -m histo-min -o - \ | unu 2op exists - 0 \ | unu quantize -b 8 -o dist.png echo "1 1 1 1 -1 -1 -3" \ | emap -i - $CAM -o emap.nrrd tend glyph -i tmp.nrrd -emap emap.nrrd -bg 0.7 0.7 0.7 -sat 0.8 \ $CAM -ur -0.92 0.92 -vr -1.0 1.2 \ -psc 345 -gsc 0.03 -atr 0.6 -o whole.eps tend slice -i tmp.nrrd -a 2 -p 0 \ | tend glyph -emap emap.nrrd -bg 0.7 0.7 0.7 -sat 0.8 \ $CAM -ur -0.3 0.3 -vr 0.2 0.8 \ -wd 1.6 0.8 0 -psc 900 -gsc 0.03 -atr 0.6 -o slice.eps rm -f emap.nrrd teem-1.11.0~svn6057/src/ten/test/roistat.c0000664000175000017500000001244312042367142017777 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" char *info = ("tensor ROI information."); int main(int argc, const char *argv[]) { const char *me; char *err; hestOpt *hopt=NULL; airArray *mop; unsigned int sx, sy, sz, ss, ii, anisoTypeNum, anisoTypeIdx, roiVoxNum, roiVoxIdx, statNum, statIdx; float *ten, *roi, *aniso, eval[3], *stat; Nrrd *nten, *_nroi, *nroi, *naniso, *nstat; int *anisoType, *measr; size_t anisoSize[2]; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "r,roi", "roi", airTypeOther, 1, 1, &_nroi, NULL, "ROI volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "i,input", "volume", airTypeOther, 1, 1, &nten, "-", "tensor volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "a,aniso", "aniso", airTypeEnum, 1, -1, &anisoType, NULL, "which anisotropy measures to measure", &anisoTypeNum, tenAniso); hestOptAdd(&hopt, "m,measr", "measr", airTypeEnum, 1, -1, &measr, NULL, "which measures/statistics to calculate", &statNum, nrrdMeasure); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (tenTensorCheck(nten, nrrdTypeFloat, AIR_TRUE, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: didn't get tensor input:\n%s\n", me, err); airMopError(mop); return 1; } nroi = nrrdNew(); airMopAdd(mop, nroi, AIR_CAST(airMopper, nrrdNuke), airMopAlways); if (nrrdConvert(nroi, _nroi, nrrdTypeFloat)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't convert ROI to float:\n%s\n", me, err); airMopError(mop); return 1; } sx = nten->axis[1].size; sy = nten->axis[2].size; sz = nten->axis[3].size; if (!(3 == nroi->dim && sx == nroi->axis[0].size && sy == nroi->axis[1].size && sz == nroi->axis[2].size)) { fprintf(stderr, "%s: ROI dimension or axis sizes don't match volume", me); airMopError(mop); return 1; } ss = sx*sy*sz; ten = AIR_CAST(float*, nten->data); roi = AIR_CAST(float*, nroi->data); /* NOTE: for time being the statistics are not weighted, because nrrdMeasureLine[]() can't take a weight vector... */ /* find number of voxels in ROI */ roiVoxNum = 0; for (ii=0; ii 0); } /* fprintf(stderr, "%s: # voxels in ROI == %u\n", me, roiVoxNum); */ /* allocate anisotropy buffers */ naniso = nrrdNew(); airMopAdd(mop, naniso, AIR_CAST(airMopper, nrrdNuke), airMopAlways); anisoSize[0] = roiVoxNum; anisoSize[1] = anisoTypeNum; if (nrrdMaybeAlloc_nva(naniso, nrrdTypeFloat, 2, anisoSize)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate aniso:\n%s\n", me, err); airMopError(mop); return 1; } aniso = AIR_CAST(float *, naniso->data); /* store all anisotropies in all ROI voxels */ roiVoxIdx = 0; for (ii=0; ii 0) { tenEigensolve_f(eval, NULL, ten + 7*ii); for (anisoTypeIdx=0; anisoTypeIdxdata); for (anisoTypeIdx=0; anisoTypeIdxdata; for (zi=0; zi epsilon) { /* three distinct roots- this is the most common case */ double mm, ss, cc; theta = atan2(sqrt(D), R)/3.0; mm = sqrt(Q); ss = sin(theta); cc = cos(theta); eval[0] = 2*mm*cc; eval[1] = mm*(-cc + sqrt(3.0)*ss); eval[2] = mm*(-cc - sqrt(3.0)*ss); roots = ROOT_THREE; /* else D is near enough to zero */ } else if (R < -epsilon || epsilon < R) { double U; /* one double root and one single root */ U = airCbrt(R); /* cube root function */ if (U > 0) { eval[0] = 2*U; eval[1] = -U; eval[2] = -U; } else { eval[0] = -U; eval[1] = -U; eval[2] = 2*U; } roots = ROOT_SINGLE_DOUBLE; } else { /* a triple root! */ eval[0] = eval[1] = eval[2] = 0.0; roots = ROOT_TRIPLE; } teem-1.11.0~svn6057/src/ten/test/tqgl.c0000664000175000017500000002013312042367142017254 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ten.h" #include "../privateTen.h" /* BAD gordon */ extern double _tenQGL_Kdist(const double RThZA[3], const double RThZB[3]); extern void _tenQGL_Klog(double klog[3], const double RThZA[3], const double RThZB[3]); extern void _tenQGL_Kexp(double RThZB[3], const double RThZA[3], const double klog[3]); extern double _tenQGL_Rdist(const double RThPhA[3], const double RThPhB[3]); extern void _tenQGL_Rlog(double rlog[3], const double RThPhA[3], const double RThPhB[3]); extern void _tenQGL_Rexp(double RThPhB[3], const double RThPhA[3], const double rlog[3]); /* normalized gradients of k or r invariants, in XYZ space, to help determine if path is really a loxodrome */ void kgrads(double grad[3][3], const double eval[3]) { double rtz[3]; tenTripleConvertSingle_d(rtz, tenTripleTypeRThetaZ, eval, tenTripleTypeEigenvalue); ELL_3V_SET(grad[0], cos(rtz[1]), sin(rtz[1]), 0); ELL_3V_SET(grad[1], -sin(rtz[1]), cos(rtz[1]), 0); ELL_3V_SET(grad[2], 0, 0, 1); } void rgrads(double grad[3][3], const double eval[3]) { double rtp[3]; tenTripleConvertSingle_d(rtp, tenTripleTypeRThetaPhi, eval, tenTripleTypeEigenvalue); ELL_3V_SET(grad[0], cos(rtp[1])*sin(rtp[2]), sin(rtp[1])*sin(rtp[2]), cos(rtp[2])); ELL_3V_SET(grad[1], -sin(rtp[1]), cos(rtp[1]), 0); ELL_3V_SET(grad[2], cos(rtp[1])*cos(rtp[2]), sin(rtp[1])*cos(rtp[2]), -sin(rtp[2])); } char *info = ("quaternion geo-lox hacking. Actually all this does is " "help debug the analytic loxodrome part, not the quaternion " "geodesic part."); int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt=NULL; airArray *mop; double tripA[3], tripB[3], evalA[3], evalB[3], rt_A[3], rt_B[3], trip[3], eval[3], lasteval[3], lastxyz[3], logAB[3], ndist; int ittype, ottype, ptype, rttype; unsigned int NN, ii; tenInterpParm *tip; void (*interp)(double oeval[3], const double evalA[3], const double evalB[3], const double tt); double (*qdist)(const double RTh_A[3], const double RTh_B[3]); void (*qlog)(double klog[3], const double RThZA[3], const double RThZB[3]); void (*qexp)(double RThZB[3], const double RThZA[3], const double klog[3]); void (*grads)(double grad[3][3], const double eval[3]); me = argv[0]; mop = airMopNew(); tip = tenInterpParmNew(); airMopAdd(mop, tip, (airMopper)tenInterpParmNix, airMopAlways); hestOptAdd(&hopt, "a", "start", airTypeDouble, 3, 3, tripA, NULL, "start triple of values"); hestOptAdd(&hopt, "b", "end", airTypeDouble, 3, 3, tripB, NULL, "end triple of values"); hestOptAdd(&hopt, "it", "type", airTypeEnum, 1, 1, &ittype, NULL, "type of given start and end triples", NULL, tenTripleType); hestOptAdd(&hopt, "ot", "type", airTypeEnum, 1, 1, &ottype, NULL, "type of triples for output", NULL, tenTripleType); hestOptAdd(&hopt, "p", "type", airTypeEnum, 1, 1, &ptype, NULL, "type of path interpolation", NULL, tenInterpType); hestOptAdd(&hopt, "n", "# steps", airTypeUInt, 1, 1, &NN, "100", "number of steps along path"); hestOptAdd(&hopt, "v", "verbosity", airTypeInt, 1, 1, &(tip->verbose), "0", "verbosity"); hestOptAdd(&hopt, "s", "stepsize", airTypeDouble, 1, 1, &(tip->convStep), "1", "step size in update"); hestOptAdd(&hopt, "r", "recurse", airTypeInt, 0, 0, &(tip->enableRecurse), NULL, "enable recursive solution, when useful"); hestOptAdd(&hopt, "mn", "minnorm", airTypeDouble, 1, 1, &(tip->minNorm), "0.000001", "minnorm of something"); hestOptAdd(&hopt, "mi", "maxiter", airTypeUInt, 1, 1, &(tip->maxIter), "0", "if non-zero, max # iterations for computation"); hestOptAdd(&hopt, "c", "conv", airTypeDouble, 1, 1, &(tip->convEps), "0.0001", "convergence threshold of length fraction"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( tenInterpTypeQuatGeoLoxK == ptype || tenInterpTypeQuatGeoLoxR == ptype )) { fprintf(stderr, "%s: need type %s or %s, not %s\n", me, airEnumStr(tenInterpType, tenInterpTypeQuatGeoLoxK), airEnumStr(tenInterpType, tenInterpTypeQuatGeoLoxR), airEnumStr(tenInterpType, ptype)); airMopError(mop); return 1; } if (tenInterpTypeQuatGeoLoxK == ptype) { interp = tenQGLInterpTwoEvalK; qdist = _tenQGL_Kdist; qlog = _tenQGL_Klog; qexp = _tenQGL_Kexp; grads = kgrads; rttype = tenTripleTypeRThetaZ; } else { interp = tenQGLInterpTwoEvalR; qdist = _tenQGL_Rdist; qlog = _tenQGL_Rlog; qexp = _tenQGL_Rexp; grads = rgrads; rttype = tenTripleTypeRThetaPhi; } fprintf(stderr, "%s: (%s) %f %f %f \n--%s--> %f %f %f\n", me, airEnumStr(tenTripleType, ittype), tripA[0], tripA[1], tripA[2], airEnumStr(tenInterpType, ptype), tripB[0], tripB[1], tripB[2]); tenTripleConvertSingle_d(evalA, tenTripleTypeEigenvalue, tripA, ittype); tenTripleConvertSingle_d(evalB, tenTripleTypeEigenvalue, tripB, ittype); tenTripleConvertSingle_d(rt_A, rttype, tripA, ittype); tenTripleConvertSingle_d(rt_B, rttype, tripB, ittype); ndist = 0; ELL_3V_SET(lasteval, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(lastxyz, AIR_NAN, AIR_NAN, AIR_NAN); qlog(logAB, rt_A, rt_B); fprintf(stderr, "%s: log = %g %g %g (%g)\n", me, logAB[0], logAB[1], logAB[2], ELL_3V_LEN(logAB)); for (ii=0; iiaxis[1].size) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: got %u DWIs, but %s gradient directions", me, dwiLen, airSprintSize_t(stmp, ngrad->axis[1].size)); return 1; } for (ni=0; nidim )) { biffAddf(TEN, "%s: didn't get a set of 3-D arrays (got %d-D)", me, ndwi[0]->dim); return 1; } if (!( AIR_IN_CL(-1, reference, (int)dwiLen-1) )) { biffAddf(TEN, "%s: reference index %d not in valid range [-1,%d]", me, reference, dwiLen-1); return 1; } if (!( AIR_EXISTS(bwX) && AIR_EXISTS(bwY) )) { biffAddf(TEN, "%s: bwX, bwY don't both exist", me); return 1; } if (!( bwX >= 0 && bwY >= 0 )) { biffAddf(TEN, "%s: bwX (%g) and bwY (%g) are not both non-negative", me, bwX, bwY); return 1; } return 0; } /* ** this assumes that all nblur[i] are valid nrrds, and does nothing ** to manage them */ int _tenEpiRegBlur(Nrrd **nblur, Nrrd **ndwi, unsigned int dwiLen, double bwX, double bwY, int verb) { static const char me[]="_tenEpiRegBlur"; NrrdResampleInfo *rinfo; airArray *mop; size_t ni, sx, sy, sz; double savemin[2], savemax[2]; if (!( bwX || bwY )) { if (verb) { fprintf(stderr, "%s:\n ", me); fflush(stderr); } for (ni=0; niaxis[0].size; sy = ndwi[0]->axis[1].size; sz = ndwi[0]->axis[2].size; mop = airMopNew(); rinfo = nrrdResampleInfoNew(); airMopAdd(mop, rinfo, (airMopper)nrrdResampleInfoNix, airMopAlways); if (bwX) { rinfo->kernel[0] = nrrdKernelGaussian; rinfo->parm[0][0] = bwX; rinfo->parm[0][1] = 3.0; /* how many stnd devs do we cut-off at */ } else { rinfo->kernel[0] = NULL; } if (bwY) { rinfo->kernel[1] = nrrdKernelGaussian; rinfo->parm[1][0] = bwY; rinfo->parm[1][1] = 3.0; /* how many stnd devs do we cut-off at */ } else { rinfo->kernel[1] = NULL; } rinfo->kernel[2] = NULL; ELL_3V_SET(rinfo->samples, sx, sy, sz); ELL_3V_SET(rinfo->min, 0, 0, 0); ELL_3V_SET(rinfo->max, sx-1, sy-1, sz-1); rinfo->boundary = nrrdBoundaryBleed; rinfo->type = nrrdTypeDefault; rinfo->renormalize = AIR_TRUE; rinfo->clamp = AIR_TRUE; if (verb) { fprintf(stderr, "%s:\n ", me); fflush(stderr); } for (ni=0; niaxis[0].min; savemax[0] = ndwi[ni]->axis[0].max; savemin[1] = ndwi[ni]->axis[1].min; savemax[1] = ndwi[ni]->axis[1].max; ndwi[ni]->axis[0].min = 0; ndwi[ni]->axis[0].max = sx-1; ndwi[ni]->axis[1].min = 0; ndwi[ni]->axis[1].max = sy-1; if (nrrdSpatialResample(nblur[ni], ndwi[ni], rinfo)) { biffMovef(TEN, NRRD, "%s: trouble blurring ndwi[%u]", me, (unsigned int)ni); airMopError(mop); return 1; } ndwi[ni]->axis[0].min = savemin[0]; ndwi[ni]->axis[0].max = savemax[0]; ndwi[ni]->axis[1].min = savemin[1]; ndwi[ni]->axis[1].max = savemax[1]; } if (verb) { fprintf(stderr, "done\n"); } airMopOkay(mop); return 0; } int _tenEpiRegThresholdFind(double *DWthrP, Nrrd **nin, int ninLen, int save, double expo) { static const char me[]="_tenEpiRegThresholdFind"; Nrrd *nhist, *ntmp; airArray *mop; int ni, bins, E; double min=0, max=0; NrrdRange *range; mop = airMopNew(); airMopAdd(mop, nhist=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); for (ni=0; nimin; max = range->max; } else { min = AIR_MIN(min, range->min); max = AIR_MAX(max, range->max); } range = nrrdRangeNix(range); } bins = AIR_MIN(1024, (int)(max - min + 1)); ntmp->axis[0].min = min; ntmp->axis[0].max = max; for (ni=0; niaxis[0].size; sy = nblur[0]->axis[1].size; sz = nblur[0]->axis[2].size; for (ni=0; nidata); for (I=0; Itype](nblur[ni]->data, I); val -= AIR_CAST(float, DWthr); thr[I] = (val >= 0 ? 1 : 0); } } if (verb) { fprintf(stderr, "done\n"); } airMopOkay(mop); return 0; } /* ** _tenEpiRegBB: find the biggest bright CC */ int _tenEpiRegBB(Nrrd *nval, Nrrd *nsize) { unsigned char *val; int *size, big; unsigned int ci; val = (unsigned char *)(nval->data); size = (int *)(nsize->data); big = 0; for (ci=0; ciaxis[0].size; ci++) { big = val[ci] ? AIR_MAX(big, size[ci]) : big; } return big; } int _tenEpiRegCC(Nrrd **nthr, int ninLen, int conny, int verb) { static const char me[]="_tenEpiRegCC"; Nrrd *nslc, *ncc, *nval, *nsize; airArray *mop; int ni, z, sz, big; mop = airMopNew(); airMopAdd(mop, nslc=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nval=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ncc=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nsize=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); sz = nthr[0]->axis[2].size; if (verb) { fprintf(stderr, "%s:\n ", me); fflush(stderr); } for (ni=0; ni there was no bright CC on this slice, move on */ } } } } if (verb) { fprintf(stderr, "done\n"); } airMopOkay(mop); return 0; } #define MEAN_X 0 #define MEAN_Y 1 #define M_02 2 #define M_11 3 #define M_20 4 /* ** _tenEpiRegMoments() ** ** the moments are stored in (of course) a nrrd, one scanline per slice, ** with each scanline containing: ** ** 0 1 2 3 4 ** mean(x) mean(y) M_02 M_11 M_20 */ int _tenEpiRegMoments(Nrrd **nmom, Nrrd **nthresh, unsigned int ninLen, int verb) { static const char me[]="_tenEpiRegMoments"; size_t sx, sy, sz, xi, yi, zi, ni; double N, mx, my, cx, cy, x, y, M02, M11, M20, *mom; float val; unsigned char *thr; sx = nthresh[0]->axis[0].size; sy = nthresh[0]->axis[1].size; sz = nthresh[0]->axis[2].size; if (verb) { fprintf(stderr, "%s:\n ", me); fflush(stderr); } for (ni=0; nidata); mom = (double *)(nmom[ni]->data); for (zi=0; zidata, ** xfr[0 + 3*(zi + sz*(A + ninLen*B))] is shear, ** xfr[1 + " ] is scale, and ** xfr[2 + " ] is translate in the transform ** that maps slice zi from volume A to volume B. */ int _tenEpiRegPairXforms(Nrrd *npxfr, Nrrd **nmom, int ninLen) { static const char me[]="_tenEpiRegPairXforms"; double *xfr, *A, *B, hh, ss, tt; int ai, bi, zi, sz; sz = nmom[0]->axis[1].size; if (nrrdMaybeAlloc_va(npxfr, nrrdTypeDouble, 4, AIR_CAST(size_t, 5), AIR_CAST(size_t, sz), AIR_CAST(size_t, ninLen), AIR_CAST(size_t, ninLen))) { biffMovef(TEN, NRRD, "%s: couldn't allocate transform nrrd", me); return 1; } nrrdAxisInfoSet_va(npxfr, nrrdAxisInfoLabel, "mx,my,h,s,t", "zi", "orig", "target"); xfr = (double *)(npxfr->data); for (bi=0; bidata) + 5*zi; B = (double*)(nmom[bi]->data) + 5*zi; ss = sqrt((A[M_20]*B[M_02] - B[M_11]*B[M_11]) / (A[M_20]*A[M_02] - A[M_11]*A[M_11])); hh = (B[M_11] - ss*A[M_11])/A[M_20]; tt = B[MEAN_Y] - A[MEAN_Y]; ELL_5V_SET(xfr + 5*(zi + sz*(ai + ninLen*bi)), A[MEAN_X], A[MEAN_Y], hh, ss, tt); } } } return 0; } #define SHEAR 2 #define SCALE 3 #define TRAN 4 int _tenEpiRegEstimHST(Nrrd *nhst, Nrrd *npxfr, int ninLen, Nrrd *ngrad) { static const char me[]="_tenEpiRegEstimHST"; double *hst, *grad, *mat, *vec, *ans, *pxfr, *gA, *gB; int z, sz, A, B, npairs, ri; Nrrd **nmat, *nvec, **ninv, *nans; airArray *mop; int order; order = 1; sz = npxfr->axis[1].size; npairs = ninLen*(ninLen-1); mop = airMopNew(); nmat = (Nrrd**)calloc(sz, sizeof(Nrrd*)); ninv = (Nrrd**)calloc(sz, sizeof(Nrrd*)); airMopAdd(mop, nmat, airFree, airMopAlways); airMopAdd(mop, ninv, airFree, airMopAlways); for (z=0; zdata); for (z=0; zdata) + (1 == order ? 9 : 27)*z; mat = (double *)(nmat[z]->data); ri = 0; for (A=0; Adata) + 0 + 5*(z + sz*(A + ninLen*B)); gA = grad + 0 + 3*A; gB = grad + 0 + 3*B; if (1 == order) { ELL_3V_SET(mat + 3*ri, gB[0] - pxfr[SCALE]*gA[0], gB[1] - pxfr[SCALE]*gA[1], gB[2] - pxfr[SCALE]*gA[2]); } else { ELL_9V_SET(mat + 9*ri, gB[0] - pxfr[SCALE]*gA[0], gB[1] - pxfr[SCALE]*gA[1], gB[2] - pxfr[SCALE]*gA[2], gB[0]*gB[0]*gB[0] - pxfr[SCALE]*gA[0]*gA[0]*gA[0], gB[1]*gB[1]*gB[1] - pxfr[SCALE]*gA[1]*gA[1]*gA[1], gB[2]*gB[2]*gB[2] - pxfr[SCALE]*gA[2]*gA[2]*gA[2], gB[0]*gB[1]*gB[2] - pxfr[SCALE]*gA[0]*gA[1]*gA[2], 1, 1); /* gB[0]*gB[1] - pxfr[SCALE]*gA[0]*gA[1], gB[0]*gB[2] - pxfr[SCALE]*gA[0]*gA[2], gB[1]*gB[2] - pxfr[SCALE]*gA[1]*gA[2]); */ } ri += 1; } } if (nrrdHasNonExist(nmat[z])) { /* as happens if there were zero slices in the segmentation output */ if (1 == order) { ELL_3V_SET(hst + 0*3, 0, 0, 0); ELL_3V_SET(hst + 1*3, 0, 0, 0); ELL_3V_SET(hst + 2*3, 0, 0, 0); } else { ELL_9V_SET(hst + 0*9, 0, 0, 0, 0, 0, 0, 0, 0, 0); ELL_9V_SET(hst + 1*9, 0, 0, 0, 0, 0, 0, 0, 0, 0); ELL_9V_SET(hst + 2*9, 0, 0, 0, 0, 0, 0, 0, 0, 0); } } else { if (ell_Nm_pseudo_inv(ninv[z], nmat[z])) { biffMovef(TEN, ELL, "%s: trouble estimating model (slice %d)", me, z); airMopError(mop); return 1; } } } vec = (double *)(nvec->data); /* ------ find Sx, Sy, Sz per slice */ for (z=0; zdata) + (1 == order ? 9 : 27)*z; ri = 0; for (A=0; Adata) + 0 + 5*(z + sz*(A + ninLen*B)); vec[ri] = pxfr[SCALE] - 1; ri += 1; } } if (ell_Nm_mul(nans, ninv[z], nvec)) { biffMovef(TEN, ELL, "%s: trouble estimating model (slice %d): Sx, Sy, Sz", me, z); airMopError(mop); return 1; } ans = (double *)(nans->data); if (1 == order) { ELL_3V_COPY(hst + 1*3, ans); } else { ELL_9V_COPY(hst + 1*9, ans); } } /* ------ find Hx, Hy, Hz per slice */ for (z=0; zdata) + (1 == order ? 9 : 27)*z; ri = 0; for (A=0; Adata) + 0 + 5*(z + sz*(A + ninLen*B)); vec[ri] = pxfr[SHEAR]; ri += 1; } } if (ell_Nm_mul(nans, ninv[z], nvec)) { biffAddf(TEN, ELL, "%s: trouble estimating model (slice %d): Hx, Hy, Hz", me, z); airMopError(mop); return 1; } ans = (double *)(nans->data); if (1 == order) { ELL_3V_COPY(hst + 0*3, ans); } else { ELL_9V_COPY(hst + 0*9, ans); } } /* ------ find Tx, Ty, Tz per slice */ for (z=0; zdata) + (1 == order ? 9 : 27)*z; ri = 0; for (A=0; Adata) + 0 + 5*(z + sz*(A + ninLen*B)); vec[ri] = pxfr[TRAN]; ri += 1; } } if (ell_Nm_mul(nans, ninv[z], nvec)) { biffMovef(TEN, ELL, "%s: trouble estimating model (slice %d): Tx, Ty, Tz", me, z); airMopError(mop); return 1; } ans = (double *)(nans->data); if (1 == order) { ELL_3V_COPY(hst + 2*3, ans); } else { ELL_9V_COPY(hst + 2*9, ans); } } airMopOkay(mop); return 0; } int _tenEpiRegFitHST(Nrrd *nhst, Nrrd **_ncc, int ninLen, double goodFrac, int prog, int verb) { static const char me[]="_tenEpiRegFitHST"; airArray *mop; Nrrd *ncc, *ntA, *ntB, *nsd, *nl2; unsigned int cc, sz, zi, sh, hi; float *mess, *two, tmp; double *hst, x, y, xx, xy, mm, bb; mop = airMopNew(); airMopAdd(mop, ncc=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntA=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntB=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nsd=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nl2=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); /* do SD and L2 projections of the CCs along the DWI axis, integrate these over the X and Y axes of the slices, and define per-slice "messiness" as the quotient of the SD integral with the L2 integral */ if (verb) { fprintf(stderr, "%s: measuring segmentation uncertainty ... ", me); fflush(stderr); } if (nrrdJoin(ncc, (const Nrrd*const*)_ncc, ninLen, 0, AIR_TRUE) || nrrdProject(ntA, ncc, 0, nrrdMeasureSD, nrrdTypeFloat) || nrrdProject(ntB, ntA, 0, nrrdMeasureSum, nrrdTypeFloat) || nrrdProject(nsd, ntB, 0, nrrdMeasureSum, nrrdTypeFloat) || nrrdProject(ntA, ncc, 0, nrrdMeasureL2, nrrdTypeFloat) || nrrdProject(ntB, ntA, 0, nrrdMeasureSum, nrrdTypeFloat) || nrrdProject(nl2, ntB, 0, nrrdMeasureSum, nrrdTypeFloat) || nrrdArithBinaryOp(ntA, nrrdBinaryOpDivide, nsd, nl2)) { biffMovef(TEN, NRRD, "%s: trouble doing CC projections", me); airMopError(mop); return 1; } if (verb) { fprintf(stderr, "done\n"); } if (prog && _tenEpiRegSave("regtmp-messy.txt", ntA, NULL, 0, "segmentation uncertainty")) { biffMovef(TEN, NRRD, "%s: EpiRegSave failed", me); airMopError(mop); return 1; } /* now ntA stores the per-slice messiness */ mess = AIR_CAST(float*, ntA->data); /* allocate an array of 2 floats per slice */ sz = ntA->axis[0].size; two = AIR_CAST(float*, calloc(2*sz, sizeof(float))); if (!two) { biffAddf(TEN, "%s: couldn't allocate tmp buffer", me); airMopError(mop); return 1; } airMopAdd(mop, two, airFree, airMopAlways); /* initial ordering: messiness, slice index */ for (zi=0; zidata); sh = nhst->axis[0].size; for (hi=0; hiaxis[1].size; ninLen = npxfr->axis[2].size; if (-1 == reference) { /* we use the estimated H,S,T vectors to determine distortion as a function of gradient direction, and then invert this */ _g = (double*)(ngrad->data) + 0 + 3*ni; if (1 == order) { hst = (double*)(nhst->data) + 0 + 9*zi; *hhP = ELL_3V_DOT(_g, hst + 0*3); *ssP = 1 + ELL_3V_DOT(_g, hst + 1*3); *ttP = ELL_3V_DOT(_g, hst + 2*3); } else { hst = (double*)(nhst->data) + 0 + 27*zi; ELL_9V_SET(grad, _g[0], _g[1], _g[2], _g[0]*_g[0]*_g[0], _g[1]*_g[1]*_g[1], _g[2]*_g[2]*_g[2], _g[0]*_g[1]*_g[2], 0, 0); /* _g[0]*_g[1], _g[0]*_g[2], _g[1]*_g[2]); */ *hhP = ELL_9V_DOT(grad, hst + 0*9); *ssP = 1 + ELL_9V_DOT(grad, hst + 1*9); *ttP = ELL_9V_DOT(grad, hst + 2*9); } } else { /* we register against a specific DWI */ xfr = (double*)(npxfr->data) + 0 + 5*(zi + sz*(reference + ninLen*ni)); *hhP = xfr[2]; *ssP = xfr[3]; *ttP = xfr[4]; } return 0; } /* ** _tenEpiRegSliceWarp ** ** Apply [hh,ss,tt] transform to nin, putting results in nout, but with ** some trickiness: ** - nwght and nidx are already allocated to the the weights (type float) ** and indices for resampling nin with "kern" and "kparm" ** - nout is already allocated to the correct size and type ** - nin is type float, but output must be type nout->type ** - nin is been transposed to have the resampled axis fastest in memory, ** but nout output will not be transposed */ int _tenEpiRegSliceWarp(Nrrd *nout, Nrrd *nin, Nrrd *nwght, Nrrd *nidx, const NrrdKernel *kern, double *kparm, double hh, double ss, double tt, double cx, double cy) { float *wght, *in, pp, pf, tmp; int *idx; unsigned int supp; size_t sx, sy, xi, yi, pb, pi; double (*ins)(void *, size_t, double), (*clamp)(double); sy = nin->axis[0].size; sx = nin->axis[1].size; supp = AIR_CAST(unsigned int, kern->support(kparm)); ins = nrrdDInsert[nout->type]; clamp = nrrdDClamp[nout->type]; in = AIR_CAST(float*, nin->data); for (xi=0; xidata); wght = AIR_CAST(float*, nwght->data); for (yi=0; yidata); wght = AIR_CAST(float*, nwght->data); kern->evalN_f(wght, wght, 2*supp*sy, kparm); for (yi=0; yidata, xi + sx*yi, clamp(ss*tmp)); idx += 2*supp; wght += 2*supp; } in += sy; } return 0; } /* ** _tenEpiRegWarp() ** */ int _tenEpiRegWarp(Nrrd **ndone, Nrrd *npxfr, Nrrd *nhst, Nrrd *ngrad, Nrrd **nin, int ninLen, int reference, const NrrdKernel *kern, double *kparm, int verb) { static const char me[]="_tenEpiRegWarp"; Nrrd *ntmp, *nfin, *nslcA, *nslcB, *nwght, *nidx; airArray *mop; int sx, sy, sz, ni, zi, supp; double hh, ss, tt, cx, cy; mop = airMopNew(); airMopAdd(mop, ntmp=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nfin=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nslcA=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nslcB=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nwght=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nidx=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (verb) { fprintf(stderr, "%s:\n ", me); fflush(stderr); } sx = nin[0]->axis[0].size; sy = nin[0]->axis[1].size; sz = nin[0]->axis[2].size; cx = sx/2.0; cy = sy/2.0; supp = (int)kern->support(kparm); if (nrrdMaybeAlloc_va(nwght, nrrdTypeFloat, 2, AIR_CAST(size_t, 2*supp), AIR_CAST(size_t, sy)) || nrrdMaybeAlloc_va(nidx, nrrdTypeInt, 2, AIR_CAST(size_t, 2*supp), AIR_CAST(size_t, sy))) { biffMovef(TEN, NRRD, "%s: trouble allocating buffers", me); airMopError(mop); return 1; } for (ni=0; nidim) { biffAddf(TEN, "%s: need a 4-D input array, not %d-D", me, _nin->dim); return 1; } if (tenGradientCheck(_ngrad, nrrdTypeDefault, 6 /* <-- HEY: not sure */)) { biffAddf(TEN, "%s: problem with given gradient list", me); return 1; } rangeAxisNum = nrrdRangeAxesGet(_nin, rangeAxisIdx); if (0 == rangeAxisNum) { /* we fall back on old behavior */ dwiAx = 0; } else if (1 == rangeAxisNum) { /* thankfully there's exactly one range axis */ dwiAx = rangeAxisIdx[0]; } else { biffAddf(TEN, "%s: have %u range axes instead of 1, don't know which " "is DWI axis", me, rangeAxisNum); return 1; } ninLen = _nin->axis[dwiAx].size; /* outdated if (!( AIR_IN_CL(6, ninLen, 120) )) { biffAddf(TEN, "%s: %u (size of axis %u, and # DWIs) is unreasonable", me, ninLen, dwiAx); return 1; } */ if (ninLen != _ngrad->axis[1].size) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: ninLen %u != # grads %s", me, ninLen, airSprintSize_t(stmp, _ngrad->axis[1].size)); return 1; } mop = airMopNew(); ngrad = nrrdNew(); ndwigrad = nrrdNew(); airMopAdd(mop, ngrad, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ndwigrad, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(ngrad, _ngrad, nrrdTypeDouble) || nrrdConvert(ndwigrad, _ngrad, nrrdTypeDouble)) { /* HACK applies */ biffMovef(TEN, NRRD, "%s: trouble converting gradients to doubles", me); airMopError(mop); return 1; } nin = (Nrrd **)calloc(ninLen, sizeof(Nrrd*)); ndwi = (Nrrd **)calloc(ninLen, sizeof(Nrrd*)); nout = (Nrrd **)calloc(ninLen, sizeof(Nrrd*)); ndwiOut = (Nrrd **)calloc(ninLen, sizeof(Nrrd*)); if (!(nin && ndwi && nout && ndwiOut)) { biffAddf(TEN, "%s: trouble allocating local arrays", me); airMopError(mop); return 1; } airMopAdd(mop, nin, airFree, airMopAlways); airMopAdd(mop, ndwi, airFree, airMopAlways); airMopAdd(mop, nout, airFree, airMopAlways); airMopAdd(mop, ndwiOut, airFree, airMopAlways); dwiIdx = -1; grad = AIR_CAST(double *, ngrad->data); dwigrad = AIR_CAST(double *, ndwigrad->data); for (ninIdx=0; ninIdx 0.0) { /* we're not allowed to use only those images that are truly DWIs, or, (we are so allowed and) this is a true DWI */ dwiIdx++; ndwi[dwiIdx] = nrrdNew(); ndwiOut[dwiIdx] = nrrdNew(); airMopAdd(mop, ndwi[dwiIdx], (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ndwiOut[dwiIdx], (airMopper)nrrdNuke, airMopAlways); if (nrrdSlice(ndwi[dwiIdx], _nin, dwiAx, AIR_CAST(unsigned int, ninIdx))) { biffMovef(TEN, NRRD, "%s: trouble slicing at %d on axis %u", me, ninIdx, dwiAx); airMopError(mop); return 1; } /* NOTE: this works because dwiIdx <= ninIdx */ ELL_3V_COPY(dwigrad + 3*dwiIdx, grad + 3*ninIdx); } else { /* we are only looking at true DWIs, and this isn't one of them */ continue; } } if (-1 == dwiIdx) { biffAddf(TEN, "%s: somehow got no DWIs", me); airMopError(mop); return 1; } /* HEY: HACK! */ ndwigrad->axis[1].size = 1 + AIR_CAST(unsigned int, dwiIdx); if (tenEpiRegister3D(ndwiOut, ndwi, ndwigrad->axis[1].size, ndwigrad, reference, bwX, bwY, fitFrac, DWthr, doCC, kern, kparm, progress, verbose)) { biffAddf(TEN, "%s: trouble", me); airMopError(mop); return 1; } dwiIdx = -1; for (ninIdx=0; ninIdx 0.0) { /* we're not allowed to use only those images that are truly DWIs, or, (we are so allowed and) this is a true DWI */ dwiIdx++; nout[ninIdx] = ndwiOut[dwiIdx]; /* fprintf(stderr, "!%s: (A) nout[%u] = %p\n", me, ninIdx, nout[ninIdx]); */ } else { /* we are only looking at true DWIs, and this isn't one of them */ nout[ninIdx] = nrrdNew(); airMopAdd(mop, nout[ninIdx], (airMopper)nrrdNuke, airMopAlways); if (nrrdSlice(nout[ninIdx], _nin, dwiAx, ninIdx)) { biffMovef(TEN, NRRD, "%s: trouble slicing at %d on axis %u", me, dwiIdx, dwiAx); airMopError(mop); return 1; } /* fprintf(stderr, "!%s: (B) nout[%u] = %p\n", me, ninIdx, nout[ninIdx]); */ } } if (nrrdJoin(_nout, (const Nrrd*const*)nout, ninLen, dwiAx, AIR_TRUE)) { biffMovef(TEN, NRRD, "%s: trouble joining output", me); airMopError(mop); return 1; } nrrdAxisInfoCopy(_nout, _nin, NULL, NRRD_AXIS_INFO_NONE); if (nrrdBasicInfoCopy(_nout, _nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT)) { /* note that we're ALWAYS copying the key/value pairs- its just too annoying to have to set nrrdStateKeyValuePairsPropagate in order for the DWI-specific key/value pairs to be set */ biffMovef(TEN, NRRD, "%s:", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/qseg.c0000664000175000017500000002177412165631065016305 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define MAX_KMEANS_ITERATIONS 50 /* Calculate the Q-ball profile from DWIs */ void _tenQball(const double b, const int gradcount, const double svals[], const double grads[], double qvals[] ) { /* Not an optimal Q-ball implementation! (Taken from submission to MICCAI 2006) Should be solved analytically in the future, implemented from recent papers. */ int i,j; double d, dist, weight, min, max; AIR_UNUSED(b); min = max = svals[1] / svals[0]; for( i = 0; i < gradcount; i++ ) { d = svals[i+1] / svals[0]; if( d > max ) max = d; else if( d < min ) min = d; } for( i = 0; i < gradcount; i++ ) { qvals[i] = 0; for( j = 0; j < gradcount; j++ ) { d = AIR_AFFINE( min, svals[j+1] / svals[0], max, 0,1 ); dist = ELL_3V_DOT(grads + 3*i, grads + 3*j); dist = AIR_ABS(dist); weight = cos( 0.5 * AIR_PI * dist ); qvals[i] += d * weight*weight*weight*weight; } } return; } /* Segments DWIs into 2 segments based on Q-ball profiles */ void _tenSegsamp2(const int gradcount, const double qvals[], const double grads[], const double qpoints[], unsigned int seg[], double dists[]) { const int segcount = 2; int i, changed=AIR_TRUE; double centroids[ 3*2 ]; /* 3*segcount */ AIR_UNUSED(grads); _tenInitcent2(gradcount, qvals, qpoints, centroids); for( i = 0; i < MAX_KMEANS_ITERATIONS && changed; i++ ) { _tenCalcdists(segcount, centroids, gradcount, qpoints, dists); changed = _tenCalccent2(gradcount, qpoints, dists, centroids, seg); /* printf( "Seg[%d]\t= { ",i ); for( j = 0; j < gradcount; j++ ) printf( "%d ", seg[j] ); printf( changed ? "}\n" : "} Convergence!\n" ); */ } } /* Gives an inital choice of 2 centroids */ void _tenInitcent2(const int gradcount, const double qvals[], const double qpoints[], double centroids[6]) { int i, maxidx; double max, dist; /* Find largest peak in Q-ball */ maxidx = 0; for( i = 0; i < gradcount; i++ ) if( qvals[maxidx] < qvals[i] ) maxidx = i; ELL_3V_COPY( centroids, qpoints +3*maxidx ); /* printf("init: max=%d cent0=[%f %f %f]\n", maxidx, centroids[0], centroids[1], centroids[2]); */ /* Find peak/axis from Q-ball furthest away from first peak */ max = 0; for( i = 0; i < gradcount; i++ ) { dist = _tenPldist(qpoints +3*i, centroids); if (dist > max) { maxidx = i; max = dist; } } ELL_3V_COPY( centroids+3, qpoints +3*maxidx ); /* printf( "\ninit: max=%d cent1=[%f %f %f]\n", maxidx, centroids[3], centroids[4], centroids[5]); */ } /* Calculates 2 new centroids (and a new segmentation) from distances between Q-balls and centroids, returns true if segmentation changed */ int _tenCalccent2(const int gradcount, const double qpoints[], const double dists[], double centroid[6], unsigned int seg[]) { #if 0 /* HEY: Attempt to implement better line-adding by adding outerproducts of points and estimating major eigenvector afterwards */ int i,changed=AIR_FALSE; double sum0[9],sum1[9],mat[9], eval[3],evec[9]; ELL_3M_ZERO_SET( sum0 ); ELL_3M_ZERO_SET( sum1 ); for( i = 0; i < gradcount; i++ ) { if( dists[i] < dists[gradcount+i] ) { ELL_3MV_OUTER( mat, qpoints +3*i, qpoints +3*i ); ELL_3M_ADD2( sum0, sum0, mat ); changed = changed || (seg[i] != 0); seg[i] = 0; } else { ELL_3MV_OUTER( mat, qpoints +3*i +gradcount, qpoints +3*i +gradcount ); ELL_3M_ADD2( sum1, sum1, mat ); changed = changed || (seg[i] != 1); seg[i] = 1; } } ell_3m_eigensolve_d( eval, evec, sum0, 0 ); ELL_3V_COPY( centroid, evec + 3*ELL_MAX3_IDX( eval[0], eval[1], eval[2] ) ); /* ELL_3V_SCALE( centroid, ELL_3V_LEN( centroid ), centroid ); */ ell_3m_eigensolve_d( eval, evec, sum1, 0 ); ELL_3V_COPY( centroid +3, evec + 3*ELL_MAX3_IDX( eval[0], eval[1], eval[2] ) ); /* ELL_3V_SCALE( centroid +3, ELL_3V_LEN( centroid ), centroid +3); Normalize */ return changed; #endif int i, sign, seg0count=0, seg1count=0, changed=AIR_FALSE; double oldcentroid[6], diff[3], sum[3]; memcpy( oldcentroid, centroid, 6 * sizeof( double )); for( i = 0; i < gradcount; i++ ) { if( dists[ 0*gradcount +i] < dists[1*gradcount +i] ) { /* Try to resolve sign so that centroid do not end up as all 0 */ /* Choose signs so that the point lies "on the same side" as */ /* the previous centroid. */ diff[0] = oldcentroid[0] - qpoints[3*i +0]; diff[1] = oldcentroid[1] - qpoints[3*i +1]; diff[2] = oldcentroid[2] - qpoints[3*i +2]; sum[0] = oldcentroid[0] + qpoints[3*i +0]; sum[1] = oldcentroid[1] + qpoints[3*i +1]; sum[2] = oldcentroid[2] + qpoints[3*i +2]; sign = (diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]) < (sum[0]*sum[0] + sum[1]*sum[1] + sum[2]*sum[2]) ? -1 : +1; changed = changed || (seg[i] != 0); seg[i] = 0; centroid[0] += sign * qpoints[3*i +0]; centroid[1] += sign * qpoints[3*i +1]; centroid[2] += sign * qpoints[3*i +2]; seg0count++; } else { diff[0] = oldcentroid[3+0] - qpoints[3*i +0]; diff[1] = oldcentroid[3+1] - qpoints[3*i +1]; diff[2] = oldcentroid[3+2] - qpoints[3*i +2]; sum[0] = oldcentroid[3+0] + qpoints[3*i +0]; sum[1] = oldcentroid[3+1] + qpoints[3*i +1]; sum[2] = oldcentroid[3+2] + qpoints[3*i +2]; sign = (diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]) < (sum[0]*sum[0] + sum[1]*sum[1] + sum[2]*sum[2]) ? -1 : +1; changed = changed || (seg[i] != 1); seg[i] = 1; centroid[3+0] += sign * qpoints[3*i +0]; centroid[3+1] += sign * qpoints[3*i +1]; centroid[3+2] += sign * qpoints[3*i +2]; seg1count++; } } centroid[0] /= seg0count; centroid[1] /= seg0count; centroid[2] /= seg0count; centroid[3+0] /= seg1count; centroid[3+1] /= seg1count; centroid[3+2] /= seg1count; /* printf("cent = %f %f %f %f %f %f\n", centroid[0],centroid[1],centroid[2],centroid[3],centroid[4],centroid[5] ); */ /* Should give error if any segment contains less than 6 elements, i.e. if( seg0count < 6 || seg1count < 6 ), since that would imply that a tensor cannot be computed for that segment. */ return changed; } /* Converts Q-values and gradients to points on the Q-ball surface */ void _tenQvals2points(const int gradcount, const double qvals[], const double grads[], double qpoints[] ) { int i; memcpy( qpoints, grads, 3 * gradcount * sizeof( double ) ); for( i = 0; i < gradcount; i++ ) { qpoints[3*i +0] *= qvals[i]; qpoints[3*i +1] *= qvals[i]; qpoints[3*i +2] *= qvals[i]; } } /* Calculates the shortest distances from each centroid/axis to each Q-ball point */ void _tenCalcdists(const int centcount, const double centroid[], const int gradcount, const double qpoints[], double dists[] ) { int i,j; for( j = 0; j < centcount; j++ ) for( i = 0; i < gradcount; i++ ) dists[j*gradcount +i] = _tenPldist(&qpoints[3*i], ¢roid[3*j]); /* printf("dists = "); for( i = 0; i < 2*gradcount; i++ ) printf( "%f ", dists[i] ); printf("\n"); */ } /* Estimates the shortest distance from a point to a line going through the origin */ double _tenPldist( const double point[], const double line[] ) { double cross[3]; double negpoint[3]; negpoint[0] = -point[0]; negpoint[1] = -point[1]; negpoint[2] = -point[2]; ELL_3V_CROSS( cross, line, negpoint ); return ELL_3V_LEN( cross ) / ELL_3V_LEN( line ); } /* Converts a segmentation into a set of 0-1 weights */ void _tenSeg2weights(const int gradcount, const int seg[], const int segcount, double weights[] ) { int i,j; for( j = 0; j < segcount; j++ ) { for( i = 0; i < gradcount; i++ ) { weights[j*gradcount +i] = (seg[i] == j) ? 1 : 0; } } return; } teem-1.11.0~svn6057/src/ten/experSpec.c0000664000175000017500000002514212165631065017275 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" static int _experAlloc(tenExperSpec* espec, unsigned int num) { static char me[]="_experAlloc"; airFree(espec->bval); espec->bval = NULL; airFree(espec->grad); espec->grad = NULL; /* espec->wght = airFree(espec->wght); */ if (!num) { biffAddf(TEN, "%s: need a non-zero number of images", me); return 1; } espec->imgNum = num; espec->bval = AIR_CALLOC(num, double); espec->grad = AIR_CALLOC(3*num, double); /* espec->wght = AIR_CALLOC(num, double); */ if (!( espec->bval && espec->grad /* && espec->wght */ )) { biffAddf(TEN, "%s: couldn't allocate for %u images", me, num); return 1; } return 0; } tenExperSpec* tenExperSpecNew(void) { tenExperSpec* espec; espec = AIR_CALLOC(1, tenExperSpec); espec->set = AIR_FALSE; espec->imgNum = 0; espec->bval = NULL; espec->grad = NULL; /* espec->wght = NULL; */ return espec; } int tenExperSpecGradSingleBValSet(tenExperSpec *espec, int insertB0, double bval, const double *grad, unsigned int gradNum) { static const char me[]="tenExperSpecGradSingleBValSet"; unsigned int ii, imgNum, ei; if (!espec) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (insertB0 && !ELL_3V_LEN(grad + 3*0)) { biffAddf(TEN, "%s: wanted insertB0 but gradients " "already start with (0,0,0)", me); return 1; } imgNum = gradNum + !!insertB0; if (_experAlloc(espec, imgNum)) { biffAddf(TEN, "%s: couldn't allocate", me); return 1; } if (insertB0) { espec->bval[0] = 0; ELL_3V_SET(espec->grad + 3*0, 1, 0, 0); ei = 1; } else { ei = 0; } for (ii=0; iibval[ei] = bval; ELL_3V_COPY(espec->grad + 3*ei, grad + 3*ii); /* espec->wght[ii] = 1.0; */ } return 0; } int tenExperSpecGradBValSet(tenExperSpec *espec, int insertB0, const double *bval, const double *grad, unsigned int bgNum) { static const char me[]="tenExperSpecGradBValSet"; unsigned int ii, imgNum, ei; if (!espec) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (insertB0 && (!ELL_3V_LEN(grad + 3*0) || !bval[0])) { biffAddf(TEN, "%s: wanted insertB0 but gradients " "already start with (0,0,0) or bvals start with 0", me); return 1; } imgNum = bgNum + !!insertB0; if (_experAlloc(espec, imgNum)) { biffAddf(TEN, "%s: couldn't allocate", me); return 1; } if (insertB0) { espec->bval[0] = 0; ELL_3V_SET(espec->grad + 3*0, 0, 0, 0); ei = 1; } else { ei = 0; } for (ii=0; iibval[ei] = bval[ii]; ELL_3V_COPY(espec->grad + 3*ei, grad + 3*ii); /* espec->wght[ii] = 1.0; */ } return 0; } /* int tenExperSpecGradBValWghtSet(tenExperSpec *espec, unsigned int imgNum, const double *bval, const double *grad, const double *wght) { static const char me[]="tenExperSpecGradBValWghtSet"; unsigned int ii; if (!espec) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (_experAlloc(espec, imgNum)) { biffAddf(TEN, "%s: couldn't allocate", me); return 1; } for (ii=0; iibval[ii] = bval[ii]; ELL_3V_COPY(espec->grad + 3*ii, grad + 3*ii); espec->wght[ii] = wght[ii]; } return 0; } */ int tenExperSpecFromKeyValueSet(tenExperSpec *espec, const Nrrd *ndwi) { static const char me[]="tenExperSpecFromKeyValueSet"; unsigned int *skip, skipNum, ii, imgNum, dwiax; Nrrd *ngrad, *nbmat; airArray *mop; double len, singleBval, *bval, *grad; for (dwiax=0; dwiaxdim; dwiax++) { if (nrrdKindList == ndwi->axis[dwiax].kind || nrrdKindVector == ndwi->axis[dwiax].kind) { break; } } if (ndwi->dim == dwiax) { biffAddf(TEN, "%s: need dwis to have a kind %s or %s axis", me, airEnumStr(nrrdKind, nrrdKindList), airEnumStr(nrrdKind, nrrdKindVector)); return 1; } else { if (0 != dwiax) { biffAddf(TEN, "%s: need dwis (kind %s or %s) along axis 0, not %u", me, airEnumStr(nrrdKind, nrrdKindList), airEnumStr(nrrdKind, nrrdKindVector), dwiax); return 1; } } for (ii=dwiax+1; iidim; ii++) { if (nrrdKindList == ndwi->axis[ii].kind || nrrdKindVector == ndwi->axis[ii].kind) { break; } } if (ii < ndwi->dim) { biffAddf(TEN, "%s: saw on %u another %s or %s kind axis, after 0", me, ii, airEnumStr(nrrdKind, nrrdKindList), airEnumStr(nrrdKind, nrrdKindVector)); return 1; } if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &singleBval, &skip, &skipNum, ndwi)) { biffAddf(TEN, "%s: trouble parsing DWI info from key/value pairs", me); return 1; } mop = airMopNew(); if (ngrad) { airMopAdd(mop, ngrad, (airMopper)nrrdNuke, airMopAlways); } if (nbmat) { airMopAdd(mop, nbmat, (airMopper)nrrdNuke, airMopAlways); } if (skip) { airMopAdd(mop, skip, airFree, airMopAlways); } if (nbmat) { biffAddf(TEN, "%s: sorry, currently can't handle B-matrices here", me); airMopError(mop); return 1; } if (skipNum) { biffAddf(TEN, "%s: sorry, currently can't handle skipping (%u) here", me, skipNum); airMopError(mop); return 1; } imgNum = ngrad->axis[1].size; bval = AIR_CALLOC(imgNum, double); airMopAdd(mop, bval, airFree, airMopAlways); grad = AIR_CAST(double *, ngrad->data); for (ii=0; iibval); airFree(espec->grad); /* espec->wght = airFree(espec->wght); */ airFree(espec); } return NULL; } double _tenExperSpec_sqe(const double *dwiMeas, const double *dwiSim, const tenExperSpec *espec, int knownB0) { unsigned int ii; double sqe; sqe = 0; if (knownB0) { for (ii=0; iiimgNum; ii++) { double dd; if (!espec->bval[ii]) { continue; } dd = dwiMeas[ii] - dwiSim[ii]; sqe += dd*dd; /* fprintf(stderr, "!%s: dwi[%u]: %g - %g -> %g\n", "_tenExperSpec_sqe", ii, dwiMeas[ii], dwiSim[ii], sqe); */ } } else { for (ii=0; iiimgNum; ii++) { double dd; dd = dwiMeas[ii] - dwiSim[ii]; sqe += dd*dd; } } return sqe; } double _tenExperSpec_nll(const double *dwiMeas, const double *dwiSim, const tenExperSpec *espec, int rician, double sigma, int knownB0) { double nll; unsigned int ii; nll = 0; if (rician) { for (ii=0; iiimgNum; ii++) { if (knownB0 && !espec->bval[ii]) { continue; } nll += -airLogRician(dwiMeas[ii], dwiSim[ii], sigma); } } else { double dd, ladd, denom; ladd = log(sigma*sqrt(2*AIR_PI)); denom = 1.0/(2*sigma*sigma); for (ii=0; iiimgNum; ii++) { if (knownB0 && !espec->bval[ii]) { continue; } dd = dwiMeas[ii] - dwiSim[ii]; nll += dd*dd*denom + ladd; } } return nll; } int tenDWMRIKeyValueFromExperSpecSet(Nrrd *ndwi, const tenExperSpec *espec) { static char me[]="tenDWMRIKeyValueFromExperSpecSet"; char keystr[AIR_STRLEN_MED], valstr[AIR_STRLEN_MED]; double maxb, bb; unsigned int ii; if (!(ndwi && espec)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } nrrdKeyValueAdd(ndwi, tenDWMRIModalityKey, tenDWMRIModalityVal); maxb = tenExperSpecMaxBGet(espec); sprintf(valstr, "%.17g", maxb); nrrdKeyValueAdd(ndwi, tenDWMRIBValueKey, valstr); for (ii=0; iiimgNum; ii++) { double vec[3]; sprintf(keystr, tenDWMRIGradKeyFmt, ii); ELL_3V_COPY(vec, espec->grad + 3*ii); bb = espec->bval[ii]; /* Thu Dec 20 03:25:20 CST 2012 this rescaling is not, btw, what is causing the small discrepency between ngrad before and after saving to KVPs */ ELL_3V_SCALE(vec, sqrt(bb/maxb), vec); sprintf(valstr, "%.17g %.17g %.17g", vec[0], vec[1], vec[2]); nrrdKeyValueAdd(ndwi, keystr, valstr); } /* HEY what if its a full B-matrix? */ return 0; } /* ** learns B0 from DWIs by simple averaging of all the dwi[ii] ** without any diffusion weighting, as indicated by espec->bval[ii], ** or, returns AIR_NAN when there are no such dwi[ii] */ double tenExperSpecKnownB0Get(const tenExperSpec *espec, const double *dwi) { unsigned int ii, nb; double ret, b0; if (!( dwi && espec )) { return AIR_NAN; } nb = 0; b0 = 0.0; for (ii=0; iiimgNum; ii++) { if (0 == espec->bval[ii]) { b0 += dwi[ii]; ++nb; } } if (nb) { ret = b0/nb; } else { ret = AIR_NAN; } return ret; } double tenExperSpecMaxBGet(const tenExperSpec *espec) { unsigned int ii; double bval; if (!( espec )) { return AIR_NAN; } bval = -1; for (ii=0; iiimgNum; ii++) { bval = AIR_MAX(bval, espec->bval[ii]); } return bval; } teem-1.11.0~svn6057/src/ten/model1Tensor2.c0000664000175000017500000001035612165631065017776 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" /* NOTE: this model is a single 2nd-order tensor, not a two-tensor model */ #define PARM_NUM 7 /* 1/sqrt(2) */ #define OST 0.70710678118654752440 static const tenModelParmDesc parmDesc[] = { /* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 1 */ {"Dxx", -TEN_MODEL_DIFF_MAX, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 2 */ {"Dxy", -TEN_MODEL_DIFF_MAX*OST, TEN_MODEL_DIFF_MAX*OST, AIR_FALSE, AIR_FALSE, 0}, /* 3 */ {"Dxz", -TEN_MODEL_DIFF_MAX*OST, TEN_MODEL_DIFF_MAX*OST, AIR_FALSE, AIR_FALSE, 0}, /* 4 */ {"Dyy", -TEN_MODEL_DIFF_MAX, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}, /* 5 */ {"Dyz", -TEN_MODEL_DIFF_MAX*OST, TEN_MODEL_DIFF_MAX*OST, AIR_FALSE, AIR_FALSE, 0}, /* 6 */ {"Dzz", -TEN_MODEL_DIFF_MAX, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0} }; static void simulate(double *dwiSim, const double *parm, const tenExperSpec *espec) { unsigned int ii; double b0; b0 = parm[0]; for (ii=0; iiimgNum; ii++) { double adc, bb; bb = espec->bval[ii]; /* safe because TEN_T3V_CONTR never looks at parm[0] */ adc = TEN_T3V_CONTR(parm, espec->grad + 3*ii); dwiSim[ii] = b0*exp(-bb*adc); } return; } static char * parmSprint(char str[AIR_STRLEN_MED], const double *parm) { sprintf(str, "(%g) [%g %g %g; %g %g; %g]", parm[0], parm[1], parm[2], parm[3], parm[4], parm[5], parm[6]); return str; } _TEN_PARM_ALLOC _TEN_PARM_RAND _TEN_PARM_STEP _TEN_PARM_DIST _TEN_PARM_COPY static int parmConvert(double *parmDst, const double *parmSrc, const tenModel *modelSrc) { int ret; if (modelSrc == tenModelBall) { TEN_T_SET(parmDst, parmSrc[0], parmSrc[1], 0, 0, parmSrc[1], 0, parmSrc[1]); ret = 0; } else if (modelSrc == tenModel1Stick) { double ten[7]; TEN_T3V_OUTER(ten, parmSrc + 2); TEN_T_SCALE(parmDst, parmSrc[1], ten); parmDst[0] = parmSrc[0]; ret = 0; } else if (modelSrc == tenModelBall1Stick) { double stick[7], ball[7], diff, frac; diff = parmSrc[1]; frac = parmSrc[2]; TEN_T3V_OUTER(stick, parmSrc + 3); TEN_T_SCALE(stick, diff, stick); TEN_T_SET(ball, 1, diff, 0, 0, diff, 0, diff); TEN_T_LERP(parmDst, frac, ball, stick); parmDst[0] = parmSrc[0]; ret = 1; } else if (modelSrc == tenModel1Cylinder) { double stick[7], ball[7], len, rad; len = parmSrc[1]; rad = parmSrc[2]; TEN_T3V_OUTER(stick, parmSrc + 3); TEN_T_SCALE(stick, len-rad, stick); TEN_T_SET(ball, 1, rad, 0, 0, rad, 0, rad); TEN_T_ADD(parmDst, ball, stick); parmDst[0] = parmSrc[0]; ret = 0; } else if (modelSrc == tenModel1Tensor2) { parmCopy(parmDst, parmSrc); ret = 0; } else { unsigned int ii; for (ii=0; iiaxis[1].size; sy = nin->axis[2].size; sz = nin->axis[3].size; if (!( AIR_IN_CL(0, loc[0], sx-1) && AIR_IN_CL(0, loc[1], sy-1) && AIR_IN_CL(0, loc[2], sz-1) )) { fprintf(stderr, "%s: location (%d,%d,%d) not inside volume " "[0..%d]x[0..%d]x[0..%d]\n", me, loc[0], loc[1], loc[2], sx-1, sy-1, sz-1); airMopError(mop); return 1; } idx = loc[0] + sx*(loc[1] + sy*loc[2]); tdata = (float*)(nin->data) + 7*idx; fprintf(stderr, "location = (%d,%d,%d) = %d\n", loc[0], loc[1], loc[2], idx); fprintf(stderr, "confidence = %g\n", tdata[0]); fprintf(stderr, "tensor =\n"); fprintf(stderr, "{%.7f,%.7f,%.7f,%.7f,%.7f,%.7f} = \n", tdata[1], tdata[2], tdata[3], tdata[4], tdata[5], tdata[6]); fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", tdata[1], tdata[2], tdata[3]); fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", tdata[2], tdata[4], tdata[5]); fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", tdata[3], tdata[5], tdata[6]); tenEigensolve_f(eval, evec, tdata); fprintf(stderr, "eigensystem = ( : ):\n"); fprintf(stderr, "% 15.7f : % 15.7f % 15.7f % 15.7f\n", eval[0], evec[0], evec[1], evec[2]); fprintf(stderr, "% 15.7f : % 15.7f % 15.7f % 15.7f\n", eval[1], evec[3], evec[4], evec[5]); fprintf(stderr, "% 15.7f : % 15.7f % 15.7f % 15.7f\n", eval[2], evec[6], evec[7], evec[8]); angle = ell_3m_to_aa_f(axis, evec); fprintf(stderr, "eigenvector rotation: %g around {%g,%g,%g}\n", angle, axis[0], axis[1], axis[2]); ell_aa_to_3m_f(mat, angle, axis); fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", mat[0], mat[1], mat[2]); fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", mat[3], mat[4], mat[5]); fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", mat[6], mat[7], mat[8]); fprintf(stderr, "anisotropies = \n"); for (i=1; i<=TEN_ANISO_MAX; i++) { fprintf(stderr, "%s: % 15.7f\n", airEnumStr(tenAniso, i), tenAnisoEval_f(eval, i)); } airMopOkay(mop); return 0; } TEND_CMD(point, INFO); teem-1.11.0~svn6057/src/ten/tendSim.c0000664000175000017500000001502212165631065016736 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" #define INFO "Simulate DW images from a tensor field" static const char *_tend_simInfoL = (INFO ". The output will be in the same form as the input to \"tend estim\". " "The B-matrices (\"-B\") can be the output from \"tend bmat\", or the " "gradients can be given directly (\"-g\"); one of these is required. " "Note that the input tensor field (\"-i\") is the basis of the output " "per-axis fields and image orientation. NOTE: this includes the " "measurement frame used in the input tensor field, which implies that " "the given gradients or B-matrices are already expressed in that " "measurement frame. "); int tend_simMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; tenEstimateContext *tec; airArray *mop; int E, oldstuff, seed, keyValueSet, outType, preOutType; Nrrd *nin, *nT2, *nbmat, *ngrad, *nout, *ntmp; char *outS; float b, sigma; /* maybe this can go in tend.c, but for some reason its explicitly set to AIR_FALSE there */ hparm->elideSingleOtherDefault = AIR_TRUE; hestOptAdd(&hopt, "old", NULL, airTypeInt, 0, 0, &oldstuff, NULL, "don't use the new tenEstimateContext functionality"); hestOptAdd(&hopt, "sigma", "sigma", airTypeFloat, 1, 1, &sigma, "0.0", "Rician noise parameter"); hestOptAdd(&hopt, "seed", "seed", airTypeInt, 1, 1, &seed, "42", "seed value for RNG which creates noise"); hestOptAdd(&hopt, "g", "grad list", airTypeOther, 1, 1, &ngrad, "", "gradient list, one row per diffusion-weighted image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "B", "B matrix", airTypeOther, 1, 1, &nbmat, "", "B matrix, one row per diffusion-weighted image. Using this " "overrides the gradient list input via \"-g\"", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "r", "reference field", airTypeOther, 1, 1, &nT2, NULL, "reference anatomical scan, with no diffusion weighting", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "i", "tensor field", airTypeOther, 1, 1, &nin, "-", "input diffusion tensor field", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "b", "b", airTypeFloat, 1, 1, &b, "1000", "b value for simulated scan"); hestOptAdd(&hopt, "kvp", NULL, airTypeInt, 0, 0, &keyValueSet, NULL, "generate key/value pairs in the NRRD header corresponding " "to the input b-value and gradients or B-matrices. "); hestOptAdd(&hopt, "t", "type", airTypeEnum, 1, 1, &outType, "float", "output type of DWIs", NULL, nrrdType); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output image (floating point)"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_simInfoL); PARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (!( nbmat || ngrad )) { fprintf(stderr, "%s: got neither B-matrix (\"-B\") " "or gradient list (\"-g\")\n", me); airMopError(mop); return 1; } if (!oldstuff) { airSrandMT(seed); tec = tenEstimateContextNew(); airMopAdd(mop, tec, (airMopper)tenEstimateContextNix, airMopAlways); preOutType = (nrrdTypeFloat == outType ? nrrdTypeFloat : nrrdTypeDouble); E = 0; if (!E) E |= tenEstimateMethodSet(tec, tenEstimate1MethodLLS); if (!E) E |= tenEstimateValueMinSet(tec, 0.0001); if (nbmat) { if (!E) E |= tenEstimateBMatricesSet(tec, nbmat, b, AIR_TRUE); } else { if (!E) E |= tenEstimateGradientsSet(tec, ngrad, b, AIR_TRUE); } if (!E) E |= tenEstimateThresholdSet(tec, 0, 0); if (!E) E |= tenEstimateUpdate(tec); if (!E) E |= tenEstimate1TensorSimulateVolume(tec, nout, sigma, b, nT2, nin, preOutType, keyValueSet); if (E) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making DWI volume (new):\n%s\n", me, err); airMopError(mop); return 1; } if (preOutType != outType) { ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); E = 0; if (!E) E |= nrrdCopy(ntmp, nout); if (!E) E |= nrrdConvert(nout, ntmp, outType); if (E) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble making output volume:\n%s\n", me, err); airMopError(mop); return 1; } } } else { if (!nbmat) { fprintf(stderr, "%s: need B-matrices for old code\n", me); airMopError(mop); return 1; } if (tenSimulate(nout, nT2, nin, nbmat, b)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making DWI volume:\n%s\n", me, err); airMopError(mop); return 1; } } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } TEND_CMD(sim, INFO); teem-1.11.0~svn6057/src/ten/tenModel.c0000664000175000017500000006541612165631065017116 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ten.h" #include "privateTen.h" const char * tenModelPrefixStr = "DWMRI_model:"; static const tenModel * str2model(const char *str) { const tenModel *ret; if (!strcmp(str, TEN_MODEL_STR_ZERO)) { ret = tenModelZero; } else if (!strcmp(str, TEN_MODEL_STR_B0)) { ret = tenModelB0; } else if (!strcmp(str, TEN_MODEL_STR_BALL)) { ret = tenModelBall; } else if (!strcmp(str, TEN_MODEL_STR_1STICK)) { ret = tenModel1Stick; } else if (!strcmp(str, TEN_MODEL_STR_1VECTOR2D)) { ret = tenModel1Vector2D; } else if (!strcmp(str, TEN_MODEL_STR_1UNIT2D)) { ret = tenModel1Unit2D; } else if (!strcmp(str, TEN_MODEL_STR_2UNIT2D)) { ret = tenModel2Unit2D; } else if (!strcmp(str, TEN_MODEL_STR_BALL1STICKEMD)) { ret = tenModelBall1StickEMD; } else if (!strcmp(str, TEN_MODEL_STR_BALL1STICK)) { ret = tenModelBall1Stick; } else if (!strcmp(str, TEN_MODEL_STR_BALL1CYLINDER)) { ret = tenModelBall1Cylinder; } else if (!strcmp(str, TEN_MODEL_STR_1CYLINDER)) { ret = tenModel1Cylinder; } else if (!strcmp(str, TEN_MODEL_STR_1TENSOR2)) { ret = tenModel1Tensor2; } else { /* we don't currently have a tenModelUnknown */ ret = NULL; } return ret; } int tenModelParse(const tenModel **model, int *plusB0, int requirePrefix, const char *_str) { static const char me[]="tenModelParse"; char *str, *modstr, *pre; airArray *mop; if (!( model && plusB0 && _str)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } str = airStrdup(_str); if (!str) { biffAddf(TEN, "%s: couldn't strdup", me); return 1; } mop = airMopNew(); airMopAdd(mop, str, airFree, airMopAlways); pre = strstr(str, tenModelPrefixStr); if (pre) { str += strlen(tenModelPrefixStr); } else { if (requirePrefix) { biffAddf(TEN, "%s: didn't see prefix \"%s\" in \"%s\"", me, tenModelPrefixStr, _str); airMopError(mop); return 1; } } airToLower(str); /* for sake of "b0" and str2model below */ if ((modstr = strchr(str, '+'))) { *modstr = '\0'; ++modstr; if (!strcmp(str, "b0")) { *plusB0 = AIR_TRUE; } else { biffAddf(TEN, "%s: string (\"%s\") prior to \"+\" not \"b0\"", me, str); airMopError(mop); return 1; } } else { *plusB0 = AIR_FALSE; modstr = str; } if (!(*model = str2model(modstr))) { biffAddf(TEN, "%s: didn't recognize \"%s\" as model", me, modstr); airMopError(mop); return 1; } airMopOkay(mop); return 0; } int tenModelFromAxisLearnPossible(const NrrdAxisInfo *axinfo) { /* HEY keep in synch with nrrdKind* code below */ return (nrrdKind3DSymMatrix == axinfo->kind || nrrdKind3DMaskedSymMatrix == axinfo->kind || airStrlen(axinfo->label)); } int tenModelFromAxisLearn(const tenModel **modelP, int *plusB0, const NrrdAxisInfo *axinfo) { static const char me[]="tenModelFromAxisLearn"; if (!(modelP && plusB0 && axinfo)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } *plusB0 = AIR_FALSE; /* first try to learn model from axis "kind" */ /* HEY should probably also support 3 vector for stick? */ /* HEY keep in synch with nrrdKind* code above */ if (nrrdKind3DSymMatrix == axinfo->kind || nrrdKind3DMaskedSymMatrix == axinfo->kind) { *modelP = tenModel1Tensor2; } else if (airStrlen(axinfo->label)) { /* try to parse from label */ if (tenModelParse(modelP, plusB0, AIR_TRUE, axinfo->label)) { biffAddf(TEN, "%s: couldn't parse label \"%s\"", me, axinfo->label); *modelP = NULL; return 1; } } else { biffAddf(TEN, "%s: don't have kind or label info to learn model", me); *modelP = NULL; return 1; } return 0; } /* ** If nB0 is given, then those B0 image values will be used. ** In this case, either the parm vector can be short by one (seems to be ** missing B0), or the parm vector includes B0, but these will be ignored ** and over-written with the B0 values from nB0. ** ** basic and axis info is derived from _nparm */ int tenModelSimulate(Nrrd *ndwi, int typeOut, tenExperSpec *espec, const tenModel *model, const Nrrd *_nB0, const Nrrd *_nparm, int keyValueSet) { static const char me[]="tenModelSimulate"; double *ddwi, *parm, (*ins)(void *v, size_t I, double d); char *dwi; size_t szOut[NRRD_DIM_MAX], II, numSamp; const Nrrd *nB0, /* B0 as doubles */ *ndparm, /* parm as doubles */ *ndpparm, /* parm as doubles, padded */ *nparm; /* final parm as doubles, padded, w/ correct B0 values */ Nrrd *ntmp; /* non-const pointer for working */ airArray *mop; unsigned int gpsze, /* given parm size */ ii; int useB0Img, needPad, axmap[NRRD_DIM_MAX]; if (!(ndwi && espec && model /* _nB0 can be NULL */ && _nparm)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!espec->imgNum) { biffAddf(TEN, "%s: given espec wants 0 images, unset?", me); return 1; } gpsze = _nparm->axis[0].size; if (model->parmNum - 1 == gpsze) { /* got one less than needed parm #, see if we got B0 */ if (!_nB0) { biffAddf(TEN, "%s: got %u parms, need %u (for %s), " "but didn't get B0 vol", me, gpsze, model->parmNum, model->name); return 1; } useB0Img = AIR_TRUE; needPad = AIR_TRUE; } else if (model->parmNum != gpsze) { biffAddf(TEN, "%s: mismatch between getting %u parms, " "needing %u (for %s)\n", me, gpsze, model->parmNum, model->name); return 1; } else { /* model->parmNum == gpsze */ needPad = AIR_FALSE; useB0Img = !!_nB0; } mop = airMopNew(); /* get parms as doubles */ if (nrrdTypeDouble == _nparm->type) { ndparm = _nparm; } else { ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(ntmp, _nparm, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: couldn't convert parm to %s", me, airEnumStr(nrrdType, nrrdTypeDouble)); airMopError(mop); return 1; } ndparm = ntmp; } /* get parms the right length */ if (!needPad) { ndpparm = ndparm; } else { ptrdiff_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX]; unsigned int ax; ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); for (ax=0; axdim; ax++) { min[ax] = (!ax ? -1 : 0); max[ax] = ndparm->axis[ax].size-1; } if (nrrdPad_nva(ntmp, ndparm, min, max, nrrdBoundaryBleed, 0.0)) { biffMovef(TEN, NRRD, "%s: couldn't pad", me); airMopError(mop); return 1; } ndpparm = ntmp; } /* put in B0 values if needed */ if (!useB0Img) { nparm = ndpparm; } else { if (nrrdTypeDouble == _nB0->type) { nB0 = _nB0; } else { ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(ntmp, _nB0, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: couldn't convert B0 to %s", me, airEnumStr(nrrdType, nrrdTypeDouble)); airMopError(mop); return 1; } nB0 = ntmp; } /* HEY: this is mostly likely a waste of memory, but its all complicated by const-correctness */ ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); if (nrrdSplice(ntmp, ndpparm, nB0, 0, 0)) { biffMovef(TEN, NRRD, "%s: couldn't splice in B0", me); airMopError(mop); return 1; } nparm = ntmp; } /* allocate output (and set axmap) */ for (ii=0; iidim; ii++) { szOut[ii] = (!ii ? espec->imgNum : nparm->axis[ii].size); axmap[ii] = (!ii ? -1 : AIR_CAST(int, ii)); } if (nrrdMaybeAlloc_nva(ndwi, typeOut, nparm->dim, szOut)) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); airMopError(mop); return 1; } if (!( ddwi = AIR_CALLOC(espec->imgNum, double))) { biffAddf(TEN, "%s: couldn't allocate dwi buffer", me); airMopError(mop); return 1; } airMopAdd(mop, ddwi, airFree, airMopAlways); numSamp = nrrdElementNumber(nparm)/nparm->axis[0].size; /* set output */ ins = nrrdDInsert[typeOut]; parm = AIR_CAST(double *, nparm->data); dwi = AIR_CAST(char *, ndwi->data); for (II=0; IIsimulate(ddwi, parm, espec); for (ii=0; iiimgNum; ii++) { ins(dwi, ii, ddwi[ii]); } parm += model->parmNum; dwi += espec->imgNum*nrrdTypeSize[typeOut]; } if (keyValueSet) { if (tenDWMRIKeyValueFromExperSpecSet(ndwi, espec)) { biffAddf(TEN, "%s: trouble", me); airMopError(mop); return 1; } } if (nrrdAxisInfoCopy(ndwi, _nparm, axmap, NRRD_AXIS_INFO_SIZE_BIT) || nrrdBasicInfoCopy(ndwi, _nparm, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffMovef(TEN, NRRD, "%s: couldn't copy axis or basic info", me); airMopError(mop); return 1; } ndwi->axis[0].kind = nrrdKindList; airMopOkay(mop); return 0; } /* ** _tenModelSqeFitSingle ** ** callable function (as opposed to tenModel method) for doing ** sqe fitting. Returns the sqe at the converged fit location ** Requires PARM_NUM length buffers testParm and grad */ double _tenModelSqeFitSingle(const tenModel *model, double *testParm, double *grad, double *parm, double *convFrac, unsigned int *itersTaken, const tenExperSpec *espec, double *dwiBuff, const double *dwiMeas, const double *parmInit, int knownB0, unsigned int minIter, unsigned int maxIter, double convEps, int verbose) { static const char me[]="_tenModelSqeFitSingle"; unsigned int iter, subIter; double step, bak, opp, val, testval, dist, td; int done; char pstr[AIR_STRLEN_MED]; step = 1; model->copy(parm, parmInit); val = model->sqe(parm, espec, dwiBuff, dwiMeas, knownB0); model->sqeGrad(grad, parm, espec, dwiBuff, dwiMeas, knownB0); if (verbose > 1) { model->sprint(pstr, parm); fprintf(stderr, "\n"); fprintf(stderr, "%s(%s): minIter = %u, maxIter = %u\n", me, model->name, minIter, maxIter); fprintf(stderr, "%s(%s): starting at %s -> %g (step %g)\n", me, model->name, pstr, val, step); } opp = 1.2; /* opportunistic step size increase */ bak = 0.5; /* scaling back because of bad step */ iter = 0; dist = convEps*8; do { subIter = 0; do { model->step(testParm, -step, grad, parm); testval = model->sqe(testParm, espec, dwiBuff, dwiMeas, knownB0); if (verbose > 1) { model->sprint(pstr, testParm); fprintf(stderr, "%s(%s): (iter %u/%u) tried %s -> %g (step %g)\n", me, model->name, iter, subIter, pstr, testval, step); } if (testval > val) { step *= bak; } subIter++; } while (testval > val && subIter <= maxIter); if (subIter > maxIter) { /* something went wrong with merely trying to find a downhill step; this has occurred previously when (because of a bug) the per-parameter bounds put the test location inside the bounding box while the initial location was outside => could never converge. Not using biff, so this is one way of trying to signal the problem */ model->copy(parm, parmInit); *convFrac = AIR_POS_INF; *itersTaken = maxIter; return AIR_POS_INF; } td = model->dist(testParm, parm); dist = (td + dist)/2; val = testval; model->copy(parm, testParm); model->sqeGrad(grad, parm, espec, dwiBuff, dwiMeas, knownB0); step *= opp; iter++; done = (iter < minIter ? AIR_FALSE : (iter > maxIter) || dist < convEps); } while (!done); *convFrac = dist/convEps; *itersTaken = iter; return val; } int tenModelSqeFit(Nrrd *nparm, Nrrd **nsqeP, Nrrd **nconvP, Nrrd **niterP, const tenModel *model, const tenExperSpec *espec, const Nrrd *ndwi, int knownB0, int saveB0, int typeOut, unsigned int minIter, unsigned int maxIter, unsigned int starts, double convEps, airRandMTState *_rng, int verbose) { static const char me[]="tenModelSqeFit"; char doneStr[13]; double *ddwi, *dwibuff, sqe, sqeBest, *dparm, *dparmBest, (*ins)(void *v, size_t I, double d), (*lup)(const void *v, size_t I); airArray *mop; unsigned int saveParmNum, dwiNum, ii, lablen, itersTaken; size_t szOut[NRRD_DIM_MAX], II, numSamp; int axmap[NRRD_DIM_MAX], erraxmap[NRRD_DIM_MAX], fitVerbose; const char *dwi; char *parm; airRandMTState *rng; Nrrd *nsqe, *nconv, *niter; /* nsqeP, nconvP, niterP can be NULL */ if (!( nparm && model && espec && ndwi )) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!( starts > 0 )) { biffAddf(TEN, "%s: need non-zero starts", me); return 1; } if (!( nrrdTypeFloat == typeOut || nrrdTypeDouble == typeOut )) { biffAddf(TEN, "%s: typeOut must be %s or %s, not %s", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeDouble), airEnumStr(nrrdType, typeOut)); return 1; } dwiNum = ndwi->axis[0].size; if (espec->imgNum != dwiNum) { biffAddf(TEN, "%s: espec expects %u images but dwi has %u on axis 0", me, espec->imgNum, AIR_CAST(unsigned int, dwiNum)); return 1; } /* allocate output (and set axmap) */ dparm = model->alloc(); dparmBest = model->alloc(); if (!( dparm && dparmBest )) { biffAddf(TEN, "%s: couldn't allocate parm vecs", me); return 1; } mop = airMopNew(); airMopAdd(mop, dparm, airFree, airMopAlways); airMopAdd(mop, dparmBest, airFree, airMopAlways); saveParmNum = saveB0 ? model->parmNum : model->parmNum-1; for (ii=0; iidim; ii++) { szOut[ii] = (!ii ? saveParmNum : ndwi->axis[ii].size); axmap[ii] = (!ii ? -1 : AIR_CAST(int, ii)); if (ii) { erraxmap[ii-1] = AIR_CAST(int, ii); } } if (nrrdMaybeAlloc_nva(nparm, typeOut, ndwi->dim, szOut)) { biffMovef(TEN, NRRD, "%s: couldn't allocate output " "(saveB0 %d, knownB0 %d)", me, saveB0, knownB0); airMopError(mop); return 1; } if (nsqeP) { nsqe = *nsqeP; if (!nsqe) { nsqe = nrrdNew(); *nsqeP = nsqe; } if (nrrdMaybeAlloc_nva(nsqe, typeOut, ndwi->dim-1, szOut+1)) { biffMovef(TEN, NRRD, "%s: couldn't allocate error output", me); airMopError(mop); return 1; } } else { nsqe = NULL; } if (nconvP) { nconv = *nconvP; if (!nconv) { nconv = nrrdNew(); *nconvP = nconv; } if (nrrdMaybeAlloc_nva(nconv, nrrdTypeDouble, ndwi->dim-1, szOut+1)) { biffMovef(TEN, NRRD, "%s: couldn't allocate conv output", me); airMopError(mop); return 1; } } else { nconv = NULL; } if (niterP) { niter = *niterP; if (!niter) { niter = nrrdNew(); *niterP = niter; } if (nrrdMaybeAlloc_nva(niter, nrrdTypeUInt, ndwi->dim-1, szOut+1)) { biffMovef(TEN, NRRD, "%s: couldn't allocate iter output", me); airMopError(mop); return 1; } } else { niter = NULL; } ddwi = AIR_CALLOC(espec->imgNum, double); dwibuff = AIR_CALLOC(espec->imgNum, double); if (!(ddwi && dwibuff)) { biffAddf(TEN, "%s: couldn't allocate dwi buffers", me); airMopError(mop); return 1; } airMopAdd(mop, ddwi, airFree, airMopAlways); airMopAdd(mop, dwibuff, airFree, airMopAlways); /* set output */ if (_rng) { rng = _rng; } else { airRandMTStateGlobalInit(); rng = airRandMTStateGlobal; } numSamp = nrrdElementNumber(ndwi)/ndwi->axis[0].size; lup = nrrdDLookup[ndwi->type]; ins = nrrdDInsert[typeOut]; parm = AIR_CAST(char *, nparm->data); dwi = AIR_CAST(char *, ndwi->data); itersTaken = 0; if (verbose) { fprintf(stderr, "%s: fitting ... ", me); fflush(stderr); } for (II=0; IIrand(dparm, rng, knownB0); sqe = model->sqeFit(dparm, &cvf, &itak, espec, dwibuff, ddwi, dparm, knownB0, minIter, maxIter, convEps, fitVerbose); if (sqe <= sqeBest) { sqeBest = sqe; model->copy(dparmBest, dparm); itersTaken = itak; convFrac = cvf; } } for (ii=0; iidata, II, sqeBest); } if (nconvP) { nrrdDInsert[nrrdTypeDouble](nconv->data, II, convFrac); } if (niterP) { nrrdDInsert[nrrdTypeUInt](niter->data, II, itersTaken); } parm += saveParmNum*nrrdTypeSize[typeOut]; dwi += espec->imgNum*nrrdTypeSize[ndwi->type]; } if (verbose) { fprintf(stderr, "%s\n", airDoneStr(0, II, numSamp, doneStr)); } if (nrrdAxisInfoCopy(nparm, ndwi, axmap, NRRD_AXIS_INFO_SIZE_BIT) || nrrdBasicInfoCopy(nparm, ndwi, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffMovef(TEN, NRRD, "%s: couldn't copy axis or basic info", me); airMopError(mop); return 1; } if (nsqeP) { if (nrrdAxisInfoCopy(nsqe, ndwi, erraxmap, NRRD_AXIS_INFO_SIZE_BIT) || nrrdBasicInfoCopy(nsqe, ndwi, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffMovef(TEN, NRRD, "%s: couldn't copy axis or basic info to error out", me); airMopError(mop); return 1; } } if (nconvP) { if (nrrdAxisInfoCopy(nconv, ndwi, erraxmap, NRRD_AXIS_INFO_SIZE_BIT) || nrrdBasicInfoCopy(nconv, ndwi, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffMovef(TEN, NRRD, "%s: couldn't copy axis or basic info to conv out", me); airMopError(mop); return 1; } } if (niterP) { if (nrrdAxisInfoCopy(niter, ndwi, erraxmap, NRRD_AXIS_INFO_SIZE_BIT) || nrrdBasicInfoCopy(niter, ndwi, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffMovef(TEN, NRRD, "%s: couldn't copy axis or basic info to iter out", me); airMopError(mop); return 1; } } lablen = (strlen(tenModelPrefixStr) + (saveB0 ? strlen("B0+") : 0) + strlen(model->name) + 1); nparm->axis[0].label = AIR_CALLOC(lablen, char); sprintf(nparm->axis[0].label, "%s%s%s", tenModelPrefixStr, saveB0 ? "B0+" : "", model->name); airMopOkay(mop); return 0; } int tenModelNllFit(Nrrd *nparm, Nrrd **nnllP, const tenModel *model, const tenExperSpec *espec, const Nrrd *ndwi, int rician, double sigma, int knownB0) { AIR_UNUSED(nparm); AIR_UNUSED(nnllP); AIR_UNUSED(model); AIR_UNUSED(espec); AIR_UNUSED(ndwi); AIR_UNUSED(rician); AIR_UNUSED(sigma); AIR_UNUSED(knownB0); return 0; } /* ** copy the B0 info if we have it ** use the same type on the way out. */ int tenModelConvert(Nrrd *nparmDst, int *convRetP, const tenModel *modelDst, const Nrrd *nparmSrc, const tenModel *_modelSrc) { static char me[]="tenModelConvert"; const tenModel *modelSrc; double *dpdst, *dpsrc, (*lup)(const void *v, size_t I), (*ins)(void *v, size_t I, double d); size_t szOut[NRRD_DIM_MAX], II, NN, tsize; airArray *mop; int withB0, axmap[NRRD_DIM_MAX], convRet=0; unsigned int parmNumDst, parmNumSrc, ii, lablen; const char *parmSrc; char *parmDst; if (!( nparmDst && modelDst && nparmSrc )) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (!_modelSrc) { /* we have to try to learn the source model from the nrrd */ if (tenModelFromAxisLearn(&modelSrc, &withB0, nparmSrc->axis + 0)) { biffAddf(TEN, "%s: couldn't learn model from src nparm", me); return 1; } } else { modelSrc = _modelSrc; if (modelSrc->parmNum == nparmSrc->axis[0].size) { withB0 = AIR_TRUE; } if (modelSrc->parmNum-1 == nparmSrc->axis[0].size) { withB0 = AIR_FALSE; } else { biffAddf(TEN, "%s: axis[0].size %u is not \"%s\" parmnum %u or 1 less", me, AIR_CAST(unsigned int, nparmSrc->axis[0].size), modelSrc->name, modelSrc->parmNum); return 1; } } mop = airMopNew(); dpdst = modelDst->alloc(); airMopAdd(mop, dpdst, airFree, airMopAlways); dpsrc = modelSrc->alloc(); airMopAdd(mop, dpsrc, airFree, airMopAlways); lup = nrrdDLookup[nparmSrc->type]; ins = nrrdDInsert[nparmSrc->type]; parmNumDst = withB0 ? modelDst->parmNum : modelDst->parmNum-1; parmNumSrc = nparmSrc->axis[0].size; for (ii=0; iidim; ii++) { szOut[ii] = (!ii ? parmNumDst : nparmSrc->axis[ii].size); axmap[ii] = (!ii ? -1 : AIR_CAST(int, ii)); } if (nrrdMaybeAlloc_nva(nparmDst, nparmSrc->type, nparmSrc->dim, szOut)) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); airMopError(mop); return 1; } NN = nrrdElementNumber(nparmSrc)/nparmSrc->axis[0].size; tsize = nrrdTypeSize[nparmSrc->type]; parmSrc = AIR_CAST(char *, nparmSrc->data); parmDst = AIR_CAST(char *, nparmDst->data); if (!withB0) { dpsrc[0] = 0; } for (II=0; IIconvert(dpdst, dpsrc, modelSrc); if (2 == convRet) { /* HEY should be enum for this value */ biffAddf(TEN, "%s: error converting from \"%s\" to \"%s\"", me, modelSrc->name, modelDst->name); airMopError(mop); return 1; } for (ii=0; iiname) + 1); nparmDst->axis[0].label = AIR_CALLOC(lablen, char); sprintf(nparmDst->axis[0].label, "%s%s%s", tenModelPrefixStr, withB0 ? "B0+" : "", modelDst->name); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/ten/TODO.txt0000664000175000017500000000010210546644230016465 0ustar domibeldomibelmove q-seg stuff into tenEstimate, and add tenEstimateModelSet() teem-1.11.0~svn6057/src/seek/0000775000175000017500000000000012203513756015327 5ustar domibeldomibelteem-1.11.0~svn6057/src/seek/setSeek.c0000664000175000017500000002607712165631065017113 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "seek.h" #include "privateSeek.h" /* ******** seekVerboseSet ** */ void seekVerboseSet(seekContext *sctx, int verbose) { if (sctx) { sctx->verbose = verbose; } return; } /* ******** seekDataSet ** ** user sets EITHER: ninscl, or, gctx and pvlIdx ** ** if ninscl: this is a vanilla scalar volume, and we can do seekTypeIsocontour ** if gctx: this is a scalar or non-scalar volume, and we can do any seekType ** ** sets from input: ** ninscl, gctx, pvl ** ** So the rest of seek can use "if (sctx->ninscl)" to see if we're working ** with a vanilla scalar volume or not ** ** invalidates: ** valItem, normItem, gradItem, evalItem, evecItem */ int seekDataSet(seekContext *sctx, const Nrrd *ninscl, gageContext *gctx, unsigned int pvlIdx) { static const char me[]="seekDataSet"; if (!( sctx && (ninscl || gctx) )) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } if (ninscl && gctx) { biffAddf(SEEK, "%s: must give ninscl or gctx, but not both", me); return 1; } if (ninscl) { if (nrrdCheck(ninscl)) { biffMovef(SEEK, NRRD, "%s: problem with volume", me); return 1; } if (3 != ninscl->dim) { biffAddf(SEEK, "%s: vanilla scalar volume must be 3-D (not %d-D)", me, ninscl->dim); return 1; } if (nrrdTypeBlock == ninscl->type) { biffAddf(SEEK, "%s: can't work with %s type values", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } sctx->ninscl = ninscl; sctx->gctx = NULL; sctx->pvl = NULL; } else { if (!( pvlIdx < gctx->pvlNum )) { biffAddf(SEEK, "%s: pvlIdx %u not < pvlNum %u", me, pvlIdx, gctx->pvlNum); return 1; } /* we assume that caller has done a gageUpdate(), so no other error checking is required (or really possible) here */ sctx->ninscl = NULL; sctx->gctx = gctx; sctx->pvl = gctx->pvl[pvlIdx]; } sctx->flag[flagData] = AIR_TRUE; sctx->sclvItem = -1; sctx->normItem = -1; sctx->gradItem = -1; sctx->evalItem = -1; sctx->evecItem = -1; sctx->hessItem = -1; return 0; } /* ******** seekSamplesSet ** ** sets: samples[3] */ int seekSamplesSet(seekContext *sctx, size_t samples[3]) { static const char me[]="seekSamplesSet"; unsigned int numZero; if (!(sctx && samples)) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } numZero = 0; numZero += 0 == samples[0]; numZero += 0 == samples[1]; numZero += 0 == samples[2]; if (!( 0 == numZero || 3 == numZero )) { biffAddf(SEEK, "%s: samples (%u,%u,%u) must all be 0 or !=0 together", me, AIR_CAST(unsigned int, samples[0]), AIR_CAST(unsigned int, samples[1]), AIR_CAST(unsigned int, samples[2])); return 1; } if (sctx->samples[0] != samples[0] || sctx->samples[1] != samples[1] || sctx->samples[2] != samples[2]) { sctx->samples[0] = samples[0]; sctx->samples[1] = samples[1]; sctx->samples[2] = samples[2]; sctx->flag[flagSamples] = AIR_TRUE; } return 0; } /* ******** seekTypeSet ** ** sets: featureType */ int seekTypeSet(seekContext *sctx, int type) { static const char me[]="seekTypeSet"; if (!sctx) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(seekType, type)) { biffAddf(SEEK, "%s: %d not a valid %s", me, type, seekType->name); return 1; } if (sctx->type != type) { sctx->type = type; sctx->flag[flagType] = AIR_TRUE; } return 0; } /* ********* seekLowerInsideSet ** ** sets: lowerInside */ int seekLowerInsideSet(seekContext *sctx, int lowerInside) { static const char me[]="seekLowerInsideSet"; if (!sctx) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } if (sctx->lowerInside != lowerInside) { sctx->lowerInside = lowerInside; sctx->flag[flagLowerInside] = AIR_TRUE; } return 0; } /* ********* seekNormalsFindSet ** ** sets: normalsFind */ int seekNormalsFindSet(seekContext *sctx, int normalsFind) { static const char me[]="seekNormalsFindSet"; if (!sctx) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } if (sctx->normalsFind != normalsFind) { sctx->normalsFind = normalsFind; sctx->flag[flagNormalsFind] = AIR_TRUE; } return 0; } int seekStrengthUseSet(seekContext *sctx, int doit) { static const char me[]="seekStrengthUseSet"; if (!sctx) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } if (sctx->strengthUse != doit) { sctx->strengthUse = doit; sctx->flag[flagStrengthUse] = AIR_TRUE; } return 0; } int seekStrengthSet(seekContext *sctx, int strengthSign, double strength) { static const char me[]="seekStrengthSet"; if (!sctx) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } if (!(1 == strengthSign || -1 == strengthSign)) { biffAddf(SEEK, "%s: strengthSign (%d) not +1 or -1", me, strengthSign); return 1; } if (!AIR_EXISTS(strength)) { biffAddf(SEEK, "%s: strength %g doesn't exist", me, strength); return 1; } if (sctx->strengthSign != strengthSign) { sctx->strengthSign = strengthSign; sctx->flag[flagStrength] = AIR_TRUE; } if (sctx->strength != strength) { sctx->strength = strength; sctx->flag[flagStrength] = AIR_TRUE; } return 0; } static int itemCheck(seekContext *sctx, int item, unsigned int wantLen) { static const char me[]="itemCheck"; if (!sctx) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } if (!(sctx->gctx && sctx->pvl)) { biffAddf(SEEK, "%s: don't have a gage context", me); return 1; } if (airEnumValCheck(sctx->pvl->kind->enm, item)) { biffAddf(SEEK, "%s: %d not valid %s item", me, item, sctx->pvl->kind->enm->name); return 1; } if (!GAGE_QUERY_ITEM_TEST(sctx->pvl->query, item)) { biffAddf(SEEK, "%s: item \"%s\" (%d) not set in query", me, airEnumStr(sctx->pvl->kind->enm, item), item); return 1; } if (sctx->pvl->kind->table[item].answerLength != wantLen) { biffAddf(SEEK, "%s: item %s has length %u, not wanted %u", me, airEnumStr(sctx->pvl->kind->enm, item), sctx->pvl->kind->table[item].answerLength, wantLen); return 1; } return 0; } /* ******** seekItemScalarSet ** ** sets: sclvItem */ int seekItemScalarSet(seekContext *sctx, int item) { static const char me[]="seekItemScalarSet"; if (itemCheck(sctx, item, 1)) { biffAddf(SEEK, "%s: trouble", me); return 1; } if (sctx->sclvItem != item) { sctx->sclvItem = item; sctx->flag[flagItemValue] = AIR_TRUE; } return 0; } /* ******** seekItemStrengthSet ** */ int seekItemStrengthSet(seekContext *sctx, int item) { static const char me[]="seekItemStrengthSet"; if (itemCheck(sctx, item, 1)) { biffAddf(SEEK, "%s: trouble", me); return 1; } if (sctx->stngItem != item) { sctx->stngItem = item; sctx->flag[flagItemStrength] = AIR_TRUE; } return 0; } /* ******** seekItemHessSet ** */ int seekItemHessSet(seekContext *sctx, int item) { char me[]="seekItemHessSet"; if (itemCheck(sctx, item, 9)) { biffAddf(SEEK, "%s: trouble", me); return 1; } if (sctx->hessItem != item) { sctx->hessItem = item; sctx->flag[flagItemHess] = AIR_TRUE; } return 0; } /* ******** seekItemGradientSet ** ** sets: gradItem */ int seekItemGradientSet(seekContext *sctx, int item) { static const char me[]="seekItemGradientSet"; if (itemCheck(sctx, item, 3)) { biffAddf(SEEK, "%s: trouble", me); return 1; } if (sctx->gradItem != item) { sctx->gradItem = item; sctx->flag[flagItemGradient] = AIR_TRUE; } /* sctx->gradAns = gageAnswerPointer(sctx->gctx, sctx->pvl, item); */ return 0; } /* ******** seekItemNormalSet ** ** sets: normItem */ int seekItemNormalSet(seekContext *sctx, int item) { static const char me[]="seekItemNormalSet"; if (itemCheck(sctx, item, 3)) { biffAddf(SEEK, "%s: trouble", me); return 1; } if (sctx->normItem != item) { sctx->normItem = item; sctx->flag[flagItemNormal] = AIR_TRUE; } /* sctx->normAns = gageAnswerPointer(sctx->gctx, sctx->pvl, item); */ return 0; } /* ******** seekItemEigensystemSet ** ** sets: evalItem, evecItem */ int seekItemEigensystemSet(seekContext *sctx, int evalItem, int evecItem) { static const char me[]="seekItemEigenvectorSet"; if (itemCheck(sctx, evalItem, 3)) { biffAddf(SEEK, "%s: trouble", me); return 1; } if (itemCheck(sctx, evecItem, 9)) { biffAddf(SEEK, "%s: trouble", me); return 1; } if (sctx->evalItem != evalItem || sctx->evecItem != evecItem) { sctx->evalItem = evalItem; sctx->evecItem = evecItem; sctx->flag[flagItemEigensystem] = AIR_TRUE; } /* sctx->evalAns = gageAnswerPointer(sctx->gctx, sctx->pvl, sctx->evalItem); sctx->evecAns = gageAnswerPointer(sctx->gctx, sctx->pvl, sctx->evecItem); */ return 0; } /* ******** seekIsovalueSet ** ** sets: isovalue */ int seekIsovalueSet(seekContext *sctx, double isovalue) { static const char me[]="seekIsovalueSet"; if (!sctx) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } if (!AIR_EXISTS(isovalue)) { biffAddf(SEEK, "%s: given isovalue %g doesn't exit", me, isovalue); return 1; } if (sctx->isovalue != isovalue) { sctx->isovalue = isovalue; sctx->flag[flagIsovalue] = AIR_TRUE; } return 0; } /* ******** seekEvalDiffThreshSet ** ** sets: difference threshold from which two eigenvalues are ** considered "similar" (cf. Eq. (4) in TVCG paper by ** Schultz/Theisel/Seidel) */ int seekEvalDiffThreshSet(seekContext *sctx, double evalDiffThresh) { char me[]="seekEvalDiffThreshSet"; if (!sctx) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } if (!AIR_EXISTS(evalDiffThresh)) { biffAddf(SEEK, "%s: given eigenvalue difference threshold %g doesn't exit", me, evalDiffThresh); return 1; } if (sctx->evalDiffThresh != evalDiffThresh) { sctx->evalDiffThresh = evalDiffThresh; sctx->flag[flagEvalDiffThresh] = AIR_TRUE; } return 0; } teem-1.11.0~svn6057/src/seek/enumsSeek.c0000664000175000017500000000625712165631065017445 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "seek.h" const char * seekBiffKey = "seek"; const char * _seekTypeStr[SEEK_TYPE_MAX+1] = { "(unknown_feature)", "isocontour", "ridge surface", "valley surface", "ridge line", "valley line", "minimal surface", "maximal surface", "OP ridge surface", "T ridge surface", "OP valley surface", "T valley surface" }; const char * _seekTypeDesc[SEEK_TYPE_MAX+1] = { "unknown_feature", "standard marching cubes surface", "ridge surface", "valley surface", "ridge line", "valley line", "minimal surface", "maximal surface", "ridge surface using outer product rule", "ridge surface using tensor T", "valley surface using outer product rule", "valley surface using tensor T" }; const char * _seekTypeStrEqv[] = { "isocontour", "ridge surface", "ridgesurface", "rs", "valley surface", "valleysurface", "vs", "ridge line", "ridgeline", "rl", "valley line", "valleyline", "vl", "minimal surface", "mins", "maximal surface", "maxs", "OP ridge surface", "ridgesurfaceop", "rsop", "T ridge surface", "ridgesurfacet", "rst", "OP valley surface", "valleysurfaceop", "vsop", "T valley surface", "valleysurfacet", "vst", "" }; const int _seekTypeValEqv[] = { seekTypeIsocontour, seekTypeRidgeSurface, seekTypeRidgeSurface, seekTypeRidgeSurface, seekTypeValleySurface, seekTypeValleySurface, seekTypeValleySurface, seekTypeRidgeLine, seekTypeRidgeLine, seekTypeRidgeLine, seekTypeValleyLine, seekTypeValleyLine, seekTypeValleyLine, seekTypeMinimalSurface, seekTypeMinimalSurface, seekTypeMaximalSurface, seekTypeMaximalSurface, seekTypeRidgeSurfaceOP, seekTypeRidgeSurfaceOP, seekTypeRidgeSurfaceOP, seekTypeRidgeSurfaceT, seekTypeRidgeSurfaceT, seekTypeRidgeSurfaceT, seekTypeValleySurfaceOP, seekTypeValleySurfaceOP, seekTypeValleySurfaceOP, seekTypeValleySurfaceT, seekTypeValleySurfaceT, seekTypeValleySurfaceT }; const airEnum _seekType = { "format", SEEK_TYPE_MAX, _seekTypeStr, NULL, _seekTypeDesc, _seekTypeStrEqv, _seekTypeValEqv, AIR_FALSE }; const airEnum *const seekType = &_seekType; teem-1.11.0~svn6057/src/seek/tables.c0000664000175000017500000004464712042367142016761 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2010, 2009 Thomas Schultz Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "seek.h" /* ** vertex, edge, and face numbering, and canonical edge arrangement ** ** Z ** ^ Y ** | ^ ** | / ** | / ** |/ ** O--------> X ** ** (6)---11---(7) +----------+ +----------+ ** /| /| /| /| /| \ /| ** 9 | 10 | / | /5/ / | / | \/ | ** / 6 / 7 / | |4|/ | / \| /\ | ** (4)----8---(5) | +----------+ | +----------+ | ** | | | | ||2|| ||3|| | | \ | | ** | (2)---3-|--(3) | +------|---+ | +----\-|---+ ** 4 / 5 / | / |1| | / |\ / |\ / ** | 1 | 2 | / /0/ | / | /\ | / ** |/ |/ |/ |/ |/ \ |/ ** (0)----0---(1) +----------+ +----------+ ** canonical edge arrangement ** creates 2 triangular and ** 1 hexagonal surface */ /* According to this layout, in the code comments, "right" denotes * positive x, "back" denotes positive y, "top" denotes positive z */ /* ** the seekContext's vidx cache uses this numbering ** (.)--------(.) ** /| /| ** 4 | / | ** / | / | ** (.)----3---(.) | ** | | | | ** | (.)-----|--(.) ** 2 / | / ** | 1 | / ** |/ |/ ** (X)----0---(.) */ /* We now only need a numbering of faces. It is: * 0: xy plane, z=0 * 1: xz plane, y=0 * 2: yz plane, x=1 * 3: xz plane, y=1 * 4: yz plane, x=0 * 5: xy plane, z=1 * * There are four unique faces to each voxel (e.g., in facevidx): * 0: xy plane, z=0 * 1: xz plane, y=0 * 2: yz plane, x=0 * 3: xy plane, z=1 */ const int seekContour3DTopoHackEdge[256] = { 0x000, 0x013, 0x025, 0x036, 0x04A, 0x059, 0x06F, 0x07C, 0x08C, 0x09F, 0x0A9, 0x0BA, 0x0C6, 0x0D5, 0x0E3, 0x0F0, 0x310, 0x303, 0x335, 0x326, 0x35A, 0x349, 0x37F, 0x36C, 0x39C, 0x38F, 0x3B9, 0x3AA, 0x3D6, 0x3C5, 0x3F3, 0x3E0, 0x520, 0x533, 0x505, 0x516, 0x56A, 0x579, 0x54F, 0x55C, 0x5AC, 0x5BF, 0x589, 0x59A, 0x5E6, 0x5F5, 0x5C3, 0x5D0, 0x630, 0x623, 0x615, 0x606, 0x67A, 0x669, 0x65F, 0x64C, 0x6BC, 0x6AF, 0x699, 0x68A, 0x6F6, 0x6E5, 0x6D3, 0x6C0, 0xA40, 0xA53, 0xA65, 0xA76, 0xA0A, 0xA19, 0xA2F, 0xA3C, 0xACC, 0xADF, 0xAE9, 0xAFA, 0xA86, 0xA95, 0xAA3, 0xAB0, 0x950, 0x943, 0x975, 0x966, 0x91A, 0x909, 0x93F, 0x92C, 0x9DC, 0x9CF, 0x9F9, 0x9EA, 0x996, 0x985, 0x9B3, 0x9A0, 0xF60, 0xF73, 0xF45, 0xF56, 0xF2A, 0xF39, 0xF0F, 0xF1C, 0xFEC, 0xFFF, 0xFC9, 0xFDA, 0xFA6, 0xFB5, 0xF83, 0xF90, 0xC70, 0xC63, 0xC55, 0xC46, 0xC3A, 0xC29, 0xC1F, 0xC0C, 0xCFC, 0xCEF, 0xCD9, 0xCCA, 0xCB6, 0xCA5, 0xC93, 0xC80, 0xC80, 0xC93, 0xCA5, 0xCB6, 0xCCA, 0xCD9, 0xCEF, 0xCFC, 0xC0C, 0xC1F, 0xC29, 0xC3A, 0xC46, 0xC55, 0xC63, 0xC70, 0xF90, 0xF83, 0xFB5, 0xFA6, 0xFDA, 0xFC9, 0xFFF, 0xFEC, 0xF1C, 0xF0F, 0xF39, 0xF2A, 0xF56, 0xF45, 0xF73, 0xF60, 0x9A0, 0x9B3, 0x985, 0x996, 0x9EA, 0x9F9, 0x9CF, 0x9DC, 0x92C, 0x93F, 0x909, 0x91A, 0x966, 0x975, 0x943, 0x950, 0xAB0, 0xAA3, 0xA95, 0xA86, 0xAFA, 0xAE9, 0xADF, 0xACC, 0xA3C, 0xA2F, 0xA19, 0xA0A, 0xA76, 0xA65, 0xA53, 0xA40, 0x6C0, 0x6D3, 0x6E5, 0x6F6, 0x68A, 0x699, 0x6AF, 0x6BC, 0x64C, 0x65F, 0x669, 0x67A, 0x606, 0x615, 0x623, 0x630, 0x5D0, 0x5C3, 0x5F5, 0x5E6, 0x59A, 0x589, 0x5BF, 0x5AC, 0x55C, 0x54F, 0x579, 0x56A, 0x516, 0x505, 0x533, 0x520, 0x3E0, 0x3F3, 0x3C5, 0x3D6, 0x3AA, 0x3B9, 0x38F, 0x39C, 0x36C, 0x37F, 0x349, 0x35A, 0x326, 0x335, 0x303, 0x310, 0x0F0, 0x0E3, 0x0D5, 0x0C6, 0x0BA, 0x0A9, 0x09F, 0x08C, 0x07C, 0x06F, 0x059, 0x04A, 0x036, 0x025, 0x013, 0x000 }; /* This case table implements the ideas from Dietrich et al. Edge Groups: an approach to understanding the mesh quality of marching methods. IEEE Trans. Vis. Comp. Graph. 2008 and has been generated from the case table distributed with macet, written by Carlos Dietrich. Re-used with kind permission of the authors. */ const int seekContour3DTopoHackTriangle[256][16] = { {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 0, 1, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 5, 2, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 5, 2, 4, 4, 2, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 1, 3, 6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 4, 0, 6, 6, 0, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 3, 6, 1, 5, 2, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 6, 4, 3, 4, 2, 3, 4, 5, 2,-1,-1,-1,-1,-1,-1,-1}, { 2, 7, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 1, 4, 0, 7, 3, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 0, 5, 3, 3, 5, 7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 4, 5, 1, 5, 3, 1, 5, 7, 3,-1,-1,-1,-1,-1,-1,-1}, { 6, 1, 7, 7, 1, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 7, 6, 2, 6, 0, 2, 6, 4, 0,-1,-1,-1,-1,-1,-1,-1}, { 5, 7, 0, 7, 1, 0, 7, 6, 1,-1,-1,-1,-1,-1,-1,-1}, { 5, 6, 4, 5, 7, 6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 9, 8, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 0, 1, 8, 8, 1, 9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 4, 9, 8, 2, 0, 5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 2, 1, 5, 1, 8, 5, 1, 9, 8,-1,-1,-1,-1,-1,-1,-1}, { 9, 8, 4, 3, 6, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 8, 0, 9, 0, 6, 9, 0, 3, 6,-1,-1,-1,-1,-1,-1,-1}, { 8, 4, 9, 3, 6, 1, 2, 0, 5,-1,-1,-1,-1,-1,-1,-1}, { 2, 3, 6, 2, 6, 9, 5, 2, 9, 8, 5, 9,-1,-1,-1,-1}, { 9, 8, 4, 7, 3, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 9, 8, 1, 8, 0, 1, 7, 3, 2,-1,-1,-1,-1,-1,-1,-1}, { 7, 3, 5, 3, 0, 5, 9, 8, 4,-1,-1,-1,-1,-1,-1,-1}, { 5, 7, 8, 8, 7, 3, 1, 9, 8, 1, 8, 3,-1,-1,-1,-1}, { 2, 7, 1, 7, 6, 1, 8, 4, 9,-1,-1,-1,-1,-1,-1,-1}, { 7, 6, 9, 9, 0, 2, 8, 0, 9, 7, 9, 2,-1,-1,-1,-1}, { 4, 9, 8, 7, 6, 1, 0, 7, 1, 5, 7, 0,-1,-1,-1,-1}, { 7, 6, 9, 8, 7, 9, 5, 7, 8,-1,-1,-1,-1,-1,-1,-1}, {10, 5, 8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 0, 1, 4,10, 5, 8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 8,10, 0, 0,10, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {10, 2, 8, 2, 4, 8, 2, 1, 4,-1,-1,-1,-1,-1,-1,-1}, {10, 5, 8, 3, 6, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 3, 6, 0, 6, 4, 0,10, 5, 8,-1,-1,-1,-1,-1,-1,-1}, { 8,10, 0,10, 2, 0, 6, 1, 3,-1,-1,-1,-1,-1,-1,-1}, {10, 2, 8, 8, 2, 3, 6, 4, 8, 6, 8, 3,-1,-1,-1,-1}, { 5, 8,10, 3, 2, 7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {10, 5, 8, 1, 4, 0, 3, 2, 7,-1,-1,-1,-1,-1,-1,-1}, { 3, 0, 7, 0,10, 7, 0, 8,10,-1,-1,-1,-1,-1,-1,-1}, { 7, 3,10,10, 3, 8, 3, 1, 8, 1, 4, 8,-1,-1,-1,-1}, { 6, 1, 7, 1, 2, 7, 8,10, 5,-1,-1,-1,-1,-1,-1,-1}, {10, 5, 8, 6, 4, 0, 2, 6, 0, 7, 6, 2,-1,-1,-1,-1}, { 0, 8, 1, 1, 8,10, 7, 6, 1, 7, 1,10,-1,-1,-1,-1}, { 6, 4, 8,10, 6, 8, 7, 6,10,-1,-1,-1,-1,-1,-1,-1}, {10, 5, 9, 9, 5, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 1, 9, 0, 9, 5, 0, 9,10, 5,-1,-1,-1,-1,-1,-1,-1}, { 9,10, 4,10, 0, 4,10, 2, 0,-1,-1,-1,-1,-1,-1,-1}, {10, 1, 9, 2, 1,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {10, 5, 9, 5, 4, 9, 3, 6, 1,-1,-1,-1,-1,-1,-1,-1}, { 9,10, 5, 5, 3, 6, 0, 3, 5, 9, 5, 6,-1,-1,-1,-1}, { 1, 3, 6,10, 2, 0, 4,10, 0, 9,10, 4,-1,-1,-1,-1}, {10, 2, 3, 6,10, 3, 9,10, 6,-1,-1,-1,-1,-1,-1,-1}, { 4, 9, 5, 9,10, 5, 3, 2, 7,-1,-1,-1,-1,-1,-1,-1}, { 3, 2, 7, 9,10, 5, 0, 9, 5, 1, 9, 0,-1,-1,-1,-1}, { 3, 0, 4, 4,10, 7, 9,10, 4, 3, 4, 7,-1,-1,-1,-1}, { 9,10, 7, 3, 9, 7, 1, 9, 3,-1,-1,-1,-1,-1,-1,-1}, { 1, 2, 6, 2, 7, 6, 9, 5, 4, 9,10, 5,-1,-1,-1,-1}, { 9,10, 5, 0, 9, 5, 0, 6, 9, 2, 6, 0, 7, 6, 2,-1}, { 7, 6, 1, 0, 7, 1, 0,10, 7, 4,10, 0, 9,10, 4,-1}, { 6, 9,10, 6,10, 7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 6,11, 9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 6,11, 9, 0, 1, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 6,11, 9, 2, 0, 5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 5, 2, 4, 2, 1, 4,11, 9, 6,-1,-1,-1,-1,-1,-1,-1}, { 1, 3, 9, 9, 3,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 0, 3, 4, 3, 9, 4, 3,11, 9,-1,-1,-1,-1,-1,-1,-1}, {11, 9, 3, 9, 1, 3, 5, 2, 0,-1,-1,-1,-1,-1,-1,-1}, { 3,11, 9, 9, 5, 2, 4, 5, 9, 3, 9, 2,-1,-1,-1,-1}, {11, 9, 6, 2, 7, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 0, 1, 4,11, 9, 6, 7, 3, 2,-1,-1,-1,-1,-1,-1,-1}, { 0, 5, 3, 5, 7, 3, 9, 6,11,-1,-1,-1,-1,-1,-1,-1}, { 6,11, 9, 5, 7, 3, 1, 5, 3, 4, 5, 1,-1,-1,-1,-1}, { 9, 1,11, 1, 7,11, 1, 2, 7,-1,-1,-1,-1,-1,-1,-1}, { 2, 7, 0, 0, 7, 4, 7,11, 4,11, 9, 4,-1,-1,-1,-1}, { 9, 1,11,11, 1, 0, 5, 7,11, 5,11, 0,-1,-1,-1,-1}, { 5, 7,11, 9, 5,11, 4, 5, 9,-1,-1,-1,-1,-1,-1,-1}, { 6,11, 4, 4,11, 8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {11, 8, 6, 8, 1, 6, 8, 0, 1,-1,-1,-1,-1,-1,-1,-1}, { 6,11, 4,11, 8, 4, 2, 0, 5,-1,-1,-1,-1,-1,-1,-1}, {11, 8, 5, 5, 1, 6, 2, 1, 5,11, 5, 6,-1,-1,-1,-1}, { 3,11, 1,11, 4, 1,11, 8, 4,-1,-1,-1,-1,-1,-1,-1}, { 0,11, 8, 3,11, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 2, 0, 5,11, 8, 4, 1,11, 4, 3,11, 1,-1,-1,-1,-1}, {11, 8, 5, 2,11, 5, 3,11, 2,-1,-1,-1,-1,-1,-1,-1}, { 8, 4,11, 4, 6,11, 2, 7, 3,-1,-1,-1,-1,-1,-1,-1}, { 7, 3, 2, 8, 0, 1, 6, 8, 1,11, 8, 6,-1,-1,-1,-1}, { 5, 7, 0, 7, 3, 0, 4,11, 8, 4, 6,11,-1,-1,-1,-1}, { 3, 5, 7, 1, 5, 3, 1, 8, 5, 6, 8, 1,11, 8, 6,-1}, { 1, 2, 4, 4, 2, 7,11, 8, 4,11, 4, 7,-1,-1,-1,-1}, { 8, 0, 2, 7, 8, 2,11, 8, 7,-1,-1,-1,-1,-1,-1,-1}, {11, 8, 4, 1,11, 4, 1, 7,11, 0, 7, 1, 5, 7, 0,-1}, { 7,11, 8, 7, 8, 5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {10, 5, 8, 6,11, 9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 1, 4, 0,10, 5, 8,11, 9, 6,-1,-1,-1,-1,-1,-1,-1}, { 2, 0,10, 0, 8,10, 6,11, 9,-1,-1,-1,-1,-1,-1,-1}, {11, 9, 6, 2, 1, 4, 8, 2, 4,10, 2, 8,-1,-1,-1,-1}, { 1, 3, 9, 3,11, 9, 5, 8,10,-1,-1,-1,-1,-1,-1,-1}, { 5, 8,10, 3,11, 9, 4, 3, 9, 0, 3, 4,-1,-1,-1,-1}, { 1,11, 9, 3,11, 1, 0, 8,10, 2, 0,10,-1,-1,-1,-1}, { 3,11, 9, 4, 3, 9, 4, 2, 3, 8, 2, 4,10, 2, 8,-1}, { 6,11, 9, 5, 8,10, 2, 7, 3,-1,-1,-1,-1,-1,-1,-1}, { 3, 2, 7,10, 5, 8, 0, 1, 4,11, 9, 6,-1,-1,-1,-1}, { 6,11, 9, 0, 8,10, 7, 0,10, 3, 0, 7,-1,-1,-1,-1}, {10, 7, 8, 7, 3, 8, 3, 4, 8, 4, 3, 1, 6,11, 9,-1}, {10, 5, 8, 1, 2, 7,11, 1, 7, 9, 1,11,-1,-1,-1,-1}, { 0, 2, 4, 2, 7, 4, 7, 9, 4, 9, 7,11,10, 5, 8,-1}, {10, 0, 8, 7, 0,10, 7, 1, 0,11, 1, 7, 9, 1,11,-1}, { 4, 8,10, 7, 4,10, 4,11, 9, 4, 7,11,-1,-1,-1,-1}, { 5, 4,10, 4,11,10, 4, 6,11,-1,-1,-1,-1,-1,-1,-1}, {11,10, 5,11, 5, 0, 6,11, 0, 1, 6, 0,-1,-1,-1,-1}, { 4, 6,11,11, 2, 0,10, 2,11, 4,11, 0,-1,-1,-1,-1}, { 2, 1, 6,11, 2, 6,10, 2,11,-1,-1,-1,-1,-1,-1,-1}, { 5, 4, 1, 1,11,10, 3,11, 1, 5, 1,10,-1,-1,-1,-1}, { 3,11,10, 5, 3,10, 0, 3, 5,-1,-1,-1,-1,-1,-1,-1}, {10, 2, 0, 4,10, 0, 4,11,10, 1,11, 4, 3,11, 1,-1}, {11,10, 2,11, 2, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 2, 7, 3, 4, 6,11,10, 4,11, 5, 4,10,-1,-1,-1,-1}, { 1, 6, 0, 6,11, 0,11, 5, 0, 5,11,10, 7, 3, 2,-1}, {11, 4, 6,10, 4,11,10, 0, 4, 7, 0,10, 3, 0, 7,-1}, {10, 7, 3, 1,10, 3,10, 6,11,10, 1, 6,-1,-1,-1,-1}, { 1, 2, 7,11, 1, 7,11, 4, 1,10, 4,11, 5, 4,10,-1}, { 0, 2, 7,11, 0, 7, 0,10, 5, 0,11,10,-1,-1,-1,-1}, { 1, 0, 4,10, 7,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 7,11,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {11, 7,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 0, 1, 4, 7,10,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 2, 0, 5,11, 7,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 1, 4, 2, 4, 5, 2,11, 7,10,-1,-1,-1,-1,-1,-1,-1}, { 7,10,11, 1, 3, 6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 4, 0, 6, 0, 3, 6,10,11, 7,-1,-1,-1,-1,-1,-1,-1}, { 5, 2, 0, 6, 1, 3,11, 7,10,-1,-1,-1,-1,-1,-1,-1}, { 7,10,11, 4, 5, 2, 3, 4, 2, 6, 4, 3,-1,-1,-1,-1}, {10,11, 2, 2,11, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {10,11, 2,11, 3, 2, 4, 0, 1,-1,-1,-1,-1,-1,-1,-1}, {11, 3,10, 3, 5,10, 3, 0, 5,-1,-1,-1,-1,-1,-1,-1}, { 4, 5, 1, 1, 5,10,11, 3, 1,11, 1,10,-1,-1,-1,-1}, { 1, 2, 6, 2,11, 6, 2,10,11,-1,-1,-1,-1,-1,-1,-1}, { 6, 4,11,11, 4, 0, 2,10,11, 2,11, 0,-1,-1,-1,-1}, {10,11, 5, 5,11, 0,11, 6, 0, 6, 1, 0,-1,-1,-1,-1}, { 4, 5,10,11, 4,10, 6, 4,11,-1,-1,-1,-1,-1,-1,-1}, { 8, 4, 9, 7,10,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 0, 1, 8, 1, 9, 8, 7,10,11,-1,-1,-1,-1,-1,-1,-1}, { 9, 8, 4, 2, 0, 5, 7,10,11,-1,-1,-1,-1,-1,-1,-1}, { 7,10,11, 1, 9, 8, 5, 1, 8, 2, 1, 5,-1,-1,-1,-1}, { 4, 9, 8, 7,10,11, 3, 6, 1,-1,-1,-1,-1,-1,-1,-1}, {11, 7,10, 0, 3, 6, 9, 0, 6, 8, 0, 9,-1,-1,-1,-1}, { 9, 8, 4, 1, 3, 6, 2, 0, 5, 7,10,11,-1,-1,-1,-1}, { 8, 5, 9, 5, 2, 9, 2, 6, 9, 6, 2, 3, 7,10,11,-1}, { 3, 2,11, 2,10,11, 4, 9, 8,-1,-1,-1,-1,-1,-1,-1}, {11, 3,10, 3, 2,10, 8, 1, 9, 8, 0, 1,-1,-1,-1,-1}, { 8, 4, 9, 3, 0, 5,10, 3, 5,11, 3,10,-1,-1,-1,-1}, { 1, 9, 8, 5, 1, 8, 5, 3, 1,10, 3, 5,11, 3,10,-1}, { 9, 8, 4, 2,10,11, 6, 2,11, 1, 2, 6,-1,-1,-1,-1}, { 2,10,11, 6, 2,11, 6, 0, 2, 9, 0, 6, 8, 0, 9,-1}, { 5,10, 0,10,11, 0,11, 1, 0, 1,11, 6, 9, 8, 4,-1}, { 5,10,11, 6, 5,11, 5, 9, 8, 5, 6, 9,-1,-1,-1,-1}, {11, 7, 8, 8, 7, 5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {11, 7, 8, 7, 5, 8, 1, 4, 0,-1,-1,-1,-1,-1,-1,-1}, { 0, 8, 2, 8, 7, 2, 8,11, 7,-1,-1,-1,-1,-1,-1,-1}, { 2, 1, 4, 4,11, 7, 8,11, 4, 2, 4, 7,-1,-1,-1,-1}, { 5, 8, 7, 8,11, 7, 1, 3, 6,-1,-1,-1,-1,-1,-1,-1}, {11, 5, 8, 7, 5,11, 6, 4, 0, 3, 6, 0,-1,-1,-1,-1}, { 3, 6, 1, 8,11, 7, 2, 8, 7, 0, 8, 2,-1,-1,-1,-1}, { 8,11, 7, 2, 8, 7, 2, 4, 8, 3, 4, 2, 6, 4, 3,-1}, { 8,11, 5,11, 2, 5,11, 3, 2,-1,-1,-1,-1,-1,-1,-1}, { 0, 1, 4,11, 3, 2, 5,11, 2, 8,11, 5,-1,-1,-1,-1}, {11, 0, 8,11, 3, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {11, 3, 1, 4,11, 1, 8,11, 4,-1,-1,-1,-1,-1,-1,-1}, { 8,11, 5, 5,11, 6, 1, 2, 5, 1, 5, 6,-1,-1,-1,-1}, { 6, 4, 0, 2, 6, 0, 2,11, 6, 5,11, 2, 8,11, 5,-1}, { 8,11, 6, 1, 8, 6, 0, 8, 1,-1,-1,-1,-1,-1,-1,-1}, {11, 6, 4,11, 4, 8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 7, 5,11, 5, 9,11, 5, 4, 9,-1,-1,-1,-1,-1,-1,-1}, { 1, 9,11,11, 5, 0, 7, 5,11, 1,11, 0,-1,-1,-1,-1}, { 7, 2, 0, 7, 0, 4,11, 7, 4, 9,11, 4,-1,-1,-1,-1}, { 1, 9,11, 7, 1,11, 2, 1, 7,-1,-1,-1,-1,-1,-1,-1}, { 3, 6, 1, 5, 4, 9,11, 5, 9, 7, 5,11,-1,-1,-1,-1}, { 0, 3, 6, 9, 0, 6, 9, 5, 0,11, 5, 9, 7, 5,11,-1}, { 9,11, 4,11, 7, 4, 7, 0, 4, 0, 7, 2, 3, 6, 1,-1}, { 9,11, 7, 2, 9, 7, 9, 3, 6, 9, 2, 3,-1,-1,-1,-1}, {11, 3, 9, 9, 3, 2, 5, 4, 9, 5, 9, 2,-1,-1,-1,-1}, {11, 3, 2, 5,11, 2, 5, 9,11, 0, 9, 5, 1, 9, 0,-1}, { 3, 0, 4, 9, 3, 4,11, 3, 9,-1,-1,-1,-1,-1,-1,-1}, { 3, 1, 9, 3, 9,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 5, 4, 9,11, 5, 9,11, 2, 5, 6, 2,11, 1, 2, 6,-1}, {11, 6, 9, 0, 2, 5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {11, 6, 1, 0,11, 1,11, 4, 9,11, 0, 4,-1,-1,-1,-1}, {11, 6, 9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 9, 6,10,10, 6, 7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 7,10, 6,10, 9, 6, 0, 1, 4,-1,-1,-1,-1,-1,-1,-1}, { 9, 6,10, 6, 7,10, 0, 5, 2,-1,-1,-1,-1,-1,-1,-1}, { 5, 1, 4, 2, 1, 5,10, 9, 6, 7,10, 6,-1,-1,-1,-1}, {10, 9, 7, 9, 3, 7, 9, 1, 3,-1,-1,-1,-1,-1,-1,-1}, { 0, 3, 4, 4, 3, 7,10, 9, 4,10, 4, 7,-1,-1,-1,-1}, { 2, 0, 5, 9, 1, 3, 7, 9, 3,10, 9, 7,-1,-1,-1,-1}, { 4, 5, 2, 3, 4, 2, 3, 9, 4, 7, 9, 3,10, 9, 7,-1}, { 2,10, 3,10, 6, 3,10, 9, 6,-1,-1,-1,-1,-1,-1,-1}, { 0, 1, 4,10, 9, 6, 3,10, 6, 2,10, 3,-1,-1,-1,-1}, {10, 9, 5, 6, 3, 5, 3, 0, 5, 5, 9, 6,-1,-1,-1,-1}, {10, 9, 6, 3,10, 6, 3, 5,10, 1, 5, 3, 4, 5, 1,-1}, { 1,10, 9, 1, 2,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {10, 9, 4, 0,10, 4, 2,10, 0,-1,-1,-1,-1,-1,-1,-1}, { 9, 1, 0, 5, 9, 0,10, 9, 5,-1,-1,-1,-1,-1,-1,-1}, { 5,10, 9, 5, 9, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 4, 6, 8, 6,10, 8, 6, 7,10,-1,-1,-1,-1,-1,-1,-1}, { 8, 0, 1, 1, 7,10, 6, 7, 1, 8, 1,10,-1,-1,-1,-1}, { 5, 2, 0, 6, 7,10, 8, 6,10, 4, 6, 8,-1,-1,-1,-1}, { 6, 7,10, 8, 6,10, 8, 1, 6, 5, 1, 8, 2, 1, 5,-1}, { 3, 7,10, 3,10, 8, 1, 3, 8, 4, 1, 8,-1,-1,-1,-1}, { 0, 3, 7,10, 0, 7, 8, 0,10,-1,-1,-1,-1,-1,-1,-1}, { 4, 1, 8, 1, 3, 8, 3,10, 8,10, 3, 7, 2, 0, 5,-1}, { 8, 5, 2, 3, 8, 2, 8, 7,10, 8, 3, 7,-1,-1,-1,-1}, { 2,10, 8, 3, 2, 8, 6, 3, 8, 4, 6, 8,-1,-1,-1,-1}, { 8, 0, 1, 6, 8, 1, 6,10, 8, 3,10, 6, 2,10, 3,-1}, { 3, 0, 5,10, 3, 5,10, 6, 3, 8, 6,10, 4, 6, 8,-1}, { 5,10, 8, 6, 3, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 2,10, 8, 4, 2, 8, 1, 2, 4,-1,-1,-1,-1,-1,-1,-1}, {10, 8, 0,10, 0, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 1, 0, 5,10, 1, 5, 1, 8, 4, 1,10, 8,-1,-1,-1,-1}, { 5,10, 8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 6, 7, 9, 7, 8, 9, 7, 5, 8,-1,-1,-1,-1,-1,-1,-1}, { 1, 4, 0, 7, 5, 8, 9, 7, 8, 6, 7, 9,-1,-1,-1,-1}, { 6, 7, 9, 9, 7, 2, 0, 8, 9, 0, 9, 2,-1,-1,-1,-1}, { 2, 1, 4, 8, 2, 4, 8, 7, 2, 9, 7, 8, 6, 7, 9,-1}, { 7, 5, 8, 8, 1, 3, 9, 1, 8, 7, 8, 3,-1,-1,-1,-1}, { 7, 5, 8, 9, 7, 8, 9, 3, 7, 4, 3, 9, 0, 3, 4,-1}, { 9, 1, 3, 7, 9, 3, 7, 8, 9, 2, 8, 7, 0, 8, 2,-1}, { 8, 9, 4, 3, 7, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 3, 2, 6, 6, 2, 9, 2, 5, 9, 5, 8, 9,-1,-1,-1,-1}, { 6, 3, 9, 3, 2, 9, 2, 8, 9, 8, 2, 5, 0, 1, 4,-1}, { 0, 8, 9, 6, 0, 9, 3, 0, 6,-1,-1,-1,-1,-1,-1,-1}, { 8, 9, 6, 3, 8, 6, 8, 1, 4, 8, 3, 1,-1,-1,-1,-1}, { 1, 2, 5, 8, 1, 5, 9, 1, 8,-1,-1,-1,-1,-1,-1,-1}, { 9, 4, 0, 2, 9, 0, 9, 5, 8, 9, 2, 5,-1,-1,-1,-1}, { 1, 0, 8, 1, 8, 9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 8, 9, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 6, 5, 4, 7, 5, 6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 7, 5, 0, 1, 7, 0, 6, 7, 1,-1,-1,-1,-1,-1,-1,-1}, { 6, 7, 2, 0, 6, 2, 4, 6, 0,-1,-1,-1,-1,-1,-1,-1}, { 1, 6, 7, 1, 7, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 5, 4, 1, 3, 5, 1, 7, 5, 3,-1,-1,-1,-1,-1,-1,-1}, { 5, 0, 3, 5, 3, 7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 4, 1, 3, 7, 4, 3, 4, 2, 0, 4, 7, 2,-1,-1,-1,-1}, { 7, 2, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 4, 6, 3, 2, 4, 3, 5, 4, 2,-1,-1,-1,-1,-1,-1,-1}, { 6, 3, 2, 5, 6, 2, 6, 0, 1, 6, 5, 0,-1,-1,-1,-1}, { 0, 4, 6, 0, 6, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 3, 1, 6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 2, 5, 4, 2, 4, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 2, 5, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, { 1, 0, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} }; teem-1.11.0~svn6057/src/seek/methodsSeek.c0000664000175000017500000001243312165631065017752 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "seek.h" #include "privateSeek.h" const int seekPresent = 42; seekContext * seekContextNew(void) { seekContext *sctx; unsigned int fi; sctx = (seekContext *)calloc(1, sizeof(seekContext)); if (sctx) { sctx->verbose = 0; sctx->ninscl = NULL; sctx->gctx = NULL; sctx->pvl = NULL; sctx->type = seekTypeUnknown; sctx->sclvItem = -1; sctx->normItem = -1; sctx->gradItem = -1; sctx->evalItem = -1; sctx->evecItem = -1; sctx->stngItem = -1; sctx->hessItem = -1; sctx->lowerInside = AIR_FALSE; sctx->normalsFind = AIR_FALSE; sctx->strengthUse = AIR_FALSE; sctx->strengthSign = 1; sctx->isovalue = AIR_NAN; sctx->evalDiffThresh = 1.0; /* roughly reasonable for uchar data; * really should depend on dynamic range */ sctx->strength = 0.0; ELL_3V_SET(sctx->samples, 0, 0, 0); /* these two magic values assume a certain level of surface smoothness, which certainly does not apply to all cases */ sctx->facesPerVoxel = 2.15; sctx->vertsPerVoxel = 1.15; sctx->pldArrIncr = 2048; sctx->nin = NULL; sctx->flag = AIR_CAST(int *, calloc(flagLast, sizeof(int))); for (fi=flagUnknown+1; fiflag[fi] = AIR_FALSE; } sctx->baseDim = 0; sctx->_shape = gageShapeNew(); sctx->shape = NULL; sctx->nsclDerived = nrrdNew(); sctx->sclvAns = NULL; sctx->normAns = NULL; sctx->gradAns = NULL; sctx->evalAns = NULL; sctx->evecAns = NULL; sctx->stngAns = NULL; sctx->hessAns = NULL; sctx->reverse = AIR_FALSE; ELL_3M_IDENTITY_SET(sctx->txfNormal); sctx->spanSize = 300; sctx->nspanHist = nrrdNew(); sctx->range = nrrdRangeNew(AIR_NAN, AIR_NAN); sctx->sx = 0; sctx->sy = 0; sctx->sz = 0; ELL_4M_IDENTITY_SET(sctx->txfIdx); sctx->nvidx = nrrdNew(); sctx->nsclv = nrrdNew(); sctx->ngrad = nrrdNew(); sctx->neval = nrrdNew(); sctx->nevec = nrrdNew(); sctx->nflip = nrrdNew(); sctx->nstng = nrrdNew(); sctx->nhess = nrrdNew(); sctx->nt = nrrdNew(); sctx->nfacevidx = nrrdNew(); sctx->nedgealpha = nrrdNew(); sctx->nedgenorm = nrrdNew(); sctx->nedgeicoord = nrrdNew(); sctx->nfacecoord = nrrdNew(); sctx->nfacenorm = nrrdNew(); sctx->nfaceicoord = nrrdNew(); sctx->npairs = nrrdNew(); sctx->ngradcontext = nrrdNew(); sctx->nhesscontext = nrrdNew(); sctx->ntcontext = nrrdNew(); sctx->nstngcontext = nrrdNew(); sctx->ntreated = nrrdNew(); sctx->vidx = NULL; sctx->sclv = NULL; sctx->grad = NULL; sctx->eval = NULL; sctx->evec = NULL; sctx->flip = NULL; sctx->stng = NULL; sctx->voxNum = 0; sctx->vertNum = 0; sctx->faceNum = 0; sctx->strengthSeenMax = AIR_NAN; sctx->time = AIR_NAN; } return sctx; } seekContext * seekContextNix(seekContext *sctx) { if (sctx) { airFree(sctx->flag); sctx->flag = NULL; sctx->_shape = gageShapeNix(sctx->_shape); sctx->nsclDerived = nrrdNuke(sctx->nsclDerived); sctx->nspanHist = nrrdNuke(sctx->nspanHist); sctx->range = nrrdRangeNix(sctx->range); sctx->nvidx = nrrdNuke(sctx->nvidx); sctx->nsclv = nrrdNuke(sctx->nsclv); sctx->ngrad = nrrdNuke(sctx->ngrad); sctx->neval = nrrdNuke(sctx->neval); sctx->nevec = nrrdNuke(sctx->nevec); sctx->nflip = nrrdNuke(sctx->nflip); sctx->nstng = nrrdNuke(sctx->nstng); sctx->nhess = nrrdNuke(sctx->nhess); sctx->nt = nrrdNuke(sctx->nt); sctx->nfacevidx = nrrdNuke(sctx->nfacevidx); sctx->nedgealpha = nrrdNuke(sctx->nedgealpha); sctx->nedgenorm = nrrdNuke(sctx->nedgenorm); sctx->nedgeicoord = nrrdNuke(sctx->nedgeicoord); sctx->nfacecoord = nrrdNuke(sctx->nfacecoord); sctx->nfacenorm = nrrdNuke(sctx->nfacenorm); sctx->nfaceicoord = nrrdNuke(sctx->nfaceicoord); sctx->npairs = nrrdNuke(sctx->npairs); sctx->ngradcontext = nrrdNuke(sctx->ngradcontext); sctx->nhesscontext = nrrdNuke(sctx->nhesscontext); sctx->ntcontext = nrrdNuke(sctx->ntcontext); sctx->nstngcontext = nrrdNuke(sctx->nstngcontext); sctx->ntreated = nrrdNuke(sctx->ntreated); airFree(sctx); } return NULL; } teem-1.11.0~svn6057/src/seek/GNUmakefile0000664000175000017500000000356712165631065017415 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := seek #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### $(L).NEED = gage limn ell nrrd biff hest air $(L).PUBLIC_HEADERS = seek.h $(L).PRIVATE_HEADERS = $(L).OBJS = enumsSeek.o tables.o methodsSeek.o setSeek.o updateSeek.o \ extract.o textract.o descend.o $(L).TESTS = test/tiso test/trv #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/seek/privateSeek.h0000664000175000017500000000505612165631065017771 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ enum { flagUnknown, flagData, flagType, flagSamples, flagLowerInside, flagNormalsFind, flagStrength, flagStrengthUse, flagItemValue, flagItemStrength, flagItemNormal, flagItemGradient, flagItemEigensystem, flagItemHess, flagIsovalue, flagEvalDiffThresh, flagNinEtAl, flagAnswerPointers, flagSxSySz, flagReverse, flagTxfNormal, flagSlabCacheAlloc, flagSclDerived, flagSpanSpaceHist, flagResult, flagLast }; typedef struct { int evti[12]; /* edge vertex index */ double (*scllup)(const void *, size_t); unsigned int esIdx, /* eigensystem index */ zi; /* slice index we're currently on */ int modeSign; const void *scldata; airArray *xyzwArr, *normArr, *indxArr; } baggage; /* extract.c: This one is also needed in textract.c: */ extern void _seekIdxProbe(seekContext *sctx, baggage *bag, double xi, double yi, double zi); /* textract.c: Some routines that are also used in descend.c */ extern void _seekHess2T(double *T, const double *evals, const double *evecs, const double evalDiffThresh, const char ridge); extern void _seekHessder2Tder(double *Tder, const double *hessder, const double *evals, const double *evecs, const double evalDiffThresh, const char ridge); extern int _seekShuffleProbeT(seekContext *sctx, baggage *bag); extern int _seekTriangulateT(seekContext *sctx, baggage *bag, limnPolyData *lpld); teem-1.11.0~svn6057/src/seek/sources.cmake0000664000175000017500000000044011263467454020022 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(SEEK_SOURCES descend.c enumsSeek.c extract.c methodsSeek.c privateSeek.h seek.h setSeek.c tables.c textract.c updateSeek.c ) ADD_TEEM_LIBRARY(seek ${SEEK_SOURCES}) teem-1.11.0~svn6057/src/seek/extract.c0000664000175000017500000010207112165631065017147 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "seek.h" #include "privateSeek.h" static baggage * baggageNew(seekContext *sctx) { baggage *bag; unsigned int sx; bag = AIR_CALLOC(1, baggage); /* this is basically the mapping from the 12 edges on each voxel to the 5 unique edges for each sample on the slab, based on the lay-out defined in the beginning of tables.c */ sx = AIR_CAST(unsigned int, sctx->sx); /* X Y */ bag->evti[ 0] = 0 + 5*(0 + sx*0); bag->evti[ 1] = 1 + 5*(0 + sx*0); bag->evti[ 2] = 1 + 5*(1 + sx*0); bag->evti[ 3] = 0 + 5*(0 + sx*1); bag->evti[ 4] = 2 + 5*(0 + sx*0); bag->evti[ 5] = 2 + 5*(1 + sx*0); bag->evti[ 6] = 2 + 5*(0 + sx*1); bag->evti[ 7] = 2 + 5*(1 + sx*1); bag->evti[ 8] = 3 + 5*(0 + sx*0); bag->evti[ 9] = 4 + 5*(0 + sx*0); bag->evti[10] = 4 + 5*(1 + sx*0); bag->evti[11] = 3 + 5*(0 + sx*1); switch (sctx->type) { case seekTypeRidgeSurface: case seekTypeRidgeSurfaceOP: case seekTypeRidgeSurfaceT: bag->esIdx = 2; bag->modeSign = -1; break; case seekTypeValleySurface: case seekTypeValleySurfaceOP: case seekTypeValleySurfaceT: bag->esIdx = 0; bag->modeSign = +1; break; case seekTypeMaximalSurface: bag->esIdx = 0; bag->modeSign = -1; break; case seekTypeMinimalSurface: bag->esIdx = 2; bag->modeSign = +1; break; default: /* biffAddf(SEEK, "%s: feature type %s not handled", me, airEnumStr(seekType, sctx->type)); return 1; */ /* without biff, we get as nasty as possible */ bag->esIdx = UINT_MAX; bag->modeSign = 0; break; } if (seekTypeIsocontour == sctx->type) { if (sctx->ninscl) { bag->scllup = nrrdDLookup[sctx->ninscl->type]; bag->scldata = sctx->ninscl->data; } else { bag->scllup = nrrdDLookup[sctx->nsclDerived->type]; bag->scldata = sctx->nsclDerived->data; } } else { bag->scllup = NULL; bag->scldata = NULL; } bag->xyzwArr = NULL; bag->normArr = NULL; bag->indxArr = NULL; return bag; } static baggage * baggageNix(baggage *bag) { if (bag) { airArrayNix(bag->normArr); airArrayNix(bag->xyzwArr); airArrayNix(bag->indxArr); airFree(bag); } return NULL; } static int outputInit(seekContext *sctx, baggage *bag, limnPolyData *lpld) { static const char me[]="outputInit"; unsigned int estVertNum, estFaceNum, minI, maxI, valI, *spanHist; airPtrPtrUnion appu; int E; if (seekTypeIsocontour == sctx->type && AIR_IN_OP(sctx->range->min, sctx->isovalue, sctx->range->max)) { unsigned int estVoxNum=0; /* estimate number of voxels, faces, and vertices involved */ spanHist = AIR_CAST(unsigned int*, sctx->nspanHist->data); valI = airIndex(sctx->range->min, sctx->isovalue, sctx->range->max, sctx->spanSize); for (minI=0; minI<=valI; minI++) { for (maxI=valI; maxIspanSize; maxI++) { estVoxNum += spanHist[minI + sctx->spanSize*maxI]; } } estVertNum = AIR_CAST(unsigned int, estVoxNum*(sctx->vertsPerVoxel)); estFaceNum = AIR_CAST(unsigned int, estVoxNum*(sctx->facesPerVoxel)); if (sctx->verbose) { fprintf(stderr, "%s: estimated vox --> vert, face: %u --> %u, %u\n", me, estVoxNum, estVertNum, estFaceNum); } } else { estVertNum = 0; estFaceNum = 0; } /* need something non-zero so that pre-allocations below aren't no-ops */ estVertNum = AIR_MAX(1, estVertNum); estFaceNum = AIR_MAX(1, estFaceNum); /* initialize limnPolyData with estimated # faces and vertices */ /* we will manage the innards of the limnPolyData entirely ourselves */ if (limnPolyDataAlloc(lpld, 0, 0, 0, 0)) { biffAddf(SEEK, "%s: trouble emptying given polydata", me); return 1; } bag->xyzwArr = airArrayNew((appu.f = &(lpld->xyzw), appu.v), &(lpld->xyzwNum), 4*sizeof(float), sctx->pldArrIncr); if (sctx->normalsFind) { bag->normArr = airArrayNew((appu.f = &(lpld->norm), appu.v), &(lpld->normNum), 3*sizeof(float), sctx->pldArrIncr); } else { bag->normArr = NULL; } bag->indxArr = airArrayNew((appu.ui = &(lpld->indx), appu.v), &(lpld->indxNum), sizeof(unsigned int), sctx->pldArrIncr); lpld->primNum = 1; /* for now, its just triangle soup */ lpld->type = AIR_CALLOC(lpld->primNum, unsigned char); lpld->icnt = AIR_CALLOC(lpld->primNum, unsigned int); lpld->type[0] = limnPrimitiveTriangles; lpld->icnt[0] = 0; /* incremented below */ E = 0; airArrayLenPreSet(bag->xyzwArr, estVertNum); E |= !(bag->xyzwArr->data); if (sctx->normalsFind) { airArrayLenPreSet(bag->normArr, estVertNum); E |= !(bag->normArr->data); } airArrayLenPreSet(bag->indxArr, 3*estFaceNum); E |= !(bag->indxArr->data); if (E) { biffAddf(SEEK, "%s: couldn't pre-allocate contour geometry (%p %p %p)", me, bag->xyzwArr->data, (sctx->normalsFind ? bag->normArr->data : NULL), bag->indxArr->data); return 1; } /* initialize output summary info */ sctx->voxNum = 0; sctx->vertNum = 0; sctx->faceNum = 0; return 0; } static double sclGet(seekContext *sctx, baggage *bag, unsigned int xi, unsigned int yi, unsigned int zi) { zi = AIR_MIN(sctx->sz-1, zi); return bag->scllup(bag->scldata, xi + sctx->sx*(yi + sctx->sy*zi)); } void _seekIdxProbe(seekContext *sctx, baggage *bag, double xi, double yi, double zi) { double idxOut[4], idxIn[4]; AIR_UNUSED(bag); ELL_4V_SET(idxOut, xi, yi, zi, 1); ELL_4MV_MUL(idxIn, sctx->txfIdx, idxOut); ELL_4V_HOMOG(idxIn, idxIn); gageProbe(sctx->gctx, idxIn[0], idxIn[1], idxIn[2]); return; } /* ** this is one of the few things that has to operate on more than one ** zi plane at once, and it is honestly probably the primary motivation ** for putting zi into the baggage. ** ** NOTE: this is doing some bounds (on the positive x, y, z edges of the ** volume) that probably should be done closer to the caller */ static int evecFlipProbe(seekContext *sctx, baggage *bag, signed char *flip, /* OUTPUT HERE */ unsigned int xi, unsigned int yi, unsigned int ziOff, unsigned int dx, unsigned int dy, unsigned int dz) { static const char me[]="evecFlipProbe"; unsigned int sx, sy, sz; double u, du, dot, wantDot, minDu, mode; double current[3], next[3], posNext[3], posA[3], posB[3], evecA[3], evecB[3]; sx = AIR_CAST(unsigned int, sctx->sx); sy = AIR_CAST(unsigned int, sctx->sy); sz = AIR_CAST(unsigned int, sctx->sz); if (!(xi + dx < sx && yi + dy < sy && bag->zi + ziOff + dz < sz)) { /* the edge we're being asked about is outside the volume */ *flip = 0; return 0; } /* Note: Strength checking is no longer performed here. * TS 2009-08-18 */ /* this edge is in bounds */ ELL_3V_SET(posA, xi, yi, bag->zi+ziOff); ELL_3V_SET(posB, xi+dx, yi+dy, bag->zi+ziOff+dz); ELL_3V_COPY(evecA, sctx->evec + 3*(bag->esIdx + 3*(ziOff + 2*(xi + sx*yi)))); ELL_3V_COPY(evecB, sctx->evec + 3*(bag->esIdx + 3*(ziOff+dz + 2*(xi+dx + sx*(yi+dy))))); #define SETNEXT(uu) \ ELL_3V_SCALE_ADD2(posNext, 1.0-(uu), posA, (uu), posB); \ _seekIdxProbe(sctx, bag, posNext[0], posNext[1], posNext[2]); \ ELL_3V_COPY(next, sctx->evecAns + 3*bag->esIdx); \ if (ELL_3V_DOT(current, next) < 0) { \ ELL_3V_SCALE(next, -1, next); \ } \ dot = ELL_3V_DOT(current, next); \ mode = bag->modeSign*airMode3_d(sctx->evalAns); ELL_3V_COPY(current, evecA); u = 0; du = 0.49999; wantDot = 0.9; /* between cos(pi/6) and cos(pi/8) */ minDu = 0.0002; while (u + du < 1.0) { SETNEXT(u+du); /* Note: This was set to -0.8 in the original code. Again, I found * that increasing it could eliminate spurious holes in the * mesh. TS 2009-08-18 */ if (mode < -0.99) { /* sorry, eigenvalue mode got too close to 2nd order isotropy */ *flip = 0; return 0; } while (dot < wantDot) { /* angle between current and next is too big; reduce step */ du /= 2; if (du < minDu) { fprintf(stderr, "%s: evector wild @ u=%g: du=%g < minDu=%g; " "dot=%g; mode = %g; " "(xi,yi,zi)=(%u,%u,%u+%u); (dx,dy,dz)=(%u,%u,%u) ", me, u, du, minDu, dot, mode, xi, yi, bag->zi, ziOff, dx, dy, dz); *flip = 0; return 0; } SETNEXT(u+du); if (mode < -0.99) { /* sorry, eigenvalue mode got too close to 2nd order isotropy */ *flip = 0; return 0; } } /* current and next have a small angle between them */ ELL_3V_COPY(current, next); u += du; } /* last fake iteration, to check endpoint explicitly */ u = 1.0; SETNEXT(u); if (dot < wantDot) { biffAddf(SEEK, "%s: confused at end of edge", me); return 1; } ELL_3V_COPY(current, next); #undef SETNEXT /* we have now tracked the eigenvector along the edge */ dot = ELL_3V_DOT(current, evecB); *flip = (dot > 0 ? +1 : -1); return 0; } /* ** !!! this has to be done as a separate second pass because of how ** !!! the flip quantities correspond to the edges between voxels ** ** For efficiency, evecFlipProbe is only executed on request (by ** setting sctx->treated: 0x01 requests all edges of this voxel, 0x02 ** states that unique edge 3 was treated, 0x04 for unique edge 4) TS */ static int evecFlipShuffleProbe(seekContext *sctx, baggage *bag) { static const char me[]="evecFlipShuffleProbe"; unsigned int xi, yi, sx, sy, si; signed char flipA, flipB, flipC; sx = AIR_CAST(unsigned int, sctx->sx); sy = AIR_CAST(unsigned int, sctx->sy); /* NOTE: these have to go all the way to sy-1 and sx-1, instead of sy-2 and sx-2 (like shuffleProbe() below) because of the need to set the flips on the edges at the positive X and Y volume boundary. The necessary bounds checking happens in evecFlipProbe(): messy, I know */ for (yi=0; yitreated[si]&0x02) { /* has been treated, just copy result */ sctx->flip[0 + 5*si] = sctx->flip[3 + 5*si]; } else if (sctx->treated[si]&0x01 || (yi!=0 && sctx->treated[xi+sx*(yi-1)]&0x01)) { /* need to treat this */ if (evecFlipProbe(sctx, bag, &flipA, xi, yi, 0, 1, 0, 0)) { biffAddf(SEEK, "%s: problem at (xi,yi) = (%u,%u), zi=0", me, xi, yi); return 1; } sctx->flip[0 + 5*si] = flipA; } if (sctx->treated[si]&0x04) { /* has been treated, just copy */ sctx->flip[1 + 5*si] = sctx->flip[4 + 5*si]; } else if (sctx->treated[si]&0x01 || (xi!=0 && sctx->treated[xi-1+sx*yi]&0x01)) { if (evecFlipProbe(sctx, bag, &flipB, xi, yi, 0, 0, 1, 0)) { biffAddf(SEEK, "%s: problem at (xi,yi) = (%u,%u), zi=0", me, xi, yi); return 1; } sctx->flip[1 + 5*si] = flipB; } if (sctx->treated[si]&0x01 || (xi!=0 && sctx->treated[xi-1+sx*yi]&0x01) || (yi!=0 && sctx->treated[xi+sx*(yi-1)]&0x01) || (xi!=0 && yi!=0 && sctx->treated[xi-1+sx*(yi-1)]&0x01)) { if (evecFlipProbe(sctx, bag, &flipA, xi, yi, 0, 0, 0, 1)) { biffAddf(SEEK, "%s: problem at (xi,yi,zi) = (%u,%u,%u)", me, xi, yi, bag->zi); return 1; } sctx->flip[2 + 5*si] = flipA; } if (sctx->treated[si]&0x01 || (yi!=0 && sctx->treated[xi+sx*(yi-1)]&0x01)) { if (evecFlipProbe(sctx, bag, &flipB, xi, yi, 1, 1, 0, 0)) { biffAddf(SEEK, "%s: problem at (xi,yi,zi) = (%u,%u,%u)", me, xi, yi, bag->zi); return 1; } sctx->flip[3 + 5*si] = flipB; sctx->treated[si]|=0x02; } else sctx->treated[si]&=0xFD; if (sctx->treated[si]&0x01 || (xi!=0 && sctx->treated[xi-1+sx*yi]&0x01)) { if (evecFlipProbe(sctx, bag, &flipC, xi, yi, 1, 0, 1, 0)) { biffAddf(SEEK, "%s: problem at (xi,yi,zi) = (%u,%u,%u)", me, xi, yi, bag->zi); return 1; } sctx->flip[4 + 5*si] = flipC; sctx->treated[si]|=0x04; } else sctx->treated[si]&=0xFB; /* ================================================= */ } } return 0; } static int shuffleProbe(seekContext *sctx, baggage *bag) { static const char me[]="shuffleProbe"; unsigned int xi, yi, sx, sy, si, spi; sx = AIR_CAST(unsigned int, sctx->sx); sy = AIR_CAST(unsigned int, sctx->sy); if (!sctx->strengthUse) { /* just request all edges */ memset(sctx->treated, 0x01, sizeof(char)*sctx->sx*sctx->sy); } else { if (!bag->zi) { /* clear full treated array */ memset(sctx->treated, 0, sizeof(char)*sctx->sx*sctx->sy); } else { /* only clear requests for edge orientation */ for (yi=0; yitreated[xi+sx*yi] &= 0xFE; } } } } for (yi=0; yizi) { /* ----------------- set/probe bottom of initial slab */ sctx->vidx[0 + 5*si] = -1; sctx->vidx[1 + 5*si] = -1; if (sctx->gctx) { /* HEY: need this check, what's the right way? */ _seekIdxProbe(sctx, bag, xi, yi, 0); } if (sctx->strengthUse) { sctx->stng[0 + 2*si] = sctx->strengthSign*sctx->stngAns[0]; if (!si) { sctx->strengthSeenMax = sctx->stng[0 + 2*si]; } sctx->strengthSeenMax = AIR_MAX(sctx->strengthSeenMax, sctx->stng[0 + 2*si]); } switch (sctx->type) { case seekTypeIsocontour: sctx->sclv[0 + 4*spi] = (sclGet(sctx, bag, xi, yi, 0) - sctx->isovalue); sctx->sclv[1 + 4*spi] = (sclGet(sctx, bag, xi, yi, 0) - sctx->isovalue); sctx->sclv[2 + 4*spi] = (sclGet(sctx, bag, xi, yi, 1) - sctx->isovalue); break; case seekTypeRidgeSurface: case seekTypeValleySurface: case seekTypeMaximalSurface: case seekTypeMinimalSurface: case seekTypeRidgeSurfaceOP: case seekTypeValleySurfaceOP: ELL_3V_COPY(sctx->grad + 3*(0 + 2*si), sctx->gradAns); ELL_3V_COPY(sctx->eval + 3*(0 + 2*si), sctx->evalAns); ELL_3M_COPY(sctx->evec + 9*(0 + 2*si), sctx->evecAns); break; } } else { /* ------------------- shuffle to bottom from top of slab */ sctx->vidx[0 + 5*si] = sctx->vidx[3 + 5*si]; sctx->vidx[1 + 5*si] = sctx->vidx[4 + 5*si]; if (sctx->strengthUse) { sctx->stng[0 + 2*si] = sctx->stng[1 + 2*si]; } switch (sctx->type) { case seekTypeIsocontour: sctx->sclv[0 + 4*spi] = sctx->sclv[1 + 4*spi]; sctx->sclv[1 + 4*spi] = sctx->sclv[2 + 4*spi]; sctx->sclv[2 + 4*spi] = sctx->sclv[3 + 4*spi]; break; case seekTypeRidgeSurface: case seekTypeValleySurface: case seekTypeMaximalSurface: case seekTypeMinimalSurface: case seekTypeRidgeSurfaceOP: case seekTypeValleySurfaceOP: ELL_3V_COPY(sctx->grad + 3*(0 + 2*si), sctx->grad + 3*(1 + 2*si)); ELL_3V_COPY(sctx->eval + 3*(0 + 2*si), sctx->eval + 3*(1 + 2*si)); ELL_3M_COPY(sctx->evec + 9*(0 + 2*si), sctx->evec + 9*(1 + 2*si)); break; } } /* ----------------------- set/probe top of slab */ sctx->vidx[2 + 5*si] = -1; sctx->vidx[3 + 5*si] = -1; sctx->vidx[4 + 5*si] = -1; if (sctx->gctx) { /* HEY: need this check, what's the right way? */ _seekIdxProbe(sctx, bag, xi, yi, bag->zi+1); } if (sctx->strengthUse) { sctx->stng[1 + 2*si] = sctx->strengthSign*sctx->stngAns[0]; sctx->strengthSeenMax = AIR_MAX(sctx->strengthSeenMax, sctx->stng[1 + 2*si]); if (sctx->stng[0+2*si]>sctx->strength || sctx->stng[1+2*si]>sctx->strength) { /* mark up to four voxels as needed */ sctx->treated[si] |= 0x01; if (xi!=0) sctx->treated[xi-1+sx*yi] |= 0x01; if (yi!=0) sctx->treated[xi+sx*(yi-1)] |= 0x01; if (xi!=0 && yi!=0) sctx->treated[xi-1+sx*(yi-1)] |= 0x01; } } switch (sctx->type) { case seekTypeIsocontour: sctx->sclv[3 + 4*spi] = (sclGet(sctx, bag, xi, yi, bag->zi+2) - sctx->isovalue); break; case seekTypeRidgeSurface: case seekTypeValleySurface: case seekTypeMaximalSurface: case seekTypeMinimalSurface: case seekTypeRidgeSurfaceOP: case seekTypeValleySurfaceOP: ELL_3V_COPY(sctx->grad + 3*(1 + 2*si), sctx->gradAns); ELL_3V_COPY(sctx->eval + 3*(1 + 2*si), sctx->evalAns); ELL_3M_COPY(sctx->evec + 9*(1 + 2*si), sctx->evecAns); break; } /* ================================================= */ } /* copy ends of this scanline left/right to margin */ if (seekTypeIsocontour == sctx->type) { ELL_4V_COPY(sctx->sclv + 4*(0 + (sx+2)*(yi+1)), sctx->sclv + 4*(1 + (sx+2)*(yi+1))); ELL_4V_COPY(sctx->sclv + 4*(sx+1 + (sx+2)*(yi+1)), sctx->sclv + 4*(sx + (sx+2)*(yi+1))); } } /* copy top and bottom scanline up/down to margin */ if (seekTypeIsocontour == sctx->type) { for (xi=0; xisclv + 4*(xi + (sx+2)*0), sctx->sclv + 4*(xi + (sx+2)*1)); ELL_4V_COPY(sctx->sclv + 4*(xi + (sx+2)*(sy+1)), sctx->sclv + 4*(xi + (sx+2)*sy)); } } /* this is done as a separate pass because it looks at values between voxels (so its indexing is not trivial to fold into loops above) */ if (seekTypeRidgeSurface == sctx->type || seekTypeValleySurface == sctx->type || seekTypeMaximalSurface == sctx->type || seekTypeMinimalSurface == sctx->type) { if (evecFlipShuffleProbe(sctx, bag)) { biffAddf(SEEK, "%s: trouble at zi=%u\n", me, bag->zi); return 1; } } return 0; } #define VAL(xx, yy, zz) (val[4*( (xx) + (yy)*(sx+2) + spi) + (zz+1)]) static void voxelGrads(double vgrad[8][3], double *val, int sx, int spi) { ELL_3V_SET(vgrad[0], VAL( 1, 0, 0) - VAL(-1, 0, 0), VAL( 0, 1, 0) - VAL( 0, -1, 0), VAL( 0, 0, 1) - VAL( 0, 0, -1)); ELL_3V_SET(vgrad[1], VAL( 2, 0, 0) - VAL( 0, 0, 0), VAL( 1, 1, 0) - VAL( 1, -1, 0), VAL( 1, 0, 1) - VAL( 1, 0, -1)); ELL_3V_SET(vgrad[2], VAL( 1, 1, 0) - VAL(-1, 1, 0), VAL( 0, 2, 0) - VAL( 0, 0, 0), VAL( 0, 1, 1) - VAL( 0, 1, -1)); ELL_3V_SET(vgrad[3], VAL( 2, 1, 0) - VAL( 0, 1, 0), VAL( 1, 2, 0) - VAL( 1, 0, 0), VAL( 1, 1, 1) - VAL( 1, 1, -1)); ELL_3V_SET(vgrad[4], VAL( 1, 0, 1) - VAL(-1, 0, 1), VAL( 0, 1, 1) - VAL( 0, -1, 1), VAL( 0, 0, 2) - VAL( 0, 0, 0)); ELL_3V_SET(vgrad[5], VAL( 2, 0, 1) - VAL( 0, 0, 1), VAL( 1, 1, 1) - VAL( 1, -1, 1), VAL( 1, 0, 2) - VAL( 1, 0, 0)); ELL_3V_SET(vgrad[6], VAL( 1, 1, 1) - VAL(-1, 1, 1), VAL( 0, 2, 1) - VAL( 0, 0, 1), VAL( 0, 1, 2) - VAL( 0, 1, 0)); ELL_3V_SET(vgrad[7], VAL( 2, 1, 1) - VAL( 0, 1, 1), VAL( 1, 2, 1) - VAL( 1, 0, 1), VAL( 1, 1, 2) - VAL( 1, 1, 0)); } #undef VAL static void vvalIsoSet(seekContext *sctx, baggage *bag, double vval[8], unsigned int xi, unsigned int yi) { unsigned int sx, si, spi, vi; AIR_UNUSED(bag); sx = AIR_CAST(unsigned int, sctx->sx); si = xi + sx*yi; spi = (xi+1) + (sx+2)*(yi+1); /* learn voxel values */ /* X Y Z */ vval[0] = sctx->sclv[4*(0 + 0*(sx+2) + spi) + 1]; vval[1] = sctx->sclv[4*(1 + 0*(sx+2) + spi) + 1]; vval[2] = sctx->sclv[4*(0 + 1*(sx+2) + spi) + 1]; vval[3] = sctx->sclv[4*(1 + 1*(sx+2) + spi) + 1]; vval[4] = sctx->sclv[4*(0 + 0*(sx+2) + spi) + 2]; vval[5] = sctx->sclv[4*(1 + 0*(sx+2) + spi) + 2]; vval[6] = sctx->sclv[4*(0 + 1*(sx+2) + spi) + 2]; vval[7] = sctx->sclv[4*(1 + 1*(sx+2) + spi) + 2]; if (sctx->strengthUse) { double s, w, ssum, wsum; /* Z X Y */ ssum = wsum = 0; #define ACCUM(vi) w = AIR_ABS(1.0/vval[vi]); ssum += w*s; wsum += w s = sctx->stng[0 + 2*(0 + 0*sx + si)]; ACCUM(0); s = sctx->stng[0 + 2*(1 + 0*sx + si)]; ACCUM(1); s = sctx->stng[0 + 2*(0 + 1*sx + si)]; ACCUM(2); s = sctx->stng[0 + 2*(1 + 1*sx + si)]; ACCUM(3); s = sctx->stng[1 + 2*(0 + 0*sx + si)]; ACCUM(4); s = sctx->stng[1 + 2*(1 + 0*sx + si)]; ACCUM(5); s = sctx->stng[1 + 2*(0 + 1*sx + si)]; ACCUM(6); s = sctx->stng[1 + 2*(1 + 1*sx + si)]; ACCUM(7); #undef ACCUM if (ssum/wsum < sctx->strength) { for (vi=0; vi<8; vi++) { vval[vi] = 0; } } } return; } static void vvalSurfSet(seekContext *sctx, baggage *bag, double vval[8], unsigned int xi, unsigned int yi) { /* static const char me[]="vvalSurfSet"; */ double evec[8][3], grad[8][3], stng[8], maxStrength=0; signed char flip[12]={0,0,0,0,0,0,0,0,0,0,0,0}, flipProd; unsigned int sx, si, vi, ei, vrti[8]; sx = AIR_CAST(unsigned int, sctx->sx); si = xi + sx*yi; vrti[0] = 0 + 2*(xi + 0 + sx*(yi + 0)); vrti[1] = 0 + 2*(xi + 1 + sx*(yi + 0)); vrti[2] = 0 + 2*(xi + 0 + sx*(yi + 1)); vrti[3] = 0 + 2*(xi + 1 + sx*(yi + 1)); vrti[4] = 1 + 2*(xi + 0 + sx*(yi + 0)); vrti[5] = 1 + 2*(xi + 1 + sx*(yi + 0)); vrti[6] = 1 + 2*(xi + 0 + sx*(yi + 1)); vrti[7] = 1 + 2*(xi + 1 + sx*(yi + 1)); /* Our strategy is to create all triangles of which at least some * part meets the strength criterion, and to trim them in a * post-process. This avoids ragged boundaries */ for (vi=0; vi<8; vi++) { ELL_3V_COPY(grad[vi], sctx->grad + 3*vrti[vi]); ELL_3V_COPY(evec[vi], sctx->evec + 3*(bag->esIdx + 3*vrti[vi])); if (sctx->strengthUse) { stng[vi] = sctx->stng[vrti[vi]]; if (!vi) { maxStrength = stng[vi]; } else { maxStrength = AIR_MAX(maxStrength, stng[vi]); } } } flipProd = 1; if (sctx->type!=seekTypeRidgeSurfaceOP && sctx->type!=seekTypeValleySurfaceOP) { for (ei=0; ei<12; ei++) { flip[ei] = sctx->flip[bag->evti[ei] + 5*si]; flipProd *= flip[ei]; } } if ((sctx->strengthUse && maxStrength < sctx->strength) || !flipProd) { /* either the corners this voxel don't meet strength, or something else is funky */ for (vi=0; vi<8; vi++) { vval[vi] = 0; } } else { if (sctx->type==seekTypeRidgeSurfaceOP || sctx->type==seekTypeValleySurfaceOP) { /* find orientation based on outer product rule */ double outer[9]; double outerevals[3],outerevecs[9]; ELL_3MV_OUTER(outer,evec[0],evec[0]); for (vi=1; vi<8; ++vi) { ELL_3MV_OUTER_INCR(outer,evec[vi],evec[vi]); } ell_3m_eigensolve_d(outerevals, outerevecs, outer, AIR_TRUE); for (vi=0; vi<8; ++vi) { if (ELL_3V_DOT(evec[vi],outerevecs)<0) ELL_3V_SCALE(evec[vi], -1.0, evec[vi]); } } else { ELL_3V_SCALE(evec[1], flip[0], evec[1]); ELL_3V_SCALE(evec[2], flip[1], evec[2]); ELL_3V_SCALE(evec[3], flip[0]*flip[2], evec[3]); ELL_3V_SCALE(evec[4], flip[4], evec[4]); ELL_3V_SCALE(evec[5], flip[4]*flip[8], evec[5]); ELL_3V_SCALE(evec[6], flip[4]*flip[9], evec[6]); ELL_3V_SCALE(evec[7], flip[4]*flip[8]*flip[10], evec[7]); } for (vi=0; vi<8; vi++) { vval[vi] = ELL_3V_DOT(grad[vi], evec[vi]); } } return; } static int triangulate(seekContext *sctx, baggage *bag, limnPolyData *lpld) { /* static const char me[]="triangulate"; */ unsigned xi, yi, sx, sy, si, spi; /* ========================================================== */ /* NOTE: these things must agree with information in tables.c */ int e2v[12][2] = { /* maps edge index to corner vertex indices */ {0, 1}, /* 0 */ {0, 2}, /* 1 */ {1, 3}, /* 2 */ {2, 3}, /* 3 */ {0, 4}, /* 4 */ {1, 5}, /* 5 */ {2, 6}, /* 6 */ {3, 7}, /* 7 */ {4, 5}, /* 8 */ {4, 6}, /* 9 */ {5, 7}, /* 10 */ {6, 7} /* 11 */ }; double vccoord[8][3] = { /* vertex corner coordinates */ {0, 0, 0}, /* 0 */ {1, 0, 0}, /* 1 */ {0, 1, 0}, /* 2 */ {1, 1, 0}, /* 3 */ {0, 0, 1}, /* 4 */ {1, 0, 1}, /* 5 */ {0, 1, 1}, /* 6 */ {1, 1, 1} /* 7 */ }; /* ========================================================== */ sx = AIR_CAST(unsigned int, sctx->sx); sy = AIR_CAST(unsigned int, sctx->sy); for (yi=0; yitype) { case seekTypeIsocontour: vvalIsoSet(sctx, bag, vval, xi, yi); break; case seekTypeRidgeSurface: case seekTypeValleySurface: case seekTypeMaximalSurface: case seekTypeMinimalSurface: case seekTypeRidgeSurfaceOP: case seekTypeValleySurfaceOP: vvalSurfSet(sctx, bag, vval, xi, yi); break; } /* determine voxel and edge case */ vcase = 0; for (vi=0; vi<8; vi++) { vcase |= (vval[vi] > 0) << vi; } if (0 == vcase || 255 == vcase) { /* no triangles added here */ continue; } /* set voxel corner gradients */ if (seekTypeIsocontour == sctx->type && sctx->normalsFind && !sctx->normAns) { voxelGrads(vgrad, sctx->sclv, sx, spi); } sctx->voxNum++; ecase = seekContour3DTopoHackEdge[vcase]; /* create new vertices as needed */ for (ei=0; ei<12; ei++) { if ((ecase & (1 << ei)) && -1 == sctx->vidx[bag->evti[ei] + 5*si]) { int ovi; double tvec[3], grad[3], tlen; /* this edge is needed for triangulation, and, we haven't already created a vertex for it */ vi0 = e2v[ei][0]; vi1 = e2v[ei][1]; ww = vval[vi0]/(vval[vi0] - vval[vi1]); ELL_3V_LERP(vert, ww, vccoord[vi0], vccoord[vi1]); ELL_4V_SET(tvertA, vert[0] + xi, vert[1] + yi, vert[2] + bag->zi, 1); ELL_4MV_MUL(tvertB, sctx->txfIdx, tvertA); /* tvertB is now in input index space */ ELL_4MV_MUL(tvertA, sctx->shape->ItoW, tvertB); /* tvertA is now in input world space */ ELL_4V_HOMOG(tvertA, tvertA); ELL_4V_HOMOG(tvertB, tvertB); ovi = sctx->vidx[bag->evti[ei] + 5*si] = airArrayLenIncr(bag->xyzwArr, 1); ELL_4V_SET_TT(lpld->xyzw + 4*ovi, float, tvertA[0], tvertA[1], tvertA[2], 1.0); /* fprintf(stderr, "!%s: vert %u: %g %g %g\n", me, ovi, tvertA[0], tvertA[1], tvertA[2]); */ if (sctx->normalsFind) { airArrayLenIncr(bag->normArr, 1); if (sctx->normAns) { gageProbe(sctx->gctx, tvertB[0], tvertB[1], tvertB[2]); ELL_3V_SCALE_TT(lpld->norm + 3*ovi, float, -1, sctx->normAns); if (sctx->reverse) { ELL_3V_SCALE(lpld->norm + 3*ovi, -1, lpld->norm + 3*ovi); } } else { ELL_3V_LERP(grad, ww, vgrad[vi0], vgrad[vi1]); ELL_3MV_MUL(tvec, sctx->txfNormal, grad); ELL_3V_NORM_TT(lpld->norm + 3*ovi, float, tvec, tlen); } } sctx->vertNum++; /* fprintf(stderr, "%s: vert %d (edge %d) of (%d,%d,%d) " "at %g %g %g\n", me, sctx->vidx[bag->evti[ei] + 5*si], ei, xi, yi, zi, vert[0] + xi, vert[1] + yi, vert[2] + bag->zi); */ } } /* add triangles */ ti = 0; tcase = seekContour3DTopoHackTriangle[vcase]; while (-1 != tcase[0 + 3*ti]) { unsigned iii; ELL_3V_SET(vii, sctx->vidx[bag->evti[tcase[0 + 3*ti]] + 5*si], sctx->vidx[bag->evti[tcase[1 + 3*ti]] + 5*si], sctx->vidx[bag->evti[tcase[2 + 3*ti]] + 5*si]); if (sctx->reverse) { int tmpi; tmpi = vii[1]; vii[1] = vii[2]; vii[2] = tmpi; } iii = airArrayLenIncr(bag->indxArr, 3); ELL_3V_COPY(lpld->indx + iii, vii); /* fprintf(stderr, "!%s: tri %u: %u %u %u\n", me, iii/3, vii[0], vii[1], vii[2]); */ lpld->icnt[0] += 3; sctx->faceNum++; ti++; } } } return 0; } static int surfaceExtract(seekContext *sctx, limnPolyData *lpld) { static const char me[]="surfaceExtract"; char done[AIR_STRLEN_SMALL]; unsigned int zi, sz; baggage *bag; bag = baggageNew(sctx); sz = AIR_CAST(unsigned int, sctx->sz); /* this creates the airArrays in bag */ if (outputInit(sctx, bag, lpld)) { biffAddf(SEEK, "%s: trouble", me); return 1; } if (sctx->verbose > 2) { fprintf(stderr, "%s: extracting ... ", me); } for (zi=0; ziverbose > 2) { fprintf(stderr, "%s", airDoneStr(0, zi, sz-2, done)); fflush(stderr); } bag->zi = zi; if (sctx->type==seekTypeRidgeSurfaceT || sctx->type==seekTypeValleySurfaceT) { if (_seekShuffleProbeT(sctx, bag) || _seekTriangulateT(sctx, bag, lpld)) trouble = 1; } else { if (shuffleProbe(sctx, bag) || triangulate(sctx, bag, lpld)) trouble = 1; } if (trouble) { biffAddf(SEEK, "%s: trouble on zi = %u", me, zi); return 1; } } if (sctx->verbose > 2) { fprintf(stderr, "%s\n", airDoneStr(0, zi, sz-2, done)); } /* this cleans up the airArrays in bag */ baggageNix(bag); return 0; } int seekExtract(seekContext *sctx, limnPolyData *lpld) { static const char me[]="seekExtract"; double time0; int E; if (!( sctx && lpld )) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } if (seekTypeIsocontour == sctx->type) { if (!AIR_EXISTS(sctx->isovalue)) { biffAddf(SEEK, "%s: didn't seem to ever set isovalue (now %g)", me, sctx->isovalue); return 1; } } if (sctx->verbose) { fprintf(stderr, "%s: --------------------\n", me); fprintf(stderr, "%s: flagResult = %d\n", me, sctx->flag[flagResult]); } /* reset max strength seen */ sctx->strengthSeenMax = AIR_NAN; /* start time */ time0 = airTime(); switch(sctx->type) { case seekTypeIsocontour: case seekTypeRidgeSurface: case seekTypeValleySurface: case seekTypeMinimalSurface: case seekTypeMaximalSurface: case seekTypeRidgeSurfaceOP: case seekTypeRidgeSurfaceT: case seekTypeValleySurfaceOP: case seekTypeValleySurfaceT: E = surfaceExtract(sctx, lpld); break; default: biffAddf(SEEK, "%s: sorry, %s extraction not implemented", me, airEnumStr(seekType, sctx->type)); return 1; break; } if (E) { biffAddf(SEEK, "%s: trouble", me); return 1; } /* end time */ sctx->time = airTime() - time0; sctx->flag[flagResult] = AIR_FALSE; return 0; } teem-1.11.0~svn6057/src/seek/seek.h0000664000175000017500000003265212165631065016440 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SEEK_HAS_BEEN_INCLUDED #define SEEK_HAS_BEEN_INCLUDED #include #include #include #include #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(seek_EXPORTS) || defined(teem_EXPORTS) # define SEEK_EXPORT extern __declspec(dllexport) # else # define SEEK_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define SEEK_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define SEEK seekBiffKey /* ******** seekType* enum ** ** the kinds of features that can be extracted with seek3DExtract */ enum { seekTypeUnknown, /* 0: nobody knows */ seekTypeIsocontour, /* 1: standard marching-cubes */ seekTypeRidgeSurface, /* 2: */ seekTypeValleySurface, /* 3: */ seekTypeRidgeLine, /* 4: */ seekTypeValleyLine, /* 5: */ seekTypeMinimalSurface, /* 6: */ seekTypeMaximalSurface, /* 7: */ seekTypeRidgeSurfaceOP, /* 8: ridge surface, using outer product rule */ seekTypeRidgeSurfaceT, /* 9: ridge surface, based on tensor T */ seekTypeValleySurfaceOP, /* 10: valley surface, using outer product rule */ seekTypeValleySurfaceT, /* 11: valley surface, based on tensor T */ seekTypeLast }; #define SEEK_TYPE_MAX 11 typedef struct { /* ------- input ------- */ int verbose; const Nrrd *ninscl; /* a vanilla scalar (only) volume */ gageContext *gctx; /* for handling non-vanilla scalar volumes */ gagePerVolume *pvl; /* for handling non-vanilla scalar volumes */ int type; /* from seekType* enum */ int sclvItem, gradItem, normItem, evalItem, evecItem, stngItem, hessItem; /* "stng" == strength */ int lowerInside, /* lower values are logically inside isosurfaces, not outside */ normalsFind, /* find normals for isosurface vertices, either with forward and central differencing on values, or via the given normItem. **NOTE** simplifying assumption: if gctx, and if normalsFind, then normItem must be set (i.e. will not find normals via differencing when we have a gctx) */ strengthUse, /* do reject contributions to the feature for which the strength measurement falls below the required threshold */ strengthSign; /* the sign (either +1 of -1) of the strength quantity when it is considered to have high magnitude. E.g. for ridge surfaces, hesseval[2] should be very negative so strengthSign == -1 */ double isovalue, /* for seekTypeIsocontour */ strength, /* if strengthUse, feature needs to satisfy strengthAns*strengthSign > strength */ evalDiffThresh; /* threshold for eigenvalue difference */ size_t samples[3]; /* user-requested dimensions of feature grid */ double facesPerVoxel, /* approximate; for pre-allocating geometry */ vertsPerVoxel; /* approximate; for pre-allocating geometry */ unsigned int pldArrIncr; /* increment for airArrays used during the creation of geometry */ /* ------ internal ----- */ int *flag; /* for controlling updates of internal state */ const Nrrd *nin; /* either ninscl or gctx->pvl->nin */ unsigned int baseDim; /* 0 for scalars, or something else */ gageShape *_shape, /* local shape, always owned */ *shape; /* not owned */ Nrrd *nsclDerived; /* for seekTypeIsocontour: volume of computed scalars, as measured by gage */ const double *sclvAns, *gradAns, *normAns, *evalAns, *evecAns, *stngAns, *hessAns; int reverse; /* for seekTypeIsocontour: need to reverse sign of scalar field normal to get the "right" isocontour normal */ double txfNormal[9]; /* for seekTypeIsocontour: how to transform normals */ size_t spanSize; /* for seekTypeIsocontour: resolution of span space along edge */ Nrrd *nspanHist; /* for seekTypeIsocontour: span space histogram */ NrrdRange *range; /* for seekTypeIsocontour: range of scalars */ size_t sx, sy, sz; /* actual dimensions of feature grid */ double txfIdx[16]; /* transforms from the index space of the feature sampling grid to the index space of the underlying volume */ int *vidx, /* 15 * sx * sy array of vertex index offsets, to support re-using of vertices across voxels and slices. Yes, this means there is allocation for edges in the voxels beyond the positive X and Y edges */ *facevidx; /* 4 * sx * sy array of vertex indices for degenerate points */ double *sclv, /* 4 * (sx+2) * (sy+2) scalar value cache, with Z as fastest axis, and one sample of padding on all sides, as needed for central-difference-based gradients */ *grad, /* 3 * 2 * sx * sy array of gradients, for crease feature extraction; axis ordering (vec,z,x,y) */ *eval, /* 3 * 2 * sx * sy array of eigenvalues */ *evec, /* 9 * 2 * sx * sy array of eigenvectors */ *hess, /* 9 * 2 * sx * sy array of hessian matrices */ *t, /* 9 * 2 * sx * sy array of T tensors */ *edgealpha, /* 5 * 3 * sx * sy position of edge intersections */ *edgenorm, /* 5 * 9 * sx * sy normal at edge intersection */ *edgeicoord, /* 5 * 9 * sx * sy index coordinates of edge intersection */ *facecoord, /* 4 * 2 * sx * sy coordinates of degenerate point on face */ *facenorm, /* 4 * 3 * sx * sy normal at degeneracy */ *faceicoord, /* 4 * 3 * sx * sy index coordinates of edge intersection */ *gradcontext, /* 3 * 2 * sx * sy spat. context of gradients */ *hesscontext, /* 9 * 2 * sx * sy spat. context of hessians */ *tcontext, /* 9 * 2 * sx * sy spat. context of T tensors */ *stngcontext; /* sx * sy lookahead for strength values */ signed char *flip, /* 2 * 5 * sx * sy record of how eigenvector(s) of interest flip along the 5 voxel edges that are unique to each voxel. Fastest axis is the two eigensystem indices that are tracked, in the case of crease lines */ *pairs, /* 4 * 12 * sx * sy connectivity on faces */ *treated; /* sx * sy mask; when strength is used, tells * us if edges need to be treated (and if they * were treated already) */ double *stng; /* 2 * sx * sy array of strength */ Nrrd *nvidx, *nsclv, /* nrrd wrappers around arrays above */ *ngrad, *neval, *nevec, *nflip, *nstng, *nhess, *nt, *nfacevidx, *nedgealpha, *nedgenorm, *nfacecoord, *nfacenorm, *npairs, *nedgeicoord, *nfaceicoord, *ngradcontext, *nhesscontext, *ntcontext, *nstngcontext, *ntreated; /* ------ output ----- */ unsigned int voxNum, vertNum, faceNum; /* number of voxels contributing to latest isosurface, and number of vertices and faces in that isosurface */ double strengthSeenMax; /* in case strength was used, the maximum vertex strength seen (from probing slabs) */ double time; /* time for extraction */ } seekContext; /* enumsSeek.c */ SEEK_EXPORT const char *seekBiffKey; SEEK_EXPORT const airEnum *const seekType; /* tables.c */ SEEK_EXPORT const int seekContour3DTopoHackEdge[256]; SEEK_EXPORT const int seekContour3DTopoHackTriangle[256][16]; /* methodsSeek.c */ SEEK_EXPORT const int seekPresent; SEEK_EXPORT seekContext *seekContextNew(void); SEEK_EXPORT seekContext *seekContextNix(seekContext *sctx); /* setSeek.c */ SEEK_EXPORT void seekVerboseSet(seekContext *sctx, int verbose); SEEK_EXPORT int seekDataSet(seekContext *sctx, const Nrrd *ninscl, gageContext *gctx, unsigned int pvlIdx); SEEK_EXPORT int seekNormalsFindSet(seekContext *sctx, int doit); SEEK_EXPORT int seekStrengthUseSet(seekContext *sctx, int doit); SEEK_EXPORT int seekStrengthSet(seekContext *sctx, int strengthSign, double strength); SEEK_EXPORT int seekSamplesSet(seekContext *sctx, size_t samples[3]); SEEK_EXPORT int seekTypeSet(seekContext *sctx, int type); SEEK_EXPORT int seekLowerInsideSet(seekContext *sctx, int lowerInside); SEEK_EXPORT int seekItemScalarSet(seekContext *sctx, int item); SEEK_EXPORT int seekItemStrengthSet(seekContext *sctx, int item); SEEK_EXPORT int seekItemNormalSet(seekContext *sctx, int item); SEEK_EXPORT int seekItemGradientSet(seekContext *sctx, int item); SEEK_EXPORT int seekItemEigensystemSet(seekContext *sctx, int evalItem, int evecItem); SEEK_EXPORT int seekItemHessSet(seekContext *sctx, int item); SEEK_EXPORT int seekIsovalueSet(seekContext *sctx, double isovalue); SEEK_EXPORT int seekEvalDiffThreshSet(seekContext *sctx, double evalDiffThresh); /* updateSeek */ SEEK_EXPORT int seekUpdate(seekContext *sctx); /* extract.c */ SEEK_EXPORT int seekExtract(seekContext *sctx, limnPolyData *lpld); /* textract.c */ SEEK_EXPORT int seekVertexStrength(Nrrd *nval, seekContext *sctx, limnPolyData *pld); /* descend.c */ SEEK_EXPORT int seekDescendToDeg(double *coord, double *botleft, double *botright, double *topleft, double *topright, int maxiter, double eps, char type); SEEK_EXPORT int seekDescendToDegCell(double *coord, double *Hbfl, double *Hbfr, double *Hbbl, double *Hbbr, double *Htfl, double *Htfr, double *Htbl, double *Htbr, int maxiter, double eps, char type); SEEK_EXPORT int seekDescendToRidge(double *coord, double *Hbfl, double *gbfl, double *Hbfr, double *gbfr, double *Hbbl, double *gbbl, double *Hbbr, double *gbbr, double *Htfl, double *gtfl, double *Htfr, double *gtfr, double *Htbl, double *gtbl, double *Htbr, double *gtbr, int maxiter, double eps, char ridge, const double evalDiffThresh); #ifdef __cplusplus } #endif #endif /* SEEK_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/seek/updateSeek.c0000664000175000017500000007353712042367142017601 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2009 Thomas Schultz Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "seek.h" #include "privateSeek.h" static int updateNinEtAl(seekContext *sctx) { static const char me[]="updateNinEtAl"; if (sctx->verbose > 5) { fprintf(stderr, "%s: --------------------\n", me); fprintf(stderr, "%s: flagData = %d\n", me, sctx->flag[flagData]); } if (!( sctx->ninscl || sctx->pvl )) { biffAddf(SEEK, "%s: data never set", me); return 1; } if (sctx->flag[flagData]) { if (sctx->ninscl) { sctx->nin = sctx->ninscl; sctx->baseDim = 0; if (gageShapeSet(sctx->_shape, sctx->ninscl, 0)) { biffMovef(SEEK, GAGE, "%s: trouble with scalar volume", me); return 1; } sctx->shape = sctx->_shape; } else { sctx->nin = sctx->pvl->nin; sctx->baseDim = sctx->pvl->kind->baseDim; sctx->shape = sctx->gctx->shape; } sctx->flag[flagData] = AIR_FALSE; sctx->flag[flagNinEtAl] = AIR_TRUE; } return 0; } static int updateAnswerPointers(seekContext *sctx) { static const char me[]="updateAnswerPointers"; if (sctx->verbose > 5) { fprintf(stderr, "%s: --------------------\n", me); fprintf(stderr, "%s: flagItemValue = %d\n", me, sctx->flag[flagItemValue]); fprintf(stderr, "%s: flagItemStrength = %d\n", me, sctx->flag[flagItemStrength]); fprintf(stderr, "%s: flagItemNormal = %d\n", me, sctx->flag[flagItemNormal]); fprintf(stderr, "%s: flagItemGradient = %d\n", me, sctx->flag[flagItemGradient]); fprintf(stderr, "%s: flagItemEigensystem = %d\n", me, sctx->flag[flagItemEigensystem]); fprintf(stderr, "%s: flagItemHess = %d\n", me, sctx->flag[flagItemHess]); fprintf(stderr, "%s: flagNinEtAl = %d\n", me, sctx->flag[flagNinEtAl]); fprintf(stderr, "%s: flagNormalsFind = %d\n", me, sctx->flag[flagNormalsFind]); fprintf(stderr, "%s: flagStrengthUse = %d\n", me, sctx->flag[flagStrengthUse]); fprintf(stderr, "%s: flagType = %d\n", me, sctx->flag[flagType]); fprintf(stderr, "%s: flagData = %d\n", me, sctx->flag[flagData]); } if (seekTypeUnknown == sctx->type) { biffAddf(SEEK, "%s: feature type never set", me); return 1; } if (sctx->flag[flagItemValue] || sctx->flag[flagItemStrength] || sctx->flag[flagItemNormal] || sctx->flag[flagItemGradient] || sctx->flag[flagItemEigensystem] || sctx->flag[flagItemHess] || sctx->flag[flagNinEtAl] || sctx->flag[flagNormalsFind] || sctx->flag[flagStrengthUse] || sctx->flag[flagType]) { /* this is apt regardless of feature type */ if (sctx->strengthUse) { if (-1 == sctx->stngItem) { biffAddf(SEEK, "%s: need to set strength item to use strength", me); return 1; } sctx->stngAns = (gageAnswerPointer(sctx->gctx, sctx->pvl, sctx->stngItem)); } else { sctx->stngAns = NULL; } switch (sctx->type) { case seekTypeIsocontour: if (!( sctx->ninscl || -1 != sctx->sclvItem )) { biffAddf(SEEK, "%s: need either scalar volume or value item set for %s", me, airEnumStr(seekType, seekTypeIsocontour)); return 1; } if (sctx->normalsFind) { /* NOTE simplifying assumption described in seek.h */ if (!( sctx->ninscl || -1 != sctx->normItem )) { biffAddf(SEEK, "%s: need either scalar volume or " "normal item set for normals for %s", me, airEnumStr(seekType, seekTypeIsocontour)); return 1; } } if (sctx->ninscl) { sctx->sclvAns = NULL; sctx->normAns = NULL; } else { sctx->sclvAns = gageAnswerPointer(sctx->gctx, sctx->pvl, sctx->sclvItem); sctx->normAns = (sctx->normalsFind ? gageAnswerPointer(sctx->gctx, sctx->pvl, sctx->normItem) : NULL); } if (sctx->flag[flagItemGradient] || sctx->flag[flagItemEigensystem] || sctx->flag[flagItemHess]) { biffAddf(SEEK, "%s: can't set gradient, Hessian, or eigensystem for %s", me, airEnumStr(seekType, seekTypeIsocontour)); return 1; } sctx->gradAns = NULL; sctx->evalAns = NULL; sctx->evecAns = NULL; sctx->hessAns = NULL; break; case seekTypeRidgeSurface: case seekTypeValleySurface: case seekTypeMaximalSurface: case seekTypeMinimalSurface: case seekTypeRidgeSurfaceOP: case seekTypeRidgeSurfaceT: case seekTypeValleySurfaceOP: case seekTypeValleySurfaceT: if ( !sctx->pvl ) { biffAddf(SEEK, "%s: can't find %s without a gage context", me, airEnumStr(seekType, sctx->type)); return 1; } if (!( -1 != sctx->gradItem && -1 != sctx->evalItem && -1 != sctx->evecItem )) { biffAddf(SEEK, "%s: grad, eval, evec items not all set", me); return 1; } if ((sctx->type==seekTypeRidgeSurfaceT || sctx->type==seekTypeValleySurfaceT) && -1 == sctx->hessItem) { biffAddf(SEEK, "%s: hess item not set", me); return 1; } if (sctx->normalsFind) { /* NOTE simplifying assumption described in seek.h */ if (-1 == sctx->normItem) { biffAddf(SEEK, "%s: need normal item set for normals for %s", me, airEnumStr(seekType, sctx->type)); return 1; } sctx->normAns = (gageAnswerPointer(sctx->gctx, sctx->pvl, sctx->normItem)); } else { sctx->normAns = NULL; } sctx->sclvAns = NULL; sctx->gradAns = gageAnswerPointer(sctx->gctx, sctx->pvl, sctx->gradItem); sctx->evalAns = gageAnswerPointer(sctx->gctx, sctx->pvl, sctx->evalItem); sctx->evecAns = gageAnswerPointer(sctx->gctx, sctx->pvl, sctx->evecItem); if (sctx->type==seekTypeRidgeSurfaceT || sctx->type==seekTypeValleySurfaceT) sctx->hessAns = gageAnswerPointer(sctx->gctx, sctx->pvl, sctx->hessItem); else sctx->hessAns = NULL; break; default: biffAddf(SEEK, "%s: sorry, %s extraction not implemented", me, airEnumStr(seekType, sctx->type)); return 1; } sctx->flag[flagItemValue] = AIR_FALSE; sctx->flag[flagItemStrength] = AIR_FALSE; sctx->flag[flagItemNormal] = AIR_FALSE; sctx->flag[flagItemGradient] = AIR_FALSE; sctx->flag[flagItemEigensystem] = AIR_FALSE; sctx->flag[flagItemHess] = AIR_FALSE; sctx->flag[flagNormalsFind] = AIR_FALSE; sctx->flag[flagAnswerPointers] = AIR_TRUE; } return 0; } static int updateSxSySz(seekContext *sctx) { static const char me[]="updateSxSySz"; size_t sizeIn[3], sizeOut[3]; double min, max, scl[3], off[3]; unsigned int axi; if (sctx->verbose > 5) { fprintf(stderr, "%s: --------------------\n", me); fprintf(stderr, "%s: flagSamples = %d\n", me, sctx->flag[flagSamples]); fprintf(stderr, "%s: flagNinEtAl = %d\n", me, sctx->flag[flagNinEtAl]); } sizeIn[0] = sctx->nin->axis[sctx->baseDim+0].size; sizeIn[1] = sctx->nin->axis[sctx->baseDim+1].size; sizeIn[2] = sctx->nin->axis[sctx->baseDim+2].size; if (sctx->flag[flagSamples] || sctx->flag[flagNinEtAl]) { if (0 == sctx->samples[0] || 0 == sctx->samples[1] || 0 == sctx->samples[2]) { ELL_3V_COPY(sizeOut, sizeIn); } else { if (!sctx->pvl) { biffAddf(SEEK, "%s: can't specify # samples (%u,%u,%u) independent of " "volume dimensions (%u,%u,%u) without a gage context", me, AIR_CAST(unsigned int, sctx->samples[0]), AIR_CAST(unsigned int, sctx->samples[1]), AIR_CAST(unsigned int, sctx->samples[2]), AIR_CAST(unsigned int, sizeIn[0]), AIR_CAST(unsigned int, sizeIn[1]), AIR_CAST(unsigned int, sizeIn[2])); return 1; } ELL_3V_COPY(sizeOut, sctx->samples); } /* want to make absolutely sure txfIdx was being set ... if (sctx->sx != sizeOut[0] || sctx->sy != sizeOut[1] || sctx->sz != sizeOut[2]) { */ sctx->sx = sizeOut[0]; sctx->sy = sizeOut[1]; sctx->sz = sizeOut[2]; /* there has got to be a better way of doing this... */ /* perhaps refer to the way origin is calculated in new nrrd resampler? */ for (axi=0; axi<3; axi++) { min = (nrrdCenterCell == sctx->shape->center ? -0.5 : 0.0); max = (nrrdCenterCell == sctx->shape->center ? sizeIn[axi] - 0.5 : sizeIn[axi] - 1.0); off[axi] = NRRD_POS(sctx->shape->center, min, max, sizeOut[axi], 0); scl[axi] = (NRRD_POS(sctx->shape->center, min, max, sizeOut[axi], 1) - off[axi]); } ELL_4V_SET(sctx->txfIdx + 0*4, scl[0], 0.0, 0.0, off[0]); ELL_4V_SET(sctx->txfIdx + 1*4, 0.0, scl[1], 0.0, off[1]); ELL_4V_SET(sctx->txfIdx + 2*4, 0.0, 0.0, scl[2], off[2]); ELL_4V_SET(sctx->txfIdx + 3*4, 0.0, 0.0, 0.0, 1.0); sctx->flag[flagSxSySz] = AIR_TRUE; sctx->flag[flagSamples] = AIR_FALSE; } return 0; } static int updateReverse(seekContext *sctx) { static const char me[]="updateReverse"; if (sctx->verbose > 5) { fprintf(stderr, "%s: --------------------\n", me); fprintf(stderr, "%s: flagNinEtAl = %d\n", me, sctx->flag[flagNinEtAl]); fprintf(stderr, "%s: flagLowerInside = %d\n", me, sctx->flag[flagLowerInside]); } if (sctx->flag[flagNinEtAl] || sctx->flag[flagLowerInside]) { double mat[9]; int reverse; ELL_34M_EXTRACT(mat, sctx->shape->ItoW); reverse = (!!sctx->lowerInside) ^ (ELL_3M_DET(mat) < 0); if (sctx->reverse != reverse) { sctx->reverse = reverse; sctx->flag[flagReverse] = AIR_TRUE; } } return 0; } static int updateTxfNormal(seekContext *sctx) { static const char me[]="updateTxfNormal"; if (sctx->verbose > 5) { fprintf(stderr, "%s: --------------------\n", me); fprintf(stderr, "%s: flagNinEtAl = %d\n", me, sctx->flag[flagNinEtAl]); fprintf(stderr, "%s: flagLowerInside = %d\n", me, sctx->flag[flagLowerInside]); } if (sctx->flag[flagNinEtAl] || sctx->flag[flagLowerInside]) { double matA[9], matB[9]; ELL_34M_EXTRACT(matA, sctx->shape->ItoW); ell_3m_inv_d(matB, matA); ELL_3M_TRANSPOSE(sctx->txfNormal, matB); if (!sctx->lowerInside) { ELL_3M_SCALE(sctx->txfNormal, -1, sctx->txfNormal); } sctx->flag[flagTxfNormal] = AIR_TRUE; } sctx->flag[flagLowerInside] = AIR_FALSE; return 0; } static int updateSlabCacheAlloc(seekContext *sctx) { static const char me[]="updateSlabCacheAlloc"; int E; if (sctx->verbose > 5) { fprintf(stderr, "%s: --------------------\n", me); fprintf(stderr, "%s: flagType = %d (type = %s)\n", me, sctx->flag[flagType], airEnumStr(seekType, sctx->type)); fprintf(stderr, "%s: flagStrengthUse = %d\n", me, sctx->flag[flagStrengthUse]); fprintf(stderr, "%s: flagSxSySz = %d\n", me, sctx->flag[flagSxSySz]); } E = 0; if (sctx->flag[flagType] || sctx->flag[flagStrengthUse] /* kind of sloppy/overkill */ || sctx->flag[flagSxSySz]) { if (!E) E |= nrrdMaybeAlloc_va(sctx->nvidx, nrrdTypeInt, 3, AIR_CAST(size_t, 15), sctx->sx, sctx->sy); if (!E) sctx->vidx = AIR_CAST(int*, sctx->nvidx->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->ntreated, nrrdTypeChar, 2, sctx->sx, sctx->sy); if (!E) sctx->treated = AIR_CAST(signed char*, sctx->ntreated->data); if (sctx->strengthUse) { if (!E) E |= nrrdMaybeAlloc_va(sctx->nstng, nrrdTypeDouble, 3, AIR_CAST(size_t, 2), sctx->sx, sctx->sy); if (!E) sctx->stng = AIR_CAST(double*, sctx->nstng->data); } if (seekTypeIsocontour == sctx->type) { if (!E) E |= nrrdMaybeAlloc_va(sctx->nsclv, nrrdTypeDouble, 3, AIR_CAST(size_t, 4), sctx->sx + 2, sctx->sy + 2); if (!E) sctx->sclv = AIR_CAST(double*, sctx->nsclv->data); } if (seekTypeRidgeSurface == sctx->type || seekTypeValleySurface == sctx->type || seekTypeMaximalSurface == sctx->type || seekTypeMinimalSurface == sctx->type || seekTypeRidgeSurfaceOP == sctx->type || seekTypeRidgeSurfaceT == sctx->type || seekTypeValleySurfaceOP == sctx->type || seekTypeValleySurfaceT == sctx->type) { if (!E) E |= nrrdMaybeAlloc_va(sctx->ngrad, nrrdTypeDouble, 4, AIR_CAST(size_t, 3), AIR_CAST(size_t, 2), sctx->sx, sctx->sy); if (!E) sctx->grad = AIR_CAST(double*, sctx->ngrad->data); } else { sctx->grad = NULL; } if (seekTypeRidgeSurface == sctx->type || seekTypeValleySurface == sctx->type || seekTypeMaximalSurface == sctx->type || seekTypeMinimalSurface == sctx->type || seekTypeRidgeSurfaceOP == sctx->type || seekTypeValleySurfaceOP == sctx->type) { if (!E) E |= nrrdMaybeAlloc_va(sctx->neval, nrrdTypeDouble, 4, AIR_CAST(size_t, 3), AIR_CAST(size_t, 2), sctx->sx, sctx->sy); if (!E) sctx->eval = AIR_CAST(double*, sctx->neval->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nevec, nrrdTypeDouble, 4, AIR_CAST(size_t, 9), AIR_CAST(size_t, 2), sctx->sx, sctx->sy); if (!E) sctx->evec = AIR_CAST(double*, sctx->nevec->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nflip, nrrdTypeChar, 3, AIR_CAST(size_t, 5), sctx->sx, sctx->sy); if (!E) sctx->flip = AIR_CAST(signed char*, sctx->nflip->data); } else { sctx->eval = NULL; sctx->evec = NULL; sctx->flip = NULL; } if (seekTypeRidgeSurfaceT == sctx->type || seekTypeValleySurfaceT == sctx->type) { if (!E) E |= nrrdMaybeAlloc_va(sctx->nfacevidx, nrrdTypeInt, 3, AIR_CAST(size_t, 4), sctx->sx, sctx->sy); if (!E) sctx->facevidx = AIR_CAST(int*, sctx->nfacevidx->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nhess, nrrdTypeDouble, 4, AIR_CAST(size_t, 9), AIR_CAST(size_t, 2), sctx->sx, sctx->sy); if (!E) sctx->hess = AIR_CAST(double*, sctx->nhess->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nt, nrrdTypeDouble, 4, AIR_CAST(size_t, 9), AIR_CAST(size_t, 2), sctx->sx, sctx->sy); if (!E) sctx->t = AIR_CAST(double*, sctx->nt->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nedgealpha, nrrdTypeDouble, 4, AIR_CAST(size_t, 5), AIR_CAST(size_t, 3), sctx->sx, sctx->sy); if (!E) sctx->edgealpha = AIR_CAST(double*, sctx->nedgealpha->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nedgenorm, nrrdTypeDouble, 4, AIR_CAST(size_t, 5), AIR_CAST(size_t, 9), sctx->sx, sctx->sy); if (!E) sctx->edgenorm = AIR_CAST(double*, sctx->nedgenorm->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nedgeicoord, nrrdTypeDouble, 4, AIR_CAST(size_t, 5), AIR_CAST(size_t, 9), sctx->sx, sctx->sy); if (!E) sctx->edgeicoord = AIR_CAST(double*, sctx->nedgeicoord->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nfacecoord, nrrdTypeDouble, 4, AIR_CAST(size_t, 4), AIR_CAST(size_t, 2), sctx->sx, sctx->sy); if (!E) sctx->facecoord = AIR_CAST(double*, sctx->nfacecoord->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nfacenorm, nrrdTypeDouble, 4, AIR_CAST(size_t, 4), AIR_CAST(size_t, 3), sctx->sx, sctx->sy); if (!E) sctx->facenorm = AIR_CAST(double*, sctx->nfacenorm->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nfaceicoord, nrrdTypeDouble, 4, AIR_CAST(size_t, 4), AIR_CAST(size_t, 3), sctx->sx, sctx->sy); if (!E) sctx->faceicoord = AIR_CAST(double*, sctx->nfaceicoord->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->npairs, nrrdTypeChar, 4, AIR_CAST(size_t, 4), AIR_CAST(size_t, 12), sctx->sx, sctx->sy); if (!E) sctx->pairs = AIR_CAST(signed char*, sctx->npairs->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->ngradcontext, nrrdTypeDouble, 4, AIR_CAST(size_t, 3), AIR_CAST(size_t, 2), sctx->sx, sctx->sy); if (!E) sctx->gradcontext = AIR_CAST(double*, sctx->ngradcontext->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nhesscontext, nrrdTypeDouble, 4, AIR_CAST(size_t, 9), AIR_CAST(size_t, 2), sctx->sx, sctx->sy); if (!E) sctx->hesscontext = AIR_CAST(double*, sctx->nhesscontext->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->ntcontext, nrrdTypeDouble, 4, AIR_CAST(size_t, 9), AIR_CAST(size_t, 2), sctx->sx, sctx->sy); if (!E) sctx->tcontext = AIR_CAST(double*, sctx->ntcontext->data); if (!E) E |= nrrdMaybeAlloc_va(sctx->nstngcontext, nrrdTypeDouble, 2, sctx->sx, sctx->sy); if (!E) sctx->stngcontext = AIR_CAST(double*, sctx->nstngcontext->data); } else { sctx->facevidx = NULL; sctx->hess = NULL; sctx->t = NULL; sctx->edgealpha = NULL; sctx->edgenorm = NULL; sctx->facecoord = NULL; sctx->facenorm = NULL; sctx->pairs = NULL; sctx->gradcontext = NULL; sctx->hesscontext = NULL; sctx->tcontext = NULL; sctx->stngcontext = NULL; } sctx->flag[flagSlabCacheAlloc] = AIR_TRUE; } if (E) { biffMovef(SEEK, NRRD, "%s: couldn't allocate all slab caches", me); return 1; } sctx->flag[flagStrengthUse] = AIR_FALSE; sctx->flag[flagSxSySz] = AIR_FALSE; return 0; } static int updateSclDerived(seekContext *sctx) { static const char me[]="updateSclDerived"; char doneStr[AIR_STRLEN_SMALL]; double *scl, idxIn[4], idxOut[4], val; unsigned int xi, yi, zi; if (sctx->verbose > 5) { fprintf(stderr, "%s: --------------------\n", me); fprintf(stderr, "%s: flagType = %d\n", me, sctx->flag[flagType]); fprintf(stderr, "%s: flagNinEtAl = %d\n", me, sctx->flag[flagNinEtAl]); } if (sctx->flag[flagType] || sctx->flag[flagNinEtAl]) { if (!( seekTypeIsocontour == sctx->type && sctx->pvl )) { nrrdEmpty(sctx->nsclDerived); } else { if (nrrdMaybeAlloc_va(sctx->nsclDerived, nrrdTypeDouble, 3, sctx->sx, sctx->sy, sctx->sz)) { biffMovef(SEEK, NRRD, "%s: couldn't allocated derived scalar volume", me); return 1; } scl = AIR_CAST(double*, sctx->nsclDerived->data); if (sctx->verbose) { fprintf(stderr, "%s: pre-computing scalar volume ... ", me); } for (zi=0; zisz; zi++) { if (sctx->verbose) { fprintf(stderr, "%s", airDoneStr(0, zi, sctx->sz-1, doneStr)); fflush(stderr); } for (yi=0; yisy; yi++) { for (xi=0; xisx; xi++) { ELL_4V_SET(idxOut, xi, yi, zi, 1.0); ELL_4MV_MUL(idxIn, sctx->txfIdx, idxOut); ELL_34V_HOMOG(idxIn, idxIn); gageProbe(sctx->gctx, idxIn[0], idxIn[1], idxIn[2]); val = sctx->sclvAns[0]; if (!AIR_EXISTS(val)) { biffAddf(SEEK, "%s: probed scalar[%u,%u,%u] %g doesn't exist", me, xi, yi, zi, val); return 1; } scl[xi + sctx->sx*(yi + sctx->sy*zi)] = val; } } } if (sctx->verbose) { fprintf(stderr, "%s\n", airDoneStr(0, zi, sctx->sz-1, doneStr)); } } sctx->flag[flagSclDerived] = AIR_TRUE; } return 0; } static int updateSpanSpaceHist(seekContext *sctx) { static const char me[]="updateSpanSpaceHist"; unsigned int sx, sy, sz, ss, xi, yi, zi, vi, si, minI, maxI, *spanHist; double min, max, val; const void *data; double (*lup)(const void *, size_t); if (sctx->verbose > 5) { fprintf(stderr, "%s: --------------------\n", me); fprintf(stderr, "%s: flagType = %d\n", me, sctx->flag[flagType]); fprintf(stderr, "%s: flagSclDerived = %d\n", me, sctx->flag[flagSclDerived]); fprintf(stderr, "%s: flagNinEtAl = %d\n", me, sctx->flag[flagNinEtAl]); } if (sctx->flag[flagType] || sctx->flag[flagSclDerived] || sctx->flag[flagNinEtAl]) { if (seekTypeIsocontour != sctx->type) { nrrdEmpty(sctx->nspanHist); sctx->range->min = AIR_NAN; sctx->range->max = AIR_NAN; } else { nrrdRangeSet(sctx->range, (sctx->ninscl ? sctx->ninscl : sctx->nsclDerived), nrrdBlind8BitRangeFalse); if (sctx->range->hasNonExist) { biffAddf(SEEK, "%s: scalar volume has non-existent values", me); return 1; } sctx->nspanHist->axis[0].min = sctx->range->min; sctx->nspanHist->axis[1].min = sctx->range->min; sctx->nspanHist->axis[0].max = sctx->range->max; sctx->nspanHist->axis[1].max = sctx->range->max; if (sctx->ninscl) { lup = nrrdDLookup[sctx->ninscl->type]; data = sctx->ninscl->data; } else { lup = nrrdDLookup[sctx->nsclDerived->type]; data = sctx->nsclDerived->data; } /* calculate the span space histogram */ if (nrrdMaybeAlloc_va(sctx->nspanHist, nrrdTypeUInt, 2, AIR_CAST(size_t, sctx->spanSize), AIR_CAST(size_t, sctx->spanSize))) { biffMovef(SEEK, NRRD, "%s: couldn't allocate space space histogram", me); return 1; } spanHist = AIR_CAST(unsigned int*, sctx->nspanHist->data); sx = sctx->sx; sy = sctx->sy; sz = sctx->sz; ss = sctx->spanSize; for (si=0; sirange->min, min, sctx->range->max, ss); maxI = airIndex(sctx->range->min, max, sctx->range->max, ss); spanHist[minI + ss*maxI]++; } } } } sctx->flag[flagSclDerived] = AIR_FALSE; sctx->flag[flagSpanSpaceHist] = AIR_TRUE; } return 0; } static int updateResult(seekContext *sctx) { static const char me[]="updateResult"; if (sctx->verbose > 5) { fprintf(stderr, "%s: --------------------\n", me); fprintf(stderr, "%s: flagIsovalue = %d\n", me, sctx->flag[flagIsovalue]); fprintf(stderr, "%s: flagAnswerPointers = %d\n", me, sctx->flag[flagAnswerPointers]); fprintf(stderr, "%s: flagType = %d\n", me, sctx->flag[flagType]); fprintf(stderr, "%s: flagSlabCacheAlloc = %d\n", me, sctx->flag[flagSlabCacheAlloc]); fprintf(stderr, "%s: flagSpanSpaceHist = %d\n", me, sctx->flag[flagSpanSpaceHist]); fprintf(stderr, "%s: flagNinEtAl = %d\n", me, sctx->flag[flagNinEtAl]); fprintf(stderr, "%s: flagReverse = %d\n", me, sctx->flag[flagReverse]); fprintf(stderr, "%s: flagTxfNormal = %d\n", me, sctx->flag[flagTxfNormal]); } if (seekTypeIsocontour != sctx->type && sctx->flag[flagIsovalue]) { biffAddf(SEEK, "%s: can't set isovalue for %s (only %s)", me, airEnumStr(seekType, sctx->type), airEnumStr(seekType, seekTypeIsocontour)); return 1; } if (sctx->strengthUse && !sctx->stngAns) { biffAddf(SEEK, "%s: can't use feature strength without a strength item", me); return 1; } /* this seems to be a very pointless exercise */ if (sctx->flag[flagIsovalue] || sctx->flag[flagEvalDiffThresh] || sctx->flag[flagAnswerPointers] || sctx->flag[flagStrengthUse] || sctx->flag[flagStrength] || sctx->flag[flagType] || sctx->flag[flagSlabCacheAlloc] || sctx->flag[flagSpanSpaceHist] || sctx->flag[flagNinEtAl] || sctx->flag[flagReverse] || sctx->flag[flagTxfNormal]) { sctx->flag[flagResult] = AIR_TRUE; sctx->flag[flagIsovalue] = AIR_FALSE; sctx->flag[flagEvalDiffThresh] = AIR_FALSE; sctx->flag[flagAnswerPointers] = AIR_FALSE; sctx->flag[flagStrengthUse] = AIR_FALSE; sctx->flag[flagStrength] = AIR_FALSE; sctx->flag[flagType] = AIR_FALSE; sctx->flag[flagSlabCacheAlloc] = AIR_FALSE; sctx->flag[flagSpanSpaceHist] = AIR_FALSE; sctx->flag[flagNinEtAl] = AIR_FALSE; sctx->flag[flagReverse] = AIR_FALSE; sctx->flag[flagTxfNormal] = AIR_FALSE; } return 0; } /* ******** seekUpdate ** ** traverses dependency graph for all of seek stuff, which necessarily ** includes nodes specific to, say, isosurfacing, even when isosurfacing ** is not being done. The idea is to use the same graph for all feature ** types, for error checking if nothing else, leavings steps as no-ops ** as needed. */ int seekUpdate(seekContext *sctx) { static const char me[]="seekUpdate"; int E; if (!sctx) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } E = 0; if (!E) E |= updateNinEtAl(sctx); if (!E) E |= updateAnswerPointers(sctx); if (!E) E |= updateSxSySz(sctx); if (!E) E |= updateReverse(sctx); if (!E) E |= updateTxfNormal(sctx); if (!E) E |= updateSlabCacheAlloc(sctx); if (!E) E |= updateSclDerived(sctx); if (!E) E |= updateSpanSpaceHist(sctx); if (!E) E |= updateResult(sctx); if (E) { biffAddf(SEEK, "%s: trouble updating", me); return 1; } return 0; } teem-1.11.0~svn6057/src/seek/test/0000775000175000017500000000000012203513756016306 5ustar domibeldomibelteem-1.11.0~svn6057/src/seek/test/trv.c0000664000175000017500000001577412042367142017300 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../seek.h" char *info = ("test crease surface extraction."); int probeParseKind(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[] = "probeParseKind"; gageKind **kindP; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } kindP = (gageKind **)ptr; airToLower(str); if (!strcmp(gageKindScl->name, str)) { *kindP = gageKindScl; } else if (!strcmp(gageKindVec->name, str)) { *kindP = gageKindVec; } else { sprintf(err, "%s: not \"%s\" or \"%s\"", me, gageKindScl->name, gageKindVec->name); return 1; } return 0; } void * probeParseKindDestroy(void *ptr) { gageKind *kind; if (ptr) { kind = AIR_CAST(gageKind *, ptr); } return NULL; } hestCB probeKindHestCB = { sizeof(gageKind *), "kind", probeParseKind, probeParseKindDestroy }; int main(int argc, const char *argv[]) { const char *me; char *err, *outS; hestOpt *hopt=NULL; airArray *mop; limnPolyData *pld, *pldSub; gageContext *gctx=NULL; gagePerVolume *pvl; Nrrd *nin, *nmeas; double kparm[3], strength, scaling[3]; seekContext *sctx; FILE *file; unsigned int ncc; size_t samples[3]; gageKind *kind; char *itemGradS; /* , *itemEvalS[2], *itemEvecS[2]; */ int itemGrad; /* , itemEval[2], itemEvec[2]; */ int E; me = argv[0]; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume to analyze", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL, "\"kind\" of volume (\"scalar\", \"vector\", \"tensor\")", NULL, NULL, &probeKindHestCB); hestOptAdd(&hopt, "s", "strength", airTypeDouble, 1, 1, &strength, "0.01", "strength"); hestOptAdd(&hopt, "gi", "grad item", airTypeString, 1, 1, &itemGradS, NULL, "item for gradient vector"); hestOptAdd(&hopt, "c", "scaling", airTypeDouble, 3, 3, scaling, "1 1 1", "amount by which to up/down-sample on each spatial axis"); hestOptAdd(&hopt, "n", "# CC", airTypeUInt, 1, 1, &ncc, "0", "if non-zero, number of CC to save"); hestOptAdd(&hopt, "o", "output LMPD", airTypeString, 1, 1, &outS, "out.lmpd", "output file to save LMPD into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); itemGrad = airEnumVal(kind->enm, itemGradS); pld = limnPolyDataNew(); airMopAdd(mop, pld, (airMopper)limnPolyDataNix, airMopAlways); pldSub = limnPolyDataNew(); airMopAdd(mop, pldSub, (airMopper)limnPolyDataNix, airMopAlways); file = airFopen(outS, stdout, "w"); airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); sctx = seekContextNew(); airMopAdd(mop, sctx, (airMopper)seekContextNix, airMopAlways); gctx = gageContextNew(); airMopAdd(mop, gctx, (airMopper)gageContextNix, airMopAlways); ELL_3V_SET(kparm, 1, 1.0, 0.0); if (!(pvl = gagePerVolumeNew(gctx, nin, kind)) || gagePerVolumeAttach(gctx, pvl) || gageKernelSet(gctx, gageKernel00, nrrdKernelBCCubic, kparm) || gageKernelSet(gctx, gageKernel11, nrrdKernelBCCubicD, kparm) || gageKernelSet(gctx, gageKernel22, nrrdKernelBCCubicDD, kparm) || gageQueryItemOn(gctx, pvl, itemGrad) || gageQueryItemOn(gctx, pvl, gageSclHessEval) || gageQueryItemOn(gctx, pvl, gageSclHessEval2) || gageQueryItemOn(gctx, pvl, gageSclHessEvec) || gageQueryItemOn(gctx, pvl, gageSclHessEvec2) || gageUpdate(gctx)) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } seekVerboseSet(sctx, 10); E = 0; if (!E) E |= seekDataSet(sctx, NULL, gctx, 0); ELL_3V_SET(samples, scaling[0]*nin->axis[kind->baseDim + 0].size, scaling[1]*nin->axis[kind->baseDim + 1].size, scaling[2]*nin->axis[kind->baseDim + 2].size); if (!E) E |= seekSamplesSet(sctx, samples); if (!E) E |= seekItemGradientSet(sctx, itemGrad); if (!E) E |= seekItemEigensystemSet(sctx, gageSclHessEval, gageSclHessEvec); if (!E) E |= seekItemNormalSet(sctx, gageSclHessEvec2); if (!E) E |= seekStrengthUseSet(sctx, AIR_TRUE); if (!E) E |= seekStrengthSet(sctx, -1, strength); if (!E) E |= seekItemStrengthSet(sctx, gageSclHessEval2); if (!E) E |= seekNormalsFindSet(sctx, AIR_TRUE); if (!E) E |= seekTypeSet(sctx, seekTypeRidgeSurface); if (!E) E |= seekUpdate(sctx); if (!E) E |= seekExtract(sctx, pld); if (E) { airMopAdd(mop, err = biffGetDone(SEEK), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } fprintf(stderr, "%s: extraction time = %g\n", me, sctx->time); nmeas = nrrdNew(); airMopAdd(mop, nmeas, (airMopper)nrrdNuke, airMopAlways); if (limnPolyDataVertexWindingFix(pld, AIR_TRUE) || limnPolyDataVertexWindingFlip(pld) || limnPolyDataVertexNormals(pld) || limnPolyDataCCFind(pld) || limnPolyDataPrimitiveArea(nmeas, pld) || limnPolyDataPrimitiveSort(pld, nmeas)) { err = biffGetDone(LIMN); fprintf(stderr, "%s: trouble sorting:\n%s", me, err); free(err); } if (ncc > 1) { double *meas; unsigned int ccIdx; nrrdSave("meas.nrrd", nmeas, NULL); ncc = AIR_MIN(ncc, nmeas->axis[0].size); meas = AIR_CAST(double *, nmeas->data); for (ccIdx=ncc; ccIdxaxis[0].size; ccIdx++) { meas[ccIdx] = 0.0; } if (!E) E |= limnPolyDataPrimitiveSelect(pldSub, pld, nmeas); if (!E) E |= limnPolyDataWriteLMPD(file, pldSub); } else { if (!E) E |= limnPolyDataWriteLMPD(file, pld); } if (E) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/seek/test/tiso.c0000664000175000017500000001234512042367142017432 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../seek.h" char *info = ("test stupid cubes isosurfaces."); int main(int argc, const char *argv[]) { const char *me; char *err, *outS; hestOpt *hopt=NULL; airArray *mop; limnPolyData *pld; gageContext *gctx=NULL; gagePerVolume *pvl; Nrrd *nin, *nmeas; double isoval, kparm[3]; seekContext *sctx; FILE *file; int usegage, E, hack; size_t samples[3]; me = argv[0]; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume to surface", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "v", "iso", airTypeDouble, 1, 1, &isoval, NULL, "isovalue"); hestOptAdd(&hopt, "g", NULL, airTypeInt, 0, 0, &usegage, NULL, "use gage too"); hestOptAdd(&hopt, "hack", NULL, airTypeInt, 0, 0, &hack, NULL, "hack"); hestOptAdd(&hopt, "o", "output LMPD", airTypeString, 1, 1, &outS, "out.lmpd", "output file to save LMPD into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); pld = limnPolyDataNew(); airMopAdd(mop, pld, (airMopper)limnPolyDataNix, airMopAlways); file = airFopen(outS, stdout, "w"); airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); sctx = seekContextNew(); airMopAdd(mop, sctx, (airMopper)seekContextNix, airMopAlways); if (usegage) { gctx = gageContextNew(); airMopAdd(mop, gctx, (airMopper)gageContextNix, airMopAlways); ELL_3V_SET(kparm, 2, 1.0, 0.0); if (!(pvl = gagePerVolumeNew(gctx, nin, gageKindScl)) || gagePerVolumeAttach(gctx, pvl) || gageKernelSet(gctx, gageKernel00, nrrdKernelBCCubic, kparm) || gageKernelSet(gctx, gageKernel11, nrrdKernelBCCubicD, kparm) || gageKernelSet(gctx, gageKernel22, nrrdKernelBCCubicDD, kparm) || gageQueryItemOn(gctx, pvl, gageSclValue) || gageQueryItemOn(gctx, pvl, gageSclNormal) || (usegage && (gageQueryItemOn(gctx, pvl, gageSclGradVec) || gageQueryItemOn(gctx, pvl, gageSclHessEval) || gageQueryItemOn(gctx, pvl, gageSclHessEvec) || gageQueryItemOn(gctx, pvl, gageSclHessEvec2))) || gageUpdate(gctx)) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } } seekVerboseSet(sctx, 10); E = 0; if (usegage) { if (!E) E |= seekDataSet(sctx, NULL, gctx, 0); if (hack) { ELL_3V_SET(samples, 5, 5, 5); if (!E) E |= seekSamplesSet(sctx, samples); if (!E) E |= seekItemGradientSet(sctx, gageSclGradVec); if (!E) E |= seekItemEigensystemSet(sctx, gageSclHessEval, gageSclHessEvec); if (!E) E |= seekItemNormalSet(sctx, gageSclHessEvec2); } else { if (!E) E |= seekItemScalarSet(sctx, gageSclValue); if (!E) E |= seekItemNormalSet(sctx, gageSclNormal); } } else { if (!E) E |= seekDataSet(sctx, nin, NULL, 0); } if (!E) E |= seekNormalsFindSet(sctx, AIR_TRUE); if (hack) { if (!E) E |= seekTypeSet(sctx, seekTypeRidgeSurface); } else { if (!E) E |= seekTypeSet(sctx, seekTypeIsocontour); if (!E) E |= seekIsovalueSet(sctx, isoval); } if (!E) E |= seekUpdate(sctx); if (!E) E |= seekExtract(sctx, pld); if (E) { airMopAdd(mop, err = biffGetDone(SEEK), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } fprintf(stderr, "%s: extraction time = %g\n", me, sctx->time); nmeas = nrrdNew(); airMopAdd(mop, nmeas, (airMopper)nrrdNuke, airMopAlways); if (limnPolyDataCCFind(pld) || limnPolyDataPrimitiveArea(nmeas, pld) || limnPolyDataPrimitiveSort(pld, nmeas)) { err = biffGetDone(LIMN); fprintf(stderr, "%s: trouble sorting:\n%s", me, err); free(err); } if (limnPolyDataWriteLMPD(file, pld)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/seek/descend.c0000664000175000017500000007700012042367142017101 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2011, 2010, 2009, 2008 Thomas Schultz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* This file collects functions that implement various gradient * descents required in the context of extracting crease surfaces */ #include "seek.h" #include "privateSeek.h" /* Tries to find a degenerate point on a bilinearly interpolated face * of symmetric second-order tensors. Uses the discriminant constraint * functions from Zheng/Parlett/Pang, TVCG 2005, and the * Newton-Raphson method with Armijo stepsize control. * * coord are coordinates relative to the given face and will be * updated in each iteration. * botleft - topright are 9-vectors, representing the symmetric * matrices at the corners of the face (input only) * maxiter is the maximum number of iterations allowed * eps is the accuracy up to which the discriminant should be zero * The discriminant scales with the Frobenius norm to the sixth power, * so the exact constraint is disc/|T|^60) || (type=='p' && det<0)) return 0; else return 4; /* sufficient accuracy, but wrong type */ } } for (iter=0; iter1 || newcoord[1]<0 || newcoord[1]>1) { if (safetyct==maxct) return 1; /* we left the cell */ alpha*=gamma; } ELL_3M_LERP(hessbot,newcoord[0],botleft,botright); ELL_3M_LERP(hesstop,newcoord[0],topleft,topright); ELL_3M_LERP(hess,newcoord[1],hessbot,hesstop); norm = sqrt(hess[0]*hess[0]+hess[4]*hess[4]+hess[8]*hess[8]+ 2*(hess[1]*hess[1]+hess[2]*hess[2]+hess[5]*hess[5])); if (norm<1e-300) return 6; /* copy over */ ten[0] = hess[0]/norm; ten[1] = hess[1]/norm; ten[2] = hess[2]/norm; ten[3] = hess[4]/norm; ten[4] = hess[5]/norm; ten[5] = hess[8]/norm; for (i=0; i<6; i++) tsqr[i] = ten[i]*ten[i]; /* evaluate the constraint function vector */ cf[0] = ten[0]*(tsqr[3]-tsqr[5])+ten[0]*(tsqr[1]-tsqr[2])+ ten[3]*(tsqr[5]-tsqr[0])+ten[3]*(tsqr[4]-tsqr[1])+ ten[5]*(tsqr[0]-tsqr[3])+ten[5]*(tsqr[2]-tsqr[4]); /* fx */ cf[1] = ten[4]*(2*(tsqr[4]-tsqr[0])-(tsqr[2]+tsqr[1])+ 2*(ten[3]*ten[0]+ten[5]*ten[0]-ten[3]*ten[5]))+ ten[1]*ten[2]*(2*ten[0]-ten[5]-ten[3]); /* fy1 */ cf[2] = ten[2]*(2*(tsqr[2]-tsqr[3])-(tsqr[1]+tsqr[4])+ 2*(ten[5]*ten[3]+ten[0]*ten[3]-ten[5]*ten[0]))+ ten[4]*ten[1]*(2*ten[3]-ten[0]-ten[5]); /* fy2 */ cf[3] = ten[1]*(2*(tsqr[1]-tsqr[5])-(tsqr[4]+tsqr[2])+ 2*(ten[0]*ten[5]+ten[3]*ten[5]-ten[0]*ten[3]))+ ten[2]*ten[4]*(2*ten[5]-ten[3]-ten[0]); /* fy3 */ cf[4] = ten[4]*(tsqr[2]-tsqr[1])+ten[1]*ten[2]*(ten[3]-ten[5]); /* fz1 */ cf[5] = ten[2]*(tsqr[1]-tsqr[4])+ten[4]*ten[1]*(ten[5]-ten[0]); /* fz2 */ cf[6] = ten[1]*(tsqr[4]-tsqr[2])+ten[2]*ten[4]*(ten[0]-ten[3]); /* fz3 */ newdiscr = cf[0]*cf[0]+cf[1]*cf[1]+cf[2]*cf[2]+cf[3]*cf[3]+ 15*(cf[4]*cf[4]+cf[5]*cf[5]+cf[6]*cf[6]); if (newdiscr0) || (type=='p' && det<0)) return 0; else return 4; /* sufficient accuracy, but wrong type */ } } if (newdiscr<=discr-0.5*alpha*dxsqr) { accept=1; discr = newdiscr; } else { alpha*=gamma; } } if (!accept) return 5; coord[0] -= alpha*dx[0]; coord[1] -= alpha*dx[1]; } return 2; /* hit maxiter */ } /* Descends to the degenerate line in a trilinearly interpolated cell * using the discriminant constraint functions and the Newton-Raphson * method with Armijo stepsize control. * * This function is NOT part of the crease extraction, but has been * used for debugging it. * * coord are coordinates relative to the given cell and will be * updated in each iteration. * Hbfl - Htbr are 9-vectors, representing the symmetric matrices at the * corners of the face (input only) * maxiter is the maximum number of iterations allowed * eps is the accuracy up to which the discriminant should be zero * The discriminant scales with the Frobenius norm to the sixth power, * so the exact constraint is disc/|T|^61 || newcoord[1]<0 || newcoord[1]>1 || newcoord[2]<0 || newcoord[2]>1) { if (safetyct==maxct) { ELL_3V_COPY(coord,newcoord); /* such that caller knows which dir was the culprit */ return 1; /* we left the cell */ } alpha*=gamma; continue; } ELL_3M_LERP(Hfrontleft, newcoord[2], Hbfl, Htfl); ELL_3M_LERP(Hbackleft, newcoord[2], Hbbl, Htbl); ELL_3M_LERP(Hleft, newcoord[1], Hfrontleft, Hbackleft); ELL_3M_LERP(Hfrontright, newcoord[2], Hbfr, Htfr); ELL_3M_LERP(Hbackright, newcoord[2], Hbbr, Htbr); ELL_3M_LERP(Hright, newcoord[1], Hfrontright, Hbackright); ELL_3M_LERP(H, newcoord[0], Hleft, Hright); norm = sqrt(H[0]*H[0]+H[4]*H[4]+H[8]*H[8]+ 2*(H[1]*H[1]+H[2]*H[2]+H[5]*H[5])); if (norm<1e-300) return 6; ten[0]=H[0]/norm; ten[1]=H[1]/norm; ten[2]=H[2]/norm; ten[3]=H[4]/norm; ten[4]=H[5]/norm; ten[5]=H[7]/norm; for (i=0; i<6; i++) tsqr[i] = ten[i]*ten[i]; /* evaluate the constraint function vector */ cf[0] = ten[0]*(tsqr[3]-tsqr[5])+ten[0]*(tsqr[1]-tsqr[2])+ ten[3]*(tsqr[5]-tsqr[0])+ ten[3]*(tsqr[4]-tsqr[1])+ten[5]*(tsqr[0]-tsqr[3])+ ten[5]*(tsqr[2]-tsqr[4]); /* fx */ cf[1] = ten[4]*(2*(tsqr[4]-tsqr[0])-(tsqr[2]+tsqr[1])+ 2*(ten[3]*ten[0]+ten[5]*ten[0]-ten[3]*ten[5]))+ ten[1]*ten[2]*(2*ten[0]-ten[5]-ten[3]); /* fy1 */ cf[2] = ten[2]*(2*(tsqr[2]-tsqr[3])-(tsqr[1]+tsqr[4])+ 2*(ten[5]*ten[3]+ten[0]*ten[3]-ten[5]*ten[0]))+ ten[4]*ten[1]*(2*ten[3]-ten[0]-ten[5]); /* fy2 */ cf[3] = ten[1]*(2*(tsqr[1]-tsqr[5])-(tsqr[4]+tsqr[2])+ 2*(ten[0]*ten[5]+ten[3]*ten[5]-ten[0]*ten[3]))+ ten[2]*ten[4]*(2*ten[5]-ten[3]-ten[0]); /* fy3 */ cf[4] = ten[4]*(tsqr[2]-tsqr[1])+ten[1]*ten[2]*(ten[3]-ten[5]); /* fz1 */ cf[5] = ten[2]*(tsqr[1]-tsqr[4])+ten[4]*ten[1]*(ten[5]-ten[0]); /* fz2 */ cf[6] = ten[1]*(tsqr[4]-tsqr[2])+ten[2]*ten[4]*(ten[0]-ten[3]); /* fz3 */ newdiscr = cf[0]*cf[0]+cf[1]*cf[1]+cf[2]*cf[2]+cf[3]*cf[3]+ 15*(cf[4]*cf[4]+cf[5]*cf[5]+cf[6]*cf[6]); if (newdiscr0) || (type=='p' && det<0)) return 0; else return 4; /* sufficient accuracy, but wrong type */ } } if (iter==0 || newdiscr<=discr-0.5*alpha*optgradsqr) { accept=1; discr = newdiscr; } else { alpha*=gamma; } } if (!accept) return 5; /* could not find a valid stepsize */ coord[0] -= alpha*optgrad[0]; coord[1] -= alpha*optgrad[1]; coord[2] -= alpha*optgrad[2]; if (iter==maxiter-1) break; /* find derivative of constraint function vector using the chain rule */ cft [0] = tsqr[3]-tsqr[5]+tsqr[1]-tsqr[2]- 2*ten[0]*ten[3]+2*ten[0]*ten[5]; /* fx/T00 */ cft [1] = 2*ten[0]*ten[1]-2*ten[3]*ten[1]; /* fx/T01 */ cft [2] = -2*ten[0]*ten[2]+2*ten[5]*ten[2]; /* fx/T02 */ cft [3] = 2*ten[0]*ten[3]+tsqr[5]-tsqr[0]+tsqr[4]-tsqr[1]- 2*ten[5]*ten[3]; /* fx/T11 */ cft [4] = 2*ten[3]*ten[4]-2*ten[5]*ten[4]; /* fx/T12 */ cft [5] = -2*ten[0]*ten[5]+2*ten[3]*ten[5]+tsqr[0]-tsqr[3]+ tsqr[2]-tsqr[4]; /* fx/T22 */ cft [6] = -4*ten[0]*ten[4]+2*ten[4]*ten[3]+2*ten[4]*ten[5]+ 2*ten[1]*ten[2]; /* fy1/T00 */ cft [7] = -2*ten[4]*ten[1]+ten[2]*(2*ten[0]-ten[5]-ten[3]); /* fy1/T01 */ cft [8] = -2*ten[4]*ten[2]+ten[1]*(2*ten[0]-ten[5]-ten[3]); /* fy1/T02 */ cft [9] = 2*ten[4]*ten[0]-2*ten[4]*ten[5]-ten[1]*ten[2]; /* fy1/T11 */ cft[10] = 6*tsqr[4]-2*tsqr[0]-(tsqr[2]+tsqr[1])+ 2*(ten[3]*ten[0]+ten[5]*ten[0]-ten[3]*ten[5]); /* fy1/T12 */ cft[11] = 2*ten[4]*ten[0]-2*ten[4]*ten[3]-ten[1]*ten[2]; /* fy1/T22 */ cft[12] = 2*ten[2]*ten[3]-2*ten[2]*ten[5]-ten[4]*ten[1]; /* fy2/T00 */ cft[13] = -2*ten[2]*ten[1]+ten[4]*(2*ten[3]-ten[0]-ten[5]); /* fy2/T01 */ cft[14] = 6*tsqr[2]-2*tsqr[3]-(tsqr[1]+tsqr[4])+ 2*(ten[5]*ten[3]+ten[0]*ten[3]-ten[5]*ten[0]); /* fy2/T02 */ cft[15] = -4*ten[2]*ten[3]+2*ten[2]*ten[5]+2*ten[2]*ten[0]+ 2*ten[4]*ten[1]; /* fy2/T11 */ cft[16] = -2*ten[2]*ten[4]+ten[1]*(2*ten[3]-ten[0]-ten[5]); /* fy2/T12 */ cft[17] = 2*ten[2]*ten[3]-2*ten[2]*ten[0]-ten[4]*ten[1]; /* fy2/T22 */ cft[18] = 2*ten[1]*ten[5]-2*ten[1]*ten[3]-ten[2]*ten[4]; /* fy3/T00 */ cft[19] = 6*tsqr[1]-2*tsqr[5]-(tsqr[4]+tsqr[2])+ 2*(ten[0]*ten[5]+ten[3]*ten[5]-ten[0]*ten[3]); /* fy3/T01 */ cft[20] = -2*ten[1]*ten[2]+ten[4]*(2*ten[5]-ten[3]-ten[0]); /* fy3/T02 */ cft[21] = 2*ten[1]*ten[5]-2*ten[1]*ten[0]-ten[2]*ten[4]; /* fy3/T11 */ cft[22] = -2*ten[1]*ten[4]+ten[2]*(2*ten[5]-ten[3]-ten[0]); /* fy3/T12 */ cft[23] = -4*ten[1]*ten[5]+2*ten[0]*ten[1]+2*ten[1]*ten[3]+ 2*ten[2]*ten[4]; /* fy3/T22 */ cft[24] = 0; /* fz1/T00 */ cft[25] = -2*ten[4]*ten[1]+ten[2]*(ten[3]-ten[5]); /* fz1/T01 */ cft[26] = 2*ten[4]*ten[2]+ten[1]*(ten[3]-ten[5]); /* fz1/T02 */ cft[27] = ten[1]*ten[2]; /* fz1/T11 */ cft[28] = tsqr[2]-tsqr[1]; /* fz1/T12 */ cft[29] = -ten[1]*ten[2]; /* fz1/T22 */ cft[30] = -ten[4]*ten[1]; /* fz2/T00 */ cft[31] = 2*ten[2]*ten[1]+ten[4]*(ten[5]-ten[0]); /* fz2/T01 */ cft[32] = tsqr[1]-tsqr[4]; /* fz2/T02 */ cft[33] = 0; /* fz2/T11 */ cft[34] = -2*ten[2]*ten[4]+ten[1]*(ten[5]-ten[0]); /* fz2/T12 */ cft[35] = ten[4]*ten[1]; /* fz2/T22 */ cft[36] = ten[2]*ten[4]; /* fz3/T00 */ cft[37] = tsqr[4]-tsqr[2]; /* fz3/T01 */ cft[38] = -2*ten[1]*ten[2]+ten[4]*(ten[0]-ten[3]); /* fz3/T02 */ cft[39] = -ten[2]*ten[4]; /* fz3/T11 */ cft[40] = 2*ten[1]*ten[4]+ten[2]*(ten[0]-ten[3]); /* fz3/T12 */ cft[41] = 0; /* fz3/T22 */ /* approximate Hessian derivative in x dir */ ELL_3M_SUB(Hder, Hright, Hleft); ELL_3M_SCALE(Hder,1.0/norm,Hder); tx[0] = Hder[0]; /* T00 / x */ tx[3] = Hder[1]; /* T01 / x */ tx[6] = Hder[2]; /* T02 / x */ tx[9] = Hder[4]; /* T11 / x */ tx[12] = Hder[5]; /* T12 / x */ tx[15] = Hder[8]; /* T22 / x */ ELL_3M_LERP(Hfront, coord[0], Hfrontleft, Hfrontright); ELL_3M_LERP(Hback, coord[0], Hbackleft, Hbackright); ELL_3M_SUB(Hder, Hback, Hfront); /* y dir */ ELL_3M_SCALE(Hder,1.0/norm,Hder); tx[1] = Hder[0]; /* T00 / y */ tx[4] = Hder[1]; /* T01 / y */ tx[7] = Hder[2]; /* T02 / y */ tx[10] = Hder[4]; /* T11 / y */ tx[13] = Hder[5]; /* T12 / y */ tx[16] = Hder[8]; /* T22 / y */ /* approximate Hessian derivative in z dir */ ELL_3M_LERP(Htopleft, coord[1], Htfl, Htbl); ELL_3M_LERP(Htopright, coord[1], Htfr, Htbr); ELL_3M_LERP(Hbotleft, coord[1], Hbfl, Hbbl); ELL_3M_LERP(Hbotright, coord[1], Hbfr, Hbbr); ELL_3M_LERP(Htop, coord[0], Htopleft, Htopright); ELL_3M_LERP(Hbot, coord[0], Hbotleft, Hbotright); ELL_3M_SUB(Hder, Htop, Hbot); /* z dir */ ELL_3M_SCALE(Hder,1.0/norm,Hder); tx[2] = Hder[0]; /* T00 / z */ tx[5] = Hder[1]; /* T01 / z */ tx[8] = Hder[2]; /* T02 / z */ tx[11] = Hder[4]; /* T11 / z */ tx[14] = Hder[5]; /* T12 / z */ tx[17] = Hder[8]; /* T22 / z */ /* matrix multiply cft*tx */ for (row=0; row<7; row++) for (col=0; col<3; col++) { i = row*3+col; cfx[i] = 0; for (j=0; j<6; j++) { cfx[i] += cft[row*6+j]*tx[j*3+col]; } } for (row=0; row<3; row++) for (col=0; col<3; col++) { i = row*3+col; denom[i] = 0; for (j=0; j<7; j++) { denom[i] += cfx[j*3+row]*cfx[j*3+col]; } } ell_3m_inv_d(inv, denom); /* multiply transpose(cfx)*cf */ for (j=0; j<7; j++) { nom[0] += cfx[j*3] * cf[j]; nom[1] += cfx[j*3+1]* cf[j]; nom[2] += cfx[j*3+2]* cf[j]; } /* compute new optgrad = inv*nom */ ELL_3MV_MUL(optgrad,inv,nom); } while (iter++1 || newcoord[1]<0 || newcoord[1]>1 || newcoord[2]<0 || newcoord[2]>1) { if (safetyct==maxct) return 1; /* we left the cell */ alpha*=gamma; } ELL_3M_LERP(Hfrontleft, newcoord[2], Hbfl, Htfl); ELL_3M_LERP(Hbackleft, newcoord[2], Hbbl, Htbl); ELL_3M_LERP(Hleft, newcoord[1], Hfrontleft, Hbackleft); ELL_3M_LERP(Hfrontright, newcoord[2], Hbfr, Htfr); ELL_3M_LERP(Hbackright, newcoord[2], Hbbr, Htbr); ELL_3M_LERP(Hright, newcoord[1], Hfrontright, Hbackright); ELL_3M_LERP(H, newcoord[0], Hleft, Hright); ell_3m_eigensolve_d(evals, evecs, H, AIR_TRUE); _seekHess2T(T,evals,evecs,evalDiffThresh,ridge); ELL_3V_LERP(gfrontleft, newcoord[2], gbfl, gtfl); ELL_3V_LERP(gbackleft, newcoord[2], gbbl, gtbl); ELL_3V_LERP(gleft, newcoord[1], gfrontleft, gbackleft); ELL_3V_LERP(gfrontright, newcoord[2], gbfr, gtfr); ELL_3V_LERP(gbackright, newcoord[2], gbbr, gtbr); ELL_3V_LERP(gright, newcoord[1], gfrontright, gbackright); ELL_3V_LERP(g, newcoord[0], gleft, gright); ell_3mv_mul_d(Tg, T, g); ELL_3V_SUB(diff,Tg,g); newdist = ELL_3V_DOT(diff,diff); if (newdist0.24) { /* do a recursive step */ double idxcenter = 0.5*(idxleft+idxright); /* simply interpolate Hessian linearly */ double hessnew[9]; double evals[3],evecs[9]; double Tnew[9], gradnew[3]; int retval; ELL_3M_LERP(hessnew,0.5,hessleft,hessright); ell_3m_eigensolve_d(evals, evecs, hessnew, AIR_TRUE); _seekHess2T(Tnew, evals, evecs, evalDiffThresh, ridge); ELL_3V_LERP(gradnew,0.5,gleft,gright); retval = findFeatureIntersection(results, Tleft, hessleft, gleft, Tnew, hessnew, gradnew, idxleft, idxcenter, ridge, evalDiffThresh, dotThresh); retval += findFeatureIntersection(results+retval, Tnew, hessnew, gradnew, Tright, hessright, gright, idxcenter, idxright, ridge, evalDiffThresh, dotThresh); return retval; } else { double d1[3], d4[3]; ell_3mv_mul_d(d1, Tleft, gleft); ELL_3V_SUB(d1,d1,gleft); ell_3mv_mul_d(d4, Tright, gright); ELL_3V_SUB(d4,d4,gright); if (ELL_3V_DOT(d1,d4)<0) { /* mark edge as crossed */ /* find assumed intersection point */ double diff[3], dlen, alpha; ELL_3V_SUB(diff,d4,d1); dlen=ELL_3V_LEN(diff); if (dlen>1e-5) { double ap=-ELL_3V_DOT(d1,diff)/dlen; alpha = ap/dlen; } else alpha = 0.5; *results = (1-alpha)*idxleft+alpha*idxright; return 1; } } return 0; } /* Assuming (simplistic) linearly interpolated Hessians and gradients, * computes the analytical normal of the crease surface. * The result is _not_ normalized. */ static void computeGradientLin(double *result, double *T, double *g, double *Txm, double *gxm, double *Txp, double *gxp, double *Tym, double *gym, double *Typ, double *gyp, double *Tzm, double *gzm, double *Tzp, double *gzp) { double Tder[9]; double gder[3]; double tmp[3], tmp1[3], tmp2[3]; double derxv[3], deryv[3], derzv[3]; ELL_3M_SUB(Tder,Txp,Txm); ELL_3V_SUB(gder,gxp,gxm); ell_3mv_mul_d(tmp,T,gder); ELL_3V_SUB(tmp,tmp,gder); ell_3mv_mul_d(derxv,Tder,g); ELL_3V_ADD2(derxv,derxv,tmp); ELL_3M_SUB(Tder,Typ,Tym); ELL_3V_SUB(gder,gyp,gym); ell_3mv_mul_d(tmp,T,gder); ELL_3V_SUB(tmp,tmp,gder); ell_3mv_mul_d(deryv,Tder,g); ELL_3V_ADD2(deryv,deryv,tmp); ELL_3M_SUB(Tder,Tzp,Tzm); ELL_3V_SUB(gder,gzp,gzm); ell_3mv_mul_d(tmp,T,gder); ELL_3V_SUB(tmp,tmp,gder); ell_3mv_mul_d(derzv,Tder,g); ELL_3V_ADD2(derzv,derzv,tmp); /* accumulate a gradient */ tmp1[0]=derxv[0]; tmp1[1]=deryv[0]; tmp1[2]=derzv[0]; tmp2[0]=derxv[1]; tmp2[1]=deryv[1]; tmp2[2]=derzv[1]; if (ELL_3V_DOT(tmp1,tmp2)<0) ELL_3V_SCALE(tmp2,-1.0,tmp2); ELL_3V_ADD2(tmp1,tmp1,tmp2); tmp2[0]=derxv[2]; tmp2[1]=deryv[2]; tmp2[2]=derzv[2]; if (ELL_3V_DOT(tmp1,tmp2)<0) ELL_3V_SCALE(tmp2,-1.0,tmp2); ELL_3V_ADD2(result,tmp1,tmp2); } /* Given a unique edge ID and an intersection point given by some value * alpha \in [0,1], compute the crease surface normal at that point */ static void computeEdgeGradient(seekContext *sctx, baggage *bag, double *res, unsigned int xi, unsigned int yi, char edgeid, double alpha) { double Txm[9], Txp[9], Tym[9], Typ[9], Tzm[9], Tzp[9], T[9], gxm[3], gxp[3], gym[3], gyp[3], gzm[3], gzp[3], g[3]; unsigned int sx = AIR_CAST(unsigned int, sctx->sx); unsigned int sy = AIR_CAST(unsigned int, sctx->sy); unsigned int sz = AIR_CAST(unsigned int, sctx->sz); unsigned int si = xi + sx*yi; unsigned int six = xi + 1 + sx*yi, siX = xi - 1 + sx*yi; unsigned int siy = xi + sx*(yi+1), siY = xi + sx*(yi-1); unsigned int sixy = xi + 1 + sx*(yi+1), sixY = xi + 1 + sx*(yi-1), siXy = xi - 1 + sx*(yi+1); /* many special cases needed to fill Txm, gxm, etc. :-( */ switch (edgeid) { case 0: ELL_3M_LERP(T, alpha, sctx->t + 9*(0+2*si), sctx->t + 9*(0+2*six)); ELL_3V_LERP(g, alpha, sctx->grad + 3*(0+2*si), sctx->grad + 3*(0+2*six)); ELL_3M_LERP(Tzp, alpha, sctx->t + 9*(1+2*si), sctx->t + 9*(1+2*six)); ELL_3V_LERP(gzp, alpha, sctx->grad + 3*(1+2*si), sctx->grad + 3*(1+2*six)); if (bag->zi==0) { ELL_3M_COPY(Tzm, T); ELL_3V_COPY(gzm, g); } else { ELL_3M_LERP(Tzm, alpha, sctx->tcontext + 9*(0+2*si), sctx->tcontext + 9*(0+2*six)); ELL_3V_LERP(gzm, alpha, sctx->gradcontext + 3*(0+2*si), sctx->gradcontext + 3*(0+2*six)); ELL_3M_SCALE(Tzm, 0.5, Tzm); ELL_3M_SCALE(Tzp, 0.5, Tzp); ELL_3V_SCALE(gzm, 0.5, gzm); ELL_3V_SCALE(gzp, 0.5, gzp); } if (yi==0) { ELL_3M_COPY(Tym, T); ELL_3V_COPY(gym, g); } else { ELL_3M_LERP(Tym, alpha, sctx->t + 9*(0+2*siY), sctx->t + 9*(0+2*sixY)); ELL_3V_LERP(gym, alpha, sctx->grad + 3*(0+2*siY), sctx->grad + 3*(0+2*sixY)); } if (yi==sy-1) { ELL_3M_COPY(Typ, T); ELL_3V_COPY(gyp, g); } else { ELL_3M_LERP(Typ, alpha, sctx->t + 9*(0+2*siy), sctx->t + 9*(0+2*sixy)); ELL_3V_LERP(gyp, alpha, sctx->grad + 3*(0+2*siy), sctx->grad + 3*(0+2*sixy)); } if (yi!=0 && yi!=sy-1) { ELL_3M_SCALE(Tym, 0.5, Tym); ELL_3M_SCALE(Typ, 0.5, Typ); ELL_3V_SCALE(gym, 0.5, gym); ELL_3V_SCALE(gyp, 0.5, gyp); } computeGradientLin(res, T, g, sctx->t + 9*(0+2*si), sctx->grad + 3*(0+2*si), sctx->t + 9*(0+2*six), sctx->grad + 3*(0+2*six), Tym, gym, Typ, gyp, Tzm, gzm, Tzp, gzp); break; case 1: ELL_3M_LERP(T, alpha, sctx->t + 9*(0+2*si), sctx->t + 9*(0+2*siy)); ELL_3V_LERP(g, alpha, sctx->grad + 3*(0+2*si), sctx->grad + 3*(0+2*siy)); ELL_3M_LERP(Tzp, alpha, sctx->t + 9*(1+2*si), sctx->t + 9*(1+2*siy)); ELL_3V_LERP(gzp, alpha, sctx->grad + 3*(1+2*si), sctx->grad + 3*(1+2*siy)); if (bag->zi==0) { ELL_3M_COPY(Tzm, T); ELL_3V_COPY(gzm, g); } else { ELL_3M_LERP(Tzm, alpha, sctx->tcontext + 9*(0+2*si), sctx->tcontext + 9*(0+2*siy)); ELL_3V_LERP(gzm, alpha, sctx->gradcontext + 3*(0+2*si), sctx->gradcontext + 3*(0+2*siy)); ELL_3M_SCALE(Tzm, 0.5, Tzm); ELL_3M_SCALE(Tzp, 0.5, Tzp); ELL_3V_SCALE(gzm, 0.5, gzm); ELL_3V_SCALE(gzp, 0.5, gzp); } if (xi==0) { ELL_3M_COPY(Txm, T); ELL_3V_COPY(gxm, g); } else { ELL_3M_LERP(Txm, alpha, sctx->t + 9*(0+2*siX), sctx->t + 9*(0+2*siXy)); ELL_3V_LERP(gxm, alpha, sctx->grad + 3*(0+2*siX), sctx->grad + 3*(0+2*siXy)); } if (xi==sx-1) { ELL_3M_COPY(Txp, T); ELL_3V_COPY(gxm, g); } else { ELL_3M_LERP(Txp, alpha, sctx->t + 9*(0+2*six), sctx->t + 9*(0+2*sixy)); ELL_3V_LERP(gxp, alpha, sctx->grad + 3*(0+2*six), sctx->grad + 3*(0+2*sixy)); } if (xi!=0 && xi!=sx-1) { ELL_3M_SCALE(Txm, 0.5, Txm); ELL_3M_SCALE(Txp, 0.5, Txp); ELL_3V_SCALE(gxm, 0.5, gxm); ELL_3V_SCALE(gxp, 0.5, gxp); } computeGradientLin(res, T, g, Txm, gxm, Txp, gxp, sctx->t + 9*(0+2*si), sctx->grad + 3*(0+2*si), sctx->t + 9*(0+2*siy), sctx->grad + 3*(0+2*siy), T, g, Tzp, gzp); break; case 2: ELL_3M_LERP(T, alpha, sctx->t + 9*(0+2*si), sctx->t + 9*(1+2*si)); ELL_3V_LERP(g, alpha, sctx->grad + 3*(0+2*si), sctx->grad + 3*(1+2*si)); if (xi==0) { ELL_3M_COPY(Txm, T); ELL_3V_COPY(gxm, g); } else { ELL_3M_LERP(Txm, alpha, sctx->t + 9*(0+2*siX), sctx->t + 9*(1+2*siX)); ELL_3V_LERP(gxm, alpha, sctx->grad + 3*(0+2*siX), sctx->grad + 3*(1+2*siX)); } if (xi==sx-1) { ELL_3M_COPY(Txp, T); ELL_3V_COPY(gxp, g); } else { ELL_3M_LERP(Txp, alpha, sctx->t + 9*(0+2*six), sctx->t + 9*(1+2*six)); ELL_3V_LERP(gxp, alpha, sctx->grad + 3*(0+2*six), sctx->grad + 3*(1+2*six)); } if (xi!=0 && xi!=sx-1) { ELL_3M_SCALE(Txm, 0.5, Txm); ELL_3M_SCALE(Txp, 0.5, Txp); ELL_3V_SCALE(gxm, 0.5, gxm); ELL_3V_SCALE(gxp, 0.5, gxp); } if (yi==0) { ELL_3M_COPY(Tym, T); ELL_3V_COPY(gym, g); } else { ELL_3M_LERP(Tym, alpha, sctx->t + 9*(0+2*siY), sctx->t + 9*(1+2*siY)); ELL_3V_LERP(gym, alpha, sctx->grad + 3*(0+2*siY), sctx->grad + 3*(1+2*siY)); } if (yi==sy-1) { ELL_3M_COPY(Typ, T); ELL_3V_COPY(gyp, g); } else { ELL_3M_LERP(Typ, alpha, sctx->t + 9*(0+2*siy), sctx->t + 9*(1+2*siy)); ELL_3V_LERP(gyp, alpha, sctx->grad + 3*(0+2*siy), sctx->grad + 3*(1+2*siy)); } if (yi!=0 && yi!=sy-1) { ELL_3M_SCALE(Tym, 0.5, Tym); ELL_3M_SCALE(Typ, 0.5, Typ); ELL_3V_SCALE(gym, 0.5, gym); ELL_3V_SCALE(gyp, 0.5, gyp); } if (bag->zi>0 && bag->zitcontext + 9*(0+2*si), sctx->t + 9*(0+2*si)); ELL_3V_LERP(gzm, alpha, sctx->gradcontext + 3*(0+2*si), sctx->grad + 3*(0+2*si)); ELL_3M_LERP(Tzp, alpha, sctx->t + 9*(1+2*si), sctx->tcontext + 9*(1+2*si)); ELL_3V_LERP(gzp, alpha, sctx->grad + 3*(1+2*si), sctx->gradcontext + 3*(1+2*si)); ELL_3M_SCALE(Tzm, 0.5, Tzm); ELL_3M_SCALE(Tzp, 0.5, Tzp); ELL_3V_SCALE(gzm, 0.5, gzm); ELL_3V_SCALE(gzp, 0.5, gzp); } else { ELL_3M_COPY(Tzm, sctx->t + 9*(0+2*si)); ELL_3V_COPY(gzm, sctx->grad + 3*(0+2*si)); ELL_3M_COPY(Tzp, sctx->t + 9*(1+2*si)); ELL_3V_COPY(gzp, sctx->grad + 3*(1+2*si)); } computeGradientLin(res, T, g, Txm, gxm, Txp, gxp, Tym, gym, Typ, gyp, Tzm, gzm, Tzp, gzp); break; case 3: ELL_3M_LERP(T, alpha, sctx->t + 9*(1+2*si), sctx->t + 9*(1+2*six)); ELL_3V_LERP(g, alpha, sctx->grad + 3*(1+2*si), sctx->grad + 3*(1+2*six)); ELL_3M_LERP(Tzm, alpha, sctx->t + 9*(0+2*si), sctx->t + 9*(0+2*six)); ELL_3V_LERP(gzm, alpha, sctx->grad + 3*(0+2*si), sctx->grad + 3*(0+2*six)); if (bag->zi==sz-2) { ELL_3M_COPY(Tzp, T); ELL_3V_COPY(gzp, g); } else { ELL_3M_LERP(Tzp, alpha, sctx->tcontext + 9*(1+2*si), sctx->tcontext + 9*(1+2*six)); ELL_3V_LERP(gzp, alpha, sctx->gradcontext + 3*(1+2*si), sctx->gradcontext + 3*(1+2*six)); ELL_3M_SCALE(Tzm, 0.5, Tzm); ELL_3M_SCALE(Tzp, 0.5, Tzp); ELL_3V_SCALE(gzm, 0.5, gzm); ELL_3V_SCALE(gzp, 0.5, gzp); } if (xi>0 && xit + 9*(1+2*siX), sctx->t + 9*(1+2*si)); ELL_3V_LERP(gxm, alpha, sctx->grad + 3*(1+2*siX), sctx->grad + 3*(1+2*si)); ELL_3M_LERP(Txp, alpha, sctx->t + 9*(1+2*six), sctx->t + 9*(1+2*sixx)); ELL_3V_LERP(gxp, alpha, sctx->grad + 3*(1+2*six), sctx->grad + 3*(1+2*sixx)); ELL_3M_SCALE(Txm, 0.5, Txm); ELL_3M_SCALE(Txp, 0.5, Txp); ELL_3V_SCALE(gxm, 0.5, gxm); ELL_3V_SCALE(gxp, 0.5, gxp); } else { ELL_3M_COPY(Txm, sctx->t + 9*(1+2*si)); ELL_3V_COPY(gxm, sctx->grad + 3*(1+2*si)); ELL_3M_COPY(Txp, sctx->t + 9*(1+2*six)); ELL_3V_COPY(gxp, sctx->grad + 3*(1+2*six)); } if (yi==0) { ELL_3M_COPY(Tym, T); ELL_3V_COPY(gym, g); } else { ELL_3M_LERP(Tym, alpha, sctx->t + 9*(1+2*siY), sctx->t + 9*(1+2*sixY)); ELL_3V_LERP(gym, alpha, sctx->grad + 3*(1+2*siY), sctx->grad + 3*(1+2*sixY)); } if (yi==sy-1) { ELL_3M_COPY(Typ, T); ELL_3V_COPY(gyp, g); } else { ELL_3M_LERP(Typ, alpha, sctx->t + 9*(1+2*siy), sctx->t + 9*(1+2*sixy)); ELL_3V_LERP(gyp, alpha, sctx->grad + 3*(1+2*siy), sctx->grad + 3*(1+2*sixy)); } if (yi!=0 && yi!=sy-1) { ELL_3M_SCALE(Tym, 0.5, Tym); ELL_3M_SCALE(Typ, 0.5, Typ); ELL_3V_SCALE(gym, 0.5, gym); ELL_3V_SCALE(gyp, 0.5, gyp); } computeGradientLin(res, T, g, Txm, gxm, Txp, gxp, Tym, gym, Typ, gyp, Tzm, gzm, Tzp, gzp); break; case 4: ELL_3M_LERP(T, alpha, sctx->t + 9*(1+2*si), sctx->t + 9*(1+2*siy)); ELL_3V_LERP(g, alpha, sctx->grad + 3*(1+2*si), sctx->grad + 3*(1+2*siy)); ELL_3M_LERP(Tzm, alpha, sctx->t + 9*(0+2*si), sctx->t + 9*(0+2*siy)); ELL_3V_LERP(gzm, alpha, sctx->grad + 3*(0+2*si), sctx->grad + 3*(0+2*siy)); if (bag->zi==sz-2) { ELL_3M_COPY(Tzp, T); ELL_3V_COPY(gzp, g); } else { ELL_3M_LERP(Tzp, alpha, sctx->tcontext + 9*(1+2*si), sctx->tcontext + 9*(1+2*siy)); ELL_3V_LERP(gzp, alpha, sctx->gradcontext + 3*(1+2*si), sctx->gradcontext + 3*(1+2*siy)); ELL_3M_SCALE(Tzm, 0.5, Tzm); ELL_3M_SCALE(Tzp, 0.5, Tzp); ELL_3V_SCALE(gzm, 0.5, gzm); ELL_3V_SCALE(gzp, 0.5, gzp); } if (xi==0) { ELL_3M_COPY(Txm, T); ELL_3V_COPY(gxm, g); } else { ELL_3M_LERP(Txm, alpha, sctx->t + 9*(1+2*siX), sctx->t + 9*(1+2*siXy)); ELL_3V_LERP(gxm, alpha, sctx->grad + 3*(1+2*siX), sctx->grad + 3*(1+2*siXy)); } if (xi==sx-1) { ELL_3M_COPY(Txp, T); ELL_3V_COPY(gxp, g); } else { ELL_3M_LERP(Txp, alpha, sctx->t + 9*(1+2*six), sctx->t + 9*(1+2*sixy)); ELL_3V_LERP(gxp, alpha, sctx->grad + 3*(1+2*six), sctx->grad + 3*(1+2*sixy)); } if (xi!=0 && xi!=sx-1) { ELL_3M_SCALE(Txm, 0.5, Txm); ELL_3M_SCALE(Txp, 0.5, Txp); ELL_3V_SCALE(gxm, 0.5, gxm); ELL_3V_SCALE(gxp, 0.5, gxp); } if (yi>0 && yit + 9*(1+2*siY), sctx->t + 9*(1+2*si)); ELL_3V_LERP(gym, alpha, sctx->grad + 3*(1+2*siY), sctx->grad + 3*(1+2*si)); ELL_3M_LERP(Typ, alpha, sctx->t + 9*(1+2*siy), sctx->t + 9*(1+2*siyy)); ELL_3V_LERP(gyp, alpha, sctx->grad + 3*(1+2*siy), sctx->grad + 3*(1+2*siyy)); ELL_3M_SCALE(Tym, 0.5, Tym); ELL_3M_SCALE(Typ, 0.5, Typ); ELL_3V_SCALE(gym, 0.5, gym); ELL_3V_SCALE(gyp, 0.5, gyp); } else { ELL_3M_COPY(Tym, sctx->t + 9*(1+2*si)); ELL_3V_COPY(gym, sctx->grad + 3*(1+2*si)); ELL_3M_COPY(Typ, sctx->t + 9*(1+2*six)); ELL_3V_COPY(gyp, sctx->grad + 3*(1+2*six)); } computeGradientLin(res, T, g, Txm, gxm, Txp, gxp, Tym, gym, Typ, gyp, Tzm, gzm, Tzp, gzp); break; } } /* Given a unique face ID and coordinates, compute the crease surface * normal at the specified degenerate point */ static void computeFaceGradient(seekContext *sctx, double *res, unsigned int xi, unsigned int yi, char faceid, double *coords) { double T[9], Txm[9], Txp[9], Tym[9], Typ[9], Tzm[9], Tzp[9], g[3], gxm[3], gxp[3], gym[3], gyp[3], gzm[3], gzp[3]; unsigned int sx = AIR_CAST(unsigned int, sctx->sx); unsigned int sy = AIR_CAST(unsigned int, sctx->sy); unsigned int si = xi + sx*yi; unsigned int six = xi + 1 + sx*yi, siX = xi - 1 + sx*yi; unsigned int siy = xi + sx*(yi+1), siY = xi + sx*(yi-1); unsigned int sixy = xi + 1 + sx*(yi+1), sixY = xi + 1 + sx*(yi-1), siXy = xi - 1 + sx*(yi+1); /* Again, lots of special cases to fill Txm, gxm, etc. */ switch (faceid) { case 0: /* bilinearly interpolate Tzp/gzp first */ ELL_3M_LERP(Txm, coords[1], sctx->t + 9*(1+2*si), sctx->t + 9*(1+2*siy)); ELL_3V_LERP(gxm, coords[1], sctx->grad + 3*(1+2*si), sctx->grad + 3*(1+2*siy)); ELL_3M_LERP(Txp, coords[1], sctx->t + 9*(1+2*six), sctx->t + 9*(1+2*sixy)); ELL_3V_LERP(gxp, coords[1], sctx->grad + 3*(1+2*six), sctx->grad + 3*(1+2*sixy)); ELL_3M_LERP(Tzp, coords[0], Txm, Txp); ELL_3V_LERP(gzp, coords[0], gxm, gxp); /* now, compute all required points on the bottom face */ ELL_3M_LERP(Txm, coords[1], sctx->t + 9*(0+2*si), sctx->t + 9*(0+2*siy)); ELL_3V_LERP(gxm, coords[1], sctx->grad + 3*(0+2*si), sctx->grad + 3*(0+2*siy)); ELL_3M_LERP(Txp, coords[1], sctx->t + 9*(0+2*six), sctx->t + 9*(0+2*sixy)); ELL_3V_LERP(gxp, coords[1], sctx->grad + 3*(0+2*six), sctx->grad + 3*(0+2*sixy)); ELL_3M_LERP(Tym, coords[0], sctx->t + 9*(0+2*si), sctx->t + 9*(0+2*six)); ELL_3V_LERP(gym, coords[0], sctx->grad + 3*(0+2*si), sctx->grad + 3*(0+2*six)); ELL_3M_LERP(Typ, coords[0], sctx->t + 9*(0+2*siy), sctx->t + 9*(0+2*sixy)); ELL_3V_LERP(gyp, coords[0], sctx->grad + 3*(0+2*siy), sctx->grad + 3*(0+2*sixy)); ELL_3M_LERP(T, coords[0], Txm, Txp); ELL_3V_LERP(g, coords[0], gxm, gxp); computeGradientLin(sctx->facenorm+3*(faceid+4*si), T, g, Txm, gxm, Txp, gxp, Tym, gym, Typ, gyp, T, g, Tzp, gzp); break; case 1: /* bilinearly interpolate Typ/gyp first */ if (yi!=sy-1) { ELL_3M_LERP(Txm, coords[1], sctx->t + 9*(0+2*siy), sctx->t + 9*(1+2*siy)); ELL_3V_LERP(gxm, coords[1], sctx->grad + 3*(0+2*siy), sctx->grad + 3*(1+2*siy)); ELL_3M_LERP(Txp, coords[1], sctx->t + 9*(0+2*sixy), sctx->t + 9*(1+2*sixy)); ELL_3V_LERP(gxp, coords[1], sctx->grad + 3*(0+2*sixy), sctx->grad + 3*(1+2*sixy)); ELL_3M_LERP(Typ, coords[0], Txm, Txp); ELL_3V_LERP(gyp, coords[0], gxm, gxp); } else { ELL_3M_LERP(Txm, coords[1], sctx->t + 9*(0+2*siY), sctx->t + 9*(1+2*siY)); ELL_3V_LERP(gxm, coords[1], sctx->grad + 3*(0+2*siY), sctx->grad + 3*(1+2*siY)); ELL_3M_LERP(Txp, coords[1], sctx->t + 9*(0+2*sixY), sctx->t + 9*(1+2*sixY)); ELL_3V_LERP(gxp, coords[1], sctx->grad + 3*(0+2*sixY), sctx->grad + 3*(1+2*sixY)); ELL_3M_LERP(Tym, coords[0], Txm, Txp); ELL_3V_LERP(gym, coords[0], gxm, gxp); } /* now, compute remaining points */ ELL_3M_LERP(Txm, coords[1], sctx->t + 9*(0+2*si), sctx->t + 9*(1+2*si)); ELL_3V_LERP(gxm, coords[1], sctx->grad + 3*(0+2*si), sctx->grad + 3*(1+2*si)); ELL_3M_LERP(Txp, coords[1], sctx->t + 9*(0+2*six), sctx->t + 9*(1+2*six)); ELL_3V_LERP(gxp, coords[1], sctx->grad + 3*(0+2*six), sctx->grad + 3*(1+2*six)); ELL_3M_LERP(Tzm, coords[0], sctx->t + 9*(0+2*si), sctx->t + 9*(0+2*six)); ELL_3V_LERP(gzm, coords[0], sctx->grad + 3*(0+2*si), sctx->grad + 3*(0+2*six)); ELL_3M_LERP(Tzp, coords[0], sctx->t + 9*(1+2*si), sctx->t + 9*(1+2*six)); ELL_3V_LERP(gzp, coords[0], sctx->grad + 3*(1+2*si), sctx->grad + 3*(1+2*six)); ELL_3M_LERP(T, coords[0], Txm, Txp); ELL_3V_LERP(g, coords[0], gxm, gxp); if (yi!=sy-1) { computeGradientLin(sctx->facenorm+3*(faceid+4*si), T, g, Txm, gxm, Txp, gxp, T, g, Typ, gyp, Tzm, gzm, Tzp, gzp); } else { computeGradientLin(sctx->facenorm+3*(faceid+4*si), T, g, Txm, gxm, Txp, gxp, Tym, gym, T, g, Tzm, gzm, Tzp, gzp); } break; case 2: /* bilinearly interpolate Txp/gxp first */ if (xi!=sx-1) { ELL_3M_LERP(Tym, coords[1], sctx->t + 9*(0+2*six), sctx->t + 9*(1+2*six)); ELL_3V_LERP(gym, coords[1], sctx->grad + 3*(0+2*six), sctx->grad + 3*(1+2*six)); ELL_3M_LERP(Typ, coords[1], sctx->t + 9*(0+2*sixy), sctx->t + 9*(1+2*sixy)); ELL_3V_LERP(gyp, coords[1], sctx->grad + 3*(0+2*sixy), sctx->grad + 3*(1+2*sixy)); ELL_3M_LERP(Txp, coords[0], Tym, Typ); ELL_3V_LERP(gxp, coords[0], gym, gyp); } else { ELL_3M_LERP(Tym, coords[1], sctx->t + 9*(0+2*siX), sctx->t + 9*(1+2*siX)); ELL_3V_LERP(gym, coords[1], sctx->grad + 3*(0+2*siX), sctx->grad + 3*(1+2*siX)); ELL_3M_LERP(Typ, coords[1], sctx->t + 9*(0+2*siXy), sctx->t + 9*(1+2*siXy)); ELL_3V_LERP(gyp, coords[1], sctx->grad + 3*(0+2*siXy), sctx->grad + 3*(1+2*siXy)); ELL_3M_LERP(Txm, coords[0], Tym, Typ); ELL_3V_LERP(gxm, coords[0], gym, gyp); } /* now, compute remaining points */ ELL_3M_LERP(Tym, coords[1], sctx->t + 9*(0+2*si), sctx->t + 9*(1+2*si)); ELL_3V_LERP(gym, coords[1], sctx->grad + 3*(0+2*si), sctx->grad + 3*(1+2*si)); ELL_3M_LERP(Typ, coords[1], sctx->t + 9*(0+2*siy), sctx->t + 9*(1+2*siy)); ELL_3V_LERP(gyp, coords[1], sctx->grad + 3*(0+2*siy), sctx->grad + 3*(1+2*siy)); ELL_3M_LERP(Tzm, coords[0], sctx->t + 9*(0+2*si), sctx->t + 9*(0+2*siy)); ELL_3V_LERP(gzm, coords[0], sctx->grad + 3*(0+2*si), sctx->grad + 3*(0+2*siy)); ELL_3M_LERP(Tzp, coords[0], sctx->t + 9*(1+2*si), sctx->t + 9*(1+2*siy)); ELL_3V_LERP(gzp, coords[0], sctx->grad + 3*(1+2*si), sctx->grad + 3*(1+2*siy)); ELL_3M_LERP(T, coords[0], Tym, Typ); ELL_3V_LERP(g, coords[0], gym, gyp); if (xi!=sx-1) { computeGradientLin(sctx->facenorm+3*(faceid+4*si), T, g, T, g, Txp, gxp, Tym, gym, Typ, gyp, Tzm, gzm, Tzp, gzp); } else { computeGradientLin(sctx->facenorm+3*(faceid+4*si), T, g, Txm, gxm, T, g, Tym, gym, Typ, gyp, Tzm, gzm, Tzp, gzp); } break; case 3: /* bilinearly interpolate Tzm/gzm first */ ELL_3M_LERP(Txm, coords[1], sctx->t + 9*(0+2*si), sctx->t + 9*(0+2*siy)); ELL_3V_LERP(gxm, coords[1], sctx->grad + 3*(0+2*si), sctx->grad + 3*(0+2*siy)); ELL_3M_LERP(Txp, coords[1], sctx->t + 9*(0+2*six), sctx->t + 9*(0+2*sixy)); ELL_3V_LERP(gxp, coords[1], sctx->grad + 3*(0+2*six), sctx->grad + 3*(0+2*sixy)); ELL_3M_LERP(Tzm, coords[0], Txm, Txp); ELL_3V_LERP(gzm, coords[0], gxm, gxp); /* now, compute all required points on the top face */ ELL_3M_LERP(Txm, coords[1], sctx->t + 9*(1+2*si), sctx->t + 9*(1+2*siy)); ELL_3V_LERP(gxm, coords[1], sctx->grad + 3*(1+2*si), sctx->grad + 3*(1+2*siy)); ELL_3M_LERP(Txp, coords[1], sctx->t + 9*(1+2*six), sctx->t + 9*(1+2*sixy)); ELL_3V_LERP(gxp, coords[1], sctx->grad + 3*(1+2*six), sctx->grad + 3*(1+2*sixy)); ELL_3M_LERP(Tym, coords[0], sctx->t + 9*(1+2*si), sctx->t + 9*(1+2*six)); ELL_3V_LERP(gym, coords[0], sctx->grad + 3*(1+2*si), sctx->grad + 3*(1+2*six)); ELL_3M_LERP(Typ, coords[0], sctx->t + 9*(1+2*siy), sctx->t + 9*(1+2*sixy)); ELL_3V_LERP(gyp, coords[0], sctx->grad + 3*(1+2*siy), sctx->grad + 3*(1+2*sixy)); ELL_3M_LERP(T, coords[0], Txm, Txp); ELL_3V_LERP(g, coords[0], gxm, gxp); computeGradientLin(sctx->facenorm+3*(faceid+4*si), T, g, Txm, gxm, Txp, gxp, Tym, gym, Typ, gyp, Tzm, gzm, T, g); break; } ELL_3V_COPY(res, sctx->facenorm+3*(faceid+4*si)); } /* small helper routines: intersection tests */ /* check if a given 2D triangle is oriented clockwise (-1) * or counter-clockwise (1). * returns 0 if given points are collinear */ static int checkTriOrientation (double *p1, double *p2, double *p3) { double test = (((p2[0]-p1[0])*(p3[1]-p1[1])) - ((p3[0]-p1[0])*(p2[1]-p1[1]))); if (test > 0) return 1; else if (test < 0) return -1; else return 0; } /* check if two given 2D lines intersect */ static int lineIntersectionTest (double *l1p1, double *l1p2, double *l2p1, double *l2p2) { int or1 = checkTriOrientation(l1p1, l1p2, l2p1); int or2 = checkTriOrientation(l1p1, l1p2, l2p2); if (or1 != or2) { or1 = checkTriOrientation(l2p1, l2p2, l1p1); or2 = checkTriOrientation(l2p1, l2p2, l1p2); if (or1 != or2) return 1; } return 0; } /* check if two given 3D triangles intersect */ static int triIntersectionTest (double *t1v1, double *t1v2, double *t1v3, double *t2v1, double *t2v2, double *t2v3) { double n1[3], n2[3], d1, d2; double diff1[3], diff2[3]; double t2sd1, t2sd2, t2sd3; ELL_3V_SUB(diff1, t1v2, t1v1); ELL_3V_SUB(diff2, t1v3, t1v1); ELL_3V_CROSS(n1,diff1,diff2); d1=-ELL_3V_DOT(n1,t1v1); /* compute scaled signed distances of t2 to plane of t1 */ t2sd1 = ELL_3V_DOT(n1, t2v1)+d1; t2sd2 = ELL_3V_DOT(n1, t2v2)+d1; t2sd3 = ELL_3V_DOT(n1, t2v3)+d1; if (t2sd1==0 && t2sd2==0 && t2sd3==0) { /* coplanar case: handle in 2D */ double t1v12d[2], t1v22d[2], t1v32d[2], t2v12d[2], t2v22d[2], t2v32d[2]; if (fabs(n1[0])>=fabs(n1[1]) && fabs(n1[0])>=fabs(n1[2])) { t1v12d[0]=t1v1[1]; t1v12d[1]=t1v1[2]; t1v22d[0]=t1v2[1]; t1v22d[1]=t1v2[2]; t1v32d[0]=t1v3[1]; t1v32d[1]=t1v3[2]; t2v12d[0]=t2v1[1]; t2v12d[1]=t2v1[2]; t2v22d[0]=t2v2[1]; t2v22d[1]=t2v2[2]; t2v32d[0]=t2v3[1]; t2v32d[1]=t2v3[2]; } else if (fabs(n1[1])>=fabs(n1[0]) && fabs(n1[1])>=fabs(n1[2])) { t1v12d[0]=t1v1[0]; t1v12d[1]=t1v1[2]; t1v22d[0]=t1v2[0]; t1v22d[1]=t1v2[2]; t1v32d[0]=t1v3[0]; t1v32d[1]=t1v3[2]; t2v12d[0]=t2v1[0]; t2v12d[1]=t2v1[2]; t2v22d[0]=t2v2[0]; t2v22d[1]=t2v2[2]; t2v32d[0]=t2v3[0]; t2v32d[1]=t2v3[2]; } else { t1v12d[0]=t1v1[0]; t1v12d[1]=t1v1[1]; t1v22d[0]=t1v2[0]; t1v22d[1]=t1v2[1]; t1v32d[0]=t1v3[0]; t1v32d[1]=t1v3[1]; t2v12d[0]=t2v1[0]; t2v12d[1]=t2v1[1]; t2v22d[0]=t2v2[0]; t2v22d[1]=t2v2[1]; t2v32d[0]=t2v3[0]; t2v32d[1]=t2v3[1]; } /* we may assume that none of the triangles is fully contained * within the other. Thus, it suffices to do a lot of 2D line-line * intersections */ if (lineIntersectionTest(t1v12d, t1v22d, t2v12d, t2v22d) || lineIntersectionTest(t1v22d, t1v32d, t2v12d, t2v22d) || lineIntersectionTest(t1v32d, t1v12d, t2v12d, t2v22d) || lineIntersectionTest(t1v12d, t1v22d, t2v22d, t2v32d) || lineIntersectionTest(t1v22d, t1v32d, t2v22d, t2v32d) || lineIntersectionTest(t1v32d, t1v12d, t2v22d, t2v32d) || lineIntersectionTest(t1v12d, t1v22d, t2v32d, t2v12d) || lineIntersectionTest(t1v22d, t1v32d, t2v32d, t2v12d) || lineIntersectionTest(t1v32d, t1v12d, t2v32d, t2v12d)) return 1; return 0; } else { /* pointers to the vertices on the same side / opposite side of plane */ double *t2s11, *t2s12, *t2s2, t2s11sd, t2s12sd, t2s2sd; double t1sd1, t1sd2, t1sd3; double *t1s11, *t1s12, *t1s2, t1s11sd, t1s12sd, t1s2sd; double t1p11, t1p12, t1p2, t2p11, t2p12, t2p2; double D[3]; /* direction vector of line */ double t1t1, t1t2, t2t1, t2t2; if (t2sd1*t2sd2>=0 && t2sd1*t2sd3<=0) { t2s11=t2v1; t2s12=t2v2; t2s2=t2v3; t2s11sd=t2sd1; t2s12sd=t2sd2; t2s2sd=t2sd3; } else if (t2sd1*t2sd3>=0 && t2sd1*t2sd2<=0) { t2s11=t2v1; t2s12=t2v3; t2s2=t2v2; t2s11sd=t2sd1; t2s12sd=t2sd3; t2s2sd=t2sd2; } else if (t2sd2*t2sd3>=0 && t2sd1*t2sd2<=0) { t2s11=t2v2; t2s12=t2v3; t2s2=t2v1; t2s11sd=t2sd2; t2s12sd=t2sd3; t2s2sd=t2sd1; } else return 0; /* all on the same side; no intersection */ /* same game for triangle 2 */ ELL_3V_SUB(diff1, t2v2, t2v1); ELL_3V_SUB(diff2, t2v3, t2v1); ELL_3V_CROSS(n2, diff1, diff2); d2=-ELL_3V_DOT(n2,t2v1); t1sd1 = ELL_3V_DOT(n2, t1v1)+d2; t1sd2 = ELL_3V_DOT(n2, t1v2)+d2; t1sd3 = ELL_3V_DOT(n2, t1v3)+d2; if (t1sd1*t1sd2>=0 && t1sd1*t1sd3<=0) { t1s11=t1v1; t1s12=t1v2; t1s2=t1v3; t1s11sd=t1sd1; t1s12sd=t1sd2; t1s2sd=t1sd3; } else if (t1sd1*t1sd3>=0 && t1sd1*t1sd2<=0) { t1s11=t1v1; t1s12=t1v3; t1s2=t1v2; t1s11sd=t1sd1; t1s12sd=t1sd3; t1s2sd=t1sd2; } else if (t1sd2*t1sd3>=0 && t1sd1*t1sd2<=0) { t1s11=t1v2; t1s12=t1v3; t1s2=t1v1; t1s11sd=t1sd2; t1s12sd=t1sd3; t1s2sd=t1sd1; } else return 0; /* all on the same side; no intersection */ /* both planes intersect in a line; check if the intervals on that * line intersect */ ELL_3V_CROSS(D,n1,n2); /* we are only interested in component magnitudes */ D[0]=fabs(D[0]); D[1]=fabs(D[1]); D[2]=fabs(D[2]); if (D[0]>=D[1] && D[0]>=D[2]) { t1p11=t1s11[0]; t1p12=t1s12[0]; t1p2=t1s2[0]; t2p11=t2s11[0]; t2p12=t2s12[0]; t2p2=t2s2[0]; } else if (D[1]>=D[0] && D[1]>=D[2]) { t1p11=t1s11[1]; t1p12=t1s12[1]; t1p2=t1s2[1]; t2p11=t2s11[1]; t2p12=t2s12[1]; t2p2=t2s2[1]; } else { t1p11=t1s11[2]; t1p12=t1s12[2]; t1p2=t1s2[2]; t2p11=t2s11[2]; t2p12=t2s12[2]; t2p2=t2s2[2]; } /* compute interval boundaries */ t1t1=t1p11+(t1p2-t1p11)*t1s11sd/(t1s11sd-t1s2sd); t1t2=t1p12+(t1p2-t1p12)*t1s12sd/(t1s12sd-t1s2sd); if (t1t1>t1t2) { double help=t1t1; t1t1=t1t2; t1t2=help; } t2t1=t2p11+(t2p2-t2p11)*t2s11sd/(t2s11sd-t2s2sd); t2t2=t2p12+(t2p2-t2p12)*t2s12sd/(t2s12sd-t2s2sd); if (t2t1>t2t2) { double help=t2t1; t2t1=t2t2; t2t2=help; } /* test for interval intersection */ if (t2t1>t1t2 || t1t1>t2t2) return 0; return 1; } } /* Score possible local topologies based on the agreement of * connecting lines with normal directions. Lower scores are * better. */ /* Connections between degenerate points on cell faces; if only four * degenerate points are present, set p31 to NULL */ static double evaluateDegConnection(double *p11, double *p12, double *p13, double *p14, double *p21, double *p22, double *p23, double *p24, double *p31, double *p32, double *p33, double *p34, double *n12, double *n13, double *n22, double *n23, double *n32, double *n33) { double diff1[3], diff2[3], diff3[3], ret; /* first, perform intersection testing */ if (triIntersectionTest(p11, p12, p13, p21, p22, p23) || triIntersectionTest(p13, p14, p11, p21, p22, p23) || triIntersectionTest(p11, p12, p13, p23, p24, p21) || triIntersectionTest(p13, p14, p11, p23, p24, p21)) return 1e20; if (p31 != NULL) { /* three pairs - some more to do */ if (triIntersectionTest(p11, p12, p13, p31, p32, p33) || triIntersectionTest(p11, p12, p13, p33, p34, p31) || triIntersectionTest(p13, p14, p11, p31, p32, p33) || triIntersectionTest(p13, p14, p11, p33, p34, p31) || triIntersectionTest(p21, p22, p23, p31, p32, p33) || triIntersectionTest(p21, p22, p23, p33, p34, p31) || triIntersectionTest(p23, p24, p21, p31, p32, p33) || triIntersectionTest(p23, p24, p21, p33, p34, p31)) return 1e20; } ELL_3V_SUB(diff1,p13,p12); ELL_3V_SUB(diff2,p23,p22); ret=fabs(ELL_3V_DOT(diff1,n12))+fabs(ELL_3V_DOT(diff1,n13))+ fabs(ELL_3V_DOT(diff2,n22))+fabs(ELL_3V_DOT(diff2,n23)); if (p31 != NULL) { ELL_3V_SUB(diff3,p33,p32); ret+=fabs(ELL_3V_DOT(diff3,n32))+fabs(ELL_3V_DOT(diff3,n33)); } return ret; } /* suggests a connectivity for a non-trivial combination of * intersection points in the plane. * pairs is output (permutation of idcs) * bestval is input/output (best value so far, start with something big) * ct is the number of points (currently assumed even) * idcs is input (idcs that still need to be permuted) * fixedct is input (number of idcs that are already fixed at this depth) * coords is an array of 2D coordinates * norms is an array of 2D vectors */ static void findConnectivity(signed char *pairs, double *bestval, int ct, char *idcs, int fixedct, double *coords, double *norms) { int i,j; if (fixedct==ct) { double weight=0; for (i=0; isx); unsigned int si = xi + sx*yi; unsigned int six = xi + 1 + sx*yi; unsigned int siy = xi + sx*(yi+1); unsigned int sixy = xi + 1 + sx*(yi+1); char inter[12]; /* indices of intersections */ int pass; /* allow multiple refined passes */ int i; /* voxel in which treated information is stored for local edge i */ /* which vertices form which unique face? */ int verti[4][4]; int voxel[4][4]; /* mask for treated information in the above voxel */ char mask[4][4]={{_SEEK_TREATED_EDGE0, _SEEK_TREATED_EDGE1, _SEEK_TREATED_EDGE0, _SEEK_TREATED_EDGE1}, {_SEEK_TREATED_EDGE0, _SEEK_TREATED_EDGE2, _SEEK_TREATED_EDGE3, _SEEK_TREATED_EDGE2}, {_SEEK_TREATED_EDGE1, _SEEK_TREATED_EDGE2, _SEEK_TREATED_EDGE4, _SEEK_TREATED_EDGE2}, {_SEEK_TREATED_EDGE3, _SEEK_TREATED_EDGE4, _SEEK_TREATED_EDGE3, _SEEK_TREATED_EDGE4}}; /* start- and endpoints of the edges */ int verts[4][4], verte[4][4]; char treat[4]={0,0,0,0}; double dpthresh[3]={0.7,0.8,0.9}; /* candidates for degenerate points */ double candidates[18]={0.5,0.5, 0.25,0.25, 0.25,0.75, 0.75,0.25, 0.75,0.75, 0.5,0.25, 0.25,0.5, 0.75,0.5, 0.6,0.75}; int cand_idx[4]={0, 1, 5, 9}; int interct; /* apparently, some C compilers cannot make these initializations in-place */ ELL_4V_SET(verti[0], 0+2*si, 0+2*six, 0+2*siy, 0+2*sixy); ELL_4V_SET(verti[1], 0+2*si, 0+2*six, 1+2*si, 1+2*six); ELL_4V_SET(verti[2], 0+2*si, 0+2*siy, 1+2*si, 1+2*siy); ELL_4V_SET(verti[3], 1+2*si, 1+2*six, 1+2*siy, 1+2*sixy); ELL_4V_SET(voxel[0], si, six, siy, si); ELL_4V_SET(voxel[1], si, six, si, si); ELL_4V_SET(voxel[2], si, siy, si, si); ELL_4V_SET(voxel[3], si, six, siy, si); ELL_4V_SET(verts[0], 0+2*si, 0+2*six, 0+2*siy, 0+2*si); ELL_4V_SET(verts[1], 0+2*si, 0+2*six, 1+2*si, 0+2*si); ELL_4V_SET(verts[2], 0+2*si, 0+2*siy, 1+2*si, 0+2*si); ELL_4V_SET(verts[3], 1+2*si, 1+2*six, 1+2*siy, 1+2*si); ELL_4V_SET(verte[0], 0+2*six, 0+2*sixy, 0+2*sixy, 0+2*siy); ELL_4V_SET(verte[1], 0+2*six, 1+2*six, 1+2*six, 1+2*si); ELL_4V_SET(verte[2], 0+2*siy, 1+2*siy, 1+2*siy, 1+2*si); ELL_4V_SET(verte[3], 1+2*six, 1+2*sixy, 1+2*sixy, 1+2*siy); /* find out which edges have not yet been treated */ for (i=0; i<4; i++) { if (!(sctx->treated[voxel[faceid][i]]&mask[faceid][i])) { treat[i]=1; /* we need to treat this */ sctx->treated[voxel[faceid][i]] |= mask[faceid][i]; } } for (pass=0; pass<3; pass++) { /* first, find intersections for edges that need treatment */ int j; for (j=0; j<4; j++) { double interpos[8]; if (!treat[j]) continue; interct=findFeatureIntersection(interpos, sctx->t + 9*verts[faceid][j], sctx->hess + 9*verts[faceid][j], sctx->grad + 3*verts[faceid][j], sctx->t + 9*verte[faceid][j], sctx->hess + 9*verte[faceid][j], sctx->grad + 3*verte[faceid][j], 0.0, 1.0, bag->esIdx==2, sctx->evalDiffThresh, dpthresh[pass]); if (interct>3) interct=3; for (i=0; iedgealpha[3*(bag->evti[edgeid[faceid][j]]+5*si)+i] = interpos[i]; switch (edgeid[faceid][j]) { case 0: x=xi+interpos[i]; y=yi; z=bag->zi; xb=xi; yb=yi; idb=0; break; case 1: x=xi; y=yi+interpos[i]; z=bag->zi; xb=xi; yb=yi; idb=1; break; case 2: x=xi+1; y=yi+interpos[i]; z=bag->zi; xb=xi+1; yb=yi; idb=1; break; case 3: x=xi+interpos[i]; y=yi+1; z=bag->zi; xb=xi; yb=yi+1; idb=0; break; case 4: x=xi; y=yi; z=bag->zi+interpos[i]; xb=xi; yb=yi; idb=2; break; case 5: x=xi+1; y=yi; z=bag->zi+interpos[i]; xb=xi+1; yb=yi; idb=2; break; case 6: x=xi; y=yi+1; z=bag->zi+interpos[i]; xb=xi; yb=yi+1; idb=2; break; case 7: x=xi+1; y=yi+1; z=bag->zi+interpos[i]; xb=xi+1; yb=yi+1; idb=2; break; case 8: x=xi+interpos[i]; y=yi; z=bag->zi+1; xb=xi; yb=yi; idb=3; break; case 9: x=xi; y=yi+interpos[i]; z=bag->zi+1; xb=xi; yb=yi; idb=4; break; case 10: x=xi+1; y=yi+interpos[i]; z=bag->zi+1; xb=xi+1; yb=yi; idb=4; break; case 11: x=xi+interpos[i]; y=yi+1; z=bag->zi+1; xb=xi; yb=yi+1; idb=3; break; } ELL_3V_SET(sctx->edgeicoord+9*(bag->evti[edgeid[faceid][j]]+5*si)+3*i, x, y, z); computeEdgeGradient(sctx, bag, sctx->edgenorm+ 9*(bag->evti[edgeid[faceid][j]]+5*si)+3*i, xb, yb, idb, interpos[i]); } } interct=0; /* number of feature intersections */ for (i=0; i<3; i++) { if (sctx->edgealpha[3*(bag->evti[edgeid[faceid][0]]+5*si)+i]>=0) inter[interct++]=i; /* numbering is local w.r.t. face */ if (sctx->edgealpha[3*(bag->evti[edgeid[faceid][1]]+5*si)+i]>=0) inter[interct++]=3+i; if (sctx->edgealpha[3*(bag->evti[edgeid[faceid][2]]+5*si)+i]>=0) inter[interct++]=6+i; if (sctx->edgealpha[3*(bag->evti[edgeid[faceid][3]]+5*si)+i]>=0) inter[interct++]=9+i; } if (interct%2==1) { /* we need to look for a degeneracy */ int k; for (k=cand_idx[pass]; kfacecoord+2*(faceid+4*si), candidates[2*k], candidates[2*k+1]); if (!seekDescendToDeg(sctx->facecoord+2*(faceid+4*si), sctx->hess + 9*verti[faceid][0], sctx->hess + 9*verti[faceid][1], sctx->hess + 9*verti[faceid][2], sctx->hess + 9*verti[faceid][3], 50, 1e-9, (bag->esIdx==2)?'l':'p')) { inter[interct++]=12; /* 12 means "deg. point on this face */ break; } } if ((pass==2) && (inter[interct-1]!=12)) { /* nothing helped, so insert a dummy vertex */ ELL_2V_SET(sctx->facecoord+2*(faceid+4*si), 0.5, 0.5); inter[interct++]=12; } if (inter[interct-1]==12) { computeFaceGradient(sctx, sctx->facenorm+3*(faceid+4*si), xi, yi, faceid, sctx->facecoord+2*(faceid+4*si)); switch (faceid) { case 0: ELL_3V_SET(sctx->faceicoord+3*(faceid+4*si), xi+sctx->facecoord[2*(faceid+4*si)], yi+sctx->facecoord[2*(faceid+4*si)+1], bag->zi); break; case 1: ELL_3V_SET(sctx->faceicoord+3*(faceid+4*si), xi+sctx->facecoord[2*(faceid+4*si)], yi, bag->zi+sctx->facecoord[2*(faceid+4*si)+1]); break; case 2: ELL_3V_SET(sctx->faceicoord+3*(faceid+4*si), xi, yi+sctx->facecoord[2*(faceid+4*si)], bag->zi+sctx->facecoord[2*(faceid+4*si)+1]); break; case 3: ELL_3V_SET(sctx->faceicoord+3*(faceid+4*si), xi+sctx->facecoord[2*(faceid+4*si)], yi+sctx->facecoord[2*(faceid+4*si)+1], bag->zi+1); break; } } } if (interct%2==0) { /* we can break out */ break; } } if (interct<=1) { /* there is no connectivity on this face */ ELL_4V_SET(sctx->pairs+12*(faceid+4*si),-1,-1,-1,-1); } else if (interct==2) { /* connectivity is straightforward */ ELL_4V_SET(sctx->pairs+12*(faceid+4*si),inter[0],inter[1],-1,-1); } else { /* we need gradients and coordinates to make a decision */ double interc[24]; /* 2D coordinates of intersection points */ double intern[24]; /* 2D normals at intersection points */ /* used to find the best pairing without self-intersection */ double bestscore=1e20; char idcs[12]; /* consider if we need to restrict this */ for (i=0; iedgeicoord[9*(bag->evti[resolved]+5*si)+3*offset], sctx->edgeicoord[9*(bag->evti[resolved]+5*si)+3*offset+1]); ELL_2V_SET(intern+2*i, sctx->edgenorm[9*(bag->evti[resolved]+5*si)+3*offset], sctx->edgenorm[9*(bag->evti[resolved]+5*si)+3*offset+1]); break; case 1: ELL_2V_SET(interc+2*i, sctx->edgeicoord[9*(bag->evti[resolved]+5*si)+3*offset], sctx->edgeicoord[9*(bag->evti[resolved]+5*si)+3*offset+2]); ELL_2V_SET(intern+2*i, sctx->edgenorm[9*(bag->evti[resolved]+5*si)+3*offset], sctx->edgenorm[9*(bag->evti[resolved]+5*si)+3*offset+2]); break; case 2: ELL_2V_SET(interc+2*i, sctx->edgeicoord[9*(bag->evti[resolved]+5*si)+3*offset+1], sctx->edgeicoord[9*(bag->evti[resolved]+5*si)+3*offset+2]); ELL_2V_SET(intern+2*i, sctx->edgenorm[9*(bag->evti[resolved]+5*si)+3*offset+1], sctx->edgenorm[9*(bag->evti[resolved]+5*si)+3*offset+2]); break; } } else { /* face feature */ switch (faceid) { case 0: case 3: ELL_2V_SET(interc+2*i, sctx->faceicoord[3*(faceid+4*si)], sctx->faceicoord[3*(faceid+4*si)+1]); ELL_2V_SET(intern+2*i, sctx->facenorm[3*(faceid+4*si)], sctx->facenorm[3*(faceid+4*si)+1]); break; case 1: ELL_2V_SET(interc+2*i, sctx->faceicoord[3*(faceid+4*si)], sctx->faceicoord[3*(faceid+4*si)+2]); ELL_2V_SET(intern+2*i, sctx->facenorm[3*(faceid+4*si)], sctx->facenorm[3*(faceid+4*si)+2]); break; case 2: ELL_2V_SET(interc+2*i, sctx->faceicoord[3*(faceid+4*si)+1], sctx->faceicoord[3*(faceid+4*si)+2]); ELL_2V_SET(intern+2*i, sctx->facenorm[3*(faceid+4*si)+1], sctx->facenorm[3*(faceid+4*si)+2]); break; } } } for (i=0; ipairs[12*(faceid+4*si)+i]=i; idcs[i]=i; } findConnectivity(sctx->pairs+12*(faceid+4*si), &bestscore, interct, idcs, 0, interc, intern); for (i=0; ipairs[12*(faceid+4*si)+i]=inter[sctx->pairs[12*(faceid+4*si)+i]]; for (i=interct; i<12; i++) sctx->pairs[12*(faceid+4*si)+i]=-1; } } static void intersectionShuffleProbe(seekContext *sctx, baggage *bag) { unsigned int xi, yi, sx, sy, si; int i; sx = AIR_CAST(unsigned int, sctx->sx); sy = AIR_CAST(unsigned int, sctx->sy); for (yi=0; yizi) { /* initialize, else copy over */ sctx->facevidx[0 + 4*si] = -1; } else { sctx->facevidx[0 + 4*si] = sctx->facevidx[3 + 4*si]; } sctx->facevidx[1 + 4*si] = sctx->facevidx[2 + 4*si] = sctx->facevidx[3 + 4*si] = -1; /* copy or reset data on the 5 unique edges */ if (sctx->treated[si]&_SEEK_TREATED_EDGE3) { /* has been treated, just copy results */ ELL_3V_COPY(sctx->edgealpha+3*(0+5*si), sctx->edgealpha+3*(3+5*si)); ELL_3M_COPY(sctx->edgenorm+9*(0+5*si), sctx->edgenorm+9*(3+5*si)); ELL_3M_COPY(sctx->edgeicoord+9*(0+5*si), sctx->edgeicoord+9*(3+5*si)); sctx->treated[si]|=_SEEK_TREATED_EDGE0; } else if (xi!=sx-1) { ELL_3V_SET(sctx->edgealpha+3*(0+5*si),-1,-1,-1); sctx->treated[si]&=0xFF^_SEEK_TREATED_EDGE0; } if (sctx->treated[si]&_SEEK_TREATED_EDGE4) { /* has been treated, just copy results */ ELL_3V_COPY(sctx->edgealpha+3*(1+5*si), sctx->edgealpha+3*(4+5*si)); ELL_3M_COPY(sctx->edgenorm+9*(1+5*si), sctx->edgenorm+9*(4+5*si)); ELL_3M_COPY(sctx->edgeicoord+9*(1+5*si), sctx->edgeicoord+9*(4+5*si)); sctx->treated[si]|=_SEEK_TREATED_EDGE1; } else if (yi!=sy-1) { ELL_3V_SET(sctx->edgealpha+3*(1+5*si),-1,-1,-1); sctx->treated[si]&=0xFF^_SEEK_TREATED_EDGE1; } /* edges within and at top of the slab are new */ ELL_3V_SET(sctx->edgealpha+3*(2+5*si),-1,-1,-1); sctx->treated[si]&=0xFF^_SEEK_TREATED_EDGE2; ELL_3V_SET(sctx->edgealpha+3*(3+5*si),-1,-1,-1); sctx->treated[si]&=0xFF^_SEEK_TREATED_EDGE3; ELL_3V_SET(sctx->edgealpha+3*(4+5*si),-1,-1,-1); sctx->treated[si]&=0xFF^_SEEK_TREATED_EDGE4; } } /* find missing deg. points, edge intersections, normals, and * connectivity on the four unique faces * this is done in a separate pass to make sure that all edge information * has been updated */ for (yi=0; yitreated[si]&_SEEK_TREATED_FACE3) { /* we can copy previous results */ ELL_2V_COPY(sctx->facecoord+2*(0+4*si), sctx->facecoord+2*(3+4*si)); ELL_3V_COPY(sctx->faceicoord+3*(0+4*si), sctx->faceicoord+3*(3+4*si)); ELL_3V_COPY(sctx->facenorm+3*(0+4*si), sctx->facenorm+3*(3+4*si)); for (i=0; i<3; i++) ELL_4V_COPY(sctx->pairs+12*(0+4*si)+4*i, sctx->pairs+12*(3+4*si)+4*i); } else if (xi!=sx-1 && yi!=sy-1) { if (sctx->treated[si]&_SEEK_TREATED_REQUEST) connectFace(sctx, bag, xi, yi, 0); else ELL_4V_SET(sctx->pairs+12*(0+4*si),-1,-1,-1,-1); } if (xi!=sx-1) { if (sctx->treated[si]&_SEEK_TREATED_REQUEST || (yi!=0 && sctx->treated[xi+sx*(yi-1)]&_SEEK_TREATED_REQUEST)) connectFace(sctx, bag, xi, yi, 1); else ELL_4V_SET(sctx->pairs+12*(1+4*si),-1,-1,-1,-1); } if (yi!=sy-1) { if (sctx->treated[si]&_SEEK_TREATED_REQUEST || (xi!=0 && sctx->treated[xi-1+sx*yi]&_SEEK_TREATED_REQUEST)) connectFace(sctx, bag, xi, yi, 2); else ELL_4V_SET(sctx->pairs+12*(2+4*si),-1,-1,-1,-1); if (xi!=sx-1) { if (sctx->treated[si]&_SEEK_TREATED_REQUEST) { connectFace(sctx, bag, xi, yi, 3); sctx->treated[si]|=_SEEK_TREATED_FACE3; } else { ELL_4V_SET(sctx->pairs+12*(3+4*si),-1,-1,-1,-1); sctx->treated[si]&=0xFF^_SEEK_TREATED_FACE3; } } } } } } /* special triangulation routine for use with T-based extraction */ int _seekTriangulateT(seekContext *sctx, baggage *bag, limnPolyData *lpld) { unsigned xi, yi, sx, sy, si, i; /* map edge indices w.r.t. faces (as used in sctx->pairs) back to * edge indices w.r.t. voxel */ char edges[6][5]={{0, 2, 3, 1,12}, {0, 5, 8, 4,13}, {2, 7,10, 5,14}, {3, 7,11, 6,15}, {1, 6, 9, 4,16}, {8,10,11, 9,17}}; sx = AIR_CAST(unsigned int, sctx->sx); sy = AIR_CAST(unsigned int, sctx->sy); for (yi=0; yistrengthUse && sctx->stng[0+2*(xi+sx*yi)] < sctx->strength && sctx->stng[1+2*(xi+sx*yi)] < sctx->strength && sctx->stng[0+2*(xi+1+sx*yi)] < sctx->strength && sctx->stng[1+2*(xi+1+sx*yi)] < sctx->strength && sctx->stng[0+2*(xi+sx*(yi+1))] < sctx->strength && sctx->stng[1+2*(xi+sx*(yi+1))] < sctx->strength && sctx->stng[0+2*(xi+1+sx*(yi+1))] < sctx->strength && sctx->stng[1+2*(xi+1+sx*(yi+1))] < sctx->strength) continue;/* all vertices below strength limit, do not create geometry */ si = xi + sx*yi; ELL_3V_SET(fvti, 0 + 4*si, 1 + 4*si, 2 + 4*(xi+1 + sx*yi)); ELL_3V_SET(fvti+3, 1 + 4*(xi + sx*(yi+1)), 2 + 4*si, 3 + 4*si); /* collect all intersection + connectivity info for this voxel */ memset(connections,-1,sizeof(connections)); for (face=0; face<6; face++) { for (i=0; i<6; i++) { int idx1, offset1, idx2, offset2, idxmap1, idxmap2; if (sctx->pairs[12*fvti[face]+2*i]==-1) break; idx1=edges[face][sctx->pairs[12*fvti[face]+2*i]/3]; offset1=sctx->pairs[12*fvti[face]+2*i]%3; idx2=edges[face][sctx->pairs[12*fvti[face]+2*i+1]/3]; offset2=sctx->pairs[12*fvti[face]+2*i+1]%3; idxmap1=3*idx1+offset1; idxmap2=3*idx2+offset2; if (idx1>11) { idxmap1=idx1+24; /* +36-12 */ degeneracies[degct++] = idxmap1; } if (idx2>11) { idxmap2=idx2+24; degeneracies[degct++] = idxmap2; } if (connections[2*idxmap1]==-1) connections[2*idxmap1]=idxmap2; else connections[2*idxmap1+1]=idxmap2; if (connections[2*idxmap2]==-1) connections[2*idxmap2]=idxmap1; else connections[2*idxmap2+1]=idxmap1; } } /* connect the degenerate points */ if (degct==2) { connections[2*degeneracies[0]+1]=degeneracies[1]; connections[2*degeneracies[1]+1]=degeneracies[0]; } else if (degct==4) { int bestchoice=0; int eidcs[4], fidcs[4]; int k; double bestscore, score; for (k=0; k<4; ++k) { eidcs[k]=3*(bag->evti[connections[2*degeneracies[k]]/3]+5*si)+ connections[2*degeneracies[k]]%3; fidcs[k]=fvti[degeneracies[k]-36]; } bestscore=evaluateDegConnection(sctx->edgeicoord+3*eidcs[0], sctx->faceicoord+3*fidcs[0], sctx->faceicoord+3*fidcs[1], sctx->edgeicoord+3*eidcs[1], sctx->edgeicoord+3*eidcs[2], sctx->faceicoord+3*fidcs[2], sctx->faceicoord+3*fidcs[3], sctx->edgeicoord+3*eidcs[3], NULL, NULL, NULL, NULL, sctx->facenorm+3*fidcs[0], sctx->facenorm+3*fidcs[1], sctx->facenorm+3*fidcs[2], sctx->facenorm+3*fidcs[3], NULL, NULL); score=evaluateDegConnection(sctx->edgeicoord+3*eidcs[0], sctx->faceicoord+3*fidcs[0], sctx->faceicoord+3*fidcs[2], sctx->edgeicoord+3*eidcs[2], sctx->edgeicoord+3*eidcs[1], sctx->faceicoord+3*fidcs[1], sctx->faceicoord+3*fidcs[3], sctx->edgeicoord+3*eidcs[3], NULL, NULL, NULL, NULL, sctx->facenorm+3*fidcs[0], sctx->facenorm+3*fidcs[2], sctx->facenorm+3*fidcs[1], sctx->facenorm+3*fidcs[3], NULL, NULL); if (scoreedgeicoord+3*eidcs[0], sctx->faceicoord+3*fidcs[0], sctx->faceicoord+3*fidcs[3], sctx->edgeicoord+3*eidcs[3], sctx->edgeicoord+3*eidcs[1], sctx->faceicoord+3*fidcs[1], sctx->faceicoord+3*fidcs[2], sctx->edgeicoord+3*eidcs[2], NULL, NULL, NULL, NULL, sctx->facenorm+3*fidcs[0], sctx->facenorm+3*fidcs[3], sctx->facenorm+3*fidcs[1], sctx->facenorm+3*fidcs[2], NULL, NULL); if (scoreevti[connections[2*degeneracies[k]]/3]+5*si)+ connections[2*degeneracies[k]]%3; fidcs[k]=fvti[degeneracies[k]-36]; } bestscore=evaluateDegConnection(sctx->edgeicoord+3*eidcs[0], sctx->faceicoord+3*fidcs[0], sctx->faceicoord+3*fidcs[1], sctx->edgeicoord+3*eidcs[1], sctx->edgeicoord+3*eidcs[2], sctx->faceicoord+3*fidcs[2], sctx->faceicoord+3*fidcs[3], sctx->edgeicoord+3*eidcs[3], sctx->edgeicoord+3*eidcs[4], sctx->faceicoord+3*fidcs[4], sctx->faceicoord+3*fidcs[5], sctx->edgeicoord+3*eidcs[5], sctx->facenorm+3*fidcs[0], sctx->facenorm+3*fidcs[1], sctx->facenorm+3*fidcs[2], sctx->facenorm+3*fidcs[3], sctx->facenorm+3*fidcs[4], sctx->facenorm+3*fidcs[5]); for (k=1; k<15; ++k) { double score=evaluateDegConnection (sctx->edgeicoord+3*eidcs[pairings[k][0]], sctx->faceicoord+3*fidcs[pairings[k][0]], sctx->faceicoord+3*fidcs[pairings[k][1]], sctx->edgeicoord+3*eidcs[pairings[k][1]], sctx->edgeicoord+3*eidcs[pairings[k][2]], sctx->faceicoord+3*fidcs[pairings[k][2]], sctx->faceicoord+3*fidcs[pairings[k][3]], sctx->edgeicoord+3*eidcs[pairings[k][3]], sctx->edgeicoord+3*eidcs[pairings[k][4]], sctx->faceicoord+3*fidcs[pairings[k][4]], sctx->faceicoord+3*fidcs[pairings[k][5]], sctx->edgeicoord+3*eidcs[pairings[k][5]], sctx->facenorm+3*fidcs[pairings[k][0]], sctx->facenorm+3*fidcs[pairings[k][1]], sctx->facenorm+3*fidcs[pairings[k][2]], sctx->facenorm+3*fidcs[pairings[k][3]], sctx->facenorm+3*fidcs[pairings[k][4]], sctx->facenorm+3*fidcs[pairings[k][5]]); if (scoreevti[polygon[j]/3] + 5*si)+polygon[j]%3; if (-1 == sctx->vidx[eidx]) { int ovi; ELL_3V_COPY(tvertA, sctx->edgeicoord+3*eidx); tvertA[3]=1.0; /* tvertB in input index space */ ELL_4MV_MUL(tvertB, sctx->txfIdx, tvertA); /* tvertA in world space */ ELL_4MV_MUL(tvertA, sctx->shape->ItoW, tvertB); ELL_4V_HOMOG(tvertA, tvertA); ovi = sctx->vidx[eidx] = airArrayLenIncr(bag->xyzwArr, 1); ELL_4V_SET_TT(lpld->xyzw + 4*ovi, float, tvertA[0], tvertA[1], tvertA[2], 1.0); if (sctx->normalsFind) { double len=ELL_3V_LEN(sctx->edgenorm+3*eidx); airArrayLenIncr(bag->normArr, 1); ELL_3V_SCALE_TT(lpld->norm + 3*ovi, float, 1.0/len, sctx->edgenorm+3*eidx); } sctx->vertNum++; } } else { /* we may need to insert a face vertex */ int fidx=fvti[polygon[j]-36]; if (-1 == sctx->facevidx[fidx]) { int ovi; ELL_3V_COPY(tvertA, sctx->faceicoord+3*fidx); tvertA[3]=1.0; /* tvertB in input index space */ ELL_4MV_MUL(tvertB, sctx->txfIdx, tvertA); /* tvertA in world space */ ELL_4MV_MUL(tvertA, sctx->shape->ItoW, tvertB); ELL_4V_HOMOG(tvertA, tvertA); ovi = sctx->facevidx[fidx] = airArrayLenIncr(bag->xyzwArr, 1); ELL_4V_SET_TT(lpld->xyzw + 4*ovi, float, tvertA[0], tvertA[1], tvertA[2], 1.0); if (sctx->normalsFind) { double len=ELL_3V_LEN(sctx->facenorm+3*fidx); airArrayLenIncr(bag->normArr, 1); ELL_3V_SCALE_TT(lpld->norm + 3*ovi, float, 1.0/len, sctx->facenorm+3*fidx); } sctx->vertNum++; } } } if (polyct>4) { /* we need to insert a helper vertex */ double tvertA[4], tvertB[4], tvertAsum[4]={0,0,0,0}, normsum[3]={0,0,0}; int ovi; unsigned int vii[3]; for (j=0; jevti[polygon[j]/3] + 5*si)+polygon[j]%3; ELL_3V_COPY(tvertA, sctx->edgeicoord+3*eidx); tvertA[3]=1.0; ELL_4V_INCR(tvertAsum,tvertA); if (ELL_3V_DOT(normsum,sctx->edgenorm+3*eidx)<0) ELL_3V_SUB(normsum,normsum,sctx->edgenorm+3*eidx); else ELL_3V_INCR(normsum,sctx->edgenorm+3*eidx); } else { int fidx=fvti[polygon[j]-36]; ELL_3V_COPY(tvertA, sctx->faceicoord+3*fidx); tvertA[3]=1.0; ELL_4V_INCR(tvertAsum,tvertA); if (ELL_3V_DOT(normsum,sctx->facenorm+3*fidx)<0) ELL_3V_SUB(normsum,normsum,sctx->facenorm+3*fidx); else ELL_3V_INCR(normsum,sctx->facenorm+3*fidx); } } /* tvertB in input index space */ ELL_4MV_MUL(tvertB, sctx->txfIdx, tvertAsum); /* tvertA in world space */ ELL_4MV_MUL(tvertA, sctx->shape->ItoW, tvertB); ELL_4V_HOMOG(tvertA, tvertA); ovi = airArrayLenIncr(bag->xyzwArr, 1); ELL_4V_SET_TT(lpld->xyzw + 4*ovi, float, tvertA[0], tvertA[1], tvertA[2], 1.0); if (sctx->normalsFind) { double len=ELL_3V_LEN(normsum); airArrayLenIncr(bag->normArr, 1); ELL_3V_SCALE_TT(lpld->norm + 3*ovi, float, 1.0/len, normsum); } sctx->vertNum++; vii[0]=ovi; vii[1]=sctx->vidx[3*(bag->evti[polygon[0]/3]+5*si)+polygon[0]%3]; for (j=0; jvidx[3*(bag->evti[polygon[0]/3] + 5*si)+ polygon[0]%3]; else vii[1]=sctx->facevidx[fvti[polygon[0]-36]]; } else { if (polygon[j+1]<36) vii[1]=sctx->vidx[3*(bag->evti[polygon[j+1]/3] + 5*si)+ polygon[j+1]%3]; else vii[1]=sctx->facevidx[fvti[polygon[j+1]-36]]; } /* check for degenerate tris */ ELL_3V_SUB(edgeA, lpld->xyzw+4*vii[1], lpld->xyzw+4*vii[0]); ELL_3V_SUB(edgeB, lpld->xyzw+4*vii[2], lpld->xyzw+4*vii[0]); ELL_3V_CROSS(norm, edgeA, edgeB); if (ELL_3V_DOT(norm,norm)!=0) { unsigned iii = airArrayLenIncr(bag->indxArr, 3); ELL_3V_COPY(lpld->indx + iii, vii); lpld->icnt[0] += 3; sctx->faceNum++; } /* else: degeneracies are caused by intersections that * more or less coincide with a grid vertex. They * should be harmless, so just don't create * deg. triangles in this case */ } } else if (polyct>2) { /* insert the actual triangles */ unsigned int vii[3], iii; if (polygon[0]<36) vii[0]=sctx->vidx[3*(bag->evti[polygon[0]/3] + 5*si)+ polygon[0]%3]; else vii[0]=sctx->facevidx[fvti[polygon[0]-36]]; if (polygon[1]<36) vii[1]=sctx->vidx[3*(bag->evti[polygon[1]/3] + 5*si)+ polygon[1]%3]; else vii[1]=sctx->facevidx[fvti[polygon[1]-36]]; if (polygon[2]<36) vii[2]=sctx->vidx[3*(bag->evti[polygon[2]/3] + 5*si)+ polygon[2]%3]; else vii[2]=sctx->facevidx[fvti[polygon[2]-36]]; iii = airArrayLenIncr(bag->indxArr, 3); ELL_3V_COPY(lpld->indx + iii, vii); lpld->icnt[0] += 3; sctx->faceNum++; if (polyct==4) { vii[1]=vii[2]; if (polygon[3]<36) vii[2]=sctx->vidx[3*(bag->evti[polygon[3]/3] + 5*si)+ polygon[3]%3]; else vii[2]=sctx->facevidx[fvti[polygon[3]-36]]; iii = airArrayLenIncr(bag->indxArr, 3); ELL_3V_COPY(lpld->indx + iii, vii); lpld->icnt[0] += 3; sctx->faceNum++; } } } } } } } return 0; } static void shuffleT(seekContext *sctx, baggage *bag) { unsigned int xi, yi, sx, sy, si; sx = AIR_CAST(unsigned int, sctx->sx); sy = AIR_CAST(unsigned int, sctx->sy); if (sctx->strengthUse) { /* requests need to be cleared initially */ for (yi=0; yitreated[xi+sx*yi] &= 0xFF^_SEEK_TREATED_REQUEST; } } } /* else, the request bits are always on */ for (yi=0; yizi) { ELL_3V_SET(sctx->vidx+3*(0+5*si), -1, -1, -1); ELL_3V_SET(sctx->vidx+3*(1+5*si), -1, -1, -1); } else { ELL_3V_COPY(sctx->vidx+3*(0+5*si), sctx->vidx+3*(3+5*si)); ELL_3V_COPY(sctx->vidx+3*(1+5*si), sctx->vidx+3*(4+5*si)); } ELL_3V_SET(sctx->vidx+3*(2+5*si),-1,-1,-1); ELL_3V_SET(sctx->vidx+3*(3+5*si),-1,-1,-1); ELL_3V_SET(sctx->vidx+3*(4+5*si),-1,-1,-1); /* strength only has future context */ if (sctx->strengthUse) { sctx->stng[0 + 2*si] = sctx->stng[1 + 2*si]; sctx->stng[1 + 2*si] = sctx->stngcontext[si]; if (sctx->stng[0+2*si]>sctx->strength || sctx->stng[1+2*si]>sctx->strength) { /* set up to four request bits */ sctx->treated[si] |= _SEEK_TREATED_REQUEST; if (xi!=0) sctx->treated[xi-1+sx*yi] |= _SEEK_TREATED_REQUEST; if (yi!=0) sctx->treated[xi+sx*(yi-1)] |= _SEEK_TREATED_REQUEST; if (xi!=0 && yi!=0) sctx->treated[xi-1+sx*(yi-1)] |= _SEEK_TREATED_REQUEST; } } /* shuffle grad, hess and t in three steps: move to past context, * shuffle in slab itself, move from future context */ ELL_3V_COPY(sctx->gradcontext + 3*(0+2*si), sctx->grad + 3*(0+2*si)); ELL_3V_COPY(sctx->grad + 3*(0+2*si), sctx->grad + 3*(1+2*si)); ELL_3V_COPY(sctx->grad + 3*(1+2*si), sctx->gradcontext + 3*(1+2*si)); ELL_3M_COPY(sctx->hesscontext + 9*(0+2*si), sctx->hess + 9*(0+2*si)); ELL_3M_COPY(sctx->hess + 9*(0+2*si), sctx->hess + 9*(1+2*si)); ELL_3M_COPY(sctx->hess + 9*(1+2*si), sctx->hesscontext + 9*(1+2*si)); ELL_3M_COPY(sctx->tcontext + 9*(0+2*si), sctx->t + 9*(0+2*si)); ELL_3M_COPY(sctx->t + 9*(0+2*si), sctx->t + 9*(1+2*si)); ELL_3M_COPY(sctx->t + 9*(1+2*si), sctx->tcontext + 9*(1+2*si)); } } } static void probeT(seekContext *sctx, baggage *bag, double zi) { unsigned int xi, yi, sx, sy, si; sx = AIR_CAST(unsigned int, sctx->sx); sy = AIR_CAST(unsigned int, sctx->sy); for (yi=0; yigctx) { /* HEY: need this check, what's the right way? */ _seekIdxProbe(sctx, bag, xi, yi, zi); } if (sctx->strengthUse) { sctx->stngcontext[si] = sctx->strengthSign*sctx->stngAns[0]; if (sctx->strengthSeenMax==AIR_NAN) { sctx->strengthSeenMax = sctx->stngcontext[si]; } sctx->strengthSeenMax = AIR_MAX(sctx->strengthSeenMax, sctx->stngcontext[si]); } ELL_3V_COPY(sctx->gradcontext + 3*(1 + 2*si), sctx->gradAns); ELL_3M_COPY(sctx->hesscontext + 9*(1 + 2*si), sctx->hessAns); _seekHess2T(sctx->tcontext + 9*(1 + 2*si), sctx->evalAns, sctx->evecAns, sctx->evalDiffThresh, (sctx->type==seekTypeRidgeSurfaceT)); } } } /* it has now become much easier to make this its own routine * (vs. adding many more case distinctions to shuffleProbe) * this only duplicates little (and trivial) code */ int _seekShuffleProbeT(seekContext *sctx, baggage *bag) { /* for high-quality normal estimation, we need two slices of data * context; to keep the code simple, separate shuffle and probe * operations - let's hope this doesn't destroy cache performance */ if (!bag->zi) { if (sctx->strengthUse) /* before the first round, initialize treated to zero */ memset(sctx->treated, 0, sizeof(char)*sctx->sx*sctx->sy); else /* request all edges */ memset(sctx->treated, _SEEK_TREATED_REQUEST, sizeof(char)*sctx->sx*sctx->sy); probeT(sctx, bag, 0); shuffleT(sctx, bag); probeT(sctx, bag, 1); } shuffleT(sctx, bag); if (bag->zi!=sctx->sz-2) probeT(sctx, bag, bag->zi+2); intersectionShuffleProbe(sctx, bag); return 0; } /* For all vertices in pld, use sctx to probe the strength measure, * and return the answer (times strengthSign) in nval. The intended * use is postfiltering (with limnPolyDataClip), which is obligatory * when using seekType*SurfaceT * * Returns 1 and adds a message to biff upon error * Returns 0 on success, -n when probing n vertices failed (strength * is set to AIR_NAN for those); note that positions outside the field * are clamped to lie on its boundary. * * This routine assumes that a strength has been set in sctx and * seekUpdate() has been run. * This routine does not modify sctx->strengthSeenMax. */ int seekVertexStrength(Nrrd *nval, seekContext *sctx, limnPolyData *pld) { static const char me[]="seekVertexStrength"; unsigned int i; double *data; int E=0; if (!(nval && sctx && pld)) { biffAddf(SEEK, "%s: got NULL pointer", me); return 1; } if (!(sctx->gctx && sctx->pvl)) { biffAddf(SEEK, "%s: need sctx with attached gageContext", me); return 1; } if (!sctx->stngAns) { biffAddf(SEEK, "%s: no strength item found. Did you enable strengthUse?", me); return 1; } if (nrrdAlloc_va(nval, nrrdTypeDouble, 1, (size_t) pld->xyzwNum)) { biffAddf(SEEK, "%s: could not allocate output", me); return 1; } data = (double*) nval->data; for (i=0; ixyzwNum; i++) { float homog[4]; ELL_4V_HOMOG(homog, pld->xyzw+4*i); if (!gageProbeSpace(sctx->gctx,homog[0],homog[1],homog[2],0,1)) { *(data++)=*(sctx->stngAns)*sctx->strengthSign; } else { *(data++)=AIR_NAN; E--; } } return E; } teem-1.11.0~svn6057/src/unangle.pl0000775000175000017500000000260112165631065016371 0ustar domibeldomibel#!/usr/bin/perl -w # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # GK uses this to process text output to be used in
    # settings; generally for usage information
    
    $line = 0;
    while (<>) {
        s//>/g;
        s/  [\b][\b]//g;
        if ($line) {
    	print;
        }
        $line += 1;
    }
    teem-1.11.0~svn6057/src/bane/0000775000175000017500000000000012203513754015303 5ustar  domibeldomibelteem-1.11.0~svn6057/src/bane/scat.c0000664000175000017500000000564512165631065016416 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    
    #include "bane.h"
    #include "privateBane.h"
    
    int
    baneRawScatterplots(Nrrd *nvg, Nrrd *nvh, Nrrd *hvol, int histEq) {
      Nrrd *gA, *hA, *gB, *hB;
      static const char me[]="baneRawScatterplots";
      int E;
    
      if (!( nvg && nvh && hvol )) {
        biffAddf(BANE, "%s: got NULL pointer", me);
        return 1;
      }
      if (baneHVolCheck(hvol)) {
        biffAddf(BANE, "%s: didn't get a valid histogram volume", me);
        return 1;
      }
    
      gA = nrrdNew(); gB = nrrdNew();
      hA = nrrdNew(); hB = nrrdNew();
      /* create initial projections */
      E = 0;
      if (!E) E |= nrrdProject(gA, hvol, 1, nrrdMeasureSum, nrrdTypeDefault);
      if (!E) E |= nrrdProject(hA, hvol, 0, nrrdMeasureSum, nrrdTypeDefault);
      if (E) {
        biffMovef(BANE, NRRD, "%s: trouble creating raw scatterplots", me);
        return 1;
      }
    
      /* do histogram equalization on them */
      if (histEq) {
        if (!E) E |= nrrdHistoEq(gB, gA, NULL, baneStateHistEqBins,
                                 baneStateHistEqSmart, 1.0);
        if (!E) E |= nrrdHistoEq(hB, hA, NULL, baneStateHistEqBins,
                                 baneStateHistEqSmart, 1.0);
      } else {
        if (!E) E |= nrrdCopy(gB, gA);
        if (!E) E |= nrrdCopy(hB, hA);
      }
      if (E) {
        biffMovef(BANE, NRRD, "%s: couldn't histogram equalize or copy", me);
        return 1;
      }
    
      /* re-orient them so they look correct on the screen */
      if (!E) E |= nrrdAxesSwap(gA, gB, 0, 1);
      if (!E) E |= nrrdAxesSwap(hA, hB, 0, 1);
      if (!E) E |= nrrdFlip(gB, gA, 1);
      if (!E) E |= nrrdFlip(hB, hA, 1);
      if (E) {
        biffMovef(BANE, NRRD, "%s: couldn't re-orient scatterplots", me);
        return 1;
      }
    
      if (!E) E |= nrrdCopy(nvg, gB);
      if (!E) E |= nrrdCopy(nvh, hB);
      if (E) {
        biffMovef(BANE, NRRD, "%s: trouble saving results to given nrrds", me);
        return 1;
      }
    
      nrrdNuke(gA); nrrdNuke(gB);
      nrrdNuke(hA); nrrdNuke(hB);
      return 0;
    }
    teem-1.11.0~svn6057/src/bane/bane.h0000664000175000017500000003604712165631065016376 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    #ifndef BANE_HAS_BEEN_INCLUDED
    #define BANE_HAS_BEEN_INCLUDED
    
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC)
    #  if defined(TEEM_BUILD) || defined(bane_EXPORTS) || defined(teem_EXPORTS)
    #    define BANE_EXPORT extern __declspec(dllexport)
    #  else
    #    define BANE_EXPORT extern __declspec(dllimport)
    #  endif
    #else /* TEEM_STATIC || UNIX */
    #  define BANE_EXPORT extern
    #endif
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #define BANE baneBiffKey
    
    /*
    ******** #define BANE_PARM_NUM
    **
    ** max number of parameters that may be needed by a baneInc, baneClip, etc
    */
    #define BANE_PARM_NUM 5
    
    
    /* -------------------------- ranges -------------------------- */
    
    /*
    ******** baneRange* enum
    **
    ** Range: nature of the values generated by a measure- are they
    ** strictly positive (such as gradient magnitude), should they be
    ** considered to be centered around zero (2nd directional
    ** derivative) or could they be anywhere (data
    ** value).
    **
    ** The job of the answer() function in the range is not to exclude
    ** any data.  Indeed, if the range is set correctly for the type
    ** of data used, then range->ans() should always return a range
    ** that is as large or larger than the one which was passed.
    ** Doing otherwise would make ranges too complicated (such as
    ** requiring a parm array), and besides, its the job of the
    ** inclusion methods to be smart about things like this.
    */
    enum {
      baneRangeUnknown,      /* 0: nobody knows */
      baneRangePositive,     /* 1: always positive: enforce that min == 0 */
      baneRangeNegative,     /* 2: always negative: enforce that max == 0 */
      baneRangeZeroCentered, /* 3: positive and negative, centered around
                                zero: enforce (conservative) centering of
                                interval around 0 */
      baneRangeAnywhere,     /* 4: anywhere: essentially a no-op */
      baneRangeLast
    };
    
    /*
    ******** baneRange struct
    **
    ** things used to operate on ranges
    */
    typedef struct {
      char name[AIR_STRLEN_SMALL];
      int type;
      double center;  /* for baneRangeAnywhere: nominal center of value range
                         NOTE: there is currently no API for setting this,
                         it has to be set manually */
      int (*answer)(double *ominP, double *omaxP,
                    double imin, double imax);
    } baneRange;
    
    /* -------------------------- inc -------------------------- */
    
    /*
    ******** baneInc* enum
    **
    ** Inc: methods for determing what range of measured values deserves
    ** to be included along one axes of a histogram volume.  Each
    ** inclusion method has some parameters (at most BANE_PARM_NUM)
    ** which are (or can be harmlessly cast to) floats.  Some of them need
    ** a histogram (a Nrrd) in order to determine the new min and max,
    ** some just use a Nrrd as a place to store some information.
    **
    ** To make matters confusing, the behavior of some of these varies with
    ** the baneRange they are associated with.
    **
    ** As version 1.7, the incParm[] is no longer used to communicate the
    ** center of a baneRangeAnywhere- that is now (as it should be) in
    ** the baneRange itself.
    */
    enum {
      baneIncUnknown,     /* 0: nobody knows */
      baneIncAbsolute,    /* 1: within explicitly specified bounds
                             -- incParm[0]: new min
                             -- incParm[1]: new max */
      baneIncRangeRatio,  /* 2: some fraction of the total range
                             -- incParm[0]: scales the size of the range, after
                                it has been sent through the associated range
                                function. */
      baneIncPercentile,  /* 3: exclude some percentile
                             -- incParm[0]: resolution of histogram generated
                             -- incParm[1]: PERCENT of hits to throw away,
                                by nibbling away at lower and upper ends of
                                range, in a manner dependant on the range type */
      baneIncStdv,        /* 4: some multiple of the standard deviation
                             -- incParm[0]: range is standard deviation
                                times this */
      baneIncLast
    };
    
    /*
    ******** baneInc struct
    **
    ** things used to calculate and describe inclusion ranges.  The return
    ** from histNew should be eventually passed to nrrdNuke.
    */
    typedef struct baneInc_t {
      char name[AIR_STRLEN_SMALL];
      int type;
      double S, SS; int num;  /* used for calculating standard dev */
      Nrrd *nhist;
      baneRange *range;
      double parm[BANE_PARM_NUM];
      void (*process[2])(struct baneInc_t *inc, double val);
      int (*answer)(double *minP, double *maxP,
                    Nrrd *hist, double *parm, baneRange *range);
    } baneInc;
    
    /* -------------------------- clip -------------------------- */
    
    /*
    ******** baneClip* enum
    **
    ** Clip: how to map values in the "raw" histogram volume to the more
    ** convenient 8-bit version.  The number of hits for the semi-constant
    ** background of a large volume can be huge, so some scheme for dealing
    ** with this is needed.
    */
    enum {
      baneClipUnknown,     /* 0: nobody knows */
      baneClipAbsolute,    /* 1: clip at explicitly specified bin count */
      baneClipPeakRatio,   /* 2: some fraction of maximum #hits in any bin */
      baneClipPercentile,  /* 3: percentile of values, sorted by hits */
      baneClipTopN,        /* 4: ignore the N bins with the highest counts */
      baneClipLast
    };
    
    /*
    ******** baneClip struct
    **
    ** things used to calculate and describe clipping
    */
    typedef struct {
      char name[AIR_STRLEN_SMALL];
      int type;
      double parm[BANE_PARM_NUM];
      int (*answer)(int *countP, Nrrd *hvol, double *clipParm);
    } baneClip;
    
    /* -------------------------- measr -------------------------- */
    
    /*
    ******** baneMeasr* enum
    **
    ** Measr: one of the kind of measurement which determines location along
    ** one of the axes of the histogram volume.
    **
    ** In this latest version of bane (1.4), I nixed the "gradient of magnitude
    ** of gradient" (GMG) based measure of the second directional
    ** derivative.  I felt that the benefits of using gage for value and
    ** derivative measurement (allowing arbitrary kernels), combined with
    ** the fact that doing GMG can't be done directly in gage (because its
    ** a derivative of pre-computed derivatives), outweighed the loss of
    ** GMG.  Besides, according to Appendix C of my Master's thesis the
    ** only thing its really good at avoiding is quantization noise in
    ** 8-bit data, but gage isn't limited to 8-bit data anyway.
    **
    ** The reason for not simply using the pre-defined gageScl values is
    ** that eventually I'll want to do things which modify/combine those
    ** values in a parameter-controlled way, something which will never be
    ** in gage.  Hence the parm array, even though nothing currently
    ** uses them.
    */
    enum {
      baneMeasrUnknown,           /* 0: nobody knows */
      baneMeasrValuePositive,     /* 1: the data value, with positive range
                                     (gageSclValue) */
      baneMeasrValueZeroCentered, /* 2: the data value, with zero-centered range
                                     (gageSclValue) */
      baneMeasrValueAnywhere,     /* 3: the data value, with anywhere range
                                     (gageSclValue) */
      baneMeasrGradMag,           /* 4: gradient magnitude (gageSclGradMag) */
      baneMeasrLaplacian,         /* 5: Laplacian (gageSclLaplacian) */
      baneMeasr2ndDD,             /* 6: Hessian-based measure of 2nd DD along
                                     gradient (gageScl2ndDD) */
      baneMeasrTotalCurv,         /* 7: L2 norm of K1, K2 principal curvatures
                                     (gageSclTotalCurv) */
      baneMeasrFlowlineCurv,      /* 8: curvature of normal streamline
                                     (gageSclFlowlineCurv) */
      baneMeasrLast
    };
    
    /*
    ******** baneMeasr struct
    **
    ** things used to calculate and describe measurements
    */
    typedef struct baneMeasr_t {
      char name[AIR_STRLEN_SMALL];
      int type;
      double parm[BANE_PARM_NUM];
      gageQuery query;       /* the gageScl query needed for this measure,
                                but NOT its recursive prerequisite expansion). */
      baneRange *range;
      int offset0;
      double (*answer)(struct baneMeasr_t *, double *, double *parm);
    } baneMeasr;
    
    /* -------------------- histogram volumes, etc. ---------------------- */
    
    /*
    ******** baneAxis struct
    **
    ** Information for how to do measurement and inclusion along each axis
    ** of the histogram volume.
    */
    typedef struct {
      unsigned int res;                     /* resolution = number of bins */
      baneMeasr *measr;
      baneInc *inc;
    } baneAxis;
    
    /*
    ******** baneHVolParm struct
    **
    ** Information for how to create a histogram volume.
    **
    */
    typedef struct {
      /* -------------- input */
      int verbose,                         /* status messages to stderr */
        makeMeasrVol,                      /* create a 3 x X x Y x Z volume of
                                              measurements, so that they aren't
                                              measured (as many as) three times */
        renormalize,                       /* use gage's mask renormalization */
        k3pack;
      const NrrdKernel *k[GAGE_KERNEL_MAX+1];
      double kparm[GAGE_KERNEL_MAX+1][NRRD_KERNEL_PARMS_NUM];
      baneClip *clip;
      double incLimit;                     /* lowest permissible fraction of the
                                              data remaining after new inclusion
                                              has been determined */
      baneAxis axis[3];
      /* -------------- internal */
      Nrrd *measrVol;
      int measrVolDone;                    /* values in measrVol are filled */
    } baneHVolParm;
    
    /* defaultsBane.c */
    BANE_EXPORT const char *baneBiffKey;
    BANE_EXPORT int baneDefVerbose;
    BANE_EXPORT int baneDefMakeMeasrVol;
    BANE_EXPORT double baneDefIncLimit;
    BANE_EXPORT int baneDefRenormalize;
    BANE_EXPORT int baneDefPercHistBins;
    BANE_EXPORT int baneStateHistEqBins;
    BANE_EXPORT int baneStateHistEqSmart;
    BANE_EXPORT int baneHack;
    
    /* rangeBane.c */
    BANE_EXPORT baneRange *baneRangeNew(int type);
    BANE_EXPORT baneRange *baneRangeCopy(baneRange *range);
    BANE_EXPORT int baneRangeAnswer(baneRange *range,
                                    double *ominP, double *omaxP,
                                    double imin, double imax);
    BANE_EXPORT baneRange *baneRangeNix(baneRange *range);
    
    /* inc.c */
    BANE_EXPORT baneInc *baneIncNew(int type, baneRange *range, double *parm);
    BANE_EXPORT void baneIncProcess(baneInc *inc, int passIdx, double val);
    BANE_EXPORT int baneIncAnswer(baneInc *inc, double *minP, double *maxP);
    BANE_EXPORT baneInc *baneIncCopy(baneInc *inc);
    BANE_EXPORT baneInc *baneIncNix(baneInc *inc);
    
    /* clip.c */
    BANE_EXPORT baneClip *baneClipNew(int type, double *parm);
    BANE_EXPORT int baneClipAnswer(int *countP, baneClip *clip, Nrrd *hvol);
    BANE_EXPORT baneClip *baneClipCopy(baneClip *clip);
    BANE_EXPORT baneClip *baneClipNix(baneClip *clip);
    
    /* measr.c */
    BANE_EXPORT baneMeasr *baneMeasrNew(int type, double *parm);
    BANE_EXPORT double baneMeasrAnswer(baneMeasr *measr, gageContext *gctx);
    BANE_EXPORT baneMeasr *baneMeasrCopy(baneMeasr *measr);
    BANE_EXPORT baneMeasr *baneMeasrNix(baneMeasr *measr);
    
    /* methodsBane.c */
    /* NOTE: this is NOT a complete API, like gage has.  Currently there
       is only API for things that have to be allocated internally */
    BANE_EXPORT const int banePresent;
    BANE_EXPORT baneHVolParm *baneHVolParmNew(void);
    BANE_EXPORT void baneHVolParmGKMSInit(baneHVolParm *hvp);
    BANE_EXPORT void baneHVolParmAxisSet(baneHVolParm *hvp, unsigned int axisIdx,
                                         unsigned int res,
                                         baneMeasr *measr, baneInc *inc);
    BANE_EXPORT void baneHVolParmClipSet(baneHVolParm *hvp, baneClip *clip);
    BANE_EXPORT baneHVolParm *baneHVolParmNix(baneHVolParm *hvp);
    
    /* valid.c */
    BANE_EXPORT int baneInputCheck(Nrrd *nin, baneHVolParm *hvp);
    BANE_EXPORT int baneHVolCheck(Nrrd *hvol);
    BANE_EXPORT int baneInfoCheck(Nrrd *info2D, int wantDim);
    BANE_EXPORT int banePosCheck(Nrrd *pos, int wantDim);
    BANE_EXPORT int baneBcptsCheck(Nrrd *Bcpts);
    
    /* hvol.c */
    BANE_EXPORT void baneProbe(double val[3],
                               Nrrd *nin, baneHVolParm *hvp, gageContext *ctx,
                               unsigned int x, unsigned int y, unsigned int z);
    BANE_EXPORT int baneFindInclusion(double min[3], double max[3],
                                      Nrrd *nin, baneHVolParm *hvp,
                                      gageContext *ctx);
    BANE_EXPORT int baneMakeHVol(Nrrd *hvol, Nrrd *nin, baneHVolParm *hvp);
    BANE_EXPORT Nrrd *baneGKMSHVol(Nrrd *nin, float gradPerc, float hessPerc);
    
    /* trnsf.c */
    BANE_EXPORT int baneOpacInfo(Nrrd *info, Nrrd *hvol, int dim, int measr);
    BANE_EXPORT int bane1DOpacInfoFrom2D(Nrrd *info1D, Nrrd *info2D);
    BANE_EXPORT int baneSigmaCalc(float *sP, Nrrd *info);
    BANE_EXPORT int banePosCalc(Nrrd *pos, float sigma, float gthresh, Nrrd *info);
    BANE_EXPORT void _baneOpacCalcA(unsigned int lutLen, float *opacLut,
                                    unsigned int numCpts, float *xo,
                                    float *pos);
    BANE_EXPORT void _baneOpacCalcB(unsigned int lutLen, float *opacLut,
                                    unsigned int numCpts, float *x, float *o,
                                    float *pos);
    BANE_EXPORT int baneOpacCalc(Nrrd *opac, Nrrd *Bcpts, Nrrd *pos);
    
    /* trex.c */
    BANE_EXPORT float *_baneTRexRead(char *fname);
    BANE_EXPORT void _baneTRexDone(void);
    
    /* scat.c */
    BANE_EXPORT int baneRawScatterplots(Nrrd *nvg, Nrrd *nvh, Nrrd *hvol,
                                        int histEq);
    
    /* gkms{Flotsam,Hvol,Scat,Pvg,Opac,Mite}.c */
    #define BANE_GKMS_DECLARE(C) BANE_EXPORT unrrduCmd baneGkms_##C##Cmd;
    #define BANE_GKMS_LIST(C) &baneGkms_##C##Cmd,
    #define BANE_GKMS_MAP(F) \
    F(hvol) \
    F(scat) \
    F(info) \
    F(pvg) \
    F(opac) \
    F(mite) \
    F(txf)
    BANE_GKMS_MAP(BANE_GKMS_DECLARE)
    BANE_EXPORT const airEnum *const baneGkmsMeasr;
    BANE_EXPORT unrrduCmd *baneGkmsCmdList[];
    BANE_EXPORT void baneGkmsUsage(const char *me, hestParm *hparm);
    BANE_EXPORT hestCB *baneGkmsHestIncStrategy;
    BANE_EXPORT hestCB *baneGkmsHestBEF;
    BANE_EXPORT hestCB *baneGkmsHestGthresh;
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* BANE_HAS_BEEN_INCLUDED */
    teem-1.11.0~svn6057/src/bane/gkmsOpac.c0000664000175000017500000001433312165631065017222 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    #include "bane.h"
    #include "privateBane.h"
    
    #define OPAC_INFO "Generate opacity functions"
    static const char *_baneGkms_opacInfoL =
      (OPAC_INFO
       ". Takes information from an \"info\" file and from a \"boundary "
       "emphasis function\" to generate 1D or 2D (depending on info file) "
       "opacity functions. ");
    int
    baneGkms_opacMain(int argc, const char **argv, const char *me,
                      hestParm *hparm) {
      hestOpt *opt = NULL;
      char *outS, *perr, *befS;
      Nrrd *ninfo, *nbef, *nout, *nmax, *npos, *nopac;
      airArray *mop;
      int pret, radius, idim;
      float sigma, gthrInfo[2], gthresh;
    
      hestOptAdd(&opt, "b", "bef", airTypeOther, 1, 1, &nbef, "1,1,0,1",
                 "boundary emphasis function mapping from \"position\" to "
                 "opacity. Can be either:\n "
                 "\b\bo filename of nrrd suitable for \"unu imap\", or:\n "
                 "\b\bo comma-separated list of four floats, with no spaces: "
                 "\"s,w,c,a\", where\n "
                 "s = shape of function, between 0.0 for box and "
                 "1.0 for tent\n "
                 "w = full-width half-max of function support\n "
                 "c = where to center function support\n "
                 "a = maximum opacity\n "
                 "If all goes well, the units for \"w\" and \"c\" are voxels.",
                 NULL, NULL, baneGkmsHestBEF);
      hestOptAdd(&opt, "s", "sigma", airTypeFloat, 1, 1, &sigma, "nan",
                 "scaling in position calculation, accounts for thickness "
                 "of transition region between materials. Lower sigmas lead to "
                 "wider peaks in opacity function. "
                 "Calculated automatically by default.");
      hestOptAdd(&opt, "g", "gthresh", airTypeOther, 1, 1, gthrInfo, "x0.04",
                 "minimum significant gradient magnitude.  Can be given "
                 "in two different ways:\n "
                 "\b\bo \"\": specify gthresh as  exactly.\n "
                 "\b\bo \"x\": gthresh is a scaling, by , of "
                 "the maximum gradient magnitude in the info file.",
                 NULL, NULL, baneGkmsHestGthresh);
      hestOptAdd(&opt, "r", "radius", airTypeInt, 1, 1, &radius, "0",
                 "radius of median filtering to apply to opacity function, "
                 "use \"0\" to signify no median filtering");
      hestOptAdd(&opt, "m", "befOut", airTypeString, 1, 1, &befS, "",
                 "if boundary emphasis function given via \"-b\" "
                 "is in the \"s,w,c,a\" form, then save out the "
                 "corresponding nrrd to , suitable for use in this "
                 "command or \"unu imap\"");
      hestOptAdd(&opt, "i", "infoIn", airTypeOther, 1, 1, &ninfo, NULL,
                 "input info file (from \"gkms info\")",
                 NULL, NULL, nrrdHestNrrd);
      hestOptAdd(&opt, "o", "opacOut", airTypeString, 1, 1, &outS, NULL,
                 "output 1D or 2D opacity function");
    
      mop = airMopNew();
      airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways);
      USAGE(_baneGkms_opacInfoL);
      PARSE();
      airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways);
      airMopAdd(mop, nmax=nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
      airMopAdd(mop, npos=nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
      airMopAdd(mop, nopac=nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
      airMopAdd(mop, nout=nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
    
      if (baneInfoCheck(ninfo, AIR_FALSE)) {
        biffAddf(BANE, "%s: didn't get a valid histogram info file", me);
        airMopError(mop); return 1;
      }
      idim = ninfo->dim-1;
      if (nbef->ptr && airStrlen(befS)) {
        if (nrrdSave(befS, nbef, NULL)) {
          biffMovef(BANE, NRRD, "%s: trouble saving boundary emphasis", me);
          airMopError(mop); return 1;
        }
      }
      if (!AIR_EXISTS(sigma)) {
        if (baneSigmaCalc(&sigma, ninfo)) {
          biffAddf(BANE, "%s: trouble calculating sigma", me);
          airMopError(mop); return 1;
        }
        fprintf(stderr, "%s: calculated sigma = %g\n", me, sigma);
      }
      if (0 == gthrInfo[0]) {
        gthresh = gthrInfo[1];
      } else {
        if (2 == idim) {
          gthresh = AIR_CAST(float, gthrInfo[1]*ninfo->axis[2].max);
        }
        else {
          if (nrrdProject(nmax, ninfo, 1, nrrdMeasureMax, nrrdTypeDefault)) {
            biffAddf(BANE, "%s: couldn't do max projection of 1D histo-info", me);
            airMopError(mop); return 1;
          }
          gthresh = gthrInfo[1]*nrrdFLookup[nmax->type](nmax->data, 0);
        }
        fprintf(stderr, "%s: calculated gthresh = %g\n", me, gthresh);
      }
      if (banePosCalc(npos, sigma, gthresh, ninfo)
          || baneOpacCalc(nopac, nbef, npos)) {
        biffAddf(BANE, "%s: trouble calculating position or opacity", me);
        airMopError(mop); return 1;
      }
      if (radius) {
        if (nrrdCheapMedian(nout, nopac, AIR_TRUE, AIR_FALSE, radius, 1.0, 2048)) {
          biffMovef(BANE, NRRD, "%s: error in median filtering", me);
          airMopError(mop); return 1;
        }
      } else {
        if (nrrdCopy(nout, nopac)) {
          biffMovef(BANE, NRRD, "%s: error in copying output", me);
          airMopError(mop); return 1;
        }
      }
      if (nrrdSave(outS, nout, NULL)) {
        biffMovef(BANE, NRRD, "%s: trouble saving opacity function", me);
        airMopError(mop); return 1;
      }
    
      airMopOkay(mop);
      return 0;
    }
    BANE_GKMS_CMD(opac, OPAC_INFO);
    
    teem-1.11.0~svn6057/src/bane/methodsBane.c0000664000175000017500000001046212165631065017706 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    
    #include "bane.h"
    #include "privateBane.h"
    
    const int
    banePresent = 42;
    
    void
    _baneAxisInit(baneAxis *axis) {
    
      axis->res = 0;
      axis->measr = NULL;
      axis->inc = NULL;
    }
    
    void
    _baneAxisEmpty(baneAxis *axis) {
    
      axis->measr = baneMeasrNix(axis->measr);
      axis->inc = baneIncNix(axis->inc);
    }
    
    baneHVolParm *
    baneHVolParmNew() {
      baneHVolParm *hvp;
      int i, j;
    
      hvp = (baneHVolParm *)calloc(1, sizeof(baneHVolParm));
      if (hvp) {
        hvp->verbose = baneDefVerbose;
        hvp->makeMeasrVol = baneDefMakeMeasrVol;
        hvp->measrVol = NULL;
        hvp->measrVolDone = AIR_FALSE;
        _baneAxisInit(hvp->axis + 0);
        _baneAxisInit(hvp->axis + 1);
        _baneAxisInit(hvp->axis + 2);
        hvp->k3pack = AIR_TRUE;
        for(i=gageKernelUnknown+1; ik[i] = NULL;
          for (j=0; jkparm[i][j] = AIR_NAN;
        }
        hvp->renormalize = baneDefRenormalize;
        hvp->clip = NULL;
        hvp->incLimit = baneDefIncLimit;
      }
      return hvp;
    }
    
    void
    baneHVolParmAxisSet(baneHVolParm *hvp, unsigned int axisIdx,
                        unsigned int res, baneMeasr *measr, baneInc *inc) {
    
      if (hvp && axisIdx <= 2) {
        _baneAxisEmpty(hvp->axis + axisIdx);
        hvp->axis[axisIdx].res = res;
        hvp->axis[axisIdx].measr = baneMeasrCopy(measr);
        hvp->axis[axisIdx].inc = baneIncCopy(inc);
      }
      return;
    }
    
    void
    baneHVolParmClipSet(baneHVolParm *hvp, baneClip *clip) {
    
      if (hvp && clip) {
        hvp->clip = baneClipNix(hvp->clip);
        hvp->clip = baneClipCopy(clip);
      }
      return;
    }
    
    baneHVolParm *
    baneHVolParmNix(baneHVolParm *hvp) {
    
      if (hvp) {
        if (hvp->measrVol) {
          nrrdNuke(hvp->measrVol);
        }
        _baneAxisEmpty(hvp->axis + 0);
        _baneAxisEmpty(hvp->axis + 1);
        _baneAxisEmpty(hvp->axis + 2);
        baneClipNix(hvp->clip);
        free(hvp);
      }
      return NULL;
    }
    
    /*
    ******** baneHVolParmGKMSInit()
    **
    ** The way Gordon does it.
    */
    void
    baneHVolParmGKMSInit(baneHVolParm *hvp) {
      baneMeasr *measr;
      baneInc *inc;
      double parm[BANE_PARM_NUM];
    
      if (hvp) {
        /* no parms to set */
        measr = baneMeasrNew(baneMeasrGradMag, parm);
        parm[0] = 1024;
        parm[1] = 0.15;
        inc = baneIncNew(baneIncPercentile, measr->range, parm);
        baneHVolParmAxisSet(hvp, 0, 256, measr, inc);
        measr = baneMeasrNix(measr);
        inc = baneIncNix(inc);
    
        /* no parms to set */
        measr = baneMeasrNew(baneMeasr2ndDD, parm);
        parm[0] = 1024;
        parm[1] = 0.25;
        inc = baneIncNew(baneIncPercentile, measr->range, parm);
        baneHVolParmAxisSet(hvp, 1, 256, measr, inc);
        measr = baneMeasrNix(measr);
        inc = baneIncNix(inc);
    
        /* no parms to set */
        measr = baneMeasrNew(baneMeasrValueAnywhere, parm);
        parm[0] = 1.0;
        inc = baneIncNew(baneIncRangeRatio, measr->range, parm);
        baneHVolParmAxisSet(hvp, 2, 256, measr, inc);
        measr = baneMeasrNix(measr);
        inc = baneIncNix(inc);
    
        nrrdKernelParse(&(hvp->k[gageKernel00]), hvp->kparm[gageKernel00],
                        "cubic:0,0.5");  /* catmull-rom */
        nrrdKernelParse(&(hvp->k[gageKernel11]), hvp->kparm[gageKernel11],
                        "cubicd:1,0");   /* b-spline */
        nrrdKernelParse(&(hvp->k[gageKernel22]), hvp->kparm[gageKernel22],
                        "cubicdd:1,0");  /* b-spline */
      }
    }
    teem-1.11.0~svn6057/src/bane/gkmsMite.c0000664000175000017500000000616312165631065017240 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    #include "bane.h"
    #include "privateBane.h"
    
    #define MITE_INFO "Modify opacity function to work with \"mite\""
    static const char *_baneGkms_miteInfoL =
      (MITE_INFO
       ". Useful when using the \"mite\" Teem library, or the \"miter\" "
       "command-line renderer.  This adds a \"stub\" axis 0, and setting the "
       "axis labels to identify the domain and range of the opacity function. "
       "The underlying opacity function is not modified.");
    int
    baneGkms_miteMain(int argc, const char **argv, const char *me,
                      hestParm *hparm) {
      hestOpt *opt = NULL;
      char *out, *perr;
      Nrrd *nin, *nout;
      airArray *mop;
      int pret, E;
    
      hestOptAdd(&opt, "i", "opacIn", airTypeOther, 1, 1, &nin, NULL,
                 "input opacity function (1 or 2 dimensional), from "
                 "\"gkms opac\"",
                 NULL, NULL, nrrdHestNrrd);
      hestOptAdd(&opt, "o", "opacOut", airTypeString, 1, 1, &out, NULL,
                 "output opacity function filename");
    
      mop = airMopNew();
      airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways);
      USAGE(_baneGkms_miteInfoL);
      PARSE();
      airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways);
    
      if (1 == nin->axis[0].size && nin->axis[0].label &&
          !strcmp("A", nin->axis[0].label)) {
        fprintf(stderr, "%s: already\n", me);
        nout = nin;
      } else {
        nout = nrrdNew();
        airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways);
        E = 0;
        if (!E) E |= nrrdAxesInsert(nout, nin, 0);
        if (!E) E |= !(nout->axis[0].label = airStrdup("A"));
        if (!E) E |= !(nout->axis[1].label = airStrdup("gage(v)"));
        if (3 == nout->dim) {
          if (!E) E |= !(nout->axis[2].label = airStrdup("gage(gm)"));
        }
        if (E) {
          biffMovef(BANE, NRRD,
                    "%s: trouble modifying opacity function nrrd", me);
          airMopError(mop); return 1;
        }
      }
      if (nrrdSave(out, nout, NULL)) {
        biffMovef(BANE, NRRD, "%s: trouble saving opacity function", me);
        airMopError(mop); return 1;
      }
    
      airMopOkay(mop);
      return 0;
    }
    BANE_GKMS_CMD(mite, MITE_INFO);
    
    teem-1.11.0~svn6057/src/bane/gkmsScat.c0000664000175000017500000001002512165631065017224 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    #include "bane.h"
    #include "privateBane.h"
    
    #define SCAT_INFO "Make V-G and V-H scatterplots"
    static const char *_baneGkms_scatInfoL =
      (SCAT_INFO
       ". These provide a quick way to inspect a histogram volume, in order to "
       "verify that the derivative inclusion ranges were appropriate, and to "
       "get an initial sense of what sorts of boundaries were present in the "
       "original volume.");
    int
    baneGkms_scatMain(int argc, const char **argv, const char *me,
                      hestParm *hparm) {
      hestOpt *opt = NULL;
      char *out[2], *perr;
      Nrrd *hvol, *nvgRaw, *nvhRaw, *nvgQuant, *nvhQuant;
      NrrdRange *vgRange, *vhRange;
      airArray *mop;
      int pret, E;
      double gamma;
    
      hestOptAdd(&opt, "g", "gamma", airTypeDouble, 1, 1, &gamma, "1.0",
                 "gamma used to brighten/darken scatterplots. "
                 "gamma > 1.0 brightens; gamma < 1.0 darkens. "
                 "Negative gammas invert values (like in xv). ");
      hestOptAdd(&opt, "i", "hvolIn", airTypeOther, 1, 1, &hvol, NULL,
                 "input histogram volume (from \"gkms hvol\")",
                 NULL, NULL, nrrdHestNrrd);
      hestOptAdd(&opt, "o", "vgOut vhOut", airTypeString, 2, 2, out, NULL,
                 "Filenames to use for two output scatterplots, (gradient "
                 "magnitude versus value, and 2nd derivative versus value); "
                 "can use PGM or PNG format");
    
      mop = airMopNew();
      airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways);
      USAGE(_baneGkms_scatInfoL);
      PARSE();
      airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways);
    
      nvgRaw = nrrdNew();
      nvhRaw = nrrdNew();
      nvgQuant = nrrdNew();
      nvhQuant = nrrdNew();
      airMopAdd(mop, nvgRaw, (airMopper)nrrdNuke, airMopAlways);
      airMopAdd(mop, nvhRaw, (airMopper)nrrdNuke, airMopAlways);
      airMopAdd(mop, nvgQuant, (airMopper)nrrdNuke, airMopAlways);
      airMopAdd(mop, nvhQuant, (airMopper)nrrdNuke, airMopAlways);
      if (baneRawScatterplots(nvgRaw, nvhRaw, hvol, AIR_TRUE)) {
        biffAddf(BANE, "%s: trouble creating raw scatterplots", me);
        airMopError(mop); return 1;
      }
      vgRange = nrrdRangeNewSet(nvgRaw, nrrdBlind8BitRangeFalse);
      vhRange = nrrdRangeNewSet(nvhRaw, nrrdBlind8BitRangeFalse);
      airMopAdd(mop, vgRange, (airMopper)nrrdRangeNix, airMopAlways);
      airMopAdd(mop, vhRange, (airMopper)nrrdRangeNix, airMopAlways);
      E = 0;
      if (!E) E |= nrrdArithGamma(nvgRaw, nvgRaw, vgRange, gamma);
      if (!E) E |= nrrdArithGamma(nvhRaw, nvhRaw, vhRange, gamma);
      if (!E) E |= nrrdQuantize(nvgQuant, nvgRaw, vgRange, 8);
      if (!E) E |= nrrdQuantize(nvhQuant, nvhRaw, vhRange, 8);
      if (E) {
        biffMovef(BANE, NRRD, "%s: trouble doing gamma or quantization", me);
        airMopError(mop); return 1;
      }
    
      if (!E) E |= nrrdSave(out[0], nvgQuant, NULL);
      if (!E) E |= nrrdSave(out[1], nvhQuant, NULL);
      if (E) {
        biffMovef(BANE, NRRD, "%s: trouble saving scatterplot images", me);
        airMopError(mop); return 1;
      }
    
      airMopOkay(mop);
      return 0;
    }
    BANE_GKMS_CMD(scat, SCAT_INFO);
    
    teem-1.11.0~svn6057/src/bane/gkmsTxf.c0000664000175000017500000001340612165631065017101 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    #include "bane.h"
    #include "privateBane.h"
    
    #define TXF_INFO "Create Levoy-style triangular 2D opacity functions"
    static const char *_baneGkms_txfInfoL =
      (TXF_INFO
       ". The triangles are in the 2D space of data value and gradient "
       "magnitude.  They can be tilted sideways and clipped at the bottom. "
       "This doesn't strictly speaking belong in \"gkms\" but there's no "
       "other good place in Teem.");
    int
    baneGkms_txfMain(int argc, const char **argv, const char *me,
                     hestParm *hparm) {
      hestOpt *opt = NULL;
      char *out, *perr;
      Nrrd *nout;
      airArray *mop;
      int pret, E, res[2], vi, gi, step;
      float min[2], max[2], top[2], v0, g0, *data, v, g,
        gwidth, width, mwidth,
        tvl, tvr, vl, vr, tmp, maxa;
    
      hestOptAdd(&opt, "r", "Vres Gres", airTypeInt, 2, 2, res, "256 256",
                 "resolution of the transfer function in value and gradient "
                 "magnitude");
      hestOptAdd(&opt, "min", "Vmin Gmin", airTypeFloat, 2, 2, min, "0.0 0.0",
                 "minimum value and grad mag in txf");
      hestOptAdd(&opt, "max", "Vmax Gmax", airTypeFloat, 2, 2, max, NULL,
                 "maximum value and grad mag in txf");
      hestOptAdd(&opt, "v", "base value", airTypeFloat, 1, 1, &v0, NULL,
                 "data value at which to position bottom of triangle");
      hestOptAdd(&opt, "g", "gthresh", airTypeFloat, 1, 1, &g0, "0.0",
                 "lowest grad mag to receive opacity");
      hestOptAdd(&opt, "gw", "gwidth", airTypeFloat, 1, 1, &gwidth, "0.0",
                 "range of grad mag values over which to apply threshold "
                 "at low gradient magnitudes");
      hestOptAdd(&opt, "top", "Vtop Gtop", airTypeFloat, 2, 2, top, NULL,
                 "data value and grad mag at center of top of triangle");
      hestOptAdd(&opt, "w", "value width", airTypeFloat, 1, 1, &width, NULL,
                 "range of values to be spanned at top of triangle");
      hestOptAdd(&opt, "mw", "value width", airTypeFloat, 1, 1, &mwidth, "0",
                 "range of values to be spanned at BOTTOM of triangle");
      hestOptAdd(&opt, "step", NULL, airTypeInt, 0, 0, &step, NULL,
                 "instead of assigning opacity inside a triangular region, "
                 "make it more like a step function, in which opacity never "
                 "decreases in increasing data value");
      hestOptAdd(&opt, "a", "max opac", airTypeFloat, 1, 1, &maxa, "1.0",
                 "highest opacity to assign");
      hestOptAdd(&opt, "o", "opacOut", airTypeString, 1, 1, &out, NULL,
                 "output opacity function filename");
    
      mop = airMopNew();
      airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways);
      USAGE(_baneGkms_txfInfoL);
      PARSE();
      airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways);
    
      nout = nrrdNew();
      airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways);
      E = 0;
      if (!E) E |= nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 3,
                                     AIR_CAST(size_t, 1),
                                     AIR_CAST(size_t, res[0]),
                                     AIR_CAST(size_t, res[1]));
      if (!E) E |= !(nout->axis[0].label = airStrdup("A"));
      if (!E) E |= !(nout->axis[1].label = airStrdup("gage(scalar:v)"));
      if (!E) nrrdAxisInfoSet_va(nout, nrrdAxisInfoMin,
                                 AIR_NAN, (double)min[0], (double)min[1]);
      if (!E) nrrdAxisInfoSet_va(nout, nrrdAxisInfoMax,
                                 AIR_NAN, (double)max[0], (double)max[1]);
      if (!E) E |= !(nout->axis[2].label = airStrdup("gage(scalar:gm)"));
      if (E) {
        biffMovef(BANE, NRRD, "%s: trouble creating opacity function nrrd", me);
        airMopError(mop); return 1;
      }
      data = (float *)nout->data;
      tvl = top[0] - width/2;
      tvr = top[0] + width/2;
      mwidth /= 2;
      for (gi=0; gi top[1]) {
            data[vi + res[0]*gi] = 0;
            continue;
          }
          tmp = AIR_CAST(float, (v - vl)/(0.00001 + vr - vl));
          tmp = 1 - AIR_ABS(2*tmp - 1);
          if (step && v > (vr + vl)/2) {
            tmp = 1;
          }
          tmp = AIR_MAX(0, tmp);
          data[vi + res[0]*gi] = tmp*maxa;
          tmp = AIR_CAST(float, AIR_AFFINE(g0 - gwidth/2, g, g0 + gwidth/2,
                                           0.0, 1.0));
          tmp = AIR_CLAMP(0, tmp, 1);
          data[vi + res[0]*gi] *= tmp;
        }
      }
      if (nrrdSave(out, nout, NULL)) {
        biffMovef(BANE, NRRD, "%s: trouble saving opacity function", me);
        airMopError(mop); return 1;
      }
    
      airMopOkay(mop);
      return 0;
    }
    BANE_GKMS_CMD(txf, TXF_INFO);
    
    teem-1.11.0~svn6057/src/bane/trex.c0000664000175000017500000000440012165631065016432 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    
    #include "bane.h"
    
    /* learned:
    ** NEVER EVER EVER bypass your own damn pseudo-constructors!
    ** "npos" used to be a Nrrd (not a pointer), and Joe's
    ** trex stuff was crashing because the if data free(data) in nrrd Alloc
    ** was freeing random stuff, but (and this is the weird part)
    ** only on some 1-D nrrds of 256 floats (pos1D info), and not others.
    */
    Nrrd *baneNpos=NULL;
    
    #define TREX_LUTLEN 256
    
    float _baneTesting[256]={0};
    
    float *
    _baneTRexRead(char *fname) {
      char me[]="_baneTRexRead";
    
      if (nrrdLoad(baneNpos=nrrdNew(), fname, NULL)) {
        fprintf(stderr, "%s: !!! trouble reading \"%s\":\n%s\n", me,
                fname, biffGet(NRRD));
        return NULL;
      }
      if (banePosCheck(baneNpos, 1)) {
        fprintf(stderr, "%s: !!! didn't get a valid p(x) file:\n%s\n", me,
                biffGet(BANE));
        return NULL;
      }
      if (TREX_LUTLEN != baneNpos->axis[0].size) {
        char stmp[AIR_STRLEN_SMALL];
        fprintf(stderr, "%s: !!! need a length %d p(x) (not %s)\n", me,
                TREX_LUTLEN, airSprintSize_t(stmp, baneNpos->axis[0].size));
        return NULL;
      }
    
      return (float *)baneNpos->data;
    }
    
    void
    _baneTRexDone() {
    
      nrrdNuke(baneNpos);
    }
    teem-1.11.0~svn6057/src/bane/defaultsBane.c0000664000175000017500000000311012165631065020042 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    #include "bane.h"
    
    const char *
    baneBiffKey = "bane";
    
    int
    baneDefVerbose = 0;
    
    int
    baneDefMakeMeasrVol = AIR_TRUE;
    
    double
    baneDefIncLimit = 0.80;  /* throwing away more than 20% is too much */
    
    int
    baneDefRenormalize = AIR_TRUE;
    
    int
    baneDefPercHistBins = 1024;
    
    int
    baneStateHistEqBins = 4096;
    
    int
    baneStateHistEqSmart = 2;
    
    int
    baneHack = 0;
    
    const char
    baneDefLHKernel00[] = "cubic:0.333,0.333";
    
    const char
    baneDefLHKernel11[] = "cubicd:0.333,0.333";
    teem-1.11.0~svn6057/src/bane/GNUmakefile0000664000175000017500000000400412165631065017356 0ustar  domibeldomibel#
    # Teem: Tools to process and visualize scientific data and images
    # Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
    # Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
    # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    #
    # This library is free software; you can redistribute it and/or
    # modify it under the terms of the GNU Lesser General Public License
    # (LGPL) as published by the Free Software Foundation; either
    # version 2.1 of the License, or (at your option) any later version.
    # The terms of redistributing and/or modifying this software also
    # include exceptions to the LGPL that facilitate static linking.
    #
    # This library is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    # Lesser General Public License for more details.
    #
    # You should have received a copy of the GNU Lesser General Public License
    # along with this library; if not, write to Free Software Foundation, Inc.,
    # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    #
    
    #### Library name
    ####
    ####
    L := bane
    ####
    ####
    ####
    
    # boilerplate: default targets and include tricks
    TEEM_ROOT ?= ../..
    TEEM_SRC ?= ..
    ifeq (,$(DEF_TARGETS))
    DEF_TARGETS = true
    dev     : $(L)/dev
    install : $(L)/install
    clean   : $(L)/clean
    clobber : $(L)/clobber
    include ../GNUmakefile
    endif
    ifeq (,$($(L).SEEN))
    $(L).SEEN := true
    
    #### Describe library here
    ####
    ####
    $(L).NEED = gage unrrdu nrrd biff air
    $(L).PUBLIC_HEADERS = bane.h
    $(L).PRIVATE_HEADERS = privateBane.h
    $(L).OBJS = defaultsBane.o rangeBane.o inc.o clip.o measr.o methodsBane.o \
    	valid.o trnsf.o scat.o hvol.o trex.o \
    	gkmsFlotsam.o \
    	gkmsHvol.o gkmsScat.o gkmsInfo.o gkmsPvg.o gkmsOpac.o \
    	gkmsMite.o gkmsTxf.o
    $(L).TESTS = test/tinfo test/sigma test/pos \
    	test/opac 
    ####
    ####
    ####
    
    # boilerplate: declare rules for this library
    include $(TEEM_SRC)/make/template.mk
    endif
    ifeq (,$(INCLUDED))
      include $(TEEM_SRC)/bin/GNUmakefile
    endif
    teem-1.11.0~svn6057/src/bane/sources.cmake0000664000175000017500000000063111113047450017762 0ustar  domibeldomibel# This variable will help provide a master list of all the sources.
    # Add new source files here.
    SET(BANE_SOURCES
      bane.h
      clip.c
      defaultsBane.c
      gkmsFlotsam.c
      gkmsHvol.c
      gkmsInfo.c
      gkmsMite.c
      gkmsOpac.c
      gkmsPvg.c
      gkmsScat.c
      gkmsTxf.c
      hvol.c
      inc.c
      measr.c
      methodsBane.c
      privateBane.h
      rangeBane.c
      scat.c
      trex.c
      trnsf.c
      valid.c
      )
    
    ADD_TEEM_LIBRARY(bane ${BANE_SOURCES})
    teem-1.11.0~svn6057/src/bane/gkmsInfo.c0000664000175000017500000000613612165631065017235 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    #include "bane.h"
    #include "privateBane.h"
    
    #define INFO_INFO "Project histogram volume for opacity function generation"
    static const char *_baneGkms_infoInfoL =
      (INFO_INFO
       ". This distills the histogram volume down to the information required "
       "to create either 1-D or 2-D opacity functions.");
    
    int
    baneGkms_infoMain(int argc, const char **argv, const char *me,
                      hestParm *hparm) {
      hestOpt *opt = NULL;
      char *outS, *perr;
      Nrrd *hvol, *nout;
      airArray *mop;
      int pret, one, measr;
    
      hestOptAdd(&opt, "m", "measr", airTypeEnum, 1, 1, &measr, "mean",
                 "How to project along the 2nd derivative axis.  Possibilities "
                 "include:\n "
                 "\b\bo \"mean\": average value\n "
                 "\b\bo \"median\": value at 50th percentile\n "
                 "\b\bo \"mode\": most common value\n "
                 "\b\bo \"min\", \"max\": probably not useful",
                 NULL, baneGkmsMeasr);
      hestOptAdd(&opt, "one", NULL, airTypeInt, 0, 0, &one, NULL,
                 "Create 1-dimensional info file; default is 2-dimensional");
      hestOptAdd(&opt, "i", "hvolIn", airTypeOther, 1, 1, &hvol, NULL,
                 "input histogram volume (from \"gkms hvol\")",
                 NULL, NULL, nrrdHestNrrd);
      hestOptAdd(&opt, "o", "infoOut", airTypeString, 1, 1, &outS, NULL,
                 "output info file, used by \"gkms pvg\" and \"gkms opac\"");
    
      mop = airMopNew();
      airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways);
      USAGE(_baneGkms_infoInfoL);
      PARSE();
      airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways);
      nout = nrrdNew();
      airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways);
    
      if (baneOpacInfo(nout, hvol, one ? 1 : 2, measr)) {
        biffAddf(BANE, "%s: trouble distilling histogram info", me);
        airMopError(mop); return 1;
      }
    
      if (nrrdSave(outS, nout, NULL)) {
        biffMovef(BANE, NRRD, "%s: trouble saving info file", me);
        airMopError(mop); return 1;
      }
    
      airMopOkay(mop);
      return 0;
    }
    BANE_GKMS_CMD(info, INFO_INFO);
    
    teem-1.11.0~svn6057/src/bane/privateBane.h0000664000175000017500000000410412173673632017723 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    #ifndef BANE_PRIVATE_HAS_BEEN_INCLUDED
    #define BANE_PRIVATE_HAS_BEEN_INCLUDED
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /* hvol.c */
    extern int _baneAxisCheck(baneAxis *axis);
    
    #define BANE_GKMS_CMD(name, info) \
    unrrduCmd baneGkms_##name##Cmd = { #name, info, \
                                       baneGkms_##name##Main, AIR_FALSE }
    
    /* USAGE, PARSE
       all copied from unrrdu/privateUnrrdu.h */
    #define USAGE(info) \
      if (!argc) { \
        hestInfo(stderr, me, (info), hparm); \
        hestUsage(stderr, opt, me, hparm); \
        hestGlossary(stderr, opt, hparm); \
        airMopError(mop); \
        return 2; \
      }
    
    #define PARSE() \
      if ((pret=hestParse(opt, argc, argv, &perr, hparm))) { \
        if (1 == pret) { \
          fprintf(stderr, "%s: %s\n", me, perr); free(perr); \
          hestUsage(stderr, opt, me, hparm); \
          airMopError(mop); \
          return 2; \
        } else { \
          exit(1); \
        } \
      }
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* BANE_PRIVATE_HAS_BEEN_INCLUDED */
    teem-1.11.0~svn6057/src/bane/valid.c0000664000175000017500000001445012165631065016555 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    
    #include "bane.h"
    #include "privateBane.h"
    
    int
    baneInputCheck (Nrrd *nin, baneHVolParm *hvp) {
      static const char me[]="baneInputCheck";
      int i;
    
      if (nrrdCheck(nin)) {
        biffMovef(BANE, NRRD, "%s: basic nrrd validity check failed", me);
        return 1;
      }
      if (3 != nin->dim) {
        biffAddf(BANE, "%s: need a 3-dimensional nrrd (not %d)", me, nin->dim);
        return 1;
      }
      if (nrrdTypeBlock == nin->type) {
        biffAddf(BANE, "%s: can't operate on block type", me);
        return 1;
      }
      if (!( AIR_EXISTS(nin->axis[0].spacing) && nin->axis[0].spacing != 0 &&
             AIR_EXISTS(nin->axis[1].spacing) && nin->axis[1].spacing != 0 &&
             AIR_EXISTS(nin->axis[2].spacing) && nin->axis[2].spacing != 0 )) {
        biffAddf(BANE, "%s: must have non-zero existent spacing for all 3 axes",
                 me);
        return 1;
      }
      for (i=0; i<=2; i++) {
        if (_baneAxisCheck(hvp->axis + i)) {
          biffAddf(BANE, "%s: trouble with axis %d", me, i);
          return 1;
        }
      }
      if (!hvp->clip) {
        biffAddf(BANE, "%s: got NULL baneClip", me);
        return 1;
      }
    
      /* all okay */
      return 0;
    }
    
    int
    baneHVolCheck (Nrrd *hvol) {
      static const char me[]="baneHVolCheck";
    
      if (3 != hvol->dim) {
        biffAddf(BANE, "%s: need dimension to be 3 (not %d)", me, hvol->dim);
        return 1;
      }
      if (nrrdTypeUChar != hvol->type) {
        biffAddf(BANE, "%s: need type to be %s (not %s)",
                 me, airEnumStr(nrrdType, nrrdTypeUChar),
                 airEnumStr(nrrdType, hvol->type));
        return 1;
      }
      if (!( AIR_EXISTS(hvol->axis[0].min) && AIR_EXISTS(hvol->axis[0].max) &&
             AIR_EXISTS(hvol->axis[1].min) && AIR_EXISTS(hvol->axis[1].max) &&
             AIR_EXISTS(hvol->axis[2].min) && AIR_EXISTS(hvol->axis[2].max) )) {
        biffAddf(BANE, "%s: axisMin and axisMax must be set for all axes", me);
        return 1;
      }
      /*
      ** NOTE: For the time being, I'm giving up on enforcing a
      ** particular kind of histogram volume
      if (strcmp(hvol->axis[0].label, baneMeasrGradMag->name)) {
        biffAddf(BANE, "%s: expected \"%s\" on axis 0 label",
                 me, baneMeasrGradMag->name);
        return 1;
      }
      if (strcmp(hvol->axis[1].label, baneMeasrLapl->name) &&
          strcmp(hvol->axis[1].label, baneMeasrHess->name)) {
        biffAddf(BANE, "%s: expected a 2nd deriv. measr on axis 1 (%s or %s)",
                 me, baneMeasrHess->name, baneMeasrLapl->name);
        return 1;
      }
      if (strcmp(hvol->axis[2].label, baneMeasrVal->name)) {
        biffAddf(BANE, "%s: expected \"%s\" on axis 2",
                 me, baneMeasrVal->name);
        return 1;
      }
      */
      return 0;
    }
    
    int
    baneInfoCheck (Nrrd *info, int wantDim) {
      static const char me[]="baneInfoCheck";
      int gotDim;
    
      if (!info) {
        biffAddf(BANE, "%s: got NULL pointer", me);
        return 1;
      }
      gotDim = info->dim;
      if (wantDim) {
        if (!(1 == wantDim || 2 == wantDim)) {
          biffAddf(BANE, "%s: wantDim should be 1 or 2, not %d", me, wantDim);
          return 1;
        }
        if (wantDim+1 != gotDim) {
          biffAddf(BANE, "%s: dim is %d, not %d", me, gotDim, wantDim+1);
          return 1;
        }
      }
      else {
        if (!(2 == gotDim || 3 == gotDim)) {
          biffAddf(BANE, "%s: dim is %d, not 2 or 3", me, gotDim);
          return 1;
        }
      }
      if (nrrdTypeFloat != info->type) {
        biffAddf(BANE, "%s: need data of type float", me);
        return 1;
      }
      if (2 != info->axis[0].size) {
        char stmp[AIR_STRLEN_SMALL];
        biffAddf(BANE, "%s: 1st axis needs size 2 (not %s)", me,
                 airSprintSize_t(stmp, info->axis[0].size));
        return 1;
      }
      return 0;
    }
    
    int
    banePosCheck (Nrrd *pos, int wantDim) {
      static const char me[]="banePosCheck";
      int gotDim;
    
      if (!pos) {
        biffAddf(BANE, "%s: got NULL pointer", me);
        return 1;
      }
      gotDim = pos->dim;
      if (wantDim) {
        if (!(1 == wantDim || 2 == wantDim)) {
          biffAddf(BANE, "%s: wantDim should be 1 or 2, not %d", me, wantDim);
          return 1;
        }
        if (wantDim != gotDim) {
          biffAddf(BANE, "%s: dim is %d, not %d", me, gotDim, wantDim);
          return 1;
        }
      }
      else {
        if (!(1 == gotDim || 2 == gotDim)) {
          biffAddf(BANE, "%s: dim is %d, not 1 or 2", me, gotDim);
          return 1;
        }
      }
      if (nrrdTypeFloat != pos->type) {
        biffAddf(BANE, "%s: need data of type float", me);
        return 1;
      }
      /* HEY? check for values in axisMin[0] and axisMax[0] ? */
      /* HEY? check for values in axisMin[0] and axisMax[0] ? */
      /* HEY? check for values in axisMin[1] and axisMax[1] ? */
      return 0;
    }
    
    int
    baneBcptsCheck (Nrrd *Bcpts) {
      static const char me[]="baneBcptsCheck";
      int i, len;
      float *data;
    
      if (2 != Bcpts->dim) {
        biffAddf(BANE, "%s: need 2-dimensional (not %d)", me, Bcpts->dim);
        return 1;
      }
      if (2 != Bcpts->axis[0].size) {
        char stmp[AIR_STRLEN_SMALL];
        biffAddf(BANE, "%s: axis#0 needs size 2 (not %s)", me,
                 airSprintSize_t(stmp, Bcpts->axis[0].size));
        return 1;
      }
      if (nrrdTypeFloat != Bcpts->type) {
        biffAddf(BANE, "%s: need data of type float", me);
        return 1;
      }
      len = Bcpts->axis[1].size;
      data = (float *)Bcpts->data;
      for (i=0; i<=len-2; i++) {
        if (!(data[0 + 2*i] <= data[0 + 2*(i+1)])) {
          biffAddf(BANE, "%s: value coord %d (%g) not <= coord %d (%g)", me,
                   i, data[0 + 2*i], i+1, data[0 + 2*(i+1)]);
          return 1;
        }
      }
      return 0;
    }
    
    teem-1.11.0~svn6057/src/bane/gkmsPvg.c0000664000175000017500000001732412165631065017077 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    #include "bane.h"
    #include "privateBane.h"
    
    float _baneGkmsDonData[] = {0 /* AIR_NEG_INF */ , 0, 0, 0,
                                0 /* AIR_NAN */ , 0, 0, 0,
                                0 /* AIR_POS_INF */ , 0, 0, 0,
                                -9.5, 0, 107, 255,   /* (3) start: blue */
                                -8.5, 51, 104, 255,
                                -7.5, 103, 117, 255,
                                -6.5, 123, 124, 255,
                                -5.5, 141, 130, 255,
                                -4.5, 156, 132, 255,
                                -3.5, 166, 131, 245,
                                -2.5, 174, 131, 231,
                                -1.5, 181, 130, 216,
                                -0.5, 255, 255, 255, /* (12) middle */
                                0.5,  255, 255, 255, /* (13) middle */
                                1.5,  192, 129, 186,
                                2.5,  197, 128, 172,
                                3.5,  200, 128, 158,
                                4.5,  204, 127, 142,
                                5.5,  210, 126, 113,
                                6.5,  212, 126, 98,
                                7.5,  213, 126, 84,
                                8.5,  216, 126, 49,
                                9.5, 220, 133, 0};  /* (22) end: orange */
    #define INVERT(d,i) \
        (d)[1 + (i)*4] = 255 - (d)[1 + (i)*4]; \
        (d)[2 + (i)*4] = 255 - (d)[2 + (i)*4]; \
        (d)[3 + (i)*4] = 255 - (d)[3 + (i)*4]
    #define PVG_HISTEQ_BINS 2048
    
    Nrrd *
    _baneGkmsDonNew(int invert) {
      static const char me[]="_baneGkmsDonNew";
      Nrrd *ret;
      float *data;
    
      if (nrrdMaybeAlloc_va(ret=nrrdNew(), nrrdTypeFloat, 2,
                            AIR_CAST(size_t, 4), AIR_CAST(size_t, 23))) {
        biffAddf(BANE, "%s: can't create output", me);
        return NULL;
      }
      data = (float *)ret->data;
      memcpy(data, _baneGkmsDonData, 4*23*sizeof(float));
      data[0 + 4*0] = AIR_NEG_INF;
      data[0 + 4*1] = AIR_NAN;
      data[0 + 4*2] = AIR_POS_INF;
      if (invert) {
        INVERT(data, 0);
        INVERT(data, 1);
        INVERT(data, 2);
        INVERT(data, 12);
        INVERT(data, 13);
      }
      return ret;
    }
    
    #define PVG_INFO "Create color-mapped pictures of p(v,g)"
    static const char *_baneGkms_pvgInfoL =
      (PVG_INFO
       ".  This produces a qualitative visualization of the boundary information "
       "that was captured in the histogram volume.  The quantity shown is called "
       "the \"position function\" in GK's published work, but a better term "
       "would be \"distance map\", as a function of value (v) and gradient "
       "magnitude (g).");
    int
    baneGkms_pvgMain(int argc, const char **argv, const char *me,
                     hestParm *hparm) {
      hestOpt *opt = NULL;
      char *outS, *perr, *mapS;
      Nrrd *ninfo, *nposA, *nposB, *ndon, *npvg;
      NrrdIoState *nio;
      airArray *mop;
      int i, pret, invert, sv, sg, smlI;
      float *pos, p, min, max, sml, newsml, newmin, newmax;
      NrrdRange *range;
    
      hestOptAdd(&opt, "inv", NULL, airTypeInt, 0, 0, &invert, NULL,
                 "Draw on white background, instead of black");
      hestOptAdd(&opt, "m", "mapOut", airTypeString, 1, 1, &mapS, "",
                 "save out the colormap used here, so that it can be applied "
                 "to other nrrds with \"unu imap -r\"");
      hestOptAdd(&opt, "i", "infoIn", airTypeOther, 1, 1, &ninfo, NULL,
                 "input info file (from \"gkms info\")",
                 NULL, NULL, nrrdHestNrrd);
      hestOptAdd(&opt, "o", "imageOut", airTypeString, 1, 1, &outS, NULL,
                 "output image, in PPM format");
    
      mop = airMopNew();
      airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways);
      USAGE(_baneGkms_pvgInfoL);
      PARSE();
      airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways);
      airMopAdd(mop, ndon=_baneGkmsDonNew(invert),
                (airMopper)nrrdNuke, airMopAlways);
      airMopAdd(mop, nposA=nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
      airMopAdd(mop, nposB=nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
      airMopAdd(mop, npvg=nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
      airMopAdd(mop, nio=nrrdIoStateNew(), (airMopper)nrrdIoStateNix,
                airMopAlways);
    
      if (airStrlen(mapS)) {
        if (nrrdSave(mapS, ndon, NULL)) {
          biffMovef(BANE, NRRD, "%s: trouble saving colormap", me);
          airMopError(mop); return 1;
        }
      }
    
      /* we use sigma = 1.0: different sigmas will scale the position,
         which will not change the coloring
         gthresh = 0.0: we want to see everything, and Simian has taught
         us that there can be boundaries at extremely low gradients */
      if (banePosCalc(nposA, 1.0, 0.0, ninfo)) {
        biffAddf(BANE, "%s: trouble calculating position", me);
        airMopError(mop); return 1;
      }
      sv = nposA->axis[0].size;
      sg = nposA->axis[1].size;
      pos = (float *)nposA->data;
    
      /* find min, max, sml, smlI: histo-eq will warp values around such
         that min-->min and max-->max, but 0-->??.  So, find smallest
         magnitide position (sml) as a stand-in for 0.0 and its index
         (smlI) */
      sml = 0;
      smlI = 0;
      min = max = AIR_NAN;
      for (i=0; idata;
      newsml = pos[smlI];
      if (min < -max) {
        newmin = min;
        newmax = -min;
      } else {
        newmin = -max;
        newmax = max;
      }
      for (i=0; iformat = nrrdFormatPNM;
      if (nrrdSave(outS, npvg, nio)) {
        biffMovef(BANE, NRRD, "%s: trouble saving pvg image", me);
        airMopError(mop); return 1;
      }
      airMopOkay(mop);
      return 0;
    }
    BANE_GKMS_CMD(pvg, PVG_INFO);
    
    teem-1.11.0~svn6057/src/bane/measr.c0000664000175000017500000001374612165631065016574 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    #include "bane.h"
    #include "privateBane.h"
    
    double
    _baneMeasr_StockAnswer(baneMeasr *measr, double *san, double *parm) {
    
      AIR_UNUSED(parm);
      return san[measr->offset0];
    }
    
    baneMeasr *
    baneMeasrNew(int type, double *parm) {
      static const char me[]="baneMeasrNew";
      baneMeasr *measr;
      int item;
    
      AIR_UNUSED(parm);
      if (!( AIR_IN_OP(baneMeasrUnknown, type, baneMeasrLast) )) {
        biffAddf(BANE, "%s: baneMeasr %d invalid", me, type);
        return NULL;
      }
      /* for now, parm is ignored */
      measr = (baneMeasr*)calloc(1, sizeof(baneMeasr));
      if (!measr) {
        biffAddf(BANE, "%s: couldn't allocate baneMeasr!", me);
        return NULL;
      }
      measr->type = type;
      measr->range = NULL;
      GAGE_QUERY_RESET(measr->query);
      switch(type) {
        /* --------------------------------------------------------------- */
      case baneMeasrValuePositive:
        item = gageSclValue;
        sprintf(measr->name, "%s, positive", airEnumStr(gageScl, item));
        GAGE_QUERY_ITEM_ON(measr->query, item);
        measr->range = baneRangeNew(baneRangePositive);
        measr->offset0 = gageKindAnswerOffset(gageKindScl, item);
        measr->answer = _baneMeasr_StockAnswer;
        break;
        /* --------------------------------------------------------------- */
      case baneMeasrValueZeroCentered:
        item = gageSclValue;
        sprintf(measr->name, "%s, zero-centered", airEnumStr(gageScl, item));
        GAGE_QUERY_ITEM_ON(measr->query, item);
        measr->range = baneRangeNew(baneRangeZeroCentered);
        measr->offset0 = gageKindAnswerOffset(gageKindScl, item);
        measr->answer = _baneMeasr_StockAnswer;
        break;
        /* --------------------------------------------------------------- */
      case baneMeasrValueAnywhere:
        item = gageSclValue;
        sprintf(measr->name, "%s, anywhere", airEnumStr(gageScl, item));
        GAGE_QUERY_ITEM_ON(measr->query, item);
        measr->range = baneRangeNew(baneRangeAnywhere);
        measr->offset0 = gageKindAnswerOffset(gageKindScl, item);
        measr->answer = _baneMeasr_StockAnswer;
        break;
        /* --------------------------------------------------------------- */
      case baneMeasrGradMag:
        item = gageSclGradMag;
        sprintf(measr->name, "%s", airEnumStr(gageScl, item));
        GAGE_QUERY_ITEM_ON(measr->query, item);
        measr->range = baneRangeNew(baneRangePositive);
        measr->offset0 = gageKindAnswerOffset(gageKindScl, item);
        measr->answer = _baneMeasr_StockAnswer;
        break;
        /* --------------------------------------------------------------- */
      case baneMeasrLaplacian:
        item = gageSclLaplacian;
        sprintf(measr->name, "%s", airEnumStr(gageScl, item));
        GAGE_QUERY_ITEM_ON(measr->query, item);
        measr->range = baneRangeNew(baneRangeZeroCentered);
        measr->offset0 = gageKindAnswerOffset(gageKindScl, item);
        measr->answer = _baneMeasr_StockAnswer;
        break;
        /* --------------------------------------------------------------- */
      case baneMeasr2ndDD:
        item = gageScl2ndDD;
        sprintf(measr->name, "%s", airEnumStr(gageScl, item));
        GAGE_QUERY_ITEM_ON(measr->query, item);
        measr->range = baneRangeNew(baneRangeZeroCentered);
        measr->offset0 = gageKindAnswerOffset(gageKindScl, item);
        measr->answer = _baneMeasr_StockAnswer;
        break;
        /* --------------------------------------------------------------- */
      case baneMeasrTotalCurv:
        item = gageSclTotalCurv;
        sprintf(measr->name, "%s", airEnumStr(gageScl, item));
        GAGE_QUERY_ITEM_ON(measr->query, item);
        measr->range = baneRangeNew(baneRangePositive);
        measr->offset0 = gageKindAnswerOffset(gageKindScl, item);
        measr->answer = _baneMeasr_StockAnswer;
        break;
        /* --------------------------------------------------------------- */
      case baneMeasrFlowlineCurv:
        item = gageSclFlowlineCurv;
        sprintf(measr->name, "%s", airEnumStr(gageScl, item));
        GAGE_QUERY_ITEM_ON(measr->query, item);
        measr->range = baneRangeNew(baneRangePositive);
        measr->offset0 = gageKindAnswerOffset(gageKindScl, item);
        measr->answer = _baneMeasr_StockAnswer;
        break;
        /* --------------------------------------------------------------- */
      default:
        biffAddf(BANE, "%s: Sorry, baneMeasr %d not implemented", me, type);
        baneMeasrNix(measr); return NULL;
      }
      return measr;
    }
    
    double
    baneMeasrAnswer(baneMeasr *measr, gageContext *gctx) {
      static const char me[]="baneMeasrAnswer";
      double ret;
    
      if (measr && gctx && 1 == gctx->pvlNum) {
        ret = measr->answer(measr, gctx->pvl[0]->answer, measr->parm);
      } else {
        fprintf(stderr, "%s: something is terribly wrong\n", me);
        ret = AIR_NAN;
      }
      return ret;
    }
    
    baneMeasr *
    baneMeasrCopy(baneMeasr *measr) {
      static const char me[]="baneMeasrCopy";
      baneMeasr *ret = NULL;
    
      ret = baneMeasrNew(measr->type, measr->parm);
      if (!ret) {
        biffAddf(BANE, "%s: couldn't make new measr", me);
        return NULL;
      }
      return ret;
    }
    
    baneMeasr *
    baneMeasrNix(baneMeasr *measr) {
    
      if (measr) {
        baneRangeNix(measr->range);
        airFree(measr);
      }
      return NULL;
    }
    teem-1.11.0~svn6057/src/bane/gkmsHvol.c0000664000175000017500000001370112165631065017246 0ustar  domibeldomibel/*
      Teem: Tools to process and visualize scientific data and images             .
      Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
      Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
      Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public License
      (LGPL) as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
      The terms of redistributing and/or modifying this software also
      include exceptions to the LGPL that facilitate static linking.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public License
      along with this library; if not, write to Free Software Foundation, Inc.,
      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    #include "bane.h"
    #include "privateBane.h"
    
    #define HVOL_INFO "Make histogram volume"
    static const char *_baneGkms_hvolInfoL =
      (HVOL_INFO
       ".  The histogram volume is a three-dimensional histogram recording "
       "the relationship between data value, gradient magnitude, and the "
       "second directional derivative along the gradient direction.  Creating "
       "it is the first step in semi-automatic transfer function generation.  ");
    
    int
    baneGkms_hvolMain(int argc, const char **argv, const char *me,
                      hestParm *hparm) {
      hestOpt *opt = NULL;
      char *out, *perr;
      Nrrd *nin, *nout;
      airArray *mop;
      int pret, dim[3], lapl, slow, gz = AIR_FALSE;
      double inc[3*(1+BANE_PARM_NUM)];
      baneHVolParm *hvp;
      NrrdIoState *nio;
      NrrdKernelSpec *ksp00, *ksp11, *ksp22;
    
      hestOptAdd(&opt, "s", "incV incG incH", airTypeOther, 3, 3, inc,
                 "f:1.0 p:0.005 p:0.015",
                 "Strategies for determining how much of the range "
                 "of a quantity should be included and quantized in its axis "
                 "of the histogram volume.  Possibilities include:\n "
                 "\b\bo \"f:\": included range is some fraction of the "
                 "total range, as scaled by F\n "
                 "\b\bo \"p:

    \": exclude the extremal P percent of " "the values\n " "\b\bo \"s:\": included range is S times the standard " "deviation of the values\n " "\b\bo \"a:,\": range is from to ", NULL, NULL, baneGkmsHestIncStrategy); hestOptAdd(&opt, "d", "dimV dimG dimH", airTypeInt, 3, 3, dim, "256 256 256", "Dimensions of histogram volume; number of samples along " "each axis"); hestOptAdd(&opt, "k00", "kernel", airTypeOther, 1, 1, &ksp00, "tent", "value reconstruction kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&opt, "k11", "kernel", airTypeOther, 1, 1, &ksp11, "cubicd:1,0", "first derivative kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&opt, "k22", "kernel", airTypeOther, 1, 1, &ksp22, "cubicdd:1,0", "second derivative kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&opt, "l", NULL, airTypeInt, 0, 0, &lapl, NULL, "Use Laplacian instead of Hessian to approximate second " "directional derivative. No faster, less accurate."); hestOptAdd(&opt, "slow", NULL, airTypeInt, 0, 0, &slow, NULL, "Instead of allocating a floating point VGH volume and measuring " "V,G,H once, measure V,G,H multiple times on separate passes " "(slower, but needs less memory)"); if (nrrdEncodingGzip->available()) { hestOptAdd(&opt, "gz", NULL, airTypeInt, 0, 0, &gz, NULL, "Use gzip compression for output histo-volume; " "much less disk space, slightly slower to read/write"); } hestOptAdd(&opt, "i", "volumeIn", airTypeOther, 1, 1, &nin, NULL, "input scalar volume for which a transfer function is needed", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "o", "hvolOut", airTypeString, 1, 1, &out, NULL, "output histogram volume, used by \"gkms scat\" and " "\"gkms info\""); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_baneGkms_hvolInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); hvp = baneHVolParmNew(); airMopAdd(mop, hvp, (airMopper)baneHVolParmNix, airMopAlways); baneHVolParmGKMSInit(hvp); hvp->makeMeasrVol = !slow; fprintf(stderr, "!%s: need to be using baneHVolParmAxisSet\n", me); /* hvp->axis[0].res = dim[perm[0]]; hvp->axis[1].res = dim[perm[1]]; hvp->axis[2].res = dim[perm[2]]; hvp->axis[1].measr = lapl ? baneMeasrLapl : baneMeasrHess; for (i=0; i<=2; i++) { hvp->ax[i].inc = baneIncArray[(int)inc[(1+BANE_INC_PARM_NUM)*perm[i]]]; for (j=0; jax[i].incParm[j] = inc[1 + j + (1+BANE_INC_PARM_NUM)*perm[i]]; } } */ hvp->k3pack = AIR_TRUE; nrrdKernelParmSet(&hvp->k[gageKernel00], hvp->kparm[gageKernel00], ksp00); nrrdKernelParmSet(&hvp->k[gageKernel11], hvp->kparm[gageKernel11], ksp11); nrrdKernelParmSet(&hvp->k[gageKernel22], hvp->kparm[gageKernel22], ksp22); if (baneMakeHVol(nout, nin, hvp)) { biffAddf(BANE, "%s: trouble making histogram volume", me); airMopError(mop); return 1; } nio->encoding = gz ? nrrdEncodingGzip : nrrdEncodingRaw; if (nrrdSave(out, nout, nio)) { biffMovef(BANE, NRRD, "%s: error saving histogram volume", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } BANE_GKMS_CMD(hvol, HVOL_INFO); teem-1.11.0~svn6057/src/bane/test/0000775000175000017500000000000012203513754016262 5ustar domibeldomibelteem-1.11.0~svn6057/src/bane/test/pvg.c0000664000175000017500000001235412042367142017226 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../bane.h" char *me; void usage() { /* 0 1 2 (3) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char *argv[]) { FILE *file; char *posStr, *ppmStr; Nrrd *pos, *ppm; float *posData, p, min, max, sml, cwght, cidxf; unsigned char *ppmData, *rgb; int v, g, sv, sg, idx, smlIdx, /* donLen = 23, */ cidx; unsigned char don[] = {0, 0, 0, /* background: black */ /* 1 */ 0, 107, 255, /* start: blue */ 51, 104, 255, 103, 117, 255, 123, 124, 255, 141, 130, 255, 156, 132, 255, 166, 131, 245, 174, 131, 231, 181, 130, 216, 187, 130, 201, /* 11 */ 255, 255, 255, /* middle: white */ /* 12 */ 255, 255, 255, 187, 130, 201, 192, 129, 186, 197, 128, 172, 200, 128, 158, 204, 127, 142, 210, 126, 113, 212, 126, 98, 213, 126, 84, 216, 126, 49, /* 22 */ 220, 133, 0}; /* end: orange */ me = argv[0]; if (3 != argc) usage(); posStr = argv[1]; ppmStr = argv[2]; if (!(file = fopen(posStr, "r"))) { fprintf(stderr, "%s: couldn't open %s for reading\n", me, posStr); usage(); } if (!(pos = nrrdNewRead(file))) { fprintf(stderr, "%s: couldn't read pos from %s:\n%s\n", me, posStr, biffGet(NRRD)); usage(); } fclose(file); if (!baneValidPos(pos, 2)) { fprintf(stderr, "%s: %s isn't a valid p(v,g) file:\n%s\n", me, posStr, biffGet(BANE)); usage(); } sv = pos->size[0]; sg = pos->size[1]; posData = (float*)(pos->data); /* assert that min = -max; */ min = max = AIR_NAN; for (g=0; g<=sg-1; g++) { for (v=0; v<=sv-1; v++) { idx = v + sv*g; p = posData[idx]; if (!AIR_EXISTS(p)) continue; if (!AIR_EXISTS(min)) { min = max = p; sml = AIR_ABS(p); } min = AIR_MIN(p, min); max = AIR_MAX(p, max); if (AIR_ABS(p) < sml) { sml = AIR_ABS(p); smlIdx = idx; } } } printf("%s: pos range: [%g,%g,%g]\n", me, min, sml, max); posData[smlIdx] = 0; if (nrrdHistoEq(pos, NULL, 2048, 3)) { fprintf(stderr, "%s: trouble doing histeq on p(v,g):\n%s\n", me, biffGet(NRRD)); exit(1); } if (!(ppm = nrrdNewPPM(sv, sg))) { fprintf(stderr, "%s: couldn't make %dx%d PPM:\n%s\n", me, sv, sg, biffGet(NRRD)); exit(1); } ppmData = (unsigned char *)(ppm->data); sml = posData[smlIdx]; for (g=0; g<=sg-1; g++) { for (v=0; v<=sv-1; v++) { idx = v + sv*g; p = posData[idx]; rgb = ppmData + 3*(v + sv*(sg-1-g)); if (!AIR_EXISTS(p)) { rgb[0] = don[0]; rgb[1] = don[1]; rgb[2] = don[2]; continue; } if (p > sml) { cidxf = AIR_AFFINE(sml, p, max, 11.5, 21.999); cidx = cidxf; cwght = cidxf - cidx; rgb[0] = AIR_AFFINE(0, cwght, 1, don[0+3*cidx], don[0+3*(cidx+1)]); rgb[1] = AIR_AFFINE(0, cwght, 1, don[1+3*cidx], don[1+3*(cidx+1)]); rgb[2] = AIR_AFFINE(0, cwght, 1, don[2+3*cidx], don[2+3*(cidx+1)]); } else { cidxf = AIR_AFFINE(min, p, sml, 1, 11.5); cidx = cidxf; cwght = cidxf - cidx; rgb[0] = AIR_AFFINE(0, cwght, 1, don[0+3*cidx], don[0+3*(cidx+1)]); rgb[1] = AIR_AFFINE(0, cwght, 1, don[1+3*cidx], don[1+3*(cidx+1)]); rgb[2] = AIR_AFFINE(0, cwght, 1, don[2+3*cidx], don[2+3*(cidx+1)]); } } } if (!(file = fopen(ppmStr, "w"))) { fprintf(stderr, "%s: couldn't open %s for writing\n", me, ppmStr); exit(1); } ppm->encoding = nrrdEncodingRaw; if (nrrdWritePNM(file, ppm)) { fprintf(stderr, "%s: trouble writing ppm:\n%s\n", me, biffGet(NRRD)); exit(1); } fclose(file); exit(0); } teem-1.11.0~svn6057/src/bane/test/apply.c0000664000175000017500000000375412042367142017563 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../bane.h" char *me; void usage() { /* 0 1 2 3 (4) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char *argv[]) { int measr; char *iStr, *mStr, *oStr; Nrrd *nin, *nout; me = argv[0]; if (4 != argc) usage(); iStr = argv[1]; mStr = argv[2]; oStr = argv[3]; if (nrrdLoad(nin=nrrdNew(), iStr)) { fprintf(stderr, "%s: trouble reading input nrrd:\n%s\n", me, biffGet(NRRD)); usage(); } if (1 != sscanf(mStr, "%d", &measr)) { fprintf(stderr, "%s: couldn't parse %s as int\n", me, mStr); usage(); } if (baneApplyMeasr(nout = nrrdNew(), nin, measr)) { fprintf(stderr, "%s: trouble:\n%s\n", me, biffGet(BANE)); exit(1); } if (nrrdSave(oStr, nout, NULL)) { fprintf(stderr, "%s: trouble writing output nrrd:\n%s\n", me, biffGet(NRRD)); usage(); } exit(0); } teem-1.11.0~svn6057/src/bane/test/sigma.c0000664000175000017500000000265212042367142017532 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../bane.h" int main(int argc, char *argv[]) { Nrrd *info; float sigma; AIR_UNUSED(argc); if (nrrdLoad(info=nrrdNew(), argv[1], NULL)) { fprintf(stderr, "trouble:\n%s\n", biffGet(BANE)); } if (baneSigmaCalc(&sigma, info)) { fprintf(stderr, "trouble:\n%s\n", biffGet(BANE)); } printf("%g\n", sigma); nrrdNuke(info); return 0; } teem-1.11.0~svn6057/src/bane/test/htrnsf.c0000664000175000017500000000600612042367142017733 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../bane.h" char *me; void usage() { /* 0 1 2 3 4 5 (6) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char *argv[]) { char *iStr, *bStr, *pStr, *sStr, *gStr, *tStr; FILE *file; Nrrd *info2, *Bcpts; dirtTrnsf *trnsf; float sigma, gthresh; me = argv[0]; if (argc != 6) usage(); bStr = argv[1]; sStr = argv[2]; gStr = argv[3]; iStr = argv[4]; tStr = argv[5]; if (!(file = fopen(iStr, "r"))) { fprintf(stderr, "%s: couldn't open info2 %s for reading\n", me, iStr); usage(); } if (!(info2 = nrrdNewRead(file))) { fprintf(stderr, "%s: trouble reading info 2%s:\n%s\n", me, iStr, biffGet(NRRD)); usage(); } fclose(file); if (!(file = fopen(bStr, "r"))) { fprintf(stderr, "%s: couldn't open b(x) %s for reading\n", me, bStr); usage(); } if (!(Bcpts = nrrdNewRead(file))) { fprintf(stderr, "%s: trouble reading b(x) %s:\n%s\n", me, bStr, biffGet(NRRD)); usage(); } fclose(file); if (!strcmp(sStr, "nan")) { sigma = airNanf(); } else { if (1 != sscanf(sStr, "%g", &sigma)) { fprintf(stderr, "%s: couldn't parse sigma %s\n", me, sStr); usage(); } } if (1 != sscanf(gStr, "%g", >hresh)) { fprintf(stderr, "%s: couldn't parse gthresh %s\n", me, gStr); usage(); } trnsf = baneNewDirtTrnsf(Bcpts, sigma, gthresh, info2); if (!trnsf) { fprintf(stderr, "%s: trouble computing opacity functions:\n%s\n", me, biffGet(BANE)); exit(1); } if (!(file = fopen(tStr, "w"))) { fprintf(stderr, "%s: couldn't open %s for writing\n", me, tStr); usage(); } if (dirtTrnsfWrite(file, trnsf)) { fprintf(stderr, "%s: trouble writing trnsf to %s\n:%s\n", me, tStr, biffGet(DIRT)); usage(); } fclose(file); nrrdNuke(info2); dirtNixTrnsf(trnsf); exit(0); } teem-1.11.0~svn6057/src/bane/test/tblah.c0000664000175000017500000000657612042367142017535 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../bane.h" char *me; void rangeTest(char *me, double imin, double imax) { double omin, omax; baneRange *range; int i; printf("input range (%g,%g) ---------------------\n", imin, imax); for (i=1; ianswer(&omin, &omax, imin, imax); printf("%s: range %s --> (%g,%g)\n", me, range->name, omin, omax); range = baneRangeNix(range); } } void incTest(char *me, int num, baneRange *range) { double *val, tmp, incParm[BANE_PARM_NUM], omin, omax, rmin, rmax; baneInc *inc; Nrrd *hist; int i, j; airSrand48(); val = (double*)malloc(num*sizeof(double)); /* from : the standard dev of a uniform distribution between A and B is sqrt((B-A)^2/12) */ for (j=0; jname); switch(i) { case baneIncAbsolute: ELL_3V_SET(incParm, -0.8, 1.5, AIR_NAN); break; case baneIncRangeRatio: ELL_3V_SET(incParm, 0.99, AIR_NAN, AIR_NAN); break; case baneIncPercentile: ELL_3V_SET(incParm, 1024, 10, AIR_NAN); break; case baneIncStdv: ELL_3V_SET(incParm, 1.0, AIR_NAN, AIR_NAN); break; } fprintf(stderr, "!%s: THIS IS BROKEN!!!\n", "incTest"); /* if (inc->passA) { for (j=0; jprocess[0](hist, val[j], incParm); } if (inc->passB) { for (j=0; jprocess[1](hist, val[j], incParm); } inc->ans(&omin, &omax, hist, incParm, range); */ printf(" --> (%g,%g)\n", omin, omax); } free(val); } int main(int argc, char *argv[]) { me = argv[0]; printf("%s ================================ range testing\n", me); rangeTest(me, -1, 2); rangeTest(me, -3, 2); printf("%s ================================ inclusion testing\n", me); incTest(me, 10000, baneRangeFloat); return 0; } teem-1.11.0~svn6057/src/bane/test/opac.c0000664000175000017500000000400112042367142017342 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../bane.h" char *me; void usage() { /* 0 1 2 3 (4) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char *argv[]) { char *bStr, *pStr, *oStr; Nrrd *b, *p, *o; me = argv[0]; if (argc != 4) usage(); bStr = argv[1]; pStr = argv[2]; oStr = argv[3]; if (nrrdLoad(b=nrrdNew(), bStr, NULL)) { fprintf(stderr, "%s: trouble reading %s:\n%s\n", me, bStr, biffGet(NRRD)); usage(); } if (nrrdLoad(p=nrrdNew(), pStr, NULL)) { fprintf(stderr, "%s: trouble reading %s:\n%s\n", me, pStr, biffGet(NRRD)); usage(); } if (baneOpacCalc(o = nrrdNew(), b, p)) { fprintf(stderr, "%s: trouble calculating opac:\n%s", me, biffGet(BANE)); exit(1); } if (nrrdSave(oStr, o, NULL)) { fprintf(stderr, "%s: trouble writing %s:\n%s\n", me, oStr, biffGet(NRRD)); exit(1); } nrrdNuke(o); nrrdNuke(b); nrrdNuke(p); exit(0); } teem-1.11.0~svn6057/src/bane/test/tinfo.c0000664000175000017500000000411212042367142017542 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../bane.h" char *me; void usage() { /* 0 1 2 3 (4) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char *argv[]) { Nrrd *hvol, *info; char *iStr, *dStr, *oStr; int dim; me = argv[0]; if (argc != 4) usage(); iStr = argv[1]; dStr = argv[2]; oStr = argv[3]; if (nrrdLoad(hvol=nrrdNew(), iStr, NULL)) { fprintf(stderr, "%s: trouble reading hvol:\n%s\n", me, biffGet(NRRD)); usage(); } if (1 != sscanf(dStr, "%d", &dim)) { fprintf(stderr, "%s: trouble parsing %s as an int\n", me, dStr); usage(); } if (baneOpacInfo(info = nrrdNew(), hvol, dim, nrrdMeasureHistoMean)) { fprintf(stderr, "%s: trouble calculting %d-D opacity info:\n%s\n", me, dim, biffGet(BANE)); exit(1); } if (nrrdSave(oStr, info, NULL)) { fprintf(stderr, "%s: trouble saving nrrd to %s:\n%s\n", me, oStr, biffGet(NRRD)); exit(1); } nrrdNuke(hvol); nrrdNuke(info); exit(0); } teem-1.11.0~svn6057/src/bane/test/pos.c0000664000175000017500000000434412042367142017233 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../bane.h" char *me; void usage() { /* 0 1 2 3 4 (5) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char *argv[]) { Nrrd *info, *pos; float sigma, gthresh; char *iStr, *oStr, *sigStr, *gthStr; me = argv[0]; if (argc != 5) { usage(); } iStr = argv[1]; sigStr = argv[2]; gthStr = argv[3]; oStr = argv[4]; if (1 != sscanf(sigStr, "%g", &sigma) || 1 != sscanf(gthStr, "%g", >hresh)) { fprintf(stderr, "%s: couldn't parse %s and %s as floats\n", me, sigStr, gthStr); usage(); } if (nrrdLoad(info=nrrdNew(), iStr, NULL)) { fprintf(stderr, "%s: trouble reading \"%s\" :\n%s\n", me, iStr, biffGet(NRRD)); exit(1); } if (banePosCalc(pos = nrrdNew(), sigma, gthresh, info)) { fprintf(stderr, "%s: trouble calculating %s:\n%s\n", me, 2 == info->dim ? "p(v,g)" : "p(v)", biffGet(BANE)); exit(1); } if (nrrdSave(oStr, pos, NULL)) { fprintf(stderr, "%s: trouble writing output to \"%s\"\n", me, oStr); exit(1); } nrrdNuke(info); nrrdNuke(pos); exit(0); } teem-1.11.0~svn6057/src/bane/rangeBane.c0000664000175000017500000001143412165631065017337 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "bane.h" #include "privateBane.h" int _baneRangePositive_Answer(double *ominP, double *omaxP, double imin, double imax) { static const char me[]="_baneRangePositive_Answer"; if (!( AIR_EXISTS(imin) && AIR_EXISTS(imax) )) { biffAddf(BANE, "%s: imin and imax don't both exist", me); return 1; } *ominP = 0; *omaxP = imax; return 0; } int _baneRangeNegative_Answer(double *ominP, double *omaxP, double imin, double imax) { static const char me[]="_baneRangeNegative_Answer"; if (!( AIR_EXISTS(imin) && AIR_EXISTS(imax) )) { biffAddf(BANE, "%s: imin and imax don't both exist", me); return 1; } *ominP = imin; *omaxP = 0; return 0; } /* ** _baneRangeZeroCentered_Answer ** ** Unlike the last version of this function, this is conservative: we ** choose the smallest zero-centered range that includes the original ** min and max. Previously the average of the min and max magnitude ** were used. */ int _baneRangeZeroCentered_Answer(double *ominP, double *omaxP, double imin, double imax) { static const char me[]="_baneRangeZeroCentered_Answer"; if (!( AIR_EXISTS(imin) && AIR_EXISTS(imax) )) { biffAddf(BANE, "%s: imin and imax don't both exist", me); return 1; } imin = AIR_MIN(imin, 0); imax = AIR_MAX(imax, 0); /* now the signs of imin and imax aren't wrong */ *ominP = AIR_MIN(-imax, imin); *omaxP = AIR_MAX(imax, -imin); return 0; } int _baneRangeAnywhere_Answer(double *ominP, double *omaxP, double imin, double imax) { static const char me[]="_baneRangeAnywhere_Answer"; if (!( AIR_EXISTS(imin) && AIR_EXISTS(imax) )) { biffAddf(BANE, "%s: imin and imax don't both exist", me); return 1; } *ominP = imin; *omaxP = imax; return 0; } baneRange * baneRangeNew(int type) { static const char me[]="baneRangeNew"; baneRange *range = NULL; if (!AIR_IN_OP(baneRangeUnknown, type, baneRangeLast)) { biffAddf(BANE, "%s: baneRange %d not valid", me, type); return NULL; } range = (baneRange *)calloc(1, sizeof(baneRange)); if (!range) { biffAddf(BANE, "%s: couldn't allocate baneRange!", me); return NULL; } range->type = type; range->center = AIR_NAN; switch(type) { case baneRangePositive: sprintf(range->name, "positive"); range->answer = _baneRangePositive_Answer; break; case baneRangeNegative: sprintf(range->name, "negative"); range->answer = _baneRangeNegative_Answer; break; case baneRangeZeroCentered: sprintf(range->name, "zero-centered"); range->answer = _baneRangeZeroCentered_Answer; break; case baneRangeAnywhere: sprintf(range->name, "anywhere"); range->answer = _baneRangeAnywhere_Answer; break; default: biffAddf(BANE, "%s: Sorry, baneRange %d not implemented", me, type); baneRangeNix(range); return NULL; } return range; } baneRange * baneRangeCopy(baneRange *range) { static const char me[]="baneRangeCopy"; baneRange *ret = NULL; ret = baneRangeNew(range->type); if (!ret) { biffAddf(BANE, "%s: couldn't make new range", me); return NULL; } ret->center = range->center; return ret; } int baneRangeAnswer(baneRange *range, double *ominP, double *omaxP, double imin, double imax) { static const char me[]="baneRangeAnswer"; if (!(range && ominP && omaxP)) { biffAddf(BANE, "%s: got NULL pointer", me); return 1; } if (range->answer(ominP, omaxP, imin, imax)) { biffAddf(BANE, "%s: trouble", me); return 1; } return 0; } baneRange * baneRangeNix(baneRange *range) { if (range) { airFree(range); } return NULL; } teem-1.11.0~svn6057/src/bane/TODO.txt0000664000175000017500000000167712026462327016626 0ustar domibeldomibellh stuff: - limit to only going upstream or downstream - threshold on grad mag - threshold on distance travelled - mirror based on make all the constants that GKMSInit sets be global defaults, so that gkms can see them too. generate string representations of inclusion strategies (unparse them), and save these as comments in the histovolume, and save all other gkms parameters in generated nrrds make sure that the NUM_PARM_MAX values are correct when 6pack filtering is in gage, support it in bane use airMop after regular and irregular maps are finished and debugged in nrrd, they should be used for opacity function calculation from boundary emphasis function control points make new range called "baneRangeValCent" which is values conceptually centered around some background value, which isn't necessarily zero, and store that value in the baneRange struct itself. the hard part: - use gage for multi-modal data, making a 4D histogram volume teem-1.11.0~svn6057/src/bane/inc.c0000664000175000017500000003007612165631065016231 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "bane.h" #include "privateBane.h" void _baneIncProcess_LearnMinMax(baneInc *inc, double val) { if (AIR_EXISTS(inc->nhist->axis[0].min)) { /* then both min and max have seen at least one valid value */ inc->nhist->axis[0].min = AIR_MIN(inc->nhist->axis[0].min, val); inc->nhist->axis[0].max = AIR_MAX(inc->nhist->axis[0].max, val); } else { inc->nhist->axis[0].min = inc->nhist->axis[0].max = val; } /* fprintf(stderr, "## _baneInc_LearnMinMax: (%g,%g)\n", inc->nhist->axis[0].min, inc->nhist->axis[0].max); */ return; } void _baneIncProcess_Stdv(baneInc *inc, double val) { inc->S += val; inc->SS += val*val; inc->num += 1; return; } void _baneIncProcess_HistFill(baneInc *inc, double val) { int *hist; unsigned int idx; idx = airIndex(inc->nhist->axis[0].min, val, inc->nhist->axis[0].max, inc->nhist->axis[0].size); /* fprintf(stderr, "## _baneInc_HistFill: (%g,%g,%g) %d ---> %d\n", inc->nhist->axis[0].min, val, inc->nhist->axis[0].max, inc->nhist->axis[0].size, idx); */ if (idx < inc->nhist->axis[0].size) { hist = (int*)inc->nhist->data; hist[idx]++; } return; } /* ** _baneIncAnswer_Absolute ** ** incParm[0]: new min ** incParm[1]: new max */ int _baneIncAnswer_Absolute(double *minP, double *maxP, Nrrd *hist, double *incParm, baneRange *range) { AIR_UNUSED(hist); AIR_UNUSED(range); *minP = incParm[0]; *maxP = incParm[1]; return 0; } /* ** _baneIncAnswer_RangeRatio ** ** incParm[0]: scales the size of the range after it has been ** sent through the associated range function. */ int _baneIncAnswer_RangeRatio(double *minP, double *maxP, Nrrd *hist, double *incParm, baneRange *range) { static const char me[]="_baneIncAnwer_RangeRatio"; double mid; if (range->answer(minP, maxP, hist->axis[0].min, hist->axis[0].max)) { biffAddf(BANE, "%s: trouble", me); return 1; } if (baneRangeAnywhere == range->type) { mid = AIR_EXISTS(range->center) ? range->center : (*minP + *maxP)/2; *minP = AIR_AFFINE(-1, -incParm[0], 0, *minP, mid); *maxP = AIR_AFFINE(0, incParm[0], 1, mid, *maxP); } else { *minP *= incParm[0]; *maxP *= incParm[0]; } return 0; } /* ** _baneIncAnswer_Percentile ** ** incParm[0]: resolution of histogram generated ** incParm[1]: PERCENT of hits to throw away, by nibbling away at ** lower and upper ends of range, in a manner dependant on the ** range type */ int _baneIncAnswer_Percentile(double *minP, double *maxP, Nrrd *nhist, double *incParm, baneRange *range) { static const char me[]="_baneIncAnswer_Percentile"; int *hist, i, histSize, sum; float minIncr, maxIncr, out, outsofar, mid, minIdx, maxIdx; double min, max; /* integrate histogram and determine how many hits to exclude */ sum = 0; hist = (int *)nhist->data; histSize = nhist->axis[0].size; for (i=0; i out = %g\n", me, histSize, sum, out); if (range->answer(&min, &max, nhist->axis[0].min, nhist->axis[0].max)) { biffAddf(BANE, "%s:", me); return 1; } fprintf(stderr, "##%s: hist's min,max (%g,%g) ---%s---> %g, %g\n", me, nhist->axis[0].min, nhist->axis[0].max, range->name, min, max); if (baneRangeAnywhere == range->type) { mid = AIR_CAST(float, (AIR_EXISTS(range->center) ? range->center : (min + max)/2)); } else { mid = 0; /* yes, this is okay. The "mid" is the value we march towards from both ends, but we control the rate of marching according to the distance to the ends. So if min == mid == 0, then there is no marching up from below HOWEVER: the mode of histogram would probably be better. */ } fprintf(stderr, "##%s: hist (%g,%g) --> min,max = (%g,%g) --> mid = %g\n", me, nhist->axis[0].min, nhist->axis[0].max, min, max, mid); if (max-mid > mid-min) { /* the max is further from the mid than the min */ maxIncr = 1; minIncr = AIR_CAST(float, (mid-min)/(max-mid)); } else { /* the min is further */ minIncr = 1; maxIncr = AIR_CAST(float, (max-mid)/(mid-min)); } if (!( AIR_EXISTS(minIncr) && AIR_EXISTS(maxIncr) )) { biffAddf(BANE, "%s: minIncr, maxIncr don't both exist", me); return 1; } fprintf(stderr, "##%s: --> {min,max}Incr = %g,%g\n", me, minIncr, maxIncr); minIdx = AIR_CAST(float, AIR_AFFINE(nhist->axis[0].min, min, nhist->axis[0].max, 0, histSize-1)); maxIdx = AIR_CAST(float, AIR_AFFINE(nhist->axis[0].min, max, nhist->axis[0].max, 0, histSize-1)); outsofar = 0; while (outsofar < out) { if (AIR_IN_CL(0, minIdx, histSize-1)) { outsofar += minIncr*hist[AIR_ROUNDUP(minIdx)]; } if (AIR_IN_CL(0, maxIdx, histSize-1)) { outsofar += maxIncr*hist[AIR_ROUNDUP(maxIdx)]; } minIdx += minIncr; maxIdx -= maxIncr; if (minIdx > maxIdx) { biffAddf(BANE, "%s: minIdx (%g) passed maxIdx (%g) during " "histogram traversal", me, minIdx, maxIdx); return 1; } } *minP = AIR_AFFINE(0, minIdx, histSize-1, nhist->axis[0].min, nhist->axis[0].max); *maxP = AIR_AFFINE(0, maxIdx, histSize-1, nhist->axis[0].min, nhist->axis[0].max); fprintf(stderr, "##%s: --> output min, max = %g, %g\n", me, *minP, *maxP); return 0; } /* ** _baneIncAnswer_Stdv() ** ** incParm[0]: range is standard deviation times this */ int _baneIncAnswer_Stdv(double *minP, double *maxP, Nrrd *hist, double *incParm, baneRange *range) { float SS, stdv, mid, mean, width; int count; count = hist->axis[1].size; mean = AIR_CAST(float, hist->axis[1].min/count); SS = AIR_CAST(float, hist->axis[1].max/count); stdv = AIR_CAST(float, sqrt(SS - mean*mean)); width = AIR_CAST(float, incParm[0]*stdv); fprintf(stderr, "##%s: mean=%g, stdv=%g --> width=%g\n", "_baneIncAnswer_Stdv", mean, stdv, width); switch (range->type) { case baneRangePositive: *minP = 0; *maxP = width; break; case baneRangeNegative: *minP = -width; *maxP = 0; break; case baneRangeZeroCentered: *minP = -width/2; *maxP = width/2; break; case baneRangeAnywhere: mid = AIR_CAST(float, AIR_EXISTS(range->center) ? range->center : mean); *minP = mid - width/2; *maxP = mid + width/2; break; default: *minP = *maxP = AIR_NAN; break; } return 0; } baneInc * baneIncNew(int type, baneRange *range, double *parm) { static const char me[]="baneIncNew"; baneInc *inc; if (!(AIR_IN_OP(baneIncUnknown, type, baneIncLast))) { biffAddf(BANE, "%s: baneInc %d invalid", me, type); return NULL; } if (!(range && parm)) { biffAddf(BANE, "%s: got NULL baneRange or parm", me); return NULL; } inc = (baneInc*)calloc(1, sizeof(baneInc)); if (!inc) { biffAddf(BANE, "%s: couldn't allocated baneInc!", me); return NULL; } inc->S = inc->SS = 0; inc->num = 0; inc->range = baneRangeCopy(range); if (!inc->range) { biffAddf(BANE, "%s: couldn't copy baneRange!", me); baneIncNix(inc); return NULL; } inc->type = type; switch (type) { /* --------------------------------------------------------- */ case baneIncAbsolute: sprintf(inc->name, "absolute"); inc->nhist = NULL; if (!( AIR_EXISTS(parm[0]) && AIR_EXISTS(parm[1]) )) { biffAddf(BANE, "%s: parm[0] and parm[1] don't both exist", me); baneIncNix(inc); return NULL; } inc->parm[0] = parm[0]; /* enforced min */ inc->parm[1] = parm[1]; /* enforced max */ inc->process[0] = NULL; inc->process[1] = NULL; inc->answer = _baneIncAnswer_Absolute; break; /* --------------------------------------------------------- */ case baneIncRangeRatio: sprintf(inc->name, "range ratio"); inc->nhist = nrrdNew(); if (!AIR_EXISTS(parm[0])) { biffAddf(BANE, "%s: parm[0] doesn't exist", me); baneIncNix(inc); return NULL; } inc->parm[0] = parm[0]; /* scaling on range */ inc->process[0] = NULL; inc->process[1] = _baneIncProcess_LearnMinMax; inc->answer = _baneIncAnswer_RangeRatio; break; /* --------------------------------------------------------- */ case baneIncPercentile: sprintf(inc->name, "percentile"); inc->nhist = nrrdNew(); if (!( AIR_EXISTS(parm[0]) && AIR_EXISTS(parm[1]) )) { biffAddf(BANE, "%s: parm[0] and parm[1] don't both exist", me); baneIncNix(inc); return NULL; } inc->parm[0] = parm[0]; /* size of histogram */ if (nrrdMaybeAlloc_va(inc->nhist, nrrdTypeInt, 1, AIR_CAST(size_t, parm[0]))) { biffMovef(BANE, NRRD, "%s: couldn't allocate histogram", me); baneIncNix(inc); return NULL; } inc->parm[1] = parm[1]; /* percentile to exclude */ inc->process[0] = _baneIncProcess_LearnMinMax; inc->process[1] = _baneIncProcess_HistFill; inc->answer = _baneIncAnswer_Percentile; break; /* --------------------------------------------------------- */ case baneIncStdv: sprintf(inc->name, "stdv"); inc->nhist = NULL; if (!AIR_EXISTS(parm[0])) { biffAddf(BANE, "%s: parm[0] doesn't exist", me); baneIncNix(inc); return NULL; } inc->parm[0] = parm[0]; /* multiple of standard dev to use */ inc->process[0] = NULL; inc->process[1] = _baneIncProcess_Stdv; inc->answer = _baneIncAnswer_Stdv; break; /* --------------------------------------------------------- */ default: biffAddf(BANE, "%s: Sorry, baneInc %d not implemented", me, type); baneIncNix(inc); return NULL; } return inc; } void baneIncProcess(baneInc *inc, int passIdx, double val) { if (inc && (0 == passIdx || 1 == passIdx) && inc->process[passIdx]) { inc->process[passIdx](inc, val); } return; } int baneIncAnswer(baneInc *inc, double *minP, double *maxP) { static const char me[]="baneIncAnswer"; if (!( inc && minP && maxP )) { biffAddf(BANE, "%s: got NULL pointer", me); return 1; } if (inc->answer(minP, maxP, inc->nhist, inc->parm, inc->range)) { biffAddf(BANE, "%s: trouble", me); return 1; } return 0; } baneInc * baneIncCopy(baneInc *inc) { static const char me[]="baneIncCopy"; baneInc *ret = NULL; ret = baneIncNew(inc->type, inc->range, inc->parm); if (!ret) { biffAddf(BANE, "%s: couldn't make new inc", me); return NULL; } return ret; } baneInc * baneIncNix(baneInc *inc) { if (inc) { baneRangeNix(inc->range); nrrdNuke(inc->nhist); airFree(inc); } return NULL; } teem-1.11.0~svn6057/src/bane/trnsf.c0000664000175000017500000003436012165631065016614 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "bane.h" #include "privateBane.h" #define BIFF_NULL "%s: got NULL pointer" #define BIFF_NRRDALLOC "%s: couldn't allocate output nrrd" int baneOpacInfo(Nrrd *info, Nrrd *hvol, int dim, int measr) { static const char me[]="baneOpacInfo"; Nrrd *proj2, *proj1, *projT; float *data2D, *data1D; int i, len, sv, sg; if (!(info && hvol)) { biffAddf(BANE, BIFF_NULL, me); return 1; } if (!(1 == dim || 2 == dim)) { biffAddf(BANE, "%s: got dimension %d, not 1 or 2", me, dim); return 1; } if (!(nrrdMeasureHistoMin == measr || nrrdMeasureHistoMax == measr || nrrdMeasureHistoMean == measr || nrrdMeasureHistoMedian == measr || nrrdMeasureHistoMode == measr)) { biffAddf(BANE, "%s: measure %d doesn't make sense for histovolume", me, dim); return 1; } if (baneHVolCheck(hvol)) { biffAddf(BANE, "%s: given nrrd doesn't seem to be a histogram volume", me); return 1; } if (1 == dim) { len = hvol->axis[2].size; if (nrrdMaybeAlloc_va(info, nrrdTypeFloat, 2, AIR_CAST(size_t, 2), AIR_CAST(size_t, len))) { biffMovef(BANE, NRRD, BIFF_NRRDALLOC, me); return 1; } info->axis[1].min = hvol->axis[2].min; info->axis[1].max = hvol->axis[2].max; data1D = (float *)info->data; /* sum up along 2nd deriv for each data value, grad mag */ if (nrrdProject(proj2=nrrdNew(), hvol, 1, nrrdMeasureSum, nrrdTypeDefault)) { biffMovef(BANE, NRRD, "%s: trouble projecting out 2nd deriv. for g(v)", me); return 1; } /* now determine average gradient at each value (0: grad, 1: value) */ if (nrrdProject(proj1=nrrdNew(), proj2, 0, measr, nrrdTypeDefault)) { biffMovef(BANE, NRRD, "%s: trouble projecting along gradient for g(v)", me); return 1; } for (i=0; itype](proj1->data, i); } nrrdNuke(proj1); nrrdNuke(proj2); /* sum up along gradient for each data value, 2nd deriv */ if (nrrdProject(proj2 = nrrdNew(), hvol, 0, nrrdMeasureSum, nrrdTypeDefault)) { biffMovef(BANE, NRRD, "%s: trouble projecting out gradient for h(v)", me); return 1; } /* now determine average gradient at each value (0: 2nd deriv, 1: value) */ if (nrrdProject(proj1 = nrrdNew(), proj2, 0, measr, nrrdTypeDefault)) { biffMovef(BANE, NRRD, "%s: trouble projecting along 2nd deriv. for h(v)", me); return 1; } for (i=0; itype](proj1->data, i); } nrrdNuke(proj1); nrrdNuke(proj2); } else { /* 2 == dim */ /* hvol axes: 0: grad, 1: 2nd deriv: 2: data value */ sv = hvol->axis[2].size; sg = hvol->axis[0].size; if (nrrdMaybeAlloc_va(info, nrrdTypeFloat, 3, AIR_CAST(size_t, 2), AIR_CAST(size_t, sv), AIR_CAST(size_t, sg))) { biffMovef(BANE, NRRD, BIFF_NRRDALLOC, me); return 1; } info->axis[1].min = hvol->axis[2].min; info->axis[1].max = hvol->axis[2].max; info->axis[2].min = hvol->axis[0].min; info->axis[2].max = hvol->axis[0].max; data2D = (float *)info->data; /* first create h(v,g) */ if (nrrdProject(proj2=nrrdNew(), hvol, 1, measr, nrrdTypeDefault)) { biffMovef(BANE, NRRD, "%s: trouble projecting (step 1) to create h(v,g)", me); return 1; } if (nrrdAxesSwap(projT=nrrdNew(), proj2, 0, 1)) { biffMovef(BANE, NRRD, "%s: trouble projecting (step 2) to create h(v,g)", me); return 1; } for (i=0; itype](projT->data, i); } nrrdNuke(proj2); nrrdNuke(projT); /* then create #hits(v,g) */ if (nrrdProject(proj2=nrrdNew(), hvol, 1, nrrdMeasureSum, nrrdTypeDefault)) { biffMovef(BANE, NRRD, "%s: trouble projecting (step 1) to create #(v,g)", me); return 1; } if (nrrdAxesSwap(projT=nrrdNew(), proj2, 0, 1)) { biffMovef(BANE, NRRD, "%s: trouble projecting (step 2) to create #(v,g)", me); return 1; } for (i=0; itype](projT->data, i); } nrrdNuke(proj2); nrrdNuke(projT); } return 0; } int bane1DOpacInfoFrom2D(Nrrd *info1D, Nrrd *info2D) { static const char me[]="bane1DOpacInfoFrom2D"; Nrrd *projH2=NULL, *projH1=NULL, *projN=NULL, *projG1=NULL; float *data1D; int i, len; if (!(info1D && info2D)) { biffAddf(BANE, BIFF_NULL, me); return 1; } if (baneInfoCheck(info2D, 2)) { biffAddf(BANE, "%s: didn't get valid 2D info", me); return 1; } len = info2D->axis[1].size; if (nrrdProject(projH2=nrrdNew(), info2D, 0, nrrdMeasureProduct, nrrdTypeDefault) || nrrdProject(projH1=nrrdNew(), projH2, 1, nrrdMeasureSum, nrrdTypeDefault) || nrrdProject(projN=nrrdNew(), info2D, 2, nrrdMeasureSum, nrrdTypeDefault) || nrrdProject(projG1=nrrdNew(), info2D, 2, nrrdMeasureHistoMean, nrrdTypeDefault)) { biffAddf(BANE, "%s: trouble creating needed projections", me); return 1; } if (nrrdMaybeAlloc_va(info1D, nrrdTypeFloat, 2, AIR_CAST(size_t, 2), AIR_CAST(size_t, len))) { biffMovef(BANE, NRRD, BIFF_NRRDALLOC, me); return 1; } info1D->axis[1].min = info2D->axis[1].min; info1D->axis[1].max = info2D->axis[1].max; data1D = (float *)info1D->data; for (i=0; itype](projG1->data, 1 + 2*i); data1D[1 + 2*i] = (nrrdFLookup[projH1->type](projH1->data, i) / nrrdFLookup[projN->type](projN->data, 1 + 2*i)); } nrrdNuke(projH2); nrrdNuke(projH1); nrrdNuke(projN); nrrdNuke(projG1); return 0; } int _baneSigmaCalc1D(float *sP, Nrrd *info1D) { static const char me[]="_baneSigmaCalc1D"; int i, len; float maxg, maxh, minh, *data; len = info1D->axis[1].size; data = (float *)info1D->data; maxg = -1; maxh = -1; minh = 1; for (i=0; idim) { if (bane1DOpacInfoFrom2D(info = nrrdNew(), _info)) { biffAddf(BANE, "%s: couldn't create 1D opac info from 2D", me); return 1; } } else { info = _info; } if (_baneSigmaCalc1D(sP, info)) { biffAddf(BANE, "%s: trouble calculating sigma", me); return 1; } if (_info != info) { nrrdNuke(info); } return 0; } int banePosCalc(Nrrd *pos, float sigma, float gthresh, Nrrd *info) { static const char me[]="banePosCalc"; int d, i, len, vi, gi, sv, sg; float *posData, *infoData, h, g, p; if (!(pos && info)) { biffAddf(BANE, BIFF_NULL, me); return 1; } if (baneInfoCheck(info, 0)) { biffAddf(BANE, "%s: didn't get a valid info", me); return 1; } d = info->dim-1; if (1 == d) { len = info->axis[1].size; if (nrrdMaybeAlloc_va(pos, nrrdTypeFloat, 1, AIR_CAST(size_t, len))) { biffMovef(BANE, NRRD, BIFF_NRRDALLOC, me); return 1; } pos->axis[0].min = info->axis[1].min; pos->axis[0].max = info->axis[1].max; posData = (float *)pos->data; infoData = (float *)info->data; for (i=0; iaxis[1].size; sg = info->axis[2].size; if (nrrdMaybeAlloc_va(pos, nrrdTypeFloat, 2, AIR_CAST(size_t, sv), AIR_CAST(size_t, sg))) { biffMovef(BANE, NRRD, BIFF_NRRDALLOC, me); return 1; } pos->axis[0].min = info->axis[1].min; pos->axis[0].max = info->axis[1].max; pos->axis[1].min = info->axis[2].min; pos->axis[1].max = info->axis[2].max; posData = (float *)pos->data; for (gi=0; giaxis[2].min, info->axis[2].max)); for (vi=0; vitype](info->data, 0 + 2*(vi + sv*gi)); /* from pg. 61 of GK's MS */ if (AIR_EXISTS(h)) { p = -sigma*sigma*h/AIR_MAX(0, g-gthresh); } else { p = AIR_NAN; } p = airIsInf_f(p) ? AIR_NAN : p; posData[vi + sv*gi] = p; } } } return 0; } void _baneOpacCalcA(unsigned int lutLen, float *opacLut, unsigned int numCpts, float *xo, float *pos) { unsigned int i, j; float p; for (i=0; i= xo[0 + 2*(numCpts-1)]) { opacLut[i] = xo[1 + 2*(numCpts-1)]; continue; } for (j=1; j o = %g\n", i, pos[i], opacLut[i]); */ } void _baneOpacCalcB(unsigned int lutLen, float *opacLut, unsigned int numCpts, float *x, float *o, float *pos) { /* static const char me[]="_baneOpacCalcB"; */ unsigned int i, j; double p, op; /* printf("%s(%d, %lu, %d, %lu, %lu, %lu): hello\n", me, lutLen, (unsigned long)opacLut, numCpts, (unsigned long)x, (unsigned long)o, (unsigned long)pos); */ /* for (i=0; i", me, i, p); fflush(stdout); */ if (!AIR_EXISTS(p)) { op = 0; goto endloop; } if (p <= x[0]) { op = o[0]; goto endloop; } if (p >= x[numCpts-1]) { op = o[numCpts-1]; goto endloop; } for (j=1; jdim; if (1 == dim) { len = pos->axis[0].size; if (nrrdMaybeAlloc_va(opac, nrrdTypeFloat, 1, AIR_CAST(size_t, len))) { biffMovef(BANE, NRRD, BIFF_NRRDALLOC, me); return 1; } opac->axis[0].min = pos->axis[0].min; opac->axis[0].max = pos->axis[0].max; odata = (float *)opac->data; bdata = (float *)Bcpts->data; pdata = (float *)pos->data; npts = Bcpts->axis[1].size; _baneOpacCalcA(len, odata, npts, bdata, pdata); } else { sv = pos->axis[0].size; sg = pos->axis[1].size; if (nrrdMaybeAlloc_va(opac, nrrdTypeFloat, 2, AIR_CAST(size_t, sv), AIR_CAST(size_t, sg))) { biffMovef(BANE, NRRD, BIFF_NRRDALLOC, me); return 1; } opac->axis[0].min = pos->axis[0].min; opac->axis[0].max = pos->axis[0].max; opac->axis[1].min = pos->axis[1].min; opac->axis[1].max = pos->axis[1].max; odata = (float *)opac->data; bdata = (float *)Bcpts->data; pdata = (float *)pos->data; npts = Bcpts->axis[1].size; _baneOpacCalcA(sv*sg, odata, npts, bdata, pdata); } return 0; } teem-1.11.0~svn6057/src/bane/gkmsFlotsam.c0000664000175000017500000002042312165631065017742 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "bane.h" #include "privateBane.h" /* ----------------------------------------------------------- */ /* ----------------------------------------------------------- */ /* ----------------------------------------------------------- */ /* ** baneGkmsParseIncStrategy ** ** inc[0]: member of baneInc* enum ** inc[1], inc[2] ... : incParm[0], incParm[1] ... */ int baneGkmsParseIncStrategy(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[]="baneGkmsParseIncStrategy"; double *inc, *incParm; int i, bins; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } inc = (double *)ptr; incParm = inc + 1; for (i=0; idata); off = AIR_CAST(float, AIR_AFFINE(0.0, shape, 1.0, 0.0, width/2)); /* positions */ bef[0 + 2*0] = cent - 2*width; bef[0 + 2*1] = cent - width/2 - off; bef[0 + 2*2] = cent - width/2 + off; bef[0 + 2*3] = cent + width/2 - off; bef[0 + 2*4] = cent + width/2 + off; bef[0 + 2*5] = cent + 2*width; if (bef[0 + 2*1] == bef[0 + 2*2]) bef[0 + 2*1] -= 0.001f; if (bef[0 + 2*2] == bef[0 + 2*3]) bef[0 + 2*2] -= 0.001f; if (bef[0 + 2*3] == bef[0 + 2*4]) bef[0 + 2*4] += 0.001f; /* opacities */ bef[1 + 2*0] = 0.0; bef[1 + 2*1] = 0.0; bef[1 + 2*2] = alpha; bef[1 + 2*3] = alpha; bef[1 + 2*4] = 0.0; bef[1 + 2*5] = 0.0; /* to tell gkms opac that this came from four floats */ (*nrrdP)->ptr = *nrrdP; } else { if (nrrdLoad(*nrrdP, str, NULL)) { airMopAdd(mop, nerr = biffGetDone(NRRD), airFree, airMopOnError); sprintf(mesg, "%s: couldn't parse \"%s\" as four-parameter BEF or " "as a nrrd filename\n", me, str); strcpy(err, mesg); strncat(err, nerr, AIR_STRLEN_HUGE-1-strlen(mesg)-1); airMopError(mop); return 1; } (*nrrdP)->ptr = NULL; } airMopOkay(mop); return 0; } hestCB _baneGkmsHestBEF = { sizeof(Nrrd *), "boundary emphasis function", baneGkmsParseBEF, (airMopper)nrrdNuke }; hestCB * baneGkmsHestBEF = &_baneGkmsHestBEF; /* ----------------------------------------------------------- */ /* ----------------------------------------------------------- */ /* ----------------------------------------------------------- */ /* ** gthr[0] = 1: some scaling of max grad "x" ** 0: absolute "" ** gthr[1] = the scaling, or the absolute */ int baneGkmsParseGthresh(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[]="baneGkmsParseGthresh"; float *gthr; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } gthr = (float *)ptr; if ('x' == str[0]) { if (1 != sscanf(str+1, "%f", gthr+1)) { sprintf(err, "%s: can't parse \"%s\" as x", me, str); return 1; } gthr[0] = 1; } else { if (1 != sscanf(str, "%f", gthr+1)) { sprintf(err, "%s: can't parse \"%s\" as float", me, str); return 1; } gthr[0] = 0; } return 0; } hestCB _baneGkmsHestGthresh = { 2*sizeof(float), "gthresh specification", baneGkmsParseGthresh, NULL }; hestCB * baneGkmsHestGthresh = &_baneGkmsHestGthresh; /* ----------------------------------------------------------- */ /* ----------------------------------------------------------- */ /* ----------------------------------------------------------- */ /* ******** baneGkmsCmdList[] ** ** NULL-terminated array of unrrduCmd pointers, as ordered by ** BANE_GKMS_MAP macro */ unrrduCmd * baneGkmsCmdList[] = { BANE_GKMS_MAP(BANE_GKMS_LIST) NULL }; /* ******** baneGkmsUsage ** ** prints out a little banner, and a listing of all available commands ** with their one-line descriptions */ void baneGkmsUsage(const char *me, hestParm *hparm) { char buff[AIR_STRLEN_LARGE], fmt[AIR_STRLEN_LARGE]; unsigned int ci, si, len, maxlen; maxlen = 0; for (ci=0; baneGkmsCmdList[ci]; ci++) { maxlen = AIR_MAX(maxlen, AIR_UINT(strlen(baneGkmsCmdList[ci]->name))); } sprintf(buff, "--- Semi-Automatic Generation of Transfer Functions ---"); sprintf(fmt, "%%%ds\n", (int)((hparm->columns-strlen(buff))/2 + strlen(buff) - 1)); fprintf(stderr, fmt, buff); for (ci=0; baneGkmsCmdList[ci]; ci++) { len = AIR_UINT(strlen(baneGkmsCmdList[ci]->name)); strcpy(buff, ""); for (si=len; siname); strcat(buff, " ... "); len = AIR_UINT(strlen(buff)); fprintf(stderr, "%s", buff); _hestPrintStr(stderr, len, len, hparm->columns, baneGkmsCmdList[ci]->info, AIR_FALSE); } } static const char * _baneGkmsMeasrStr[] = { "(unknown measr)", "min", "max", "mean", "median", "mode" }; const int _baneGkmsMeasrVal[] = { nrrdMeasureUnknown, nrrdMeasureHistoMin, nrrdMeasureHistoMax, nrrdMeasureHistoMean, nrrdMeasureHistoMedian, nrrdMeasureHistoMode }; const airEnum _baneGkmsMeasr = { "measurement", 5, _baneGkmsMeasrStr, _baneGkmsMeasrVal, NULL, NULL, NULL, AIR_FALSE }; const airEnum *const baneGkmsMeasr = &_baneGkmsMeasr; teem-1.11.0~svn6057/src/bane/clip.c0000664000175000017500000001145112165631065016403 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "bane.h" #include "privateBane.h" int _baneClipAnswer_Absolute(int *countP, Nrrd *hvol, double *clipParm) { AIR_UNUSED(hvol); *countP = (int)(clipParm[0]); return 0; } int _baneClipAnswer_PeakRatio(int *countP, Nrrd *hvol, double *clipParm) { int *hits, maxhits; size_t idx, num; hits = (int *)hvol->data; maxhits = 0; num = nrrdElementNumber(hvol); for (idx=0; idxdata; num = nrrdElementNumber(ncopy); qsort(hits, num, sizeof(int), nrrdValCompare[nrrdTypeInt]); sum = 0; for (hi=0; hidata; num = nrrdElementNumber(copy); qsort(hits, num, sizeof(int), nrrdValCompare[nrrdTypeInt]); tmp = AIR_CLAMP(0, (int)clipParm[0], (int)num-1); *countP = hits[num-tmp-1]; nrrdNuke(copy); return 0; } baneClip * baneClipNew(int type, double *parm) { static const char me[]="baneClipNew"; baneClip *clip; if (!( AIR_IN_OP(baneClipUnknown, type, baneClipLast) )) { biffAddf(BANE, "%s: baneClip %d invalid", me, type); return NULL; } if (!parm) { biffAddf(BANE, "%s: got NULL pointer", me); return NULL; } if (!(AIR_EXISTS(parm[0]))) { biffAddf(BANE, "%s: parm[0] doesn't exist", me); return NULL; } clip = (baneClip*)calloc(1, sizeof(baneClip)); if (!clip) { biffAddf(BANE, "%s: couldn't allocate baneClip!", me); return NULL; } clip->parm[0] = parm[0]; clip->type = type; switch(type) { case baneClipAbsolute: sprintf(clip->name, "absolute"); clip->answer = _baneClipAnswer_Absolute; break; case baneClipPeakRatio: sprintf(clip->name, "peak ratio"); clip->answer = _baneClipAnswer_PeakRatio; break; case baneClipPercentile: sprintf(clip->name, "percentile"); clip->answer = _baneClipAnswer_Percentile; break; case baneClipTopN: sprintf(clip->name, "top N"); clip->answer = _baneClipAnswer_TopN; break; default: biffAddf(BANE, "%s: sorry, baneClip %d not implemented", me, type); baneClipNix(clip); return NULL; break; } return clip; } int baneClipAnswer(int *countP, baneClip *clip, Nrrd *hvol) { static const char me[]="baneClipAnswer"; if (!( countP && clip && hvol )) { biffAddf(BANE, "%s: got NULL pointer", me); return 0; } if (clip->answer(countP, hvol, clip->parm)) { biffAddf(BANE, "%s: trouble", me); return 0; } return 0; } baneClip * baneClipCopy(baneClip *clip) { static const char me[]="baneClipCopy"; baneClip *ret = NULL; ret = baneClipNew(clip->type, clip->parm); if (!ret) { biffAddf(BANE, "%s: couldn't make new clip", me); return NULL; } return ret; } baneClip * baneClipNix(baneClip *clip) { if (clip) { airFree(clip->name); airFree(clip); } return NULL; } teem-1.11.0~svn6057/src/bane/hvol.c0000664000175000017500000004271312165631065016431 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "bane.h" #include "privateBane.h" /* hz is defined in the aix system in /usr/include/sys/m_param.h . This causes compilation errors for later functions which want to use this as a variable. */ #ifdef hz # undef hz #endif /* ** learned: don't ever count on interleaved printfs to stdout ** and stderr to appear in the right order */ int _baneAxisCheck (baneAxis *ax) { static const char me[]="_baneAxisCheck"; if (!(ax->res >= 2)) { biffAddf(BANE, "%s: need resolution at least 2 (not %d)", me, ax->res); return 1; } if (!ax->measr) { biffAddf(BANE, "%s: have NULL baneMeasr", me); return 1; } if (!ax->inc) { biffAddf(BANE, "%s: have NULL baneInc", me); return 1; } /* all okay */ return 0; } void baneProbe(double val[3], Nrrd *nin, baneHVolParm *hvp, gageContext *ctx, unsigned int x, unsigned int y, unsigned int z) { float *data=NULL; if (hvp->makeMeasrVol) { data = ( (float*)(hvp->measrVol->data) + 3*(x + nin->axis[0].size*(y + nin->axis[1].size*z)) ); } if (!hvp->makeMeasrVol || !hvp->measrVolDone) { gageProbe(ctx, x, y, z); val[0] = baneMeasrAnswer(hvp->axis[0].measr, ctx); val[1] = baneMeasrAnswer(hvp->axis[1].measr, ctx); val[2] = baneMeasrAnswer(hvp->axis[2].measr, ctx); if (hvp->makeMeasrVol) { data[0] = AIR_CAST(float, val[0]); data[1] = AIR_CAST(float, val[1]); data[2] = AIR_CAST(float, val[2]); } } else { val[0] = data[0]; val[1] = data[1]; val[2] = data[2]; } return; } int baneFindInclusion(double min[3], double max[3], Nrrd *nin, baneHVolParm *hvp, gageContext *ctx) { static const char me[]="baneFindInclusion"; char prog[13], aname[3][AIR_STRLEN_SMALL] = {"grad-mag", "2nd deriv", "data value"}; int sx, sy, sz, x, y, z, E, ai; baneInc *inc[3]; /* HEY HEY HEY: The variable "hist" is used before its value is set. Nrrd *hist[3]; */ double val[3]; /* conveniance copies */ sx = nin->axis[0].size; sy = nin->axis[1].size; sz = nin->axis[2].size; inc[0] = hvp->axis[0].inc; inc[1] = hvp->axis[1].inc; inc[2] = hvp->axis[2].inc; if (hvp->verbose) { fprintf(stderr, "%s: inclusions: %s %s %s\n", me, inc[0]->name, inc[1]->name, inc[2]->name); fprintf(stderr, "%s: measures: %s %s %s\n", me, hvp->axis[0].measr->name, hvp->axis[1].measr->name, hvp->axis[2].measr->name); /* fprintf(stderr, "%s: gage query:\n", me); ctx->pvl[0]->kind->queryPrint(stderr, ctx->pvl[0]->query); */ } /* Determining the inclusion ranges for the histogram volume takes some work- either finding the min and max values of some measure, and/or making a histogram of them. The needed work for the three measures should done simultaneously during a given pass through the volume, so we break up the work into three stages- "passA", "passB", and then the final determination of ranges, "ans". Here we start with passA. If the chosen inclusion methods don't have anything to do at this stage (the callback is NULL), we don't do anything */ if (hvp->verbose) { fprintf(stderr, "%s: pass A of inclusion initialization ... ", me); fflush(stderr); } if (inc[0]->process[0] || inc[1]->process[0] || inc[2]->process[0]) { /* fprintf(stderr, "%s: inclusion pass CBs = %p %p %p \n", me, incPass[0], incPass[1], incPass[2]); */ if (hvp->makeMeasrVol && !hvp->measrVol) { if (nrrdMaybeAlloc_va(hvp->measrVol=nrrdNew(), nrrdTypeFloat, 4, AIR_CAST(size_t, 3), AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, sz))) { biffMovef(BANE, NRRD, "%s: couldn't allocate 3x%dx%dx%d VGH volume", me, sx, sy, sz); return 1; } } for (z=0; zverbose && !((y+sy*z)%200)) { fprintf(stderr, "%s", airDoneStr(0, y+sy*z, sy*sz, prog)); fflush(stderr); } for (x=0; xprocess[0]) inc[0]->process[0](inc[0], val[0]); if (inc[1]->process[0]) inc[1]->process[0](inc[1], val[1]); if (inc[2]->process[0]) inc[2]->process[0](inc[2], val[2]); } } } if (hvp->makeMeasrVol) { hvp->measrVolDone = AIR_TRUE; /* nrrdSave("VGH.nrrd", hvp->measrVol, NULL); */ } } if (hvp->verbose) fprintf(stderr, "\b\b\b\b\b\b done\n"); /* HEY HEY HEY: The variable "hist" is used before its value is set. if (hvp->verbose > 1) { fprintf(stderr, "%s: after pass A; ranges: [%g,%g] [%g,%g] [%g,%g]\n", me, hist[0]->axis[0].min, hist[0]->axis[0].max, hist[1]->axis[0].min, hist[1]->axis[0].max, hist[2]->axis[0].min, hist[2]->axis[0].max); } */ /* second stage of initialization, includes creating histograms */ if (hvp->verbose) { fprintf(stderr, "%s: pass B of inclusion initialization ... ", me); fflush(stderr); } if (inc[0]->process[1] || inc[1]->process[1] || inc[2]->process[1]) { if (hvp->makeMeasrVol && !hvp->measrVol) { if (nrrdMaybeAlloc_va(hvp->measrVol=nrrdNew(), nrrdTypeFloat, 4, AIR_CAST(size_t, 3), AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, sz))) { biffMovef(BANE, NRRD, "%s: couldn't allocate 3x%dx%dx%d VGH volume", me, sx, sy, sz); return 1; } } for (z=0; zverbose && !((y+sy*z)%200)) { fprintf(stderr, "%s", airDoneStr(0, y+sy*z, sy*sz, prog)); fflush(stderr); } for (x=0; xprocess[1]) inc[0]->process[1](inc[0], val[0]); if (inc[1]->process[1]) inc[1]->process[1](inc[1], val[1]); if (inc[2]->process[1]) inc[2]->process[1](inc[2], val[2]); } } } if (hvp->makeMeasrVol) { hvp->measrVolDone = AIR_TRUE; } } if (hvp->verbose) fprintf(stderr, "\b\b\b\b\b\b done\n"); /* HEY HEY HEY: The variable "hist" is used before its value is set. if (hvp->verbose > 1) { fprintf(stderr, "%s: after pass B; ranges: [%g,%g] [%g,%g] [%g,%g]\n", me, hist[0]->axis[0].min, hist[0]->axis[0].max, hist[1]->axis[0].min, hist[1]->axis[0].max, hist[2]->axis[0].min, hist[2]->axis[0].max); } */ /* now the real work of determining the inclusion */ if (hvp->verbose) { fprintf(stderr, "%s: determining inclusion ... ", me); fflush(stderr); } E = 0; if (!E) { ai = 0; E |= baneIncAnswer(inc[0], 0 + min, 0 + max); } if (!E) { ai = 1; E |= baneIncAnswer(inc[1], 1 + min, 1 + max); } if (!E) { ai = 2; E |= baneIncAnswer(inc[2], 2 + min, 2 + max); } if (E) { biffAddf(BANE, "%s: problem calculating inclusion for axis %d (%s)", me, ai, aname[ai]); return 1; } if (hvp->verbose) fprintf(stderr, "done\n"); /* HEY HEY HEY: The variable "hist" is used before its value is set. nrrdNuke(hist[0]); nrrdNuke(hist[1]); nrrdNuke(hist[2]); */ return 0; } int baneMakeHVol(Nrrd *hvol, Nrrd *nin, baneHVolParm *hvp) { static const char me[]="baneMakeHVol"; char prog[13]; gageContext *ctx; gagePerVolume *pvl; int E, sx, sy, sz, shx, shy, shz, x, y, z, hx, hy, hz, *rhvdata, clipVal, hval, pad; /* these are doubles because ultimately the inclusion functions use doubles, because I wanted the most generality */ double val[3], min[3], max[3]; size_t hidx, included; float fracIncluded; unsigned char *nhvdata; Nrrd *rawhvol; airArray *mop; if (!(hvol && nin && hvp)) { biffAddf(BANE, "%s: got NULL pointer", me); return 1; } if (baneInputCheck(nin, hvp)) { biffAddf(BANE, "%s: something wrong with input volume or parameters", me); return 1; } /* set up */ sx = nin->axis[0].size; sy = nin->axis[1].size; sz = nin->axis[2].size; mop = airMopNew(); ctx = gageContextNew(); airMopAdd(mop, ctx, (airMopper)gageContextNix, airMopAlways); pvl = gagePerVolumeNew(ctx, nin, gageKindScl); gageParmSet(ctx, gageParmVerbose, 0); gageParmSet(ctx, gageParmRenormalize, hvp->renormalize); gageParmSet(ctx, gageParmCheckIntegrals, AIR_TRUE); if (!hvp->k3pack) { biffAddf(BANE, "%s: code currently assumes k3pack", me); airMopError(mop); return 1; } gageParmSet(ctx, gageParmK3Pack, hvp->k3pack); E = 0; if (!E) E |= gagePerVolumeAttach(ctx, pvl); if (!E) E |= gageKernelSet(ctx, gageKernel00, hvp->k[gageKernel00], hvp->kparm[gageKernel00]); if (!E) E |= gageKernelSet(ctx, gageKernel11, hvp->k[gageKernel11], hvp->kparm[gageKernel11]); if (!E) E |= gageKernelSet(ctx, gageKernel22, hvp->k[gageKernel22], hvp->kparm[gageKernel22]); if (!E) E |= gageQueryReset(ctx, pvl); if (!E) E |= gageQueryAdd(ctx, pvl, hvp->axis[0].measr->query); if (!E) E |= gageQueryAdd(ctx, pvl, hvp->axis[1].measr->query); if (!E) E |= gageQueryAdd(ctx, pvl, hvp->axis[2].measr->query); if (!E) E |= gageUpdate(ctx); if (E) { biffMovef(BANE, GAGE, "%s: trouble setting up gage", me); airMopError(mop); return 1; } pad = ctx->radius; if (baneFindInclusion(min, max, nin, hvp, ctx)) { biffAddf(BANE, "%s: trouble finding inclusion ranges", me); airMopError(mop); return 1; } if (max[0] == min[0]) { max[0] += 1; if (hvp->verbose) fprintf(stderr, "%s: fixing range 0 [%g,%g] --> [%g,%g]\n", me, min[0], min[0], min[0], max[0]); } if (max[1] == min[1]) { max[1] += 1; if (hvp->verbose) fprintf(stderr, "%s: fixing range 1 [%g,%g] --> [%g,%g]\n", me, min[1], min[1], min[1], max[1]); } if (max[2] == min[2]) { max[2] += 1; if (hvp->verbose) fprintf(stderr, "%s: fixing range 2 [%g,%g] --> [%g,%g]\n", me, min[2], min[2], min[2], max[2]); } if (hvp->verbose) fprintf(stderr, "%s: inclusion: 0:[%g,%g], 1:[%g,%g], 2:[%g,%g]\n", me, min[0], max[0], min[1], max[1], min[2], max[2]); /* construct the "raw" (un-clipped) histogram volume */ if (hvp->verbose) { fprintf(stderr, "%s: creating raw histogram volume ... ", me); fflush(stderr); } shx = hvp->axis[0].res; shy = hvp->axis[1].res; shz = hvp->axis[2].res; if (nrrdMaybeAlloc_va(rawhvol=nrrdNew(), nrrdTypeInt, 3, AIR_CAST(size_t, shx), AIR_CAST(size_t, shy), AIR_CAST(size_t, shz))) { biffMovef(BANE, NRRD, "%s: couldn't allocate raw histovol (%dx%dx%d)", me, shx, shy, shz); airMopError(mop); return 1; } airMopAdd(mop, rawhvol, (airMopper)nrrdNuke, airMopAlways); rhvdata = (int *)rawhvol->data; included = 0; for (z=pad; zverbose && !((y-pad+(sy-2*pad)*(z-pad))%200)) { fprintf(stderr, "%s", airDoneStr(0, y-pad+(sy-2*pad)*(z-pad), (sy-2*pad)*(sz-2*pad), prog)); fflush(stderr); } for (x=pad; xincLimit) { biffAddf(BANE, "%s: included only %g%% of data, wanted at least %g%%", me, 100*fracIncluded, 100*hvp->incLimit); airMopError(mop); return 1; } if (hvp->verbose) { fprintf(stderr, "\b\b\b\b\b\b done\n"); fprintf(stderr, "%s: included %g%% of original voxels\n", me, fracIncluded*100); } /* determine the clipping value and produce the final histogram volume */ if (baneClipAnswer(&clipVal, hvp->clip, rawhvol)) { biffAddf(BANE, "%s: trouble determining clip value", me); airMopError(mop); return 1; } if (hvp->verbose) fprintf(stderr, "%s: will clip at %d\n", me, clipVal); if (hvp->verbose) { fprintf(stderr, "%s: creating 8-bit histogram volume ... ", me); fflush(stderr); } if (nrrdMaybeAlloc_va(hvol, nrrdTypeUChar, 3, AIR_CAST(size_t, shx), AIR_CAST(size_t, shy), AIR_CAST(size_t, shz))) { biffMovef(BANE, NRRD, "%s: couldn't alloc finished histovol", me); airMopError(mop); return 1; } airMopAdd(mop, hvol, (airMopper)nrrdEmpty, airMopOnError); hvol->axis[0].min = min[0]; hvol->axis[1].min = min[1]; hvol->axis[2].min = min[2]; hvol->axis[0].max = max[0]; hvol->axis[1].max = max[1]; hvol->axis[2].max = max[2]; hvol->axis[0].label = airStrdup(hvp->axis[0].measr->name); hvol->axis[1].label = airStrdup(hvp->axis[1].measr->name); hvol->axis[2].label = airStrdup(hvp->axis[2].measr->name); hvol->axis[0].center = nrrdCenterCell; hvol->axis[1].center = nrrdCenterCell; hvol->axis[2].center = nrrdCenterCell; nhvdata = (unsigned char *)hvol->data; for (hz=0; hzverbose && !((hy+shy*hz)%200)) { fprintf(stderr, "%s", airDoneStr(0, hy+shy*hz, shy*shz, prog)); fflush(stderr); } for (hx=0; hxverbose) fprintf(stderr, "\b\b\b\b\b\b done\n"); airMopOkay(mop); return 0; } Nrrd * baneGKMSHVol(Nrrd *nin, float gradPerc, float hessPerc) { static const char me[]="baneGKMSHVol"; baneHVolParm *hvp; Nrrd *hvol; if (!(hvp = baneHVolParmNew())) { biffAddf(BANE, "%s: couldn't get hvol parm struct", me); return NULL; } baneHVolParmGKMSInit(hvp); hvp->axis[0].inc->parm[1] = gradPerc; hvp->axis[1].inc->parm[1] = hessPerc; hvol = nrrdNew(); if (baneMakeHVol(hvol, nin, hvp)) { biffAddf(BANE, "%s: trouble making GKMS histogram volume", me); free(hvp); return NULL; } baneHVolParmNix(hvp); return hvol; } /* int baneApplyMeasr(Nrrd *nout, Nrrd *nin, int measr) { static const char me[]="baneApplyMeasr"; int sx, sy, sz, x, y, z, marg; baneMeasrType msr; nrrdBigInt idx; float (*insert)(void *, nrrdBigInt, float); if (3 != nin->dim) { biffAddf(BANE, "%s: need a 3-dimensional nrrd (not %d)", me, nin->dim); return 1; } if (!( AIR_IN_OP(nrrdTypeUnknown, nin->type, nrrdTypeLast) && nin->type != nrrdTypeBlock )) { biffAddf(BANE, "%s: must have a scalar type nrrd", me); return 1; } if (!( AIR_EXISTS(nin->axis[0].spacing) && nin->axis[0].spacing > 0 && AIR_EXISTS(nin->axis[1].spacing) && nin->axis[1].spacing > 0 && AIR_EXISTS(nin->axis[2].spacing) && nin->axis[2].spacing > 0 )) { biffAddf(BANE, "%s: must have positive spacing for all three axes", me); return 1; } sx = nin->axis[0].size; sy = nin->axis[1].size; sz = nin->axis[2].size; marg = baneMeasrMargin[measr]; msr = baneMeasr[measr]; if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 3, AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, sz))) { biffMovef(BANE, NRRD, "%s: couldn't alloc output nrrd", me); return 1; } nout->axis[0].spacing = nin->axis[0].spacing; nout->axis[1].spacing = nin->axis[1].spacing; nout->axis[2].spacing = nin->axis[2].spacing; insert = nrrdFInsert[nrrdTypeFloat]; for (z=marg; zdata, idx, msr(nin, idx)); } } } return 0; } */ teem-1.11.0~svn6057/src/mite/0000775000175000017500000000000012203513753015333 5ustar domibeldomibelteem-1.11.0~svn6057/src/mite/shade.c0000664000175000017500000001646612165631065016604 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mite.h" #include "privateMite.h" miteShadeSpec * miteShadeSpecNew(void) { miteShadeSpec *shpec; shpec = (miteShadeSpec *)calloc(1, sizeof(miteShadeSpec)); if (shpec) { shpec->method = miteShadeMethodUnknown; shpec->vec0 = gageItemSpecNew(); shpec->vec1 = gageItemSpecNew(); shpec->scl0 = gageItemSpecNew(); shpec->scl1 = gageItemSpecNew(); if (!( shpec->vec0 && shpec->vec1 && shpec->scl0 && shpec->scl1 )) { return NULL; } } return shpec; } miteShadeSpec * miteShadeSpecNix(miteShadeSpec *shpec) { if (shpec) { shpec->vec0 = gageItemSpecNix(shpec->vec0); shpec->vec1 = gageItemSpecNix(shpec->vec1); shpec->scl0 = gageItemSpecNix(shpec->scl0); shpec->scl1 = gageItemSpecNix(shpec->scl1); airFree(shpec); } return NULL; } /* ******** miteShadeSpecParse ** ** set up a miteShadeSpec based on a string. Valid forms are: ** ** none ** phong: ** litten:,,, ** ** where and are specifications of 3-vector and scalar ** parsable by miteVariableParse */ int miteShadeSpecParse(miteShadeSpec *shpec, char *shadeStr) { static const char me[]="miteShadeSpecParse"; char *buff, *qstr, *tok, *state; airArray *mop; int ansLength; mop = airMopNew(); if (!( shpec && airStrlen(shadeStr) )) { biffAddf(MITE, "%s: got NULL pointer and/or empty string", me); airMopError(mop); return 1; } buff = airToLower(airStrdup(shadeStr)); if (!buff) { biffAddf(MITE, "%s: couldn't strdup shading spec", me); airMopError(mop); return 1; } airMopAdd(mop, buff, airFree, airMopAlways); shpec->method = miteShadeMethodUnknown; if (!strcmp("none", buff)) { shpec->method = miteShadeMethodNone; } else if (buff == strstr(buff, "phong:")) { shpec->method = miteShadeMethodPhong; qstr = buff + strlen("phong:"); if (miteVariableParse(shpec->vec0, qstr)) { biffAddf(MITE, "%s: couldn't parse \"%s\" as shading vector", me, qstr); airMopError(mop); return 1; } ansLength = shpec->vec0->kind->table[shpec->vec0->item].answerLength; if (3 != ansLength) { biffAddf(MITE, "%s: \"%s\" isn't a vector (answer length is %d, not 3)", me, qstr, ansLength); airMopError(mop); return 1; } shpec->method = miteShadeMethodPhong; } else if (buff == strstr(buff, "litten:")) { qstr = buff + strlen("litten:"); /* ---- first vector */ tok = airStrtok(qstr, ",", &state); if (miteVariableParse(shpec->vec0, tok)) { biffAddf(MITE, "%s: couldn't parse \"%s\" as first lit-tensor vector", me, tok); airMopError(mop); return 1; } ansLength = shpec->vec0->kind->table[shpec->vec0->item].answerLength; if (3 != ansLength) { biffAddf(MITE, "%s: \"%s\" isn't a vector (answer length is %d, not 3)", me, qstr, ansLength); airMopError(mop); return 1; } /* ---- second vector */ tok = airStrtok(qstr, ",", &state); if (miteVariableParse(shpec->vec1, tok)) { biffAddf(MITE, "%s: couldn't parse \"%s\" as second lit-tensor vector", me, tok); airMopError(mop); return 1; } ansLength = shpec->vec1->kind->table[shpec->vec1->item].answerLength; if (3 != ansLength) { biffAddf(MITE, "%s: \"%s\" isn't a vector (answer length is %d, not 3)", me, qstr, ansLength); airMopError(mop); return 1; } /* ---- first scalar */ tok = airStrtok(qstr, ",", &state); if (miteVariableParse(shpec->scl0, tok)) { biffAddf(MITE, "%s: couldn't parse \"%s\" as first lit-tensor scalar", me, tok); airMopError(mop); return 1; } ansLength = shpec->scl0->kind->table[shpec->scl0->item].answerLength; if (1 != ansLength) { biffAddf(MITE, "%s: \"%s\" isn't a scalar (answer length is %d, not 1)", me, qstr, ansLength); airMopError(mop); return 1; } /* ---- second scalar */ tok = airStrtok(qstr, ",", &state); if (miteVariableParse(shpec->scl1, tok)) { biffAddf(MITE, "%s: couldn't parse \"%s\" as second lit-tensor scalar", me, tok); airMopError(mop); return 1; } ansLength = shpec->scl1->kind->table[shpec->scl1->item].answerLength; if (1 != ansLength) { biffAddf(MITE, "%s: \"%s\" isn't a scalar (answer length is %d, not 1)", me, qstr, ansLength); airMopError(mop); return 1; } shpec->method = miteShadeMethodLitTen; } else { biffAddf(MITE, "%s: shading specification \"%s\" not understood", me, shadeStr); airMopError(mop); return 1; } airMopOkay(mop); return 0; } void miteShadeSpecPrint(char *buff, const miteShadeSpec *shpec) { static const char me[]="miteShadeSpecPrint"; char var[4][AIR_STRLEN_MED]; if (buff && shpec) { switch(shpec->method) { case miteShadeMethodNone: sprintf(buff, "none"); break; case miteShadeMethodPhong: miteVariablePrint(var[0], shpec->vec0); sprintf(buff, "phong:%s", var[0]); break; case miteShadeMethodLitTen: miteVariablePrint(var[0], shpec->vec0); miteVariablePrint(var[1], shpec->vec1); miteVariablePrint(var[2], shpec->scl0); miteVariablePrint(var[3], shpec->scl1); sprintf(buff, "litten:%s,%s,%s,%s", var[0], var[1], var[2], var[3]); break; default: sprintf(buff, "%s: unknown shade method!", me); break; } } return; } void miteShadeSpecQueryAdd(gageQuery queryScl, gageQuery queryVec, gageQuery queryTen, gageQuery queryMite, miteShadeSpec *shpec) { if (shpec) { switch(shpec->method) { case miteShadeMethodNone: /* no queries to add */ break; case miteShadeMethodPhong: miteQueryAdd(queryScl, queryVec, queryTen, queryMite, shpec->vec0); break; case miteShadeMethodLitTen: miteQueryAdd(queryScl, queryVec, queryTen, queryMite, shpec->vec0); miteQueryAdd(queryScl, queryVec, queryTen, queryMite, shpec->vec1); miteQueryAdd(queryScl, queryVec, queryTen, queryMite, shpec->scl0); miteQueryAdd(queryScl, queryVec, queryTen, queryMite, shpec->scl1); break; default: break; } } return; } teem-1.11.0~svn6057/src/mite/privateMite.h0000664000175000017500000000336612165631065020011 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MITE_PRIVATE_HAS_BEEN_INCLUDED #define MITE_PRIVATE_HAS_BEEN_INCLUDED #ifdef __cplusplus extern "C" { #endif /* # if GAGE_TYPE_FLOAT # define limnVtoQN_GT limnVtoQN_f # else # define limnVtoQN_GT limnVtoQN_d # endif */ /* txf.c */ extern double *_miteAnswerPointer(miteThread *mtt, gageItemSpec *isp); extern int _miteNtxfAlphaAdjust(miteRender *mrr, miteUser *muu); extern int _miteStageSet(miteThread *mtt, miteRender *mrr); extern void _miteStageRun(miteThread *mtt, miteUser *muu); /* user.c */ extern int _miteUserCheck(miteUser *muu); #ifdef __cplusplus } #endif #endif /* MITE_PRIVATE_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/mite/mite.h0000664000175000017500000005715212165631065016460 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MITE_HAS_BEEN_INCLUDED #define MITE_HAS_BEEN_INCLUDED #include #include #include #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(mite_EXPORTS) || defined(teem_EXPORTS) # define MITE_EXPORT extern __declspec(dllexport) # else # define MITE_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define MITE_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define MITE miteBiffKey /* ******** mite_t ** ** the type used for representing and storing transfer function *range* ** (color, opacity, etc) information is: ** 1: float ** 0: double */ #if 0 typedef float mite_t; # define mite_nt nrrdTypeFloat # define mite_at airTypeFloat # define limnVTOQN limnVtoQN_f # define MITE_T_DOUBLE 0 #else typedef double mite_t; # define mite_nt nrrdTypeDouble # define mite_at airTypeDouble # define limnVTOQN limnVtoQN_d # define MITE_T_DOUBLE 1 #endif enum { miteRangeUnknown = -1, miteRangeAlpha, /* 0: "A", opacity */ miteRangeRed, /* 1: "R" */ miteRangeGreen, /* 2: "G" */ miteRangeBlue, /* 3: "B" */ miteRangeEmissivity, /* 4: "E" */ miteRangeKa, /* 5: "a", phong ambient term */ miteRangeKd, /* 6: "d", phong diffuse */ miteRangeKs, /* 7: "s", phong specular */ miteRangeSP, /* 8: "p", phong specular power */ miteRangeLast }; #define MITE_RANGE_NUM 9 /* ** There are some constraints on how the nrrd as transfer function is ** stored and used: ** 1) all transfer functions are lookup tables: there is no ** interpolation other than nearest neighbor (actually, someday linear ** interpolation may be supported, but that's it) ** 2) regardless of the centerings of the axes of nxtf, the lookup table ** axes will be treated as though they were cell centered (linear ** interpolation will always use node centering) ** 3) the logical dimension of the transfer function is always one less ** ntxf->dim, with axis 0 always for the range of the function, and axes ** 1 and onwards for the domain. For instance, a univariate opacity map ** is 2D, with ntxf->axis[0].size == 1. ** ** So, ntxf->dim-1 is the number of variables in the domain of the transfer ** function, and ntxf->axis[0].size is the number of variables in the range. */ /* ******** miteUser struct ** ** all the input parameters for mite specified by the user, as well as ** a mop for cleaning up memory used during rendering. Currently, ** unlike gage, there is no API for setting these- you go in the ** struct and set them yourself. ** ** Mite can currently handle scalar, 3-vector, and (symmetric) tensor ** volumes, one (at most) of each. All these volumes must have the ** same 3-D size, because we're only using one gageContext per thread, ** and the gageContext is what stores the convolution kernel weights ** evaluated per sample. */ typedef struct { Nrrd *nsin, /* scalar volume being rendered */ *nvin, /* 3-vector volume being rendered */ *ntin, /* tensor volume being rendered */ **ntxf, /* array of pointers to nrrds containing transfer functions, these are never altered (the ntxf in miteRender, however, is created and altered based on this ntxf) */ *nout; /* output image container, for five-channel output RGBAZ. We'll nrrdMaybeAlloc all the image data and put it in here, but we won't nrrdNuke(nout), just like we won't nrrdNuke nsin, nvin, ntin, or any of the ntxf[i] */ double *debug; /* data for verbose pixel (verbUi, verbVi) debug */ airArray *debugArr; /* airArray around debug */ Nrrd *ndebug; /* nrrd re-allocation of debug data */ int debugIdx; /* index into debug data */ int ntxfNum; /* allocated and valid length of ntxf[] */ /* the issue of regular shading, txf-based shading, and surface normals: phong and lit-tensor shading ("regular shading") methods need to specify one or more vectors that are used for shading calculations. These will be parsed from muu->shadeStr into mrr->shadeSpec, which in turn will determine the pointer values of mtt->shade{Vec,Scl}{0,1}. ENTIRELY SEPARATE FROM THIS, a "surface normal" can be specified in muu->normalStr, which (if non-empty), will be parsed into mrr->normalSpec, which in turn will determine the pointer values of mtt->normal. This normal is used for the miteVal quantities involving "N". Both shading and normal specifications can be given, since they are used in separate computations. If the user wants to do miteVal-based stuff with the same quantity specified in (say) a phong shading specification, its up to them to verify that the normalStr and the shadeStr refer to the same vector. */ char shadeStr[AIR_STRLEN_MED], /* how to do shading */ normalStr[AIR_STRLEN_MED]; /* what is the "surface normal" */ /* for each possible element of the txf range, what value should it start at prior to rendering. Mainly needed to store non-unity values for the quantities not covered by a transfer function */ mite_t rangeInit[MITE_RANGE_NUM]; double refStep, /* length of "unity" for doing opacity correction */ rayStep, /* distance between sampling planes */ opacMatters, /* minimal significant opacity, currently used to assign a Z depth (really "Tw") for each rendered ray */ opacNear1; /* opacity close enough to unity for the sake of doing early ray termination */ hooverContext *hctx; /* context and input for all hoover-related things, including camera and image parameters */ double fakeFrom[3], /* if non-NaN, then the "V"-dependent miteVal's will use this as the nominal eye point, instead of what is actually being used in the hooverContext */ vectorD[3]; /* some vector to use for something */ /* local initial copies of kernels, later passed to gageKernelSet */ NrrdKernelSpec *ksp[GAGE_KERNEL_MAX+1]; gageShape *shape; /* used just for setting hooverContext->shape */ gageContext *gctx0; /* context and input for all gage-related things, including all kernels. This is gageContextCopy'd for multi-threaded use (hence the 0) */ limnLight *lit; /* a struct for all lighting info, although currently only the ambient and *first* directional light are used */ int normalSide, /* determines direction of gradient that is used as normal for shading: 1: the standard way: normal points to lower values (higher values are more "inside"); 0: "two-sided": dot-products are abs()'d -1: normal points to higher values (lower values more "inside") Setting up the values this way allows for idioms like "if (muu->normalSide) . . .", meaning, if the lighting is one-sided */ verbUi, verbVi; /* pixel coordinate for which to turn on verbosity */ airArray *umop; /* for things allocated which are used across multiple renderings */ /* output information from last rendering */ double rendTime, /* rendering time, in seconds */ sampRate; /* rate (KHz) at which samples were rendered */ } miteUser; struct miteThread_t; /* ******** miteShadeMethod* enum ** ** the different ways that shading can be done */ enum { miteShadeMethodUnknown, miteShadeMethodNone, /* 1: no direction shading based on anything in the miteShadeSpec: just ambient, though things are still composited with the over operator, and transfer functions are free to implement whatever shading they can */ miteShadeMethodPhong, /* 2: what mite has been doing all along */ miteShadeMethodLitTen, /* 3: (tensor-based) lit-tensors */ miteShadeMethodLast }; /* ******** miteShadeSpec struct ** ** describes how to do shading. With more and more generality in the ** expressions that are evaluated for transfer function application, ** there is less need for this "shading" per se (phong shading can be ** expressed with multiplicative and additive transfer functions). ** But its here for the time being . . . */ typedef struct { int method; /* from miteShadeMethod* enum */ gageItemSpec *vec0, *vec1, *scl0, *scl1; /* things to use for shading. How these are interpreted is determined by shadeMethod: phong: vec0 is used as normal litten: lit-tensors based on vec0 and vec1, as weighted by scl0, scl1 */ } miteShadeSpec; /* ******** miteRender ** ** rendering-parameter-set-specific (but non-thread-specific) state, ** used internally by mite. The primary role here is to store information ** derived from the miteUser, in a form which is more immediately useful ** for rendering. */ typedef struct { Nrrd **ntxf; /* array of transfer function nrrds. The difference from those in miteUser is that opacity correction (based on rayStep and refStep) has been applied to these, and these have been converted/unquantized to type mite_t */ int ntxfNum; /* allocated and valid length of ntxf[] */ int sclPvlIdx, vecPvlIdx, tenPvlIdx; /* indices of the different gageKinds of volumes in the gageContext's array of gagePerVolumes. Probably a hack */ miteShadeSpec *shadeSpec; /* information based on muu->shadeStr */ gageItemSpec *normalSpec; /* information based on muu->normalStr */ double time0; /* rendering start time */ gageQuery queryMite; /* record of the miteVal quantities which we'll need to compute per-sample */ int queryMiteNonzero; /* shortcut miteVal computation if possible */ /* as long as there's no mutex around how the miteThreads are airMopAdded to the miteUser's mop, these have to be _allocated_ in mrendRenderBegin, instead of mrendThreadBegin, which still has the role of initializing them */ struct miteThread_t *tt[HOOVER_THREAD_MAX]; airArray *rmop; /* for things allocated which are rendering (or rendering parameter) specific and which are not thread-specific */ } miteRender; /* ******** miteStageOp* enum ** ** the kinds of things we can do per txf to modify the range ** variables. previously mite only supported separable transfer ** functions (i.e., multiplication only). It is tempting to use all ** the operations available as nrrdBinaryOps, but that would lead to ** goofy ones like Mod and GreaterThan, which either require or create ** integral values which aren't of much use in transfer functions. ** More generality in how opacities and colors are assigned will ** likely be supported by some simple programmability, supported by ** the likes of the funk library, which will be entirely separate from ** the miteStageOp mechanism. */ enum { miteStageOpUnknown, /* 0 */ miteStageOpMin, /* 1 */ miteStageOpMax, /* 2 */ miteStageOpAdd, /* 3 */ miteStageOpMultiply, /* 4 */ miteStageOpLast }; #define MITE_STAGE_OP_MAX 4 typedef struct { double *val; /* the txf axis variable, computed either by gage or by mite. This points into the answer vector in one of the thread's pervolumes, or into ansMiteVal in the miteThread. It can be either a scalar or a vector */ int size, /* number of entries along this txf axis */ op; /* from miteStageOp* enum. Note that this operation applies to ALL the range variables adjusted by this txf (can't add color while multiplying opacity) */ unsigned int (*qn)(const double *); /* if non-NULL: callback for doing vector quantization of vector-valued txf domain variables */ double min, max; /* quantization range of values which is covered by this axis of the txf; copied from corresponding axis of the nrrd */ mite_t *data; /* pointer to txf data. If non-NULL, the following fields are meaningful */ int rangeIdx[MITE_RANGE_NUM], /* indices into miteThread's range, so that we know which quantities to update */ rangeNum; /* number of range variables set by the txf == number of pointers in range[] to use */ char *label; /* pointer into axis label identifying txf domain variable, NOT COPIED */ } miteStage; /* ******** miteVal* enum ** ** the quantities not measured by gage (but often reliant on gage-based ** measurements) which can appear in the transfer function domain. ** In many respects, these behave like gage queries, and these are ** associated with a gageKind (miteValGageKind), but it is hardly a ** real, bona fide, gageKind. The answers for these are stored in ** the miteThread, in lieu of a gagePerVolume */ enum { miteValUnknown, /* 0: nobody knows */ miteValXw, /* 1: "Xw", X position, world space (double[1]) */ miteValXi, /* 2: "Xi", X " , index " (double[1]) */ miteValYw, /* 3: "Yw", Y " , world " (double[1]) */ miteValYi, /* 4: "Yi", Y " , index " (double[1]) */ miteValZw, /* 5: "Zw", Z " , world " (double[1]) */ miteValZi, /* 6: "Zi", Z " , index " (double[1]) */ miteValRw, /* 7: "Rw", dist to origin, world " (double[1]) */ miteValRi, /* 8: "Ri", dist to origin, index " (double[1]) */ miteValTw, /* 9: "Tw", ray position (double[1]) */ miteValTi, /* 10: "Ti", ray index (ray sample #) (double[1]) */ miteValView, /* 11: "V", the view vector (double[3]) */ miteValNormal, /* 12: "N", the nominal surface normal, as measured by a scalar, vector, or tensor kind, and then determined by the semantics of muu->normalSide */ miteValNdotV, /* 13: "NdotV", surface normal dotted w/ view vector (towards eye) (double[1]) */ miteValNdotL, /* 14: "NdotL", surface normal dotted w/ light vector (towards the light source) (double[1]) */ miteValVrefN, /* 15: "VrefN", view vector reflected across normal (double[3]) */ miteValGTdotV, /* 16: "GTdotV", normal curvature in view direction, the contraction of the geometry tensor along the view vector (double[1]) */ miteValVdefT, /* 17: "defT", view direction, deflected by tensor, then normalized (double[3]) */ miteValVdefTdotV, /* 18: "VdefTdotV", VdefT dotted back with V, not the same as the tensor contraction along V, (double[1]) */ miteValWdotD, /* 19: "WdotD", world space position dotted with muu->vectorD */ miteValLast }; #define MITE_VAL_ITEM_MAX 19 /* ******** miteThread ** ** thread-specific state for mite's internal use */ typedef struct miteThread_t { gageContext *gctx; /* per-thread context */ double *ansScl, /* pointer to gageKindScl answer vector */ *nPerp, *geomTens, /* convenience pointers into ansScl */ *ansVec, /* pointer to gageKindVec answer vector */ *ansTen, /* pointer to tenGageKind answer vector */ *ansMiteVal, /* room for all the miteVal answers, which unlike ans{Scl,Vec,Ten} is allocated by mite instead of by gage */ **directAnsMiteVal, /* pointers into ansMiteVal */ *_normal, /* pointer into ans{Scl,Vec,Ten} array above, (NOT ansMiteVal); this will determine the value of miteValNormal according to the semantics of muu->normalSide */ *shadeVec0, *shadeVec1, *shadeScl0, *shadeScl1; /* pointers into the ans* arrays above, used for shading */ int verbose, /* non-zero if (ui,vi) = (muu->verbUi,muu->verbVi) Because of the actions associated with recording values in muu->ndebug, it is currently reasonable for verbose to be non-zero for at most *ONE* pixel: it has to be one pixel because only one thread should be creating and storing this per- pixel information in the miteUser */ skip, /* non-zero if we shouldn't actually do any work for this pixel */ thrid, /* thread ID */ ui, vi, /* image coords of current ray */ raySample, /* number of samples finished in this ray */ samples; /* number of samples handled so far by this thread */ miteStage *stage; /* array of stages for txf computation */ int stageNum; /* number of stages == length of stage[] */ mite_t range[MITE_RANGE_NUM], /* rendering variables, which are either copied from miteUser's rangeInit[], or over-written by txf evaluation */ rayStep, /* per-ray step (may need to be different for each ray to enable sampling on planes) */ V[3], /* per-ray view direction */ RR, GG, BB, TT, /* per-ray composited values */ ZZ; /* for storing ray-depth when opacity passed muu->opacMatters */ airArray *rmop; /* for things allocated which are rendering (or rendering parameter) specific and which are thread-specific */ } miteThread; /* defaultsMite.c */ MITE_EXPORT const int mitePresent; MITE_EXPORT const char *miteBiffKey; MITE_EXPORT double miteDefRefStep; MITE_EXPORT int miteDefRenorm; MITE_EXPORT int miteDefNormalSide; MITE_EXPORT double miteDefOpacNear1; MITE_EXPORT double miteDefOpacMatters; /* kindnot.c */ MITE_EXPORT const airEnum *const miteVal; MITE_EXPORT gageKind *miteValGageKind; /* txf.c */ MITE_EXPORT const airEnum *const miteStageOp; MITE_EXPORT char miteRangeChar[MITE_RANGE_NUM+1]; MITE_EXPORT int miteVariableParse(gageItemSpec *isp, const char *label); MITE_EXPORT void miteVariablePrint(char *buff, const gageItemSpec *isp); MITE_EXPORT int miteNtxfCheck(const Nrrd *ntxf); MITE_EXPORT void miteQueryAdd(gageQuery queryScl, gageQuery queryVec, gageQuery queryTen, gageQuery queryMite, gageItemSpec *isp); /* user.c */ MITE_EXPORT miteUser *miteUserNew(void); MITE_EXPORT miteUser *miteUserNix(miteUser *muu); /* shade.c */ MITE_EXPORT miteShadeSpec *miteShadeSpecNew(void); MITE_EXPORT miteShadeSpec *miteShadeSpecNix(miteShadeSpec *); MITE_EXPORT int miteShadeSpecParse(miteShadeSpec *shpec, char *shadeStr); MITE_EXPORT void miteShadeSpecPrint(char *buff, const miteShadeSpec *shpec); MITE_EXPORT void miteShadeSpecQueryAdd(gageQuery queryScl, gageQuery queryVec, gageQuery queryTen, gageQuery queryMite, miteShadeSpec *shpec); /* renderMite.c */ MITE_EXPORT int miteRenderBegin(miteRender **mrrP, miteUser *muu); MITE_EXPORT int miteRenderEnd(miteRender *mrr, miteUser *muu); /* thread.c */ MITE_EXPORT miteThread *miteThreadNew(void); MITE_EXPORT miteThread *miteThreadNix(miteThread *mtt); MITE_EXPORT int miteThreadBegin(miteThread **mttP, miteRender *mrr, miteUser *muu, int whichThread); MITE_EXPORT int miteThreadEnd(miteThread *mtt, miteRender *mrr, miteUser *muu); /* ray.c */ MITE_EXPORT int miteRayBegin(miteThread *mtt, miteRender *mrr, miteUser *muu, int uIndex, int vIndex, double rayLen, double rayStartWorld[3], double rayStartIndex[3], double rayDirWorld[3], double rayDirIndex[3]); MITE_EXPORT double miteSample(miteThread *mtt, miteRender *mrr, miteUser *muu, int num, double rayT, int inside, double samplePosWorld[3], double samplePosIndex[3]); MITE_EXPORT int miteRayEnd(miteThread *mtt, miteRender *mrr, miteUser *muu); #ifdef __cplusplus } #endif #endif /* MITE_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/mite/quad.didthis.txt0000664000175000017500000000476007673511053020472 0ustar domibeldomibelalias quadvol ../nrrd/test/quadvol alias PAD "unu pad -min -1 -1 0 -max M+1 M+1 M -b pad -v -3" alias QUAD "quadvol -s 20 20 20 -min -1.1 -1.1 -1.1 -max 1.1 1.1 1.1 " QUAD -c 0.8 -0.8 -off 0.00 | PAD -o 04.nrrd QUAD -c 0.4 -0.8 -off 0.22 | PAD -o 14.nrrd QUAD -c 0.0 -0.8 -off 0.45 | PAD -o 24.nrrd QUAD -c -0.4 -0.8 -off 0.67 | PAD -o 34.nrrd QUAD -c -0.8 -0.8 -off 0.90 | PAD -o 44.nrrd QUAD -c 0.8 -0.4 -off -0.22 | PAD -o 03.nrrd QUAD -c 0.4 -0.4 -off -0.00 | PAD -o 13.nrrd QUAD -c 0.0 -0.4 -off 0.23 | PAD -o 23.nrrd QUAD -c -0.4 -0.4 -off 0.45 | PAD -o 33.nrrd QUAD -c 0.8 0.0 -off -0.45 | PAD -o 02.nrrd QUAD -c 0.4 0.0 -off -0.23 | PAD -o 12.nrrd QUAD -c 0.0 0.0 -off 0.00 | PAD -o 22.nrrd QUAD -c 0.8 0.4 -off -0.67 | PAD -o 01.nrrd QUAD -c 0.4 0.4 -off -0.45 | PAD -o 11.nrrd QUAD -c 0.8 0.8 -off -0.90 | PAD -o 00.nrrd unu 2op x 00.nrrd 0 | unu 2op - - 10 -o BB.nrrd unu join -i 04.nrrd 14.nrrd 24.nrrd 34.nrrd 44.nrrd -a 0 -o 4.nrrd unu join -i 03.nrrd 13.nrrd 23.nrrd 33.nrrd BB.nrrd -a 0 -o 3.nrrd unu join -i 02.nrrd 12.nrrd 22.nrrd BB.nrrd BB.nrrd -a 0 -o 2.nrrd unu join -i 01.nrrd 11.nrrd BB.nrrd BB.nrrd BB.nrrd -a 0 -o 1.nrrd unu join -i 00.nrrd BB.nrrd BB.nrrd BB.nrrd BB.nrrd -a 0 -o 0.nrrd unu join -i 0.nrrd 1.nrrd 2.nrrd 3.nrrd 4.nrrd -a 1 \ | unu axinfo -a 0 -sp 0.105263 \ | unu axinfo -a 1 -sp 0.105263 -o quad.nrrd echo "0 1" \ | unu reshape -s 1 2 \ | unu resample -s = 512 -k tent \ | unu save -f text -o ramp.txt echo "-1.0 0.0 -0.1 0.0 0.1 1.0 1.0 1.0" \ | unu reshape -s 2 4 \ | unu imap -i ramp.txt -r -m - \ | unu axinfo -a 0 -l "A" \ | unu axinfo -a 1 -l "gage(v)" -mm -1.0 1.0 -o txf.nrrd echo "1 0 1 0.65 0.65 0.75 0 0.78 0" \ | unu reshape -s 3 3 \ | unu rmap -i ramp.txt -m - \ | unu axdelete -a 1 \ | unu axinfo -a 0 -l "RGB" \ | unu axinfo -a 1 -l "gage(k2)" -mm -2.2 2.2 -o txf2.nrrd echo unu axinfo -a 1 -l "gage(gc)" -mm -4 4 -o txf2.nrrd echo unu axinfo -a 1 -l "gage(mc)" -mm -2.2 2.2 -o txf2.nrrd echo unu axinfo -a 1 -l "gage(k1)" -mm -2.2 2.2 -o txf2.nrrd echo unu axinfo -a 1 -l "gage(k2)" -mm -2.2 2.2 -o txf2.nrrd miter -i quad.nrrd \ -k00 cubic:1,0 -k11 cubicd:1,0 -k22 cubicdd:1,0 \ -or -rh -fr 1 -3 7.5 \ -dn -2 -di 0 -df 2 -ar \ -ur -1.3 1.3 -vr -1.3 1.3 \ -ld -1 -1 -2 -ads 0.3 0.7 0.23 -sp 60 \ -is 400 400 -step 0.01 \ -txf txf.nrrd txf2.nrrd \ -o rend.nrrd rm -f {4,3,2,1,0}{4,3,2,1,0}.nrrd rm -f {4,3,2,1,0}.nrrd rm -f BB.nrrd rm -f quad.nrrd rm -f ramp.txt txf.nrrd txf2.nrrd teem-1.11.0~svn6057/src/mite/user.c0000664000175000017500000001667012165631065016473 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mite.h" #include "privateMite.h" miteUser * miteUserNew() { miteUser *muu; int i; muu = (miteUser *)calloc(1, sizeof(miteUser)); if (!muu) return NULL; muu->umop = airMopNew(); muu->nsin = NULL; muu->nvin = NULL; muu->ntin = NULL; muu->ntxf = NULL; /* managed by user (with miter: hest) */ muu->nout = NULL; /* managed by user (with miter: hest) */ muu->debug = NULL; muu->debugArr = NULL; muu->ndebug = NULL; /* not allocated until the debug pixel is rendered, see miteRayBegin */ muu->ntxfNum = 0; muu->shadeStr[0] = 0; muu->normalStr[0] = 0; for (i=0; irangeInit[i] = 1.0; } muu->normalSide = miteDefNormalSide; muu->refStep = miteDefRefStep; muu->rayStep = AIR_NAN; muu->opacMatters = miteDefOpacMatters; muu->opacNear1 = miteDefOpacNear1; muu->hctx = hooverContextNew(); ELL_3V_SET(muu->fakeFrom, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(muu->vectorD, 0, 0, 0); airMopAdd(muu->umop, muu->hctx, (airMopper)hooverContextNix, airMopAlways); for (i=gageKernelUnknown+1; iksp[i] = NULL; } muu->shape = gageShapeNew(); muu->gctx0 = gageContextNew(); airMopAdd(muu->umop, muu->shape, (airMopper)gageShapeNix, airMopAlways); airMopAdd(muu->umop, muu->gctx0, (airMopper)gageContextNix, airMopAlways); /* gageParmSet(muu->gctx0, gageParmRequireAllSpacings, AIR_FALSE); */ muu->lit = limnLightNew(); airMopAdd(muu->umop, muu->lit, (airMopper)limnLightNix, airMopAlways); muu->normalSide = miteDefNormalSide; muu->verbUi = muu->verbVi = -1; muu->rendTime = 0; muu->sampRate = 0; return muu; } miteUser * miteUserNix(miteUser *muu) { if (muu) { airMopOkay(muu->umop); airFree(muu); } return NULL; } int _miteUserCheck(miteUser *muu) { static const char me[]="_miteUserCheck"; int T, gotOpac; gageItemSpec isp; gageQuery queryScl, queryVec, queryTen, queryMite; miteShadeSpec *shpec; airArray *mop; unsigned int axi; if (!muu) { biffAddf(MITE, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); if (!( muu->ntxfNum >= 1 )) { biffAddf(MITE, "%s: need at least one transfer function", me); airMopError(mop); return 1; } gotOpac = AIR_FALSE; GAGE_QUERY_RESET(queryScl); GAGE_QUERY_RESET(queryVec); GAGE_QUERY_RESET(queryTen); GAGE_QUERY_RESET(queryMite); /* not actually used here */ /* add on all queries associated with transfer functions */ for (T=0; TntxfNum; T++) { if (miteNtxfCheck(muu->ntxf[T])) { biffAddf(MITE, "%s: ntxf[%d] (%d of %d) can't be used as a txf", me, T, T+1, muu->ntxfNum); airMopError(mop); return 1; } /* NOTE: no error checking because miteNtxfCheck succeeded */ for (axi=1; axintxf[T]->dim; axi++) { miteVariableParse(&isp, muu->ntxf[T]->axis[axi].label); miteQueryAdd(queryScl, queryVec, queryTen, queryMite, &isp); } gotOpac |= !!strchr(muu->ntxf[T]->axis[0].label, 'A'); } if (!gotOpac) { fprintf(stderr, "\n\n%s: ****************************************" "************************\n", me); fprintf(stderr, "%s: !!! WARNING !!! opacity (\"A\") not set " "by any transfer function\n", me); fprintf(stderr, "%s: ****************************************" "************************\n\n\n", me); } /* add on "normal"-based queries */ if (airStrlen(muu->normalStr)) { miteVariableParse(&isp, muu->normalStr); if (miteValGageKind == isp.kind) { biffAddf(MITE, "%s: normalStr \"%s\" refers to a miteVal " "(normal must be data-intrinsic)", me, muu->normalStr); airMopError(mop); return 1; } if (3 != isp.kind->table[isp.item].answerLength) { biffAddf(MITE, "%s: %s not a vector: can't be used as normal", me, muu->normalStr); return 1; } miteQueryAdd(queryScl, queryVec, queryTen, queryMite, &isp); } /* add on shading-based queries */ shpec = miteShadeSpecNew(); airMopAdd(mop, shpec, (airMopper)miteShadeSpecNix, airMopAlways); if (miteShadeSpecParse(shpec, muu->shadeStr)) { biffAddf(MITE, "%s: couldn't parse shading spec \"%s\"", me, muu->shadeStr); airMopError(mop); return 1; } miteShadeSpecQueryAdd(queryScl, queryVec, queryTen, queryMite, shpec); /* see if anyone asked for an unspecified normal */ if ((GAGE_QUERY_ITEM_TEST(queryMite, miteValNdotV) || GAGE_QUERY_ITEM_TEST(queryMite, miteValNdotL) || GAGE_QUERY_ITEM_TEST(queryMite, miteValVrefN)) && !airStrlen(muu->normalStr)) { biffAddf(MITE, "%s: txf or shading requested a miteVal's use of the " "\"normal\", but one has not been specified in muu->normalStr", me); airMopError(mop); return 1; } /* see if we have volumes for requested queries */ if (GAGE_QUERY_NONZERO(queryScl) && !(muu->nsin)) { biffAddf(MITE, "%s: txf or shading require %s volume, but don't have one", me, gageKindScl->name); airMopError(mop); return 1; } if (GAGE_QUERY_NONZERO(queryVec) && !(muu->nvin)) { biffAddf(MITE, "%s: txf or shading require %s volume, but don't have one", me, gageKindVec->name); airMopError(mop); return 1; } if (GAGE_QUERY_NONZERO(queryTen) && !(muu->ntin)) { biffAddf(MITE, "%s: txf or shading require %s volume, but don't have one", me, tenGageKind->name); airMopError(mop); return 1; } /* check appropriateness of given volumes */ if (muu->nsin) { if (gageVolumeCheck(muu->gctx0, muu->nsin, gageKindScl)) { biffMovef(MITE, GAGE, "%s: trouble with input %s volume", me, gageKindScl->name); airMopError(mop); return 1; } } if (muu->nvin) { if (gageVolumeCheck(muu->gctx0, muu->nvin, gageKindVec)) { biffMovef(MITE, GAGE, "%s: trouble with input %s volume", me, gageKindVec->name); airMopError(mop); return 1; } } if (muu->ntin) { if (gageVolumeCheck(muu->gctx0, muu->ntin, tenGageKind)) { biffMovef(MITE, GAGE, "%s: trouble with input %s volume", me, tenGageKind->name); airMopError(mop); return 1; } } if (!muu->nout) { biffAddf(MITE, "%s: rendered image nrrd is NULL", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/mite/txf.c0000664000175000017500000005374512165631065016322 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* learned: don't confuse allocate an array of structs with an array of pointers to structs. Don't be surprised when you bus error because of the difference */ #include "mite.h" #include "privateMite.h" char miteRangeChar[MITE_RANGE_NUM+1] = "ARGBEadsp"; const char * _miteStageOpStr[] = { "(unknown miteStageOp)", "min", "max", "add", "multiply" }; const int _miteStageOpVal[] = { miteStageOpUnknown, miteStageOpMin, miteStageOpMax, miteStageOpAdd, miteStageOpMultiply }; const char * _miteStageOpStrEqv[] = { "min", "max", "add", "+", "multiply", "*", "x", "" }; const int _miteStageOpValEqv[] = { miteStageOpMin, miteStageOpMax, miteStageOpAdd, miteStageOpAdd, miteStageOpMultiply, miteStageOpMultiply, miteStageOpMultiply }; const airEnum _miteStageOp = { "miteStageOp", MITE_STAGE_OP_MAX, _miteStageOpStr, _miteStageOpVal, NULL, _miteStageOpStrEqv, _miteStageOpValEqv, AIR_FALSE }; const airEnum *const miteStageOp = &_miteStageOp; /* ******** miteVariableParse() ** ** takes a string (usually the label from a nrrd axis) and parses it ** to determine the gageItemSpec from it (which means finding the ** kind and item). The valid formats are: ** ** "" : NULL kind, 0 item ** : miteValGageKind (DEPRECATED) ** mite() : miteValGageKind ** gage() : gageKindScl (DEPRECATED) ** gage(scalar:) : gageKindScl ** gage(vector:) : gageKindVec ** gage(tensor:) : tenGageKind ** ** Notice that "scalar", "vector", and "tensor" do NOT refer to the type ** of the quantity being measured, but rather to the type of volume in ** which quantity is measured (i.e., the gageKind used) */ int miteVariableParse(gageItemSpec *isp, const char *label) { static const char me[]="miteVariableParse"; char *buff, *endparen, *kqstr, *col, *kstr, *qstr; airArray *mop; if (!( isp && label )) { biffAddf(MITE, "%s: got NULL pointer", me); return 1; } if (0 == strlen(label)) { /* nothing was specified; we try to indicate that by mimicking the return of gageItemSpecNew() */ isp->item = 0; isp->kind = NULL; return 0; } /* else given string was non-empty */ mop = airMopNew(); buff = airStrdup(label); if (!buff) { biffAddf(MITE, "%s: couldn't strdup label!", me); airMopError(mop); return 1; } airMopAdd(mop, buff, airFree, airMopAlways); if (strstr(buff, "gage(") == buff) { /* txf domain variable is to be measured directly by gage */ if (!(endparen = strstr(buff, ")"))) { biffAddf(MITE, "%s: didn't see close paren after \"gage(\"", me); airMopError(mop); return 1; } *endparen = 0; kqstr = buff + strlen("gage("); /* first see if its a (deprecated) gageKindScl specification */ isp->item = airEnumVal(gageScl, kqstr); if (0 != isp->item) { isp->kind = gageKindScl; fprintf(stderr, "\n%s: WARNING: deprecated use of txf domain " "\"gage(%s)\" without explicit gage kind specification; " "should use \"gage(%s:%s)\" instead\n\n", me, kqstr, gageKindScl->name, kqstr); } else { /* should be of form ":" */ col = strstr(kqstr, ":"); if (!col) { biffAddf(MITE, "%s: didn't see \":\" separator between gage " "kind and item", me); airMopError(mop); return 1; } *col = 0; kstr = kqstr; qstr = col+1; if (!strcmp(gageKindScl->name, kstr)) { isp->kind = gageKindScl; } else if (!strcmp(gageKindVec->name, kstr)) { isp->kind = gageKindVec; } else if (!strcmp(tenGageKind->name, kstr)) { isp->kind = tenGageKind; } else { biffAddf(MITE, "%s: don't recognized \"%s\" gage kind", me, kstr); airMopError(mop); return 1; } isp->item = airEnumVal(isp->kind->enm, qstr); if (0 == isp->item) { biffAddf(MITE, "%s: couldn't parse \"%s\" as a %s variable", me, qstr, isp->kind->name); airMopError(mop); return 1; } } } else if (strstr(buff, "mite(") == buff) { /* txf domain variable is *not* directly measured by gage */ if (!(endparen = strstr(buff, ")"))) { biffAddf(MITE, "%s: didn't see close paren after \"mite(\"", me); airMopError(mop); return 1; } *endparen = 0; qstr = buff + strlen("mite("); isp->item = airEnumVal(miteVal, qstr); if (0 == isp->item) { biffAddf(MITE, "%s: couldn't parse \"%s\" as a miteVal variable", me, qstr); airMopError(mop); return 1; } isp->kind = miteValGageKind; } else { /* didn't start with "gage(" or "mite(" */ isp->item = airEnumVal(miteVal, label); if (0 != isp->item) { /* its measured by mite */ isp->kind = miteValGageKind; fprintf(stderr, "\n%s: WARNING: deprecated use of txf domain " "\"%s\"; should use \"mite(%s)\" instead\n\n", me, label, label); } else { biffAddf(MITE, "%s: \"%s\" not a recognized variable", me, label); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } void miteVariablePrint(char *buff, const gageItemSpec *isp) { static const char me[]="miteVariablePrint"; if (!(isp->kind)) { strcpy(buff, ""); } else if (gageKindScl == isp->kind || gageKindVec == isp->kind || tenGageKind == isp->kind) { sprintf(buff, "gage(%s:%s)", isp->kind->name, airEnumStr(isp->kind->enm, isp->item)); } else if (miteValGageKind == isp->kind) { sprintf(buff, "%s(%s)", isp->kind->name, airEnumStr(isp->kind->enm, isp->item)); } else { sprintf(buff, "(%s: unknown gageKind!)", me); } return; } int miteNtxfCheck(const Nrrd *ntxf) { static const char me[]="miteNtxfCheck"; char *rangeStr, *domStr; gageItemSpec isp; unsigned int rii, axi; int ilog2; if (nrrdCheck(ntxf)) { biffMovef(MITE, NRRD, "%s: basic nrrd validity check failed", me); return 1; } if (!( nrrdTypeFloat == ntxf->type || nrrdTypeDouble == ntxf->type || nrrdTypeUChar == ntxf->type )) { biffAddf(MITE, "%s: need a type %s, %s or %s nrrd (not %s)", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeDouble), airEnumStr(nrrdType, nrrdTypeUChar), airEnumStr(nrrdType, ntxf->type)); return 1; } if (!( 2 <= ntxf->dim )) { biffAddf(MITE, "%s: nrrd dim (%d) isn't at least 2 (for a 1-D txf)", me, ntxf->dim); return 1; } rangeStr = ntxf->axis[0].label; if (0 == airStrlen(rangeStr)) { biffAddf(MITE, "%s: axis[0]'s label doesn't specify txf range", me); return 1; } if (airStrlen(rangeStr) != ntxf->axis[0].size) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(MITE, "%s: axis[0]'s size %s, but label specifies %s values", me, airSprintSize_t(stmp1, ntxf->axis[0].size), airSprintSize_t(stmp2, airStrlen(rangeStr))); return 1; } for (rii=0; riidim; axi++) { if (1 == ntxf->axis[axi].size) { biffAddf(MITE, "%s: # samples on axis %d must be > 1", me, axi); return 1; } domStr = ntxf->axis[axi].label; if (0 == airStrlen(domStr)) { biffAddf(MITE, "%s: axis[%d] of txf didn't specify a domain variable", me, axi); return 1; } if (miteVariableParse(&isp, domStr)) { biffAddf(MITE, "%s: couldn't parse txf domain \"%s\" for axis %d\n", me, domStr, axi); return 1; } if (!( 1 == isp.kind->table[isp.item].answerLength || 3 == isp.kind->table[isp.item].answerLength )) { biffAddf(MITE, "%s: %s (item %d) not a scalar or vector " "(answerLength = %d): " "can't be a txf domain variable", me, domStr, isp.item, isp.kind->table[isp.item].answerLength); return 1; } if (3 == isp.kind->table[isp.item].answerLength) { /* has to be right length for one of the quantization schemes */ ilog2 = airLog2(ntxf->axis[axi].size); if (-1 == ilog2) { char stmp[AIR_STRLEN_SMALL]; biffAddf(MITE, "%s: txf axis size for %s must be power of 2 (not %s)", me, domStr, airSprintSize_t(stmp, ntxf->axis[axi].size)); return 1; } else { if (!( AIR_IN_CL(8, ilog2, 16) )) { biffAddf(MITE, "%s: log_2 of txf axis size for %s should be in " "range [8,16] (not %d)", me, domStr, ilog2); return 1; } } } else { if (!( AIR_EXISTS(ntxf->axis[axi].min) && AIR_EXISTS(ntxf->axis[axi].max) )) { biffAddf(MITE, "%s: min and max of axis %d aren't both set", me, axi); return 1; } if (!( ntxf->axis[axi].min < ntxf->axis[axi].max )) { biffAddf(MITE, "%s: min (%g) not less than max (%g) on axis %d", me, ntxf->axis[axi].min, ntxf->axis[axi].max, axi); return 1; } } } return 0; } /* ******** miteQueryAdd() ** ** This looks a given gageItemSpec and sets the bits in the ** gageKindScl and tenGageKind queries that are required to calculate ** the quantity ** ** NOTE: This does NOT initialize the query{Scl,Vec,Ten}: it ** just adds on new items to the existing queries ** ** HEY: this is really unfortunate: each new gage type use for ** volume rendering in mite will have to explicitly added as ** arguments here, which is part of the reason that mite may end ** up explicitly depending on the libraries supplying those gageKinds ** (like how mite now must depend on ten) ** ** The queryMite argument is a little odd- its not a real gage kind, ** but we use it to organize which of the miteVal quantities we take ** the time to compute in miteSample(). */ void miteQueryAdd(gageQuery queryScl, gageQuery queryVec, gageQuery queryTen, gageQuery queryMite, gageItemSpec *isp) { static const char me[]="miteQueryAdd"; if (NULL == isp->kind) { /* nothing to add */ } else if (gageKindScl == isp->kind) { GAGE_QUERY_ITEM_ON(queryScl, isp->item); } else if (gageKindVec == isp->kind) { GAGE_QUERY_ITEM_ON(queryVec, isp->item); } else if (tenGageKind == isp->kind) { GAGE_QUERY_ITEM_ON(queryTen, isp->item); } else if (miteValGageKind == isp->kind) { /* regardless of whether the mite query requires scl, vec, or ten queries, we add it to the quantites that have to be computed per-sample */ GAGE_QUERY_ITEM_ON(queryMite, isp->item); /* HEY: some these have useful analogs for tensor data, but I won't be able to express them. This means that while Phong shading of *scalar* volumes can be implemented with transfer functions, it is currently not possible in *tensor* volumes (for instance, using the gradient of fractional anisotropy) */ switch(isp->item) { case miteValVrefN: case miteValNdotV: case miteValNdotL: /* the "N" can be a normalized vector from any of the available kinds (except miteValGageKind!), and its associated query will be handled elsewhere, so there's nothing to add here */ break; case miteValGTdotV: GAGE_QUERY_ITEM_ON(queryScl, gageSclGeomTens); break; case miteValVdefT: GAGE_QUERY_ITEM_ON(queryTen, tenGageTensor); case miteValVdefTdotV: GAGE_QUERY_ITEM_ON(queryTen, tenGageTensor); break; } } else { fprintf(stderr, "%s: PANIC: unrecognized non-NULL gageKind\n", me); exit(1); } return; } int _miteNtxfCopy(miteRender *mrr, miteUser *muu) { static const char me[]="_miteNtxfCopy"; int ni, E; mrr->ntxf = AIR_CALLOC(muu->ntxfNum, Nrrd *); if (!mrr->ntxf) { biffAddf(MITE, "%s: couldn't calloc %d ntxf pointers", me, muu->ntxfNum); return 1; } mrr->ntxfNum = muu->ntxfNum; airMopAdd(mrr->rmop, mrr->ntxf, airFree, airMopAlways); E = 0; for (ni=0; nintxfNum; ni++) { mrr->ntxf[ni] = nrrdNew(); if (!E) airMopAdd(mrr->rmop, mrr->ntxf[ni], (airMopper)nrrdNuke, airMopAlways); if (!( nrrdTypeUChar == muu->ntxf[ni]->type || nrrdTypeFloat == muu->ntxf[ni]->type || nrrdTypeDouble == muu->ntxf[ni]->type )) { biffAddf(MITE, "%s: sorry, can't handle txf of type %s (only %s, %s, %s)", me, airEnumStr(nrrdType, muu->ntxf[ni]->type), airEnumStr(nrrdType, nrrdTypeUChar), airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeDouble)); return 1; } /* note that key/values need to be copied for the sake of identifying a non-default miteStageOp */ switch(muu->ntxf[ni]->type) { case nrrdTypeUChar: if (!E) E |= nrrdUnquantize(mrr->ntxf[ni], muu->ntxf[ni], nrrdTypeUChar); if (!E) E |= nrrdKeyValueCopy(mrr->ntxf[ni], muu->ntxf[ni]); break; case mite_nt: if (!E) E |= nrrdCopy(mrr->ntxf[ni], muu->ntxf[ni]); break; default: /* will be either float or double (whatever mite_nt isn't) */ if (!E) E |= nrrdConvert(mrr->ntxf[ni], muu->ntxf[ni], mite_nt); if (!E) E |= nrrdKeyValueCopy(mrr->ntxf[ni], muu->ntxf[ni]); break; } } if (E) { biffMovef(MITE, NRRD, "%s: troubling copying/converting all ntxfs", me); return 1; } return 0; } int _miteNtxfAlphaAdjust(miteRender *mrr, miteUser *muu) { static const char me[]="_miteNtxfAlphaAdjust"; int ni, ei, ri, nnum, rnum; Nrrd *ntxf; mite_t *data, alpha, frac; if (_miteNtxfCopy(mrr, muu)) { biffAddf(MITE, "%s: trouble copying/converting transfer functions", me); return 1; } frac = muu->rayStep/muu->refStep; for (ni=0; nintxfNum; ni++) { ntxf = mrr->ntxf[ni]; if (!strchr(ntxf->axis[0].label, miteRangeChar[miteRangeAlpha])) { continue; } /* else this txf sets opacity */ data = (mite_t *)ntxf->data; rnum = ntxf->axis[0].size; nnum = nrrdElementNumber(ntxf)/rnum; for (ei=0; eiaxis[0].label[ri] == miteRangeChar[miteRangeAlpha]) { alpha = data[ri + rnum*ei]; data[ri + rnum*ei] = 1 - pow(AIR_MAX(0, 1-alpha), frac); } } } } return 0; } int _miteStageNum(miteRender *mrr) { int num, ni; num = 0; for (ni=0; nintxfNum; ni++) { num += mrr->ntxf[ni]->dim - 1; } return num; } void _miteStageInit(miteStage *stage) { int rii; stage->val = NULL; stage->size = -1; stage->op = miteStageOpUnknown; stage->qn = NULL; stage->min = stage->max = AIR_NAN; stage->data = NULL; for (rii=0; rii<=MITE_RANGE_NUM-1; rii++) { stage->rangeIdx[rii] = -1; } stage->rangeNum = -1; stage->label = NULL; return; } double * _miteAnswerPointer(miteThread *mtt, gageItemSpec *isp) { static const char me[]="_miteAnswerPointer"; double *ret; if (!isp->kind) { /* we got a NULL kind (as happens with output of gageItemSpecNew(), or miteVariableParse of an empty string); only NULL return is sensible */ return NULL; } if (gageKindScl == isp->kind) { ret = mtt->ansScl; } else if (gageKindVec == isp->kind) { ret = mtt->ansVec; } else if (tenGageKind == isp->kind) { ret = mtt->ansTen; } else if (miteValGageKind == isp->kind) { ret = mtt->ansMiteVal; } else { fprintf(stderr, "\nPANIC: %s: unknown gageKind!\n", me); exit(1); } ret += gageKindAnswerOffset(isp->kind, isp->item); return ret; } /* ** _miteStageSet ** ** ALLOCATES and initializes stage array in a miteThread */ int _miteStageSet(miteThread *mtt, miteRender *mrr) { static const char me[]="_miteStageSet"; char *value; int ni, di, stageIdx, rii, stageNum, ilog2; Nrrd *ntxf; miteStage *stage; gageItemSpec isp; char rc; stageNum = _miteStageNum(mrr); /* fprintf(stderr, "!%s: stageNum = %d\n", me, stageNum); */ mtt->stage = AIR_CALLOC(stageNum, miteStage); if (!mtt->stage) { biffAddf(MITE, "%s: couldn't alloc array of %d stages", me, stageNum); return 1; } airMopAdd(mtt->rmop, mtt->stage, airFree, airMopAlways); mtt->stageNum = stageNum; stageIdx = 0; for (ni=0; nintxfNum; ni++) { ntxf = mrr->ntxf[ni]; for (di=ntxf->dim-1; di>=1; di--) { stage = mtt->stage + stageIdx; _miteStageInit(stage); miteVariableParse(&isp, ntxf->axis[di].label); stage->val = _miteAnswerPointer(mtt, &isp); stage->label = ntxf->axis[di].label; /* fprintf(stderr, "!%s: ans=%p + offset[%d]=%d == %p\n", me, mtt->ans, dom, kind->ansOffset[dom], stage->val); */ stage->size = ntxf->axis[di].size; stage->min = ntxf->axis[di].min; stage->max = ntxf->axis[di].max; if (di > 1) { stage->data = NULL; } else { stage->data = (mite_t *)ntxf->data; value = nrrdKeyValueGet(ntxf, "miteStageOp"); if (value) { stage->op = airEnumVal(miteStageOp, value); if (miteStageOpUnknown == stage->op) { stage->op = miteStageOpMultiply; } } else { stage->op = miteStageOpMultiply; } if (1 == isp.kind->table[isp.item].answerLength) { stage->qn = NULL; } else if (3 == isp.kind->table[isp.item].answerLength) { char stmp[AIR_STRLEN_SMALL]; ilog2 = airLog2(ntxf->axis[di].size); switch(ilog2) { case 8: stage->qn = limnVtoQN_d[ limnQN8octa]; break; case 9: stage->qn = limnVtoQN_d[ limnQN9octa]; break; case 10: stage->qn = limnVtoQN_d[limnQN10octa]; break; case 11: stage->qn = limnVtoQN_d[limnQN11octa]; break; case 12: stage->qn = limnVtoQN_d[limnQN12octa]; break; case 13: stage->qn = limnVtoQN_d[limnQN13octa]; break; case 14: stage->qn = limnVtoQN_d[limnQN14octa]; break; case 15: stage->qn = limnVtoQN_d[limnQN15octa]; break; case 16: stage->qn = limnVtoQN_d[limnQN16octa]; break; default: biffAddf(MITE, "%s: txf axis %d size %s not usable for " "vector txf domain variable %s", me, di, airSprintSize_t(stmp, ntxf->axis[di].size), ntxf->axis[di].label); return 1; break; } } else { biffAddf(MITE, "%s: %s not scalar or vector (len = %d): can't be " "a txf domain variable", me, ntxf->axis[di].label, isp.kind->table[isp.item].answerLength); return 1; } stage->rangeNum = ntxf->axis[0].size; for (rii=0; riirangeNum; rii++) { rc = ntxf->axis[0].label[rii]; stage->rangeIdx[rii] = strchr(miteRangeChar, rc) - miteRangeChar; /* fprintf(stderr, "!%s: range: %c -> %d\n", "_miteStageSet", ntxf->axis[0].label[rii], stage->rangeIdx[rii]); */ } } stageIdx++; } } return 0; } void _miteStageRun(miteThread *mtt, miteUser *muu) { static const char me[]="_miteStageRun"; int stageIdx, ri, rii; unsigned int txfIdx, finalIdx; miteStage *stage; mite_t *rangeData; double *dbg=NULL; finalIdx = 0; if (mtt->verbose) { dbg = muu->debug + muu->debugIdx; } for (stageIdx=0; stageIdxstageNum; stageIdx++) { stage = &(mtt->stage[stageIdx]); if (stage->qn) { /* its a vector-valued txf domain variable */ txfIdx = stage->qn(stage->val); /* right now, we can't store vector-valued txf domain variables */ } else { /* its a scalar txf domain variable */ txfIdx = airIndexClamp(stage->min, *(stage->val), stage->max, stage->size); if (mtt->verbose) { fprintf(stderr, "!%s: %s=%g in [%g,%g]/%u -> %u\n", me, stage->label, *(stage->val), stage->min, stage->max, stage->size, txfIdx); dbg[0 + 2*stageIdx] = *(stage->val); } } finalIdx = stage->size*finalIdx + txfIdx; if (mtt->verbose) { dbg[1 + 2*stageIdx] = txfIdx; } if (stage->data) { rangeData = stage->data + stage->rangeNum*finalIdx; for (rii=0; riirangeNum; rii++) { ri = stage->rangeIdx[rii]; switch(stage->op) { case miteStageOpMin: mtt->range[ri] = AIR_MIN(mtt->range[ri], rangeData[rii]); break; case miteStageOpMax: mtt->range[ri] = AIR_MAX(mtt->range[ri], rangeData[rii]); break; case miteStageOpAdd: mtt->range[ri] += rangeData[rii]; break; case miteStageOpMultiply: default: mtt->range[ri] *= rangeData[rii]; break; } } finalIdx = 0; } } return; } teem-1.11.0~svn6057/src/mite/kindnot.c0000664000175000017500000001111612165631065017151 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mite.h" #include "privateMite.h" const char * _miteValStr[] = { "(unknown miteVal)", "Xw", "Xi", "Yw", "Yi", "Zw", "Zi", "Rw", "Ri", "Tw", "Ti", "V", "N", "NdotV", "NdotL", "VrefN", "GTdotV", "VdefT", "VdefTdotV", "WdotD" }; const int _miteValVal[] = { miteValUnknown, miteValXw, miteValXi, miteValYw, miteValYi, miteValZw, miteValZi, miteValRw, miteValRi, miteValTw, miteValTi, miteValView, miteValNormal, miteValNdotV, miteValNdotL, miteValVrefN, miteValGTdotV, miteValVdefT, miteValVdefTdotV, miteValWdotD, }; const char * _miteValStrEqv[] = { "xw", "xi", "yw", "yi", "zw", "zi", "rw", "ri", "tw", "ti", "view", "v", "normal", "n", "ndotv", "vdotn", "ndotl", "ldotn", "vrefn", "gtdotv", "vdeft", "vdeftdotv", "wdotd", "" }; int _miteValValEqv[] = { miteValXw, miteValXi, miteValYw, miteValYi, miteValZw, miteValZi, miteValRw, miteValRi, miteValTw, miteValTi, miteValView, miteValView, miteValNormal, miteValNormal, miteValNdotV, miteValNdotV, miteValNdotL, miteValNdotL, miteValVrefN, miteValGTdotV, miteValVdefT, miteValVdefTdotV, miteValWdotD }; const airEnum _miteVal = { "miteVal", MITE_VAL_ITEM_MAX, _miteValStr, _miteValVal, NULL, _miteValStrEqv, _miteValValEqv, AIR_FALSE }; const airEnum *const miteVal = &_miteVal; /* ** again, this is not a true gageKind- mainly because these items may ** depend on items in different gageKinds (scalar and vector). So, ** the prerequisites here are all blank. Go look in miteQueryAdd() ** to see these items' true prereqs */ gageItemEntry _miteValTable[MITE_VAL_ITEM_MAX+1] = { /* enum value len,deriv, prereqs, parent item, index, needData*/ {miteValUnknown, 0, 0, {0}, 0, 0, AIR_FALSE}, {miteValXw, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValXi, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValYw, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValYi, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValZw, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValZi, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValRw, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValRi, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValTw, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValTi, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValView, 3, 0, {0}, 0, 0, AIR_FALSE}, {miteValNormal, 3, 0, {0}, 0, 0, AIR_FALSE}, {miteValNdotV, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValNdotL, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValVrefN, 3, 0, {0}, 0, 0, AIR_FALSE}, {miteValGTdotV, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValVdefT, 3, 0, {0}, 0, 0, AIR_FALSE}, {miteValVdefTdotV, 1, 0, {0}, 0, 0, AIR_FALSE}, {miteValWdotD, 1, 0, {0}, 0, 0, AIR_FALSE} }; gageKind _miteValGageKind = { AIR_FALSE, "mite", &_miteVal, 0, 0, MITE_VAL_ITEM_MAX, _miteValTable, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; gageKind * miteValGageKind = &_miteValGageKind; teem-1.11.0~svn6057/src/mite/GNUmakefile0000664000175000017500000000357012165631065017416 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := mite #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### (ell for macros) #### $(L).NEED = ten hoover limn gage ell nrrd biff air $(L).PUBLIC_HEADERS = mite.h $(L).PRIVATE_HEADERS = privateMite.h $(L).OBJS = defaultsMite.o kindnot.o txf.o shade.o \ user.o renderMite.o thread.o ray.o #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/mite/sources.cmake0000664000175000017500000000042111113047450020010 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(MITE_SOURCES defaultsMite.c kindnot.c mite.h privateMite.h ray.c renderMite.c shade.c thread.c txf.c user.c ) ADD_TEEM_LIBRARY(mite ${MITE_SOURCES}) teem-1.11.0~svn6057/src/mite/renderMite.c0000664000175000017500000001432512165631065017606 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mite.h" #include "privateMite.h" miteRender * _miteRenderNew(void) { miteRender *mrr; mrr = (miteRender *)calloc(1, sizeof(miteRender)); if (mrr) { mrr->rmop = airMopNew(); if (!mrr->rmop) { airFree(mrr); return NULL; } mrr->ntxf = NULL; mrr->ntxfNum = 0; mrr->sclPvlIdx = -1; mrr->vecPvlIdx = -1; mrr->tenPvlIdx = -1; mrr->normalSpec = gageItemSpecNew(); airMopAdd(mrr->rmop, mrr->normalSpec, (airMopper)gageItemSpecNix, airMopAlways); mrr->shadeSpec = miteShadeSpecNew(); airMopAdd(mrr->rmop, mrr->shadeSpec, (airMopper)miteShadeSpecNix, airMopAlways); mrr->time0 = AIR_NAN; GAGE_QUERY_RESET(mrr->queryMite); mrr->queryMiteNonzero = AIR_FALSE; } return mrr; } miteRender * _miteRenderNix(miteRender *mrr) { if (mrr) { airMopOkay(mrr->rmop); airFree(mrr); } return NULL; } int miteRenderBegin(miteRender **mrrP, miteUser *muu) { static const char me[]="miteRenderBegin"; gagePerVolume *pvl; int E, T, pvlIdx; gageQuery queryScl, queryVec, queryTen; gageItemSpec isp; unsigned int axi, thr; if (!(mrrP && muu)) { biffAddf(MITE, "%s: got NULL pointer", me); return 1; } if (_miteUserCheck(muu)) { biffAddf(MITE, "%s: problem with user-set parameters", me); return 1; } if (!( *mrrP = _miteRenderNew() )) { biffAddf(MITE, "%s: couldn't alloc miteRender", me); return 1; } if (_miteNtxfAlphaAdjust(*mrrP, muu)) { biffAddf(MITE, "%s: trouble copying and alpha-adjusting txfs", me); return 1; } GAGE_QUERY_RESET(queryScl); GAGE_QUERY_RESET(queryVec); GAGE_QUERY_RESET(queryTen); GAGE_QUERY_RESET((*mrrP)->queryMite); for (T=0; TntxfNum; T++) { for (axi=1; axintxf[T]->dim; axi++) { miteVariableParse(&isp, muu->ntxf[T]->axis[axi].label); miteQueryAdd(queryScl, queryVec, queryTen, (*mrrP)->queryMite, &isp); } } miteVariableParse((*mrrP)->normalSpec, muu->normalStr); miteQueryAdd(queryScl, queryVec, queryTen, (*mrrP)->queryMite, (*mrrP)->normalSpec); miteShadeSpecParse((*mrrP)->shadeSpec, muu->shadeStr); miteShadeSpecQueryAdd(queryScl, queryVec, queryTen, (*mrrP)->queryMite, (*mrrP)->shadeSpec); (*mrrP)->queryMiteNonzero = GAGE_QUERY_NONZERO((*mrrP)->queryMite); E = 0; pvlIdx = 0; if (muu->nsin) { if (!E) E |= !(pvl = gagePerVolumeNew(muu->gctx0, muu->nsin, gageKindScl)); if (!E) E |= gageQuerySet(muu->gctx0, pvl, queryScl); if (!E) E |= gagePerVolumeAttach(muu->gctx0, pvl); if (!E) (*mrrP)->sclPvlIdx = pvlIdx++; } if (muu->nvin) { if (!E) E |= !(pvl = gagePerVolumeNew(muu->gctx0, muu->nvin, gageKindVec)); if (!E) E |= gageQuerySet(muu->gctx0, pvl, queryVec); if (!E) E |= gagePerVolumeAttach(muu->gctx0, pvl); if (!E) (*mrrP)->vecPvlIdx = pvlIdx++; } if (muu->ntin) { if (!E) E |= !(pvl = gagePerVolumeNew(muu->gctx0, muu->ntin, tenGageKind)); if (!E) E |= gageQuerySet(muu->gctx0, pvl, queryTen); if (!E) E |= gagePerVolumeAttach(muu->gctx0, pvl); if (!E) (*mrrP)->tenPvlIdx = pvlIdx++; } if (!E) E |= gageKernelSet(muu->gctx0, gageKernel00, muu->ksp[gageKernel00]->kernel, muu->ksp[gageKernel00]->parm); if (!E) E |= gageKernelSet(muu->gctx0, gageKernel11, muu->ksp[gageKernel11]->kernel, muu->ksp[gageKernel11]->parm); if (!E) E |= gageKernelSet(muu->gctx0, gageKernel22, muu->ksp[gageKernel22]->kernel, muu->ksp[gageKernel22]->parm); if (!E) E |= gageUpdate(muu->gctx0); if (E) { biffMovef(MITE, GAGE, "%s: gage trouble", me); return 1; } fprintf(stderr, "!%s: kernel support = %d^3 samples\n", me, 2*muu->gctx0->radius); if (nrrdMaybeAlloc_va(muu->nout, mite_nt, 3, AIR_CAST(size_t, 5) /* RGBAZ */ , AIR_CAST(size_t, muu->hctx->imgSize[0]), AIR_CAST(size_t, muu->hctx->imgSize[1]))) { biffMovef(MITE, NRRD, "%s: nrrd trouble", me); return 1; } muu->nout->axis[1].center = nrrdCenterCell; muu->nout->axis[1].min = muu->hctx->cam->uRange[0]; muu->nout->axis[1].max = muu->hctx->cam->uRange[1]; muu->nout->axis[2].center = nrrdCenterCell; muu->nout->axis[2].min = muu->hctx->cam->vRange[0]; muu->nout->axis[2].max = muu->hctx->cam->vRange[1]; for (thr=0; thrhctx->numThreads; thr++) { (*mrrP)->tt[thr] = miteThreadNew(); if (!((*mrrP)->tt[thr])) { biffAddf(MITE, "%s: couldn't allocate thread[%d]", me, thr); return 1; } airMopAdd((*mrrP)->rmop, (*mrrP)->tt[thr], (airMopper)miteThreadNix, airMopAlways); } (*mrrP)->time0 = airTime(); return 0; } int miteRenderEnd(miteRender *mrr, miteUser *muu) { unsigned int thr; double samples; muu->rendTime = airTime() - mrr->time0; samples = 0; for (thr=0; thrhctx->numThreads; thr++) { samples += mrr->tt[thr]->samples; } muu->sampRate = samples/(1000.0*muu->rendTime); _miteRenderNix(mrr); return 0; } teem-1.11.0~svn6057/src/mite/ray.c0000664000175000017500000002673112165631065016307 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mite.h" #include "privateMite.h" int miteRayBegin(miteThread *mtt, miteRender *mrr, miteUser *muu, int uIndex, int vIndex, double rayLen, double rayStartWorld[3], double rayStartIndex[3], double rayDirWorld[3], double rayDirIndex[3]) { airPtrPtrUnion appu; AIR_UNUSED(mrr); AIR_UNUSED(rayStartWorld); AIR_UNUSED(rayStartIndex); AIR_UNUSED(rayDirIndex); mtt->ui = uIndex; mtt->vi = vIndex; mtt->rayStep = (muu->rayStep*rayLen / (muu->hctx->cam->vspFaar - muu->hctx->cam->vspNeer)); if (!uIndex) { fprintf(stderr, "%d/%d ", vIndex, muu->hctx->imgSize[1]); fflush(stderr); } mtt->verbose = (uIndex == muu->verbUi && vIndex == muu->verbVi); mtt->skip = (muu->verbUi >= 0 && muu->verbVi >= 0 && !mtt->verbose); if (mtt->verbose) { /* create muu->ndebug */ muu->ndebug = nrrdNew(); /* we want to store the value and index for each txf domain variable, plus the RGBAZ computed for that sample */ muu->ndebug->axis[0].size = 2*mtt->stageNum + 5; /* we really do want to associate ndebug with the miteUser's mop, because the information stored in it has to persist for as long as the user wants: mite itself doesn't call miteUserNix */ airMopAdd(muu->umop, muu->ndebug, (airMopper)nrrdNuke, airMopAlways); /* but the scope of the debug array allocation is within this ray */ muu->debugArr = airArrayNew((appu.d = &(muu->debug), appu.v), NULL, sizeof(double), 128); } mtt->raySample = 0; mtt->RR = mtt->GG = mtt->BB = 0.0; mtt->TT = 1.0; mtt->ZZ = AIR_NAN; ELL_3V_SCALE(mtt->V, -1, rayDirWorld); return 0; } void _miteRGBACalc(mite_t *R, mite_t *G, mite_t *B, mite_t *A, miteThread *mtt, miteRender *mrr, miteUser *muu) { static const char me[]="_miteRGBACalc"; mite_t tmp, ad[3], /* ambient+diffuse light contribution */ s[3] = {0,0,0}, /* specular light contribution */ col[3], E, ka, kd, ks, sp, /* txf-determined rendering variables */ LdotN=0, HdotN, H[3], N[3]; /* for lighting calculation */ col[0] = mtt->range[miteRangeRed]; col[1] = mtt->range[miteRangeGreen]; col[2] = mtt->range[miteRangeBlue]; E = mtt->range[miteRangeEmissivity]; ka = mtt->range[miteRangeKa]; kd = mtt->range[miteRangeKd]; ks = mtt->range[miteRangeKs]; ELL_3V_SCALE(ad, ka, muu->lit->amb); switch (mrr->shadeSpec->method) { case miteShadeMethodNone: /* nothing to do */ break; case miteShadeMethodPhong: if (kd || ks) { ELL_3V_NORM(N, mtt->shadeVec0, tmp); if (1 == muu->normalSide) { ELL_3V_SCALE(N, -1, N); } /* else -1==side --> N = -1*-1*N = N or 0==side --> N = N, so there's nothing to do */ if (kd) { LdotN = ELL_3V_DOT(muu->lit->dir[0], N); if (!muu->normalSide) { LdotN = AIR_ABS(LdotN); } if (LdotN > 0) { ELL_3V_SCALE_INCR(ad, LdotN*kd, muu->lit->col[0]); } } if (ks) { sp = mtt->range[miteRangeSP]; ELL_3V_ADD2(H, muu->lit->dir[0], mtt->V); ELL_3V_NORM(H, H, tmp); HdotN = ELL_3V_DOT(H, N); if (!muu->normalSide) { HdotN = AIR_ABS(HdotN); } if (HdotN > 0) { HdotN = pow(HdotN, sp); ELL_3V_SCALE(s, HdotN*ks, muu->lit->col[0]); } } } break; case miteShadeMethodLitTen: fprintf(stderr, "!%s: lit-tensor not yet implemented\n", me); break; default: fprintf(stderr, "!%s: PANIC, shadeMethod %d unimplemented\n", me, mrr->shadeSpec->method); exit(1); break; } *R = (E - 1 + ad[0])*col[0] + s[0]; *G = (E - 1 + ad[1])*col[1] + s[1]; *B = (E - 1 + ad[2])*col[2] + s[2]; *A = mtt->range[miteRangeAlpha]; *A = AIR_CLAMP(0.0, *A, 1.0); /* if (mtt->verbose) { fprintf(stderr, "%s: col[] = %g,%g,%g; A,E = %g,%g; Kads = %g,%g,%g\n", me, col[0], col[1], col[2], mtt->range[miteRangeAlpha], E, ka, kd, ks); fprintf(stderr, "%s: N = (%g,%g,%g), L = (%g,%g,%g) ---> LdotN = %g\n", me, N[0], N[1], N[2], muu->lit->dir[0][0], muu->lit->dir[0][1], muu->lit->dir[0][2], LdotN); fprintf(stderr, "%s: ad[] = %g,%g,%g\n", me, ad[0], ad[1], ad[2]); fprintf(stderr, "%s: --> R,G,B,A = %g,%g,%g,%g\n", me, *R, *G, *B, *A); } */ return; } double miteSample(miteThread *mtt, miteRender *mrr, miteUser *muu, int num, double rayT, int inside, double samplePosWorld[3], double samplePosIndex[3]) { static const char me[]="miteSample"; mite_t R, G, B, A; double *NN; double NdotV, kn[3], knd[3], ref[3], len, *dbg=NULL; if (!inside) { return mtt->rayStep; } if (mtt->skip) { /* we have one verbose pixel, but we're not on it */ return 0.0; } /* early ray termination */ if (1-mtt->TT >= muu->opacNear1) { mtt->TT = 0.0; return 0.0; } /* set (fake) view based on fake from */ if (AIR_EXISTS(muu->fakeFrom[0])) { ELL_3V_SUB(mtt->V, samplePosWorld, muu->fakeFrom); ELL_3V_NORM(mtt->V, mtt->V, len); } /* do probing at this location to determine values of everything that might appear in the txf domain */ if (gageProbe(mtt->gctx, samplePosIndex[0], samplePosIndex[1], samplePosIndex[2])) { biffAddf(MITE, "%s: gage trouble: %s (%d)", me, mtt->gctx->errStr, mtt->gctx->errNum); return AIR_NAN; } if (mrr->queryMiteNonzero) { /* There is some optimal trade-off between slowing things down with too many branches on all possible checks of queryMite, and slowing things down with doing the work of setting them all. This code has not been profiled whatsoever */ mtt->directAnsMiteVal[miteValXw][0] = samplePosWorld[0]; mtt->directAnsMiteVal[miteValXi][0] = samplePosIndex[0]; mtt->directAnsMiteVal[miteValYw][0] = samplePosWorld[1]; mtt->directAnsMiteVal[miteValYi][0] = samplePosIndex[1]; mtt->directAnsMiteVal[miteValZw][0] = samplePosWorld[2]; mtt->directAnsMiteVal[miteValZi][0] = samplePosIndex[2]; mtt->directAnsMiteVal[miteValRw][0] = ELL_3V_LEN(samplePosWorld); mtt->directAnsMiteVal[miteValRi][0] = ELL_3V_LEN(samplePosIndex); mtt->directAnsMiteVal[miteValTw][0] = rayT; mtt->directAnsMiteVal[miteValTi][0] = num; ELL_3V_COPY(mtt->directAnsMiteVal[miteValView], mtt->V); NN = mtt->directAnsMiteVal[miteValNormal]; if (mtt->_normal) { if (1 == muu->normalSide) { ELL_3V_SCALE(NN, -1, mtt->_normal); } else { ELL_3V_COPY(NN, mtt->_normal); } } if ((GAGE_QUERY_ITEM_TEST(mrr->queryMite, miteValNdotV) || GAGE_QUERY_ITEM_TEST(mrr->queryMite, miteValNdotL) || GAGE_QUERY_ITEM_TEST(mrr->queryMite, miteValVrefN))) { mtt->directAnsMiteVal[miteValNdotV][0] = ELL_3V_DOT(NN, mtt->V); mtt->directAnsMiteVal[miteValNdotL][0] = ELL_3V_DOT(NN, muu->lit->dir[0]); if (!muu->normalSide) { mtt->directAnsMiteVal[miteValNdotV][0] = AIR_ABS(mtt->directAnsMiteVal[miteValNdotV][0]); mtt->directAnsMiteVal[miteValNdotL][0] = AIR_ABS(mtt->directAnsMiteVal[miteValNdotL][0]); } NdotV = mtt->directAnsMiteVal[miteValNdotV][0]; ELL_3V_SCALE_ADD2(ref, 2*NdotV, NN, -1, mtt->V); ELL_3V_NORM(mtt->directAnsMiteVal[miteValVrefN], ref, len); } if (GAGE_QUERY_ITEM_TEST(mrr->queryMite, miteValGTdotV)) { ELL_3MV_MUL(kn, mtt->nPerp, mtt->V); ELL_3V_NORM(kn, kn, len); ELL_3MV_MUL(knd, mtt->geomTens, kn); mtt->directAnsMiteVal[miteValGTdotV][0] = ELL_3V_DOT(knd, kn); } } /* initialize txf range quantities, and apply all txfs */ if (mtt->verbose) { muu->debugIdx = airArrayLenIncr(muu->debugArr, muu->ndebug->axis[0].size); } memcpy(mtt->range, muu->rangeInit, MITE_RANGE_NUM*sizeof(mite_t)); _miteStageRun(mtt, muu); /* if there's opacity, do shading and compositing */ if (mtt->range[miteRangeAlpha]) { /* fprintf(stderr, "%s: mtt->TT = %g\n", me, mtt->TT); */ /* if (mtt->verbose) { fprintf(stderr, "%s: before compositing: RGBT = %g,%g,%g,%g\n", me, mtt->RR, mtt->GG, mtt->BB, mtt->TT); } */ _miteRGBACalc(&R, &G, &B, &A, mtt, mrr, muu); mtt->RR += mtt->TT*A*R; mtt->GG += mtt->TT*A*G; mtt->BB += mtt->TT*A*B; mtt->TT *= 1-A; /* if (mtt->verbose) { fprintf(stderr, "%s: after compositing: RGBT = %g,%g,%g,%g\n", me, mtt->RR, mtt->GG, mtt->BB, mtt->TT); } */ /* fprintf(stderr, "%s: mtt->TT = %g\n", me, mtt->TT); */ } else { R = G = B = A = 0; } if (mtt->verbose) { dbg = muu->debug + muu->debugIdx; dbg[0 + 2*mtt->stageNum] = R; dbg[1 + 2*mtt->stageNum] = G; dbg[2 + 2*mtt->stageNum] = B; dbg[3 + 2*mtt->stageNum] = A; dbg[4 + 2*mtt->stageNum] = rayT; } /* set Z if it hasn't been set already */ if (1-mtt->TT >= muu->opacMatters && !AIR_EXISTS(mtt->ZZ)) { mtt->ZZ = rayT; } /* this is used to index mtt->debug */ mtt->raySample += 1; return mtt->rayStep; } int miteRayEnd(miteThread *mtt, miteRender *mrr, miteUser *muu) { int idx, slen, stageIdx; mite_t *imgData; double A; AIR_UNUSED(mrr); mtt->samples += mtt->raySample; idx = mtt->ui + (muu->nout->axis[1].size)*mtt->vi; imgData = (mite_t*)muu->nout->data; A = 1 - mtt->TT; if (A) { ELL_5V_SET(imgData + 5*idx, mtt->RR/A, mtt->GG/A, mtt->BB/A, A, mtt->ZZ); } else { ELL_5V_SET(imgData + 5*idx, 0, 0, 0, 0, AIR_NAN); } if (mtt->verbose) { /* muu->debug may be over-allocated, but that's harmless */ muu->ndebug->axis[1].size = mtt->raySample; nrrdWrap_va(muu->ndebug, muu->debug, nrrdTypeDouble, 2, AIR_CAST(size_t, muu->ndebug->axis[0].size), AIR_CAST(size_t, mtt->raySample)); airArrayNix(muu->debugArr); slen = 0; for (stageIdx=0; stageIdxstageNum; stageIdx++) { slen += strlen(mtt->stage[stageIdx].label) + 2; } slen += strlen("R,G,B,A,Z") + 1; muu->ndebug->axis[0].label = (char *)calloc(slen, sizeof(char)); for (stageIdx=0; stageIdxstageNum; stageIdx++) { strcat(muu->ndebug->axis[0].label, mtt->stage[stageIdx].label); strcat(muu->ndebug->axis[0].label, ",,"); } strcat(muu->ndebug->axis[0].label, "R,G,B,A,Z"); } return 0; } teem-1.11.0~svn6057/src/mite/thread.c0000664000175000017500000001263112165631065016755 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mite.h" #include "privateMite.h" miteThread * miteThreadNew() { static const char me[]="miteThreadNew"; miteThread *mtt; int ii; mtt = (miteThread *)calloc(1, sizeof(miteThread)); if (!mtt) { biffAddf(MITE, "%s: couldn't calloc miteThread", me); return NULL; } mtt->rmop = airMopNew(); if (!mtt->rmop) { biffAddf(MITE, "%s: couldn't calloc thread's mop", me); airFree(mtt); return NULL; } mtt->gctx = NULL; mtt->ansScl = mtt->ansVec = mtt->ansTen = NULL; mtt->_normal = NULL; mtt->shadeVec0 = NULL; mtt->shadeVec1 = NULL; mtt->shadeScl0 = NULL; mtt->shadeScl1 = NULL; /* were miteVal a full-fledged gageKind, the following would be done by gagePerVolumeNew */ mtt->ansMiteVal = AIR_CALLOC(gageKindTotalAnswerLength(miteValGageKind), double); mtt->directAnsMiteVal = AIR_CALLOC(miteValGageKind->itemMax+1, double *); if (!(mtt->ansMiteVal && mtt->directAnsMiteVal)) { biffAddf(MITE, "%s: couldn't calloc miteVal answer arrays", me); return NULL; } for (ii=0; ii<=miteValGageKind->itemMax; ii++) { mtt->directAnsMiteVal[ii] = mtt->ansMiteVal + gageKindAnswerOffset(miteValGageKind, ii); } mtt->verbose = 0; mtt->skip = 0; mtt->thrid = -1; mtt->ui = mtt->vi = -1; mtt->raySample = 0; mtt->samples = 0; mtt->stage = NULL; /* mtt->range[], rayStep, V, RR, GG, BB, TT initialized in miteRayBegin or in miteSample */ return mtt; } miteThread * miteThreadNix(miteThread *mtt) { mtt->ansMiteVal = (double *)airFree(mtt->ansMiteVal); mtt->directAnsMiteVal = (double **)airFree(mtt->directAnsMiteVal); airMopOkay(mtt->rmop); airFree(mtt); return NULL; } /* ******** miteThreadBegin() ** ** this has some of the body of what would be miteThreadInit */ int miteThreadBegin(miteThread **mttP, miteRender *mrr, miteUser *muu, int whichThread) { static const char me[]="miteThreadBegin"; /* all the miteThreads have already been allocated */ (*mttP) = mrr->tt[whichThread]; if (!whichThread) { /* this is the first thread- it just points to the parent gageContext */ (*mttP)->gctx = muu->gctx0; } else { /* we have to generate a new gageContext */ (*mttP)->gctx = gageContextCopy(muu->gctx0); if (!(*mttP)->gctx) { biffMovef(MITE, GAGE, "%s: couldn't set up thread %d", me, whichThread); return 1; } } if (-1 != mrr->sclPvlIdx) { (*mttP)->ansScl = (*mttP)->gctx->pvl[mrr->sclPvlIdx]->answer; (*mttP)->nPerp = ((*mttP)->ansScl + gageKindAnswerOffset(gageKindScl, gageSclNPerp)); (*mttP)->geomTens = ((*mttP)->ansScl + gageKindAnswerOffset(gageKindScl, gageSclGeomTens)); } else { (*mttP)->ansScl = NULL; (*mttP)->nPerp = NULL; (*mttP)->geomTens = NULL; } (*mttP)->ansVec = (-1 != mrr->vecPvlIdx ? (*mttP)->gctx->pvl[mrr->vecPvlIdx]->answer : NULL); (*mttP)->ansTen = (-1 != mrr->tenPvlIdx ? (*mttP)->gctx->pvl[mrr->tenPvlIdx]->answer : NULL); (*mttP)->thrid = whichThread; (*mttP)->raySample = 0; (*mttP)->samples = 0; (*mttP)->verbose = 0; (*mttP)->skip = 0; (*mttP)->_normal = _miteAnswerPointer(*mttP, mrr->normalSpec); /* set up shading answers */ switch(mrr->shadeSpec->method) { case miteShadeMethodNone: /* nothing to do */ break; case miteShadeMethodPhong: (*mttP)->shadeVec0 = _miteAnswerPointer(*mttP, mrr->shadeSpec->vec0); break; case miteShadeMethodLitTen: (*mttP)->shadeVec0 = _miteAnswerPointer(*mttP, mrr->shadeSpec->vec0); (*mttP)->shadeVec1 = _miteAnswerPointer(*mttP, mrr->shadeSpec->vec1); (*mttP)->shadeScl0 = _miteAnswerPointer(*mttP, mrr->shadeSpec->scl0); (*mttP)->shadeScl1 = _miteAnswerPointer(*mttP, mrr->shadeSpec->scl1); break; default: biffAddf(MITE, "%s: shade method %d not implemented!", me, mrr->shadeSpec->method); return 1; break; } if (_miteStageSet(*mttP, mrr)) { biffAddf(MITE, "%s: trouble setting up stage array", me); return 1; } return 0; } int miteThreadEnd(miteThread *mtt, miteRender *mrr, miteUser *muu) { AIR_UNUSED(mtt); AIR_UNUSED(mrr); AIR_UNUSED(muu); return 0; } teem-1.11.0~svn6057/src/mite/defaultsMite.c0000664000175000017500000000261112165631065020131 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mite.h" #include "privateMite.h" const int mitePresent = 42; const char * miteBiffKey = "mite"; double miteDefRefStep = 0.01; int miteDefRenorm = AIR_FALSE; int miteDefNormalSide = 1; double miteDefOpacNear1 = 0.98; double miteDefOpacMatters = 0.05; teem-1.11.0~svn6057/src/biff/0000775000175000017500000000000012203513754015304 5ustar domibeldomibelteem-1.11.0~svn6057/src/biff/privateBiff.h0000664000175000017500000000362212165631065017724 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef BIFF_PRIVATE_HAS_BEEN_INCLUDED #define BIFF_PRIVATE_HAS_BEEN_INCLUDED #ifdef __cplusplus extern "C" { #endif /* ** This private header exists because these functions are used in ** the biff sources, but no where else. Also, they take a va_list, ** which is unusual, and (currently) used for no other public functions ** in Teem. Use of va_list args complicates python wrapping (at least ** with the current ctypeslib mechanism), so these functions are being ** taken out of the public API. */ /* biffmsg.c */ extern void _biffMsgAddVL(biffMsg *msg, const char *errfmt, va_list args); extern void _biffMsgMoveVL(biffMsg *dest, biffMsg *src, const char *errfmt, va_list args); #ifdef __cplusplus } #endif #endif /* BIFF_PRIVATE_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/biff/biffbiff.c0000664000175000017500000002462112165631065017215 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "biff.h" #include "privateBiff.h" /* ** Until Teem has its own printf implementation, this will have to do; ** it is imperfect because these are not functionally identical. */ #if defined(WIN32) || defined(_WIN32) # define snprintf _snprintf #endif static biffMsg ** _bmsg=NULL; /* master array of biffMsg pointers */ static unsigned int _bmsgNum=0; /* length of _biffErr == # keys maintained */ static airArray * _bmsgArr=NULL; /* air array of _biffErr and _biffNum */ #define __INCR 2 typedef union { biffMsg ***b; void **v; } _beu; /* ** _bmsgStart() ** ** allocates data structers needed by biff. Panics if ** anything goes wrong. ** ** NOTE: Can be harmlessly called multiple times. */ static void _bmsgStart(void) { static const char me[]="[biff] _bmsgStart"; _beu uu; if (_bmsgArr) { /* its non-NULL, must have been called already */ return; } uu.b = &_bmsg; _bmsgArr = airArrayNew(uu.v, &_bmsgNum, sizeof(biffMsg*), __INCR); if (!_bmsgArr) { fprintf(stderr, "%s: PANIC: couldn't allocate internal data\n", me); /* exit(1); */ } /* airArrayPointerCB(_bmsgArr, NULL, (airMopper)biffMsgNix);*/ return; } static void _bmsgFinish(void) { if (_bmsgArr) { /* setting _bmsgArr to NULL is needed to put biff back in initial state so that next calls to biff re-trigger _bmsgStart() */ _bmsgArr = airArrayNuke(_bmsgArr); } return; } /* ** _bmsgFind() ** ** returns the biffMsg (in _bmsg) of the entry with the given key, or ** NULL if it was not found */ static biffMsg * _bmsgFind(const char *key) { static const char me[]="[biff] _bmsgFind"; biffMsg *msg; unsigned int ii; if (!key) { fprintf(stderr, "%s: PANIC got NULL key", me); return NULL; /* exit(1); */ } msg = NULL; if (_bmsgNum) { for (ii=0; ii<_bmsgNum; ii++) { if (!strcmp(_bmsg[ii]->key, key)) { msg = _bmsg[ii]; break; } } } return msg; } /* ** assumes that msg really is in _bmsg[] */ static unsigned int _bmsgFindIdx(biffMsg *msg) { unsigned int ii; for (ii=0; ii<_bmsgNum; ii++) { if (msg == _bmsg[ii]) { break; } } return ii; } /* ** _bmsgAdd() ** ** if given key already has a biffMsg in _bmsg, returns that. ** otherise, adds a new biffMsg for given key to _bmsg, and returns it ** panics if there is a problem */ static biffMsg * _bmsgAdd(const char *key) { static const char me[]="[biff] _bmsgAdd"; unsigned int ii; biffMsg *msg; msg = NULL; /* find if key exists already */ for (ii=0; ii<_bmsgNum; ii++) { if (!strcmp(key, _bmsg[ii]->key)) { msg = _bmsg[ii]; break; } } if (!msg) { /* have to add new biffMsg */ ii = airArrayLenIncr(_bmsgArr, 1); if (!_bmsg) { fprintf(stderr, "%s: PANIC: couldn't accommodate one more key\n", me); return NULL; /* exit(1); */ } msg = _bmsg[ii] = biffMsgNew(key); } return msg; } /***********************************************************************/ /***********************************************************************/ /* ******** biffAdd() ** ** Adds string "err" at key "key", whether or not there are any ** existing messages there. Since biffSet() was killed ** Wed Apr 20 11:11:51 EDT 2005, this has become the main biff ** function. */ void biffAdd(const char *key, const char *err) { biffMsg *msg; _bmsgStart(); msg = _bmsgAdd(key); biffMsgAdd(msg, err); return; } static void _biffAddVL(const char *key, const char *errfmt, va_list args) { biffMsg *msg; _bmsgStart(); msg = _bmsgAdd(key); _biffMsgAddVL(msg, errfmt, args); return; } /* ******** biffAddf() ** ** Adds string "err" at key "key", whether or not there are any ** existing messages there. This version accepts a printf style ** format string as input. */ void biffAddf(const char *key, const char *errfmt, ...) { va_list args; va_start(args, errfmt); _biffAddVL(key, errfmt, args); va_end(args); return; } #if 0 /* ******** biffAddf_e ** ** calls (eventually) biffMsgAdd if msg is non-NULL, otherwise calls ** biffAdd if msg is NULL. */ void biffAddf_e(biffMsg *msg, const char *key, const char *errfmt, ...) { va_list args; va_start(args, errfmt); if (msg) { _biffMsgAddVL(msg, errfmt, args); } else { _biffAddVL(key, errfmt, args); } va_end(args); return; } #endif /* ******** biffMaybeAdd() ** ** wrapper around biffAdd() but doesn't actually do anything if !useBiff */ void biffMaybeAdd(const char *key, const char *err, int useBiff) { if (useBiff) { biffAdd(key, err); } return; } void biffMaybeAddf(int useBiff, const char *key, const char *errfmt, ...) { va_list args; va_start(args, errfmt); if (useBiff) { _biffAddVL(key, errfmt, args); } va_end(args); return; } /* ******** biffGet() ** ** creates a string which records all the errors at given key and ** returns it. Returns NULL in case of error. This function should ** be considered a glorified strdup(): it is the callers responsibility ** to free() this string later */ char * /*Teem: allocates char* */ /* this comment is an experiment */ biffGet(const char *key) { static const char me[]="biffGet"; char *ret; biffMsg *msg; _bmsgStart(); msg = _bmsgFind(key); if (!msg) { static const char err[]="[%s] No information for this key!"; size_t errlen; fprintf(stderr, "%s: WARNING: no information for key \"%s\"\n", me, key); errlen = strlen(err)+strlen(key)+1; ret = AIR_CALLOC(errlen, char); if (!ret) { fprintf(stderr, "%s: PANIC: unable to allocate buffer\n", me); return NULL; /* exit(1); */ } snprintf(ret, errlen, err, key); return ret; } ret = AIR_CALLOC(biffMsgStrlen(msg)+1, char); if (!ret) { fprintf(stderr, "%s: PANIC: unable to allocate buffer\n", me); return NULL; /* exit(1); */ } biffMsgStrSet(ret, msg); return ret; } /* ******** biffGetStrlen() ** ** for when you want to allocate the buffer for the biff string, this is ** how you learn its length */ unsigned int biffGetStrlen(const char *key) { static const char me[]="biffGetStrlen"; biffMsg *msg; unsigned int len; _bmsgStart(); msg = _bmsgFind(key); if (!msg) { fprintf(stderr, "%s: WARNING: no information for key \"%s\"\n", me, key); return 0; } len = biffMsgStrlen(msg); len += 1; /* GLK forgets if the convention is that the caller allocates for one more to include '\0'; this is safer */ return len; } /* ******** biffSetStr() ** ** for when you want to allocate the buffer for the biff string, this is ** how you get the error message itself */ void biffSetStr(char *str, const char *key) { static const char me[]="biffSetStr"; biffMsg *msg; if (!str) { fprintf(stderr, "%s: ERROR: got NULL buffer for \"%s\"\n", me, key); return; } _bmsgStart(); msg = _bmsgFind(key); if (!msg) { fprintf(stderr, "%s: WARNING: no information for key \"%s\"\n", me, key); return; } biffMsgStrSet(str, msg); return; } /* ******** biffCheck() ** ** sees how many messages there are for a given key; ** Note that this is just a simple wrapper around biffMsgErrNum */ unsigned int biffCheck(const char *key) { _bmsgStart(); return biffMsgErrNum(_bmsgFind(key)); } /* ******** biffDone() ** ** frees everything associated with given key, and shrinks list of keys, ** and calls _bmsgFinish() if there are no keys left */ void biffDone(const char *key) { static const char me[]="biffDone"; unsigned int idx; biffMsg *msg; _bmsgStart(); msg = _bmsgFind(key); if (!msg) { fprintf(stderr, "%s: WARNING: no information for key \"%s\"\n", me, key); return; } idx = _bmsgFindIdx(msg); biffMsgNix(msg); if (_bmsgNum > 1) { /* if we have more than one key in action, move the last biffMsg to the position that was just cleared up */ _bmsg[idx] = _bmsg[_bmsgNum-1]; } airArrayLenIncr(_bmsgArr, -1); /* if that was the last key, close shop */ if (!_bmsgArr->len) { _bmsgFinish(); } return; } void biffMove(const char *destKey, const char *err, const char *srcKey) { static const char me[]="biffMove"; biffMsg *dest, *src; _bmsgStart(); dest = _bmsgAdd(destKey); src = _bmsgFind(srcKey); if (!src) { fprintf(stderr, "%s: WARNING: key \"%s\" unknown\n", me, srcKey); return; } biffMsgMove(dest, src, err); return; } static void _biffMoveVL(const char *destKey, const char *srcKey, const char *errfmt, va_list args) { static const char me[]="biffMovev"; biffMsg *dest, *src; _bmsgStart(); dest = _bmsgAdd(destKey); src = _bmsgFind(srcKey); if (!src) { fprintf(stderr, "%s: WARNING: key \"%s\" unknown\n", me, srcKey); return; } _biffMsgMoveVL(dest, src, errfmt, args); return; } void biffMovef(const char *destKey, const char *srcKey, const char *errfmt, ...) { va_list args; va_start(args, errfmt); _biffMoveVL(destKey, srcKey, errfmt, args); va_end(args); return; } char * biffGetDone(const char *key) { char *ret; _bmsgStart(); ret = biffGet(key); biffDone(key); /* will call _bmsgFinish if this is the last key */ return ret; } /* ---- BEGIN non-NrrdIO */ void biffSetStrDone(char *str, const char *key) { _bmsgStart(); biffSetStr(str, key); biffDone(key); /* will call _bmsgFinish if this is the last key */ return; } /* ---- END non-NrrdIO */ /* this is the end */ teem-1.11.0~svn6057/src/biff/biff.h0000664000175000017500000001126012165631065016366 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef BIFF_HAS_BEEN_INCLUDED #define BIFF_HAS_BEEN_INCLUDED /* ---- BEGIN non-NrrdIO */ #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(biff_EXPORTS) || defined(teem_EXPORTS) # define BIFF_EXPORT extern __declspec(dllexport) # else # define BIFF_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define BIFF_EXPORT extern #endif /* ---- END non-NrrdIO */ #ifdef __cplusplus extern "C" { #endif /* ** biffMsg struct ** ** externally usable thing for holding error messages */ typedef struct { char *key; /* string for identifying the general source of the error message; set once, at time of biffMsg creation */ char **err; /* array of error strings; the err array itself is NOT null-terminated */ unsigned int errNum; /* length of "err" == # strings stored */ airArray *errArr; /* air array for err and num */ } biffMsg; /* biffmsg.c */ /* ---- BEGIN non-NrrdIO */ BIFF_EXPORT const int biffPresent; /* ---- END non-NrrdIO */ BIFF_EXPORT biffMsg *biffMsgNew(const char *key); BIFF_EXPORT biffMsg *biffMsgNix(biffMsg *msg); BIFF_EXPORT void biffMsgAdd(biffMsg *msg, const char *err); BIFF_EXPORT void biffMsgClear(biffMsg *msg); BIFF_EXPORT unsigned int biffMsgLineLenMax(const biffMsg *msg); BIFF_EXPORT void biffMsgMove(biffMsg *dest, biffMsg *src, const char *err); /* ---- BEGIN non-NrrdIO */ BIFF_EXPORT void biffMsgAddf(biffMsg *msg, const char *errfmt, ...) #ifdef __GNUC__ __attribute__ ((format(printf,2,3))) #endif ; BIFF_EXPORT void biffMsgMovef(biffMsg *dest, biffMsg *src, const char *errfmt, ...) #ifdef __GNUC__ __attribute__ ((format(printf,3,4))) #endif ; /* ---- END non-NrrdIO */ BIFF_EXPORT unsigned int biffMsgErrNum(const biffMsg *msg); BIFF_EXPORT unsigned int biffMsgStrlen(const biffMsg *msg); BIFF_EXPORT void biffMsgStrSet(char *ret, const biffMsg *msg); /* ---- BEGIN non-NrrdIO */ BIFF_EXPORT char *biffMsgStrAlloc(const biffMsg *msg); BIFF_EXPORT char *biffMsgStrGet(const biffMsg *msg); /* ---- END non-NrrdIO */ BIFF_EXPORT biffMsg *biffMsgNoop; /* biffbiff.c */ BIFF_EXPORT void biffAdd(const char *key, const char *err); BIFF_EXPORT void biffAddf(const char *key, const char *errfmt, ...) #ifdef __GNUC__ __attribute__ ((format(printf,2,3))) #endif ; BIFF_EXPORT void biffMaybeAdd(const char *key, const char *err, int useBiff); BIFF_EXPORT void biffMaybeAddf(int useBiff, const char *key, const char *errfmt, ... ) #ifdef __GNUC__ __attribute__ ((format(printf,3,4))) #endif ; BIFF_EXPORT char *biffGet(const char *key); BIFF_EXPORT unsigned int biffGetStrlen(const char *key); BIFF_EXPORT void biffSetStr(char *str, const char *key); /* ---- BEGIN non-NrrdIO */ BIFF_EXPORT unsigned int biffCheck(const char *key); BIFF_EXPORT void biffMove(const char *destKey, const char *err, const char *srcKey); BIFF_EXPORT void biffMovef(const char *destKey, const char *srcKey, const char *errfmt, ...) #ifdef __GNUC__ __attribute__ ((format(printf,3,4))) #endif ; BIFF_EXPORT void biffSetStrDone(char *str, const char *key); /* ---- END non-NrrdIO */ BIFF_EXPORT void biffDone(const char *key); BIFF_EXPORT char *biffGetDone(const char *key); #ifdef __cplusplus } #endif #endif /* BIFF_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/biff/GNUmakefile0000664000175000017500000000343012165631065017361 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := biff #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### $(L).NEED = air $(L).PUBLIC_HEADERS = biff.h $(L).PRIVATE_HEADERS = privateBiff.h $(L).OBJS = biffmsg.o biffbiff.o $(L).TESTS = test/test #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/biff/biffmsg.c0000664000175000017500000001757312165631065017105 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "biff.h" #include "privateBiff.h" /* ---- BEGIN non-NrrdIO */ const int biffPresent = 42; /* ---- END non-NrrdIO */ /* ** with the Nov'09 re-write of biff, this sourcefile becomes the only ** place where a static buffer is used for message handling; this ** should eventually be avoided by using things like asprintf and ** vasprintf which allocated the string as needed */ #define _HACK_STRLEN AIR_STRLEN_HUGE #define _MSG_INCR 2 biffMsg * biffMsgNew(const char *key) { static const char me[]="biffMsgNew"; biffMsg *msg; if (!key) { fprintf(stderr, "%s: PANIC got NULL key\n", me); return NULL; /* exit(1); */ } msg = AIR_CALLOC(1, biffMsg); if (msg) { airPtrPtrUnion appu; msg->key = airStrdup(key); msg->err = NULL; msg->errNum = 0; appu.cp = &(msg->err); msg->errArr = airArrayNew(appu.v, &(msg->errNum), sizeof(char*), _MSG_INCR); if (msg->errArr) { airArrayPointerCB(msg->errArr, NULL, airFree); } } if (!( msg && msg->key && msg->errArr )) { fprintf(stderr, "%s: PANIC couldn't calloc new msg\n", me); return NULL; /* exit(1); */ } return msg; } biffMsg * biffMsgNix(biffMsg *msg) { if (msg && msg != biffMsgNoop) { airFree(msg->key); airArrayLenSet(msg->errArr, 0); /* frees all msg->err[i] */ airArrayNuke(msg->errArr); airFree(msg); } return NULL; } /* ** adds a given message to the given entry. The message is processed to ** convert all whitespace into ' ', and to eliminate whitespace at the ** end of the message. */ void biffMsgAdd(biffMsg *msg, const char *err) { static const char me[]="biffMsgAdd"; unsigned int idx; if (biffMsgNoop == msg) { return; } if (!( msg && err )) { fprintf(stderr, "%s: PANIC got NULL msg (%p) or err (%p)\n", me, AIR_VOIDP(msg), AIR_CVOIDP(err)); /* exit(1); */ } idx = airArrayLenIncr(msg->errArr, 1); if (!msg->err) { fprintf(stderr, "%s: PANIC: couldn't add message to %s\n", me, msg->key); /* exit(1); */ } if (!( msg->err[idx] = airOneLinify(airStrdup(err)) )) { fprintf(stderr, "%s: PANIC: couldn't alloc message to %s\n", me, msg->key); /* exit(1); */ } return; } void _biffMsgAddVL(biffMsg *msg, const char *errfmt, va_list args) { char errstr[_HACK_STRLEN]; vsprintf(errstr, errfmt, args); biffMsgAdd(msg, errstr); return; } /* ---- BEGIN non-NrrdIO */ void biffMsgAddf(biffMsg *msg, const char *errfmt, ...) { va_list args; va_start(args, errfmt); _biffMsgAddVL(msg, errfmt, args); va_end(args); return; } /* ---- END non-NrrdIO */ void biffMsgClear(biffMsg *msg) { if (biffMsgNoop == msg) { return; } airArrayLenSet(msg->errArr, 0); /* frees all msg->err[i] */ /* but msg->key stays allocated */ return; } /* ** max length of line formatted "[] \n" */ unsigned int biffMsgLineLenMax(const biffMsg *msg) { unsigned int ii, len, maxlen; if (biffMsgNoop == msg) { return 0; } maxlen = 0; for (ii=0; iierrNum; ii++) { len = AIR_UINT(strlen(msg->err[ii]) + strlen(msg->key) + strlen("[] \n")); maxlen = AIR_MAX(maxlen, len); } return maxlen; } /* ******** biffMsgMove ** ** "src" is not const because we clear it after moving things out */ void biffMsgMove(biffMsg *dest, biffMsg *src, const char *err) { static const char me[]="biffMsgMove"; unsigned int ii; char *buff; if (biffMsgNoop == dest || biffMsgNoop == src) { return; } if (!( dest && src )) { fprintf(stderr, "%s: PANIC got NULL msg (%p %p)\n", me, AIR_VOIDP(dest), AIR_VOIDP(src)); /* exit(1); */ } /* if src and dest are same, this degenerates to biffMsgAdd */ if (dest == src && airStrlen(err)) { biffMsgAdd(dest, err); return; } buff = AIR_CALLOC(biffMsgLineLenMax(src)+1, char); if (!buff) { fprintf(stderr, "%s: PANIC: can't allocate buffer\n", me); /* exit(1); */ } for (ii=0; iierrNum; ii++) { sprintf(buff, "[%s] %s", src->key, src->err[ii]); biffMsgAdd(dest, buff); } free(buff); biffMsgClear(src); if (airStrlen(err)) { biffMsgAdd(dest, err); } return; } void _biffMsgMoveVL(biffMsg *dest, biffMsg *src, const char *errfmt, va_list args) { char errstr[_HACK_STRLEN]; vsprintf(errstr, errfmt, args); biffMsgMove(dest, src, errstr); return; } void biffMsgMovef(biffMsg *dest, biffMsg *src, const char *errfmt, ...) { va_list args; va_start(args, errfmt); _biffMsgMoveVL(dest, src, errfmt, args); va_end(args); return; } /* ******** biffMsgErrNum ** ** returns number of errors in a message */ unsigned int biffMsgErrNum(const biffMsg *msg) { if (biffMsgNoop == msg) { return 0; } if (!msg) { return 0; } return msg->errNum; } /* ******** biffMsgStrlen ** ** returns length of string (not including null termination, as usual) ** of the error message that will be generated by biffMsgStrSet */ unsigned int biffMsgStrlen(const biffMsg *msg) { static const char me[]="biffMsgStrlen"; unsigned int ii, len; if (biffMsgNoop == msg) { return 0; } if (!( msg )) { fprintf(stderr, "%s: PANIC got NULL msg %p\n", me, AIR_CVOIDP(msg)); return 0; /* exit(1); */ } len = 0; for (ii=0; iierrNum; ii++) { len += AIR_UINT(strlen(msg->key) + strlen(msg->err[ii]) + strlen("[] \n")); } return len+1; } char * biffMsgStrAlloc(const biffMsg *msg) { static const char me[]="biffMsgStrAlloc"; char *ret; unsigned int len; if (biffMsgNoop == msg) { return NULL; } len = biffMsgStrlen(msg); ret = AIR_CALLOC(len+1, char); if (!ret) { fprintf(stderr, "%s: PANIC couldn't alloc string", me); return NULL; /* exit(1); */ } return ret; } /* ** ret is assumed to be allocated for biffMsgStrlen()+1, or is the ** the return from biffMsgStrAlloc */ void biffMsgStrSet(char *ret, const biffMsg *msg) { static const char me[]="biffMsgStrSet"; char *buff; unsigned int ii; if (biffMsgNoop == msg) { return; } buff = AIR_CALLOC(biffMsgLineLenMax(msg)+1, char); if (!buff) { fprintf(stderr, "%s: PANIC couldn't alloc buffer", me); /* exit(1); */ } strcpy(ret, ""); for (ii=msg->errNum; ii>0; ii--) { sprintf(buff, "[%s] %s\n", msg->key, msg->err[ii-1]); strcat(ret, buff); } free(buff); } char * biffMsgStrGet(const biffMsg *msg) { char *ret; if (biffMsgNoop == msg) { return NULL; } ret = biffMsgStrAlloc(msg); biffMsgStrSet(ret, msg); return ret; } biffMsg _biffMsgNoop = { NULL, NULL, 0, NULL }; /* ******** biffMsgNoop ** ** pass this instead of a real biffMsg (allocated by biffMsgNew) as a ** flag to say, "don't bother, really". This turns all the biffMsg ** functions into no-ops (except that var-args are still consumed ** where they are used) */ biffMsg * biffMsgNoop = &_biffMsgNoop; teem-1.11.0~svn6057/src/biff/sources.cmake0000664000175000017500000000032011610114016017751 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(BIFF_SOURCES privateBiff.h biff.h biffmsg.c biffbiff.c ) ADD_TEEM_LIBRARY(biff ${BIFF_SOURCES}) teem-1.11.0~svn6057/src/biff/test/0000775000175000017500000000000012203513754016263 5ustar domibeldomibelteem-1.11.0~svn6057/src/biff/test/test.c0000664000175000017500000000714412165631065017417 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../biff.h" int main() { char *tmp, *s1, *s2; biffMsg *msg1, *msg2; /* biffAdd("axis", "the first error axis"); biffAdd("axis", "the second error axis"); biffAdd("axis", "the third error axis"); biffAdd("chard", "the first error chard"); biffAdd("chard", "the second error chard"); biffAdd("chard", "the third error chard"); biffAdd("bingo", "zero-eth bingo message"); biffMove("bingo", NULL, "chard"); biffAdd("bingo", "the first error bingo"); biffAdd("bingo", "the second bll boo boo boo error bingo"); biffAdd("bingo", "the third error bingo"); printf("%s\n", (tmp = biffGet("bingo"))); free(tmp); biffDone("bingo"); printf("%s\n", (tmp = biffGet("chard"))); free(tmp); biffDone("chard"); printf("%s\n", (tmp = biffGet("axis"))); free(tmp); biffDone("axis"); biffAdd("harold", "the first error harold"); biffAdd("harold", "the second error harold"); biffAdd("harold", "the third error harold"); printf("%s\n", (tmp = biffGet("harold"))); free(tmp); */ biffAdd("axis", "the first error axis"); biffAdd("axis", "the second error axis"); biffAdd("axis", "the third error axis"); biffAdd("axis", "the fourth error axis"); biffAdd("axis", "the fifth error axis"); printf("%s", (tmp = biffGet("axis"))); free(tmp); biffDone("axis"); biffAdd("axo", "the first error axis"); biffAdd("axo", "the second error axis"); biffAdd("axo", "the third error axis"); biffAdd("axo", "the fourth error axis"); biffAdd("axo", "the fifth error axis"); printf("%s", (tmp = biffGetDone("axo"))); free(tmp); printf("=================================\n"); msg1 = biffMsgNew("roberts"); biffMsgAdd(msg1, "biffMsgAdd hello, said roberts"); biffMsgAddf(msg1, "biffMsgAddf: there's an int %d and a float %g", 42, AIR_PI); s1 = biffMsgStrGet(msg1); printf("from msg1:\n%s", s1); s1 = airFree(s1); msg2 = biffMsgNew("sue"); biffMsgAdd(msg2, "biffMsgAdd hi from sue"); biffMsgAddf(msg2, "biffMsgAddf: another float %g", AIR_PI*AIR_PI); s2 = biffMsgStrGet(msg2); printf("from msg2:\n%s", s2); s2 = airFree(s2); biffMsgMovef(msg1, msg2, "biffMsgMovef: good int %d", 10); s1 = biffMsgStrGet(msg1); printf("from msg1:\n%s", s1); s1 = airFree(s1); printf("=================================\n"); msg1 = biffMsgNix(msg1); msg2 = biffMsgNix(msg2); /* biffAddf("test", "%s: this is a test %d %f", "me", 1, 2.0); printf("%s\n", (tmp = biffGet("test"))); free(tmp); biffDone("test"); */ exit(0); } teem-1.11.0~svn6057/src/biff/TODO.txt0000664000175000017500000000100112042322251016571 0ustar domibeldomibelI love biff: [gnocchi:~/Deft/src] gk% vprobe -i crop.nhdr -k tensor -q hess -s 1 1 1 -o hess.nrrd gageKindTotalAnswerLength: PANIC: [gage] gageKindCheck: kind "tensor" item max 65 > GAGE_ITEM_MAX 63 *** fix memory-in-use problem after a biffGetDone() biffDisable() allow more flexibility in logging- not just strings, but ints, or both make this all thread safe- the "keys" that are used should somehow be make specific to a given thread, or at least this specificity should be enabled at caller's request teem-1.11.0~svn6057/src/nrrd/0000775000175000017500000000000012203513757015346 5ustar domibeldomibelteem-1.11.0~svn6057/src/nrrd/axis.c0000664000175000017500000011016512202611310016441 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ------------------------------------------------------------ */ void _nrrdAxisInfoInit(NrrdAxisInfo *axis) { int dd; if (axis) { axis->size = 0; axis->spacing = axis->thickness = AIR_NAN; axis->min = axis->max = AIR_NAN; for (dd=0; ddspaceDirection[dd] = AIR_NAN; } axis->center = nrrdCenterUnknown; axis->kind = nrrdKindUnknown; axis->label = (char *)airFree(axis->label); axis->units = (char *)airFree(axis->units); } } void _nrrdAxisInfoNewInit(NrrdAxisInfo *axis) { if (axis) { axis->label = NULL; axis->units = NULL; _nrrdAxisInfoInit(axis); } } /* ------------------------------------------------------------ */ /* ******** nrrdKindIsDomain ** ** returns non-zero for kinds (from nrrdKind* enum) that are domain ** axes, or independent variable axes, or resample-able axes, all ** different ways of describing the same thing */ int nrrdKindIsDomain(int kind) { return (nrrdKindDomain == kind || nrrdKindSpace == kind || nrrdKindTime == kind); } /* ******** nrrdKindSize ** ** returns suggested size (length) of an axis with the given kind, or, ** 0 if either (1) there is no suggested size because the axis is the ** kind of an independent or domain variable or (2) the kind is invalid */ unsigned int nrrdKindSize(int kind) { static const char me[]="nrrdKindSize"; unsigned int ret; if (!( AIR_IN_OP(nrrdKindUnknown, kind, nrrdKindLast) )) { /* they gave us invalid or unknown kind */ return 0; } switch (kind) { case nrrdKindDomain: case nrrdKindSpace: case nrrdKindTime: case nrrdKindList: case nrrdKindPoint: case nrrdKindVector: case nrrdKindCovariantVector: case nrrdKindNormal: ret = 0; break; case nrrdKindStub: case nrrdKindScalar: ret = 1; break; case nrrdKindComplex: case nrrdKind2Vector: ret = 2; break; case nrrdKind3Color: case nrrdKindRGBColor: case nrrdKindHSVColor: case nrrdKindXYZColor: ret = 3; break; case nrrdKind4Color: case nrrdKindRGBAColor: ret = 4; break; case nrrdKind3Vector: case nrrdKind3Normal: ret = 3; break; case nrrdKind4Vector: case nrrdKindQuaternion: ret = 4; break; case nrrdKind2DSymMatrix: ret = 3; break; case nrrdKind2DMaskedSymMatrix: ret = 4; break; case nrrdKind2DMatrix: ret = 4; break; case nrrdKind2DMaskedMatrix: ret = 5; break; case nrrdKind3DSymMatrix: ret = 6; break; case nrrdKind3DMaskedSymMatrix: ret = 7; break; case nrrdKind3DMatrix: ret = 9; break; case nrrdKind3DMaskedMatrix: ret = 10; break; default: fprintf(stderr, "%s: PANIC: nrrdKind %d not implemented!\n", me, kind); ret = UINT_MAX; } return ret; } /* ** _nrrdKindAltered: ** ** implements logic for how kind should be updated when samples ** along the axis are altered */ int _nrrdKindAltered(int kindIn, int resampling) { int kindOut; if (nrrdStateKindNoop) { kindOut = nrrdKindUnknown; /* HEY: setting the kindOut to unknown is arguably not a no-op. It is more like pointedly and stubbornly simplistic. So maybe nrrdStateKindNoop could be renamed .. */ } else { if (nrrdKindIsDomain(kindIn) || (0 == nrrdKindSize(kindIn) && !resampling)) { kindOut = kindIn; } else { kindOut = nrrdKindUnknown; } } return kindOut; } /* ** _nrrdAxisInfoCopy ** ** HEY: we have a void return even though this function potentially ** involves calling airStrdup!! */ void _nrrdAxisInfoCopy(NrrdAxisInfo *dest, const NrrdAxisInfo *src, int bitflag) { int ii; if (!(NRRD_AXIS_INFO_SIZE_BIT & bitflag)) { dest->size = src->size; } if (!(NRRD_AXIS_INFO_SPACING_BIT & bitflag)) { dest->spacing = src->spacing; } if (!(NRRD_AXIS_INFO_THICKNESS_BIT & bitflag)) { dest->thickness = src->thickness; } if (!(NRRD_AXIS_INFO_MIN_BIT & bitflag)) { dest->min = src->min; } if (!(NRRD_AXIS_INFO_MAX_BIT & bitflag)) { dest->max = src->max; } if (!(NRRD_AXIS_INFO_SPACEDIRECTION_BIT & bitflag)) { for (ii=0; iispaceDirection[ii] = src->spaceDirection[ii]; } } if (!(NRRD_AXIS_INFO_CENTER_BIT & bitflag)) { dest->center = src->center; } if (!(NRRD_AXIS_INFO_KIND_BIT & bitflag)) { dest->kind = src->kind; } if (!(NRRD_AXIS_INFO_LABEL_BIT & bitflag)) { if (dest->label != src->label) { dest->label = (char *)airFree(dest->label); dest->label = (char *)airStrdup(src->label); } } if (!(NRRD_AXIS_INFO_UNITS_BIT & bitflag)) { if (dest->units != src->units) { dest->units = (char *)airFree(dest->units); dest->units = (char *)airStrdup(src->units); } } return; } /* ******** nrrdAxisInfoCopy() ** ** For copying all the per-axis peripheral information. Takes a ** permutation "map"; map[d] tells from which axis in input should the ** output axis d copy its information. The length of this permutation ** array is nout->dim. If map is NULL, the identity permutation is ** assumed. If map[i]==-1 for any i in [0,dim-1], then nothing is ** copied into axis i of output. The "bitflag" field controls which ** per-axis fields will NOT be copied; if bitflag==0, then all fields ** are copied. The value of bitflag should be |'s of NRRD_AXIS_INFO_* ** defines. ** ** Decided to Not use Biff, since many times map will be NULL, in ** which case the only error is getting a NULL nrrd, or an invalid map ** permutation, which will probably be unlikely given the contexts in ** which this is called. For the paranoid, the integer return value ** indicates error. ** ** Sun Feb 27 21:12:57 EST 2005: decided to allow nout==nin, so now ** use a local array of NrrdAxisInfo as buffer. */ int nrrdAxisInfoCopy(Nrrd *nout, const Nrrd *nin, const int *axmap, int bitflag) { NrrdAxisInfo axisBuffer[NRRD_DIM_MAX]; const NrrdAxisInfo *axis; unsigned int from, axi; if (!(nout && nin)) { return 1; } if (axmap) { for (axi=0; axidim; axi++) { if (-1 == axmap[axi]) { continue; } if (!AIR_IN_CL(0, axmap[axi], (int)nin->dim-1)) { return 3; } } } if (nout == nin) { /* copy axis info to local buffer */ for (axi=0; axidim; axi++) { _nrrdAxisInfoNewInit(axisBuffer + axi); _nrrdAxisInfoCopy(axisBuffer + axi, nin->axis + axi, bitflag); } axis = axisBuffer; } else { axis = nin->axis; } for (axi=0; axidim; axi++) { if (axmap && -1 == axmap[axi]) { /* for this axis, we don't touch a thing */ continue; } from = axmap ? (unsigned int)axmap[axi] : axi; _nrrdAxisInfoCopy(nout->axis + axi, axis + from, bitflag); } if (nout == nin) { /* free dynamically allocated stuff */ for (axi=0; axidim; axi++) { _nrrdAxisInfoInit(axisBuffer + axi); } } return 0; } /* ******** nrrdAxisInfoSet_nva() ** ** Simple means of setting fields of the axis array in the nrrd. ** ** type to pass for third argument: ** nrrdAxisInfoSize: size_t* ** nrrdAxisInfoSpacing: double* ** nrrdAxisInfoThickness: double* ** nrrdAxisInfoMin: double* ** nrrdAxisInfoMax: double* ** nrrdAxisInfoSpaceDirection: double (*var)[NRRD_SPACE_DIM_MAX] ** nrrdAxisInfoCenter: int* ** nrrdAxisInfoKind: int* ** nrrdAxisInfoLabel: char** ** nrrdAxisInfoUnits: char** ** ** Note that in the case of nrrdAxisInfoSpaceDirection, we only access ** spaceDim elements of info.V[ai] (so caller can allocate it for less ** than NRRD_SPACE_DIM_MAX if they know what they're doing) */ void nrrdAxisInfoSet_nva(Nrrd *nrrd, int axInfo, const void *_info) { _nrrdAxisInfoSetPtrs info; int exists; unsigned int ai, si, minsi; if (!( nrrd && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast) && _info )) { return; } info.P = _info; for (ai=0; aidim; ai++) { switch (axInfo) { case nrrdAxisInfoSize: nrrd->axis[ai].size = info.ST[ai]; break; case nrrdAxisInfoSpacing: nrrd->axis[ai].spacing = info.D[ai]; break; case nrrdAxisInfoThickness: nrrd->axis[ai].thickness = info.D[ai]; break; case nrrdAxisInfoMin: nrrd->axis[ai].min = info.D[ai]; break; case nrrdAxisInfoMax: nrrd->axis[ai].max = info.D[ai]; break; case nrrdAxisInfoSpaceDirection: /* we won't allow setting an invalid direction */ exists = AIR_EXISTS(info.V[ai][0]); minsi = nrrd->spaceDim; for (si=0; sispaceDim; si++) { nrrd->axis[ai].spaceDirection[si] = info.V[ai][si]; if (exists ^ AIR_EXISTS(info.V[ai][si])) { minsi = 0; break; } } for (si=minsi; siaxis[ai].spaceDirection[si] = AIR_NAN; } break; case nrrdAxisInfoCenter: nrrd->axis[ai].center = info.I[ai]; break; case nrrdAxisInfoKind: nrrd->axis[ai].kind = info.I[ai]; break; case nrrdAxisInfoLabel: nrrd->axis[ai].label = (char *)airFree(nrrd->axis[ai].label); nrrd->axis[ai].label = (char *)airStrdup(info.CP[ai]); break; case nrrdAxisInfoUnits: nrrd->axis[ai].units = (char *)airFree(nrrd->axis[ai].units); nrrd->axis[ai].units = (char *)airStrdup(info.CP[ai]); break; } } if (nrrdAxisInfoSpaceDirection == axInfo) { for (ai=nrrd->dim; aiaxis[ai].spaceDirection[si] = AIR_NAN; } } } return; } /* ******** nrrdAxisInfoSet_va() ** ** var args front-end for nrrdAxisInfoSet_nva ** ** types to pass, one for each dimension: ** nrrdAxisInfoSize: size_t ** nrrdAxisInfoSpacing: double ** nrrdAxisInfoThickness: double ** nrrdAxisInfoMin: double ** nrrdAxisInfoMax: double ** nrrdAxisInfoSpaceDirection: double* ** nrrdAxisInfoCenter: int ** nrrdAxisInfoKind: int ** nrrdAxisInfoLabel: char* ** nrrdAxisInfoUnits: char* */ void nrrdAxisInfoSet_va(Nrrd *nrrd, int axInfo, ...) { NRRD_TYPE_BIGGEST *buffer[NRRD_DIM_MAX]; _nrrdAxisInfoSetPtrs info; unsigned int ai, si; va_list ap; double *dp, svec[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX]; if (!( nrrd && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast) )) { return; } info.P = buffer; va_start(ap, axInfo); for (ai=0; aidim; ai++) { switch (axInfo) { case nrrdAxisInfoSize: info.ST[ai] = va_arg(ap, size_t); /* printf("!%s: got int[%d] = %d\n", "nrrdAxisInfoSet", d, info.I[ai]); */ break; case nrrdAxisInfoSpaceDirection: dp = va_arg(ap, double*); /* punting on using info enum */ /* printf("!%s: got dp = %lu\n", "nrrdAxisInfoSet", (unsigned long)(dp)); */ for (si=0; sispaceDim; si++) { /* nrrd->axis[ai].spaceDirection[si] = dp[si]; */ svec[ai][si] = dp[si]; } for (si=nrrd->spaceDim; siaxis[ai].spaceDirection[si] = AIR_NAN; */ svec[ai][si] = dp[si]; } break; case nrrdAxisInfoCenter: case nrrdAxisInfoKind: info.I[ai] = va_arg(ap, int); /* printf("!%s: got int[%d] = %d\n", "nrrdAxisInfoSet", d, info.I[ai]); */ break; case nrrdAxisInfoSpacing: case nrrdAxisInfoThickness: case nrrdAxisInfoMin: case nrrdAxisInfoMax: info.D[ai] = va_arg(ap, double); /* printf("!%s: got double[%d] = %g\n", "nrrdAxisInfoSet", d, info.D[ai]); */ break; case nrrdAxisInfoLabel: /* we DO NOT do the airStrdup() here because this pointer value is just going to be handed to nrrdAxisInfoSet_nva(), which WILL do the airStrdup(); we're not violating the rules for axis labels */ info.CP[ai] = va_arg(ap, char *); /* printf("!%s: got char*[%d] = |%s|\n", "nrrdAxisInfoSet", d, info.CP[ai]); */ break; case nrrdAxisInfoUnits: /* see not above */ info.CP[ai] = va_arg(ap, char *); break; } } va_end(ap); if (nrrdAxisInfoSpaceDirection != axInfo) { /* now set the quantities which we've gotten from the var args */ nrrdAxisInfoSet_nva(nrrd, axInfo, info.P); } else { nrrdAxisInfoSet_nva(nrrd, axInfo, svec); } return; } /* ******** nrrdAxisInfoGet_nva() ** ** get any of the axis fields into an array ** ** Note that getting axes labels involves implicitly allocating space ** for them, due to the action of airStrdup(). The user is ** responsible for free()ing these strings when done with them. ** ** type to pass for third argument: ** nrrdAxisInfoSize: size_t* ** nrrdAxisInfoSpacing: double* ** nrrdAxisInfoThickness: double* ** nrrdAxisInfoMin: double* ** nrrdAxisInfoMax: double* ** nrrdAxisInfoSpaceDirection: double (*var)[NRRD_SPACE_DIM_MAX] ** nrrdAxisInfoCenter: int* ** nrrdAxisInfoKind: int* ** nrrdAxisInfoLabel: char** ** nrrdAxisInfoUnits: char** */ void nrrdAxisInfoGet_nva(const Nrrd *nrrd, int axInfo, void *_info) { _nrrdAxisInfoGetPtrs info; unsigned int ai, si; if (!( nrrd && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast) )) { return; } info.P = _info; for (ai=0; aidim; ai++) { switch (axInfo) { case nrrdAxisInfoSize: info.ST[ai] = nrrd->axis[ai].size; break; case nrrdAxisInfoSpacing: info.D[ai] = nrrd->axis[ai].spacing; break; case nrrdAxisInfoThickness: info.D[ai] = nrrd->axis[ai].thickness; break; case nrrdAxisInfoMin: info.D[ai] = nrrd->axis[ai].min; break; case nrrdAxisInfoMax: info.D[ai] = nrrd->axis[ai].max; break; case nrrdAxisInfoSpaceDirection: for (si=0; sispaceDim; si++) { info.V[ai][si] = nrrd->axis[ai].spaceDirection[si]; } for (si=nrrd->spaceDim; siaxis[ai].center; break; case nrrdAxisInfoKind: info.I[ai] = nrrd->axis[ai].kind; break; case nrrdAxisInfoLabel: /* note airStrdup()! */ info.CP[ai] = airStrdup(nrrd->axis[ai].label); break; case nrrdAxisInfoUnits: /* note airStrdup()! */ info.CP[ai] = airStrdup(nrrd->axis[ai].units); break; } } if (nrrdAxisInfoSpaceDirection == axInfo) { for (ai=nrrd->dim; aidim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast) )) { return; } if (nrrdAxisInfoSpaceDirection != axInfo) { info.P = buffer; nrrdAxisInfoGet_nva(nrrd, axInfo, info.P); } else { nrrdAxisInfoGet_nva(nrrd, axInfo, svec); } va_start(ap, axInfo); for (ai=0; aidim; ai++) { ptr = va_arg(ap, void*); /* printf("!%s(%d): ptr = %lu\n", "nrrdAxisInfoGet", d, (unsigned long)ptr); */ switch (axInfo) { case nrrdAxisInfoSize: *((size_t*)ptr) = info.ST[ai]; break; case nrrdAxisInfoSpacing: case nrrdAxisInfoThickness: case nrrdAxisInfoMin: case nrrdAxisInfoMax: *((double*)ptr) = info.D[ai]; /* printf("!%s: got double[%d] = %lg\n", "nrrdAxisInfoGet", d, *((double*)ptr)); */ break; case nrrdAxisInfoSpaceDirection: for (si=0; sispaceDim; si++) { ((double*)ptr)[si] = svec[ai][si]; } for (si=nrrd->spaceDim; sidim-1 )) { return AIR_NAN; } center = _nrrdCenter(nrrd->axis[ax].center); min = nrrd->axis[ax].min; max = nrrd->axis[ax].max; size = nrrd->axis[ax].size; return NRRD_POS(center, min, max, size, idx); } /* ******** nrrdAxisInfoIdx() ** ** given a nrrd, an axis, and a (floating point) world space position, ** return the index implied the axis's min, max, and center. ** Does the opposite of nrrdAxisPos(). ** ** does not use biff */ double nrrdAxisInfoIdx(const Nrrd *nrrd, unsigned int ax, double pos) { int center; size_t size; double min, max; if (!( nrrd && ax <= nrrd->dim-1 )) { return AIR_NAN; } center = _nrrdCenter(nrrd->axis[ax].center); min = nrrd->axis[ax].min; max = nrrd->axis[ax].max; size = nrrd->axis[ax].size; return NRRD_IDX(center, min, max, size, pos); } /* ******** nrrdAxisInfoPosRange() ** ** given a nrrd, an axis, and two (floating point) index space positions, ** return the range of positions implied the axis's min, max, and center ** The opposite of nrrdAxisIdxRange() */ void nrrdAxisInfoPosRange(double *loP, double *hiP, const Nrrd *nrrd, unsigned int ax, double loIdx, double hiIdx) { int center, flip = 0; size_t size; double min, max, tmp; if (!( loP && hiP && nrrd && ax <= nrrd->dim-1 )) { *loP = *hiP = AIR_NAN; return; } center = _nrrdCenter(nrrd->axis[ax].center); min = nrrd->axis[ax].min; max = nrrd->axis[ax].max; size = nrrd->axis[ax].size; if (loIdx > hiIdx) { flip = 1; tmp = loIdx; loIdx = hiIdx; hiIdx = tmp; } if (nrrdCenterCell == center) { *loP = AIR_AFFINE(0, loIdx, size, min, max); *hiP = AIR_AFFINE(0, hiIdx+1, size, min, max); } else { *loP = AIR_AFFINE(0, loIdx, size-1, min, max); *hiP = AIR_AFFINE(0, hiIdx, size-1, min, max); } if (flip) { tmp = *loP; *loP = *hiP; *hiP = tmp; } return; } /* ******** nrrdAxisInfoIdxRange() ** ** given a nrrd, an axis, and two (floating point) world space positions, ** return the range of index space implied the axis's min, max, and center ** The opposite of nrrdAxisPosRange(). ** ** Actually- there are situations where sending an interval through ** nrrdAxisIdxRange -> nrrdAxisPosRange -> nrrdAxisIdxRange ** such as in cell centering, when the range of positions given does ** not even span one sample. Such as: ** axis->size = 4, axis->min = -4, axis->max = 4, loPos = 0, hiPos = 1 ** --> nrrdAxisIdxRange == (2, 1.5) --> nrrdAxisPosRange == (2, -1) ** The basic problem is that because of the 0.5 offset inherent in ** cell centering, there are situations where (in terms of the arguments ** to nrrdAxisIdxRange()) loPos < hiPos, but *loP > *hiP. */ void nrrdAxisInfoIdxRange(double *loP, double *hiP, const Nrrd *nrrd, unsigned int ax, double loPos, double hiPos) { int center, flip = 0; size_t size; double min, max, tmp; if (!( loP && hiP && nrrd && ax <= nrrd->dim-1 )) { *loP = *hiP = AIR_NAN; return; } center = _nrrdCenter(nrrd->axis[ax].center); min = nrrd->axis[ax].min; max = nrrd->axis[ax].max; size = nrrd->axis[ax].size; if (loPos > hiPos) { flip = 1; tmp = loPos; loPos = hiPos; hiPos = tmp; } if (nrrdCenterCell == center) { if (min < max) { *loP = AIR_AFFINE(min, loPos, max, 0, size); *hiP = AIR_AFFINE(min, hiPos, max, -1, size-1); } else { *loP = AIR_AFFINE(min, loPos, max, -1, size-1); *hiP = AIR_AFFINE(min, hiPos, max, 0, size); } } else { *loP = AIR_AFFINE(min, loPos, max, 0, size-1); *hiP = AIR_AFFINE(min, hiPos, max, 0, size-1); } if (flip) { tmp = *loP; *loP = *hiP; *hiP = tmp; } return; } void nrrdAxisInfoSpacingSet(Nrrd *nrrd, unsigned int ax) { int sign; double min, max, tmp; if (!( nrrd && ax <= nrrd->dim-1 )) { return; } min = nrrd->axis[ax].min; max = nrrd->axis[ax].max; if (!( AIR_EXISTS(min) && AIR_EXISTS(max) )) { /* there's no actual basis on which to set the spacing information, but we have to set it something, so here goes .. */ nrrd->axis[ax].spacing = nrrdDefaultSpacing; return; } if (min > max) { tmp = min; min = max; max = tmp; sign = -1; } else { sign = 1; } /* the skinny */ nrrd->axis[ax].spacing = NRRD_SPACING(_nrrdCenter(nrrd->axis[ax].center), min, max, nrrd->axis[ax].size); nrrd->axis[ax].spacing *= sign; return; } void nrrdAxisInfoMinMaxSet(Nrrd *nrrd, unsigned int ax, int defCenter) { int center; double spacing; if (!( nrrd && ax <= nrrd->dim-1 )) { return; } center = _nrrdCenter2(nrrd->axis[ax].center, defCenter); spacing = nrrd->axis[ax].spacing; if (!AIR_EXISTS(spacing)) spacing = nrrdDefaultSpacing; if (nrrdCenterCell == center) { nrrd->axis[ax].min = 0; nrrd->axis[ax].max = spacing*AIR_CAST(double, nrrd->axis[ax].size); } else { nrrd->axis[ax].min = 0; nrrd->axis[ax].max = spacing*AIR_CAST(double, nrrd->axis[ax].size - 1); } return; } /* ---- BEGIN non-NrrdIO */ /* ** not using the value comparators in accessors.c because of their ** slightly strange behavior WRT infinity (+inf < -42). This code ** may eventually warrant wider availability, for now its here but ** accessible to nrrd files via privateNrrd.h */ int _nrrdDblcmp(double aa, double bb) { int nna, nnb, ret; nna = AIR_EXISTS(aa) || !airIsNaN(aa); nnb = AIR_EXISTS(bb) || !airIsNaN(bb); if (nna && nnb) { /* both either exist or are an infinity */ ret = (aa < bb ? -1 : (aa > bb ? 1 : 0)); } else { /* one or the other is NaN */ ret = (nna < nnb ? -1 : (nna > nnb ? 1 : 0)); } return ret; } /* ******** nrrdAxisInfoCompare ** ** compares all fields in the NrrdAxisInfoCompare ** ** See comment about logic of return value above nrrdCompare() ** ** NOTE: the structure of this code is very similar to that of ** nrrdCompare, and any improvements here should be reflected there */ int nrrdAxisInfoCompare(const NrrdAxisInfo *axisA, const NrrdAxisInfo *axisB, int *differ, char explain[AIR_STRLEN_LARGE]) { static const char me[]="nrrdAxisInfoCompare"; unsigned int saxi; if (!(axisA && axisB && differ)) { biffAddf(NRRD, "%s: got NULL pointer (%p, %p, or %p)", me, AIR_CVOIDP(axisA), AIR_CVOIDP(axisB), AIR_VOIDP(differ)); return 1; } if (explain) { strcpy(explain, ""); } if (axisA->size != axisB->size) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; *differ = axisA->size < axisB->size ? -1 : 1; if (explain) { sprintf(explain, "axisA->size=%s %s axisB->size=%s", airSprintSize_t(stmp1, axisA->size), *differ < 0 ? "<" : ">", airSprintSize_t(stmp2, axisB->size)); } return 0; } #define DOUBLE_COMPARE(VAL, STR) \ *differ = _nrrdDblcmp(axisA->VAL, axisB->VAL); \ if (*differ) { \ if (explain) { \ sprintf(explain, "axisA->%s %.17g %s axisB->%s %.17g", \ STR, axisA->VAL, *differ < 0 ? "<" : ">", \ STR, axisB->VAL); \ } \ return 0; \ } DOUBLE_COMPARE(spacing, "spacing"); DOUBLE_COMPARE(thickness, "thickness"); DOUBLE_COMPARE(min, "min"); DOUBLE_COMPARE(max, "max"); for (saxi=0; saxicenter != axisB->center) { *differ = axisA->center < axisB->center ? -1 : 1; if (explain) { sprintf(explain, "axisA->center %s %s axisB->center %s", airEnumStr(nrrdCenter, axisA->center), *differ < 0 ? "<" : ">", airEnumStr(nrrdCenter, axisB->center)); } return 0; } if (axisA->kind != axisB->kind) { *differ = axisA->kind < axisB->kind ? -1 : 1; if (explain) { sprintf(explain, "axisA->kind %s %s axisB->kind %s", airEnumStr(nrrdKind, axisA->kind), *differ < 0 ? "<" : ">", airEnumStr(nrrdKind, axisB->kind)); } return 0; } *differ = airStrcmp(axisA->label, axisB->label); if (*differ) { if (explain) { /* can't safely print whole labels because of fixed-size of explain */ sprintf(explain, "axisA->label %s axisB->label", *differ < 0 ? "<" : ">"); if (strlen(explain) + airStrlen(axisA->label) + airStrlen(axisB->label) + 2*strlen(" \"\" ") + 1 < AIR_STRLEN_LARGE) { /* ok, we can print them */ sprintf(explain, "axisA->label \"%s\" %s axisB->label \"%s\"", axisA->label ? axisA->label : "", *differ < 0 ? "<" : ">", axisB->label ? axisB->label : ""); } } return 0; } *differ = airStrcmp(axisA->units, axisB->units); if (*differ) { if (explain) { /* can't print whole string because of fixed-size of explain */ sprintf(explain, "axisA->units %s axisB->units", *differ < 0 ? "<" : ">"); } return 0; } return 0; } /* ---- END non-NrrdIO */ /* ******** nrrdDomainAxesGet ** ** Based on the per-axis "kind" field, learns which are the domain ** (resample-able) axes of an image, in other words, the axes which ** correspond to independent variables. The return value is the ** number of domain axes, and that many values are set in the given ** axisIdx[] array ** ** NOTE: this takes a wild guess that an unset (nrrdKindUnknown) kind ** is a domain axis. */ unsigned int nrrdDomainAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]) { unsigned int domAxi, axi; if (!( nrrd && axisIdx )) { return 0; } domAxi = 0; for (axi=0; axidim; axi++) { if (nrrdKindUnknown == nrrd->axis[axi].kind || nrrdKindIsDomain(nrrd->axis[axi].kind)) { axisIdx[domAxi++] = axi; } } return domAxi; } int _nrrdSpaceVecExists(const Nrrd *nrrd, unsigned int axi) { unsigned int sai; int ret; if (!( nrrd && axi < nrrd->dim && nrrd->spaceDim )) { ret = AIR_FALSE; } else { ret = AIR_TRUE; for (sai=0; saispaceDim; sai++) { ret &= AIR_EXISTS(nrrd->axis[axi].spaceDirection[sai]); } } return ret; } unsigned int nrrdSpatialAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]) { unsigned int spcAxi, axi; if (!( nrrd && axisIdx && nrrd->spaceDim)) { return 0; } spcAxi = 0; for (axi=0; axidim; axi++) { if (_nrrdSpaceVecExists(nrrd, axi)) { axisIdx[spcAxi++] = axi; } } return spcAxi; } /* ******** nrrdRangeAxesGet ** ** Based on the per-axis "kind" field, learns which are the range ** (non-resample-able) axes of an image, in other words, the axes ** which correspond to dependent variables. The return value is the ** number of range axes; that number of values are set in the given ** axisIdx[] array ** ** Note: this really is as simple as returning the complement of the ** axis selected by nrrdDomainAxesGet() */ unsigned int nrrdRangeAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]) { unsigned int domNum, domIdx[NRRD_DIM_MAX], rngAxi, axi, ii, isDom; if (!( nrrd && axisIdx )) { return 0; } domNum = nrrdDomainAxesGet(nrrd, domIdx); rngAxi = 0; for (axi=0; axidim; axi++) { isDom = AIR_FALSE; for (ii=0; iidim; axi++) { isSpc = AIR_FALSE; for (ii=0; iispacing, ** vector = all NaNs ** ** nrrdSpacingStatusScalarWithSpace There *is* a surrounding space, but the ** given axis does not live in that space, ** because it has no space direction. Caller ** may want to think about what's going on. ** *spacing = axis->spacing, ** vector = all NaNs ** ** nrrdSpacingStatusDirection There is a surrounding space, in which ** this axis has a direction V: ** *spacing = |V| (length of direction), ** vector = V/|V| (normalized direction) ** NOTE: it is still possible for both ** *spacing and vector to be all NaNs!! */ int nrrdSpacingCalculate(const Nrrd *nrrd, unsigned int ax, double *spacing, double vector[NRRD_SPACE_DIM_MAX]) { int ret; if (!( nrrd && spacing && vector && ax <= nrrd->dim-1 && !_nrrdCheck(nrrd, AIR_FALSE, AIR_FALSE) )) { /* there's a problem with the arguments. Note: the _nrrdCheck() call does not check on non-NULL-ity of nrrd->data */ ret = nrrdSpacingStatusUnknown; if (spacing) { *spacing = AIR_NAN; } if (vector) { nrrdSpaceVecSetNaN(vector); } } else { if (AIR_EXISTS(nrrd->axis[ax].spacing)) { if (nrrd->spaceDim > 0) { ret = nrrdSpacingStatusScalarWithSpace; } else { ret = nrrdSpacingStatusScalarNoSpace; } *spacing = nrrd->axis[ax].spacing; nrrdSpaceVecSetNaN(vector); } else { if (nrrd->spaceDim > 0 && _nrrdSpaceVecExists(nrrd, ax)) { ret = nrrdSpacingStatusDirection; *spacing = nrrdSpaceVecNorm(nrrd->spaceDim, nrrd->axis[ax].spaceDirection); nrrdSpaceVecScale(vector, 1.0/(*spacing), nrrd->axis[ax].spaceDirection); } else { ret = nrrdSpacingStatusNone; *spacing = AIR_NAN; nrrdSpaceVecSetNaN(vector); } } } return ret; } int nrrdOrientationReduce(Nrrd *nout, const Nrrd *nin, int setMinsFromOrigin) { static const char me[]="nrrdOrientationReduce"; unsigned int spatialAxisNum, spatialAxisIdx[NRRD_DIM_MAX], saxii; NrrdAxisInfo *axis; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL spacing", me); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: trouble doing initial copying", me); return 1; } } if (!nout->spaceDim) { /* we're done! */ return 0; } spatialAxisNum = nrrdSpatialAxesGet(nout, spatialAxisIdx); for (saxii=0; saxiiaxis + spatialAxisIdx[saxii]; axis->spacing = nrrdSpaceVecNorm(nout->spaceDim, axis->spaceDirection); if (setMinsFromOrigin) { axis->min = (saxii < nout->spaceDim ? nout->spaceOrigin[saxii] : AIR_NAN); } } nrrdSpaceSet(nout, nrrdSpaceUnknown); return 0; } teem-1.11.0~svn6057/src/nrrd/parseNrrd.c0000664000175000017500000013106512165631065017460 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ** _nrrdReadNrrdParseField() ** ** This is for parsing the stuff BEFORE the colon */ int _nrrdReadNrrdParseField(NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParseField"; char *next, *buff, *colon, *keysep; int ret, fld=nrrdField_unknown, noField, badField=AIR_FALSE; next = nio->line + nio->pos; /* determining if the line is a comment is simple */ if (NRRD_COMMENT_CHAR == next[0]) { return nrrdField_comment; } if (!( buff = airStrdup(next) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't allocate buffer!", me); return nrrdField_unknown; } /* #1: "...if you see a colon, then look for an equal sign..." */ /* Look for colon: if no colon, or failed to parse as a field, look for * equal sign, if that failed then error */ /* Let the separator be := */ /* Escape \n */ colon = strstr(buff, ": "); noField = !colon; if (colon) { *colon = '\0'; badField = ( nrrdField_unknown == (fld = airEnumVal(nrrdField, buff)) ); } if (noField || badField) { keysep = strstr(buff, ":="); if (!keysep) { if (noField) { biffMaybeAddf(useBiff, NRRD, "%s: didn't see \": \" or \":=\" in line", me); } else { biffMaybeAddf(useBiff, NRRD, "%s: failed to parse \"%s\" as field identifier", me, buff); } free(buff); return nrrdField_unknown; } free(buff); ret = nrrdField_keyvalue; } else { /* *colon = '\0'; */ /* else we successfully parsed a field identifier */ next += strlen(buff) + 2; free(buff); /* skip whitespace prior to start of first field descriptor */ next += strspn(next, _nrrdFieldSep); nio->pos = AIR_CAST(int, next - nio->line); ret = fld; } return ret; } /* ** NOTE: it is a common but unfortunate property of these parsers that ** they set values in the nrrd first, and then check their validity ** later. The reason for this is mostly the desire to centralize ** validity checking in one place, and right now that's in the ** _nrrdFieldCheck[] array of checkers */ static int _nrrdReadNrrdParse_nonfield(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); AIR_UNUSED(useBiff); /* char c; c= 10; write(2,&c,1); c= 69; write(2,&c,1); c=108; write(2,&c,1); c= 32; write(2,&c,1); c= 67; write(2,&c,1); c=104; write(2,&c,1); c=101; write(2,&c,1); c= 32; write(2,&c,1); c= 86; write(2,&c,1); c=105; write(2,&c,1); c=118; write(2,&c,1); c=101; write(2,&c,1); c= 33; write(2,&c,1); c= 10; write(2,&c,1); c= 10; write(2,&c,1); */ return 0; } static int _nrrdReadNrrdParse_comment(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_comment"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; /* this skips the '#' at nio->line[nio->pos] and any other ' ' and '#' */ if (nrrdCommentAdd(nrrd, info)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble adding comment", me); return 1; } return 0; } static int _nrrdReadNrrdParse_content(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_content"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; if (strlen(info) && !(nrrd->content = airStrdup(info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't strdup() content", me); return 1; } return 0; } static int _nrrdReadNrrdParse_number(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { /* static const char me[]="_nrrdReadNrrdParse_number"; char *info; info = nio->line + nio->pos; if (1 != sscanf(info, NRRD_BIG_INT_PRINTF, &(nrrd->num))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse number \"%s\"", me, info); return 1; } */ AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); AIR_UNUSED(useBiff); /* It was decided to just completely ignore this field. "number" is ** entirely redundant with the (required) sizes field, and there is no ** need to save it to, or learn it from, the header. In fact the "num" ** field was eliminated from the Nrrd struct some time ago, in favor of ** the nrrdElementNumber() function. It may seem odd or unfortunate that ** ** number: Hank Hill sells propane and propane accessories ** ** is a valid field specification, but at least Peggy is proud ... */ return 0; } static int _nrrdReadNrrdParse_type(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_type"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; if (!(nrrd->type = airEnumVal(nrrdType, info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse type \"%s\"", me, info); return 1; } if (_nrrdFieldCheck[nrrdField_type](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } #define _PARSE_ONE_VAL(FIELD, CONV, TYPE) \ if (1 != sscanf(info, CONV, &(FIELD))) { \ biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse " TYPE \ " from \"%s\"", me, info); \ return 1; \ } static int _nrrdReadNrrdParse_block_size(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_block_size"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; if (1 != airSingleSscanf(info, "%z", &(nrrd->blockSize))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse size_t" " from \"%s\"", me, info); } /* because blockSize and type fields may appear in any order, we can't use _nrrdFieldCheck[] */ return 0; } static int _nrrdReadNrrdParse_dimension(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_dimension"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _PARSE_ONE_VAL(nrrd->dim, "%u", "unsigned int"); if (_nrrdFieldCheck[nrrdField_dimension](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } /* ** checking nrrd->dim against zero is valid because it is initialized ** to zero, and, _nrrdReadNrrdParse_dimension() won't allow it to be ** set to anything outside the range [1, NRRD_DIM_MAX] */ #define _CHECK_HAVE_DIM \ if (0 == nrrd->dim) { \ biffMaybeAddf(useBiff, NRRD, \ "%s: don't yet have a valid dimension", me); \ return 1; \ } #define _CHECK_HAVE_SPACE_DIM \ if (0 == nrrd->spaceDim) { \ biffMaybeAddf(useBiff, NRRD, \ "%s: don't yet have a valid space dimension", me); \ return 1; \ } #define _CHECK_GOT_ALL_VALUES \ if (nrrd->dim != ret) { \ biffMaybeAddf(useBiff, NRRD, \ "%s: parsed %d values, but dimension is %d", \ me, ret, nrrd->dim); \ return 1; \ } static int _nrrdReadNrrdParse_sizes(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_sizes"; unsigned int ret; size_t val[NRRD_DIM_MAX]; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_DIM; ret = airParseStrZ(val, info, _nrrdFieldSep, nrrd->dim); _CHECK_GOT_ALL_VALUES; nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoSize, val); /* HEY: this is a very imperfect check of excess info */ if (nrrd->dim+1 == airParseStrZ(val, info, _nrrdFieldSep, nrrd->dim+1)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d sizes", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_sizes](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_spacings(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_spacings"; unsigned int ret; double val[NRRD_DIM_MAX]; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_DIM; ret = airParseStrD(val, info, _nrrdFieldSep, nrrd->dim); _CHECK_GOT_ALL_VALUES; nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoSpacing, val); /* HEY: this is a very imperfect check of excess info */ if (nrrd->dim+1 == airParseStrD(val, info, _nrrdFieldSep, nrrd->dim+1)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d spacings", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_spacings](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_thicknesses(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_thicknesses"; unsigned int ret; double val[NRRD_DIM_MAX]; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_DIM; ret = airParseStrD(val, info, _nrrdFieldSep, nrrd->dim); _CHECK_GOT_ALL_VALUES; nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoThickness, val); /* HEY: this is a very imperfect check of excess info */ if (nrrd->dim+1 == airParseStrD(val, info, _nrrdFieldSep, nrrd->dim+1)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d thicknesses", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_thicknesses](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_axis_mins(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_axis_mins"; unsigned int ret; double val[NRRD_DIM_MAX]; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_DIM; ret = airParseStrD(val, info, _nrrdFieldSep, nrrd->dim); _CHECK_GOT_ALL_VALUES; nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoMin, val); /* HEY: this is a very imperfect check of excess info */ if (nrrd->dim+1 == airParseStrD(val, info, _nrrdFieldSep, nrrd->dim+1)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d axis mins", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_axis_mins](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_axis_maxs(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_axis_maxs"; unsigned int ret; double val[NRRD_DIM_MAX]; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_DIM; ret = airParseStrD(val, info, _nrrdFieldSep, nrrd->dim); _CHECK_GOT_ALL_VALUES; nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoMax, val); /* HEY: this is a very imperfect check of excess info */ if (nrrd->dim+1 == airParseStrD(val, info, _nrrdFieldSep, nrrd->dim+1)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d axis maxs", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_axis_maxs](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } int _nrrdSpaceVectorParse(double val[NRRD_SPACE_DIM_MAX], char **hhP, unsigned int spaceDim, int useBiff) { static const char me[]="_nrrdSpaceVectorParse"; char *hh, *buff, sep[]=",)"; airArray *mop; unsigned int ret, dd; size_t length; mop = airMopNew(); hh = *hhP; /* skip past space */ length = strspn(hh, _nrrdFieldSep); hh += length; /* make sure we have something */ if (!*hh) { biffMaybeAddf(useBiff, NRRD, "%s: hit end of string before seeing (", me); airMopError(mop); return 1; } /* first, see if we're getting the non-vector */ if ( (strstr(hh, _nrrdNoSpaceVector) == hh) ) { if (!hh[strlen(_nrrdNoSpaceVector)] || strchr(_nrrdFieldSep, hh[strlen(_nrrdNoSpaceVector)])) { /* yes, we got the non-vector */ for (dd=0; dd spaceDim) { biffMaybeAddf(useBiff, NRRD, "%s: space dimension is %d, but seem to have %d " "coefficients", me, spaceDim, ret); airMopError(mop); return 1; } /* try to parse the values */ ret = airParseStrD(val, buff+1, ",", spaceDim); if (spaceDim != ret) { biffMaybeAddf(useBiff, NRRD, "%s: parsed %d values, but space dimension is %d", me, ret, spaceDim); airMopError(mop); return 1; } } /* probably not useful */ for (dd=spaceDim; ddline + nio->pos; _CHECK_HAVE_DIM; _CHECK_HAVE_SPACE_DIM; for (dd=0; dddim; dd++) { if (_nrrdSpaceVectorParse(nrrd->axis[dd].spaceDirection, &info, nrrd->spaceDim, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble getting space vector %d of %d", me, dd+1, nrrd->dim); return 1; } } if (strlen(info) != strspn(info, _nrrdFieldSep)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d directions", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_space_directions](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_centers(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_centers"; unsigned int ai; char *tok, *info, *last; airArray *mop; AIR_UNUSED(file); mop = airMopNew(); info = airStrdup(nio->line + nio->pos); airMopAdd(mop, info, airFree, airMopAlways); _CHECK_HAVE_DIM; for (ai=0; aidim; ai++) { tok = airStrtok(!ai ? info : NULL, _nrrdFieldSep, &last); if (!tok) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't extract string for center %d of %d", me, ai+1, nrrd->dim); airMopError(mop); return 1; } if (!strcmp(tok, NRRD_UNKNOWN)) { nrrd->axis[ai].center = nrrdCenterUnknown; continue; } if (!strcmp(tok, NRRD_NONE)) { nrrd->axis[ai].center = nrrdCenterUnknown; continue; } if (!(nrrd->axis[ai].center = airEnumVal(nrrdCenter, tok))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse center \"%s\" for axis %d", me, tok, ai); airMopError(mop); return 1; } } if (airStrtok(!ai ? info : NULL, _nrrdFieldSep, &last)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d centers", me, nrrd->dim); airMopError(mop); return 1; } if (_nrrdFieldCheck[nrrdField_centers](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } static int _nrrdReadNrrdParse_kinds(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_kinds"; unsigned int ai; char *info, *tok, *last; airArray *mop; AIR_UNUSED(file); mop = airMopNew(); info = airStrdup(nio->line + nio->pos); airMopAdd(mop, info, airFree, airMopAlways); _CHECK_HAVE_DIM; for (ai=0; aidim; ai++) { tok = airStrtok(!ai ? info : NULL, _nrrdFieldSep, &last); if (!tok) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't extract string for kind %d of %d", me, ai+1, nrrd->dim); airMopError(mop); return 1; } if (!strcmp(tok, NRRD_UNKNOWN)) { nrrd->axis[ai].kind = nrrdKindUnknown; continue; } if (!strcmp(tok, NRRD_NONE)) { nrrd->axis[ai].center = nrrdKindUnknown; continue; } if (!(nrrd->axis[ai].kind = airEnumVal(nrrdKind, tok))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse \"%s\" kind %d of %d", me, tok, ai+1, nrrd->dim); airMopError(mop); return 1; } } if (airStrtok(!ai ? info : NULL, _nrrdFieldSep, &last)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d kinds", me, nrrd->dim); airMopError(mop); return 1; } /* can't run this now because kinds can come before sizes, in which case the kind/size check in _nrrdFieldCheck_kinds will incorrectly flag an error ... if (_nrrdFieldCheck[nrrdField_kinds](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); airMopError(mop); return 1; } */ airMopOkay(mop); return 0; } static char * _nrrdGetQuotedString(char **hP, int useBiff) { static const char me[]="_nrrdGetQuotedString"; char *h, *buff, *ret; airArray *buffArr; unsigned int pos; airPtrPtrUnion appu; h = *hP; /* skip past space */ /* printf("!%s: h |%s|\n", me, h);*/ h += strspn(h, _nrrdFieldSep); /* printf("!%s: h |%s|\n", me, h);*/ /* make sure we have something */ if (!*h) { biffMaybeAddf(useBiff, NRRD, "%s: hit end of string before seeing opening \"", me); return NULL; } /* make sure we have a starting quote */ if ('"' != *h) { biffMaybeAddf(useBiff, NRRD, "%s: didn't start with \"", me); return NULL; } h++; /* parse string until end quote */ buff = NULL; appu.c = &buff; buffArr = airArrayNew(appu.v, NULL, sizeof(char), 2); if (!buffArr) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't create airArray", me); return NULL; } pos = airArrayLenIncr(buffArr, 1); /* pos should get 0 */ while (h[pos]) { /* printf("!%s: h+%d |%s|\n", me, pos, h+pos); */ if ('\"' == h[pos]) { break; } if ('\\' == h[pos] && '\"' == h[pos+1]) { h += 1; } buff[pos] = h[pos]; pos = airArrayLenIncr(buffArr, 1); } if ('\"' != h[pos]) { biffMaybeAddf(useBiff, NRRD, "%s: didn't see ending \" soon enough", me); return NULL; } h += pos + 1; buff[pos] = 0; ret = airStrdup(buff); airArrayNuke(buffArr); *hP = h; return ret; } static int _nrrdReadNrrdParse_labels(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_labels"; char *h; /* this is the "here" pointer which gradually progresses through all the labels (for all axes) */ unsigned int ai; char *info; AIR_UNUSED(file); /* because we have to correctly interpret quote marks, we can't simply rely on airParseStrS */ info = nio->line + nio->pos; /* printf("!%s: info |%s|\n", me, info); */ _CHECK_HAVE_DIM; h = info; for (ai=0; aidim; ai++) { if (!( nrrd->axis[ai].label = _nrrdGetQuotedString(&h, useBiff) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't get get label %d of %d\n", me, ai+1, nrrd->dim); return 1; } } if (strlen(h) != strspn(h, _nrrdFieldSep)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d labels", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_labels](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_units(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_units"; char *h; /* this is the "here" pointer which gradually progresses through all the units (for all axes) */ unsigned int ai; char *info; AIR_UNUSED(file); /* because we have to correctly interpret quote marks, we can't simply rely on airParseStrS */ info = nio->line + nio->pos; /* printf("!%s: info |%s|\n", me, info); */ _CHECK_HAVE_DIM; h = info; for (ai=0; aidim; ai++) { if (!( nrrd->axis[ai].units = _nrrdGetQuotedString(&h, useBiff) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't get get unit %d of %d\n", me, ai+1, nrrd->dim); return 1; } } if (strlen(h) != strspn(h, _nrrdFieldSep)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d units", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_units](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_min(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); AIR_UNUSED(useBiff); /* This field is no longer assumed to be anything meaningful, because nrrd->min no longer exists with the advent of NrrdRange. But, having the field is not an error, to not trip on older NRRD00.01 and NRRD0001 files which (legitimately) used it */ return 0; } static int _nrrdReadNrrdParse_max(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); AIR_UNUSED(useBiff); /* nrrd->max no longer exists, see above */ return 0; } static int _nrrdReadNrrdParse_old_min(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_old_min"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _PARSE_ONE_VAL(nrrd->oldMin, "%lg", "double"); if (_nrrdFieldCheck[nrrdField_old_min](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_old_max(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_old_max"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _PARSE_ONE_VAL(nrrd->oldMax, "%lg", "double"); if (_nrrdFieldCheck[nrrdField_old_max](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_endian(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_endian"; char *info; AIR_UNUSED(file); AIR_UNUSED(nrrd); info = nio->line + nio->pos; if (!(nio->endian = airEnumVal(airEndian, info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse endian \"%s\"", me, info); return 1; } return 0; } static int _nrrdReadNrrdParse_encoding(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_encoding"; char *info; int etype; AIR_UNUSED(file); AIR_UNUSED(nrrd); info = nio->line + nio->pos; if (!(etype = airEnumVal(nrrdEncodingType, info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse encoding \"%s\"", me, info); return 1; } nio->encoding = nrrdEncodingArray[etype]; return 0; } static int _nrrdReadNrrdParse_line_skip(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_line_skip"; char *info; AIR_UNUSED(file); AIR_UNUSED(nrrd); info = nio->line + nio->pos; _PARSE_ONE_VAL(nio->lineSkip, "%u", "unsigned int"); /* now that its unsigned, what error checking can I do? if (!(0 <= nio->lineSkip)) { biffMaybeAddf(useBiff, NRRD, "%s: lineSkip value %d invalid", me, nio->lineSkip); return 1; } */ return 0; } static int _nrrdReadNrrdParse_byte_skip(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_byte_skip"; char *info; AIR_UNUSED(file); AIR_UNUSED(nrrd); info = nio->line + nio->pos; _PARSE_ONE_VAL(nio->byteSkip, "%ld", "long int"); /* this check is being removed to enable the undocumented (in the file format spec) ability to say "byte skip: -N-1" in order to skip backwards from EOF by N bytes ** if (!(-1 <= nio->byteSkip)) { ** biffMaybeAddf(useBiff, NRRD, ** "%s: byteSkip value %ld invalid", me, nio->byteSkip); ** return 1; ** } */ return 0; } static int _nrrdReadNrrdParse_keyvalue(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_keyvalue"; char *keysep, *line, *key, *value; AIR_UNUSED(file); /* we know this will find something */ line = airStrdup(nio->line + nio->pos); if (!line) { biffMaybeAddf(useBiff, NRRD, "%s: can't allocate parse line", me); return 1; } keysep = strstr(line, ":="); if (!keysep) { biffMaybeAddf(useBiff, NRRD, "%s: didn't see \":=\" key/value delimiter in \"%s\"", me, line); free(line); return 1; } keysep[0] = 0; keysep[1] = 0; key = line; value = keysep+2; /* convert escape sequences */ airUnescape(key); airUnescape(value); nrrdKeyValueAdd(nrrd, key, value); free(line); return 0; } static int _nrrdReadNrrdParse_sample_units(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_sample_units"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; if (strlen(info) && !(nrrd->sampleUnits = airStrdup(info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't strdup() sampleUnits", me); return 1; } if (_nrrdFieldCheck[nrrdField_sample_units](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_space(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_space"; char *info; int space; AIR_UNUSED(file); info = nio->line + nio->pos; if (nio->seen[nrrdField_space_dimension]) { biffMaybeAddf(useBiff, NRRD, "%s: can't specify space after specifying " "space dimension (%d)", me, nrrd->spaceDim); return 1; } if (!(space = airEnumVal(nrrdSpace, info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse space \"%s\"", me, info); return 1; } if (nrrdSpaceSet(nrrd, space)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } if (_nrrdFieldCheck[nrrdField_space](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_space_dimension(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_space_dimension"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; if (nio->seen[nrrdField_space]) { biffMaybeAddf(useBiff, NRRD, "%s: can't specify space dimension after specifying " "space (%s)", me, airEnumStr(nrrdSpace, nrrd->space)); return 1; } _PARSE_ONE_VAL(nrrd->spaceDim, "%u", "unsigned int"); if (_nrrdFieldCheck[nrrdField_space_dimension](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_space_units(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_space_units"; char *h; /* this is the "here" pointer which gradually progresses through all the units (for all axes) */ unsigned int ai; char *info; AIR_UNUSED(file); /* because we have to correctly interpret quote marks, we can't simply rely on airParseStrS */ info = nio->line + nio->pos; /* printf("!%s: info |%s|\n", me, info); */ _CHECK_HAVE_SPACE_DIM; h = info; for (ai=0; aispaceDim; ai++) { if (!( nrrd->spaceUnits[ai] = _nrrdGetQuotedString(&h, useBiff) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't get get space unit %d of %d", me, ai+1, nrrd->spaceDim); return 1; } } if (_nrrdGetQuotedString(&h, AIR_FALSE)) { biffMaybeAddf(useBiff, NRRD, "%s: seemed to have more than expected %d space units", me, nrrd->spaceDim); return 1; } if (_nrrdFieldCheck[nrrdField_space_units](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_space_origin(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_space_origin"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_SPACE_DIM; if (_nrrdSpaceVectorParse(nrrd->spaceOrigin, &info, nrrd->spaceDim, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse origin \"%s\"", me, info); return 1; } if (_nrrdFieldCheck[nrrdField_space_origin](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_measurement_frame(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_measurement_frame"; double colvec[NRRD_SPACE_DIM_MAX]; unsigned int dd, ii; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_SPACE_DIM; for (dd=0; ddspaceDim; dd++) { /* we are going through the *columns* of the mf matrix */ if (_nrrdSpaceVectorParse(colvec, &info, nrrd->spaceDim, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble getting space vector %d of %d", me, dd+1, nrrd->spaceDim); return 1; } for (ii=0; iimeasurementFrame[dd][ii] = (ii < nrrd->spaceDim ? colvec[ii] : AIR_NAN); } } if (strlen(info) != strspn(info, _nrrdFieldSep)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d directions", me, nrrd->spaceDim); return 1; } for (dd=nrrd->spaceDim; ddmeasurementFrame[dd][ii] = AIR_NAN; } } if (_nrrdFieldCheck[nrrdField_measurement_frame](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } int _nrrdContainsPercentThisAndMore(const char *str, char thss) { const char *hh, *tmp; tmp = str; do { hh = strchr(tmp, '%'); if (!( hh && hh[1] )) { return 0; } if ('%' == hh[1]) { /* its an escaped % */ tmp = hh + 2; } else { break; } } while (tmp[0]); hh++; hh += strspn(hh, "0123456789"); if (!( hh[0] == thss )) { return 0; } hh += strcspn(hh, _nrrdFieldSep); return !!hh; } unsigned int _nrrdDataFNNumber(NrrdIoState *nio) { unsigned int ret; int ii; if (nio->dataFNFormat) { /* datafiles given in iterator form; count number of values */ ret = 0; for (ii = nio->dataFNMin; ((nio->dataFNStep > 0 && ii <= nio->dataFNMax) || (nio->dataFNStep < 0 && ii >= nio->dataFNMax)); ii += nio->dataFNStep) { ret += 1; } } else if (nio->dataFNArr->len) { /* datafiles given as an explicit list, or as a single file name, and in either case, nrrdDataFNAdd() is used to add them to the dataFNArr */ ret = nio->dataFNArr->len; } else { /* datafile is same as (attached) header file */ ret = 1; } return ret; } /* ** this always requires that the per-axis size fields have been set */ int _nrrdDataFNCheck(NrrdIoState *nio, Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdDataFNCheck"; size_t pieceSize, pieceNum; char stmp[AIR_STRLEN_SMALL]; if (!nio->seen[nrrdField_sizes]) { biffMaybeAddf(useBiff, NRRD, "%s: sorry, currently can't handle " "multiple detached data files without first knowing " "the \"%s\" field", me, airEnumStr(nrrdField, nrrdField_sizes)); return 1; } if (nio->dataFileDim < nrrd->dim) { /* this requires that the per-axis size fields have been set */ _nrrdSplitSizes(&pieceSize, &pieceNum, nrrd, nio->dataFileDim); if (pieceNum != _nrrdDataFNNumber(nio)) { biffMaybeAddf(useBiff, NRRD, "%s: expected %s filenames (of %u-D pieces) " "but got %u", me, airSprintSize_t(stmp, pieceNum), nio->dataFileDim, _nrrdDataFNNumber(nio)); return 1; } } else { /* we're getting data in "slabs" with the same dimension as the nrrd, so for simplicity we assume that they're all equal size */ if (_nrrdDataFNNumber(nio) > nrrd->axis[nrrd->dim-1].size) { biffMaybeAddf(useBiff, NRRD, "%s: can't have more pieces (%u) than axis %u " "slices (%s) when nrrd dimension and " "datafile dimension are both %u", me, _nrrdDataFNNumber(nio), nrrd->dim-1, airSprintSize_t(stmp, nrrd->axis[nrrd->dim-1].size), nrrd->dim); return 1; } if ((double)nrrd->axis[nrrd->dim-1].size/_nrrdDataFNNumber(nio) != nrrd->axis[nrrd->dim-1].size/_nrrdDataFNNumber(nio)) { biffMaybeAddf(useBiff, NRRD, "%s: number of datafiles (%d) doesn't divide into " "number of axis %u slices (%s)", me, (int)_nrrdDataFNNumber(nio), nrrd->dim-1, airSprintSize_t(stmp, nrrd->axis[nrrd->dim-1].size)); return 1; } } return 0; } /* ** Sat Jan 29 16:44:50 EST 2005: this used to "open the separate ** datafile, and set the FILE* in nio->dataFile, which otherwise will ** stay NULL", but now we support multiple detached data files. So. ** ** The job of this function is to map the "data file" specification to ** one or more filenames that can be passed direction to fopen for ** reading in the data. This involves parsing the various formats for ** identifying multiple data files, and possibly prefixing them with ** nio->path. */ static int _nrrdReadNrrdParse_data_file(FILE *ffile, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_data_file"; char *info, *nums; unsigned int linelen, tmp; airArray *mop; mop = airMopNew(); info = airStrdup(nio->line + nio->pos); if (!info) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't copy line!", me); return 1; } airMopAdd(mop, info, airFree, airMopAlways); /* HEY: this change should be made someday if (_nrrdContainsPercentThisAndMore(info, 'd') || _nrrdContainsPercentThisAndMore(info, 'u')) { */ if (_nrrdContainsPercentThisAndMore(info, 'd')) { /* ---------------------------------------------------------- */ /* --------- format.%d [] ----------- */ /* ---------------------------------------------------------- */ size_t sspn; _CHECK_HAVE_DIM; nums = info + strcspn(info, _nrrdFieldSep); sspn = strspn(nums, _nrrdFieldSep); nums[0] = 0; /* terminate so that format is now in info */ nums += sspn; if (!( 3 == sscanf(nums, "%d %d %d",&(nio->dataFNMin), &(nio->dataFNMax), &(nio->dataFNStep)) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse three ints (min, max, step) after " "data filename template", me); airMopError(mop); return 1; } if ( 4 == sscanf(nums, "%d %d %d %u", &(nio->dataFNMin), &(nio->dataFNMax), &(nio->dataFNStep), &(nio->dataFileDim)) ) { if (!AIR_IN_CL(1, nio->dataFileDim, nrrd->dim)) { biffMaybeAddf(useBiff, NRRD, "%s: datafile dimension %u outside valid range [1,%u]", me, nio->dataFileDim, nrrd->dim); airMopError(mop); return 1; } } else { nio->dataFileDim = nrrd->dim-1; } if (0 == nio->dataFNStep) { biffMaybeAddf(useBiff, NRRD, "%s: file number step must be non-zero", me); airMopError(mop); return 1; } if ((nio->dataFNMax - nio->dataFNMin)*(nio->dataFNStep) < 0) { biffMaybeAddf(useBiff, NRRD, "%s: file number max %d not approached from min %d " "by step %d", me, nio->dataFNMax, nio->dataFNMin, nio->dataFNStep); airMopError(mop); return 1; } if (!( nio->dataFNFormat = airStrdup(info) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't copy data filename format", me); airMopError(mop); return 1; } if (_nrrdDataFNCheck(nio, nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble with number of datafiles", me); airMopError(mop); return 1; } } else if (!strncmp(info, NRRD_LIST_FLAG, strlen(NRRD_LIST_FLAG))) { /* ---------------------------------------------------------- */ /* ------------------------- LIST --------------------------- */ /* ---------------------------------------------------------- */ _CHECK_HAVE_DIM; if (_nrrdHeaderCheck(nrrd, nio, AIR_TRUE)) { biffMaybeAddf(useBiff, NRRD, "%s: NRRD header is incomplete. \"" NRRD_LIST_FLAG "\" data file specification must be " "contiguous with end of header!", me); airMopError(mop); return 1; } info += strlen(NRRD_LIST_FLAG); if (info[0]) { if (1 == sscanf(info, "%u", &(nio->dataFileDim))) { if (!AIR_IN_CL(1, nio->dataFileDim, nrrd->dim)) { biffMaybeAddf(useBiff, NRRD, "%s: datafile dimension %u outside " "valid range [1,%u]", me, nio->dataFileDim, nrrd->dim); airMopError(mop); return 1; } } else { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse info after \"" NRRD_LIST_FLAG "\" as an int", me); airMopError(mop); return 1; } } else { /* nothing after NRRD_LIST_FLAG, so dataFileDim is implicit */ nio->dataFileDim = nrrd->dim-1; } /* read in all the datafile names */ do { /* yes, nio->line is re-used/over-written here, but I don't think that's a problem */ if (_nrrdOneLine(&linelen, nio, ffile)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble getting file name line", me); airMopError(mop); return 1; } if (linelen > 0) { tmp = airArrayLenIncr(nio->dataFNArr, 1); nio->dataFN[tmp] = airStrdup(nio->line); } } while (linelen > 0); if (_nrrdDataFNCheck(nio, nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble with number of datafiles", me); airMopError(mop); return 1; } } else { /* ---------------------------------------------------------- */ /* -------------------- (single filename) ------------------- */ /* ---------------------------------------------------------- */ /* there is apparently only a single detached data file; for this its okay to not yet know nrrd->dim */ tmp = airArrayLenIncr(nio->dataFNArr, 1); nio->dataFN[tmp] = airStrdup(info); nio->dataFileDim = 0; } airMopOkay(mop); return 0; } /* ******** nrrdFieldInfoParse[NRRD_FIELD_MAX+1]() ** ** These are all for parsing the stuff AFTER the colon */ int (*nrrdFieldInfoParse[NRRD_FIELD_MAX+1])(FILE *, Nrrd *, NrrdIoState *, int) = { _nrrdReadNrrdParse_nonfield, _nrrdReadNrrdParse_comment, _nrrdReadNrrdParse_content, _nrrdReadNrrdParse_number, _nrrdReadNrrdParse_type, _nrrdReadNrrdParse_block_size, _nrrdReadNrrdParse_dimension, _nrrdReadNrrdParse_space, _nrrdReadNrrdParse_space_dimension, _nrrdReadNrrdParse_sizes, _nrrdReadNrrdParse_spacings, _nrrdReadNrrdParse_thicknesses, _nrrdReadNrrdParse_axis_mins, _nrrdReadNrrdParse_axis_maxs, _nrrdReadNrrdParse_space_directions, _nrrdReadNrrdParse_centers, _nrrdReadNrrdParse_kinds, _nrrdReadNrrdParse_labels, _nrrdReadNrrdParse_units, _nrrdReadNrrdParse_min, _nrrdReadNrrdParse_max, _nrrdReadNrrdParse_old_min, _nrrdReadNrrdParse_old_max, _nrrdReadNrrdParse_endian, _nrrdReadNrrdParse_encoding, _nrrdReadNrrdParse_line_skip, _nrrdReadNrrdParse_byte_skip, _nrrdReadNrrdParse_keyvalue, _nrrdReadNrrdParse_sample_units, _nrrdReadNrrdParse_space_units, _nrrdReadNrrdParse_space_origin, _nrrdReadNrrdParse_measurement_frame, _nrrdReadNrrdParse_data_file }; /* kernel parsing is all in kernel.c */ teem-1.11.0~svn6057/src/nrrd/encodingRaw.c0000664000175000017500000001622712165631065017762 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" static int _nrrdEncodingRaw_available(void) { return AIR_TRUE; } static int _nrrdEncodingRaw_read(FILE *file, void *data, size_t elementNum, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingRaw_read"; size_t ret, bsize; int fd, dio, car; long savePos; char *data_c; size_t elementSize, maxChunkSize, remainderValue, chunkSize; size_t retTmp; char stmp[3][AIR_STRLEN_SMALL]; bsize = nrrdElementSize(nrrd)*elementNum; if (nio->format->usesDIO) { fd = fileno(file); dio = airDioTest(fd, data, bsize); } else { fd = -1; dio = airNoDio_format; } if (airNoDio_okay == dio) { if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "with direct I/O ... "); } ret = airDioRead(fd, data, bsize); if (ret != bsize) { biffAddf(NRRD, "%s: airDioRead got read only %s of %sbytes " "(%g%% of expected)", me, airSprintSize_t(stmp[0], ret), airSprintSize_t(stmp[1], bsize), 100.0*AIR_CAST(double, ret)/AIR_CAST(double, bsize)); return 1; } } else { if (2 <= nrrdStateVerboseIO) { if (AIR_DIO && nio->format->usesDIO) { fprintf(stderr, "with fread(), not DIO: %s ...", airNoDioErr(dio)); } } /* HEY: There's a bug in fread/fwrite in gcc 4.2.1 (with SnowLeopard). When it reads/writes a >=2GB data array, it pretends to succeed (i.e. the return value is the right number) but it hasn't actually read/written the data. The work-around is to loop over the data, reading/writing 1GB (or smaller) chunks. */ ret = 0; data_c = (char *)data; elementSize = nrrdElementSize(nrrd); maxChunkSize = 1024 * 1024 * 1024 / elementSize; while(ret < elementNum) { remainderValue = elementNum-ret; if (remainderValue < maxChunkSize) { chunkSize = remainderValue; } else { chunkSize = maxChunkSize; } retTmp = fread(&(data_c[ret*elementSize]), elementSize, chunkSize, file); ret += retTmp; if (retTmp != chunkSize) { biffAddf(NRRD, "%s: fread got only %s %s-sized things, not %s " "(%g%% of expected)", me, airSprintSize_t(stmp[0], ret), airSprintSize_t(stmp[1], nrrdElementSize(nrrd)), airSprintSize_t(stmp[2], elementNum), 100.0*AIR_CAST(double, ret)/AIR_CAST(double, elementNum)); return 1; } } car = fgetc(file); if (EOF != car) { if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "%s: WARNING: finished reading raw data, " "but file not at EOF\n", me); } ungetc(car, file); } if (2 <= nrrdStateVerboseIO && nio->byteSkip && stdin != file) { savePos = ftell(file); if (!fseek(file, 0, SEEK_END)) { double frac = (AIR_CAST(double, bsize) /AIR_CAST(double, ftell(file) + 1)); fprintf(stderr, "(%s: used %g%% of file for nrrd data)\n", me, 100.0*frac); fseek(file, savePos, SEEK_SET); } } } return 0; } static int _nrrdEncodingRaw_write(FILE *file, const void *data, size_t elementNum, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingRaw_write"; int fd, dio; size_t ret, bsize; const char *data_c; size_t elementSize, maxChunkSize, remainderValue, chunkSize; size_t retTmp; char stmp[3][AIR_STRLEN_SMALL]; bsize = nrrdElementSize(nrrd)*elementNum; if (nio->format->usesDIO) { fd = fileno(file); dio = airDioTest(fd, data, bsize); } else { fd = -1; dio = airNoDio_format; } if (airNoDio_okay == dio) { if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "with direct I/O ... "); } ret = airDioWrite(fd, data, bsize); if (ret != bsize) { biffAddf(NRRD, "%s: airDioWrite wrote only %s of %s bytes " "(%g%% of expected)", me, airSprintSize_t(stmp[0], ret), airSprintSize_t(stmp[1], bsize), 100.0*AIR_CAST(double, ret)/AIR_CAST(double, bsize)); return 1; } } else { if (2 <= nrrdStateVerboseIO) { if (AIR_DIO && nio->format->usesDIO) { fprintf(stderr, "with fread(), not DIO: %s ...", airNoDioErr(dio)); } } /* HEY: There's a bug in fread/fwrite in gcc 4.2.1 (with SnowLeopard). When it reads/writes a >=2GB data array, it pretends to succeed (i.e. the return value is the right number) but it hasn't actually read/written the data. The work-around is to loop over the data, reading/writing 1GB (or smaller) chunks. */ ret = 0; data_c = AIR_CAST(const char *, data); elementSize = nrrdElementSize(nrrd); maxChunkSize = 1024 * 1024 * 1024 / elementSize; while(ret < elementNum) { remainderValue = elementNum-ret; if (remainderValue < maxChunkSize) { chunkSize = remainderValue; } else { chunkSize = maxChunkSize; } retTmp = fwrite(&(data_c[ret*elementSize]), elementSize, chunkSize, file); ret += retTmp; if (retTmp != chunkSize) { biffAddf(NRRD, "%s: fwrite wrote only %s %s-sized things, not %s " "(%g%% of expected)", me, airSprintSize_t(stmp[0], ret), airSprintSize_t(stmp[1], nrrdElementSize(nrrd)), airSprintSize_t(stmp[2], elementNum), 100.0*AIR_CAST(double, ret)/AIR_CAST(double, elementNum)); return 1; } } fflush(file); /* if (ferror(file)) { biffAddf(NRRD, "%s: ferror returned non-zero", me); return 1; } */ } return 0; } const NrrdEncoding _nrrdEncodingRaw = { "raw", /* name */ "raw", /* suffix */ AIR_TRUE, /* endianMatters */ AIR_FALSE, /* isCompression */ _nrrdEncodingRaw_available, _nrrdEncodingRaw_read, _nrrdEncodingRaw_write }; const NrrdEncoding *const nrrdEncodingRaw = &_nrrdEncodingRaw; teem-1.11.0~svn6057/src/nrrd/encodingGzip.c0000664000175000017500000002543112165631065020137 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" static int _nrrdEncodingGzip_available(void) { #if TEEM_ZLIB return AIR_TRUE; #else return AIR_FALSE; #endif } /* ** Maximum size that allow zlib to try to read or write at once. ** The real limit is UINT_MAX, but a smaller value here permits ** exercising the multi-chunk capability of the code below. */ static unsigned int _nrrdZlibMaxChunk = UINT_MAX; /* ** nio->byteSkip < 0 functionality contributed by Katharina Quintus */ static int _nrrdEncodingGzip_read(FILE *file, void *_data, size_t elNum, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingGzip_read"; #if TEEM_ZLIB size_t sizeData, sizeRed; int error; long int bi; unsigned int didread, sizeChunk, maxChunk; char *data; gzFile gzfin; airPtrPtrUnion appu; sizeData = nrrdElementSize(nrrd)*elNum; /* Create the gzFile for reading in the gzipped data. */ if ((gzfin = _nrrdGzOpen(file, "rb")) == Z_NULL) { /* there was a problem */ biffAddf(NRRD, "%s: error opening gzFile", me); return 1; } /* keeps track of how many bytes have been successfully read in */ sizeRed = 0; /* zlib can only handle data sizes up to UINT_MAX ==> if there's more than UINT_MAX bytes to read in, we read in in chunks. However, we wrap a value _nrrdZlibMaxChunk around UINT_MAX for testing purposes. Given how sizeChunk is used below, we also cap chunk size at _nrrdZlibMaxChunk/2 to prevent overflow. */ maxChunk = _nrrdZlibMaxChunk/2; sizeChunk = AIR_CAST(unsigned int, AIR_MIN(sizeData, maxChunk)); if (nio->byteSkip < 0) { /* We don't know the size of the size to skip before the data, so decompress the data first into a temporary memory buffer. Then the byteskipping is then just memcpy-ing the appropriate region of memory from "buff" into the given "_data" pointer */ char *buff; airArray *buffArr; long backwards; /* setting the airArray increment to twice the chunk size means that for headers that are small compared to the data, the airArray never actually has to reallocate. The unit is 1 because we are managing the reading in terms of bytes (sizeof(char)==1 by definition) */ buff = NULL; appu.c = &buff; buffArr = airArrayNew(appu.v, NULL, 1, 2*sizeChunk); airArrayLenSet(buffArr, sizeChunk); if (!( buffArr && buffArr->data )) { biffAddf(NRRD, "%s: couldn't initialize airArray\n", me); return 1; } /* we keep reading in chunks as long as there hasn't been an error, and we haven't hit EOF (EOF signified by read == 0). Unlike the code below (for positive byteskip), we are obligated to read until the bitter end, and can't update sizeChunk to encompass only the required data. */ while (!(error = _nrrdGzRead(gzfin, buff + sizeRed, sizeChunk, &didread)) && didread > 0) { sizeRed += didread; if (didread >= sizeChunk) { /* we were able to read as much data as we requested, maybe there is more, so we need to make our temp buffer bigger */ unsigned int newlen = buffArr->len + sizeChunk; if (newlen < buffArr->len) { biffAddf(NRRD, "%s: array size will exceed uint capacity", me); return 1; } airArrayLenSet(buffArr, newlen); if (!buffArr->data) { biffAddf(NRRD, "%s: couldn't re-allocate data buffer", me); return 1; } } } if (error) { biffAddf(NRRD, "%s: error reading from gzFile", me); return 1; } /* backwards is (positive) number of bytes AFTER data that we ignore */ backwards = -nio->byteSkip - 1; if (sizeRed < sizeData + AIR_CAST(size_t, backwards)) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: expected %s bytes but received only %s", me, airSprintSize_t(stmp1, sizeData + AIR_CAST(size_t, backwards)), airSprintSize_t(stmp2, sizeRed)); return 1; } /* also handles nio->byteSkip == -N-1 signifying extra N bytes at end */ memcpy(_data, buff + sizeRed - sizeData - backwards, sizeData); airArrayNuke(buffArr); } else { /* no negative byteskip: after byteskipping, we can read directly into given data buffer */ if (nio->byteSkip > 0) { for (bi=0; bibyteSkip; bi++) { unsigned char b; /* Check to see if a single byte was able to be read. */ if (_nrrdGzRead(gzfin, &b, 1, &didread) != 0 || didread != 1) { biffAddf(NRRD, "%s: hit an error skipping byte %ld of %ld", me, bi, nio->byteSkip); return 1; } } } /* Pointer to chunks as we read them. */ data = AIR_CAST(char *, _data); while (!(error = _nrrdGzRead(gzfin, data, sizeChunk, &didread)) && didread > 0) { /* Increment the data pointer to the next available chunk. */ data += didread; sizeRed += didread; /* We only want to read as much data as we need, so we need to check to make sure that we don't request data that might be there but that we don't want. This will reduce sizeChunk when we get to the last block (which may be smaller than the original sizeChunk). */ if (sizeData >= sizeRed && sizeData - sizeRed < sizeChunk) { sizeChunk = AIR_CAST(unsigned int, sizeData - sizeRed); } } if (error) { biffAddf(NRRD, "%s: error reading from gzFile", me); return 1; } /* Check to see if we got out as much as we thought we should. */ if (sizeRed != sizeData) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: expected %s bytes but received %s", me, airSprintSize_t(stmp1, sizeData), airSprintSize_t(stmp2, sizeRed)); return 1; } } /* Close the gzFile. Since _nrrdGzClose does not close the FILE* we will not encounter problems when dataFile is closed later. */ if (_nrrdGzClose(gzfin)) { biffAddf(NRRD, "%s: error closing gzFile", me); return 1; } return 0; #else AIR_UNUSED(file); AIR_UNUSED(_data); AIR_UNUSED(elNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: sorry, this nrrd not compiled with gzip enabled", me); return 1; #endif } static int _nrrdEncodingGzip_write(FILE *file, const void *_data, size_t elNum, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingGzip_write"; #if TEEM_ZLIB size_t sizeData, sizeWrit; int fmt_i=0, error; const char *data; char fmt[4]; gzFile gzfout; unsigned int wrote, sizeChunk; sizeData = nrrdElementSize(nrrd)*elNum; /* Set format string based on the NrrdIoState parameters. */ fmt[fmt_i++] = 'w'; if (0 <= nio->zlibLevel && nio->zlibLevel <= 9) fmt[fmt_i++] = AIR_CAST(char, '0' + nio->zlibLevel); switch (nio->zlibStrategy) { case nrrdZlibStrategyHuffman: fmt[fmt_i++] = 'h'; break; case nrrdZlibStrategyFiltered: fmt[fmt_i++] = 'f'; break; case nrrdZlibStrategyDefault: default: break; } fmt[fmt_i] = 0; /* Create the gzFile for writing in the gzipped data. */ if ((gzfout = _nrrdGzOpen(file, fmt)) == Z_NULL) { /* there was a problem */ biffAddf(NRRD, "%s: error opening gzFile", me); return 1; } /* zlib can only handle data sizes up to UINT_MAX ==> if there's more than UINT_MAX bytes to write out, we write out in chunks. As above, we wrap _nrrdZlibMaxChunk around UINT_MAX for testing purposes. */ sizeChunk = AIR_CAST(unsigned int, AIR_MIN(sizeData, _nrrdZlibMaxChunk)); /* keeps track of what how much has been successfully written */ sizeWrit = 0; /* Pointer to the chunks as we write them. */ data = AIR_CAST(const char *, _data); /* Ok, now we can begin writing. */ while ((error = _nrrdGzWrite(gzfout, AIR_CVOIDP(data), sizeChunk, &wrote)) == 0 && wrote > 0) { /* Increment the data pointer to the next available spot. */ data += wrote; sizeWrit += wrote; /* We only want to write as much data as we need, so we need to check to make sure that we don't write more data than is there. This will reduce sizeChunk when we get to the last block (which may be smaller than the original sizeChunk). */ if (sizeData >= sizeWrit && sizeData - sizeWrit < sizeChunk) sizeChunk = AIR_CAST(unsigned int, sizeData - sizeWrit); } if (error) { biffAddf(NRRD, "%s: error writing to gzFile", me); return 1; } /* Check to see if we wrote out as much as we thought we should. */ if (sizeWrit != sizeData) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: expected to write %s bytes, but only wrote %s", me, airSprintSize_t(stmp1, sizeData), airSprintSize_t(stmp2, sizeWrit)); return 1; } /* Close the gzFile. Since _nrrdGzClose does not close the FILE* we will not encounter problems when dataFile is closed later. */ if (_nrrdGzClose(gzfout)) { biffAddf(NRRD, "%s: error closing gzFile", me); return 1; } return 0; #else AIR_UNUSED(file); AIR_UNUSED(_data); AIR_UNUSED(elNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: sorry, this nrrd not compiled with zlib " "(needed for gzip) enabled", me); return 1; #endif } const NrrdEncoding _nrrdEncodingGzip = { "gzip", /* name */ "raw.gz", /* suffix */ AIR_TRUE, /* endianMatters */ AIR_TRUE, /* isCompression */ _nrrdEncodingGzip_available, _nrrdEncodingGzip_read, _nrrdEncodingGzip_write }; const NrrdEncoding *const nrrdEncodingGzip = &_nrrdEncodingGzip; teem-1.11.0~svn6057/src/nrrd/comment.c0000664000175000017500000000661512165631065017164 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ******** nrrdCommentAdd() ** ** Adds a given string to the list of comments ** Leading spaces (' ') and comment chars ('#') are not included. ** ** This function does NOT use biff. */ int nrrdCommentAdd(Nrrd *nrrd, const char *_str) { /* static const char me[]="nrrdCommentAdd";*/ char *str; unsigned int ii; if (!(nrrd && _str)) { /* sprintf(err, "%s: got NULL pointer", me); biffMaybeAdd(NRRD, err, useBiff); */ return 1; } _str += strspn(_str, " #"); if (!strlen(_str)) { /* we don't bother adding comments with no length */ return 0; } if (!strcmp(_str, _nrrdFormatURLLine0) || !strcmp(_str, _nrrdFormatURLLine1)) { /* sneaky hack: don't store the format URL comment lines */ return 0; } str = airStrdup(_str); if (!str) { /* sprintf(err, "%s: couldn't strdup given string", me); biffMaybeAdd(NRRD, err, useBiff); */ return 1; } /* clean out carraige returns that would screw up reader */ airOneLinify(str); ii = airArrayLenIncr(nrrd->cmtArr, 1); if (!nrrd->cmtArr->data) { /* sprintf(err, "%s: couldn't lengthen comment array", me); biffMaybeAdd(NRRD, err, useBiff); */ return 1; } nrrd->cmt[ii] = str; return 0; } /* ******** nrrdCommentClear() ** ** blows away comments, but does not blow away the comment airArray */ void nrrdCommentClear(Nrrd *nrrd) { if (nrrd) { airArrayLenSet(nrrd->cmtArr, 0); } } /* ******** nrrdCommentCopy() ** ** copies comments from one nrrd to another ** Existing comments in nout are blown away ** ** This does NOT use biff. */ int nrrdCommentCopy(Nrrd *nout, const Nrrd *nin) { /* static const char me[]="nrrdCommentCopy"; */ int E; unsigned int numc, ii; if (!(nout && nin)) { /* sprintf(err, "%s: got NULL pointer", me); biffMaybeAdd(NRRD, err, useBiff); */ return 1; } if (nout == nin) { /* can't satisfy semantics of copying with nout==nin */ return 2; } nrrdCommentClear(nout); numc = nin->cmtArr->len; E = 0; for (ii=0; iicmt[ii]); } if (E) { /* sprintf(err, "%s: couldn't add all comments", me); biffMaybeAdd(NRRD, err, useBiff); */ return 3; } return 0; } teem-1.11.0~svn6057/src/nrrd/nrrdMacros.h0000664000175000017500000001562612165631065017643 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NRRD_MACROS_HAS_BEEN_INCLUDED #define NRRD_MACROS_HAS_BEEN_INCLUDED #ifdef __cplusplus extern "C" { #endif /* ******** NRRD_CELL_POS, NRRD_NODE_POS, NRRD_POS ******** NRRD_CELL_IDX, NRRD_NODE_IDX, NRRD_IDX ** ** the guts of nrrdAxisPos() and nrrdAxisIdx(), for converting ** between "index space" location and "position" or "world space" location, ** given the centering, min and max "position", and number of samples. ** ** Unlike nrrdAxisPos() and nrrdAxisIdx(), this assumes that center ** is either nrrdCenterCell or nrrdCenterNode, but not nrrdCenterUnknown. */ /* index to position, cell centering */ #define NRRD_CELL_POS(min, max, size, idx) \ AIR_AFFINE(0, (idx) + 0.5, (size), (min), (max)) /* index to position, node centering */ #define NRRD_NODE_POS(min, max, size, idx) \ AIR_AFFINE(0, (idx), (size)-1, (min), (max)) /* index to position, either centering */ #define NRRD_POS(center, min, max, size, idx) \ (nrrdCenterCell == (center) \ ? NRRD_CELL_POS((min), (max), (size), (idx)) \ : NRRD_NODE_POS((min), (max), (size), (idx))) /* position to index, cell centering */ #define NRRD_CELL_IDX(min, max, size, pos) \ (AIR_AFFINE((min), (pos), (max), 0, (size)) - 0.5) /* position to index, node centering */ #define NRRD_NODE_IDX(min, max, size, pos) \ AIR_AFFINE((min), (pos), (max), 0, (size)-1) /* position to index, either centering */ #define NRRD_IDX(center, min, max, size, pos) \ (nrrdCenterCell == (center) \ ? NRRD_CELL_IDX((min), (max), (size), (pos)) \ : NRRD_NODE_IDX((min), (max), (size), (pos))) /* ******** NRRD_SPACING ** ** the guts of nrrdAxisSpacing(), determines the inter-sample ** spacing, given centering, min and max "position", and number of samples ** ** Unlike nrrdAxisSpacing, this assumes that center is either ** nrrdCenterCell or nrrdCenterNode, but not nrrdCenterUnknown. */ #define NRRD_SPACING(center, min, max, size) \ (nrrdCenterCell == center \ ? ((max) - (min))/AIR_CAST(double, size) \ : ((max) - (min))/(AIR_CAST(double, (size)- 1))) \ /* ******** NRRD_COORD_UPDATE ** ** This is for doing the "carrying" associated with gradually incrementing an ** array of coordinates. Assuming that the given coordinate array "coord" has ** been incremented by adding 1 to coord[0], this macro will propagating the ** change up to higher axes (when the coordinate has reached the size on a ** lower axis.) In addition, the final statement of the macro prevents the ** last index from going past a valid value. ** ** Assumptions: ** -- coord[] and size[] should both be arrays of unsigned integral values, ** presumably size_t ** -- size[i] is >= 1 for all i= (size)[ddd]; \ ddd++) { \ (coord)[ddd] = 0; \ (coord)[ddd+1]++; \ } \ if (dim) { \ (coord)[(dim)-1] = AIR_MIN((coord)[(dim)-1], (size)[(dim)-1]-1); \ } \ } /* ******** NRRD_COORD_INCR ** ** increments coord[idx] (by one) and then calls NRRD_COORD_UPDATE to ** propagate this change as necessary to higher numbered axes. Does ** nothing if idx>=dim, since that would be an invalid index into ** coord[] and size[] */ #define NRRD_COORD_INCR(coord, size, dim, idx) \ if ((idx) < (dim)) { \ (coord)[(idx)]++; \ NRRD_COORD_UPDATE((coord)+(idx), (size)+(idx), (dim)-(idx)); \ } /* ******** NRRD_INDEX_GEN ** ** Given array coordinates "coord" and sizes "size", both of length "dim", ** this calculates the linear index represented by coord (assuming lower ** coordinates are for *faster* axes), and stores it in "I". Has the same ** assumptions as NRRD_COORD_UPDATE. */ #define NRRD_INDEX_GEN(I, coord, size, dim) \ { \ unsigned int ddd = (dim); \ (I) = 0; \ while (ddd) { \ ddd--; \ (I) = (coord)[ddd] + (size)[ddd]*(I); \ } \ } /* ******** NRRD_COORD_GEN ** ** opposite of NRRD_INDEX_GEN: going from linear index "I" to ** coordinate array "coord". */ #define NRRD_COORD_GEN(coord, size, dim, I) \ { \ unsigned int ddd; \ size_t myI = (I); \ for (ddd=0; ddd<(dim); ddd++) { \ (coord)[ddd] = myI % (size)[ddd]; \ myI /= (size)[ddd]; \ } \ } #ifdef __cplusplus } #endif #endif /* NRRD_MACROS_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/nrrd/read.c0000664000175000017500000005311012165631065016425 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" #if TEEM_BZIP2 #include #endif /* The "/ *Teem:" (without space) comments in here are an experiment */ char _nrrdRelativePathFlag[] = "./"; char _nrrdFieldSep[] = " \t"; char _nrrdLineSep[] = "\r\n"; char _nrrdNoSpaceVector[] = "none"; char _nrrdTextSep[] = " ,\t"; /* ** return length of next "line" in nio->headerStringRead */ unsigned int _nrrdHeaderStringOneLineStrlen(NrrdIoState *nio) { return AIR_CAST(unsigned int, strcspn(nio->headerStringRead + nio->headerStrpos, _nrrdLineSep)); } /* ** read next "line" in nio->headerStringRead */ unsigned int _nrrdHeaderStringOneLine(NrrdIoState *nio) { unsigned int len1, len2; len1 = _nrrdHeaderStringOneLineStrlen(nio); strncpy(nio->line, nio->headerStringRead + nio->headerStrpos, len1); nio->line[len1] = '\0'; nio->headerStrpos += len1; len2 = AIR_CAST(unsigned int, strspn(nio->headerStringRead + nio->headerStrpos, _nrrdLineSep)); nio->headerStrpos += len2; return len1; } /* ** _nrrdOneLine ** ** wrapper around airOneLine; does re-allocation of line buffer ** ("line") in the NrrdIoState if needed. The return value semantics ** are similar, except that what airOneLine would return, we put ** in *lenP. If there is an error (airOneLine returned 0, ** something couldn't be allocated), *lenP is set to 0, and ** we return 1. HITTING EOF IS NOT ACTUALLY AN ERROR, see code ** below. Otherwise we return 0. ** ** Does use biff */ int _nrrdOneLine(unsigned int *lenP, NrrdIoState *nio, FILE *file) { static const char me[]="_nrrdOneLine"; char **line; airArray *mop, *lineArr; airPtrPtrUnion appu; unsigned int lineIdx, len, needLen; if (!( lenP && nio && (file || nio->headerStringRead))) { biffAddf(NRRD, "%s: got NULL pointer (%p, %p, %p/%p)", me, AIR_CAST(void*, lenP), AIR_CAST(void*, nio), AIR_CAST(void*, file), nio->headerStringRead); return 1; } if (0 == nio->lineLen) { /* nio->line hasn't been allocated for anything */ nio->lineLen = 3; nio->line = (char*)malloc(nio->lineLen); if (!nio->line) { biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen); *lenP = 0; return 1; } } if (file) { len = airOneLine(file, nio->line, nio->lineLen); } else { /* NOTE: NULL-ity error check above makes this safe */ needLen = _nrrdHeaderStringOneLineStrlen(nio); if (needLen+1 > nio->lineLen) { nio->lineLen = needLen+1; airFree(nio->line); /* lose previous allocated line */ nio->line = (char*)malloc(nio->lineLen); if (!nio->line) { biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen); *lenP = 0; return 1; } } len = _nrrdHeaderStringOneLine(nio); } if (len <= nio->lineLen) { /* otherwise we hit EOF (or end of nio->headerStringRead) before a newline, or the line (possibly empty) fit within the nio->line, neither of which is an error here */ *lenP = len; } else { /* line didn't fit in buffer, so we have to increase line buffer size and put the line together in pieces */ /* NOTE: this will never happen when reading from nio->headerStringRead */ appu.cp = &line; lineArr = airArrayNew(appu.v, NULL, sizeof(char *), 1); if (!lineArr) { biffAddf(NRRD, "%s: couldn't allocate airArray", me); *lenP = 0; return 1; } airArrayPointerCB(lineArr, airNull, airFree); mop = airMopNew(); airMopAdd(mop, lineArr, (airMopper)airArrayNuke, airMopAlways); while (len == nio->lineLen+1) { lineIdx = airArrayLenIncr(lineArr, 1); if (!lineArr->data) { biffAddf(NRRD, "%s: couldn't increment line buffer array", me); *lenP = 0; airMopError(mop); return 1; } line[lineIdx] = nio->line; nio->lineLen *= 2; nio->line = (char*)malloc(nio->lineLen); if (!nio->line) { biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen); *lenP = 0; airMopError(mop); return 1; } len = airOneLine(file, nio->line, nio->lineLen); } /* last part did fit in nio->line buffer, also save this into line[] */ lineIdx = airArrayLenIncr(lineArr, 1); if (!lineArr->data) { biffAddf(NRRD, "%s: couldn't increment line buffer array", me); *lenP = 0; airMopError(mop); return 1; } line[lineIdx] = nio->line; nio->lineLen *= 3; /* for good measure */ nio->line = (char*)malloc(nio->lineLen); if (!nio->line) { biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen); *lenP = 0; airMopError(mop); return 1; } /* now concatenate everything into a new nio->line */ strcpy(nio->line, ""); for (lineIdx=0; lineIdxlen; lineIdx++) { strcat(nio->line, line[lineIdx]); } /* HEY: API is bad: *lenP should be a size_t pointer! */ *lenP = AIR_UINT(strlen(nio->line)) + 1; airMopError(mop); } return 0; } /* ** _nrrdCalloc() ** ** allocates the data for the array, but only if necessary (as informed by ** nio->oldData and nio->oldDataSize). ** ** as a recent feature, this will handle the extra work of allocating ** memory in the special way required for direct IO, if possible. For ** this to work, though, the FILE *file has to be passed. Since file ** is not otherwise needed, it can be passed as NULL for non-direct-IO ** situations. In any case, if the directIO-compatible allocation fails ** its not error, and we revert to regular allocation. ** ** NOTE: this assumes the checking that is done by _nrrdHeaderCheck */ int _nrrdCalloc(Nrrd *nrrd, NrrdIoState *nio, FILE *file) { static const char me[]="_nrrdCalloc"; size_t needDataSize; int fd; needDataSize = nrrdElementNumber(nrrd)*nrrdElementSize(nrrd); if (nio->oldData && needDataSize == nio->oldDataSize) { /* re-use old data */ nrrd->data = nio->oldData; /* its not an error to have a directIO-incompatible pointer, so there's no other error checking to do here */ } else { nrrd->data = airFree(nrrd->data); fd = file ? fileno(file) : -1; if (nrrdEncodingRaw == nio->encoding && -1 != fd && airNoDio_okay == airDioTest(fd, NULL, needDataSize)) { nrrd->data = airDioMalloc(needDataSize, fd); } if (!nrrd->data) { /* directIO-compatible allocation wasn't tried, or it failed */ nrrd->data = malloc(needDataSize); } if (!nrrd->data) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: couldn't allocate %s things of size %s", me, airSprintSize_t(stmp1, nrrdElementNumber(nrrd)), airSprintSize_t(stmp2, nrrdElementSize(nrrd))); return 1; } } /* make it look like it came from calloc(), as used by nrrdNew() */ memset(nrrd->data, 0, needDataSize); return 0; } /* ******** nrrdLineSkip ** ** public for the sake of things like "unu make" ** uses the NrrdIoState for its line buffer (used by _nrrdOneLine) */ int nrrdLineSkip(FILE *dataFile, NrrdIoState *nio) { static const char me[]="nrrdLineSkip"; unsigned int lsi, skipRet; /* For compressed data: If you don't actually have ascii headers on top of your gzipped data then you will potentially huge lines while _nrrdOneLine looks for line terminations. Quoting Gordon: "Garbage in, Garbage out." */ if (!( dataFile && nio )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } for (lsi=0; lsilineSkip; lsi++) { if (_nrrdOneLine(&skipRet, nio, dataFile)) { biffAddf(NRRD, "%s: error skipping line %u of %u", me, lsi+1, nio->lineSkip); return 1; } if (!skipRet) { biffAddf(NRRD, "%s: hit EOF skipping line %u of %u", me, lsi+1, nio->lineSkip); return 1; } } return 0; } /* ******** nrrdByteSkip ** ** public for the sake of things like "unu make" ** uses nio for information about how much data should actually be skipped ** with negative byteSkip */ int nrrdByteSkip(FILE *dataFile, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="nrrdByteSkip"; int skipRet; size_t bsize; if (!( dataFile && nrrd && nio )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nio->encoding->isCompression) { biffAddf(NRRD, "%s: this function can't work with compressed " "encoding %s", me, nio->encoding->name); return 1; } if (nio->byteSkip < 0) { long backwards; if (nrrdEncodingRaw != nio->encoding) { biffAddf(NRRD, "%s: this function can do backwards byte skip only " "in %s encoding, not %s", me, nrrdEncodingRaw->name, nio->encoding->name); return 1; } if (stdin == dataFile) { biffAddf(NRRD, "%s: can't fseek on stdin", me); return 1; } bsize = nrrdElementNumber(nrrd)/_nrrdDataFNNumber(nio); bsize *= nrrdElementSize(nrrd); /* backwards is (positive) number of bytes AFTER data that we ignore */ backwards = -nio->byteSkip - 1; /* HEY what if bsize fits in size_t but not in (signed) long? */ if (fseek(dataFile, -AIR_CAST(long, bsize) - backwards, SEEK_END)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: failed to fseek(dataFile, %s, SEEK_END)", me, airSprintSize_t(stmp, bsize)); return 1; } if (nrrdStateVerboseIO >= 2) { fprintf(stderr, "(%s: actually skipped %d bytes)\n", me, (int)ftell(dataFile)); } } else { if ((stdin == dataFile) || (-1==fseek(dataFile, nio->byteSkip, SEEK_CUR))) { long skipi; /* fseek failed, perhaps because we're reading stdin, so we revert to consuming the input one byte at a time */ for (skipi=0; skipibyteSkip; skipi++) { skipRet = fgetc(dataFile); if (EOF == skipRet) { biffAddf(NRRD, "%s: hit EOF skipping byte %ld of %ld", me, skipi, nio->byteSkip); return 1; } } } } return 0; } /* ** _nrrdRead() ** ** read in nrrd from a given file *OR* given string. The main job of ** this function is to start reading the file/string, to determine the ** format, and then call the appropriate format's reader. This means ** that the various encoding (data) readers can assume that ** nio->format is usefully set. ** ** If (file), the only input information that nio is used for is ** nio->path, so that detached header-relative data files can be ** found. If (string), the headerStr-related fields in the _nio will ** be set/used */ int _nrrdRead(Nrrd *nrrd, FILE *file, const char *string, NrrdIoState *_nio) { static const char me[]="_nrrdRead"; unsigned int llen; NrrdIoState *nio; int nfi; airArray *mop; /* sanity check, for good measure */ if (!nrrdSanity()) { biffAddf(NRRD, "%s: sanity check FAILED: have to fix and re-compile", me); return 1; } if (!((file || string) && nrrd)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (file && string) { biffAddf(NRRD, "%s: can't read from both file and string", me); return 1; } mop = airMopNew(); if (_nio) { nio = _nio; } else { nio = nrrdIoStateNew(); if (!nio) { biffAddf(NRRD, "%s: couldn't alloc I/O struct", me); return 1; } airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); } /* remember old data pointer and allocated size. Whether or not to free() this memory will be decided later */ nio->oldData = nrrd->data; nio->oldDataSize = (nio->oldData ? nrrdElementNumber(nrrd)*nrrdElementSize(nrrd) : 0); /* fprintf(stderr, "!%s: nio->oldData = %p, oldDataSize = %d\n", me, nio->oldData, (int)(nio->oldDataSize)); */ nrrd->data = NULL; /* initialize given nrrd (but we have thwarted freeing existing memory) */ nrrdInit(nrrd); /* tell the nio where to find the string to read from */ nio->headerStringRead = string; if (_nrrdOneLine(&llen, nio, file)) { biffAddf(NRRD, "%s: error getting first line (containing \"magic\")", me); airMopError(mop); return 1; } if (!llen) { biffAddf(NRRD, "%s: immediately hit EOF", me); airMopError(mop); return 1; } nio->format = nrrdFormatUnknown; for (nfi = nrrdFormatTypeUnknown+1; nfi < nrrdFormatTypeLast; nfi++) { if (nrrdFormatArray[nfi]->contentStartsLike(nio)) { nio->format = nrrdFormatArray[nfi]; break; } } if (nrrdFormatUnknown == nio->format) { char linestart[AIR_STRLEN_SMALL], stmp[AIR_STRLEN_SMALL]; airStrcpy(linestart, AIR_STRLEN_SMALL, nio->line); if (strlen(linestart) != strlen(nio->line)) { biffAddf(NRRD, "%s: couldn't parse (length %s) line starting " "with \"%s\" as magic or beginning of any recognized format", me, airSprintSize_t(stmp, strlen(nio->line)), linestart); } else { biffAddf(NRRD, "%s: couldn't parse \"%s\" as magic or beginning " "of any recognized format", me, nio->line); } airMopError(mop); return 1; } if (string && nrrdFormatNRRD != nio->format) { biffAddf(NRRD, "%s: sorry, can only read %s files from strings (not %s)", me, nrrdFormatNRRD->name, nio->format->name); airMopError(mop); return 1; } /* try to read the file */ if (nio->format->read(file, nrrd, nio)) { biffAddf(NRRD, "%s: trouble reading %s file", me, nio->format->name); airMopError(mop); return 1; } /* reshape up grayscale images, if desired */ if (nio->format->isImage && 2 == nrrd->dim && nrrdStateGrayscaleImage3D) { if (nrrdAxesInsert(nrrd, nrrd, 0)) { biffAddf(NRRD, "%s:", me); return 1; } } /* free prior memory if we didn't end up using it */ /* HEY: could actually do a check on the nio to refine this */ if (nio->oldData != nrrd->data) { nio->oldData = airFree(nio->oldData); nio->oldDataSize = 0; } /* finally, make sure that what we're returning isn't malformed somehow, except that we (probably stupidly) allow nrrd->data to be NULL, given the possibility of using nio->skipData */ if (_nrrdCheck(nrrd, AIR_FALSE, AIR_TRUE)) { biffAddf(NRRD, "%s: problem with nrrd after reading", me); return 1; } airMopOkay(mop); return 0; } /* ******** nrrdRead() ** ** now just a wrapper around _nrrdRead(); reads a NRRD from a FILE * */ int nrrdRead(Nrrd *nrrd, FILE *file, NrrdIoState *_nio) { static const char me[]="nrrdRead"; if (_nrrdRead(nrrd, file, NULL, _nio)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ******** nrrdStringRead() ** ** also a wrapper around _nrrdRead(); reads a NRRD from a char *. ** ** Because the same underlying _nrrdRead() is used, the same semantics ** about using existing nrrd->data when possible applies, as does the ** action of nrrdStateGrayscaleImage3D */ int nrrdStringRead(Nrrd *nrrd, const char *string, NrrdIoState *_nio) { static const char me[]="nrrdRead"; if (_nrrdRead(nrrd, NULL, string, _nio)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ** _nrrdSplitName() ** ** splits a file name into a path and a base filename. The path ** separator is '/', but there is a hack (thanks Torsten Rohlfing) ** which allows '\' to work on Windows. The division between the path ** and the base is the last path separator in the file name. The path ** is everything prior to this, and base is everything after (so the ** base does NOT start with the path separator). If there is not a ** '/' in the name, or if a path separator appears as the last ** character, then the path is set to ".", and the name is copied into ** base. */ void _nrrdSplitName(char **dirP, char **baseP, const char *name) { char *where; if (dirP) { *dirP = (char *)airFree(*dirP); } if (baseP) { *baseP = (char *)airFree(*baseP); } where = strrchr(name, '/'); #ifdef _WIN32 /* Deal with Windows "\" path separators; thanks to Torsten Rohlfing */ if ( !where || (strrchr(name, '\\') > where) ) { where = strrchr(name, '\\'); } #endif /* we found a valid break if the last directory character is somewhere in the string except the last character */ if (where && airStrlen(where) > 1) { if (dirP) { *dirP = airStrdup(name); (*dirP)[where - name] = 0; } if (baseP) { *baseP = airStrdup(where + 1); } } else { /* if the name had no slash, its in the current directory, which means that we need to explicitly store "." as the header directory in case we have header-relative data. */ if (dirP) { *dirP = airStrdup("."); } if (baseP) { *baseP = airStrdup(name); } } return; } /* ******** nrrdLoad() ** ** ** ** call tree for this, to help figure out what's going on ** ** read.c/nrrdLoad ** | read.c/_nrrdSplitName ** | read.c/nrrdRead ** | nio->format->read ** = formatNRRD.c/_nrrdFormatNRRD_read: ** | read.c/_nrrdOneLine ** | parseNrrd.c/_nrrdReadNrrdParseField ** | parseNrrd.c/nrrdFieldInfoParse[] ** = parseNrrd.c/_nrrdReadNrrdParse_data_file ** | fopen(dataName) ** | formatNRRD.c/_nrrdHeaderCheck ** | read.c/nrrdLineSkip ** | read.c/nrrdByteSkip ** | nio->encoding->read ** = encodingRaw.c/_nrrdEncodingRaw_read ** | read.c/_nrrdCalloc ** | formatNRRD.c/nrrdSwapEndian ** | miscAir.c/airFclose ** ** (more documentation here) ** ** sneakiness: returns 2 if the reason for problem was a failed fopen(). ** */ int /*Teem: biff if (ret) */ nrrdLoad(Nrrd *nrrd, const char *filename, NrrdIoState *nio) { static const char me[]="nrrdLoad"; FILE *file; airArray *mop; if (!(nrrd && filename)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); if (!nio) { nio = nrrdIoStateNew(); if (!nio) { biffAddf(NRRD, "%s: couldn't alloc I/O struct", me); return 1; } airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); } /* we save the directory of the filename given to us so that if it turns out that this is a detached header with a header-relative data file, then we will know how to find the data file */ _nrrdSplitName(&(nio->path), NULL, filename); /* printf("!%s: |%s|%s|\n", me, nio->dir, nio->base); */ if (!( file = airFopen(filename, stdin, "rb") )) { biffAddf(NRRD, "%s: fopen(\"%s\",\"rb\") failed: %s", me, filename, strerror(errno)); airMopError(mop); return 2; } airMopAdd(mop, file, (airMopper)airFclose, airMopOnError); /* non-error exiting is handled below */ if (nrrdRead(nrrd, file, nio)) { biffAddf(NRRD, "%s: trouble reading \"%s\"", me, filename); airMopError(mop); return 1; } if (nrrdFormatNRRD == nio->format && nio->keepNrrdDataFileOpen && file == nio->dataFile ) { /* we have to keep the datafile open. If was attached, we can't close file, because that is the datafile. If was detached, file != nio->dataFile, so we can close file. */ } else { /* always close non-NRRD files */ airFclose(file); } airMopOkay(mop); return 0; } int nrrdLoadMulti(Nrrd *const *nin, unsigned int ninLen, const char *fnameFormat, unsigned int numStart, NrrdIoState *nio) { static const char me[]="nrrdLoadMulti"; char *fname; airArray *mop; unsigned int nii; if (!(nin && fnameFormat)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( _nrrdContainsPercentThisAndMore(fnameFormat, 'u') )) { biffAddf(NRRD, "%s: given format \"%s\" doesn't seem to " "have the \"%%u\" conversion specification to sprintf " "an unsigned int\n", me, fnameFormat); return 1; } mop = airMopNew(); /* should be big enough for the number replacing the format sequence */ fname = AIR_CAST(char *, malloc(strlen(fnameFormat) + 128)); if (!(fname)) { biffAddf(NRRD, "%s: couldn't allocate local fname buffer", me); airMopError(mop); return 1; } airMopAdd(mop, fname, airFree, airMopAlways); for (nii=0; niispaceDim. ** ** The first six spaces here are PATIENT-ORIENTED spaces, which are ** properly speaking aligned with the patient, and not the scanner ** itself. But nrrdSpaceScannerXYZ and nrrdSpaceScannerXYZTime are ** DEVICE-ORIENTED spaces, irrespective of the patient, used in a ** previous version of the DICOM standard. When the two spaces are ** lined up with normal patient orientation in the scanner, ** nrrdSpaceScannerXYZ is the same as nrrdSpaceLeftPosteriorSuperior. ** To quote Part 3 (Information Object Definitions) of the DICOM spec ** (page 275): "If a patient lies parallel to the ground, face-up on ** the table, with his feet-to-head direction same as the ** front-to-back direction of the imaging equipment, the direction of ** the axes of this patient based coordinate system and the equipment ** based coordinate system in previous versions of this Standard will ** coincide." ** ** Keep in sync: ** enumsNrrd.c: nrrdSpace airEnum ** simple.c: int nrrdSpaceDimension(int space) */ enum { nrrdSpaceUnknown, nrrdSpaceRightAnteriorSuperior, /* 1: NIFTI-1 (right-handed) */ nrrdSpaceLeftAnteriorSuperior, /* 2: standard Analyze (left-handed) */ nrrdSpaceLeftPosteriorSuperior, /* 3: DICOM 3.0 (right-handed) */ nrrdSpaceRightAnteriorSuperiorTime, /* 4: */ nrrdSpaceLeftAnteriorSuperiorTime, /* 5: */ nrrdSpaceLeftPosteriorSuperiorTime, /* 6: */ nrrdSpaceScannerXYZ, /* 7: ACR/NEMA 2.0 (pre-DICOM 3.0) */ nrrdSpaceScannerXYZTime, /* 8: */ nrrdSpace3DRightHanded, /* 9: */ nrrdSpace3DLeftHanded, /* 10: */ nrrdSpace3DRightHandedTime, /* 11: */ nrrdSpace3DLeftHandedTime, /* 12: */ nrrdSpaceLast }; #define NRRD_SPACE_MAX 12 /* ******** nrrdSpacingStatus* enum ** ** a way of describing how spacing information is known or not known for a ** given axis, as determined by nrrdSpacingCalculate */ enum { nrrdSpacingStatusUnknown, /* 0: nobody knows, or invalid axis choice */ nrrdSpacingStatusNone, /* 1: neither axis->spacing nor axis->spaceDirection is set */ nrrdSpacingStatusScalarNoSpace, /* 2: axis->spacing set, w/out space info */ nrrdSpacingStatusScalarWithSpace, /* 3: axis->spacing set, but there *is* space info, which means the spacing does *not* live in the surrounding space */ nrrdSpacingStatusDirection, /* 4: axis->spaceDirection set, and measured according to surrounding space */ nrrdSpacingStatusLast }; #define NRRD_SPACING_STATUS_MAX 4 /* ******** nrrdOriginStatus* enum ** ** how origin information was or was not computed by nrrdOriginCalculate */ enum { nrrdOriginStatusUnknown, /* 0: nobody knows, or invalid parms */ nrrdOriginStatusDirection, /* 1: chosen axes have spaceDirections */ nrrdOriginStatusNoMin, /* 2: axis->min doesn't exist */ nrrdOriginStatusNoMaxOrSpacing, /* 3: axis->max or ->spacing doesn't exist */ nrrdOriginStatusOkay, /* 4: all is well */ nrrdOriginStatusLast }; /* ---- BEGIN non-NrrdIO */ /* ******** nrrdMeasure enum ** ** ways to "measure" some portion of the array ** NEEDS TO BE IN SYNC WITH: ** - nrrdMeasure airEnum in enumsNrrd.c ** - nrrdMeasureLine function array in measure.c */ enum { nrrdMeasureUnknown, nrrdMeasureMin, /* 1: smallest value */ nrrdMeasureMax, /* 2: biggest value */ nrrdMeasureMean, /* 3: average of values */ nrrdMeasureMedian, /* 4: value at 50th percentile */ nrrdMeasureMode, /* 5: most common value */ nrrdMeasureProduct, /* 6: product of all values */ nrrdMeasureSum, /* 7: sum of all values */ nrrdMeasureL1, /* 8 */ nrrdMeasureL2, /* 9 */ nrrdMeasureL4, /* 10 */ nrrdMeasureNormalizedL2, /* 11 */ nrrdMeasureRootMeanSquare, /* 12 */ nrrdMeasureLinf, /* 13 */ nrrdMeasureVariance, /* 14 */ nrrdMeasureSD, /* 15: standard deviation */ nrrdMeasureCoV, /* 16: coefficient of variation */ nrrdMeasureSkew, /* 17: skew */ nrrdMeasureLineSlope, /* 18: slope of line of best fit */ nrrdMeasureLineIntercept, /* 19: y-intercept of line of best fit */ nrrdMeasureLineError, /* 20: error of line fitting */ /* ** the nrrduMeasureHisto* measures interpret the array as a ** histogram of some implied value distribution */ nrrdMeasureHistoMin, /* 21 */ nrrdMeasureHistoMax, /* 22 */ nrrdMeasureHistoMean, /* 23 */ nrrdMeasureHistoMedian, /* 24 */ nrrdMeasureHistoMode, /* 25 */ nrrdMeasureHistoProduct, /* 26 */ nrrdMeasureHistoSum, /* 27 */ nrrdMeasureHistoL2, /* 28 */ nrrdMeasureHistoVariance, /* 29 */ nrrdMeasureHistoSD, /* 30 */ nrrdMeasureLast }; #define NRRD_MEASURE_MAX 30 #define NRRD_MEASURE_DESC \ "Possibilities include:\n " \ "\b\bo \"min\", \"max\", \"mean\", \"median\", \"mode\", \"variance\", " \ "\"skew\", \"sum\", \"product\", : (self-explanatory)\n " \ "\b\bo \"intc\", \"slope\", \"error\": " \ "intercept, slope, and error from line fitting\n " \ "\b\bo \"stdv\": standard deviation\n " \ "\b\bo \"cov\": coefficient of variation\n " \ "\b\bo \"product\", \"sum\": product or sum of all values\n " \ "\b\bo \"L1\", \"L2\", \"L4\", \"NL2\", \"RMS\", \"Linf\": vector norms\n " \ "\b\bo \"histo-min\", \"histo-max\", \"histo-mean\"," \ "\"histo-median\", \"histo-mode\", \"histo-product\", \"histo-l2\", " \ "\"histo-sum\", \"histo-variance\", \"histo-sd\": same measures, " \ "but for situations " \ "where we're given not the original values, but a histogram of them." /* ******** nrrdBlind8BitRange ** ** whether or not to blindly say that the range of 8-bit data is ** [0,255] (uchar) or [SCHAR_MIN,SCHAR_MAX] (signed char) */ enum { nrrdBlind8BitRangeUnknown, /* 0 */ nrrdBlind8BitRangeTrue, /* 1: blindly use the widest extrema (e.g., [0-255] for uchar, regardless of what's really present in the data values */ nrrdBlind8BitRangeFalse, /* 2: use the exact value range in the data */ nrrdBlind8BitRangeState, /* 3: defer to nrrdStateBlind8BitMinMax */ nrrdBlind8BitRangeLast }; #define NRRD_BLIND_8BIT_RANGE_MAX 3 /* ******** nrrdUnaryOp enum ** ** for unary operations on nrrds */ enum { nrrdUnaryOpUnknown, nrrdUnaryOpNegative, /* 1 */ nrrdUnaryOpReciprocal, /* 2 */ nrrdUnaryOpSin, /* 3 */ nrrdUnaryOpCos, /* 4 */ nrrdUnaryOpTan, /* 5 */ nrrdUnaryOpAsin, /* 6 */ nrrdUnaryOpAcos, /* 7 */ nrrdUnaryOpAtan, /* 8 */ nrrdUnaryOpExp, /* 9 */ nrrdUnaryOpLog, /* 10 */ nrrdUnaryOpLog2, /* 11 */ nrrdUnaryOpLog10, /* 12 */ nrrdUnaryOpLog1p, /* 13 */ nrrdUnaryOpExpm1, /* 14 */ nrrdUnaryOpSqrt, /* 15 */ nrrdUnaryOpCbrt, /* 16 */ nrrdUnaryOpErf, /* 17 */ nrrdUnaryOpNerf, /* 18 */ nrrdUnaryOpCeil, /* 19 */ nrrdUnaryOpFloor, /* 20 */ nrrdUnaryOpRoundUp, /* 21 */ nrrdUnaryOpRoundDown, /* 22 */ nrrdUnaryOpAbs, /* 23 */ nrrdUnaryOpSgn, /* 24 */ nrrdUnaryOpExists, /* 25 */ nrrdUnaryOpRand, /* 26 */ nrrdUnaryOpNormalRand, /* 27 */ nrrdUnaryOpIf, /* 28 */ nrrdUnaryOpZero, /* 29 */ nrrdUnaryOpOne, /* 30 */ nrrdUnaryOpLast }; #define NRRD_UNARY_OP_MAX 30 /* ******** nrrdBinaryOp enum ** ** for binary operations on nrrds */ enum { nrrdBinaryOpUnknown, nrrdBinaryOpAdd, /* 1 */ nrrdBinaryOpSubtract, /* 2 */ nrrdBinaryOpMultiply, /* 3 */ nrrdBinaryOpDivide, /* 4 */ nrrdBinaryOpPow, /* 5 */ nrrdBinaryOpSgnPow, /* 6 */ nrrdBinaryOpFlippedSgnPow, /* 7 */ nrrdBinaryOpMod, /* 8 */ nrrdBinaryOpFmod, /* 9 */ nrrdBinaryOpAtan2, /* 10 */ nrrdBinaryOpMin, /* 11 */ nrrdBinaryOpMax, /* 12 */ nrrdBinaryOpLT, /* 13 */ nrrdBinaryOpLTE, /* 14 */ nrrdBinaryOpGT, /* 15 */ nrrdBinaryOpGTE, /* 16 */ nrrdBinaryOpCompare, /* 17 */ nrrdBinaryOpEqual, /* 18 */ nrrdBinaryOpNotEqual, /* 19 */ nrrdBinaryOpExists, /* 20 */ nrrdBinaryOpIf, /* 21 */ nrrdBinaryOpNormalRandScaleAdd, /* 22 */ nrrdBinaryOpRicianRand, /* 23 */ nrrdBinaryOpLast }; #define NRRD_BINARY_OP_MAX 23 /* ******** nrrdTernaryOp ** ** for ternary operations on nrrds */ enum { nrrdTernaryOpUnknown, nrrdTernaryOpAdd, /* 1 */ nrrdTernaryOpMultiply, /* 2 */ nrrdTernaryOpMin, /* 3 */ nrrdTernaryOpMinSmooth, /* 4 */ nrrdTernaryOpMax, /* 5 */ nrrdTernaryOpMaxSmooth, /* 6 */ nrrdTernaryOpLTSmooth, /* 7 */ nrrdTernaryOpGTSmooth, /* 8 */ nrrdTernaryOpClamp, /* 9 */ nrrdTernaryOpIfElse, /* 10 */ nrrdTernaryOpLerp, /* 11 */ nrrdTernaryOpExists, /* 12 */ nrrdTernaryOpInOpen, /* 13 */ nrrdTernaryOpInClosed, /* 14 */ nrrdTernaryOpGaussian, /* 15 */ nrrdTernaryOpRician, /* 16 */ nrrdTernaryOpLast }; #define NRRD_TERNARY_OP_MAX 16 /* ******** nrrdFFTWPlanRigor ** ** Different levels of rigor in FFTW planning */ enum { nrrdFFTWPlanRigorUnknown, nrrdFFTWPlanRigorEstimate, /* 1 */ nrrdFFTWPlanRigorMeasure, /* 2 */ nrrdFFTWPlanRigorPatient, /* 3 */ nrrdFFTWPlanRigorExhaustive, /* 4 */ nrrdFFTWPlanRigorLast }; #define NRRD_FFTW_PLAN_RIGOR_MAX 4 /* ******** nrrdResampleNonExistent ** ** different ways that nrrdResample should handle non-existent values */ enum { nrrdResampleNonExistentUnknown, nrrdResampleNonExistentNoop, /* 1 */ nrrdResampleNonExistentRenormalize, /* 2 */ nrrdResampleNonExistentWeight, /* 3 */ nrrdResampleNonExistentLast }; #define NRRD_RESAMPLE_NON_EXISTENT_MAX 3 /* ---- END non-NrrdIO */ #ifdef __cplusplus } #endif #endif /* NRRD_ENUMS_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/nrrd/filt.c0000664000175000017500000005733612165631065016466 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" int _nrrdCM_median(const float *hist, float half) { float sum = 0; const float *hpt; hpt = hist; while(sum < half) sum += *hpt++; return AIR_CAST(int, hpt - 1 - hist); } int _nrrdCM_mode(const float *hist, int bins) { float max; int i, mi; mi = -1; max = 0; for (i=0; i max) ) { max = hist[i]; mi = i; } } return mi; } #define INDEX(nin, range, lup, idxIn, bins, val) ( \ val = (lup)((nin)->data, (idxIn)), \ airIndex((range)->min, (val), (range)->max, bins) \ ) void _nrrdCM_printhist(const float *hist, int bins, const char *desc) { int i; printf("%s:\n", desc); for (i=0; itype]; num = nrrdElementNumber(nin); if (1 == wght) { /* uniform weighting-> can do sliding histogram optimization */ /* initialize histogram */ half = AIR_CAST(float, diam/2 + 1); memset(hist, 0, bins*sizeof(float)); for (X=0; Xmin, range->max, bins, idx); /* printf(" median idx = %d -> val = %g\n", idx, val); */ nrrdDInsert[nout->type](nout->data, X, val); /* probably update histogram for next iteration */ if (X < (int)num-radius-1) { hist[INDEX(nin, range, lup, X+radius+1, bins, val)]++; hist[INDEX(nin, range, lup, X-radius, bins, val)]--; } } } else { /* non-uniform weighting --> slow and stupid */ wt = _nrrdCM_wtAlloc(radius, wght); half = 0.5; for (X=radius; X<(int)num-radius; X++) { memset(hist, 0, bins*sizeof(float)); for (I=-radius; I<=radius; I++) { hist[INDEX(nin, range, lup, I+X, bins, val)] += wt[I+radius]; } idx = mode ? _nrrdCM_mode(hist, bins) : _nrrdCM_median(hist, half); val = NRRD_NODE_POS(range->min, range->max, bins, idx); nrrdDInsert[nout->type](nout->data, X, val); } free(wt); } } void _nrrdCheapMedian2D(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, int radius, float wght, int bins, int mode, float *hist) { /* static const char me[]="_nrrdCheapMedian2D"; */ int X, Y, I, J; int sx, sy, idx, diam; float half, *wt; double val, (*lup)(const void *, size_t); diam = 2*radius + 1; sx = AIR_CAST(unsigned int, nin->axis[0].size); sy = AIR_CAST(unsigned int, nin->axis[1].size); lup = nrrdDLookup[nin->type]; if (1 == wght) { /* uniform weighting-> can do sliding histogram optimization */ half = AIR_CAST(float, diam*diam/2 + 1); for (Y=radius; Ymin, range->max, bins, idx); nrrdDInsert[nout->type](nout->data, X + sx*Y, val); /* probably update histogram for next iteration */ if (X < sx-radius-1) { for (J=-radius; J<=radius; J++) { hist[INDEX(nin, range, lup, X+radius+1 + sx*(J+Y), bins, val)]++; hist[INDEX(nin, range, lup, X-radius + sx*(J+Y), bins, val)]--; } } } } } else { /* non-uniform weighting --> slow and stupid */ wt = _nrrdCM_wtAlloc(radius, wght); half = 0.5; for (Y=radius; Ymin, range->max, bins, idx); nrrdDInsert[nout->type](nout->data, X + sx*Y, val); } } free(wt); } } void _nrrdCheapMedian3D(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, int radius, float wght, int bins, int mode, float *hist) { static const char me[]="_nrrdCheapMedian3D"; char done[13]; int X, Y, Z, I, J, K; int sx, sy, sz, idx, diam; float half, *wt; double val, (*lup)(const void *, size_t); diam = 2*radius + 1; sx = AIR_CAST(int, nin->axis[0].size); sy = AIR_CAST(int, nin->axis[1].size); sz = AIR_CAST(int, nin->axis[2].size); lup = nrrdDLookup[nin->type]; fprintf(stderr, "%s: ... ", me); if (1 == wght) { /* uniform weighting-> can do sliding histogram optimization */ half = AIR_CAST(float, diam*diam*diam/2 + 1); fflush(stderr); for (Z=radius; Zmin, range->max, bins, idx); nrrdDInsert[nout->type](nout->data, X + sx*(Y + sy*Z), val); /* probably update histogram for next iteration */ if (X < sx-radius-1) { for (K=-radius; K<=radius; K++) { for (J=-radius; J<=radius; J++) { hist[INDEX(nin, range, lup, X+radius+1 + sx*(J+Y + sy*(K+Z)), bins, val)]++; hist[INDEX(nin, range, lup, X-radius + sx*(J+Y + sy*(K+Z)), bins, val)]--; } } } } } } } else { /* non-uniform weighting --> slow and stupid */ wt = _nrrdCM_wtAlloc(radius, wght); half = 0.5; for (Z=radius; Zmin, range->max, bins, idx); nrrdDInsert[nout->type](nout->data, X + sx*(Y + sy*Z), val); } } } free(wt); } fprintf(stderr, "\b\b\b\b\b\b done\n"); } /* ******** nrrdCheapMedian ** ** histogram-based median or mode filtering ** !mode: median filtering ** mode: mode filtering */ int nrrdCheapMedian(Nrrd *_nout, const Nrrd *_nin, int pad, int mode, unsigned int radius, float wght, unsigned int bins) { static const char me[]="nrrdCheapMedian", func[]="cmedian"; NrrdRange *range; float *hist; Nrrd *nout, *nin; airArray *mop; unsigned int minsize; if (!(_nin && _nout)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!(radius >= 1)) { biffAddf(NRRD, "%s: need radius >= 1 (got %d)", me, radius); return 1; } if (!(bins >= 1)) { biffAddf(NRRD, "%s: need bins >= 1 (got %d)", me, bins); return 1; } if (!(AIR_IN_CL(1, _nin->dim, 3))) { biffAddf(NRRD, "%s: sorry, can only handle dim 1, 2, 3 (not %d)", me, _nin->dim); return 1; } minsize = AIR_CAST(unsigned int, _nin->axis[0].size); if (_nin->dim > 1) { minsize = AIR_MIN(minsize, AIR_CAST(unsigned int, _nin->axis[1].size)); } if (_nin->dim > 2) { minsize = AIR_MIN(minsize, AIR_CAST(unsigned int, _nin->axis[2].size)); } if (!pad && minsize < 2*radius+1) { biffAddf(NRRD, "%s: minimum nrrd size (%d) smaller than filtering " "window size (%d) with radius %d; must enable padding", me, minsize, 2*radius+1, radius); return 1; } if (_nout == _nin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (nrrdTypeBlock == _nin->type) { biffAddf(NRRD, "%s: can't filter nrrd type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } mop = airMopNew(); /* set nin based on _nin */ airMopAdd(mop, nin=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (pad) { airMopAdd(mop, nout=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdSimplePad_va(nin, _nin, radius, nrrdBoundaryBleed)) { biffAddf(NRRD, "%s: trouble padding input", me); airMopError(mop); return 1; } } else { if (nrrdCopy(nin, _nin)) { biffAddf(NRRD, "%s: trouble copying input", me); airMopError(mop); return 1; } nout = _nout; } if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: failed to create initial copy of input", me); airMopError(mop); return 1; } range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeFalse); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); if (!(hist = (float*)calloc(bins, sizeof(float)))) { biffAddf(NRRD, "%s: couldn't allocate histogram (%d bins)", me, bins); airMopError(mop); return 1; } airMopAdd(mop, hist, airFree, airMopAlways); if (!AIR_EXISTS(wght)) { wght = 1.0; } switch (nin->dim) { case 1: _nrrdCheapMedian1D(nout, nin, range, radius, wght, bins, mode, hist); break; case 2: _nrrdCheapMedian2D(nout, nin, range, radius, wght, bins, mode, hist); break; case 3: _nrrdCheapMedian3D(nout, nin, range, radius, wght, bins, mode, hist); break; default: biffAddf(NRRD, "%s: sorry, %d-dimensional median unimplemented", me, nin->dim); airMopError(mop); return 1; } nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE); if (nrrdContentSet_va(nout, func, nin, "%d,%d,%g,%d", mode, radius, wght, bins)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } /* basic info handled by nrrdCopy above */ /* set _nout based on nout */ if (pad) { if (nrrdSimpleCrop(_nout, nout, radius)) { biffAddf(NRRD, "%s: trouble cropping output", me); airMopError(mop); return 1; } } else { /* we've already set output in _nout == nout */ } airMopOkay(mop); return 0; } /* ** returns intersection of parabolas c(x) = spc^2 (x - xi)^2 + yi */ static double intx(double xx0, double yy0, double xx1, double yy1, double spc) { double ss; ss = spc*spc; return (yy1/ss + xx1*xx1 - (yy0/ss + xx0*xx0))/(2*(xx1 - xx0)); } /* ** squared-distance transform of single scanline ** ** based on code published with: ** Distance Transforms of Sampled Functions ** Pedro F. Felzenszwalb and Daniel P. Huttenlocher ** Cornell Computing and Information Science TR2004-1963 ** ** dd: output (pre-allocated for "len") ** ff: input function (pre-allocated for "len") ** zz: buffer (pre-allocated for "len"+1) for locations of ** boundaries between parabolas ** vv: buffer (pre-allocated for "len") for locations of ** parabolas in lower envelope ** ** The major modification from the published method is the inclusion ** of the "spc" parameter that gives the inter-sample spacing, so that ** the multi-dimensional version can work on non-isotropic samples. ** ** FLT_MIN and FLT_MAX are used to be consistent with the ** initialization in nrrdDistanceL2(), which uses FLT_MAX ** to be compatible with the case of using floats */ static void distanceL2Sqrd1D(double *dd, const double *ff, double *zz, unsigned int *vv, size_t len, double spc) { size_t kk, qq; if (!( dd && ff && zz && vv && len > 0 )) { /* error */ return; } kk = 0; vv[0] = 0; zz[0] = -FLT_MAX; zz[1] = +FLT_MAX; for (qq=1; qqtype || nrrdTypeDouble == ndist->type )) { /* MWC: This error condition was/is being ignored. */ /* biffAddf(NRRD, "%s: sorry, can only process type %s or %s (not %s)", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeDouble), airEnumStr(nrrdType, ndist->type)); */ } spcSomeExist = AIR_FALSE; spcSomeNonExist = AIR_FALSE; for (di=0; didim; di++) { nrrdSpacingCalculate(ndist, di, spc + di, vector); spcSomeExist |= AIR_EXISTS(spc[di]); spcSomeNonExist |= !AIR_EXISTS(spc[di]); } if (spcSomeExist && spcSomeNonExist) { biffAddf(NRRD, "%s: axis spacings must all exist or all non-exist", me); return 1; } if (!spcSomeExist) { for (di=0; didim; di++) { spc[di] = 1.0; } } *spcMean = 0; for (di=0; didim; di++) { *spcMean += spc[di]; } *spcMean /= ndist->dim; sizeMax = 0; for (di=0; didim; di++) { sizeMax = AIR_MAX(sizeMax, ndist->axis[di].size); } /* create mop and allocate tmp buffers */ mop = airMopNew(); ntmpA = nrrdNew(); airMopAdd(mop, ntmpA, (airMopper)nrrdNuke, airMopAlways); if (ndist->dim > 2) { ntmpB = nrrdNew(); airMopAdd(mop, ntmpB, (airMopper)nrrdNuke, airMopAlways); } else { ntmpB = NULL; } if (nrrdCopy(ntmpA, ndist) || (ndist->dim > 2 && nrrdCopy(ntmpB, ndist))) { biffAddf(NRRD, "%s: couldn't allocate image buffers", me); } dd = AIR_CAST(double *, calloc(sizeMax, sizeof(double))); ff = AIR_CAST(double *, calloc(sizeMax, sizeof(double))); zz = AIR_CAST(double *, calloc(sizeMax+1, sizeof(double))); vv = AIR_CAST(unsigned int *, calloc(sizeMax, sizeof(unsigned int))); airMopAdd(mop, dd, airFree, airMopAlways); airMopAdd(mop, ff, airFree, airMopAlways); airMopAdd(mop, zz, airFree, airMopAlways); airMopAdd(mop, vv, airFree, airMopAlways); if (!( dd && ff && zz && vv )) { /* MWC: This error condition was/is being ignored. */ /* biffAddf(NRRD, "%s: couldn't allocate scanline buffers", me); */ } /* set up array of buffers */ npass[0] = ndist; for (di=1; didim; di++) { npass[di] = (di % 2) ? ntmpA : ntmpB; } npass[ndist->dim] = ndist; /* run the multiple passes */ /* what makes the indexing here so simple is that by assuming that we're processing every axis, the input to any given pass can be logically considered a 2-D image (a list of scanlines), where the second axis is the merge of all input axes but the first. With the rotational shuffle of axes through passes, the initial axis and the set of other axes swap places, so its like the 2-D image is being transposed. NOTE: the Nrrds that were allocated as buffers are really being mis-used, in that the axis sizes and raster ordering of what we're storing there is *not* the same as told by axis[].size */ lup = nrrdDLookup[ndist->type]; ins = nrrdDInsert[ndist->type]; for (di=0; didim; di++) { size_t lineIdx, lineNum, valIdx, valNum; valNum = ndist->axis[di].size; lineNum = nrrdElementNumber(ndist)/valNum; for (lineIdx=0; lineIdxdata, valIdx + valNum*lineIdx); } /* do the transform */ distanceL2Sqrd1D(dd, ff, zz, vv, valNum, spc[di]); /* write dd to output scanline */ for (valIdx=0; valIdxdata, lineIdx + lineNum*valIdx, dd[valIdx]); } } } airMopOkay(mop); return 0; } /* ** helper function for distance transforms, is called by things that want to do ** specific kinds of transforms. */ static int _distanceBase(Nrrd *nout, const Nrrd *nin, int typeOut, const int *axisDo, double thresh, double bias, int insideHigher) { static const char me[]="_distanceBase"; size_t ii, nn; double (*lup)(const void *, size_t), (*ins)(void *, size_t, double); double spcMean; if (!( nout && nin )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(NRRD, "%s: need scalar type for distance transform (not %s)", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!( nrrdTypeDouble == typeOut || nrrdTypeFloat == typeOut )) { biffAddf(NRRD, "%s: sorry, can only transform to type %s or %s " "(not %s)", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeDouble), airEnumStr(nrrdType, typeOut)); return 1; } if (axisDo) { biffAddf(NRRD, "%s: sorry, selective axis transform not implemented", me); return 1; } if (!AIR_EXISTS(thresh)) { biffAddf(NRRD, "%s: threshold (%g) doesn't exist", me, thresh); return 1; } if (nrrdConvert(nout, nin, typeOut)) { biffAddf(NRRD, "%s: couldn't allocate output", me); return 1; } lup = nrrdDLookup[nout->type]; ins = nrrdDInsert[nout->type]; nn = nrrdElementNumber(nout); for (ii=0; iidata, ii); if (insideHigher) { bb = bias*(val - thresh); ins(nout->data, ii, val > thresh ? bb*bb : FLT_MAX); } else { bb = bias*(thresh - val); ins(nout->data, ii, val <= thresh ? bb*bb : FLT_MAX); } } if (distanceL2Sqrd(nout, &spcMean)) { biffAddf(NRRD, "%s: trouble doing transform", me); return 1; } for (ii=0; iidata, ii)); /* here's where the distance is tweaked downwards by half a sample width */ ins(nout->data, ii, AIR_MAX(0, val - spcMean/2)); } return 0; } /* ******** nrrdDistanceL2 ** ** computes euclidean (L2) distance transform of input image, after ** thresholding at "thresh". ** ** NOTE: the output of this is slightly offset from what one might ** expect; decreased by half of the average (over all axes) sample ** spacing. The reason for this is so that when the transform is ** applied to the inverted image and negated, to create a full ** signed distance map, the transition from interior to exterior ** distance values is smooth. Without this trick, there is a ** small little plateau at the transition. */ int nrrdDistanceL2(Nrrd *nout, const Nrrd *nin, int typeOut, const int *axisDo, double thresh, int insideHigher) { static const char me[]="nrrdDistanceL2"; if (_distanceBase(nout, nin, typeOut, axisDo, thresh, 0, insideHigher)) { biffAddf(NRRD, "%s: trouble doing distance transform", me); return 1; } return 0; } int nrrdDistanceL2Biased(Nrrd *nout, const Nrrd *nin, int typeOut, const int *axisDo, double thresh, double bias, int insideHigher) { static const char me[]="nrrdDistanceL2Biased"; if (_distanceBase(nout, nin, typeOut, axisDo, thresh, bias, insideHigher)) { biffAddf(NRRD, "%s: trouble doing distance transform", me); return 1; } return 0; } int nrrdDistanceL2Signed(Nrrd *nout, const Nrrd *nin, int typeOut, const int *axisDo, double thresh, int insideHigher) { static const char me[]="nrrdDistanceL2Signed"; airArray *mop; Nrrd *ninv; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); ninv = nrrdNew(); airMopAdd(mop, ninv, (airMopper)nrrdNuke, airMopAlways); if (nrrdDistanceL2(nout, nin, typeOut, axisDo, thresh, insideHigher) || nrrdDistanceL2(ninv, nin, typeOut, axisDo, thresh, !insideHigher) || nrrdArithUnaryOp(ninv, nrrdUnaryOpNegative, ninv) || nrrdArithBinaryOp(nout, nrrdBinaryOpAdd, nout, ninv)) { biffAddf(NRRD, "%s: trouble doing or combining transforms", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/nrrd/convertNrrd.c0000664000175000017500000002101612173672004020015 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ** making these typedefs here allows us to use one token for both ** constructing function names, and for specifying argument types */ typedef signed char CH; typedef unsigned char UC; typedef signed short SH; typedef unsigned short US; /* Microsoft apparently uses 'IN' as a keyword, so we changed 'IN' to 'JN'. */ typedef signed int JN; typedef unsigned int UI; typedef airLLong LL; /* ui64 to double conversion is not implemented, sorry */ #if _MSC_VER < 1300 typedef airLLong UL; #else typedef airULLong UL; #endif typedef float FL; typedef double DB; typedef size_t IT; /* typedef long double LD; */ /* ** I don't think that I can get out of defining this macro twice, ** because of the rules of C preprocessor macro expansion. If ** you can figure out a way to not use two identical macros, then ** email me (glk@uchicago.edu) and I'll send you money for dinner. ** ** >>> MAP1 and MAP2 need to be identical <<< */ #define MAP1(F, A) \ F(A, CH) \ F(A, UC) \ F(A, SH) \ F(A, US) \ F(A, JN) \ F(A, UI) \ F(A, LL) \ F(A, UL) \ F(A, FL) \ F(A, DB) /* F(A, LD) */ #define MAP2(F, A) \ F(A, CH) \ F(A, UC) \ F(A, SH) \ F(A, US) \ F(A, JN) \ F(A, UI) \ F(A, LL) \ F(A, UL) \ F(A, FL) \ F(A, DB) /* F(A, LD) */ /* ** _nrrdConv() ** ** given two arrays, a and b, of different types (Ta and Tb) but equal ** size N, _nrrdConvTaTb(a, b, N) will copy all the values from b into ** a, thereby effecting the same type-conversion as one gets with a ** cast. See K+R Appendix A6 (pg. 197) for the details of what that ** entails. There are plenty of situations where the results are ** "undefined" (assigning -23 to an unsigned char); the point here is ** simply to make available on arrays all the same behavior you can ** get from scalars. */ #define CONV_DEF(TA, TB) \ static void \ _nrrdConv##TA##TB(TA *a, const TB *b, IT N) { \ size_t ii; \ for (ii=0; ii() ** ** same as _nrrdConv(), but with clamping to the representable ** range of values of the output type, using a double as intermediate ** storage type HEY WHICH MEANS THAT LONG LONG (BOTH SIGNED AND UNSIGNED) ** may suffer loss of data!!! */ #define CLCV_DEF(TA, TB) \ static void \ _nrrdClCv##TA##TB(TA *a, const TB *b, IT N) { \ size_t ii; \ for (ii=0; ii() ** ** like _nrrdClCv() and _nrrdConv(), but with the ** ability to control if there is rounding and/or clamping. As above, ** there may be loss of precision with long long input. */ #define CCRD_DEF(TA, TB) \ static void \ _nrrdCcrd##TA##TB(TA *a, const TB *b, IT N, \ int doClamp, int roundd) { \ size_t ii; \ for (ii=0; ii 0 \ ? floor(ccrdTmp + 0.5) \ : (roundd < 0 \ ? ceil(ccrdTmp - 0.5) \ : ccrdTmp)); \ ccrdTmp = (doClamp ? _nrrdDClamp##TA(ccrdTmp) : ccrdTmp); \ a[ii] = AIR_CAST(TA, ccrdTmp); \ } \ } /* ** These makes the definition of later arrays shorter */ typedef void (*CF)(void *, const void *, IT); typedef void (*CN)(void *, const void *, IT, int, int); /* ** the individual converter's appearance in the array initialization, ** using the cast to the typedefs above */ #define CONV_LIST(TA, TB) (CF)_nrrdConv##TA##TB, #define CLCV_LIST(TA, TB) (CF)_nrrdClCv##TA##TB, #define CCRD_LIST(TA, TB) (CN)_nrrdCcrd##TA##TB, /* ** the brace-delimited list of all converters _to_ type TA */ #define CONVTO_LIST(_dummy_, TA) {NULL, MAP2(CONV_LIST, TA) NULL}, #define CLCVTO_LIST(_dummy_, TA) {NULL, MAP2(CLCV_LIST, TA) NULL}, #define CCRDTO_LIST(_dummy_, TA) {NULL, MAP2(CCRD_LIST, TA) NULL}, /* ** This is where the actual emitted code starts ... */ /* ** the clamping functions were moved here from accessors.c in order ** to create the combined clamp-and-convert functions */ /* ******** nrrdFClamp ** ** for integral types, clamps a given float to the range representable ** by that type; for floating point types we just return ** the given number, since every float must fit in a double. */ static float _nrrdFClampCH(FL v) { return AIR_CLAMP(SCHAR_MIN, v, SCHAR_MAX);} static float _nrrdFClampUC(FL v) { return AIR_CLAMP(0, v, UCHAR_MAX);} static float _nrrdFClampSH(FL v) { return AIR_CLAMP(SHRT_MIN, v, SHRT_MAX);} static float _nrrdFClampUS(FL v) { return AIR_CLAMP(0, v, USHRT_MAX);} static float _nrrdFClampJN(FL v) { return AIR_CLAMP(INT_MIN, v, INT_MAX);} static float _nrrdFClampUI(FL v) { return AIR_CLAMP(0, v, UINT_MAX);} static float _nrrdFClampLL(FL v) { return AIR_CLAMP(NRRD_LLONG_MIN, v, NRRD_LLONG_MAX);} static float _nrrdFClampUL(FL v) { return AIR_CLAMP(0, v, NRRD_ULLONG_MAX);} static float _nrrdFClampFL(FL v) { return v; } static float _nrrdFClampDB(FL v) { return v; } float (* nrrdFClamp[NRRD_TYPE_MAX+1])(FL) = { NULL, _nrrdFClampCH, _nrrdFClampUC, _nrrdFClampSH, _nrrdFClampUS, _nrrdFClampJN, _nrrdFClampUI, _nrrdFClampLL, _nrrdFClampUL, _nrrdFClampFL, _nrrdFClampDB, NULL}; /* ******** nrrdDClamp ** ** same as nrrdDClamp, but for doubles. One change: in the case of ** floats, doubles are clamped to the range -FLT_MAX to FLT_MAX. */ static double _nrrdDClampCH(DB v) { return AIR_CLAMP(SCHAR_MIN, v, SCHAR_MAX);} static double _nrrdDClampUC(DB v) { return AIR_CLAMP(0, v, UCHAR_MAX);} static double _nrrdDClampSH(DB v) { return AIR_CLAMP(SHRT_MIN, v, SHRT_MAX);} static double _nrrdDClampUS(DB v) { return AIR_CLAMP(0, v, USHRT_MAX);} static double _nrrdDClampJN(DB v) { return AIR_CLAMP(INT_MIN, v, INT_MAX);} static double _nrrdDClampUI(DB v) { return AIR_CLAMP(0, v, UINT_MAX);} static double _nrrdDClampLL(DB v) { return AIR_CLAMP(NRRD_LLONG_MIN, v, NRRD_LLONG_MAX);} static double _nrrdDClampUL(DB v) { return AIR_CLAMP(0, v, NRRD_ULLONG_MAX);} static double _nrrdDClampFL(DB v) { return AIR_CLAMP(-FLT_MAX, v, FLT_MAX); } static double _nrrdDClampDB(DB v) { return v; } double (* nrrdDClamp[NRRD_TYPE_MAX+1])(DB) = { NULL, _nrrdDClampCH, _nrrdDClampUC, _nrrdDClampSH, _nrrdDClampUS, _nrrdDClampJN, _nrrdDClampUI, _nrrdDClampLL, _nrrdDClampUL, _nrrdDClampFL, _nrrdDClampDB, NULL}; /* ** Define all the converters. */ MAP1(MAP2, CONV_DEF) MAP1(MAP2, CLCV_DEF) MAP1(MAP2, CCRD_DEF) /* ** Initialize the converter arrays. ** ** Each definition generates one incredibly long line of text, which ** hopefully will not break a poor compiler with limitations on ** line-length... */ CF _nrrdConv[NRRD_TYPE_MAX+1][NRRD_TYPE_MAX+1] = { {NULL}, MAP1(CONVTO_LIST, _dummy_) {NULL} }; CF _nrrdClampConv[NRRD_TYPE_MAX+1][NRRD_TYPE_MAX+1] = { {NULL}, MAP1(CLCVTO_LIST, _dummy_) {NULL} }; CN _nrrdCastClampRound[NRRD_TYPE_MAX+1][NRRD_TYPE_MAX+1] = { {NULL}, MAP1(CCRDTO_LIST, _dummy_) {NULL} }; teem-1.11.0~svn6057/src/nrrd/simple.c0000664000175000017500000014102612165631065017007 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" #include /* The "/ *Teem:" (without space) comments in here are an experiment */ const char * nrrdBiffKey = "nrrd"; /* ******** nrrdSpaceDimension ** ** returns expected dimension of given space (from nrrdSpace* enum), or, ** 0 if there is no expected dimension. ** ** The expected behavior here is to return 0 for nrrdSpaceUnknown, because ** that is the right answer, not because its an error of any kind. */ unsigned int nrrdSpaceDimension(int space) { static const char me[]="nrrdSpaceDimension"; unsigned int ret; if (!( AIR_IN_OP(nrrdSpaceUnknown, space, nrrdSpaceLast) )) { /* they gave us invalid or unknown space */ return 0; } switch (space) { case nrrdSpaceRightAnteriorSuperior: case nrrdSpaceLeftAnteriorSuperior: case nrrdSpaceLeftPosteriorSuperior: case nrrdSpaceScannerXYZ: case nrrdSpace3DRightHanded: case nrrdSpace3DLeftHanded: ret = 3; break; case nrrdSpaceRightAnteriorSuperiorTime: case nrrdSpaceLeftAnteriorSuperiorTime: case nrrdSpaceLeftPosteriorSuperiorTime: case nrrdSpaceScannerXYZTime: case nrrdSpace3DRightHandedTime: case nrrdSpace3DLeftHandedTime: ret = 4; break; default: fprintf(stderr, "%s: PANIC: nrrdSpace %d not implemented!\n", me, space); ret = UINT_MAX; /* exit(1); */ break; } return ret; } /* ******** nrrdSpaceSet ** ** What to use to set space, when a value from nrrdSpace enum is known, ** or, to nullify all space-related information when passed nrrdSpaceUnknown */ int nrrdSpaceSet(Nrrd *nrrd, int space) { static const char me[]="nrrdSpaceSet"; unsigned axi, saxi; if (!nrrd) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdSpaceUnknown == space) { nrrd->space = nrrdSpaceUnknown; nrrd->spaceDim = 0; for (axi=0; axiaxis[axi].spaceDirection); } for (saxi=0; saxispaceUnits[saxi]); nrrd->spaceUnits[saxi] = NULL; } nrrdSpaceVecSetNaN(nrrd->spaceOrigin); } else { if (airEnumValCheck(nrrdSpace, space)) { biffAddf(NRRD, "%s: given space (%d) not valid", me, space); return 1; } nrrd->space = space; nrrd->spaceDim = nrrdSpaceDimension(space); } return 0; } /* ******** nrrdSpaceDimensionSet ** ** What to use to set space, based on spaceDim alone (nrrd->space set to ** nrrdSpaceUnknown) */ int nrrdSpaceDimensionSet(Nrrd *nrrd, unsigned int spaceDim) { static const char me[]="nrrdSpaceDimensionSet"; if (!nrrd) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( spaceDim <= NRRD_SPACE_DIM_MAX )) { biffAddf(NRRD, "%s: given spaceDim (%u) not valid", me, spaceDim); return 1; } nrrd->space = nrrdSpaceUnknown; nrrd->spaceDim = spaceDim; return 0; } /* ******** nrrdSpaceOriginGet ** ** retrieves the spaceOrigin from given nrrd, and returns spaceDim ** Indices 0 through spaceDim-1 are set in given vector[] to coords ** of space origin, and all further indices are set to NaN. That is, ** this really does write to all NRRD_SPACE_DIM_MAX elements of vector[] */ unsigned int nrrdSpaceOriginGet(const Nrrd *nrrd, double vector[NRRD_SPACE_DIM_MAX]) { unsigned int sdi, ret; if (nrrd && vector) { for (sdi=0; sdispaceDim; sdi++) { vector[sdi] = nrrd->spaceOrigin[sdi]; } for (sdi=nrrd->spaceDim; sdispaceDim; } else { ret = 0; } return ret; } /* ******** nrrdSpaceOriginSet ** ** convenience function for setting spaceOrigin. ** Note: space (or spaceDim) must be already set. ** The given "vector" is only read for spaceDim elements ** ** returns 1 if there were problems, 0 otherwise */ int nrrdSpaceOriginSet(Nrrd *nrrd, const double *vector) { static const char me[]="nrrdSpaceOriginSet"; unsigned int sdi; if (!( nrrd && vector )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( 0 < nrrd->spaceDim && nrrd->spaceDim <= NRRD_SPACE_DIM_MAX )) { biffAddf(NRRD, "%s: set spaceDim %d not valid", me, nrrd->spaceDim); return 1; } for (sdi=0; sdispaceDim; sdi++) { nrrd->spaceOrigin[sdi] = vector[sdi]; } for (sdi=nrrd->spaceDim; sdispaceOrigin[sdi] = AIR_NAN; } return 0; } /* ******** nrrdOriginCalculate ** ** makes an effort to calculate something like an "origin" (as in ** nrrd->spaceOrigin) from the per-axis min, max, or spacing, when ** there is no real space information. Like the spaceOrigin, this ** location is supposed to be THE CENTER of the first sample. To ** avoid making assumptions about the nrrd or the caller, a default ** sample centering (defaultCenter) has to be provided (use either ** nrrdCenterNode or nrrdCenterCell). The axes that are used ** for the origin calculation have to be given explicitly- but they ** are likely the return of nrrdDomainAxesGet ** ** The computed origin is put into the given vector (origin). The return ** value takes on values from the nrrdOriginStatus* enum: ** ** nrrdOriginStatusUnknown: invalid arguments (e.g. NULL pointer, or ** axis values out of range) ** ** nrrdOriginStatusDirection: the chosen axes have spaceDirection set, ** which means caller should instead be using ** nrrdSpaceOriginGet ** ** nrrdOriginStatusNoMin: can't compute "origin" without axis->min ** ** nrrdOriginStatusNoMaxOrSpacing: can't compute origin without (axis->min ** and) either axis->max or axis->spacing ** ** nrrdOriginStatusOkay: all is well */ int nrrdOriginCalculate(const Nrrd *nrrd, unsigned int *axisIdx, unsigned int axisIdxNum, int defaultCenter, double *origin) { const NrrdAxisInfo *axis[NRRD_SPACE_DIM_MAX]; int center, okay, gotSpace, gotMin, gotMaxOrSpacing; unsigned int ai; double min, spacing; #define ERROR \ if (origin) { \ for (ai=0; aidim; } if (!okay) { ERROR; return nrrdOriginStatusUnknown; } /* learn axisInfo pointers */ for (ai=0; aiaxis + axisIdx[ai]; } gotSpace = AIR_FALSE; for (ai=0; aispaceDirection[0]); } if (nrrd->spaceDim > 0 && gotSpace) { ERROR; return nrrdOriginStatusDirection; } gotMin = AIR_TRUE; for (ai=0; aimin); } if (!gotMin) { ERROR; return nrrdOriginStatusNoMin; } gotMaxOrSpacing = AIR_TRUE; for (ai=0; aimax) || AIR_EXISTS(axis[ai]->spacing)); } if (!gotMaxOrSpacing) { ERROR; return nrrdOriginStatusNoMaxOrSpacing; } for (ai=0; aisize; min = axis[ai]->min; center = (nrrdCenterUnknown != axis[ai]->center ? axis[ai]->center : defaultCenter); denom = AIR_CAST(double, nrrdCenterCell == center ? size : size-1); spacing = (AIR_EXISTS(axis[ai]->spacing) ? axis[ai]->spacing : (axis[ai]->max - min)/denom); origin[ai] = min + (nrrdCenterCell == center ? spacing/2 : 0); } return nrrdOriginStatusOkay; } void nrrdSpaceVecCopy(double dst[NRRD_SPACE_DIM_MAX], const double src[NRRD_SPACE_DIM_MAX]) { unsigned int ii; for (ii=0; iispaceOrigin, nin->spaceOrigin); ** for (ai=0; aidim; ai++) { ** _nrrdSpaceVecScaleAdd2(nout->spaceOrigin, ** 1.0, nout->spaceOrigin, ** min[ai], nin->axis[ai].spaceDirection); ** } ** ** but the problem with this is that non-spatial axes end up clobbering ** the existance of otherwise existing spaceOrigin and spaceDirections. ** It was decided, however, that this logic should be outside the ** arithmetic functions below, not inside. NOTE: the old functionality ** is stuck in ITK 2.2, via NrrdIO. */ void nrrdSpaceVecScaleAdd2(double sum[NRRD_SPACE_DIM_MAX], double sclA, const double vecA[NRRD_SPACE_DIM_MAX], double sclB, const double vecB[NRRD_SPACE_DIM_MAX]) { unsigned int ii; for (ii=0; iicontent) ? airStrdup(nin->content) : airStrdup(nrrdStateUnknownContent)); if (!ret) { fprintf(stderr, "%s: PANIC: content strdup failed!\n", me); return NULL; } return ret; } int _nrrdContentSet_nva(Nrrd *nout, const char *func, char *content, const char *format, va_list arg) { static const char me[]="_nrrdContentSet_nva"; char *buff; if (nrrdStateDisableContent) { /* we kill content always */ nout->content = (char *)airFree(nout->content); return 0; } buff = (char *)malloc(128*AIR_STRLEN_HUGE); if (!buff) { biffAddf(NRRD, "%s: couln't alloc buffer!", me); return 1; } nout->content = (char *)airFree(nout->content); /* we are currently praying that this won't overflow the "buff" array */ /* HEY: replace with vsnprintf or whatever when its available */ vsprintf(buff, format, arg); nout->content = (char *)calloc(strlen("(,)") + airStrlen(func) + 1 /* '(' */ + airStrlen(content) + 1 /* ',' */ + airStrlen(buff) + 1 /* ')' */ + 1, sizeof(char)); /* '\0' */ if (!nout->content) { biffAddf(NRRD, "%s: couln't alloc output content!", me); airFree(buff); return 1; } sprintf(nout->content, "%s(%s%s%s)", func, content, airStrlen(buff) ? "," : "", buff); airFree(buff); /* no NULL assignment, else compile warnings */ return 0; } int _nrrdContentSet_va(Nrrd *nout, const char *func, char *content, const char *format, ...) { static const char me[]="_nrrdContentSet_va"; va_list ap; va_start(ap, format); if (_nrrdContentSet_nva(nout, func, content, format, ap)) { biffAddf(NRRD, "%s:", me); free(content); return 1; } va_end(ap); /* free(content); */ return 0; } /* ******** nrrdContentSet ** ** Kind of like sprintf, but for the content string of the nrrd. ** ** Whether or not we write a new content for an old nrrd ("nin") with ** NULL content is decided here, according to ** nrrdStateAlwaysSetContent. ** ** Does the string allocation and some attempts at error detection. ** Does allow nout==nin, which requires some care. */ int nrrdContentSet_va(Nrrd *nout, const char *func, const Nrrd *nin, const char *format, ...) { static const char me[]="nrrdContentSet_va"; va_list ap; char *content; if (!(nout && func && nin && format)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdStateDisableContent) { /* we kill content always */ nout->content = (char *)airFree(nout->content); return 0; } if (!nin->content && !nrrdStateAlwaysSetContent) { /* there's no input content, and we're not supposed to invent any content, so after freeing nout's content we're done */ nout->content = (char *)airFree(nout->content); return 0; } /* we copy the input nrrd content first, before blowing away the output content, in case nout == nin */ content = _nrrdContentGet(nin); va_start(ap, format); if (_nrrdContentSet_nva(nout, func, content, format, ap)) { biffAddf(NRRD, "%s:", me); va_end(ap); free(content); return 1; } va_end(ap); free(content); return 0; } /* ******** nrrdDescribe ** ** writes verbose description of nrrd to given file */ void nrrdDescribe(FILE *file, const Nrrd *nrrd) { unsigned int ai; char stmp[AIR_STRLEN_SMALL]; if (file && nrrd) { fprintf(file, "Nrrd at 0x%p:\n", AIR_CVOIDP(nrrd)); fprintf(file, "Data at 0x%p is %s elements of type %s.\n", nrrd->data, airSprintSize_t(stmp, nrrdElementNumber(nrrd)), airEnumStr(nrrdType, nrrd->type)); if (nrrdTypeBlock == nrrd->type) { fprintf(file, "The blocks have size %s\n", airSprintSize_t(stmp, nrrd->blockSize)); } if (airStrlen(nrrd->content)) { fprintf(file, "Content = \"%s\"\n", nrrd->content); } fprintf(file, "%d-dimensional array, with axes:\n", nrrd->dim); for (ai=0; aidim; ai++) { if (airStrlen(nrrd->axis[ai].label)) { fprintf(file, "%d: (\"%s\") ", ai, nrrd->axis[ai].label); } else { fprintf(file, "%d: ", ai); } fprintf(file, "%s-centered, size=%s, ", airEnumStr(nrrdCenter, nrrd->axis[ai].center), airSprintSize_t(stmp, nrrd->axis[ai].size)); airSinglePrintf(file, NULL, "spacing=%lg, \n", nrrd->axis[ai].spacing); airSinglePrintf(file, NULL, "thickness=%lg, \n", nrrd->axis[ai].thickness); airSinglePrintf(file, NULL, " axis(Min,Max) = (%lg,", nrrd->axis[ai].min); airSinglePrintf(file, NULL, "%lg)\n", nrrd->axis[ai].max); if (airStrlen(nrrd->axis[ai].units)) { fprintf(file, "units=%s, \n", nrrd->axis[ai].units); } } /* airSinglePrintf(file, NULL, "The min, max values are %lg", nrrd->min); airSinglePrintf(file, NULL, ", %lg\n", nrrd->max); */ airSinglePrintf(file, NULL, "The old min, old max values are %lg", nrrd->oldMin); airSinglePrintf(file, NULL, ", %lg\n", nrrd->oldMax); /* fprintf(file, "hasNonExist = %d\n", nrrd->hasNonExist); */ if (nrrd->cmtArr->len) { fprintf(file, "Comments:\n"); for (ai=0; aicmtArr->len; ai++) { fprintf(file, "%s\n", nrrd->cmt[ai]); } } fprintf(file, "\n"); } } int nrrdSpaceVecExists(unsigned int sdim, double vec[NRRD_SPACE_DIM_MAX]) { int exists; unsigned int ii; exists = AIR_EXISTS(vec[0]); for (ii=1; iispace || !airEnumValCheck(nrrdSpace, nrrd->space) )) { biffMaybeAddf(useBiff, NRRD, "%s: space %d invalid", me, nrrd->space); return 1; } if (!( nrrd->spaceDim <= NRRD_SPACE_DIM_MAX )) { biffMaybeAddf(useBiff, NRRD, "%s: space dimension %d is outside " "valid range [0,NRRD_SPACE_DIM_MAX] = [0,%d]", me, nrrd->dim, NRRD_SPACE_DIM_MAX); return 1; } if (nrrd->spaceDim) { if (nrrd->space) { if (nrrdSpaceDimension(nrrd->space) != nrrd->spaceDim) { biffMaybeAddf(useBiff, NRRD, "%s: space %s has dimension %d but spaceDim is %d", me, airEnumStr(nrrdSpace, nrrd->space), nrrdSpaceDimension(nrrd->space), nrrd->spaceDim); return 1; } } /* check that all coeffs of spaceOrigin have consistent existance */ exists = AIR_EXISTS(nrrd->spaceOrigin[0]); for (ii=0; iispaceDim; ii++) { if (exists ^ AIR_EXISTS(nrrd->spaceOrigin[ii])) { biffMaybeAddf(useBiff, NRRD, "%s: existance of space origin coefficients must " "be consistent (val[0] not like val[%d])", me, ii); return 1; } } /* check that all coeffs of measurementFrame have consistent existance */ exists = AIR_EXISTS(nrrd->measurementFrame[0][0]); for (dd=0; ddspaceDim; dd++) { for (ii=0; iispaceDim; ii++) { if (exists ^ AIR_EXISTS(nrrd->measurementFrame[dd][ii])) { biffMaybeAddf(useBiff, NRRD, "%s: existance of measurement frame coefficients " "must be consistent: [col][row] [%d][%d] not " "like [0][0])", me, dd, ii); return 1; } } } /* check on space directions */ for (dd=0; dddim; dd++) { exists = AIR_EXISTS(nrrd->axis[dd].spaceDirection[0]); for (ii=1; iispaceDim; ii++) { if (exists ^ AIR_EXISTS(nrrd->axis[dd].spaceDirection[ii])) { biffMaybeAddf(useBiff, NRRD, "%s: existance of space direction %d coefficients " "must be consistent (val[0] not like val[%d])", me, dd, ii); return 1; } } if (exists) { if (AIR_EXISTS(nrrd->axis[dd].min) || AIR_EXISTS(nrrd->axis[dd].max) || AIR_EXISTS(nrrd->axis[dd].spacing) || !!airStrlen(nrrd->axis[dd].units)) { biffMaybeAddf(useBiff, NRRD, "%s: axis[%d] has a direction vector, and so can't " "have min, max, spacing, or units set", me, dd); return 1; } } } } else { /* else there's not supposed to be anything in "space" */ if (nrrd->space) { biffMaybeAddf(useBiff, NRRD, "%s: space %s can't be set with spaceDim %d", me, airEnumStr(nrrdSpace, nrrd->space), nrrd->spaceDim); return 1; } /* -------- */ exists = AIR_FALSE; for (dd=0; ddspaceUnits[dd]); } if (exists) { biffMaybeAddf(useBiff, NRRD, "%s: spaceDim is 0, but space units is set", me); return 1; } /* -------- */ exists = AIR_FALSE; for (dd=0; ddspaceOrigin[dd]); } if (exists) { biffMaybeAddf(useBiff, NRRD, "%s: spaceDim is 0, but space origin is set", me); return 1; } /* -------- */ exists = AIR_FALSE; for (dd=0; ddaxis[ii].spaceDirection[dd]); } } if (exists) { biffMaybeAddf(useBiff, NRRD, "%s: spaceDim is 0, but space directions are set", me); return 1; } } return 0; } /* --------------------- per-field checks ---------------- ** ** Strictly speacking, these checks only apply to the nrrd itself, not ** to a potentially incomplete nrrd in the process of being read, so ** the NrrdIoState stuff is not an issue. This limits the utility of ** these to the field parsers for handling the more complex state ** involved in parsing some of the NRRD fields (like units). ** ** return 0 if it is valid, and 1 if there is an error */ static int _nrrdFieldCheck_noop(const Nrrd *nrrd, int useBiff) { AIR_UNUSED(nrrd); AIR_UNUSED(useBiff); return 0; } static int _nrrdFieldCheck_type(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_type"; if (airEnumValCheck(nrrdType, nrrd->type)) { biffMaybeAddf(useBiff, NRRD, "%s: type (%d) is not valid", me, nrrd->type); return 1; } return 0; } static int _nrrdFieldCheck_block_size(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_block_size"; char stmp[AIR_STRLEN_SMALL]; if (nrrdTypeBlock == nrrd->type && (!(0 < nrrd->blockSize)) ) { biffMaybeAddf(useBiff, NRRD, "%s: type is %s but nrrd->blockSize (%s) invalid", me, airEnumStr(nrrdType, nrrdTypeBlock), airSprintSize_t(stmp, nrrd->blockSize)); return 1; } if (nrrdTypeBlock != nrrd->type && (0 < nrrd->blockSize)) { biffMaybeAddf(useBiff, NRRD, "%s: type is %s (not block) but blockSize is %s", me, airEnumStr(nrrdType, nrrd->type), airSprintSize_t(stmp, nrrd->blockSize)); return 1; } return 0; } static int _nrrdFieldCheck_dimension(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_dimension"; if (!AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX)) { biffMaybeAddf(useBiff, NRRD, "%s: dimension %u is outside valid range [1,%d]", me, nrrd->dim, NRRD_DIM_MAX); return 1; } return 0; } static int _nrrdFieldCheck_space(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_space"; if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdFieldCheck_space_dimension(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_space_dimension"; if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdFieldCheck_sizes(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_sizes"; size_t size[NRRD_DIM_MAX]; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSize, size); if (_nrrdSizeCheck(size, nrrd->dim, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble with array sizes", me); return 1; } return 0; } static int _nrrdFieldCheck_spacings(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_spacings"; double val[NRRD_DIM_MAX]; unsigned int ai; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSpacing, val); for (ai=0; aidim; ai++) { if (!( !airIsInf_d(val[ai]) && (airIsNaN(val[ai]) || (0 != val[ai])) )) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d spacing (%g) invalid", me, ai, val[ai]); return 1; } } if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdFieldCheck_thicknesses(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_thicknesses"; double val[NRRD_DIM_MAX]; unsigned int ai; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoThickness, val); for (ai=0; aidim; ai++) { /* note that unlike spacing, we allow zero thickness, but it makes no sense to be negative */ if (!( !airIsInf_d(val[ai]) && (airIsNaN(val[ai]) || (0 <= val[ai])) )) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d thickness (%g) invalid", me, ai, val[ai]); return 1; } } return 0; } static int _nrrdFieldCheck_axis_mins(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_axis_mins"; double val[NRRD_DIM_MAX]; unsigned int ai; int ret; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoMin, val); for (ai=0; aidim; ai++) { if ((ret=airIsInf_d(val[ai]))) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d min %sinf invalid", me, ai, 1==ret ? "+" : "-"); return 1; } } if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } /* HEY: contemplate checking min != max, but what about stub axes ... */ return 0; } static int _nrrdFieldCheck_axis_maxs(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_axis_maxs"; double val[NRRD_DIM_MAX]; unsigned int ai; int ret; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoMax, val); for (ai=0; aidim; ai++) { if ((ret=airIsInf_d(val[ai]))) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d max %sinf invalid", me, ai, 1==ret ? "+" : "-"); return 1; } } if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } /* HEY: contemplate checking min != max, but what about stub axes ... */ return 0; } static int _nrrdFieldCheck_space_directions(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_space_directions"; if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: space info problem", me); return 1; } return 0; } static int _nrrdFieldCheck_centers(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_centers"; unsigned int ai; int val[NRRD_DIM_MAX]; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoCenter, val); for (ai=0; aidim; ai++) { if (!( nrrdCenterUnknown == val[ai] || !airEnumValCheck(nrrdCenter, val[ai]) )) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d center %d invalid", me, ai, val[ai]); return 1; } } return 0; } static int _nrrdFieldCheck_kinds(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_kinds"; int val[NRRD_DIM_MAX]; unsigned int wantLen, ai; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoKind, val); for (ai=0; aidim; ai++) { if (!( nrrdKindUnknown == val[ai] || !airEnumValCheck(nrrdKind, val[ai]) )) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d kind %d invalid", me, ai, val[ai]); return 1; } wantLen = nrrdKindSize(val[ai]); if (wantLen && wantLen != nrrd->axis[ai].size) { char stmp[AIR_STRLEN_SMALL]; biffMaybeAddf(useBiff, NRRD, "%s: axis %d kind %s requires size %u, but have %s", me, ai, airEnumStr(nrrdKind, val[ai]), wantLen, airSprintSize_t(stmp, nrrd->axis[ai].size)); return 1; } } return 0; } static int _nrrdFieldCheck_labels(const Nrrd *nrrd, int useBiff) { /* char me[]="_nrrdFieldCheck_labels"; */ AIR_UNUSED(nrrd); AIR_UNUSED(useBiff); /* don't think there's anything to do here: the label strings are either NULL (which is okay) or non-NULL, but we have no restrictions on the validity of the strings */ return 0; } static int _nrrdFieldCheck_units(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_units"; /* as with labels- the strings themselves don't need checking themselves */ /* but per-axis units cannot be set for axes with space directions ... */ if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: space info problem", me); return 1; } return 0; } static int _nrrdFieldCheck_old_min(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_old_min"; int ret; if ((ret=airIsInf_d(nrrd->oldMin))) { biffMaybeAddf(useBiff, NRRD, "%s: old min %sinf invalid", me, 1==ret ? "+" : "-"); return 1; } /* oldMin == oldMax is perfectly valid */ return 0; } static int _nrrdFieldCheck_old_max(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_old_max"; int ret; if ((ret=airIsInf_d(nrrd->oldMax))) { biffMaybeAddf(useBiff, NRRD, "%s: old max %sinf invalid", me, 1==ret ? "+" : "-"); return 1; } /* oldMin == oldMax is perfectly valid */ return 0; } static int _nrrdFieldCheck_keyvalue(const Nrrd *nrrd, int useBiff) { /* char me[]="_nrrdFieldCheck_keyvalue"; */ AIR_UNUSED(nrrd); AIR_UNUSED(useBiff); /* nrrdKeyValueAdd() ensures that keys aren't repeated, not sure what other kind of checking can be done */ return 0; } static int _nrrdFieldCheck_space_units(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_space_units"; /* not sure if there's anything to specifically check for the space units themselves ... */ if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: space info problem", me); return 1; } return 0; } static int _nrrdFieldCheck_space_origin(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_space_origin"; /* pre-Fri Feb 11 04:25:36 EST 2005, I thought that the spaceOrigin must be known to describe the space/orientation stuff, but that's too restrictive, which is why below says AIR_FALSE instead of AIR_TRUE */ if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: space info problem", me); return 1; } return 0; } static int _nrrdFieldCheck_measurement_frame(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_measurement_frame"; if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: space info problem", me); return 1; } return 0; } int (*_nrrdFieldCheck[NRRD_FIELD_MAX+1])(const Nrrd *, int useBiff) = { _nrrdFieldCheck_noop, /* nonfield */ _nrrdFieldCheck_noop, /* comment */ _nrrdFieldCheck_noop, /* content */ _nrrdFieldCheck_noop, /* number */ _nrrdFieldCheck_type, _nrrdFieldCheck_block_size, _nrrdFieldCheck_dimension, _nrrdFieldCheck_space, _nrrdFieldCheck_space_dimension, _nrrdFieldCheck_sizes, _nrrdFieldCheck_spacings, _nrrdFieldCheck_thicknesses, _nrrdFieldCheck_axis_mins, _nrrdFieldCheck_axis_maxs, _nrrdFieldCheck_space_directions, _nrrdFieldCheck_centers, _nrrdFieldCheck_kinds, _nrrdFieldCheck_labels, _nrrdFieldCheck_units, _nrrdFieldCheck_noop, /* min */ _nrrdFieldCheck_noop, /* max */ _nrrdFieldCheck_old_min, _nrrdFieldCheck_old_max, _nrrdFieldCheck_noop, /* endian */ _nrrdFieldCheck_noop, /* encoding */ _nrrdFieldCheck_noop, /* line_skip */ _nrrdFieldCheck_noop, /* byte_skip */ _nrrdFieldCheck_keyvalue, _nrrdFieldCheck_noop, /* sample units */ _nrrdFieldCheck_space_units, _nrrdFieldCheck_space_origin, _nrrdFieldCheck_measurement_frame, _nrrdFieldCheck_noop, /* data_file */ }; int _nrrdCheck(const Nrrd *nrrd, int checkData, int useBiff) { static const char me[]="_nrrdCheck"; int fi; if (!nrrd) { biffMaybeAddf(useBiff, NRRD, "%s: got NULL pointer", me); return 1; } if (checkData) { if (!(nrrd->data)) { biffMaybeAddf(useBiff, NRRD, "%s: nrrd %p has NULL data pointer", me, AIR_CVOIDP(nrrd)); return 1; } } for (fi=nrrdField_unknown+1; fidim != n2->dim) { biffMaybeAddf(useBiff, NRRD, "%s: n1->dim (%u) != n2->dim (%u)", me, n1->dim, n2->dim); return 0; } for (ai=0; aidim; ai++) { if (n1->axis[ai].size != n2->axis[ai].size) { biffMaybeAddf(useBiff, NRRD, "%s: n1->axis[%d].size (%s) " "!= n2->axis[%d].size (%s)", me, ai, airSprintSize_t(stmp[0], n1->axis[ai].size), ai, airSprintSize_t(stmp[1], n2->axis[ai].size)); return 0; } } return 1; } /* ******** nrrdElementSize() ** ** So just how many bytes long is one element in this nrrd? This is ** needed (over the simple nrrdTypeSize[] array) because some nrrds ** may be of "block" type, and because it does bounds checking on ** nrrd->type. Returns 0 if given a bogus nrrd->type, or if the block ** size isn't greater than zero (in which case it sets nrrd->blockSize ** to 0, just out of spite). This function never returns a negative ** value; using (!nrrdElementSize(nrrd)) is a sufficient check for ** invalidity. ** ** Besides learning how many bytes long one element is, this function ** is useful as a way of detecting an invalid blocksize on a block nrrd. */ size_t nrrdElementSize (const Nrrd *nrrd) { if (!( nrrd && !airEnumValCheck(nrrdType, nrrd->type) )) { return 0; } if (nrrdTypeBlock != nrrd->type) { return nrrdTypeSize[nrrd->type]; } /* else its block type */ if (nrrd->blockSize > 0) { return nrrd->blockSize; } /* else we got an invalid block size */ /* nrrd->blockSize = 0; */ return 0; } /* ******** nrrdElementNumber() ** ** takes the place of old "nrrd->num": the number of elements in the ** nrrd, which is just the product of the axis sizes. A return of 0 ** means there's a problem. Negative numbers are never returned. ** ** does NOT use biff */ size_t nrrdElementNumber (const Nrrd *nrrd) { size_t num, size[NRRD_DIM_MAX]; unsigned int ai; if (!nrrd) { return 0; } /* else */ nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSize, size); if (_nrrdSizeCheck(size, nrrd->dim, AIR_FALSE)) { /* the nrrd's size information is invalid, can't proceed */ return 0; } num = 1; for (ai=0; aidim; ai++) { /* negative numbers and overflow were caught by _nrrdSizeCheck() */ num *= size[ai]; } return num; } /* ** obviously, this requires that the per-axis size fields have been set */ void _nrrdSplitSizes(size_t *pieceSize, size_t *pieceNum, Nrrd *nrrd, unsigned int split) { unsigned int ai; size_t size[NRRD_DIM_MAX]; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSize, size); *pieceSize = 1; for (ai=0; aidim; ai++) { *pieceNum *= size[ai]; } return; } /* ******** nrrdHasNonExistSet() ** ** This function will always (assuming type is valid) set the value of ** nrrd->hasNonExist to either nrrdNonExistTrue or nrrdNonExistFalse, ** and it will return that value. For lack of a more sophisticated ** policy, blocks are currently always considered to be existent ** values (because nrrdTypeIsIntegral[nrrdTypeBlock] is currently true). ** This function will ALWAYS determine the correct answer and set the ** value of nrrd->hasNonExist: it ignores the value of ** nrrd->hasNonExist on the input nrrd. Exception: if nrrd is null or ** type is bogus, no action is taken and nrrdNonExistUnknown is ** returned. ** ** Because this will return either nrrdNonExistTrue or nrrdNonExistFalse, ** and because the C boolean value of these are true and false (respectively), ** it is possible (and encouraged) to use the return of this function ** as the expression of a conditional: ** ** if (nrrdHasNonExistSet(nrrd)) { ** ... handle existance of non-existent values ... ** } */ /* int nrrdHasNonExistSet(Nrrd *nrrd) { size_t I, N; float val; if (!( nrrd && !airEnumValCheck(nrrdType, nrrd->type) )) return nrrdNonExistUnknown; if (nrrdTypeIsIntegral[nrrd->type]) { nrrd->hasNonExist = nrrdNonExistFalse; } else { nrrd->hasNonExist = nrrdNonExistFalse; N = nrrdElementNumber(nrrd); for (I=0; Itype](nrrd->data, I); if (!AIR_EXISTS(val)) { nrrd->hasNonExist = nrrdNonExistTrue; break; } } } return nrrd->hasNonExist; } */ static int _nrrdCheckEnums(void) { static const char me[]="_nrrdCheckEnums"; char which[AIR_STRLEN_SMALL]; if (nrrdFormatTypeLast-1 != NRRD_FORMAT_TYPE_MAX) { strcpy(which, "nrrdFormat"); goto err; } if (nrrdTypeLast-1 != NRRD_TYPE_MAX) { strcpy(which, "nrrdType"); goto err; } if (nrrdEncodingTypeLast-1 != NRRD_ENCODING_TYPE_MAX) { strcpy(which, "nrrdEncodingType"); goto err; } if (nrrdCenterLast-1 != NRRD_CENTER_MAX) { strcpy(which, "nrrdCenter"); goto err; } if (nrrdAxisInfoLast-1 != NRRD_AXIS_INFO_MAX) { strcpy(which, "nrrdAxisInfo"); goto err; } /* can't really check on endian enum */ if (nrrdField_last-1 != NRRD_FIELD_MAX) { strcpy(which, "nrrdField"); goto err; } if (nrrdHasNonExistLast-1 != NRRD_HAS_NON_EXIST_MAX) { strcpy(which, "nrrdHasNonExist"); goto err; } /* ---- BEGIN non-NrrdIO */ if (nrrdBoundaryLast-1 != NRRD_BOUNDARY_MAX) { strcpy(which, "nrrdBoundary"); goto err; } if (nrrdMeasureLast-1 != NRRD_MEASURE_MAX) { strcpy(which, "nrrdMeasure"); goto err; } if (nrrdUnaryOpLast-1 != NRRD_UNARY_OP_MAX) { strcpy(which, "nrrdUnaryOp"); goto err; } if (nrrdBinaryOpLast-1 != NRRD_BINARY_OP_MAX) { strcpy(which, "nrrdBinaryOp"); goto err; } if (nrrdTernaryOpLast-1 != NRRD_TERNARY_OP_MAX) { strcpy(which, "nrrdTernaryOp"); goto err; } /* ---- END non-NrrdIO */ /* no errors so far */ return 0; err: biffAddf(NRRD, "%s: Last vs. MAX incompatibility for %s enum", me, which); return 1; } /* ****** nrrdSanity ** ** makes sure that all the basic assumptions of nrrd hold for ** the architecture/etc which we're currently running on. ** ** returns 1 if all is okay, 0 if there is a problem */ int /*Teem: biff if (!ret) */ nrrdSanity(void) { static const char me[]="nrrdSanity"; int aret, type; size_t maxsize; airLLong tmpLLI; airULLong tmpULLI; static int _nrrdSanity = 0; if (_nrrdSanity) { /* we've been through this once before and things looked okay ... */ /* Is this thread-safe? I think so. If we assume that any two threads are going to compute the same value, isn't it the case that, at worse, both of them will go through all the tests and then set _nrrdSanity to the same thing? */ return 1; } aret = airSanity(); if (aret != airInsane_not) { biffAddf(NRRD, "%s: airSanity() failed: %s", me, airInsaneErr(aret)); return 0; } /* ---- BEGIN non-NrrdIO */ /* Odd: this sanity checker isn't part of airSanity, nor can it be without adding to airInsaneErr's list of what can go wrong, but someone should be calling it, since we have no other correctness test of the Mersenne-Twister code within Teem (not counting the CTests in teem/Testing) */ if (!airRandMTSanity()) { biffAddf(NRRD, "%s: airRandMTSanity failed", me); return 0; } /* ---- END non-NrrdIO */ if (airEnumValCheck(nrrdEncodingType, nrrdDefaultWriteEncodingType)) { biffAddf(NRRD, "%s: nrrdDefaultWriteEncodingType (%d) not in valid " "range [%d,%d]", me, nrrdDefaultWriteEncodingType, nrrdEncodingTypeUnknown+1, nrrdEncodingTypeLast-1); return 0; } if (airEnumValCheck(nrrdCenter, nrrdDefaultCenter)) { biffAddf(NRRD, "%s: nrrdDefaultCenter (%d) not in valid range [%d,%d]", me, nrrdDefaultCenter, nrrdCenterUnknown+1, nrrdCenterLast-1); return 0; } /* ---- BEGIN non-NrrdIO */ if (!( nrrdTypeDefault == nrrdDefaultResampleType || !airEnumValCheck(nrrdType, nrrdDefaultResampleType) )) { biffAddf(NRRD, "%s: nrrdDefaultResampleType (%d) not in valid range [%d,%d]", me, nrrdDefaultResampleType, nrrdTypeUnknown, nrrdTypeLast-1); return 0; } if (airEnumValCheck(nrrdBoundary, nrrdDefaultResampleBoundary)) { biffAddf(NRRD, "%s: nrrdDefaultResampleBoundary (%d) " "not in valid range [%d,%d]", me, nrrdDefaultResampleBoundary, nrrdBoundaryUnknown+1, nrrdBoundaryLast-1); return 0; } if (airEnumValCheck(nrrdType, nrrdStateMeasureType)) { biffAddf(NRRD, "%s: nrrdStateMeasureType (%d) not in valid range [%d,%d]", me, nrrdStateMeasureType, nrrdTypeUnknown+1, nrrdTypeLast-1); return 0; } if (airEnumValCheck(nrrdType, nrrdStateMeasureHistoType)) { biffAddf(NRRD, "%s: nrrdStateMeasureHistoType (%d) not in " "valid range [%d,%d]", me, nrrdStateMeasureType, nrrdTypeUnknown+1, nrrdTypeLast-1); return 0; } /* ---- END non-NrrdIO */ if (!( nrrdTypeSize[nrrdTypeChar] == sizeof(char) && nrrdTypeSize[nrrdTypeUChar] == sizeof(unsigned char) && nrrdTypeSize[nrrdTypeShort] == sizeof(short) && nrrdTypeSize[nrrdTypeUShort] == sizeof(unsigned short) && nrrdTypeSize[nrrdTypeInt] == sizeof(int) && nrrdTypeSize[nrrdTypeUInt] == sizeof(unsigned int) && nrrdTypeSize[nrrdTypeLLong] == sizeof(airLLong) && nrrdTypeSize[nrrdTypeULLong] == sizeof(airULLong) && nrrdTypeSize[nrrdTypeFloat] == sizeof(float) && nrrdTypeSize[nrrdTypeDouble] == sizeof(double) )) { biffAddf(NRRD, "%s: sizeof() for nrrd types has problem: " "expected (%u,%u,%u,%u,%u,%u,%u,%u,%u,%u) " "but got (%u,%u,%u,%u,%u,%u,%u,%u,%u,%u)", me, AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeChar]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeUChar]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeShort]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeUShort]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeInt]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeUInt]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeLLong]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeULLong]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeFloat]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeDouble]), AIR_CAST(unsigned int, sizeof(char)), AIR_CAST(unsigned int, sizeof(unsigned char)), AIR_CAST(unsigned int, sizeof(short)), AIR_CAST(unsigned int, sizeof(unsigned short)), AIR_CAST(unsigned int, sizeof(int)), AIR_CAST(unsigned int, sizeof(unsigned int)), AIR_CAST(unsigned int, sizeof(airLLong)), AIR_CAST(unsigned int, sizeof(airULLong)), AIR_CAST(unsigned int, sizeof(float)), AIR_CAST(unsigned int, sizeof(double))); return 0; } /* check on NRRD_TYPE_SIZE_MAX */ maxsize = 0; for (type=nrrdTypeUnknown+1; type<=nrrdTypeLast-2; type++) { maxsize = AIR_MAX(maxsize, nrrdTypeSize[type]); } if (maxsize != NRRD_TYPE_SIZE_MAX) { biffAddf(NRRD, "%s: actual max type size is %u != %u == NRRD_TYPE_SIZE_MAX", me, AIR_CAST(unsigned int, maxsize), NRRD_TYPE_SIZE_MAX); return 0; } /* check on NRRD_TYPE_BIGGEST */ if (maxsize != sizeof(NRRD_TYPE_BIGGEST)) { biffAddf(NRRD, "%s: actual max type size is %u != " "%u == sizeof(NRRD_TYPE_BIGGEST)", me, AIR_CAST(unsigned int, maxsize), AIR_CAST(unsigned int, sizeof(NRRD_TYPE_BIGGEST))); return 0; } /* nrrd-defined min/max values for 64-bit integral types */ /* NOTE: because signed integral overflow is undefined in C, the tests for signed long long no longer use overflow (and an assumption of two's complement representation) to assess the correctness of NRRD_LLONG_MAX and NRRD_LLONG_MIN. We merely test that these values can be stored, which we do via indirect (perhaps needlessly so) means. (h/t Sean McBride for pointing this out) */ tmpLLI = _nrrdLLongMaxHelp(_nrrdLLongMaxHelp(_NRRD_LLONG_MAX_HELP)); if (!( tmpLLI > 0 && NRRD_LLONG_MAX == tmpLLI )) { biffAddf(NRRD, "%s: long long int can't hold NRRD_LLONG_MAX (" AIR_LLONG_FMT ")", me, NRRD_LLONG_MAX); return 0; } tmpLLI = _nrrdLLongMinHelp(_nrrdLLongMinHelp(_NRRD_LLONG_MIN_HELP)); if (!( tmpLLI < 0 && NRRD_LLONG_MIN == tmpLLI )) { biffAddf(NRRD, "%s: long long int can't hold NRRD_LLONG_MIN (" AIR_LLONG_FMT ")", me, NRRD_LLONG_MIN); return 0; } tmpULLI = _nrrdULLongMaxHelp(NRRD_ULLONG_MAX); if (tmpULLI != 0) { biffAddf(NRRD, "%s: unsigned long long int max (" AIR_ULLONG_FMT ") incorrect", me, NRRD_ULLONG_MAX); return 0; } if (_nrrdCheckEnums()) { biffAddf(NRRD, "%s: problem with enum definition", me); return 0; } if (!( NRRD_DIM_MAX >= 3 )) { biffAddf(NRRD, "%s: NRRD_DIM_MAX == %u seems awfully small, doesn't it?", me, NRRD_DIM_MAX); return 0; } if (!nrrdTypeIsIntegral[nrrdTypeBlock]) { biffAddf(NRRD, "%s: nrrdTypeInteger[nrrdTypeBlock] is not true, things " "could get wacky", me); return 0; } /* HEY: any other assumptions built into Teem? */ _nrrdSanity = 1; return 1; } /* ---- BEGIN non-NrrdIO */ void nrrdSanityOrDie(const char *me) { char *err; if (!nrrdSanity()) { fprintf(stderr, "******************************************\n"); fprintf(stderr, "******************************************\n"); fprintf(stderr, "\n"); fprintf(stderr, " %s: Nrrd sanity check failed.\n", me); fprintf(stderr, "\n"); fprintf(stderr, " This probably means that there was an error\n"); fprintf(stderr, " in the configuration, compilation, or basic\n"); fprintf(stderr, " variable definitions used for building Teem.\n"); fprintf(stderr, " Error message:\n"); fprintf(stderr, "%s\n", err = biffGetDone(NRRD)); fprintf(stderr, "\n"); fprintf(stderr, "******************************************\n"); fprintf(stderr, "******************************************\n"); free(err); exit(1); } } void nrrdZeroSet(Nrrd *nout) { if (!_nrrdCheck(nout, AIR_TRUE, AIR_FALSE)) { memset(nout->data, 0, nrrdElementNumber(nout)*nrrdElementSize(nout)); } return; } /* ---- END non-NrrdIO */ teem-1.11.0~svn6057/src/nrrd/range.c0000664000175000017500000002503512165631065016613 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" NrrdRange * nrrdRangeNew(double min, double max) { NrrdRange *range; range = (NrrdRange *)calloc(1, sizeof(NrrdRange)); if (range) { range->min = min; range->max = max; range->hasNonExist = nrrdHasNonExistUnknown; } return range; } NrrdRange * nrrdRangeCopy(const NrrdRange *rin) { NrrdRange *rout=NULL; if (rin) { rout = nrrdRangeNew(rin->min, rin->max); rout->hasNonExist = rin->hasNonExist; } return rout; } NrrdRange * nrrdRangeNix(NrrdRange *range) { airFree(range); return NULL; } void nrrdRangeReset(NrrdRange *range) { if (range) { range->min = AIR_NAN; range->max = AIR_NAN; range->hasNonExist = nrrdHasNonExistUnknown; } } /* ** not using biff (obviously) */ void nrrdRangeSet(NrrdRange *range, const Nrrd *nrrd, int blind8BitRange) { NRRD_TYPE_BIGGEST _min, _max; int blind; if (!range) { return; } if (nrrd && !airEnumValCheck(nrrdType, nrrd->type) && nrrdTypeBlock != nrrd->type) { blind = (nrrdBlind8BitRangeTrue == blind8BitRange || (nrrdBlind8BitRangeState == blind8BitRange && nrrdStateBlind8BitRange)); if (blind && 1 == nrrdTypeSize[nrrd->type]) { if (nrrdTypeChar == nrrd->type) { range->min = SCHAR_MIN; range->max = SCHAR_MAX; } else { range->min = 0; range->max = UCHAR_MAX; } range->hasNonExist = nrrdHasNonExistFalse; } else { nrrdMinMaxExactFind[nrrd->type](&_min, &_max, &(range->hasNonExist), nrrd); range->min = nrrdDLoad[nrrd->type](&_min); range->max = nrrdDLoad[nrrd->type](&_max); } } else { range->min = range->max = AIR_NAN; range->hasNonExist = nrrdHasNonExistUnknown; } return; } /* ******** nrrdRangePercentileSet ** ** this is called when information about the range of values in the ** nrrd is requested; and the learned information is put into "range" ** (overwriting whatever is there!) ** ** uses biff */ int nrrdRangePercentileSet(NrrdRange *range, const Nrrd *nrrd, double minPerc, double maxPerc, unsigned int hbins, int blind8BitRange) { static const char me[]="nrrdRangePercentileSet"; airArray *mop; Nrrd *nhist; double allmin, allmax, minval, maxval, *hist, sum, total, sumPerc; unsigned int hi; if (!(range && nrrd)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 0; } nrrdRangeSet(range, nrrd, blind8BitRange); /* range->min and range->max are the full range of nrrd (except maybe for blind8) */ if (!minPerc && !maxPerc) { /* wanted full range; there is nothing more to do */ return 0; } if (!hbins) { biffAddf(NRRD, "%s: sorry, non-histogram-based percentiles not " "currently implemented (need hbins > 0)", me); return 1; } if (!(hbins >= 5)) { biffAddf(NRRD, "%s: # histogram bins %u unreasonably small", me, hbins); return 1; } if (range->hasNonExist) { biffAddf(NRRD, "%s: sorry, can currently do histogram-based percentiles " "only in arrays with no non-existent values", me); return 1; } mop = airMopNew(); allmin = range->min; allmax = range->max; nhist = nrrdNew(); airMopAdd(mop, nhist, (airMopper)nrrdNuke, airMopAlways); /* the histogram is over the entire range of values */ if (nrrdHisto(nhist, nrrd, range, NULL, hbins, nrrdTypeDouble)) { biffAddf(NRRD, "%s: trouble making histogram", me); airMopError(mop); return 1; } hist = AIR_CAST(double *, nhist->data); total = AIR_CAST(double, nrrdElementNumber(nrrd)); if (minPerc) { minval = AIR_NAN; sumPerc = AIR_ABS(minPerc)*total/100.0; sum = hist[0]; for (hi=1; hi= sumPerc) { minval = AIR_AFFINE(0, hi-1, hbins-1, nhist->axis[0].min, nhist->axis[0].max); break; } } if (hi == hbins || !AIR_EXISTS(minval)) { biffAddf(NRRD, "%s: failed to find lower %g-percentile value", me, minPerc); airMopError(mop); return 1; } range->min = (minPerc > 0 ? minval : 2*allmin - minval); } if (maxPerc) { maxval = AIR_NAN; sumPerc = AIR_ABS(maxPerc)*total/100.0; sum = hist[hbins-1]; for (hi=hbins-1; hi; hi--) { sum += hist[hi-1]; if (sum >= sumPerc) { maxval = AIR_AFFINE(0, hi, hbins-1, nhist->axis[0].min, nhist->axis[0].max); break; } } if (!hi || !AIR_EXISTS(maxval)) { biffAddf(NRRD, "%s: failed to find upper %g-percentile value", me, maxPerc); airMopError(mop); return 1; } range->max = (maxPerc > 0 ? maxval : 2*allmax - maxval); } airMopOkay(mop); return 0; } /* ******** nrrdRangePercentileFromStringSet ** ** Implements smarts of figuring out a range from no info ("nan"), ** a known percentile (e.g. "3%") or a known explicit value (e.g. "3"), ** for both min and max. Used by "unu quantize" and others. */ int nrrdRangePercentileFromStringSet(NrrdRange *range, const Nrrd *nrrd, const char *_minStr, const char *_maxStr, unsigned int hbins, int blind8BitRange) { static const char me[]="nrrdRangePercentileFromStringSet"; double minVal, maxVal, minPerc, maxPerc; char *minStr, *maxStr; unsigned int mmIdx; airArray *mop; if (!(range && nrrd && _minStr && _maxStr)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); minStr = airStrdup(_minStr); airMopAdd(mop, minStr, airFree, airMopAlways); maxStr = airStrdup(_maxStr); airMopAdd(mop, maxStr, airFree, airMopAlways); /* parse min and max */ minVal = maxVal = minPerc = maxPerc = AIR_NAN; for (mmIdx=0; mmIdx<=1; mmIdx++) { int percwant; double val, *mmv, *mmp; char *mmStr; if (0 == mmIdx) { mmv = &minVal; mmp = &minPerc; mmStr = minStr; } else { mmv = &maxVal; mmp = &maxPerc; mmStr = maxStr; } if (airEndsWith(mmStr, NRRD_MINMAX_PERC_SUFF)) { percwant = AIR_TRUE; mmStr[strlen(mmStr)-strlen(NRRD_MINMAX_PERC_SUFF)] = '\0'; } else { percwant = AIR_FALSE; } if (1 != airSingleSscanf(mmStr, "%lf", &val)) { biffAddf(NRRD, "%s: couldn't parse \"%s\" for %s", me, !mmIdx ? _minStr : _maxStr, !mmIdx ? "minimum" : "maximum"); airMopError(mop); return 1; } if (percwant) { if (!AIR_EXISTS(val)) { biffAddf(NRRD, "%s: %s percentile must exist", me, !mmIdx ? "minimum" : "maximum"); airMopError(mop); return 1; } /* setting a finite percentile */ *mmp = val; } else if (!AIR_EXISTS(val)) { /* don't want a percentile, and given value is nan => same as full range == zero percentile */ *mmp = 0.0; } else { /* value exists: given explicitly */ *mmv = val; } } /* so whenever one end of the range is not given explicitly, it has been mapped to a statement about the percentile, which does require learning about the nrrd's values */ if (AIR_EXISTS(minPerc) || AIR_EXISTS(maxPerc)) { if (nrrdRangePercentileSet(range, nrrd, AIR_EXISTS(minPerc) ? minPerc : 0.0, AIR_EXISTS(maxPerc) ? maxPerc : 0.0, hbins, blind8BitRange)) { biffAddf(NRRD, "%s: trouble finding percentile range", me); airMopError(mop); return 1; } } /* whatever explicit values were given are now stored */ if (AIR_EXISTS(minVal)) { range->min = minVal; } if (AIR_EXISTS(maxVal)) { range->max = maxVal; } airMopOkay(mop); return 0; } /* ** wrapper around nrrdRangeSet that (effectively) sets range->min ** and range->min only if they didn't already exist */ void nrrdRangeSafeSet(NrrdRange *range, const Nrrd *nrrd, int blind8BitRange) { double minIn, maxIn; if (!range) { return; } minIn = range->min; maxIn = range->max; nrrdRangeSet(range, nrrd, blind8BitRange); if (AIR_EXISTS(minIn)) { range->min = minIn; } if (AIR_EXISTS(maxIn)) { range->max = maxIn; } return; } /* ** does not use biff */ NrrdRange * nrrdRangeNewSet(const Nrrd *nrrd, int blind8BitRange) { NrrdRange *range; range = nrrdRangeNew(0, 0); /* doesn't matter what values are used here */ nrrdRangeSet(range, nrrd, blind8BitRange); return range; } /* ******** nrrdHasNonExist ** ** returns the nrrdHasNonExist* enum value appropriate for a given nrrd. ** By cleverness, this value can be used as a regular C boolean, so that ** the function will act as you expect. ** ** (the existence of this function implies that I'll never have an airEnum ** of the same name, which would be the usual thing to do with a C enum, ** but I don't think an airEnum for this would be useful) */ int nrrdHasNonExist(const Nrrd *nrrd) { NRRD_TYPE_BIGGEST _min, _max; int ret; if (nrrd && !airEnumValCheck(nrrdType, nrrd->type) && nrrdTypeBlock != nrrd->type) { if (nrrdTypeIsIntegral[nrrd->type]) { ret = nrrdHasNonExistFalse; } else { /* HEY: this could be optimized by being more specialized */ nrrdMinMaxExactFind[nrrd->type](&_min, &_max, &ret, nrrd); } } else { ret = nrrdHasNonExistUnknown; } return ret; } teem-1.11.0~svn6057/src/nrrd/keyvalue.c0000664000175000017500000002116612165631065017345 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /*** **** NONE of the nrrdKeyValue functions use biff. **** They don't use them now, and they never should. **** Unless I change my mind. ***/ /* ******** nrrdKeyValueSize ** ** returns the number of key/value pairs in a nrrd */ unsigned int nrrdKeyValueSize(const Nrrd *nrrd) { if (!nrrd) { return 0; } return nrrd->kvpArr->len; } /* ******** nrrdKeyValueIndex ** ** given an int in [0 .. #key/value pairs - 1], sets *keyP and *valueP ** to put to the corresponding key and value. ** ** NOTE: whether or not *keyP and *valueP are set to pointers to memory ** "inside" the nrrd struct (pointers which you had better not free()!) ** is controlled by nrrdStateKeyValueReturnInternalPointers, which defaults ** to AIR_FALSE */ void nrrdKeyValueIndex(const Nrrd *nrrd, char **keyP, char **valueP, unsigned int ki) { if (!( nrrd && keyP && valueP && ki < nrrd->kvpArr->len )) { if (keyP) { *keyP = NULL; } if (valueP) { *valueP = NULL; } return; } if (nrrdStateKeyValueReturnInternalPointers) { *keyP = nrrd->kvp[0 + 2*ki]; *valueP = nrrd->kvp[1 + 2*ki]; } else { *keyP = airStrdup(nrrd->kvp[0 + 2*ki]); *valueP = airStrdup(nrrd->kvp[1 + 2*ki]); } return; } static unsigned int _kvpIdxFind(const Nrrd *nrrd, const char *key, int *found) { unsigned int nk, ki, ret; nk = nrrd->kvpArr->len; for (ki=0; kikvp[0 + 2*ki], key)) { break; } } if (kikvpArr->len; for (ki=0; kikvp[0 + 2*ki] = (char *)airFree(nrrd->kvp[0 + 2*ki]); nrrd->kvp[1 + 2*ki] = (char *)airFree(nrrd->kvp[1 + 2*ki]); } airArrayLenSet(nrrd->kvpArr, 0); return; } int nrrdKeyValueErase(Nrrd *nrrd, const char *key) { unsigned int nk, ki; int found; if (!( nrrd && key )) { /* got NULL pointer */ return 1; } ki = _kvpIdxFind(nrrd, key, &found); if (!found) { return 0; } nrrd->kvp[0 + 2*ki] = (char *)airFree(nrrd->kvp[0 + 2*ki]); nrrd->kvp[1 + 2*ki] = (char *)airFree(nrrd->kvp[1 + 2*ki]); nk = nrrd->kvpArr->len; for (; kikvp[0 + 2*ki] = nrrd->kvp[0 + 2*(ki+1)]; nrrd->kvp[1 + 2*ki] = nrrd->kvp[1 + 2*(ki+1)]; } airArrayLenIncr(nrrd->kvpArr, -1); return 0; } /* ******** nrrdKeyValueAdd ** ** This will COPY the given strings, and so does not depend on ** them existing past the return of this function ** ** NOTE: Despite what might be most logical, there is no effort made ** here to cleanup key or value, including any escaping or filtering ** that might be warranted for white space other than \n ** ** does NOT use BIFF */ int nrrdKeyValueAdd(Nrrd *nrrd, const char *key, const char *value) { unsigned int ki; int found; if (!( nrrd && key && value )) { /* got NULL pointer */ return 1; } if (!strlen(key)) { /* reject empty keys */ return 1; } ki = _kvpIdxFind(nrrd, key, &found); if (found) { /* over-writing value for an existing key, so have to free old value */ airFree(nrrd->kvp[1 + 2*ki]); nrrd->kvp[1 + 2*ki] = airStrdup(value); } else { /* adding value for a new key */ ki = airArrayLenIncr(nrrd->kvpArr, 1); nrrd->kvp[0 + 2*ki] = airStrdup(key); nrrd->kvp[1 + 2*ki] = airStrdup(value); } return 0; } /* ******** nrrdKeyValueGet ** ** NOTE: whether or not *keyP and *valueP are set to pointers to memory ** "inside" the nrrd struct (pointers which you had better not free()!) ** is controlled by nrrdStateKeyValueReturnInternalPointers, which defaults ** to AIR_FALSE ** ** does NOT use BIFF */ char * nrrdKeyValueGet(const Nrrd *nrrd, const char *key) { char *ret; unsigned int ki; int found; if (!( nrrd && key )) { /* got NULL pointer */ return NULL; } ki = _kvpIdxFind(nrrd, key, &found); if (found) { if (nrrdStateKeyValueReturnInternalPointers) { ret = nrrd->kvp[1 + 2*ki]; } else { ret = airStrdup(nrrd->kvp[1 + 2*ki]); } } else { ret = NULL; } return ret; } /* ** Does the escaping of special characters in a string that ** is being written either to "FILE *file" or "char *dst" ** (WHICH IS ASSUMED to be allocated to be big enough!) ** Which characters to escape should be put in string "toescape" ** currently supported: \n \ " ** Also, converts characters in "tospace" to a space. Being in ** toescape trumps being in tospace, so tospace can be harmlessly ** set to, say, AIR_WHITESPACE. ** ** accident of history that this function is in this file */ void _nrrdWriteEscaped(FILE *file, char *dst, const char *str, const char *toescape, const char *tospace) { /* static const char me[]="_nrrdWriteEscaped"; */ size_t ci, gslen; /* given strlen */ gslen = strlen(str); for (ci=0; cikvpArr->len; ki++) { key = nin->kvp[0 + 2*ki]; value = nin->kvp[1 + 2*ki]; if (nrrdKeyValueAdd(nout, key, value)) { return 3; } } return 0; } teem-1.11.0~svn6057/src/nrrd/newenc.txt0000664000175000017500000000331107574130504017364 0ustar domibeldomibelto add zlib compression to nrrd: change to meaning of "byte skip": in uncompressed data, the bytes are skipped by _nrrdReadNrrd() before the _nrrdReadData*() function is called. In compressed data, they are not skipped in _nrrdReadNrrd(), and it is the job of the _nrrdReadData*() function to skip the bytes in the decompressed stream. -- arraysNrrd.c: added elements to nrrdEncodingEndianMatters[] and nrrdEncodingCompression[] (unconditionally), and added a conditional value to nrrdEncodingAvailable[] -- enumsNrrd.c: added elements to _nrrdEncoding{Str,StrEqv,ValEqv} (unconditionally) (actually, had to create the Eqv arrays, but further new encodings won't need to) -- gzio.c: had to hack with the zlib library because of API deficiencies -- nrrd.h: added "int encoding" arg to nrrdFitsInFormat, because now we have encodings that may not fit in a given format -- nrrdEnums.h: added nrrdEncodingGzip to nrrdEncoding* enum. (unconditionally). If we did this conditional on TEEM_ZLIB then anyone wanting to use nrrd with zlib would also have to define TEEM_ZLIB... -- privateNrrd.h: (conditionally) added declarations for the new zlib interface functions that were created in gzio.c -- read.c: created new function, _nrrdReadDataZlib, for reading data with zlib compression, and (unconditionally) added it to the nrrdReadData[] array. If TEEM_ZLIB isn't turned, you get a stub function with a biff message saying, "sorry, zlib not enabled" -- simple.c: added "int encoding" arg to nrrdFitsInFormat(), because now we have encodings that may not fit in a given format -- write.c: created new function, _nrrdWriteDataZlib, for writing data with zlib compression, and (unconditionally) added it to the nrrdWriteData[] array teem-1.11.0~svn6057/src/nrrd/encodingAscii.c0000664000175000017500000001307112165631065020253 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" static FILE *_fileSave = NULL; static int _nrrdEncodingAscii_available(void) { return AIR_TRUE; } static int _nrrdEncodingAscii_read(FILE *file, void *_data, size_t elNum, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingAscii_read"; char numbStr[AIR_STRLEN_HUGE]; /* HEY: fix this */ char *nstr; size_t I; char *data; int tmp; AIR_UNUSED(nio); _fileSave = file; if (nrrdTypeBlock == nrrd->type) { biffAddf(NRRD, "%s: can't read nrrd type %s from %s", me, airEnumStr(nrrdType, nrrdTypeBlock), nrrdEncodingAscii->name); return 1; } data = (char*)_data; I = 0; while (I < elNum) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; /* HEY: we can easily suffer here from a standard buffer overflow problem; this was a source of a mysterious unu crash: echo "0 0 0 0 1 0 0 0 0" \ | unu reshape -s 9 1 1 \ | unu pad -min 0 0 0 -max 8 8 8 \ | unu make -s 9 9 9 -t float -e ascii -ls 9 \ -spc LPS -orig "(0,0,0)" -dirs "(1,0,0) (0,1,0) (0,0,1)" This particular case is resolved by changing AIR_STRLEN_HUGE to AIR_STRLEN_HUGE*100, but the general problem remains. This motivated adding the memory corruption test */ if (1 != fscanf(file, "%s", numbStr)) { biffAddf(NRRD, "%s: couldn't parse element %s of %s", me, airSprintSize_t(stmp1, I+1), airSprintSize_t(stmp2, elNum)); return 1; } if (file != _fileSave) { fprintf(stderr, "%s: PANIC memory corruption detected\n", me); /* this may crash, hence the fprintf above to help debug */ biffAddf(NRRD, "%s: PANIC memory corruption detected", me); return 1; } if (!strcmp(",", numbStr)) { /* its an isolated comma, not a value, pass over this */ continue; } /* get past any commas prefixing a number (without space) */ nstr = numbStr + strspn(numbStr, ","); if (nrrd->type >= nrrdTypeInt) { /* sscanf supports putting value directly into this type */ if (1 != airSingleSscanf(nstr, nrrdTypePrintfStr[nrrd->type], (void*)(data + I*nrrdElementSize(nrrd)))) { biffAddf(NRRD, "%s: couldn't parse %s %s of %s (\"%s\")", me, airEnumStr(nrrdType, nrrd->type), airSprintSize_t(stmp1, I+1), airSprintSize_t(stmp2, elNum), nstr); return 1; } } else { /* sscanf value into an int first */ if (1 != airSingleSscanf(nstr, "%d", &tmp)) { biffAddf(NRRD, "%s: couldn't parse element %s of %s (\"%s\")", me, airSprintSize_t(stmp1, I+1), airSprintSize_t(stmp2, elNum), nstr); return 1; } nrrdIInsert[nrrd->type](data, I, tmp); } I++; } return 0; } static int _nrrdEncodingAscii_write(FILE *file, const void *_data, size_t elNum, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingAscii_write"; char buff[AIR_STRLEN_MED]; size_t bufflen, linelen; const char *data; size_t I; if (nrrdTypeBlock == nrrd->type) { biffAddf(NRRD, "%s: can't write nrrd type %s to %s", me, airEnumStr(nrrdType, nrrdTypeBlock), nrrdEncodingAscii->name); return 1; } data = AIR_CAST(const char*, _data); linelen = 0; for (I=0; Itype](buff, data); if (1 == nrrd->dim) { fprintf(file, "%s\n", buff); } else if (nrrd->dim == 2 && nrrd->axis[0].size <= nio->valsPerLine) { fprintf(file, "%s%c", buff, (I+1)%(nrrd->axis[0].size) ? ' ' : '\n'); } else { bufflen = strlen(buff); if (linelen+bufflen+1 <= nio->charsPerLine) { fprintf(file, "%s%s", I ? " " : "", buff); linelen += (I ? 1 : 0) + bufflen; } else { fprintf(file, "\n%s", buff); linelen = bufflen; } } data += nrrdElementSize(nrrd); } /* just to be sure, we always end with a carraige return */ fprintf(file, "\n"); return 0; } const NrrdEncoding _nrrdEncodingAscii = { "ASCII", /* name */ "ascii", /* suffix */ AIR_FALSE, /* endianMatters */ AIR_FALSE, /* isCompression */ _nrrdEncodingAscii_available, _nrrdEncodingAscii_read, _nrrdEncodingAscii_write }; const NrrdEncoding *const nrrdEncodingAscii = &_nrrdEncodingAscii; teem-1.11.0~svn6057/src/nrrd/cc.c0000664000175000017500000007052112165631065016104 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ** learned: if you have globals, such as _nrrdCC_verb, which are ** defined and declared here, but which are NOT initialized, then ** C++ apps which are linking against Teem will have problems!!! ** This was first seen on the mac. */ int _nrrdCC_verb = 0; int _nrrdCC_EqvIncr = 10000; /* HEY: this has to be big so that ccfind is not stuck constantly re-allocating the eqv array. This is will be one of the best places to test the new multiplicative reallocation strategy, planned for Teem 2.0 */ int _nrrdCCFind_1(Nrrd *nout, unsigned int *numid, const Nrrd *nin) { /* static const char me[]="_nrrdCCFind_1"; */ unsigned int sx, I, id, lval, val, *out, (*lup)(const void *, size_t); lup = nrrdUILookup[nin->type]; out = AIR_CAST(unsigned int*, nout->data); out[0] = id = 0; *numid = 1; sx = AIR_CAST(unsigned int, nin->axis[0].size); lval = lup(nin->data, 0); for (I=1; Idata, I); if (lval != val) { id++; (*numid)++; } out[I] = id; lval = val; } return 0; } /* ** layout of value (pvl) and index (pid) cache: ** ** 2 3 4 --> X ** 1 . . oddly, index 0 is never used ** . . . ** | ** v Y */ int _nrrdCCFind_2(Nrrd *nout, unsigned int *numid, airArray *eqvArr, const Nrrd *nin, unsigned int conny) { static const char me[]="_nrrdCCFind_2"; double vl=0, pvl[5]={0,0,0,0,0}; unsigned int id, pid[5]={0,0,0,0,0}, (*lup)(const void *, size_t), *out; unsigned int p, x, y, sx, sy; id = 0; /* sssh! compiler warnings */ lup = nrrdUILookup[nin->type]; out = AIR_CAST(unsigned int*, nout->data); sx = AIR_CAST(unsigned int, nin->axis[0].size); sy = AIR_CAST(unsigned int, nin->axis[1].size); #define GETV_2(x,y) ((AIR_IN_CL(0, AIR_CAST(int, x), AIR_CAST(int, sx-1)) \ && AIR_IN_CL(0, AIR_CAST(int, y), AIR_CAST(int, sy-1))) \ ? lup(nin->data, (x) + sx*(y)) \ : 0.5) /* value that can't come from an array of uints */ #define GETI_2(x,y) ((AIR_IN_CL(0, AIR_CAST(int, x), AIR_CAST(int, sx-1)) \ && AIR_IN_CL(0, AIR_CAST(int, y), AIR_CAST(int, sy-1))) \ ? out[(x) + sx*(y)] \ : AIR_CAST(unsigned int, -1)) /* CC index (probably!) never assigned */ *numid = 0; for (y=0; y p = %d, id = %d, *numid = %d\n", p, id, *numid); } out[x + sx*y] = id; } } return 0; } /* ** ** 5 6 7 --> X ** 8 9 10 ** 11 12 13 ** | ** v Y ** 2 3 4 ** / 1 . . again, 0 index never used, for reasons forgotten ** Z . . . */ int _nrrdCCFind_3(Nrrd *nout, unsigned int *numid, airArray *eqvArr, const Nrrd *nin, unsigned int conny) { /* static const char me[]="_nrrdCCFind_3" ; */ double pvl[14]={0,0,0,0,0,0,0,0,0,0,0,0,0,0}, vl=0; unsigned int id, *out, (*lup)(const void *, size_t), pid[14]={0,0,0,0,0,0,0,0,0,0,0,0,0,0}; unsigned int p, x, y, z, sx, sy, sz; id = 0; /* sssh! compiler warnings */ lup = nrrdUILookup[nin->type]; out = AIR_CAST(unsigned int*, nout->data); sx = AIR_CAST(unsigned int, nin->axis[0].size); sy = AIR_CAST(unsigned int, nin->axis[1].size); sz = AIR_CAST(unsigned int, nin->axis[2].size); #define GETV_3(x,y,z) ((AIR_IN_CL(0, AIR_CAST(int, x), AIR_CAST(int, sx-1)) \ && AIR_IN_CL(0, AIR_CAST(int, y), AIR_CAST(int, sy-1)) \ && AIR_IN_CL(0, AIR_CAST(int, z), AIR_CAST(int, sz-1)))\ ? lup(nin->data, (x) + sx*((y) + sy*(z))) \ : 0.5) #define GETI_3(x,y,z) ((AIR_IN_CL(0, AIR_CAST(int, x), AIR_CAST(int, sx-1)) \ && AIR_IN_CL(0, AIR_CAST(int, y), AIR_CAST(int, sy-1)) \ && AIR_IN_CL(0, AIR_CAST(int, z), AIR_CAST(int, sz-1)))\ ? out[(x) + sx*((y) + sy*(z))] \ : AIR_CAST(unsigned int, -1)) *numid = 0; for (z=0; zdata[I] is the value in nin inside CC #I. */ int nrrdCCFind(Nrrd *nout, Nrrd **nvalP, const Nrrd *nin, int type, unsigned int conny) { static const char me[]="nrrdCCFind", func[]="ccfind"; Nrrd *nfpid; /* first-pass IDs */ airArray *mop, *eqvArr; unsigned int *fpid, numid, numsettleid, *map, (*lup)(const void *, size_t), (*ins)(void *, size_t, unsigned int); int ret; size_t I, NN; void *val; if (!(nout && nin)) { /* NULL nvalP okay */ biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: nout == nin disallowed", me); return 1; } if (!( nrrdTypeIsIntegral[nin->type] && nrrdTypeIsUnsigned[nin->type] && nrrdTypeSize[nin->type] <= 4 )) { biffAddf(NRRD, "%s: can only find connected components in " "1, 2, or 4 byte unsigned integral values (not %s)", me, airEnumStr(nrrdType, nin->type)); return 1; } if (nrrdTypeDefault != type) { if (!( AIR_IN_OP(nrrdTypeUnknown, type, nrrdTypeLast) )) { biffAddf(NRRD, "%s: got invalid target type %d", me, type); return 1; } if (!( nrrdTypeIsIntegral[type] && nrrdTypeIsUnsigned[nin->type] && nrrdTypeSize[type] <= 4 )) { biffAddf(NRRD, "%s: can only save connected components to 1, 2, or 4 byte " "unsigned integral values (not %s)", me, airEnumStr(nrrdType, type)); return 1; } } if (!( conny <= nin->dim )) { biffAddf(NRRD, "%s: connectivity value must be in [1..%d] for %d-D " "data (not %d)", me, nin->dim, nin->dim, conny); return 1; } if (nrrdConvert(nfpid=nrrdNew(), nin, nrrdTypeUInt)) { biffAddf(NRRD, "%s: couldn't allocate fpid %s array to match input size", me, airEnumStr(nrrdType, nrrdTypeUInt)); return 1; } mop = airMopNew(); airMopAdd(mop, nfpid, (airMopper)nrrdNuke, airMopAlways); eqvArr = airArrayNew(NULL, NULL, 2*sizeof(unsigned int), _nrrdCC_EqvIncr); airMopAdd(mop, eqvArr, (airMopper)airArrayNuke, airMopAlways); ret = 0; switch(nin->dim) { case 1: ret = _nrrdCCFind_1(nfpid, &numid, nin); break; case 2: ret = _nrrdCCFind_2(nfpid, &numid, eqvArr, nin, conny); break; case 3: ret = _nrrdCCFind_3(nfpid, &numid, eqvArr, nin, conny); break; default: ret = _nrrdCCFind_N(nfpid, &numid, eqvArr, nin, conny); break; } if (ret) { biffAddf(NRRD, "%s: initial pass failed", me); airMopError(mop); return 1; } map = AIR_MALLOC(numid, unsigned int); airMopAdd(mop, map, airFree, airMopAlways); numsettleid = airEqvMap(eqvArr, map, numid); /* convert fpid values to final id values */ fpid = (unsigned int*)(nfpid->data); NN = nrrdElementNumber(nfpid); for (I=0; Itype, 1, AIR_CAST(size_t, numsettleid))) { biffAddf(NRRD, "%s: couldn't allocate output value list", me); airMopError(mop); return 1; } airMopAdd(mop, nvalP, (airMopper)airSetNull, airMopOnError); airMopAdd(mop, *nvalP, (airMopper)nrrdNuke, airMopOnError); val = (*nvalP)->data; lup = nrrdUILookup[nin->type]; ins = nrrdUIInsert[nin->type]; /* I'm not sure if its more work to do all the redundant assignments or to check whether or not to do them */ for (I=0; Idata, I)); } } if (nrrdTypeDefault != type) { if (numsettleid-1 > nrrdTypeMax[type]) { biffAddf(NRRD, "%s: max cc id %u is too large to fit in output type %s", me, numsettleid-1, airEnumStr(nrrdType, type)); airMopError(mop); return 1; } } else { type = (numsettleid-1 <= nrrdTypeMax[nrrdTypeUChar] ? nrrdTypeUChar : (numsettleid-1 <= nrrdTypeMax[nrrdTypeUShort] ? nrrdTypeUShort : nrrdTypeUInt)); } if (nrrdConvert(nout, nfpid, type)) { biffAddf(NRRD, "%s: trouble converting to final output", me); airMopError(mop); return 1; } if (nrrdContentSet_va(nout, func, nin, "%s,%d", airEnumStr(nrrdType, type), conny)) { biffAddf(NRRD, "%s:", me); return 1; } if (nout != nin) { nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE); } /* basic info handled by nrrdConvert */ airMopOkay(mop); return 0; } int _nrrdCCAdj_1(unsigned char *out, int numid, const Nrrd *nin) { AIR_UNUSED(out); AIR_UNUSED(numid); AIR_UNUSED(nin); return 0; } int _nrrdCCAdj_2(unsigned char *out, unsigned int numid, const Nrrd *nin, unsigned int conny) { unsigned int (*lup)(const void *, size_t), x, y, sx, sy, id=0; double pid[5]={0,0,0,0,0}; lup = nrrdUILookup[nin->type]; sx = AIR_CAST(unsigned int, nin->axis[0].size); sy = AIR_CAST(unsigned int, nin->axis[1].size); for (y=0; ytype]; sx = AIR_CAST(unsigned int, nin->axis[0].size); sy = AIR_CAST(unsigned int, nin->axis[1].size); sz = AIR_CAST(unsigned int, nin->axis[2].size); for (z=0; zdim) )) { biffAddf(NRRD, "%s: connectivity value must be in [1..%d] for %d-D " "data (not %d)", me, nin->dim, nin->dim, conny); return 1; } maxid = nrrdCCMax(nin); if (nrrdMaybeAlloc_va(nout, nrrdTypeUChar, 2, AIR_CAST(size_t, maxid+1), AIR_CAST(size_t, maxid+1))) { biffAddf(NRRD, "%s: trouble allocating output", me); return 1; } out = (unsigned char *)(nout->data); switch(nin->dim) { case 1: ret = _nrrdCCAdj_1(out, maxid+1, nin); break; case 2: ret = _nrrdCCAdj_2(out, maxid+1, nin, conny); break; case 3: ret = _nrrdCCAdj_3(out, maxid+1, nin, conny); break; default: ret = _nrrdCCAdj_N(out, maxid+1, nin, conny); break; } if (ret) { biffAddf(NRRD, "%s: trouble", me); return 1; } /* this goofiness is just so that histo-based projections return the sorts of values that we expect */ nout->axis[0].center = nout->axis[1].center = nrrdCenterCell; nout->axis[0].min = nout->axis[1].min = -0.5; nout->axis[0].max = nout->axis[1].max = maxid + 0.5; if (nrrdContentSet_va(nout, func, nin, "%d", conny)) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } /* ******** nrrdCCMerge ** ** Slightly-too-multi-purpose tool for merging small connected components ** (CCs) into larger ones, according to a number of possible different ** constraints, as explained below. ** ** valDir: (value direction) uses information about the original values ** in the CC to constrain whether darker gets merged into brighter, or vice ** versa, or neither. For non-zero valDir values, a non-NULL _nval (from ** nrrdCCFind) must be passed. ** valDir > 0 : merge dark CCs into bright, but not vice versa ** valDir = 0 : merge either way, values are irrelevant ** valDir < 0 : merge bright CCs into dark, but not vice versa ** When merging with multiple neighbors (maxNeighbor > 1), the value ** of the largest neighbor is considered. ** ** maxSize: a cap on how large "small" is- CCs any larger than maxSize are ** not merged, as they are deemed too significant. Or, a maxSize of 0 says ** size is no object for merging CCs. ** ** maxNeighbor: a maximum number of neighbors that a CC can have (either ** bigger than the CC or not) if it is to be merged. Use 1 to merge ** isolated islands into their surrounds, 2 to merge CC with the larger ** of their two neighbors, etc., or 0 to allow any number of neighbors. ** ** conny: passed to nrrdCCAdjacency() when determining neighbors ** ** In order to prevent weirdness, the merging done in one call to this ** function is not transitive: if A is merged to B, then B will not be ** merged to anything else, even if meets all the requirements defined ** by the given parameters. This is accomplished by working from the ** smallest CCs to the largest. Iterated calls may be needed to acheive ** the desired effect. ** ** Note: the output of this is not "settled"- the CC id values are not ** shiftward downwards to their lowest possible values, since this would ** needlessly invalidate the nval value store. */ int nrrdCCMerge(Nrrd *nout, const Nrrd *nin, Nrrd *_nval, int valDir, unsigned int maxSize, unsigned int maxNeighbor, unsigned int conny) { static const char me[]="nrrdCCMerge", func[]="ccmerge"; const char *valcnt; unsigned int _i, i, j, bigi=0, numid, *size, *sizeId, *nn, /* number of neighbors */ *val=NULL, *hit, (*lup)(const void *, size_t), (*ins)(void *, size_t, unsigned int); Nrrd *nadj, *nsize, *nval=NULL, *nnn; unsigned char *adj; unsigned int *map, *id; airArray *mop; size_t I, NN; mop = airMopNew(); if (!( nout && nrrdCCValid(nin) )) { /* _nval can be NULL */ biffAddf(NRRD, "%s: invalid args", me); airMopError(mop); return 1; } if (valDir) { airMopAdd(mop, nval = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nval, _nval, nrrdTypeUInt)) { biffAddf(NRRD, "%s: value-directed merging needs usable nval", me); airMopError(mop); return 1; } val = (unsigned int*)(nval->data); } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } } airMopAdd(mop, nadj = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nsize = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nnn = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdCCSize(nsize, nin) || nrrdCopy(nnn, nsize) /* just to allocate to right size and type */ || nrrdCCAdjacency(nadj, nin, conny)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } size = (unsigned int*)(nsize->data); adj = (unsigned char*)(nadj->data); nn = (unsigned int*)(nnn->data); numid = AIR_CAST(unsigned int, nsize->axis[0].size); for (i=0; i maxSize)) { continue; } if (maxNeighbor && (nn[i] > maxNeighbor)) { continue; } /* find biggest neighbor, exploiting the fact that we already sorted CC ids on size. j descends through indices of id[], bigi goes through CC ids which are larger than CC i */ for (j=numid-1; j>_i; j--) { bigi = id[j]; if (adj[bigi + numid*i]) break; } if (j == _i) { continue; /* we had no neighbors ?!?! */ } if (valDir && (AIR_CAST(int, val[bigi]) - AIR_CAST(int, val[i]))*valDir < 0 ) { continue; } /* else all criteria for merging have been met */ map[i] = bigi; hit[bigi] = AIR_TRUE; } lup = nrrdUILookup[nin->type]; ins = nrrdUIInsert[nout->type]; NN = nrrdElementNumber(nin); for (I=0; Idata, I, map[lup(nin->data, I)]); } valcnt = ((_nval && _nval->content) ? _nval->content : nrrdStateUnknownContent); if ( (valDir && nrrdContentSet_va(nout, func, nin, "%c(%s),%d,%d,%d", (valDir > 0 ? '+' : '-'), valcnt, maxSize, maxNeighbor, conny)) || (!valDir && nrrdContentSet_va(nout, func, nin, ".,%d,%d,%d", maxSize, maxNeighbor, conny)) ) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } /* basic info handled by nrrdCopy */ airMopOkay(mop); return 0; } /* ******** nrrdCCRevalue() ** ** assigns the original values back to the connected components ** obviously, this could be subsumed by nrrdApply1DLut(), but this ** is so special purpose that it seemed simpler to code from scratch */ int nrrdCCRevalue (Nrrd *nout, const Nrrd *nin, const Nrrd *nval) { static const char me[]="nrrdCCRevalue"; size_t I, NN; unsigned int (*vlup)(const void *, size_t), (*ilup)(const void *, size_t), (*ins)(void *, size_t, unsigned int); if (!( nout && nrrdCCValid(nin) && nval )) { biffAddf(NRRD, "%s: invalid args", me); return 1; } if (nrrdConvert(nout, nin, nval->type)) { biffAddf(NRRD, "%s: couldn't initialize output", me); return 1; } NN = nrrdElementNumber(nin); vlup = nrrdUILookup[nval->type]; ilup = nrrdUILookup[nin->type]; ins = nrrdUIInsert[nout->type]; for (I=0; Idata, I, vlup(nval->data, ilup(nin->data, I))); } /* basic info handled by nrrdConvert */ return 0; } int nrrdCCSettle(Nrrd *nout, Nrrd **nvalP, const Nrrd *nin) { static const char me[]="nrrdCCSettle", func[]="ccsettle"; unsigned int numid, maxid, jd, id, *map, (*lup)(const void *, size_t), (*ins)(void *, size_t, unsigned int); size_t I, NN; airArray *mop; mop = airMopNew(); if (!( nout && nrrdCCValid(nin) )) { /* nvalP can be NULL */ biffAddf(NRRD, "%s: invalid args", me); airMopError(mop); return 1; } if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: initial copy failed", me); airMopError(mop); return 1; } maxid = nrrdCCMax(nin); lup = nrrdUILookup[nin->type]; ins = nrrdUIInsert[nin->type]; NN = nrrdElementNumber(nin); map = AIR_CALLOC(maxid+1, unsigned int); /* we do need it zeroed out */ if (!map) { biffAddf(NRRD, "%s: couldn't allocate internal LUT", me); airMopError(mop); return 1; } airMopAdd(mop, map, airFree, airMopAlways); for (I=0; Idata, I)] = 1; } numid = 0; for (jd=0; jd<=maxid; jd++) { numid += map[jd]; } if (nvalP) { if (!(*nvalP)) { *nvalP = nrrdNew(); } if (nrrdMaybeAlloc_va(*nvalP, nin->type, 1, AIR_CAST(size_t, numid))) { biffAddf(NRRD, "%s: couldn't allocate output value list", me); airMopError(mop); return 1; } airMopAdd(mop, nvalP, (airMopper)airSetNull, airMopOnError); airMopAdd(mop, *nvalP, (airMopper)nrrdNuke, airMopOnError); } id = 0; for (jd=0; jd<=maxid; jd++) { if (map[jd]) { map[jd] = id; if (nvalP) { ins((*nvalP)->data, id, jd); } id++; } } for (I=0; Idata, I, map[lup(nin->data, I)]); } if (nrrdContentSet_va(nout, func, nin, "")) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } /* basic info handled by nrrdCopy */ airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/nrrd/arith.c0000664000175000017500000007702712165631065016636 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ******** nrrdArithGamma() ** ** map the values in a nrrd through a power function; essentially: ** val = pow(val, 1/gamma), but this is after the val has been normalized ** to be in the range of 0.0 to 1.0 (assuming that the given min and ** max really are the full range of the values in the nrrd). Thus, ** the given min and max values are fixed points of this ** transformation. Using a negative gamma means that after the pow() ** function has been applied, the value is inverted with respect to ** min and max (like in xv). */ int nrrdArithGamma(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range, double gamma) { static const char me[]="nrrdArithGamma", func[]="gamma"; double val, min, max; size_t I, num; NrrdRange *range; airArray *mop; double (*lup)(const void *, size_t); double (*ins)(void *, size_t, double); if (!(nout && nin)) { /* _range can be NULL */ biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( AIR_EXISTS(gamma) )) { biffAddf(NRRD, "%s: gamma doesn't exist", me); return 1; } if (!( nrrdTypeBlock != nin->type && nrrdTypeBlock != nout->type )) { biffAddf(NRRD, "%s: can't deal with %s type", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: couldn't initialize by copy to output", me); return 1; } } mop = airMopNew(); if (_range) { range = nrrdRangeCopy(_range); nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); } else { range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeTrue); } airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); min = range->min; max = range->max; if (min == max) { /* this is stupid. We want min < max to avoid making NaNs */ max += 1; } lup = nrrdDLookup[nin->type]; ins = nrrdDInsert[nout->type]; gamma = 1/gamma; num = nrrdElementNumber(nin); if (gamma < 0.0) { gamma = -gamma; for (I=0; Idata, I); val = AIR_AFFINE(min, val, max, 0.0, 1.0); val = pow(val, gamma); val = AIR_AFFINE(1.0, val, 0.0, min, max); ins(nout->data, I, val); } } else { for (I=0; Idata, I); val = AIR_AFFINE(min, val, max, 0.0, 1.0); val = pow(val, gamma); val = AIR_AFFINE(0.0, val, 1.0, min, max); ins(nout->data, I, val); } } if (nrrdContentSet_va(nout, func, nin, "%g,%g,%g", min, max, gamma)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } if (nout != nin) { nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE); } /* basic info handled by nrrdCopy above */ airMopOkay(mop); return 0; } /* ---------------------------- unary -------------- */ static double _nrrdUnaryOpNegative(double a) {return -a;} static double _nrrdUnaryOpReciprocal(double a) {return 1.0/a;} static double _nrrdUnaryOpSin(double a) {return sin(a);} static double _nrrdUnaryOpCos(double a) {return cos(a);} static double _nrrdUnaryOpTan(double a) {return tan(a);} static double _nrrdUnaryOpAsin(double a) {return asin(a);} static double _nrrdUnaryOpAcos(double a) {return acos(a);} static double _nrrdUnaryOpAtan(double a) {return atan(a);} static double _nrrdUnaryOpExp(double a) {return exp(a);} static double _nrrdUnaryOpLog(double a) {return log(a);} static double _nrrdUnaryOpLog2(double a) {return log(a)/0.69314718;} static double _nrrdUnaryOpLog10(double a) {return log10(a);} /* This code for log1p and expm1 comes from http://www.plunk.org/~hatch/rightway.php which in turn references http://www.cs.berkeley.edu/~wkahan/Math128/Sumnfp.pdf from the great Kahan of IEEE 754 fame, but sadly that URL no longer works (though the Math128 directory is still there, as are other documents) */ static double _nrrdUnaryOpLog1p(double a) { double b; b = 1.0 + a; if (b == 1.0) { /* "a" was so close to zero that we had underflow when adding to 1, resulting in something that is exactly equal to 1. So, use the first term of Taylor expansion of log(x+1) around 0 == x */ return a; } /* else "a" was not so near zero; but GLK doesn't fully grasp the design of this expression */ return log(b)*a/(b-1); } static double _nrrdUnaryOpExpm1(double x) { double u; u = exp(x); if (u == 1.0) { /* "x" was so close to 0.0 that exp return exactly 1; subtracting 1 from this will give a constant for a range of x's. Instead, use the Taylor expansion of exp(x)-1 around 0 == x */ return x; } else if (u-1.0 == -1.0) { /* "x" was close enough to -inf that exp returned something so close to 0 that subtracting 1 resulted in exactly -1; return that */ return -1.0; } /* else "x" was neither near 0.0 or -inf, but GLK doesn't fully grasp the design of this expression */ return (u-1.0)*x/log(u); } static double _nrrdUnaryOpSqrt(double a) {return sqrt(a);} static double _nrrdUnaryOpCbrt(double a) {return airCbrt(a);} static double _nrrdUnaryOpErf(double a) {return airErf(a);} static double _nrrdUnaryOpNerf(double a) {return (1+airErf(a))/2;} static double _nrrdUnaryOpCeil(double a) {return ceil(a);} static double _nrrdUnaryOpFloor(double a) {return floor(a);} static double _nrrdUnaryOpRoundUp(double a) {return AIR_ROUNDUP(a);} static double _nrrdUnaryOpRoundDown(double a) {return AIR_ROUNDDOWN(a);} static double _nrrdUnaryOpAbs(double a) {return AIR_ABS(a);} static double _nrrdUnaryOpSgn(double a) { return (a < 0.0 ? -1 : (a > 0.0 ? 1 : 0));} static double _nrrdUnaryOpExists(double a) {return AIR_EXISTS(a);} static double _nrrdUnaryOpRand(double a) { AIR_UNUSED(a); return airDrandMT(); } static double _nrrdUnaryOpNormalRand(double a) { double v; AIR_UNUSED(a); airNormalRand(&v, NULL); return v; } static double _nrrdUnaryOpIf(double a) { return (a ? 1 : 0); } static double _nrrdUnaryOpZero(double a) { AIR_UNUSED(a); return 0.0; } static double _nrrdUnaryOpOne(double a) { AIR_UNUSED(a); return 1.0; } double (*_nrrdUnaryOp[NRRD_UNARY_OP_MAX+1])(double) = { NULL, _nrrdUnaryOpNegative, _nrrdUnaryOpReciprocal, _nrrdUnaryOpSin, _nrrdUnaryOpCos, _nrrdUnaryOpTan, _nrrdUnaryOpAsin, _nrrdUnaryOpAcos, _nrrdUnaryOpAtan, _nrrdUnaryOpExp, _nrrdUnaryOpLog, _nrrdUnaryOpLog2, _nrrdUnaryOpLog10, _nrrdUnaryOpLog1p, _nrrdUnaryOpExpm1, _nrrdUnaryOpSqrt, _nrrdUnaryOpCbrt, _nrrdUnaryOpErf, _nrrdUnaryOpNerf, _nrrdUnaryOpCeil, _nrrdUnaryOpFloor, _nrrdUnaryOpRoundUp, _nrrdUnaryOpRoundDown, _nrrdUnaryOpAbs, _nrrdUnaryOpSgn, _nrrdUnaryOpExists, _nrrdUnaryOpRand, _nrrdUnaryOpNormalRand, _nrrdUnaryOpIf, _nrrdUnaryOpZero, _nrrdUnaryOpOne }; int nrrdArithUnaryOp(Nrrd *nout, int op, const Nrrd *nin) { static const char me[]="nrrdArithUnaryOp"; size_t N, I; int size[NRRD_DIM_MAX]; double (*insert)(void *v, size_t I, double d), (*lookup)(const void *v, size_t I), (*uop)(double), val; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(NRRD, "%s: can't operate on type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (airEnumValCheck(nrrdUnaryOp, op)) { biffAddf(NRRD, "%s: unary op %d invalid", me, op); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s:", me); return 1; } } nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); uop = _nrrdUnaryOp[op]; N = nrrdElementNumber(nin); lookup = nrrdDLookup[nin->type]; insert = nrrdDInsert[nin->type]; for (I=0; Idata, I); insert(nout->data, I, uop(val)); } if (nrrdContentSet_va(nout, airEnumStr(nrrdUnaryOp, op), nin, "")) { biffAddf(NRRD, "%s:", me); return 1; } nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_ALL ^ (NRRD_BASIC_INFO_OLDMIN_BIT | NRRD_BASIC_INFO_OLDMAX_BIT)); return 0; } /* ---------------------------- binary -------------- */ static double _nrrdBinaryOpAdd(double a, double b) {return a + b;} static double _nrrdBinaryOpSubtract(double a, double b) {return a - b;} static double _nrrdBinaryOpMultiply(double a, double b) {return a * b;} static double _nrrdBinaryOpDivide(double a, double b) {return a / b;} static double _nrrdBinaryOpPow(double a, double b) {return pow(a,b);} static double _nrrdBinaryOpSgnPow(double a, double b) {return airSgnPow(a,b);} static double _nrrdBinaryOpFlippedSgnPow(double a, double b) {return airFlippedSgnPow(a,b);} static double _nrrdBinaryOpMod(double a, double b) { return AIR_MOD((int)a,(int)b);} static double _nrrdBinaryOpFmod(double a, double b) {return fmod(a,b);} static double _nrrdBinaryOpAtan2(double a, double b) {return atan2(a,b);} static double _nrrdBinaryOpMin(double a, double b) {return AIR_MIN(a,b);} static double _nrrdBinaryOpMax(double a, double b) {return AIR_MAX(a,b);} static double _nrrdBinaryOpLT(double a, double b) {return (a < b);} static double _nrrdBinaryOpLTE(double a, double b) {return (a <= b);} static double _nrrdBinaryOpGT(double a, double b) {return (a > b);} static double _nrrdBinaryOpGTE(double a, double b) {return (a >= b);} static double _nrrdBinaryOpCompare(double a, double b) { return (a < b ? -1 : (a > b ? 1 : 0));} static double _nrrdBinaryOpEqual(double a, double b) {return (a == b);} static double _nrrdBinaryOpNotEqual(double a, double b) {return (a != b);} static double _nrrdBinaryOpExists(double a, double b) {return (AIR_EXISTS(a) ? a : b);} static double _nrrdBinaryOpIf(double a, double b) {return (a ? a : b);} static double _nrrdBinaryOpNormalRandScaleAdd(double a, double b) { double v; airNormalRand(&v, NULL); return a + b*v; } static double _nrrdBinaryOpRicianRand(double a, double b) { double vr, vi, rr, ri; airNormalRand(&rr, &ri); vr = a + b*rr; vi = b*ri; return sqrt(vr*vr + vi*vi); } double (*_nrrdBinaryOp[NRRD_BINARY_OP_MAX+1])(double, double) = { NULL, _nrrdBinaryOpAdd, _nrrdBinaryOpSubtract, _nrrdBinaryOpMultiply, _nrrdBinaryOpDivide, _nrrdBinaryOpPow, _nrrdBinaryOpSgnPow, _nrrdBinaryOpFlippedSgnPow, _nrrdBinaryOpMod, _nrrdBinaryOpFmod, _nrrdBinaryOpAtan2, _nrrdBinaryOpMin, _nrrdBinaryOpMax, _nrrdBinaryOpLT, _nrrdBinaryOpLTE, _nrrdBinaryOpGT, _nrrdBinaryOpGTE, _nrrdBinaryOpCompare, _nrrdBinaryOpEqual, _nrrdBinaryOpNotEqual, _nrrdBinaryOpExists, _nrrdBinaryOpIf, _nrrdBinaryOpNormalRandScaleAdd, _nrrdBinaryOpRicianRand }; /* ******** nrrdArithBinaryOp ** ** this is a simplified version of nrrdArithIterBinaryOp, written after ** that, in a hurry, to operate directly on two nrrds, instead with ** the NrrdIter nonsense */ int nrrdArithBinaryOp(Nrrd *nout, int op, const Nrrd *ninA, const Nrrd *ninB) { static const char me[]="nrrdArithBinaryOp"; char *contA, *contB; size_t N, I, size[NRRD_DIM_MAX]; double (*ins)(void *v, size_t I, double d), (*lupA)(const void *v, size_t I), (*lupB)(const void *v, size_t I), (*bop)(double a, double b), valA, valB; if (!( nout && !nrrdCheck(ninA) && !nrrdCheck(ninB) )) { biffAddf(NRRD, "%s: NULL pointer or invalid args", me); return 1; } if (nrrdTypeBlock == ninA->type || nrrdTypeBlock == ninB->type) { biffAddf(NRRD, "%s: can't operate on type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!nrrdSameSize(ninA, ninB, AIR_TRUE)) { biffAddf(NRRD, "%s: size mismatch between arguments", me); return 1; } if (airEnumValCheck(nrrdBinaryOp, op)) { biffAddf(NRRD, "%s: binary op %d invalid", me, op); return 1; } nrrdAxisInfoGet_nva(ninA, nrrdAxisInfoSize, size); if (!( nout == ninA || nout == ninB)) { if (_nrrdMaybeAllocMaybeZero_nva(nout, ninA->type, ninA->dim, size, AIR_FALSE /* zero when no realloc */)) { biffAddf(NRRD, "%s: couldn't allocate output nrrd", me); return 1; } if (nrrdAxisInfoCopy(nout, ninA, NULL, NRRD_AXIS_INFO_NONE)) { biffAddf(NRRD, "%s:", me); return 1; } nrrdBasicInfoCopy(nout, ninA, (NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))); } nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_ALL ^ (NRRD_BASIC_INFO_OLDMIN_BIT | NRRD_BASIC_INFO_OLDMAX_BIT)); bop = _nrrdBinaryOp[op]; N = nrrdElementNumber(ninA); lupA = nrrdDLookup[ninA->type]; lupB = nrrdDLookup[ninB->type]; ins = nrrdDInsert[nout->type]; for (I=0; Idata, I); valB = lupB(ninB->data, I); ins(nout->data, I, bop(valA, valB)); } contA = _nrrdContentGet(ninA); contB = _nrrdContentGet(ninB); if (_nrrdContentSet_va(nout, airEnumStr(nrrdBinaryOp, op), contA, "%s", contB)) { biffAddf(NRRD, "%s:", me); free(contA); free(contB); return 1; } free(contA); free(contB); return 0; } int nrrdArithIterBinaryOpSelect(Nrrd *nout, int op, NrrdIter *inA, NrrdIter *inB, unsigned int which) { static const char me[]="nrrdArithIterBinaryOpSelect"; char *contA, *contB; size_t N, I, size[NRRD_DIM_MAX]; int type; double (*insert)(void *v, size_t I, double d), (*bop)(double a, double b), valA, valB; const Nrrd *nin; if (!(nout && inA && inB)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(nrrdBinaryOp, op)) { biffAddf(NRRD, "%s: binary op %d invalid", me, op); return 1; } if (!( 0 == which || 1 == which )) { biffAddf(NRRD, "%s: which %u not 0 or 1", me, which); return 1; } nin = (0 == which ? _NRRD_ITER_NRRD(inA) : _NRRD_ITER_NRRD(inB)); if (!nin) { biffAddf(NRRD, "%s: selected input %u is a fixed value", me, which); return 1; } type = nin->type; nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); if (_nrrdMaybeAllocMaybeZero_nva(nout, type, nin->dim, size, AIR_FALSE /* zero when no realloc */)) { biffAddf(NRRD, "%s: couldn't allocate output nrrd", me); return 1; } nrrdBasicInfoCopy(nout, nin, (NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))); nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_ALL ^ (NRRD_BASIC_INFO_OLDMIN_BIT | NRRD_BASIC_INFO_OLDMAX_BIT)); bop = _nrrdBinaryOp[op]; /* fprintf(stderr, "%s: inA->left = %d, inB->left = %d\n", me, (int)(inA->left), (int)(inB->left)); */ N = nrrdElementNumber(nin); insert = nrrdDInsert[type]; for (I=0; Idata, I, bop(valA, valB)); } contA = nrrdIterContent(inA); contB = nrrdIterContent(inB); if (_nrrdContentSet_va(nout, airEnumStr(nrrdBinaryOp, op), contA, "%s", contB)) { biffAddf(NRRD, "%s:", me); free(contA); free(contB); return 1; } if (nout != nin) { nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE); } free(contA); free(contB); return 0; } int nrrdArithIterBinaryOp(Nrrd *nout, int op, NrrdIter *inA, NrrdIter *inB) { static const char me[]="nrrdArithIterBinaryOp"; unsigned int which; if (!(nout && inA && inB)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } which = (_NRRD_ITER_NRRD(inA) ? 0 : (_NRRD_ITER_NRRD(inB) ? 1 : 2)); if (2 == which) { biffAddf(NRRD, "%s: can't operate on two fixed values", me); return 1; } if (nrrdArithIterBinaryOpSelect(nout, op, inA, inB, which)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ---------------------------- ternary -------------- */ static double _nrrdTernaryOpAdd(double a, double b, double c) { return a + b + c; } static double _nrrdTernaryOpMultiply(double a, double b, double c) { return a * b * c; } static double _nrrdTernaryOpMin(double a, double b, double c) { b = AIR_MIN(b, c); return AIR_MIN(a, b); } /* ** minsmooth(x, w, M) is like min(x,M), but starting at value M-w, values ** are lowered (via erf), so that the output is asymptotic to M */ static double _nrrdTernaryOpMinSmooth(double x, double width, double max) { double tran; tran = max - width; return (tran < max /* using the function as intended */ ? (x < tran ? x : airErf((x-tran)*0.886226925452758/(max - tran))*(max - tran) + tran) : AIR_MIN(x, max)); /* transition in wrong place; revert to simple max() */ } static double _nrrdTernaryOpMax(double a, double b, double c) { b = AIR_MAX(b, c); return AIR_MAX(a, b); } /* ** maxsmooth(m, w, x) is like max(m,x), but starting at value m+w, values ** are raised (via erf), so that the output is asymptotic to m */ static double _nrrdTernaryOpMaxSmooth(double min, double width, double x) { double tran; tran = min + width; return (min < tran /* using the function as intended */ ? (tran < x ? x : airErf((x-tran)*0.886226925452758/(min - tran))*(min - tran) + tran) : AIR_MAX(x, min)); /* transition in wrong place; revert to simple max() */ } static double _nrrdTernaryOpLTSmooth(double a, double w, double b) { return AIR_AFFINE(-1.0, airErf((b-a)/w), 1.0, 0.0, 1.0); } static double _nrrdTernaryOpGTSmooth(double a, double w, double b) { return AIR_AFFINE(-1.0, airErf((a-b)/w), 1.0, 0.0, 1.0); } static double _nrrdTernaryOpClamp(double a, double b, double c) { return AIR_CLAMP(a, b, c); } static double _nrrdTernaryOpIfElse(double a, double b, double c) { return (a ? b : c); } static double _nrrdTernaryOpLerp(double a, double b, double c) { /* we do something more than the simple lerp here because we want to facilitate usage as something which can get around non-existent values (b and c as NaN or Inf) without getting polluted by them. */ if (0.0 == a) { return b; } else if (1.0 == a) { return c; } else { return AIR_LERP(a, b, c); } } static double _nrrdTernaryOpExists(double a, double b, double c) { return (AIR_EXISTS(a) ? b : c); } static double _nrrdTernaryOpInOpen(double a, double b, double c) { return (AIR_IN_OP(a, b, c)); } static double _nrrdTernaryOpInClosed(double a, double b, double c) { return (AIR_IN_CL(a, b, c)); } static double _nrrdTernaryOpGaussian(double x, double mu, double sig) { return airGaussian(x, mu, sig); } static double _nrrdTernaryOpRician(double x, double mu, double sig) { return airRician(x, mu, sig); } double (*_nrrdTernaryOp[NRRD_TERNARY_OP_MAX+1])(double, double, double) = { NULL, _nrrdTernaryOpAdd, _nrrdTernaryOpMultiply, _nrrdTernaryOpMin, _nrrdTernaryOpMinSmooth, _nrrdTernaryOpMax, _nrrdTernaryOpMaxSmooth, _nrrdTernaryOpLTSmooth, _nrrdTernaryOpGTSmooth, _nrrdTernaryOpClamp, _nrrdTernaryOpIfElse, _nrrdTernaryOpLerp, _nrrdTernaryOpExists, _nrrdTernaryOpInOpen, _nrrdTernaryOpInClosed, _nrrdTernaryOpGaussian, _nrrdTernaryOpRician }; /* ******** nrrdArithTerneryOp ** ** HEY: UNTESTED UNTESTED UNTESTED UNTESTED UNTESTED UNTESTED UNTESTED ** ** this is a simplified version of nrrdArithIterTernaryOp, written after ** that, in a hurry, to operate directly on three nrrds, instead with ** the NrrdIter nonsense */ int nrrdArithTernaryOp(Nrrd *nout, int op, const Nrrd *ninA, const Nrrd *ninB, const Nrrd *ninC) { static const char me[]="nrrdArithTernaryOp"; char *contA, *contB, *contC; size_t N, I, size[NRRD_DIM_MAX]; double (*ins)(void *v, size_t I, double d), (*lupA)(const void *v, size_t I), (*lupB)(const void *v, size_t I), (*lupC)(const void *v, size_t I), (*top)(double a, double b, double c), valA, valB, valC; if (!( nout && !nrrdCheck(ninA) && !nrrdCheck(ninB) && !nrrdCheck(ninC) )) { biffAddf(NRRD, "%s: NULL pointer or invalid args", me); return 1; } if (!( nrrdSameSize(ninA, ninB, AIR_TRUE) && nrrdSameSize(ninA, ninC, AIR_TRUE) )) { biffAddf(NRRD, "%s: size mismatch between arguments", me); return 1; } if (airEnumValCheck(nrrdTernaryOp, op)) { biffAddf(NRRD, "%s: ternary op %d invalid", me, op); return 1; } nrrdAxisInfoGet_nva(ninA, nrrdAxisInfoSize, size); if (!( nout == ninA || nout == ninB || nout == ninC)) { if (_nrrdMaybeAllocMaybeZero_nva(nout, ninA->type, ninA->dim, size, AIR_FALSE /* zero when no realloc */)) { biffAddf(NRRD, "%s: couldn't allocate output nrrd", me); return 1; } if (nrrdAxisInfoCopy(nout, ninA, NULL, NRRD_AXIS_INFO_NONE)) { biffAddf(NRRD, "%s:", me); return 1; } nrrdBasicInfoCopy(nout, ninA, (NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))); } nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_ALL ^ (NRRD_BASIC_INFO_OLDMIN_BIT | NRRD_BASIC_INFO_OLDMAX_BIT)); top = _nrrdTernaryOp[op]; N = nrrdElementNumber(ninA); lupA = nrrdDLookup[ninA->type]; lupB = nrrdDLookup[ninB->type]; lupC = nrrdDLookup[ninC->type]; ins = nrrdDInsert[nout->type]; for (I=0; Idata, I); valB = lupB(ninB->data, I); valC = lupC(ninC->data, I); ins(nout->data, I, top(valA, valB, valC)); } contA = _nrrdContentGet(ninA); contB = _nrrdContentGet(ninB); contC = _nrrdContentGet(ninC); if (_nrrdContentSet_va(nout, airEnumStr(nrrdTernaryOp, op), contA, "%s,%s", contB, contC)) { biffAddf(NRRD, "%s:", me); free(contA); free(contB); free(contC); return 1; } free(contA); free(contB); free(contC); return 0; } int nrrdArithIterTernaryOpSelect(Nrrd *nout, int op, NrrdIter *inA, NrrdIter *inB, NrrdIter *inC, unsigned int which) { static const char me[]="nrrdArithIterTernaryOpSelect"; char *contA, *contB, *contC; size_t N, I, size[NRRD_DIM_MAX]; int type; double (*insert)(void *v, size_t I, double d), (*top)(double a, double b, double c), valA, valB, valC; const Nrrd *nin; if (!(nout && inA && inB && inC)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(nrrdTernaryOp, op)) { biffAddf(NRRD, "%s: ternary op %d invalid", me, op); return 1; } if (!( 0 == which || 1 == which || 2 == which )) { biffAddf(NRRD, "%s: which %u not valid, want 0, 1, or 2", me, which); return 1; } nin = (0 == which ? _NRRD_ITER_NRRD(inA) : (1 == which ? _NRRD_ITER_NRRD(inB) : _NRRD_ITER_NRRD(inC))); if (!nin) { biffAddf(NRRD, "%s: selected input %u is a fixed value", me, which); return 1; } type = nin->type; nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); if (_nrrdMaybeAllocMaybeZero_nva(nout, type, nin->dim, size, AIR_FALSE /* zero when no realloc */)) { biffAddf(NRRD, "%s: couldn't allocate output nrrd", me); return 1; } nrrdBasicInfoCopy(nout, nin, (NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))); nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_ALL ^ (NRRD_BASIC_INFO_OLDMIN_BIT | NRRD_BASIC_INFO_OLDMAX_BIT)); top = _nrrdTernaryOp[op]; /* fprintf(stderr, "%!s: inA->left = %d, inB->left = %d\n", me, (int)(inA->left), (int)(inB->left)); */ N = nrrdElementNumber(nin); insert = nrrdDInsert[type]; for (I=0; Idata, I, top(valA, valB, valC)); } contA = nrrdIterContent(inA); contB = nrrdIterContent(inB); contC = nrrdIterContent(inC); if (_nrrdContentSet_va(nout, airEnumStr(nrrdTernaryOp, op), contA, "%s,%s", contB, contC)) { biffAddf(NRRD, "%s:", me); free(contA); free(contB); free(contC); return 1; } if (nout != nin) { nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE); } free(contA); free(contB); free(contC); return 0; } int nrrdArithIterTernaryOp(Nrrd *nout, int op, NrrdIter *inA, NrrdIter *inB, NrrdIter *inC) { static const char me[]="nrrdArithIterTernaryOp"; unsigned int which; if (!(nout && inA && inB && inC)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } which = (_NRRD_ITER_NRRD(inA) ? 0 : (_NRRD_ITER_NRRD(inB) ? 1 : (_NRRD_ITER_NRRD(inC) ? 2 : 3 ))); if (3 == which) { biffAddf(NRRD, "%s: can't operate on 3 fixed values", me); return 1; } if (nrrdArithIterTernaryOpSelect(nout, op, inA, inB, inC, which)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } int nrrdArithAffine(Nrrd *nout, double minIn, const Nrrd *nin, double maxIn, double minOut, double maxOut, int clamp) { static const char me[]="nrrdArithAffine"; size_t I, N; double (*ins)(void *v, size_t I, double d), (*lup)(const void *v, size_t I); if ( !nout || nrrdCheck(nin) ) { biffAddf(NRRD, "%s: got NULL pointer or invalid input", me); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: couldn't initialize output", me); return 1; } } N = nrrdElementNumber(nin); ins = nrrdDInsert[nout->type]; lup = nrrdDLookup[nin->type]; for (I=0; Idata, I); val = AIR_AFFINE(minIn, val, maxIn, minOut, maxOut); if (clamp) { val = AIR_CLAMP(minOut, val, maxOut); } ins(nout->data, I, val); } /* HEY: it would be much better if the ordering here was the same as in AIR_AFFINE, but that's not easy with the way the content functions are now set up */ if (nrrdContentSet_va(nout, "affine", nin, "%g,%g,%g,%g", minIn, maxIn, minOut, maxOut)) { biffAddf(NRRD, "%s:", me); } return 0; } int nrrdArithIterAffine(Nrrd *nout, NrrdIter *minIn, NrrdIter *in, NrrdIter *maxIn, NrrdIter *minOut, NrrdIter *maxOut, int clamp) { static const char me[]="nrrdArithInterAffine"; double (*ins)(void *v, size_t I, double d), mini, vin, maxi, mino, maxo, vout; const Nrrd *nin; char *contA, *contB, *contC, *contD, *contE; size_t I, N; if (!(nout && minIn && in && maxIn && minOut && maxOut)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } nin = (_NRRD_ITER_NRRD(in) ? _NRRD_ITER_NRRD(in) : (_NRRD_ITER_NRRD(minIn) ? _NRRD_ITER_NRRD(minIn) : (_NRRD_ITER_NRRD(maxIn) ? _NRRD_ITER_NRRD(maxIn) : (_NRRD_ITER_NRRD(minOut) ? _NRRD_ITER_NRRD(minOut) : _NRRD_ITER_NRRD(maxOut))))); if (!nin) { biffAddf(NRRD, "%s: can't operate solely on fixed values", me); return 1; } if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: couldn't initialize output", me); return 1; } N = nrrdElementNumber(nin); ins = nrrdDInsert[nout->type]; for (I=0; Idata, I, vout); } contA = nrrdIterContent(in); contB = nrrdIterContent(minIn); contC = nrrdIterContent(maxIn); contD = nrrdIterContent(maxOut); contE = nrrdIterContent(maxOut); /* HEY: same annoyance about order of arguments as in function above */ if (_nrrdContentSet_va(nout, "affine", contA, "%s,%s,%s,%s", contB, contC, contD, contE)) { biffAddf(NRRD, "%s:", me); free(contA); free(contB); free(contC); free(contD); free(contE); return 1; } free(contA); free(contB); free(contC); free(contD); free(contE); return 0; } unsigned int nrrdCRC32(const Nrrd *nin, int endian) { size_t nn; /* NULL nrrd or data */ if (!nin || !(nin->data) || !(nn = nrrdElementSize(nin)*nrrdElementNumber(nin)) || airEnumValCheck(airEndian, endian)) { return 0; } return airCRC32(AIR_CAST(const unsigned char *, nin->data), nn, nrrdElementSize(nin), endian == airMyEndian() ? AIR_FALSE : AIR_TRUE); } teem-1.11.0~svn6057/src/nrrd/ccmethods.c0000664000175000017500000000746412165631065017476 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" int nrrdCCValid(const Nrrd *nin) { static const char me[]="nrrdCCValid"; if (nrrdCheck(nin)) { biffAddf(NRRD, "%s: basic validity check failed", me); return 0; } if (!( nrrdTypeIsIntegral[nin->type] )) { biffAddf(NRRD, "%s: need an integral type (not %s)", me, airEnumStr(nrrdType, nin->type)); return 0; } if (!( nrrdTypeSize[nin->type] <= 2 || nrrdTypeInt == nin->type || nrrdTypeUInt == nin->type )) { biffAddf(NRRD, "%s: valid connected component types are 1- and 2-byte " "integers, and %s and %s", me, airEnumStr(nrrdType, nrrdTypeInt), airEnumStr(nrrdType, nrrdTypeUInt)); return 0; } return 1; } /* ** things we could sensibly measure on CCs: ** - size ** - # neighbors (needs conny argument) ** - what else? */ unsigned int nrrdCCSize(Nrrd *nout, const Nrrd *nin) { static const char me[]="nrrdCCSize", func[]="ccsize"; unsigned int *out, maxid, (*lup)(const void *, size_t); size_t I, NN; if (!( nout && nrrdCCValid(nin) )) { biffAddf(NRRD, "%s: invalid args", me); return 1; } maxid = nrrdCCMax(nin); if (nrrdMaybeAlloc_va(nout, nrrdTypeUInt, 1, AIR_CAST(size_t, maxid+1))) { biffAddf(NRRD, "%s: can't allocate output", me); return 1; } out = (unsigned int *)(nout->data); lup = nrrdUILookup[nin->type]; NN = nrrdElementNumber(nin); for (I=0; Idata, I)] += 1; } if (nrrdContentSet_va(nout, func, nin, "")) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } /* ******** nrrdCCMax ** ** returns the highest CC ID, or 0 if there were problems ** ** does NOT use biff */ unsigned int nrrdCCMax(const Nrrd *nin) { unsigned int (*lup)(const void *, size_t), id, max; size_t I, NN; if (!nrrdCCValid(nin)) { return 0; } lup = nrrdUILookup[nin->type]; NN = nrrdElementNumber(nin); max = 0; for (I=0; Idata, I); max = AIR_MAX(max, id); } return max; } /* ******** nrrdCCNum ** ** returns the number of connected components (the # of CC IDs assigned) ** a return of 0 indicates an error */ unsigned int nrrdCCNum(const Nrrd *nin) { unsigned int (*lup)(const void *, size_t), num; size_t I, max, NN; unsigned char *hist; if (!nrrdCCValid(nin)) { return 0; } lup = nrrdUILookup[nin->type]; NN = nrrdElementNumber(nin); max = nrrdCCMax(nin); hist = (unsigned char *)calloc(max+1, sizeof(unsigned char)); if (!hist) { return 0; } for (I=0; Idata, I)] = 1; } num = 0; for (I=0; I<=max; I++) { num += hist[I]; } free(hist); return num; } teem-1.11.0~svn6057/src/nrrd/kernel.c0000664000175000017500000027354512202620612016776 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" /* ** summary of information about how the kernel parameter vector is set: numParm parm[0] parm[1] parm[2] nrrdKernelHann 2 scale cut-off nrrdKernelBlackman 2 scale cut-off nrrdKernelCatmullRom 0 nrrdKernelBSpline3 0 nrrdKernelBSpline3ApproxInverse 0 nrrdKernelBSpline5 0 nrrdKernelBSpline5ApproxInverse 0 nrrdKernelBSpline7 0 nrrdKernelBSpline7ApproxInverse 0 nrrdKernelZero 1 scale nrrdKernelBox 1 scale nrrdKernelCatmullRomSupportDebug 1 support nrrdKernelBoxSupportDebug 1 support nrrdKernelCos4SupportDebug 1 support nrrdKernelCheap 1 scale nrrdKernelHermiteScaleSpaceFlag 0 nrrdKernelTent 1 scale nrrdKernelForwDiff 1 scale nrrdKernelCentDiff 1 scale nrrdKernelBCCubic 3 scale B C nrrdKernelAQuartic 2 scale A nrrdKernelC3Quintic 0 nrrdKernelC4Hexic 0 nrrdKernelC4HexicApproxInverse 0 nrrdKernelC5Septic 0 nrrdKernelGaussian 2 sigma cut-off nrrdKernelDiscreteGaussian 2 sigma cut-off nrrdKernelTMF[][][] 1 a ** Note that when parm[0] is named "scale", that parameter is optional, ** and the default is 1.0, when given in string form ** E.g. "tent" is understood as "tent:1", ** but "gauss:4" isn't complete and won't parse; while "gauss:1,4" is good */ /* these functions replace what had been a lot of identical functions for similar kernels */ static double returnZero(const double *parm) { AIR_UNUSED(parm); return 0.0; } static double returnOne(const double *parm) { AIR_UNUSED(parm); return 1.0; } static double returnTwo(const double *parm) { AIR_UNUSED(parm); return 2.0; } static double returnThree(const double *parm) { AIR_UNUSED(parm); return 3.0; } static double returnFour(const double *parm) { AIR_UNUSED(parm); return 4.0; } /* ------------------------------------------------------------ */ /* learned: if you copy/paste the macros for these kernels into ** other code, you *have* to make sure that the arguments for the ** kernels that are supposed to be reals, are not passed as an ** integral type (had this problem trying to re-use _BCCUBIC ** with constant B="1" and C="0" and had trouble figuring out ** why the kernel was give garbage results */ /* the "zero" kernel is here more as a template than for anything else (as well as when you need the derivative of nrrdKernelForwDiff or any other piece-wise constant kernels) In particular the support method is pretty silly. */ #define _ZERO(x) 0 static double _nrrdZeroSup(const double *parm) { double S; S = parm[0]; return S; } static double _nrrdZero1_d(double x, const double *parm) { double S; S = parm[0]; x = AIR_ABS(x)/S; return _ZERO(x)/S; } static float _nrrdZero1_f(float x, const double *parm) { float S; S = AIR_CAST(float, parm[0]); x = AIR_ABS(x)/S; return _ZERO(x)/S; } static void _nrrdZeroN_d(double *f, const double *x, size_t len, const double *parm) { double S; double t; size_t i; S = parm[0]; for (i=0; i 0.5 ? 0 : (x < 0.5 ? 1 : 0.5)) static double _nrrdBoxSup(const double *parm) { double S; S = parm[0]; /* adding the 0.5 is to ensure that weights computed within the support really do catch all the non-zero values */ return S/2 + 0.5; } static double _nrrdBox1_d(double x, const double *parm) { double S; S = parm[0]; x = AIR_ABS(x)/S; return _BOX(x)/S; } static float _nrrdBox1_f(float x, const double *parm) { float S; S = AIR_CAST(float, parm[0]); x = AIR_ABS(x)/S; return AIR_CAST(float, _BOX(x)/S); } static void _nrrdBoxN_d(double *f, const double *x, size_t len, const double *parm) { double S; double t; size_t i; S = parm[0]; for (i=0; i 0.5 \ ? 0.0 \ : cos(AIR_PI*x)*cos(AIR_PI*x)*cos(AIR_PI*x)*cos(AIR_PI*x)) static double _nrrdCos4SDInt(const double *parm) { AIR_UNUSED(parm); return 3.0/8.0; } static double _nrrdCos4SDSup(const double *parm) { return parm[0]; } static double _nrrdCos4SD1_d(double x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return COS4(x); } static float _nrrdCos4SD1_f(float x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return AIR_CAST(float, COS4(x)); } static void _nrrdCos4SDN_d(double *f, const double *x, size_t len, const double *parm) { size_t i; AIR_UNUSED(parm); for (i=0; i 0.5 \ ? 0.0 \ : -4*AIR_PI*(cos(AIR_PI*x)*cos(AIR_PI*x)*cos(AIR_PI*x) \ *sin(AIR_PI*x))) static double _nrrdDCos4SDSup(const double *parm) { return parm[0]; } static double _nrrdDCos4SD1_d(double x, const double *parm) { int sgn; AIR_UNUSED(parm); if (x < 0) { x = -x; sgn = -1; } else { sgn = 1; } return sgn*DCOS4(x); } static float _nrrdDCos4SD1_f(float x, const double *parm) { int sgn; AIR_UNUSED(parm); if (x < 0) { x = -x; sgn = -1; } else { sgn = 1; } return AIR_CAST(float, sgn*DCOS4(x)); } static void _nrrdDCos4SDN_d(double *f, const double *x, size_t len, const double *parm) { int sgn; double t; size_t i; AIR_UNUSED(parm); for (i=0; i 0.5 \ ? 0.0 \ : -2*AIR_PI*AIR_PI*(cos(AIR_PI*2*x) + cos(AIR_PI*4*x))) static double _nrrdDDCos4SDSup(const double *parm) { return parm[0]; } static double _nrrdDDCos4SD1_d(double x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return DDCOS4(x); } static float _nrrdDDCos4SD1_f(float x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return AIR_CAST(float, DDCOS4(x)); } static void _nrrdDDCos4SDN_d(double *f, const double *x, size_t len, const double *parm) { size_t i; AIR_UNUSED(parm); for (i=0; i 0.5 \ ? 0.0 \ : 4*AIR_PI*AIR_PI*AIR_PI*(sin(2*AIR_PI*x) + 2*sin(4*AIR_PI*x))) static double _nrrdDDDCos4SDSup(const double *parm) { return parm[0]; } static double _nrrdDDDCos4SD1_d(double x, const double *parm) { int sgn; AIR_UNUSED(parm); if (x < 0) { x = -x; sgn = -1; } else { sgn = 1; } return sgn*DDDCOS4(x); } static float _nrrdDDDCos4SD1_f(float x, const double *parm) { int sgn; AIR_UNUSED(parm); if (x < 0) { x = -x; sgn = -1; } else { sgn = 1; } return AIR_CAST(float, sgn*DDDCOS4(x)); } static void _nrrdDDDCos4SDN_d(double *f, const double *x, size_t len, const double *parm) { int sgn; double t; size_t i; AIR_UNUSED(parm); for (i=0; i= 1 ? 0 : 1 - x) static double _nrrdTentSup(const double *parm) { double S; S = parm[0]; return S; } static double _nrrdTent1_d(double x, const double *parm) { double S; S = parm[0]; x = AIR_ABS(x)/S; return S ? _TENT(x)/S : x == 0; } static float _nrrdTent1_f(float x, const double *parm) { float S; S = AIR_CAST(float, parm[0]); x = AIR_ABS(x)/S; return S ? _TENT(x)/S : x == 0; } static void _nrrdTentN_d(double *f, const double *x, size_t len, const double *parm) { double S; double t; size_t i; S = parm[0]; for (i=0; i= 2.0 ? 0 : \ (x >= 1.0 \ ? (((-B/6 - C)*x + B + 5*C)*x -2*B - 8*C)*x + 4*B/3 + 4*C \ : ((2 - 3*B/2 - C)*x - 3 + 2*B + C)*x*x + 1 - B/3)) static double _nrrdBCSup(const double *parm) { double S; S = parm[0]; return 2*S; } static double _nrrdBC1_d(double x, const double *parm) { double S; double B, C; S = parm[0]; B = parm[1]; C = parm[2]; x = AIR_ABS(x)/S; return _BCCUBIC(x, B, C)/S; } static float _nrrdBC1_f(float x, const double *parm) { float B, C, S; S = AIR_CAST(float, parm[0]); B = AIR_CAST(float, parm[1]); C = AIR_CAST(float, parm[2]); x = AIR_ABS(x)/S; return _BCCUBIC(x, B, C)/S; } static void _nrrdBCN_d(double *f, const double *x, size_t len, const double *parm) { double S; double t, B, C; size_t i; S = parm[0]; B = parm[1]; C = parm[2]; for (i=0; i= 2.0 ? 0.0 : \ (x >= 1.0 \ ? ((-B/2 - 3*C)*x + 2*B + 10*C)*x -2*B - 8*C \ : ((6 - 9*B/2 - 3*C)*x - 6 + 4*B + 2*C)*x)) static double _nrrdDBCSup(const double *parm) { double S; S = parm[0]; return 2*S; } static double _nrrdDBC1_d(double x, const double *parm) { double S; double B, C; int sgn = 1; S = parm[0]; B = parm[1]; C = parm[2]; if (x < 0) { x = -x; sgn = -1; } x /= S; return sgn*_DBCCUBIC(x, B, C)/(S*S); } static float _nrrdDBC1_f(float x, const double *parm) { float B, C, S; int sgn = 1; S = AIR_CAST(float, parm[0]); B = AIR_CAST(float, parm[1]); C = AIR_CAST(float, parm[2]); if (x < 0) { x = -x; sgn = -1; } x /= S; return AIR_CAST(float, sgn*_DBCCUBIC(x, B, C)/(S*S)); } static void _nrrdDBCN_d(double *f, const double *x, size_t len, const double *parm) { double S; double t, B, C; size_t i; int sgn; S = parm[0]; B = parm[1]; C = parm[2]; for (i=0; i= 2.0 ? 0 : \ (x >= 1.0 \ ? (-B - 6*C)*x + 2*B + 10*C \ : (12 - 9*B - 6*C)*x - 6 + 4*B + 2*C )) static double _nrrdDDBCSup(const double *parm) { double S; S = parm[0]; return 2*S; } static double _nrrdDDBC1_d(double x, const double *parm) { double S; double B, C; S = parm[0]; B = parm[1]; C = parm[2]; x = AIR_ABS(x)/S; return _DDBCCUBIC(x, B, C)/(S*S*S); } static float _nrrdDDBC1_f(float x, const double *parm) { float B, C, S; S = AIR_CAST(float, parm[0]); B = AIR_CAST(float, parm[1]); C = AIR_CAST(float, parm[2]); x = AIR_ABS(x)/S; return _DDBCCUBIC(x, B, C)/(S*S*S); } static void _nrrdDDBCN_d(double *f, const double *x, size_t len, const double *parm) { double S; double t, B, C; size_t i; S = parm[0]; B = parm[1]; C = parm[2]; for (i=0; i= 3.0 ? 0 : \ (x >= 2.0 \ ? A*(-54 + x*(81 + x*(-45 + x*(11 - x)))) \ : (x >= 1.0 \ ? 4 - 6*A + x*(-10 + 25*A + x*(9 - 33*A \ + x*(-3.5 + 17*A + x*(0.5 - 3*A)))) \ : 1 + x*x*(-3 + 6*A + x*((2.5 - 10*A) + x*(-0.5 + 4*A)))))) static double _nrrdA4Sup(const double *parm) { double S; S = parm[0]; return 3*S; } static double _nrrdA41_d(double x, const double *parm) { double S; double A; S = parm[0]; A = parm[1]; x = AIR_ABS(x)/S; return _AQUARTIC(x, A)/S; } static float _nrrdA41_f(float x, const double *parm) { float A, S; S = AIR_CAST(float, parm[0]); A = AIR_CAST(float, parm[1]); x = AIR_ABS(x)/S; return AIR_CAST(float, _AQUARTIC(x, A)/S); } static void _nrrdA4N_d(double *f, const double *x, size_t len, const double *parm) { double S; double t, A; size_t i; S = parm[0]; A = parm[1]; for (i=0; i= 3.0 ? 0 : \ (x >= 2.0 \ ? A*(81 + x*(-90 + x*(33 - 4*x))) \ : (x >= 1.0 \ ? -10 + 25*A + x*(18 - 66*A + x*(-10.5 + 51*A + x*(2 - 12*A))) \ : x*(-6 + 12*A + x*(7.5 - 30*A + x*(-2 + 16*A)))))) static double _nrrdDA4Sup(const double *parm) { double S; S = parm[0]; return 3*S; } static double _nrrdDA41_d(double x, const double *parm) { double S; double A; int sgn = 1; S = parm[0]; A = parm[1]; if (x < 0) { x = -x; sgn = -1; } x /= S; return sgn*_DAQUARTIC(x, A)/(S*S); } static float _nrrdDA41_f(float x, const double *parm) { float A, S; int sgn = 1; S = AIR_CAST(float, parm[0]); A = AIR_CAST(float, parm[1]); if (x < 0) { x = -x; sgn = -1; } x /= S; return AIR_CAST(float, sgn*_DAQUARTIC(x, A)/(S*S)); } static void _nrrdDA4N_d(double *f, const double *x, size_t len, const double *parm) { double S; double t, A; size_t i; int sgn; S = parm[0]; A = parm[1]; for (i=0; i= 3.0 ? 0 : \ (x >= 2.0 \ ? A*(-90 + x*(66 - x*12)) \ : (x >= 1.0 \ ? 18 - 66*A + x*(-21 + 102*A + x*(6 - 36*A)) \ : -6 + 12*A + x*(15 - 60*A + x*(-6 + 48*A))))) static double _nrrdDDA4Sup(const double *parm) { double S; S = parm[0]; return 3*S; } static double _nrrdDDA41_d(double x, const double *parm) { double S; double A; S = parm[0]; A = parm[1]; x = AIR_ABS(x)/S; return _DDAQUARTIC(x, A)/(S*S*S); } static float _nrrdDDA41_f(float x, const double *parm) { float S, A; S = AIR_CAST(float, parm[0]); A = AIR_CAST(float, parm[1]); x = AIR_ABS(x)/S; return _DDAQUARTIC(x, A)/(S*S*S); } static void _nrrdDDA4N_d(double *f, const double *x, size_t len, const double *parm) { double S; double t, A; size_t i; S = parm[0]; A = parm[1]; for (i=0; i= 2.0 ? 0 : \ (x >= 1.0 \ ? 0.8 + x*x*(-2 + x*(2 + x*(-0.75 + x*0.1))) \ : 0.7 + x*x*(-1 + x*x*(0.75 - x*0.3)))) static double _c3quint1_d(double x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return _C3QUINTIC(x); } static float _c3quint1_f(float x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return AIR_CAST(float, _C3QUINTIC(x)); } static void _c3quintN_d(double *f, const double *x, size_t len, const double *parm) { double t; size_t i; AIR_UNUSED(parm); for (i=0; i= 2.0 ? 0 : \ (x >= 1.0 \ ? x*(-4 + x*(6 + x*(-3 + x*0.5))) \ : x*(-2 + x*x*(3 - x*1.5)))) static double _Dc3quint1_d(double x, const double *parm) { int sgn = 1; AIR_UNUSED(parm); if (x < 0) { x = -x; sgn = -1; } return sgn*_DC3QUINTIC(x); } static float _Dc3quint1_f(float x, const double *parm) { int sgn = 1; AIR_UNUSED(parm); if (x < 0) { x = -x; sgn = -1; } return AIR_CAST(float, sgn*_DC3QUINTIC(x)); } static void _Dc3quintN_d(double *f, const double *x, size_t len, const double *parm) { double t; size_t i; int sgn; AIR_UNUSED(parm); for (i=0; i= 2.0 ? 0 : \ (x >= 1.0 \ ? -4 + x*(12 + x*(-9 + x*2)) \ : -2 + x*x*(9 - x*6))) static double _DDc3quint1_d(double x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return _DDC3QUINTIC(x); } static float _DDc3quint1_f(float x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return AIR_CAST(float, _DDC3QUINTIC(x)); } static void _DDc3quintN_d(double *f, const double *x, size_t len, const double *parm) { double t; size_t i; AIR_UNUSED(parm); for (i=0; i= 3.0 \ ? 0 \ : (x >= 2.0 \ ? 1539.0/160.0 + x*(-189.0/8.0 + x*(747.0/32.0 + x*(-12.0 + x*(109.0/32.0 + x*(-61.0/120.0 + x/32.0))))) \ : (x >= 1.0 \ ? 3.0/160.0 + x*(35.0/8.0 + x*(-341.0/32.0 + x*(10.0 + x*(-147.0/32.0 + x*(25.0/24.0 - x*3.0/32.0))))) \ : 69.0/80.0 + x*x*(-23.0/16.0 + x*x*(19.0/16.0 + x*(-7.0/12.0 + x/16.0))) ))) static double _c4hex1_d(double x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return _C4HEXIC(x); } static float _c4hex1_f(float x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return AIR_CAST(float, _C4HEXIC(x)); } static void _c4hexN_d(double *f, const double *x, size_t len, const double *parm) { double t; size_t i; AIR_UNUSED(parm); for (i=0; i= 3.0 \ ? 0 \ : (x >= 2.0 \ ? -189.0/8.0 + x*(747.0/16.0 + x*(-36.0 + x*(109.0/8.0 + x*(-61.0/24.0 + x*(3.0/16.0))))) \ : (x >= 1.0 \ ? 35.0/8.0 + x*(-341.0/16.0 + x*(30 + x*(-147.0/8.0 + x*(125.0/24.0 + x*(-9.0/16.0))))) \ : x*(-23.0/8.0 + x*x*(19.0/4.0 + x*(-35.0/12.0 + x*(3.0/8.0)))) ))) static double _Dc4hex1_d(double x, const double *parm) { int sgn = 1; AIR_UNUSED(parm); if (x < 0) { x = -x; sgn = -1; } return sgn*_DC4HEXIC(x); } static float _Dc4hex1_f(float x, const double *parm) { int sgn = 1; AIR_UNUSED(parm); if (x < 0) { x = -x; sgn = -1; } return AIR_CAST(float, sgn*_DC4HEXIC(x)); } static void _Dc4hexN_d(double *f, const double *x, size_t len, const double *parm) { double t; size_t i; int sgn; AIR_UNUSED(parm); for (i=0; i= 3.0 \ ? 0 \ : (x >= 2.0 \ ? 747.0/16.0 + x*(-72.0 + x*(327.0/8.0 + x*(-61.0/6.0 + x*15.0/16.0))) \ : (x >= 1.0 \ ? -341.0/16.0 + x*(60 + x*(-441.0/8.0 + x*(125.0/6.0 - x*45.0/16.0))) \ : -23.0/8.0 + x*x*(57.0/4.0 + x*(-35.0/3.0 + x*(15.0/8.0))) ))) static double _DDc4hex1_d(double x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return _DDC4HEXIC(x); } static float _DDc4hex1_f(float x, const double *parm) { AIR_UNUSED(parm); x = AIR_ABS(x); return AIR_CAST(float, _DDC4HEXIC(x)); } static void _DDc4hexN_d(double *f, const double *x, size_t len, const double *parm) { double t; size_t i; AIR_UNUSED(parm); for (i=0; i= 3.0 \ ? 0 \ : (x >= 2.0 \ ? -72.0 + x*(327.0/4.0 + x*(-61.0/2.0 + 15*x/4)) \ : (x >= 1.0 \ ? 60 + x*(-441.0/4.0 + x*(125.0/2.0 - 45*x/4)) \ : x*(57.0/2.0 + x*(-35 + 15*x/2)) \ ))) static double _DDDc4hex1_d(double x, const double *parm) { int sgn = 1; AIR_UNUSED(parm); if (x < 0) { x = -x; sgn = -1; } return sgn*_DDDC4HEXIC(x); } static float _DDDc4hex1_f(float x, const double *parm) { int sgn = 1; AIR_UNUSED(parm); if (x < 0) { x = -x; sgn = -1; } return AIR_CAST(float, sgn*_DDDC4HEXIC(x)); } static void _DDDc4hexN_d(double *f, const double *x, size_t len, const double *parm) { double t; size_t i; int sgn; AIR_UNUSED(parm); for (i=0; i= sig*cut ? 0 \ : exp(-x*x/(2.0*sig*sig))/(sig*2.50662827463100050241)) static double _nrrdGInt(const double *parm) { double cut; cut = parm[1]; return airErf(cut/sqrt(2.0)); } static double _nrrdGSup(const double *parm) { double sig, cut; sig = parm[0]; cut = parm[1]; return sig*cut; } static double _nrrdG1_d(double x, const double *parm) { double sig, cut; sig = parm[0]; cut = parm[1]; x = AIR_ABS(x); return _GAUSS(x, sig, cut); } static float _nrrdG1_f(float x, const double *parm) { float sig, cut; sig = AIR_CAST(float, parm[0]); cut = AIR_CAST(float, parm[1]); x = AIR_ABS(x); return AIR_CAST(float, _GAUSS(x, sig, cut)); } static void _nrrdGN_d(double *f, const double *x, size_t len, const double *parm) { double sig, cut, t; size_t i; sig = parm[0]; cut = parm[1]; for (i=0; i 0 \ ? (xx > abscut \ ? 0 \ : airBesselInExpScaled(AIR_CAST(int, xx + 0.5), sig*sig)) \ : xx <= 0.5) /* the last line used to be AIR_MAX(0.5, (ret)), but the problem was that even for reasonable cutoffs, like sigma=6, there would be a sudden change in the non-zero values at the edge of the kernel with slowly increasing sigma. The real solution is to have a smarter way of determining where to cut-off this particular kernel, the discrete gaussian, when the values are at the low level one would expect with "sigma=6" when talking about a continuous Gaussian */ #define _DGABSCUT(ret, sig, cut) \ (ret) = 0.5 + ceil((sig)*(cut)); \ (ret) = AIR_MAX(2.5, (ret)) static double _nrrdDiscGaussianSup(const double *parm) { double ret; _DGABSCUT(ret, parm[0], parm[1]); return ret; } /* the functions are out of their usual definition order because we use the function evaluation to determine the integral, rather than trying to do it analytically */ static double _nrrdDiscGaussian1_d(double xx, const double *parm) { double sig, cut; sig = parm[0]; _DGABSCUT(cut, sig, parm[1]); xx = AIR_ABS(xx); return _DISCRETEGAUSS(xx, sig, cut); } static double _nrrdDiscGaussianInt(const double *parm) { double sum, cut; int ii, supp; _DGABSCUT(cut, parm[0], parm[1]); supp = AIR_CAST(int, cut); sum = 0.0; for (ii=-supp; ii<=supp; ii++) { sum += _nrrdDiscGaussian1_d(ii, parm); } return sum; } static float _nrrdDiscGaussian1_f(float xx, const double *parm) { double sig, cut; sig = parm[0]; _DGABSCUT(cut, sig, parm[1]); xx = AIR_ABS(xx); return AIR_CAST(float, _DISCRETEGAUSS(xx, sig, cut)); } static void _nrrdDiscGaussianN_d(double *f, const double *x, size_t len, const double *parm) { double sig, cut, tt; size_t ii; sig = parm[0]; _DGABSCUT(cut, sig, parm[1]); for (ii=0; ii= sig*cut ? 0 \ : -exp(-x*x/(2.0*sig*sig))*x/(sig*sig*sig*2.50662827463100050241)) static double _nrrdDGInt(const double *parm) { AIR_UNUSED(parm); return 0; } static double _nrrdDGSup(const double *parm) { double sig, cut; sig = parm[0]; cut = parm[1]; return sig*cut; } static double _nrrdDG1_d(double x, const double *parm) { double sig, cut; int sgn = 1; sig = parm[0]; cut = parm[1]; if (x < 0) { x = -x; sgn = -1; } return sgn*_DGAUSS(x, sig, cut); } static float _nrrdDG1_f(float x, const double *parm) { float sig, cut; int sgn = 1; sig = AIR_CAST(float, parm[0]); cut = AIR_CAST(float, parm[1]); if (x < 0) { x = -x; sgn = -1; } return AIR_CAST(float, sgn*_DGAUSS(x, sig, cut)); } static void _nrrdDGN_d(double *f, const double *x, size_t len, const double *parm) { double sig, cut, t; size_t i; int sgn; sig = parm[0]; cut = parm[1]; for (i=0; i= sig*cut ? 0 \ : exp(-x*x/(2.0*sig*sig))*(x*x-sig*sig) / \ (sig*sig*sig*sig*sig*2.50662827463100050241)) static double _nrrdDDGInt(const double *parm) { double sig, cut; sig = parm[0]; cut = parm[1]; return -0.79788456080286535587*cut*exp(-cut*cut/2)/(sig*sig); } static double _nrrdDDGSup(const double *parm) { double sig, cut; sig = parm[0]; cut = parm[1]; return sig*cut; } static double _nrrdDDG1_d(double x, const double *parm) { double sig, cut; sig = parm[0]; cut = parm[1]; x = AIR_ABS(x); return _DDGAUSS(x, sig, cut); } static float _nrrdDDG1_f(float x, const double *parm) { float sig, cut; sig = AIR_CAST(float, parm[0]); cut = AIR_CAST(float, parm[1]); x = AIR_ABS(x); return AIR_CAST(float, _DDGAUSS(x, sig, cut)); } static void _nrrdDDGN_d(double *f, const double *x, size_t len, const double *parm) { double sig, cut, t; size_t i; sig = parm[0]; cut = parm[1]; for (i=0; i a=0.0 */ parm[0] = 0.0; } else { biffAddf(NRRD, "%s: TMF kernels require 3 arguments D, C, A " "in the form tmf:D,C,A", me); airMopError(mop); return 1; } if (_nrrdKernelParseTMFInt(&tmfD, tmfStr[0]) || _nrrdKernelParseTMFInt(&tmfC, tmfStr[1]) || _nrrdKernelParseTMFInt(&tmfA, tmfStr[2])) { biffAddf(NRRD, "%s: problem parsing \"%s,%s,%s\" as D,C,A " "for TMF kernel", me, tmfStr[0], tmfStr[1], tmfStr[2]); airMopError(mop); return 1; } if (!AIR_IN_CL(-1, tmfD, (int)nrrdKernelTMF_maxD)) { biffAddf(NRRD, "%s: derivative value %d outside range [-1,%d]", me, tmfD, nrrdKernelTMF_maxD); airMopError(mop); return 1; } if (!AIR_IN_CL(-1, tmfC, (int)nrrdKernelTMF_maxC)) { biffAddf(NRRD, "%s: continuity value %d outside range [-1,%d]", me, tmfC, nrrdKernelTMF_maxC); airMopError(mop); return 1; } if (!AIR_IN_CL(1, tmfA, (int)nrrdKernelTMF_maxA)) { biffAddf(NRRD, "%s: accuracy value %d outside range [1,%d]", me, tmfA, nrrdKernelTMF_maxA); airMopError(mop); return 1; } /* fprintf(stderr, "!%s: D,C,A = %d,%d,%d --> %d,%d,%d\n", me, tmfD, tmfC, tmfA, tmfD+1, tmfC+1, tmfA); */ *kernelP = nrrdKernelTMF[tmfD+1][tmfC+1][tmfA]; } else { /* its not a TMF */ if (!(*kernelP = _nrrdKernelStrToKern(kstr))) { biffAddf(NRRD, "%s: kernel \"%s\" not recognized", me, kstr); airMopError(mop); return 1; } if ((*kernelP)->numParm > NRRD_KERNEL_PARMS_NUM) { biffAddf(NRRD, "%s: kernel \"%s\" requests %d parameters > max %d", me, kstr, (*kernelP)->numParm, NRRD_KERNEL_PARMS_NUM); airMopError(mop); return 1; } if (*kernelP == nrrdKernelGaussian || *kernelP == nrrdKernelGaussianD || *kernelP == nrrdKernelGaussianDD || *kernelP == nrrdKernelDiscreteGaussian || *kernelP == nrrdKernelBoxSupportDebug || *kernelP == nrrdKernelCos4SupportDebug || *kernelP == nrrdKernelCos4SupportDebugD || *kernelP == nrrdKernelCos4SupportDebugDD || *kernelP == nrrdKernelCos4SupportDebugDDD) { /* for these kernels, we need all the parameters given explicitly */ needParm = (*kernelP)->numParm; } else { /* For everything else (note that TMF kernels are handled separately), we can make do with one less than the required, by using the default spacing */ needParm = ((*kernelP)->numParm > 0 ? (*kernelP)->numParm - 1 : 0); } if (needParm > 0 && !pstr) { biffAddf(NRRD, "%s: didn't get any of %d required doubles after " "colon in \"%s\"", me, needParm, kstr); airMopError(mop); return 1; } for (haveParm=0; haveParm<(*kernelP)->numParm; haveParm++) { if (!pstr) break; if (1 != sscanf(pstr, "%lg", parm+haveParm)) { biffAddf(NRRD, "%s: trouble parsing \"%s\" as double (in \"%s\")", me, _pstr, _str); airMopError(mop); return 1; } if ((pstr = strchr(pstr, ','))) { pstr++; if (!*pstr) { biffAddf(NRRD, "%s: nothing after last comma in \"%s\" (in \"%s\")", me, _pstr, _str); airMopError(mop); return 1; } } } /* haveParm is now the number of parameters that were parsed. */ if (haveParm < needParm) { biffAddf(NRRD, "%s: parsed only %d of %d required doubles " "from \"%s\" (in \"%s\")", me, haveParm, needParm, _pstr, _str); airMopError(mop); return 1; } else if (haveParm == needParm && needParm == (*kernelP)->numParm-1) { /* shift up parsed values, and set parm[0] to default */ for (jj=haveParm; jj>=1; jj--) { parm[jj] = parm[jj-1]; } parm[0] = nrrdDefaultKernelParm0; } else { if (pstr) { biffAddf(NRRD, "%s: \"%s\" (in \"%s\") has more than %d doubles", me, _pstr, _str, (*kernelP)->numParm); airMopError(mop); return 1; } } } /* fprintf(stderr, "%s: %g %g %g %g %g\n", me, parm[0], parm[1], parm[2], parm[3], parm[4]); */ airMopOkay(mop); return 0; } int nrrdKernelSpecParse(NrrdKernelSpec *ksp, const char *str) { static const char me[]="nrrdKernelSpecParse"; const NrrdKernel *kern; double kparm[NRRD_KERNEL_PARMS_NUM]; if (!( ksp && str )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdKernelParse(&kern, kparm, str)) { biffAddf(NRRD, "%s: ", me); return 1; } nrrdKernelSpecSet(ksp, kern, kparm); return 0; } /* ** note that the given string has to be allocated for a certain size ** which is plenty big */ int nrrdKernelSpecSprint(char str[AIR_STRLEN_LARGE], const NrrdKernelSpec *ksp) { static const char me[]="nrrdKernelSpecSprint"; unsigned int warnLen = AIR_STRLEN_LARGE/3; char stmp[AIR_STRLEN_LARGE]; if (!( str && ksp )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (strlen(ksp->kernel->name) > warnLen) { biffAddf(NRRD, "%s: kernel name (len %s) might lead to overflow", me, airSprintSize_t(stmp, strlen(ksp->kernel->name))); return 1; } if (strstr(ksp->kernel->name, "TMF")) { /* these are handled differently; the identification of the kernel is actually packaged as kernel parameters */ if (!(ksp->kernel->name == strstr(ksp->kernel->name, "TMF"))) { biffAddf(NRRD, "%s: TMF kernel name %s didn't start with TMF", me, ksp->kernel->name); return 1; } /* 0123456789012 */ /* TMF_dX_cX_Xef */ if (!( 13 == strlen(ksp->kernel->name) && '_' == ksp->kernel->name[3] && '_' == ksp->kernel->name[6] && '_' == ksp->kernel->name[9] )) { biffAddf(NRRD, "%s: sorry, expected strlen(%s) = 13 with 3 _s", me, ksp->kernel->name); return 1; } sprintf(str, "tmf:%c,%c,%c", ksp->kernel->name[5], ksp->kernel->name[8], ksp->kernel->name[10]); /* see if the single parm should be added on */ if (0.0 != ksp->parm[0]) { sprintf(stmp, ",%.17g", ksp->parm[0]); strcat(str, stmp); } } else { strcpy(str, ksp->kernel->name); if (ksp->kernel->numParm) { unsigned int pi; for (pi=0; pikernel->numParm; pi++) { sprintf(stmp, "%c%.17g", (!pi ? ':' : ','), ksp->parm[pi]); if (strlen(str) + strlen(stmp) > warnLen) { biffAddf(NRRD, "%s: kernel parm %u could overflow", me, pi); return 1; } strcat(str, stmp); } } } return 0; } int nrrdKernelSprint(char str[AIR_STRLEN_LARGE], const NrrdKernel *kernel, const double kparm[NRRD_KERNEL_PARMS_NUM]) { static const char me[]="nrrdKernelSprint"; NrrdKernelSpec ksp; nrrdKernelSpecSet(&ksp, kernel, kparm); if (nrrdKernelSpecSprint(str, &ksp)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } int nrrdKernelCompare(const NrrdKernel *kernA, const double parmA[NRRD_KERNEL_PARMS_NUM], const NrrdKernel *kernB, const double parmB[NRRD_KERNEL_PARMS_NUM], int *differ, char explain[AIR_STRLEN_LARGE]) { static const char me[]="nrrdKernelCompare"; unsigned int pnum, pidx; if (!(kernA && kernB && differ)) { biffAddf(NRRD, "%s: got NULL pointer (%p, %p, or %p)", me, AIR_CVOIDP(kernA), AIR_CVOIDP(kernB), AIR_VOIDP(differ)); return 1; } if (kernA != kernB) { *differ = kernA < kernB ? -1 : 1; if (explain) { sprintf(explain, "kernA %s kernB", *differ < 0 ? "<" : ">"); } return 0; } pnum = kernA->numParm; if (!pnum) { /* actually, we're done: no parms and kernels are equal */ *differ = 0; return 0; } if (!(parmA && parmB)) { biffAddf(NRRD, "%s: kernel %s needs %u parms but got NULL parm vectors", me, kernA->name ? kernA->name : "(unnamed)", pnum); return 0; } for (pidx=0; pidx", pidx, parmB[pidx]); } return 0; } } /* so far nothing unequal */ *differ = 0; return 0; } /* ******** nrrdKernelCheck ** ** Makes sure a given kernel is behaving as expected */ int nrrdKernelCheck(const NrrdKernel *kern, const double parm[NRRD_KERNEL_PARMS_NUM], size_t evalNum, double epsilon, unsigned int diffOkEvalMax, unsigned int diffOkIntglMax, const NrrdKernel *ikern, const double iparm[NRRD_KERNEL_PARMS_NUM]) { const NrrdKernel *parsedkern; double parsedparm[NRRD_KERNEL_PARMS_NUM], supp, integral; static const char me[]="nrrdKernelCheck"; char kstr[AIR_STRLEN_LARGE], explain[AIR_STRLEN_LARGE], stmp[AIR_STRLEN_SMALL]; int differ; size_t evalIdx; double *dom_d, *ran_d, wee; float *dom_f, *ran_f; unsigned int diffOkEvalNum, diffOkIntglNum; airArray *mop; if (!kern) { biffAddf(NRRD, "%s: got NULL kernel", me); return 1; } if (!(evalNum > 20)) { biffAddf(NRRD, "%s: need evalNum > 20", me); return 1; } if (!(kern->name && kern->support && kern->integral && kern->eval1_f && kern->evalN_f && kern->eval1_d && kern->evalN_d)) { biffAddf(NRRD, "%s: kernel has NULL fields (%d,%d,%d,%d,%d,%d,%d)", me, !!(kern->name), !!(kern->support), !!(kern->integral), !!(kern->eval1_f), !!(kern->evalN_f), !!(kern->eval1_d), !!(kern->evalN_d)); return 0; } if (nrrdKernelSprint(kstr, kern, parm)) { biffAddf(NRRD, "%s: trouble", me); return 1; } if (nrrdKernelParse(&parsedkern, parsedparm, kstr)) { biffAddf(NRRD, "%s: trouble parsing |%s| back to kern/parm pair", me, kstr); return 1; } if (nrrdKernelCompare(kern, parm, parsedkern, parsedparm, &differ, explain)) { biffAddf(NRRD, "%s: trouble comparing", me); return 1; } if (differ) { biffAddf(NRRD, "%s: given and re-parsed kernels differ: %s", me, explain); return 1; } supp = kern->support(parm); /* wee is the step between evaluation points */ wee = 2*supp/AIR_CAST(double, evalNum); if ( (kern->eval1_d)(supp+wee/1000, parm) || (kern->eval1_d)(supp+wee, parm) || (kern->eval1_d)(supp+10*wee, parm) || (kern->eval1_d)(-supp-wee/1000, parm) || (kern->eval1_d)(-supp-wee, parm) || (kern->eval1_d)(-supp-10*wee, parm) ) { if (nrrdKernelCheap != kern) { /* the "cheap" kernel alone gets a pass on reporting its support */ biffAddf(NRRD, "%s: kern %s is non-zero outside support %g", me, kstr, supp); return 1; } } mop = airMopNew(); /* allocate domain and range for both float and double */ dom_d = AIR_CALLOC(evalNum, double); airMopAdd(mop, dom_d, airFree, airMopAlways); ran_d = AIR_CALLOC(evalNum, double); airMopAdd(mop, ran_d, airFree, airMopAlways); dom_f = AIR_CALLOC(evalNum, float); airMopAdd(mop, dom_f, airFree, airMopAlways); ran_f = AIR_CALLOC(evalNum, float); airMopAdd(mop, ran_f, airFree, airMopAlways); if (!( dom_d && ran_d && dom_f && ran_f )) { biffAddf(NRRD, "%s: couldn't alloc buffers for %s values for %s", me, airSprintSize_t(stmp, evalNum), kstr); airMopError(mop); return 1; } for (evalIdx=0; evalIdxevalN_f(ran_f, dom_f, evalNum, parm); kern->evalN_d(ran_d, dom_d, evalNum, parm); /* for (evalIdx=0; evalIdx %g\n", AIR_CAST(unsigned int, evalIdx), dom_d[evalIdx], ran_d[evalIdx]); } */ /* compare evaluations (and maybe derivatives) and numerically compute integral */ diffOkEvalNum = 0; diffOkIntglNum = 0; integral = 0.0; for (evalIdx=0; evalIdxeval1_f(dom_f[evalIdx], parm); single_d = kern->eval1_d(dom_d[evalIdx], parm); integral += single_d; /* single float vs vector float */ if (nrrdKernelForwDiff == kern || nrrdKernelBCCubic == kern || nrrdKernelBCCubicDD == kern || nrrdKernelAQuarticDD == kern) { /* HEY this is crazy: need a special epsilon for these kernels; WHY WHY do these kernels evaluate to different things in the single versus the vector case? */ float specEps; if (nrrdKernelForwDiff == kern) { specEps = 5e-9; } else if (nrrdKernelBCCubic == kern) { specEps = 5e-8; } else if (nrrdKernelBCCubicDD == kern) { specEps = 5e-8; } else if (nrrdKernelAQuarticDD == kern) { specEps = 5e-8; } else { specEps = 0.0; } if (fabs(single_f - ran_f[evalIdx]) > specEps) { biffAddf(NRRD, "%s: %s (eval1_f(%.17g)=%.17g) != " "(evalN_f(%.17g)=%.17g) by %.17g > %.17g", me, kstr, dom_f[evalIdx], single_f, dom_f[evalIdx], ran_f[evalIdx], fabs(single_f - ran_f[evalIdx]), specEps); airMopError(mop); return 1; } } else { if (single_f != ran_f[evalIdx]) { biffAddf(NRRD, "%s: %s (eval1_f(%.17g)=%.17g) != " "(evalN_f(%.17g)=%.17g)", me, kstr, dom_f[evalIdx], single_f, dom_f[evalIdx], ran_f[evalIdx]); airMopError(mop); return 1; } } /* single double vs vector double */ if (single_d != ran_d[evalIdx]) { biffAddf(NRRD, "%s: %s (eval1_d(%.17g)=%.17g) != (evalN_d(%.17g)=%.17g)", me, kstr, dom_d[evalIdx], single_d, dom_d[evalIdx], ran_d[evalIdx]); airMopError(mop); return 1; } /* single float vs single double */ if (fabs(single_f - single_d) > epsilon) { diffOkEvalNum++; if (diffOkEvalNum > diffOkEvalMax) { biffAddf(NRRD, "%s: %s |eval1_f(%.17g)=%.17g) - (eval1_d(%.17g)=%.17g)|" " %.17g > epsilon %.17g too many times (%u > %u)", me, kstr, dom_f[evalIdx], single_f, dom_d[evalIdx], single_d, fabs(single_f - single_d), epsilon, diffOkEvalNum, diffOkEvalMax); airMopError(mop); return 1; } } /* check whether we're the derivative of ikern */ if (ikern) { double forw, back, ndrv; forw = ikern->eval1_d(dom_d[evalIdx] + wee/2, iparm); back = ikern->eval1_d(dom_d[evalIdx] - wee/2, iparm); ndrv = (forw - back)/wee; if (fabs(ndrv - single_d) > epsilon) { diffOkIntglNum++; if (diffOkIntglNum > diffOkIntglMax) { biffAddf(NRRD, "%s: %s(%.17g) |num deriv(%s) %.17g - %.17g| " "%.17g > %.17g too many times (%u > %u)", me, kstr, dom_d[evalIdx], ikern->name, ndrv, single_d, fabs(ndrv - single_d), epsilon, diffOkIntglNum, diffOkIntglMax); airMopError(mop); return 1; } } } } integral *= 2*supp/(AIR_CAST(double, evalNum)); /* the "cheap" kernel alone gets a pass on reporting its integral */ if (nrrdKernelCheap != kern) { double hackeps=10; /* hackeps is clearly a hack to permit the integral to have greater error than any single evaluation; there must be a more principled way to set this */ if (fabs(integral - kern->integral(parm)) > hackeps*epsilon) { biffAddf(NRRD, "%s: %s |numerical integral %.17g - claimed %.17g| " "%.17g > %.17g", me, kstr, integral, kern->integral(parm), fabs(integral - kern->integral(parm)), hackeps*epsilon); airMopError(mop); return 1; } } /* HEY check being derivative of ikern/iparm */ AIR_UNUSED(ikern); AIR_UNUSED(iparm); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/nrrd/superset.c0000664000175000017500000004534112165631065017373 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ******** nrrdSplice() ** ** (opposite of nrrdSlice): replaces one slice of a nrrd with ** another nrrd. Will allocate memory for output only if nout != nin. */ int nrrdSplice(Nrrd *nout, const Nrrd *nin, const Nrrd *nslice, unsigned int axis, size_t pos) { static const char me[]="nrrdSplice", func[]="splice"; size_t I, rowLen, /* length of segment */ colStep, /* distance between start of each segment */ colLen; /* number of periods */ unsigned int ai; char *src, *dest, *sliceCont; char stmp[2][AIR_STRLEN_SMALL]; if (!(nin && nout && nslice)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nslice) { biffAddf(NRRD, "%s: nout==nslice disallowed", me); return 1; } /* check that desired slice location is legit */ if (!( axis < nin->dim )) { biffAddf(NRRD, "%s: slice axis %d out of bounds (0 to %d)", me, axis, nin->dim-1); return 1; } if (!( pos < nin->axis[axis].size )) { biffAddf(NRRD, "%s: position %s out of bounds (0 to %s)", me, airSprintSize_t(stmp[0], pos), airSprintSize_t(stmp[1], nin->axis[axis].size-1)); return 1; } /* check that slice will fit in nin */ if (nrrdCheck(nslice) || nrrdCheck(nin)) { biffAddf(NRRD, "%s: input or slice not valid nrrd", me); return 1; } if (!( nin->dim-1 == nslice->dim )) { biffAddf(NRRD, "%s: dim of slice (%d) not one less than " "dim of input (%d)", me, nslice->dim, nin->dim); return 1; } if (!( nin->type == nslice->type )) { biffAddf(NRRD, "%s: type of slice (%s) != type of input (%s)", me, airEnumStr(nrrdType, nslice->type), airEnumStr(nrrdType, nin->type)); return 1; } if (nrrdTypeBlock == nin->type) { if (!( nin->blockSize == nslice->blockSize )) { biffAddf(NRRD, "%s: input's blockSize (%s) != subvolume's (%s)", me, airSprintSize_t(stmp[0], nin->blockSize), airSprintSize_t(stmp[1], nslice->blockSize)); return 1; } } for (ai=0; aidim; ai++) { if (!( nin->axis[ai + (ai >= axis)].size == nslice->axis[ai].size )) { biffAddf(NRRD, "%s: input ax %d size (%s) != slices ax %d size (%s)", me, ai + (ai >= axis), airSprintSize_t(stmp[0], nin->axis[ai + (ai >= axis)].size), ai, airSprintSize_t(stmp[1], nslice->axis[ai].size)); return 1; } } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s:", me); return 1; } } /* else we're going to splice in place */ /* the following was copied from nrrdSlice() */ /* set up control variables */ rowLen = colLen = 1; for (ai=0; aidim; ai++) { if (ai < axis) { rowLen *= nin->axis[ai].size; } else if (ai > axis) { colLen *= nin->axis[ai].size; } } rowLen *= nrrdElementSize(nin); colStep = rowLen*nin->axis[axis].size; /* the skinny */ src = (char *)nout->data; /* switched src,dest from nrrdSlice() */ dest = (char *)nslice->data; src += rowLen*pos; for (I=0; Idim == nsub->dim )) { biffAddf(NRRD, "%s: input's dim (%d) != subvolume's dim (%d)", me, nin->dim, nsub->dim); return 1; } if (!( nin->type == nsub->type )) { biffAddf(NRRD, "%s: input's type (%s) != subvolume's type (%s)", me, airEnumStr(nrrdType, nin->type), airEnumStr(nrrdType, nsub->type)); return 1; } if (nrrdTypeBlock == nin->type) { if (!( nin->blockSize == nsub->blockSize )) { biffAddf(NRRD, "%s: input's blockSize (%s) != subvolume's (%s)", me, airSprintSize_t(stmp[0], nin->blockSize), airSprintSize_t(stmp[1], nsub->blockSize)); return 1; } } for (ai=0; aidim; ai++) { if (!( min[ai] + nsub->axis[ai].size - 1 <= nin->axis[ai].size - 1)) { biffAddf(NRRD, "%s: axis %d range of inset indices [%s,%s] not within " "input indices [0,%s]", me, ai, airSprintSize_t(stmp[0], min[ai]), airSprintSize_t(stmp[1], min[ai] + nsub->axis[ai].size - 1), airSprintSize_t(stmp[2], nin->axis[ai].size - 1)); return 1; } } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s:", me); return 1; } } /* else we're going to inset in place */ /* WARNING: following code copied/modified from nrrdCrop(), so the meanings of "in"/"out", "src"/"dest" are all messed up */ nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, szIn); nrrdAxisInfoGet_nva(nsub, nrrdAxisInfoSize, szOut); numLines = 1; for (ai=1; aidim; ai++) { numLines *= szOut[ai]; } lineSize = szOut[0]*nrrdElementSize(nin); /* the skinny */ typeSize = nrrdElementSize(nin); dataIn = (char *)nout->data; dataOut = (char *)nsub->data; for (ai=0; aidim; ai++) { cIn[ai] = cOut[ai] + min[ai]; } NRRD_INDEX_GEN(idxOut, cOut, szOut, nin->dim); NRRD_INDEX_GEN(idxIn, cIn, szIn, nin->dim); memcpy(dataIn + idxIn*typeSize, dataOut + idxOut*typeSize, lineSize); /* the lowest coordinate in cOut[] will stay zero, since we are copying one (1-D) scanline at a time */ NRRD_COORD_INCR(cOut, szOut, nin->dim, 1); } /* HEY: before Teem version 2.0 figure out nrrdKind stuff here */ strcpy(buff1, "["); for (ai=0; aidim; ai++) { sprintf(buff2, "%s%s", (ai ? "," : ""), airSprintSize_t(stmp[0], min[ai])); strcat(buff1, buff2); } strcat(buff1, "]"); subCont = _nrrdContentGet(nsub); if (nrrdContentSet_va(nout, func, nin, "%s,%s", subCont, buff1)) { biffAddf(NRRD, "%s:", me); free(subCont); return 1; } free(subCont); /* basic info copied by nrrdCopy above */ return 0; } #define MIRROR(N, I, M) \ M = (I < 0 ? -I : I); \ M = M % (2*N); \ M = (M >= N \ ? 2*N - 1 - M \ : M) \ size_t _nrrdMirror_64(size_t N, ptrdiff_t I) { size_t M; M = (I < 0 ? -I : I); M = M % (2*N); M = (M >= N ? 2*N - 1 - M : M); return M; } unsigned int _nrrdMirror_32(unsigned int N, int I) { unsigned int M; M = (I < 0 ? -I : I); M = M % (2*N); M = (M >= N ? 2*N - 1 - M : M); return M; } /* ******** nrrdPad_va() ** ** strictly for padding */ int nrrdPad_va(Nrrd *nout, const Nrrd *nin, const ptrdiff_t *min, const ptrdiff_t *max, int boundary, ...) { static const char me[]="nrrdPad_va", func[]="pad"; char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_MED]; double padValue=AIR_NAN; int outside; /* whether current sample in output has any coordinates that are outside the input volume (this is per-sample, not per-axis) */ unsigned int ai; ptrdiff_t cIn[NRRD_DIM_MAX]; /* coords for line start, in input */ size_t typeSize, idxIn, idxOut, /* linear indices for input and output */ numOut, /* number of elements in output nrrd */ szIn[NRRD_DIM_MAX], szOut[NRRD_DIM_MAX], cOut[NRRD_DIM_MAX]; /* coords for line start, in output */ va_list ap; char *dataIn, *dataOut; char stmp[2][AIR_STRLEN_SMALL]; if (!(nout && nin && min && max)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (!AIR_IN_OP(nrrdBoundaryUnknown, boundary, nrrdBoundaryLast)) { biffAddf(NRRD, "%s: boundary behavior %d invalid", me, boundary); return 1; } if (nrrdBoundaryWeight == boundary) { biffAddf(NRRD, "%s: boundary strategy %s not applicable here", me, airEnumStr(nrrdBoundary, boundary)); return 1; } if (nrrdTypeBlock == nin->type && nrrdBoundaryPad == boundary) { biffAddf(NRRD, "%s: with nrrd type %s, boundary %s not valid", me, airEnumStr(nrrdType, nrrdTypeBlock), airEnumStr(nrrdBoundary, nrrdBoundaryPad)); return 1; } va_start(ap, boundary); if (nrrdBoundaryPad == boundary) { padValue = va_arg(ap, double); } va_end(ap); switch(boundary) { case nrrdBoundaryPad: case nrrdBoundaryBleed: case nrrdBoundaryWrap: case nrrdBoundaryMirror: break; default: biffAddf(NRRD, "%s: sorry boundary %s (%d) unimplemented\n", me, airEnumStr(nrrdBoundary, boundary), boundary); return 1; } /* printf("!%s: boundary = %d, padValue = %g\n", me, boundary, padValue); */ nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, szIn); for (ai=0; aidim; ai++) { if (!( min[ai] <= 0 )) { biffAddf(NRRD, "%s: axis %d min (%s) not <= 0", me, ai, airSprintPtrdiff_t(stmp[0], min[ai])); return 1; } if (!( (size_t)max[ai] >= szIn[ai]-1)) { biffAddf(NRRD, "%s: axis %d max (%s) not >= size-1 (%s)", me, ai, airSprintPtrdiff_t(stmp[0], max[ai]), airSprintSize_t(stmp[1], szIn[ai]-1)); return 1; } } /* this shouldn't actually be necessary .. */ if (!nrrdElementSize(nin)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); return 1; } /* allocate */ numOut = 1; for (ai=0; aidim; ai++) { numOut *= (szOut[ai] = -min[ai] + max[ai] + 1); } nout->blockSize = nin->blockSize; if (nrrdMaybeAlloc_nva(nout, nin->type, nin->dim, szOut)) { biffAddf(NRRD, "%s:", me); return 1; } /* the skinny */ typeSize = nrrdElementSize(nin); dataIn = (char *)nin->data; dataOut = (char *)nout->data; for (ai=0; aidim; ai++) { cIn[ai] = cOut[ai] + min[ai]; switch(boundary) { case nrrdBoundaryPad: /* HEY, this shouldn't result in any input coordinate being set */ case nrrdBoundaryBleed: if (!AIR_IN_CL(0, cIn[ai], (ptrdiff_t)szIn[ai]-1)) { cIn[ai] = AIR_CLAMP(0, cIn[ai], (ptrdiff_t)szIn[ai]-1); outside = 1; } break; case nrrdBoundaryWrap: if (!AIR_IN_CL(0, cIn[ai], (ptrdiff_t)szIn[ai]-1)) { /* HEY: shouldn't have to cast szIn[ai] to ptrdiff_t */ cIn[ai] = AIR_MOD(cIn[ai], (ptrdiff_t)szIn[ai]); outside = 1; } break; case nrrdBoundaryMirror: if (!AIR_IN_CL(0, cIn[ai], (ptrdiff_t)szIn[ai]-1)) { cIn[ai] = _nrrdMirror_64(szIn[ai], cIn[ai]); outside = 1; } break; } } NRRD_INDEX_GEN(idxIn, cIn, szIn, nin->dim); if (!outside) { /* the cIn coords are within the input nrrd: do memcpy() of whole 1-D scanline, then artificially bump for-loop to the end of the scanline */ memcpy(dataOut + idxOut*typeSize, dataIn + idxIn*typeSize, szIn[0]*typeSize); idxOut += nin->axis[0].size-1; cOut[0] += nin->axis[0].size-1; } else { /* we copy only a single value */ if (nrrdBoundaryPad == boundary) { nrrdDInsert[nout->type](dataOut, idxOut, padValue); } else { memcpy(dataOut + idxOut*typeSize, dataIn + idxIn*typeSize, typeSize); } } NRRD_COORD_INCR(cOut, szOut, nin->dim, 0); } if (nrrdAxisInfoCopy(nout, nin, NULL, (NRRD_AXIS_INFO_SIZE_BIT | NRRD_AXIS_INFO_MIN_BIT | NRRD_AXIS_INFO_MAX_BIT ))) { biffAddf(NRRD, "%s:", me); return 1; } for (ai=0; aidim; ai++) { nrrdAxisInfoPosRange(&(nout->axis[ai].min), &(nout->axis[ai].max), nin, ai, AIR_CAST(double, min[ai]), AIR_CAST(double, max[ai])); if (!nrrdStateKindNoop && nout->axis[ai].size == nin->axis[ai].size) { /* nothing changed along this axis; the kind should be preserved */ nout->axis[ai].kind = nin->axis[ai].kind; } else { nout->axis[ai].kind = _nrrdKindAltered(nin->axis[ai].kind, AIR_FALSE); } } strcpy(buff1, ""); for (ai=0; aidim; ai++) { sprintf(buff2, "%s[%s,%s]", (ai ? "x" : ""), airSprintPtrdiff_t(stmp[0], min[ai]), airSprintPtrdiff_t(stmp[1], max[ai])); strcat(buff1, buff2); } if (nrrdBoundaryPad == boundary) { sprintf(buff2, "%s(%g)", airEnumStr(nrrdBoundary, nrrdBoundaryPad), padValue); } else { strcpy(buff2, airEnumStr(nrrdBoundary, boundary)); } if (nrrdContentSet_va(nout, func, nin, "%s,%s", buff1, buff2)) { biffAddf(NRRD, "%s:", me); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } /* copy origin, then shift it along the spatial axes */ nrrdSpaceVecCopy(nout->spaceOrigin, nin->spaceOrigin); for (ai=0; aidim; ai++) { if (AIR_EXISTS(nin->axis[ai].spaceDirection[0])) { nrrdSpaceVecScaleAdd2(nout->spaceOrigin, 1.0, nout->spaceOrigin, AIR_CAST(double, min[ai]), nin->axis[ai].spaceDirection); } } return 0; } /* ******** nrrdPad_nva() ** ** unlike other {X_va,X_nva} pairs, nrrdPad_nva() is a wrapper around ** nrrdPad_va() instead of the other way around. */ int nrrdPad_nva(Nrrd *nout, const Nrrd *nin, const ptrdiff_t *min, const ptrdiff_t *max, int boundary, double padValue) { static const char me[]="nrrdPad_nva"; int E; if (!AIR_IN_OP(nrrdBoundaryUnknown, boundary, nrrdBoundaryLast)) { biffAddf(NRRD, "%s: boundary behavior %d invalid", me, boundary); return 1; } if (nrrdBoundaryPad == boundary) { E = nrrdPad_va(nout, nin, min, max, boundary, padValue); } else { E = nrrdPad_va(nout, nin, min, max, boundary); } if (E) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } /* ******** nrrdSimplePad_va() ** ** pads by a given amount on top and bottom of EVERY axis */ int nrrdSimplePad_va(Nrrd *nout, const Nrrd *nin, unsigned int pad, int boundary, ...) { static const char me[]="nrrdSimplePad_va"; unsigned ai; int ret; ptrdiff_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX]; double padValue; va_list ap; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } for (ai=0; aidim; ai++) { min[ai] = -AIR_CAST(ptrdiff_t, pad); max[ai] = nin->axis[ai].size-1 + pad; } va_start(ap, boundary); if (nrrdBoundaryPad == boundary) { padValue = va_arg(ap, double); ret = nrrdPad_va(nout, nin, min, max, boundary, padValue); } else { ret = nrrdPad_va(nout, nin, min, max, boundary); } va_end(ap); if (ret) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } /* ******** nrrdSimplePad_nva() ** ** unlike other {X_va,X_nva} pairs, nrrdSimplePad_nva() is a wrapper ** around nrrdSimplePad_va() instead of the other way around. */ int nrrdSimplePad_nva(Nrrd *nout, const Nrrd *nin, unsigned int pad, int boundary, double padValue) { static const char me[]="nrrdSimplePad_nva"; int E; if (!AIR_IN_OP(nrrdBoundaryUnknown, boundary, nrrdBoundaryLast)) { biffAddf(NRRD, "%s: boundary behavior %d invalid", me, boundary); return 1; } if (nrrdBoundaryPad == boundary) { E = nrrdSimplePad_va(nout, nin, pad, boundary, padValue); } else { E = nrrdSimplePad_va(nout, nin, pad, boundary); } if (E) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } teem-1.11.0~svn6057/src/nrrd/encodingHex.c0000664000175000017500000001155212165631065017751 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" static const int _nrrdWriteHexTable[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /* ** -2: not allowed, error ** -1: whitespace ** [0,15]: values */ static const int _nrrdReadHexTable[128] = { /* 0 1 2 3 4 5 6 7 8 9 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, /* 0 */ -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, /* 10 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 20 */ -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, /* 30 */ -2, -2, -2, -2, -2, -2, -2, -2, 0, 1, /* 40 */ 2, 3, 4, 5, 6, 7, 8, 9, -2, -2, /* 50 */ -2, -2, -2, -2, -2, 10, 11, 12, 13, 14, /* 60 */ 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 70 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 80 */ -2, -2, -2, -2, -2, -2, -2, 10, 11, 12, /* 90 */ 13, 14, 15, -2, -2, -2, -2, -2, -2, -2, /* 100 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 110 */ -2, -2, -2, -2, -2, -2, -2, -2 /* 120 */ }; static int _nrrdEncodingHex_available(void) { return AIR_TRUE; } static int _nrrdEncodingHex_read(FILE *file, void *_data, size_t elNum, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingHex_read"; size_t nibIdx, nibNum; unsigned char *data; int car=0, nib; AIR_UNUSED(nio); data = AIR_CAST(unsigned char *, _data); nibIdx = 0; nibNum = 2*elNum*nrrdElementSize(nrrd); if (nibNum/elNum != 2*nrrdElementSize(nrrd)) { biffAddf(NRRD, "%s: size_t can't hold 2*(#bytes in array)\n", me); return 1; } while (nibIdx < nibNum) { unsigned char nibshift; car = fgetc(file); if (EOF == car) break; nib = _nrrdReadHexTable[car & 127]; if (-2 == nib) { /* not a valid hex character */ break; } if (-1 == nib) { /* its white space */ continue; } /* else it is a valid character, representing a value from 0 to 15 */ nibshift = AIR_CAST(unsigned char, nib << (4*(1-(nibIdx & 1)))); /* HEY not sure why the cast is needed with gcc v4.8 -Wconversion */ *data = AIR_CAST(unsigned char, *data + nibshift); data += nibIdx & 1; nibIdx++; } if (nibIdx != nibNum) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; if (EOF == car) { biffAddf(NRRD, "%s: hit EOF getting byte %s of %s", me, airSprintSize_t(stmp1, nibIdx/2), airSprintSize_t(stmp2, nibNum/2)); } else { biffAddf(NRRD, "%s: hit invalid character ('%c') getting " "byte %s of %s", me, car, airSprintSize_t(stmp1, nibIdx/2), airSprintSize_t(stmp2, nibNum/2)); } return 1; } return 0; } static int _nrrdEncodingHex_write(FILE *file, const void *_data, size_t elNum, const Nrrd *nrrd, NrrdIoState *nio) { /* static const char me[]="_nrrdEncodingHex_write"; */ const unsigned char *data; size_t byteIdx, byteNum; unsigned int bytesPerLine; bytesPerLine = AIR_MAX(1, nio->charsPerLine/2); data = AIR_CAST(const unsigned char*, _data); byteNum = elNum*nrrdElementSize(nrrd); for (byteIdx=0; byteIdx>4], _nrrdWriteHexTable[(*data)&15]); if (bytesPerLine-1 == byteIdx % bytesPerLine) { fprintf(file, "\n"); } data++; } /* just to be sure, we always end with a carraige return */ fprintf(file, "\n"); return 0; } const NrrdEncoding _nrrdEncodingHex = { "hex", /* name */ "hex", /* suffix */ AIR_TRUE, /* endianMatters */ AIR_FALSE, /* isCompression */ _nrrdEncodingHex_available, _nrrdEncodingHex_read, _nrrdEncodingHex_write }; const NrrdEncoding *const nrrdEncodingHex = &_nrrdEncodingHex; teem-1.11.0~svn6057/src/nrrd/fftNrrd.c0000664000175000017500000002451312165631065017124 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" #if TEEM_FFTW3 /* =========================================================== */ #include const int nrrdFFTWEnabled = AIR_TRUE; int nrrdFFTWWisdomRead(FILE *file) { static const char me[]="nrrdFFTWWisdomRead"; if (!(file)) { biffAddf(NRRD, "%s: given file NULL", me); return 1; } if (!fftw_import_wisdom_from_file(file)) { biffAddf(NRRD, "%s: trouble importing wisdom", me); return 1; } return 0; } static void * _nrrdFftwFreeWrapper(void *ptr) { fftw_free(ptr); return NULL; } static void _nrrdDimsReverse(fftw_iodim *dims, unsigned int len) { fftw_iodim buff[NRRD_DIM_MAX]; unsigned int ii; for (ii=0; iidim > 1 && 2 == _nin->axis[0].size )) { biffAddf(NRRD, "%s: nin doesn't look like a complex-valued array", me); return 1; } if (!( axesNum >= 1 )) { biffAddf(NRRD, "%s: axesNum 0, no axes to transform?", me); return 1; } for (axi=0; axi<_nin->dim; axi++) { axisDo[axi] = 0; } for (axi=0; axidim )) { biffAddf(NRRD, "%s: axis %u (axes[%u]) out of range [1,%u]", me, axes[axi], axi, _nin->dim-1); return 1; } axisDo[axes[axi]]++; if (2 == axisDo[axes[axi]]) { biffAddf(NRRD, "%s: axis %u (axes[%u]) already transformed", me, axes[axi], axi); return 1; } } NN = nrrdElementNumber(_nin); /* We always make a new buffer to hold the double-type copy of input for two reasons: if input is not double we have to convert it, and we want input to be const, and we can't have const with the plan creation over-writing the input (except with FFTW_ESTIMATE). Given that, we might as well use the memory-alignment-savvy fftw_malloc; the freeing is handled by both _nrrdFftwFreeWrapper and nrrdNix. */ /* (NN = 2 * number of complex values) */ inData = AIR_CAST(double *, fftw_malloc(NN*sizeof(double))); if (!inData) { biffAddf(NRRD, "%s: couldn't allocate input data copy", me); return 1; } mop = airMopNew(); airMopAdd(mop, inData, _nrrdFftwFreeWrapper, airMopAlways); nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNix /* NOT Nuke */, airMopAlways); nrrdAxisInfoGet_nva(_nin, nrrdAxisInfoSize, inSize); /* we don't copy data yet; it may be over-written during plan creation */ if (nrrdWrap_nva(nin, inData, nrrdTypeDouble, _nin->dim, inSize)) { biffAddf(NRRD, "%s: couldn't wrap or copy input", me); airMopError(mop); return 1; } /* But on the output, we just use regular malloc, because we don't (yet) have a way of telling nrrd to use fftw_malloc/fftw_free instead of the generic malloc/free, and we don't want two whole copies of the output (one that is memory-aligned, internal to this function, and one that isn't, in nout) */ if (nrrdMaybeAlloc_nva(nout, nrrdTypeDouble, _nin->dim, inSize)) { biffAddf(NRRD, "%s: couldn't allocate output", me); airMopError(mop); return 1; } outData = AIR_CAST(double *, nout->data); /* As far as GLK can tell, the guru interface is needed, and the "advanced" fftw_plan_many_dft won't work, because its simplistic accounting of stride can't handle having non-contiguous non-transformed axes (e.g. transforming only axes 2 and not 1, 3 in a 3-D complex-valued array) */ txfRank = howRank = 0; stride = 1; nprod = 1; for (axi=1; axidim; axi++) { if (axisDo[axi]) { txfDims[txfRank].n = AIR_CAST(int, inSize[axi]); txfDims[txfRank].is = txfDims[txfRank].os = AIR_CAST(int, stride); nprod *= inSize[axi]; txfRank++; } else { howDims[howRank].n = AIR_CAST(int, inSize[axi]); howDims[howRank].is = howDims[howRank].os = AIR_CAST(int, stride); howRank++; } stride *= inSize[axi]; } _nrrdDimsReverse(txfDims, txfRank); _nrrdDimsReverse(howDims, howRank); /* fprintf(stderr, "!%s: ------------- txfRank %u, howRank %u\n", me, txfRank, howRank); for (axi=0; axidata; if (nrrdConvert(nin, _nin, nrrdTypeDouble)) { biffAddf(NRRD, "%s: couldn't initialize input", me); airMopError(mop); return 1; } /* a reasonable assert: data buffer was allocated correctly */ if (dataBef != nin->data) { biffAddf(NRRD, "%s: pre-allocation error; nrrdConvert reallocated data", me); airMopError(mop); return 1; } /* run the transform; HEY, no indication of success? */ fftw_execute(plan); /* if wanted, remove the sqrt(nprod) scaling that fftw adds at each pass */ if (rescale) { double scale; scale = sqrt(1.0/AIR_CAST(double, nprod)); for (II=0; IIaxis[0].kind = nrrdKindComplex; airMopOkay(mop); return 0; } int nrrdFFTWWisdomWrite(FILE *file) { static const char me[]="nrrdFFTWWisdomWrite"; if (!(file)) { biffAddf(NRRD, "%s: given file NULL", me); return 1; } /* HEY: weird that there's no return value on this function? */ fftw_export_wisdom_to_file(file); return 0; } #else /* TEEM_FFTW3 ========================================================= */ /* we do NOT have the FFTW library to link against; have to supply the same symbols anyway */ const int nrrdFFTWEnabled = AIR_FALSE; int nrrdFFTWWisdomRead(FILE *file) { AIR_UNUSED(file); return 0; } int nrrdFFT(Nrrd *nout, const Nrrd *nin, unsigned int *axes, unsigned int axesNum, int sign, int rescale, int rigor) { static const char me[]="nrrdFFT"; AIR_UNUSED(nout); AIR_UNUSED(nin); AIR_UNUSED(axes); AIR_UNUSED(axesNum); AIR_UNUSED(sign); AIR_UNUSED(rescale); AIR_UNUSED(rigor); biffAddf(NRRD, "%s: sorry, non-fftw3 version not yet implemented\n", me); return 1; } int nrrdFFTWWisdomWrite(FILE *file) { AIR_UNUSED(file); return 0; } #endif /* TEEM_FFTW3 ======================================================== */ teem-1.11.0~svn6057/src/nrrd/methodsNrrd.c0000664000175000017500000010463412165631065020013 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* Wed Sep 14 05:55:40 EDT 2005: these are no longer used void nrrdPeripheralInit(Nrrd *nrrd) { nrrdBasicInfoInit(nrrd, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT); return; } int nrrdPeripheralCopy(Nrrd *nout, const Nrrd *nin) { nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT); return 0; } */ /* ---- BEGIN non-NrrdIO */ const int nrrdPresent = 42; /* ---- END non-NrrdIO */ /* ------------------------------------------------------------ */ void nrrdIoStateInit(NrrdIoState *nio) { if (nio) { nio->path = (char *)airFree(nio->path); nio->base = (char *)airFree(nio->base); nio->line = (char *)airFree(nio->line); nio->dataFNFormat = (char *)airFree(nio->dataFNFormat); /* the way IO to/from strings works, I don't think this should be freed */ nio->headerStringRead = NULL; nio->headerStringWrite = NULL; airArrayLenSet(nio->dataFNArr, 0); /* closing this is always someone else's responsibility */ nio->headerFile = NULL; nio->dataFile = NULL; nio->dataFileDim = 0; nio->dataFNMin = 0; nio->dataFNMax = 0; nio->dataFNStep = 0; nio->dataFNIndex = 0; nio->lineLen = 0; nio->pos = 0; nio->endian = airEndianUnknown; nio->lineSkip = 0; nio->headerStrlen = 0; nio->headerStrpos = 0; nio->byteSkip = 0; memset(nio->seen, 0, (NRRD_FIELD_MAX+1)*sizeof(int)); nio->detachedHeader = AIR_FALSE; nio->bareText = nrrdDefaultWriteBareText; nio->charsPerLine = nrrdDefaultWriteCharsPerLine; nio->valsPerLine = nrrdDefaultWriteValsPerLine; nio->skipData = AIR_FALSE; nio->skipFormatURL = AIR_FALSE; nio->keepNrrdDataFileOpen = AIR_FALSE; nio->zlibLevel = -1; nio->zlibStrategy = nrrdZlibStrategyDefault; nio->bzip2BlockSize = -1; nio->learningHeaderStrlen = AIR_FALSE; nio->oldData = NULL; nio->oldDataSize = 0; nio->format = nrrdFormatUnknown; nio->encoding = nrrdEncodingUnknown; } return; } NrrdIoState * nrrdIoStateNew(void) { NrrdIoState *nio; nio = (NrrdIoState *)calloc(1, sizeof(NrrdIoState)); if (nio) { airPtrPtrUnion appu; nio->path = NULL; nio->base = NULL; nio->line = NULL; nio->dataFNFormat = NULL; nio->dataFN = NULL; nio->headerStringRead = NULL; nio->headerStringWrite = NULL; appu.cp = &(nio->dataFN); nio->dataFNArr = airArrayNew(appu.v, NULL, sizeof(char *), NRRD_FILENAME_INCR); airArrayPointerCB(nio->dataFNArr, airNull, airFree); nio->format = nrrdFormatUnknown; nio->encoding = nrrdEncodingUnknown; nrrdIoStateInit(nio); } return nio; } NrrdIoState * nrrdIoStateNix(NrrdIoState *nio) { nio->path = (char *)airFree(nio->path); nio->base = (char *)airFree(nio->base); nio->line = (char *)airFree(nio->line); nio->dataFNFormat = (char *)airFree(nio->dataFNFormat); nio->dataFNArr = airArrayNuke(nio->dataFNArr); /* the NrrdIoState never owned nio->oldData; we don't free it */ airFree(nio); /* no NULL assignment, else compile warnings */ return NULL; } /* ---- BEGIN non-NrrdIO */ /* ------------------------------------------------------------ */ void _nrrdResampleInfoInit(NrrdResampleInfo *info) { int i, d; for (d=0; dkernel[d] = NULL; info->samples[d] = 0; info->parm[d][0] = nrrdDefaultKernelParm0; for (i=1; iparm[d][i] = AIR_NAN; info->min[d] = info->max[d] = AIR_NAN; } info->boundary = nrrdDefaultResampleBoundary; info->type = nrrdDefaultResampleType; info->renormalize = nrrdDefaultResampleRenormalize; info->round = nrrdDefaultResampleRound; info->clamp = nrrdDefaultResampleClamp; info->cheap = nrrdDefaultResampleCheap; info->padValue = nrrdDefaultResamplePadValue; } NrrdResampleInfo * nrrdResampleInfoNew(void) { NrrdResampleInfo *info; info = (NrrdResampleInfo*)(calloc(1, sizeof(NrrdResampleInfo))); if (info) { /* explicitly sets pointers to NULL */ _nrrdResampleInfoInit(info); } return info; } NrrdResampleInfo * nrrdResampleInfoNix(NrrdResampleInfo *info) { info = (NrrdResampleInfo *)airFree(info); return NULL; } /* ------------------------------------------------------------ */ NrrdKernelSpec * nrrdKernelSpecNew(void) { NrrdKernelSpec *ksp; int i; ksp = (NrrdKernelSpec *)calloc(1, sizeof(NrrdKernelSpec)); if (ksp) { ksp->kernel = NULL; for (i=0; iparm[i] = airNaN(); /* valgrind complained about AIR_NAN at -O2 */ } } return ksp; } NrrdKernelSpec * nrrdKernelSpecCopy(const NrrdKernelSpec *oldKsp) { NrrdKernelSpec *ksp=NULL; if (oldKsp) { ksp = (NrrdKernelSpec *)calloc(1, sizeof(NrrdKernelSpec)); if (ksp) { memcpy(ksp, oldKsp, sizeof(NrrdKernelSpec)); } } return ksp; } NrrdKernelSpec * nrrdKernelSpecNix(NrrdKernelSpec *ksp) { ksp = (NrrdKernelSpec *)airFree(ksp); return NULL; } void nrrdKernelSpecSet(NrrdKernelSpec *ksp, const NrrdKernel *k, const double kparm[NRRD_KERNEL_PARMS_NUM]) { unsigned int p; if (ksp && k && kparm) { ksp->kernel = k; for (p=0; p<(k->numParm); p++) { ksp->parm[p] = kparm[p]; } } } void nrrdKernelParmSet(const NrrdKernel **kP, double kparm[NRRD_KERNEL_PARMS_NUM], NrrdKernelSpec *ksp) { int p; if (kP && kparm && ksp) { *kP = ksp->kernel; for (p=0; pparm[p]; } } } /* ---- END non-NrrdIO */ /* ------------------------------------------------------------ */ /* see axis.c for axis-specific "methods" */ /* ------------------------------------------------------------ */ /* ******** nrrdBasicInfoInit ** ** resets "basic" (per-array) information ** formerly nrrdPeripheralInit ** ** the bitflag communicates which fields should *not* be initialized */ void nrrdBasicInfoInit(Nrrd *nrrd, int bitflag) { int dd, ee; if (!nrrd) { return; } if (!(NRRD_BASIC_INFO_DATA_BIT & bitflag)) { nrrd->data = airFree(nrrd->data); } if (!(NRRD_BASIC_INFO_TYPE_BIT & bitflag)) { nrrd->type = nrrdTypeUnknown; } if (!(NRRD_BASIC_INFO_BLOCKSIZE_BIT & bitflag)) { nrrd->blockSize = 0; } if (!(NRRD_BASIC_INFO_DIMENSION_BIT & bitflag)) { nrrd->dim = 0; } if (!(NRRD_BASIC_INFO_CONTENT_BIT & bitflag)) { nrrd->content = (char *)airFree(nrrd->content); } if (!(NRRD_BASIC_INFO_SAMPLEUNITS_BIT & bitflag)) { nrrd->sampleUnits = (char *)airFree(nrrd->sampleUnits); } if (!(NRRD_BASIC_INFO_SPACE_BIT & bitflag)) { nrrd->space = nrrdSpaceUnknown; nrrd->spaceDim = 0; } if (!(NRRD_BASIC_INFO_SPACEDIMENSION_BIT & bitflag)) { nrrd->space = nrrdSpaceUnknown; nrrd->spaceDim = 0; } if (!(NRRD_BASIC_INFO_SPACEUNITS_BIT & bitflag)) { for (dd=0; ddspaceUnits[dd] = (char *)airFree(nrrd->spaceUnits[dd]); } } if (!(NRRD_BASIC_INFO_SPACEORIGIN_BIT & bitflag)) { for (dd=0; ddspaceOrigin[dd] = AIR_NAN; } } if (!(NRRD_BASIC_INFO_MEASUREMENTFRAME_BIT & bitflag)) { for (dd=0; ddmeasurementFrame[dd][ee] = AIR_NAN; } } } if (!(NRRD_BASIC_INFO_OLDMIN_BIT & bitflag)) { nrrd->oldMin = AIR_NAN; } if (!(NRRD_BASIC_INFO_OLDMAX_BIT & bitflag)) { nrrd->oldMax = AIR_NAN; } if (!(NRRD_BASIC_INFO_COMMENTS_BIT & bitflag)) { nrrdCommentClear(nrrd); } if (!(NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT & bitflag)) { nrrdKeyValueClear(nrrd); } return; } /* ******** nrrdBasicInfoCopy ** ** copies "basic" (per-array) information ** formerly known as nrrdPeripheralCopy, which was not used consistently ** ** the bitflag communicates which fields should *not* be copied */ int nrrdBasicInfoCopy(Nrrd *dest, const Nrrd *src, int bitflag) { static const char me[]="nrrdBasicInfoCopy"; unsigned int dd, ee; if (!( dest && src )) return 0; if (dest == src) { /* nothing to do */ return 0; } if (!(NRRD_BASIC_INFO_DATA_BIT & bitflag)) { dest->data = src->data; } if (!(NRRD_BASIC_INFO_TYPE_BIT & bitflag)) { dest->type = src->type; } if (!(NRRD_BASIC_INFO_BLOCKSIZE_BIT & bitflag)) { dest->blockSize = src->blockSize; } if (!(NRRD_BASIC_INFO_DIMENSION_BIT & bitflag)) { dest->dim = src->dim; } if (!(NRRD_BASIC_INFO_CONTENT_BIT & bitflag)) { dest->content = (char *)airFree(dest->content); dest->content = airStrdup(src->content); if (src->content && !dest->content) { biffAddf(NRRD, "%s: couldn't copy content", me); return 1; } } if (!(NRRD_BASIC_INFO_SAMPLEUNITS_BIT & bitflag)) { dest->sampleUnits = (char *)airFree(dest->sampleUnits); dest->sampleUnits = airStrdup(src->sampleUnits); if (src->sampleUnits && !dest->sampleUnits) { biffAddf(NRRD, "%s: couldn't copy sampleUnits", me); return 1; } } if (!(NRRD_BASIC_INFO_SPACE_BIT & bitflag)) { dest->space = src->space; } if (!(NRRD_BASIC_INFO_SPACEDIMENSION_BIT & bitflag)) { dest->spaceDim = src->spaceDim; } if (!(NRRD_BASIC_INFO_SPACEUNITS_BIT & bitflag)) { for (dd=0; ddspaceDim; dd++) { dest->spaceUnits[dd] = (char *)airFree(dest->spaceUnits[dd]); dest->spaceUnits[dd] = airStrdup(src->spaceUnits[dd]); if (src->spaceUnits[dd] && !dest->spaceUnits[dd]) { biffAddf(NRRD, "%s: couldn't copy spaceUnits[%d]", me, dd); return 1; } } for (dd=src->spaceDim; ddspaceUnits[dd] = (char *)airFree(dest->spaceUnits[dd]); } } if (!(NRRD_BASIC_INFO_SPACEORIGIN_BIT & bitflag)) { for (dd=0; ddspaceDim-1) { dest->spaceOrigin[dd] = src->spaceOrigin[dd]; } else { dest->spaceOrigin[dd] = AIR_NAN; } } } if (!(NRRD_BASIC_INFO_MEASUREMENTFRAME_BIT & bitflag)) { for (dd=0; ddspaceDim-1 && ee <= src->spaceDim-1) { dest->measurementFrame[dd][ee] = src->measurementFrame[dd][ee]; } else { dest->measurementFrame[dd][ee] = AIR_NAN; } } } for (dd=src->spaceDim; ddspaceOrigin[dd] = AIR_NAN; } } if (!(NRRD_BASIC_INFO_OLDMIN_BIT & bitflag)) { dest->oldMin = src->oldMin; } if (!(NRRD_BASIC_INFO_OLDMAX_BIT & bitflag)) { dest->oldMax = src->oldMax; } if (!(NRRD_BASIC_INFO_COMMENTS_BIT & bitflag)) { if (nrrdCommentCopy(dest, src)) { biffAddf(NRRD, "%s: trouble copying comments", me); return 1; } } if (!(NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT & bitflag)) { if (nrrdKeyValueCopy(dest, src)) { biffAddf(NRRD, "%s: trouble copying key/value pairs", me); return 1; } } return 0; } /* ******* nrrdInit ** ** initializes a nrrd to default state. All nrrd functions in the ** business of initializing a nrrd struct use this function. Mostly ** just sets values to 0, NaN, "", NULL, or Unknown */ void nrrdInit(Nrrd *nrrd) { int ii; if (nrrd) { nrrdBasicInfoInit(nrrd, NRRD_BASIC_INFO_NONE); for (ii=0; iiaxis + ii); } } return; } /* ******** nrrdNew() ** ** creates and initializes a Nrrd ** ** this does NOT use biff */ Nrrd * nrrdNew(void) { int ii; Nrrd *nrrd; airPtrPtrUnion appu; nrrd = (Nrrd*)(calloc(1, sizeof(Nrrd))); if (!nrrd) { return NULL; } /* explicitly set pointers to NULL, since calloc isn't officially guaranteed to do that. */ nrrd->data = NULL; for (ii=0; iiaxis + ii); } for (ii=0; iispaceUnits[ii] = NULL; } nrrd->content = NULL; nrrd->sampleUnits = NULL; /* create comment airArray (even though it starts empty) */ nrrd->cmt = NULL; appu.cp = &(nrrd->cmt); nrrd->cmtArr = airArrayNew(appu.v, NULL, sizeof(char *), NRRD_COMMENT_INCR); if (!nrrd->cmtArr) { return NULL; } airArrayPointerCB(nrrd->cmtArr, airNull, airFree); /* create key/value airArray (even thought it starts empty) */ nrrd->kvp = NULL; appu.cp = &(nrrd->kvp); nrrd->kvpArr = airArrayNew(appu.v, NULL, 2*sizeof(char *), NRRD_KEYVALUE_INCR); if (!nrrd->kvpArr) { return NULL; } /* key/value airArray uses no callbacks for now */ /* finish initializations */ nrrdInit(nrrd); return nrrd; } /* ******** nrrdNix() ** ** does nothing with the array data inside, just does whatever is needed ** to free the nrrd itself ** ** returns NULL ** ** this does NOT use biff */ Nrrd * nrrdNix(Nrrd *nrrd) { int ii; if (nrrd) { for (ii=0; iiaxis[ii])); } for (ii=0; iispaceUnits[ii] = (char *)airFree(nrrd->spaceUnits[ii]); } nrrd->content = (char *)airFree(nrrd->content); nrrd->sampleUnits = (char *)airFree(nrrd->sampleUnits); nrrdCommentClear(nrrd); nrrd->cmtArr = airArrayNix(nrrd->cmtArr); nrrdKeyValueClear(nrrd); nrrd->kvpArr = airArrayNix(nrrd->kvpArr); airFree(nrrd); } return NULL; } /* ******** nrrdEmpty() ** ** frees data inside nrrd AND resets all its state, so its the ** same as what comes from nrrdNew(). This includes free()ing ** any comments. */ Nrrd * nrrdEmpty(Nrrd *nrrd) { if (nrrd) { nrrd->data = airFree(nrrd->data); nrrdInit(nrrd); } return nrrd; } /* ******** nrrdNuke() ** ** blows away the nrrd and everything inside ** ** always returns NULL */ Nrrd * nrrdNuke(Nrrd *nrrd) { if (nrrd) { nrrdEmpty(nrrd); nrrdNix(nrrd); } return NULL; } /* ------------------------------------------------------------ */ int _nrrdSizeCheck(const size_t *size, unsigned int dim, int useBiff) { static const char me[]="_nrrdSizeCheck"; size_t num, pre; unsigned int ai; pre = num = 1; for (ai=0; aiblockSize at some other time. */ int nrrdWrap_nva(Nrrd *nrrd, void *data, int type, unsigned int dim, const size_t *size) { static const char me[]="nrrdWrap_nva"; if (!(nrrd && size)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } nrrd->data = data; nrrd->type = type; nrrd->dim = dim; if (_nrrdSizeCheck(size, dim, AIR_TRUE)) { biffAddf(NRRD, "%s:", me); return 1; } nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoSize, size); return 0; } /* ******** nrrdWrap_va() ** ** Minimal var args wrapper around nrrdWrap_nva, with the advantage of ** taking all the axes sizes as the var args. ** ** This is THE BEST WAY to wrap a nrrd around existing raster data, ** assuming that the dimension is known at compile time. ** ** If successful, returns 0, otherwise, 1. ** This does use biff. */ int nrrdWrap_va(Nrrd *nrrd, void *data, int type, unsigned int dim, ...) { static const char me[]="nrrdWrap_va"; va_list ap; size_t size[NRRD_DIM_MAX]; unsigned int ai; if (!(nrrd && data)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } va_start(ap, dim); for (ai=0; aidata; for (I=0; Idata) { if (nrrdMaybeAlloc_nva(nout, nin->type, nin->dim, size)) { biffAddf(NRRD, "%s: couldn't allocate data", me); return 1; } memcpy(nout->data, nin->data, nrrdElementNumber(nin)*nrrdElementSize(nin)); } else { /* someone is trying to copy structs without data, fine fine fine */ if (nrrdWrap_nva(nout, NULL, nin->type, nin->dim, size)) { biffAddf(NRRD, "%s: couldn't allocate data", me); return 1; } } nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_SIZE_BIT); /* if nin->data non-NULL (second branch above), this will harmlessly unset and set type and dim */ nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_DATA_BIT | bitflag); if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | bitflag)) { biffAddf(NRRD, "%s: trouble copying basic info", me); return 1; } return 0; } /* ******** nrrdCopy ** ** copy method for nrrds. nout will end up as an "exact" copy of nin. ** New space for data is allocated here, and output nrrd points to it. ** Comments from old are added to comments for new, so these are also ** newly allocated. nout->ptr is not set, nin->ptr is not read. */ int nrrdCopy(Nrrd *nout, const Nrrd *nin) { static const char me[]="nrrdCopy"; if (_nrrdCopy(nout, nin, NRRD_BASIC_INFO_NONE)) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } /* ******** nrrdAlloc_nva() ** ** allocates data array and sets information. If this is a block type ** nrrd, it is necessary to set nrrd->blockSize PRIOR to calling ** this function. ** ** This function will always allocate more memory (via calloc), but ** it will free() nrrd->data if it is non-NULL when passed in. ** ** This function takes the same "don't mess with peripheral information" ** attitude as nrrdWrap(). ** ** Note to Gordon: don't get clever and change ANY axis-specific ** information here. It may be very convenient to set that before ** nrrdAlloc or nrrdMaybeAlloc ** ** Note: This function DOES use biff */ int nrrdAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size) { static const char me[]="nrrdAlloc_nva"; size_t num, esize; char stmp[2][AIR_STRLEN_SMALL]; if (!(nrrd && size)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(nrrdType, type)) { biffAddf(NRRD, "%s: type (%d) is invalid", me, type); return 1; } if (nrrdTypeBlock == type) { if (!(0 < nrrd->blockSize)) { biffAddf(NRRD, "%s: given nrrd->blockSize %s invalid", me, airSprintSize_t(stmp[0], nrrd->blockSize)); return 1; } } if (!AIR_IN_CL(1, dim, NRRD_DIM_MAX)) { biffAddf(NRRD, "%s: dim (%d) not in valid range [1,%d]", me, dim, NRRD_DIM_MAX); return 1; } nrrd->data = airFree(nrrd->data); if (nrrdWrap_nva(nrrd, NULL, type, dim, size)) { biffAddf(NRRD, "%s:", me); return 1 ; } num = nrrdElementNumber(nrrd); esize = nrrdElementSize(nrrd); nrrd->data = calloc(num, esize); if (!(nrrd->data)) { biffAddf(NRRD, "%s: calloc(%s,%s) failed", me, airSprintSize_t(stmp[0], num), airSprintSize_t(stmp[1], esize)); return 1 ; } return 0; } /* ******** nrrdAlloc_va() ** ** Handy wrapper around nrrdAlloc_nva, which takes, as its vararg list, ** all the axes sizes. */ int nrrdAlloc_va(Nrrd *nrrd, int type, unsigned int dim, ...) { static const char me[]="nrrdAlloc_va"; size_t size[NRRD_DIM_MAX]; unsigned int ai; va_list ap; if (!nrrd) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } va_start(ap, dim); for (ai=0; aitype) { biffAddf(NRRD, "%s: can't change from one block nrrd to another", me); return 1; } if (!(0 < nrrd->blockSize)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: given nrrd->blockSize %s invalid", me, airSprintSize_t(stmp, nrrd->blockSize)); return 1; } elementSizeWant = nrrd->blockSize; } else { elementSizeWant = nrrdTypeSize[type]; } if (_nrrdSizeCheck(size, dim, AIR_TRUE)) { biffAddf(NRRD, "%s:", me); return 1; } if (!(nrrd->data)) { need = 1; } else { numWant = 1; for (ai=0; aidata, type, dim, size)) { biffAddf(NRRD, "%s:", me); return 1; } /* but we may have to initialize memory */ if (zeroWhenNoAlloc) { memset(nrrd->data, 0, nrrdElementNumber(nrrd)*nrrdElementSize(nrrd)); } } return 0; } /* ******** nrrdMaybeAlloc_nva ** ** NOTE: this is now just a wrapper around _nrrdMaybeAllocMaybeZero_nva; ** below info referred to original implementation. ** ** calls nrrdAlloc_nva if the requested space is different than ** what is currently held ** ** also subscribes to the "don't mess with peripheral information" philosophy */ int nrrdMaybeAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size) { static const char me[]="nrrdMaybeAlloc_nva"; int ret; ret = _nrrdMaybeAllocMaybeZero_nva(nrrd, type, dim, size, AIR_TRUE); if (ret) { biffAddf(NRRD, "%s: trouble", me); } return ret; } /* ******** nrrdMaybeAlloc_va() ** ** Handy wrapper around nrrdAlloc, which takes, as its vararg list ** all the axes sizes, thereby calculating the total number. */ int nrrdMaybeAlloc_va(Nrrd *nrrd, int type, unsigned int dim, ...) { static const char me[]="nrrdMaybeAlloc_va"; size_t size[NRRD_DIM_MAX]; unsigned int ai; va_list ap; if (!nrrd) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } va_start(ap, dim); for (ai=0; aidim != ninB->dim) { *differ = ninA->dim < ninB->dim ? -1 : 1; if (explain) { sprintf(explain, "nin{A,B}->dim %u %s %u", ninA->dim, *differ < 0 ? "<" : ">", ninB->dim); } return 0; } } if (ninA->type != ninB->type) { *differ = ninA->type < ninB->type ? -1 : 1; if (explain) { sprintf(explain, "nin{A,B}->type %s %s %s", airEnumStr(nrrdType, ninA->type), *differ < 0 ? "<" : ">", airEnumStr(nrrdType, ninB->type)); } return 0; } numA = nrrdElementNumber(ninA); numB = nrrdElementNumber(ninB); if (numA != numB) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; *differ = numA < numB ? -1 : 1; sprintf(explain, "element # {A,B} %s %s %s", airSprintSize_t(stmp1, numA), *differ < 0 ? "<" : ">", airSprintSize_t(stmp2, numB)); return 0; } /* this will always set *differ */ if (nrrdArrayCompare(ninA->type, ninA->data, ninB->data, numA, epsilon, differ, explain)) { biffAddf(NRRD, "%s: problem comparing values", me); return 1; } if (onlyData || *differ) { /* If caller only cared about data values, we're done whether or not they were different. else, if the data values are different, we're done. The explanation of any difference in values is already in "explain" (if non-NULL) */ return 0; } for (axi=0; axidim; axi++) { /* this always sets *differ */ if (nrrdAxisInfoCompare(ninA->axis + axi, ninB->axis + axi, differ, explain)) { biffAddf(NRRD, "%s: problem comparing axis %u", me, axi); return 1; } if (*differ) { char tmpexplain[AIR_STRLEN_LARGE]; /* the explanation, if wanted, is in "explain", but we add context */ sprintf(tmpexplain, "(axis %u) %s", axi, explain); airStrcpy(explain, AIR_STRLEN_LARGE, tmpexplain); return 0; } } #define STRING_COMPARE(VAL, STR) \ *differ = airStrcmp(ninA->VAL, ninB->VAL); \ if (*differ) { \ if (explain) { \ /* can't print whole string because of fixed-size of explain */ \ sprintf(explain, "ninA->%s %s ninB->%s", \ STR, *differ < 0 ? "<" : ">", STR); \ } \ return 0; \ } STRING_COMPARE(content, "content"); STRING_COMPARE(sampleUnits, "sampleUnits"); if (ninA->space != ninB->space) { *differ = ninA->space < ninB->space ? -1 : 1; if (explain) { sprintf(explain, "ninA->space %s %s ninB->space %s", airEnumStr(nrrdSpace, ninA->space), *differ < 0 ? "<" : ">", airEnumStr(nrrdSpace, ninB->space)); } return 0; } if (ninA->spaceDim != ninB->spaceDim) { *differ = ninA->spaceDim < ninB->spaceDim ? -1 : 1; if (explain) { sprintf(explain, "ninA->spaceDim %u %s ninB->spaceDim %u", ninA->spaceDim, *differ < 0 ? "<" : ">", ninB->spaceDim); } return 0; } if (ninA->blockSize != ninB->blockSize) { *differ = ninA->blockSize < ninB->blockSize ? -1 : 1; if (explain) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; sprintf(explain, "ninA->blockSize %s %s ninB->blockSize %s", airSprintSize_t(stmp1, ninA->blockSize), *differ < 0 ? "<" : ">", airSprintSize_t(stmp2, ninB->blockSize)); } return 0; } #define DOUBLE_COMPARE(VAL, STR) \ *differ = _nrrdDblcmp(ninA->VAL, ninB->VAL); \ if (*differ) { \ if (explain) { \ sprintf(explain, "ninA->%s %.17g %s ninB->%s %.17g", \ STR, ninA->VAL, *differ < 0 ? "<" : ">", \ STR, ninB->VAL); \ } \ return 0; \ } for (saxi=0; saxicmtArr->len != ninB->cmtArr->len) { *differ = ninA->cmtArr->len < ninB->cmtArr->len ? -1 : 1; if (explain) { sprintf(explain, "ninA # comments %u %s ninB # comments %u", ninA->cmtArr->len, *differ < 0 ? "<" : ">", ninB->cmtArr->len); } return 0; } else { unsigned int ii; char stmp[AIR_STRLEN_SMALL]; for (ii=0; iicmtArr->len; ii++) { sprintf(stmp, "comment[%u]", ii); STRING_COMPARE(cmt[ii], stmp); } } if (ninA->kvpArr->len != ninB->kvpArr->len) { *differ = ninA->kvpArr->len < ninB->kvpArr->len ? -1 : 1; if (explain) { sprintf(explain, "ninA # key/values %u %s ninB # key/values %u", ninA->kvpArr->len, *differ < 0 ? "<" : ">", ninB->kvpArr->len); } return 0; } else { unsigned int ii; char stmp[AIR_STRLEN_SMALL]; for (ii=0; iikvpArr->len; ii++) { sprintf(stmp, "key/value key[%u]", ii); STRING_COMPARE(kvp[2*ii + 0], stmp); sprintf(stmp, "key/value value[%u]", ii); STRING_COMPARE(kvp[2*ii + 1], stmp); } } #undef STRING_COMPARE /* ninA->ptr and ninB->ptr are not to be accessed */ /* if we've gotten this far, all fields were equal */ *differ = 0; return 0; } /* ******** nrrdPPM() ** ** for making a nrrd suitable for holding PPM data ** ** "don't mess with peripheral information" */ int nrrdPPM(Nrrd *ppm, size_t sx, size_t sy) { static const char me[]="nrrdPPM"; char stmp[2][AIR_STRLEN_SMALL]; if (nrrdMaybeAlloc_va(ppm, nrrdTypeUChar, 3, AIR_CAST(size_t, 3), sx, sy)) { biffAddf(NRRD, "%s: couldn't allocate %s x %s 24-bit image", me, airSprintSize_t(stmp[0], sx), airSprintSize_t(stmp[1], sy)); return 1; } return 0; } /* ******** nrrdPGM() ** ** for making a nrrd suitable for holding PGM data ** ** "don't mess with peripheral information" */ int nrrdPGM(Nrrd *pgm, size_t sx, size_t sy) { static const char me[]="nrrdPGM"; char stmp[2][AIR_STRLEN_SMALL]; if (nrrdMaybeAlloc_va(pgm, nrrdTypeUChar, 2, sx, sy)) { biffAddf(NRRD, "%s: couldn't allocate %s x %s 8-bit image", me, airSprintSize_t(stmp[0], sx), airSprintSize_t(stmp[1], sy)); return 1; } return 0; } /* ---- END non-NrrdIO */ teem-1.11.0~svn6057/src/nrrd/histogram.c0000664000175000017500000005740512165631065017522 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ******** nrrdHisto() ** ** makes a 1D histogram of a given size and type ** ** pre-NrrdRange policy: ** this looks at nin->min and nin->max to see if they are both non-NaN. ** If so, it uses these as the range of the histogram, otherwise it ** finds the min and max present in the volume. If nin->min and nin->max ** are being used as the histogram range, then values which fall outside ** this are ignored (they don't contribute to the histogram). ** ** post-NrrdRange policy: */ int nrrdHisto(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range, const Nrrd *nwght, size_t bins, int type) { static const char me[]="nrrdHisto", func[]="histo"; size_t I, num, idx; airArray *mop; NrrdRange *range; double min, max, eps, val, count, incr, (*lup)(const void *v, size_t I); if (!(nin && nout)) { /* _range and nwght can be NULL */ biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (!(bins > 0)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: bins value (%s) invalid", me, airSprintSize_t(stmp, bins)); return 1; } if (airEnumValCheck(nrrdType, type) || nrrdTypeBlock == type) { biffAddf(NRRD, "%s: invalid nrrd type %d", me, type); return 1; } if (nwght) { if (nout==nwght) { biffAddf(NRRD, "%s: nout==nwght disallowed", me); return 1; } if (nrrdTypeBlock == nwght->type) { biffAddf(NRRD, "%s: nwght type %s invalid", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!nrrdSameSize(nin, nwght, AIR_TRUE)) { biffAddf(NRRD, "%s: nwght size mismatch with nin", me); return 1; } lup = nrrdDLookup[nwght->type]; } else { lup = NULL; } if (nrrdMaybeAlloc_va(nout, type, 1, bins)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: failed to alloc histo array (len %s)", me, airSprintSize_t(stmp, bins)); return 1; } mop = airMopNew(); /* nout->axis[0].size set */ nout->axis[0].spacing = AIR_NAN; nout->axis[0].thickness = AIR_NAN; if (nout && AIR_EXISTS(nout->axis[0].min) && AIR_EXISTS(nout->axis[0].max)) { /* HEY: total hack to externally nail down min and max of histogram: use the min and max already set on axis[0] */ /* HEY: shouldn't this blatent hack be further restricted by also checking the existence of range->min and range->max ? */ min = nout->axis[0].min; max = nout->axis[0].max; } else { if (_range) { range = nrrdRangeCopy(_range); nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); } else { range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); } airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); min = range->min; max = range->max; nout->axis[0].min = min; nout->axis[0].max = max; } eps = (min == max ? 1.0 : 0.0); nout->axis[0].center = nrrdCenterCell; /* nout->axis[0].label set below */ /* make histogram */ num = nrrdElementNumber(nin); for (I=0; Itype](nin->data, I); if (AIR_EXISTS(val)) { if (val < min || val > max+eps) { /* value is outside range; ignore it */ continue; } if (AIR_IN_CL(min, val, max)) { idx = airIndex(min, val, max+eps, AIR_CAST(unsigned int, bins)); /* printf("!%s: %d: index(%g, %g, %g, %d) = %d\n", me, (int)I, min, val, max, bins, idx); */ /* count is a double in order to simplify clamping the hit values to the representable range for nout->type */ count = nrrdDLookup[nout->type](nout->data, idx); incr = nwght ? lup(nwght->data, I) : 1; count = nrrdDClamp[nout->type](count + incr); nrrdDInsert[nout->type](nout->data, idx, count); } } } if (nrrdContentSet_va(nout, func, nin, "%d", bins)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } nout->axis[0].label = (char *)airFree(nout->axis[0].label); nout->axis[0].label = (char *)airStrdup(nout->content); if (!nrrdStateKindNoop) { nout->axis[0].kind = nrrdKindDomain; } airMopOkay(mop); return 0; } int nrrdHistoCheck(const Nrrd *nhist) { static const char me[]="nrrdHistoCheck"; if (!nhist) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdTypeBlock == nhist->type) { biffAddf(NRRD, "%s: has non-scalar %s type", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (nrrdHasNonExist(nhist)) { biffAddf(NRRD, "%s: has non-existent values", me); return 1; } if (1 != nhist->dim) { biffAddf(NRRD, "%s: dim == %u != 1", me, nhist->dim); return 1; } if (!(nhist->axis[0].size > 1)) { biffAddf(NRRD, "%s: has single sample along sole axis", me); return 1; } return 0; } int nrrdHistoDraw(Nrrd *nout, const Nrrd *nin, size_t sy, int showLog, double max) { static const char me[]="nrrdHistoDraw", func[]="dhisto"; char cmt[AIR_STRLEN_MED], stmp[AIR_STRLEN_SMALL]; unsigned int ki, numticks, *linY, *logY, tick, *ticks; double hits, maxhits, usemaxhits; unsigned char *pgmData; airArray *mop; int E; size_t sx, xi, yi, maxhitidx; if (!(nin && nout && sy > 0)) { biffAddf(NRRD, "%s: invalid args", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (nrrdHistoCheck(nin)) { biffAddf(NRRD, "%s: input nrrd not a histogram", me); return 1; } sx = nin->axis[0].size; nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_DATA_BIT); if (nrrdMaybeAlloc_va(nout, nrrdTypeUChar, 2, sx, sy)) { biffAddf(NRRD, "%s: failed to allocate histogram image", me); return 1; } /* perhaps I should be using nrrdAxisInfoCopy */ nout->axis[0].spacing = nout->axis[1].spacing = AIR_NAN; nout->axis[0].thickness = nout->axis[1].thickness = AIR_NAN; nout->axis[0].min = nin->axis[0].min; nout->axis[0].max = nin->axis[0].max; nout->axis[0].center = nout->axis[1].center = nrrdCenterCell; nout->axis[0].label = (char *)airStrdup(nin->axis[0].label); nout->axis[1].label = (char *)airFree(nout->axis[1].label); pgmData = (unsigned char *)nout->data; maxhits = 0.0; maxhitidx = 0; for (xi=0; xitype](nin->data, xi); if (maxhits < hits) { maxhits = hits; maxhitidx = xi; } } if (AIR_EXISTS(max) && max > 0) { usemaxhits = max; } else { usemaxhits = maxhits; } nout->axis[1].min = usemaxhits; nout->axis[1].max = 0; numticks = AIR_CAST(unsigned int, log10(usemaxhits + 1)); mop = airMopNew(); ticks = AIR_CALLOC(numticks, unsigned int); airMopMem(mop, &ticks, airMopAlways); linY = AIR_CALLOC(sx, unsigned int); airMopMem(mop, &linY, airMopAlways); logY = AIR_CALLOC(sx, unsigned int); airMopMem(mop, &logY, airMopAlways); if (!(ticks && linY && logY)) { biffAddf(NRRD, "%s: failed to allocate temp arrays", me); airMopError(mop); return 1; } for (ki=0; kitype](nin->data, xi); linY[xi] = airIndex(0, hits, usemaxhits, AIR_CAST(unsigned int, sy)); logY[xi] = airIndex(0, log10(hits+1), log10(usemaxhits+1), AIR_CAST(unsigned int, sy)); /* printf("%d -> %d,%d", x, linY[x], logY[x]); */ } for (yi=0; yi= logY[xi] ? 0 /* above log curve */ : (yi >= linY[xi] ? 128 /* below log curve, above normal curve */ : 255 /* below log curve, below normal curve */ ) ) : (!showLog ? (yi >= linY[xi] ? 0 : 255) : (yi >= logY[xi] /* above log curve */ ? (!tick ? 0 /* not on tick mark */ : 255) /* on tick mark */ : (yi >= linY[xi] /* below log curve, above normal curve */ ? (!tick ? 128 /* not on tick mark */ : 0) /* on tick mark */ :255 /* below log curve, below normal curve */ ) ) ) ); } } E = 0; sprintf(cmt, "min value: %g\n", nout->axis[0].min); if (!E) E |= nrrdCommentAdd(nout, cmt); sprintf(cmt, "max value: %g\n", nout->axis[0].max); if (!E) E |= nrrdCommentAdd(nout, cmt); sprintf(cmt, "max hits: %g, in bin %s, around value %g\n", maxhits, airSprintSize_t(stmp, maxhitidx), nrrdAxisInfoPos(nout, 0, AIR_CAST(double, maxhitidx))); if (!E) E |= nrrdCommentAdd(nout, cmt); if (!E) E |= nrrdContentSet_va(nout, func, nin, "%s", airSprintSize_t(stmp, sy)); if (E) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } /* bye */ airMopOkay(mop); return 0; } /* ******** nrrdHistoAxis ** ** replace scanlines along one scanline with a histogram of the scanline ** ** this looks at nin->min and nin->max to see if they are both non-NaN. ** If so, it uses these as the range of the histogram, otherwise it ** finds the min and max present in the volume ** ** By its very nature, and by the simplicity of this implemention, ** this can be a slow process due to terrible memory locality. User ** may want to permute axes before and after this, but that can be ** slow too... */ int nrrdHistoAxis(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range, unsigned int hax, size_t bins, int type) { static const char me[]="nrrdHistoAxis", func[]="histax"; int map[NRRD_DIM_MAX]; unsigned int ai, hidx; size_t szIn[NRRD_DIM_MAX], szOut[NRRD_DIM_MAX], size[NRRD_DIM_MAX], coordIn[NRRD_DIM_MAX], coordOut[NRRD_DIM_MAX]; size_t I, hI, num; double val, count; airArray *mop; NrrdRange *range; if (!(nin && nout)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (!(bins > 0)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: bins value (%s) invalid", me, airSprintSize_t(stmp, bins)); return 1; } if (airEnumValCheck(nrrdType, type) || nrrdTypeBlock == type) { biffAddf(NRRD, "%s: invalid nrrd type %d", me, type); return 1; } if (!( hax <= nin->dim-1 )) { biffAddf(NRRD, "%s: axis %d is not in range [0,%d]", me, hax, nin->dim-1); return 1; } mop = airMopNew(); if (_range) { range = nrrdRangeCopy(_range); nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); } else { range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); } airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); size[hax] = bins; if (nrrdMaybeAlloc_nva(nout, type, nin->dim, size)) { biffAddf(NRRD, "%s: failed to alloc output nrrd", me); airMopError(mop); return 1; } /* copy axis information */ for (ai=0; aidim; ai++) { map[ai] = ai != hax ? (int)ai : -1; } nrrdAxisInfoCopy(nout, nin, map, NRRD_AXIS_INFO_NONE); /* axis hax now has to be set manually */ nout->axis[hax].size = bins; nout->axis[hax].spacing = AIR_NAN; /* min and max convey the information */ nout->axis[hax].thickness = AIR_NAN; nout->axis[hax].min = range->min; nout->axis[hax].max = range->max; nout->axis[hax].center = nrrdCenterCell; if (nin->axis[hax].label) { nout->axis[hax].label = AIR_CALLOC(strlen("histax()") + strlen(nin->axis[hax].label) + 1, char); if (nout->axis[hax].label) { sprintf(nout->axis[hax].label, "histax(%s)", nin->axis[hax].label); } else { biffAddf(NRRD, "%s: couldn't allocate output label", me); airMopError(mop); return 1; } } else { nout->axis[hax].label = NULL; } if (!nrrdStateKindNoop) { nout->axis[hax].kind = nrrdKindDomain; } /* the skinny: we traverse the input samples in linear order, and increment the bin in the histogram for the scanline we're in. This is not terribly clever, and the memory locality is a disaster */ nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, szIn); nrrdAxisInfoGet_nva(nout, nrrdAxisInfoSize, szOut); memset(coordIn, 0, NRRD_DIM_MAX*sizeof(size_t)); num = nrrdElementNumber(nin); for (I=0; Itype](nin->data, I); if (AIR_EXISTS(val) && AIR_IN_CL(range->min, val, range->max)) { hidx = airIndex(range->min, val, range->max, AIR_CAST(unsigned int, bins)); memcpy(coordOut, coordIn, nin->dim*sizeof(size_t)); coordOut[hax] = (unsigned int)hidx; NRRD_INDEX_GEN(hI, coordOut, szOut, nout->dim); count = nrrdDLookup[nout->type](nout->data, hI); count = nrrdDClamp[nout->type](count + 1); nrrdDInsert[nout->type](nout->data, hI, count); } NRRD_COORD_INCR(coordIn, szIn, nin->dim, 0); } if (nrrdContentSet_va(nout, func, nin, "%d,%d", hax, bins)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } nrrdBasicInfoInit(nout, (NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | 0 /* what? */ )); airMopOkay(mop); return 0; } int nrrdHistoJoint(Nrrd *nout, const Nrrd *const *nin, const NrrdRange *const *_range, unsigned int numNin, const Nrrd *nwght, const size_t *bins, int type, const int *clamp) { static const char me[]="nrrdHistoJoint", func[]="jhisto"; int skip, hadContent; double val, count, incr, (*lup)(const void *v, size_t I); size_t Iin, Iout, numEl, coord[NRRD_DIM_MAX], totalContentStrlen; airArray *mop; NrrdRange **range; unsigned int nii, ai; /* error checking */ /* nwght can be NULL -> weighting is constant 1.0 */ if (!(nout && nin && bins && clamp)) { biffAddf(NRRD, "%s: got NULL pointer (%p, %p, %p, %p)", me, AIR_VOIDP(nout), AIR_CVOIDP(nin), AIR_CVOIDP(bins), AIR_CVOIDP(clamp)); return 1; } if (!(numNin >= 1)) { biffAddf(NRRD, "%s: need numNin >= 1 (not %d)", me, numNin); return 1; } if (numNin > NRRD_DIM_MAX) { biffAddf(NRRD, "%s: can only deal with up to %d nrrds (not %d)", me, NRRD_DIM_MAX, numNin); return 1; } for (nii=0; niitype) { biffAddf(NRRD, "%s: nin[%d] type %s invalid", me, nii, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } } if (airEnumValCheck(nrrdType, type) || nrrdTypeBlock == type) { biffAddf(NRRD, "%s: invalid nrrd type %d", me, type); return 1; } mop = airMopNew(); range = AIR_CALLOC(numNin, NrrdRange*); airMopAdd(mop, range, airFree, airMopAlways); for (ai=0; ai= 1)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: need bins[%u] >= 1 (not %s)", me, ai, airSprintSize_t(stmp, bins[ai])); return 1; } if (ai && !nrrdSameSize(nin[0], nin[ai], AIR_TRUE)) { biffAddf(NRRD, "%s: nin[0] (n1) mismatch with nin[%u] (n2)", me, ai); return 1; } } /* check nwght */ if (nwght) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; if (nout==nwght) { biffAddf(NRRD, "%s: nout==nwght disallowed", me); return 1; } if (nrrdTypeBlock == nwght->type) { biffAddf(NRRD, "%s: nwght type %s invalid", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (nrrdElementNumber(nin[0]) != nrrdElementNumber(nwght)) { biffAddf(NRRD, "%s: element # in nwght %s != nin[0] %s", me, airSprintSize_t(stmp2, nrrdElementNumber(nin[0])), airSprintSize_t(stmp1, nrrdElementNumber(nwght))); return 1; } lup = nrrdDLookup[nwght->type]; } else { lup = NULL; } /* allocate output nrrd */ if (nrrdMaybeAlloc_nva(nout, type, numNin, bins)) { biffAddf(NRRD, "%s: couldn't allocate output histogram", me); return 1; } hadContent = 0; totalContentStrlen = 0; for (ai=0; aiaxis[ai].size = bins[ai]; nout->axis[ai].spacing = AIR_NAN; nout->axis[ai].thickness = AIR_NAN; nout->axis[ai].min = range[ai]->min; nout->axis[ai].max = range[ai]->max; nout->axis[ai].center = nrrdCenterCell; if (!nrrdStateKindNoop) { nout->axis[ai].kind = nrrdKindDomain; } if (nin[ai]->content) { hadContent = 1; totalContentStrlen += strlen(nin[ai]->content); nout->axis[ai].label = AIR_CALLOC(strlen("histo(,)") + strlen(nin[ai]->content) + 11 + 1, char); if (nout->axis[ai].label) { char stmp[AIR_STRLEN_SMALL]; sprintf(nout->axis[ai].label, "histo(%s,%s)", nin[ai]->content, airSprintSize_t(stmp, bins[ai])); } else { biffAddf(NRRD, "%s: couldn't allocate output label #%u", me, ai); return 1; } } else { nout->axis[ai].label = (char *)airFree(nout->axis[ai].label); totalContentStrlen += 2; } } /* the skinny */ numEl = nrrdElementNumber(nin[0]); for (Iin=0; Iintype](nin[ai]->data, Iin); if (!AIR_EXISTS(val)) { /* coordinate d in the joint histo can't be determined if nin[ai] has a non-existent value here */ skip = 1; break; } if (!AIR_IN_CL(range[ai]->min, val, range[ai]->max)) { if (clamp[ai]) { val = AIR_CLAMP(range[ai]->min, val, range[ai]->max); } else { skip = 1; break; } } coord[ai] = AIR_CAST(size_t, airIndexClampULL(range[ai]->min, val, range[ai]->max, bins[ai])); /* printf(" -> coord = %d; ", coord[d]); fflush(stdout); */ } if (skip) { continue; } NRRD_INDEX_GEN(Iout, coord, bins, numNin); count = nrrdDLookup[nout->type](nout->data, Iout); incr = nwght ? lup(nwght->data, Iin) : 1.0; count = nrrdDClamp[nout->type](count + incr); nrrdDInsert[nout->type](nout->data, Iout, count); } /* HEY: switch to nrrdContentSet_va? */ if (hadContent) { nout->content = AIR_CALLOC(strlen(func) + strlen("()") + numNin*strlen(",") + totalContentStrlen + 1, char); if (nout->content) { size_t len; sprintf(nout->content, "%s(", func); for (ai=0; aicontent); strcpy(nout->content + len, nin[ai]->content ? nin[ai]->content : "?"); len = strlen(nout->content); nout->content[len] = ai < numNin-1 ? ',' : ')'; } nout->content[len+1] = '\0'; } else { biffAddf(NRRD, "%s: couldn't allocate output content", me); return 1; } } airMopOkay(mop); return 0; } /* ******** nrrdHistoThresholdOtsu ** ** does simple Otsu tresholding of a histogram, with a variable exponont. ** When "expo" is 2.0, it computes variance; lower values probably represent ** greater insensitivities to outliers. Idea from ... */ int nrrdHistoThresholdOtsu(double *threshP, const Nrrd *_nhist, double expo) { static const char me[]="nrrdHistoThresholdOtsu"; unsigned int histLen, histIdx, maxIdx; Nrrd *nhist, *nbvar; double *hist, *bvar, thresh, num0, num1, mean0, mean1, onum0, onum1, omean0, omean1, max; airArray *mop; if (!(threshP && _nhist)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdHistoCheck(_nhist)) { biffAddf(NRRD, "%s: input nrrd not a histogram", me); return 1; } mop = airMopNew(); airMopAdd(mop, nhist = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nbvar = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nhist, _nhist, nrrdTypeDouble) || nrrdCopy(nbvar, nhist)) { biffAddf(NRRD, "%s: making local copies", me); airMopError(mop); return 1; } hist = AIR_CAST(double*, nhist->data); bvar = AIR_CAST(double*, nbvar->data); histLen = AIR_CAST(unsigned int, nhist->axis[0].size); num1 = mean1 = 0; for (histIdx=0; histIdx max) { max = bvar[histIdx]; maxIdx = histIdx; } } thresh = maxIdx; } else { thresh = histLen/2; } if (AIR_EXISTS(nhist->axis[0].min) && AIR_EXISTS(nhist->axis[0].max)) { thresh = NRRD_CELL_POS(nhist->axis[0].min, nhist->axis[0].max, histLen, thresh); } *threshP = thresh; airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/nrrd/format.c0000664000175000017500000001013512165631065017002 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ** what a NrrdFormat can assume: ** -- that nio->format has been set to you already ** -- for read(): that nio->path has been set to the path of the file being ** read in, if the information was ever available ** -- for contentStartsLike() and read(): that nio->line contains the ** first line of of the file, in order to determine the file type ** ** what a NrrdFormat has to do: ** -- respect nio->skipData to whatever extent makes sense on top of how the ** NrrdEncoding respects it (by making read and write no-ops). ** nrrdFormatNRRD, for instance, won't create empty detached data files ** if nio->skipData. ** -- determine what NrrdEncoding to use, if there's a choice ** -- respect nrrdStateVerboseIO with messages to stderr, if possible ** ** The "unknown" format is intended as a template for writing new formats. */ static int _nrrdFormatUnknown_available(void) { /* insert code here */ return AIR_FALSE; } static int _nrrdFormatUnknown_nameLooksLike(const char *filename) { /* insert code here */ AIR_UNUSED(filename); return AIR_FALSE; } static int _nrrdFormatUnknown_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { static const char me[]="_nrrdFormatUnknown_fitsInto"; if (!(nrrd && encoding)) { biffMaybeAddf(useBiff, NRRD, "%s: got NULL nrrd (%p) or encoding (%p)", me, AIR_CVOIDP(nrrd), AIR_CVOIDP(encoding)); return AIR_FALSE; } /* insert code here */ return AIR_FALSE; } static int _nrrdFormatUnknown_contentStartsLike(NrrdIoState *nio) { /* insert code here */ AIR_UNUSED(nio); return AIR_FALSE; } static int _nrrdFormatUnknown_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatUnknown_read"; /* insert code here, and remove error handling below */ AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: ERROR!!! trying to read unknown format", me); return 1; } static int _nrrdFormatUnknown_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatUnknown_write"; /* insert code here, and remove error handling below */ AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: ERROR!!! trying to write unknown format", me); return 1; } static const NrrdFormat _nrrdFormatUnknown = { "unknown", AIR_FALSE, /* isImage */ AIR_TRUE, /* readable */ AIR_FALSE, /* usesDIO */ _nrrdFormatUnknown_available, _nrrdFormatUnknown_nameLooksLike, _nrrdFormatUnknown_fitsInto, _nrrdFormatUnknown_contentStartsLike, _nrrdFormatUnknown_read, _nrrdFormatUnknown_write }; const NrrdFormat *const nrrdFormatUnknown = &_nrrdFormatUnknown; const NrrdFormat *const nrrdFormatArray[NRRD_FORMAT_TYPE_MAX+1] = { &_nrrdFormatUnknown, &_nrrdFormatNRRD, &_nrrdFormatPNM, &_nrrdFormatPNG, &_nrrdFormatVTK, &_nrrdFormatText, &_nrrdFormatEPS }; teem-1.11.0~svn6057/src/nrrd/nrrdDefines.h0000664000175000017500000001100512165631065017757 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NRRD_DEFINES_HAS_BEEN_INCLUDED #define NRRD_DEFINES_HAS_BEEN_INCLUDED #include #ifdef __cplusplus extern "C" { #endif /* feel free to set these to higher values and recompile */ #define NRRD_DIM_MAX 16 /* Max array dimension (nrrd->dim) */ #define NRRD_SPACE_DIM_MAX 8 /* Max dimension of "space" around array (nrrd->spaceDim) */ #define NRRD_EXT_NRRD ".nrrd" #define NRRD_EXT_NHDR ".nhdr" #define NRRD_EXT_PGM ".pgm" #define NRRD_EXT_PPM ".ppm" #define NRRD_EXT_PNG ".png" #define NRRD_EXT_VTK ".vtk" #define NRRD_EXT_TEXT ".txt" #define NRRD_EXT_EPS ".eps" /* HEY: should this be renamed -> MAXNUM ? Would be more consistent with other Teem pound-define names */ #define NRRD_KERNEL_PARMS_NUM 8 /* max # arguments to a kernel- this is weird: it isn't the max of any of the NrrdKernels defined by the nrrd library (that is more like 3), but is the max number of parms of any NrrdKernel used by anyone using Teem, such as in gage. Enforcing one global max simplifies implementation. */ /* ** For the 64-bit integer types (not standard except in C99), we used ** to try to use the names for the _MIN and _MAX values which are used ** in C99 (as well as gcc) such as LLONG_MAX, or those used on SGI ** such as LONGLONG_MAX. However, since the tests (in nrrdSanity) ** were re-written to detect overflow based on manipulation of ** specific values, we might as well also define the _MIN and _MAX in ** terms of explicit values (which agree with those defined by C99). */ #define NRRD_LLONG_MAX AIR_LLONG(9223372036854775807) #define NRRD_LLONG_MIN (-NRRD_LLONG_MAX-AIR_LLONG(1)) #define NRRD_ULLONG_MAX AIR_ULLONG(18446744073709551615) /* ** Chances are, you shouldn't mess with these */ /* ---- BEGIN non-NrrdIO */ /* suffix string that indicates percentile-based min/max */ #define NRRD_MINMAX_PERC_SUFF "%" /* ---- END non-NrrdIO */ #define NRRD_COMMENT_CHAR '#' #define NRRD_FILENAME_INCR 32 #define NRRD_COMMENT_INCR 16 #define NRRD_KEYVALUE_INCR 32 #define NRRD_LIST_FLAG "LIST" #define NRRD_PNM_COMMENT "# NRRD>" /* this is designed to be robust against the mungling that xv does, but no promises for any other image programs */ #define NRRD_PNG_FIELD_KEY "NRRD" /* this is the key used for getting nrrd fields into/out of png comments */ #define NRRD_PNG_COMMENT_KEY "NRRD#" /* this is the key used for getting nrrd comments into/out of png comments */ #define NRRD_UNKNOWN "???" /* how to represent something unknown in a field of the nrrd header, when it being unknown is not an error */ #define NRRD_NONE "none" /* like NRRD_UNKNOWN, but with an air of certainty */ #ifdef __cplusplus } #endif #endif /* NRRD_DEFINES_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/nrrd/enumsNrrd.c0000664000175000017500000011560412177242445017501 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" /* ** Rules of thumb for editing these things. The airEnum definitions are ** unfortunately EXTREMELY sensitive to small typo errors, and there is ** no good way to detect the errors. So: ** ** 1) Be awake and undistracted. Turn down the music. ** 2) When editing the char arrays, make sure that you put commas ** where you mean them to be. C's automatic string concatenation ** is not your friend here. In fact, EXPLOIT the fact that you can have ** a comma after the last element of a list (of strings)- it decreases ** the chances that adding a new element at the end will be thwarted by ** the lack of a comma at the end of the previous (and previously last) ** string. ** 3) When editing the *StrEqv and *ValEqv arrays, make absolutely ** sure that both are changed in parallel. Use only one enum value ** per line; putting all equivalents on that line, and make sure that ** there is one line in both *StrEqv and *ValEqv for all the possible ** enum values, and that there are as many elements in each line. ** 4) Make sure that additions here are reflected in nrrdEnums.h and ** vice versa. */ /* ------------------------ nrrdFormat ------------------------- */ static const char * _nrrdFormatTypeStr[NRRD_FORMAT_TYPE_MAX+1] = { "(unknown_format)", "nrrd", "pnm", "png", "vtk", "text", "eps", }; static const char * _nrrdFormatTypeDesc[NRRD_FORMAT_TYPE_MAX+1] = { "unknown_format", "native format for nearly raw raster data", "Portable aNy Map: includes PGM for grayscale and PPM for color", "Portable Network Graphics: lossless compression of 8- and 16-bit data", "Visualization ToolKit STRUCTURED_POINTS data", "white-space-delimited plain text encoding of 2-D float array", "Encapsulated PostScript images", }; static const char * _nrrdFormatTypeStrEqv[] = { "nrrd", "pnm", "png", "vtk", "table", "text", "txt", "eps", "" }; static const int _nrrdFormatTypeValEqv[] = { nrrdFormatTypeNRRD, nrrdFormatTypePNM, nrrdFormatTypePNG, nrrdFormatTypeVTK, nrrdFormatTypeText, nrrdFormatTypeText, nrrdFormatTypeText, nrrdFormatTypeEPS, }; airEnum _nrrdFormatType = { "format", NRRD_FORMAT_TYPE_MAX, _nrrdFormatTypeStr, NULL, _nrrdFormatTypeDesc, _nrrdFormatTypeStrEqv, _nrrdFormatTypeValEqv, AIR_FALSE }; const airEnum *const nrrdFormatType = &_nrrdFormatType; /* ------------------------ nrrdType ------------------------- */ static const char * _nrrdTypeStr[NRRD_TYPE_MAX+1] = { "(unknown_type)", "signed char", "unsigned char", "short", "unsigned short", "int", "unsigned int", "long long int", "unsigned long long int", "float", "double", "block", }; static const char * _nrrdTypeDesc[NRRD_TYPE_MAX+1] = { "unknown type", "signed 1-byte integer", "unsigned 1-byte integer", "signed 2-byte integer", "unsigned 2-byte integer", "signed 4-byte integer", "unsigned 4-byte integer", "signed 8-byte integer", "unsigned 8-byte integer", "4-byte floating point", "8-byte floating point", "size user-defined at run-time", }; #define ntCH nrrdTypeChar #define ntUC nrrdTypeUChar #define ntSH nrrdTypeShort #define ntUS nrrdTypeUShort #define ntIN nrrdTypeInt #define ntUI nrrdTypeUInt #define ntLL nrrdTypeLLong #define ntUL nrrdTypeULLong #define ntFL nrrdTypeFloat #define ntDB nrrdTypeDouble #define ntBL nrrdTypeBlock static const char * _nrrdTypeStrEqv[] = { "signed char", /* but NOT just "char" */ "int8", "int8_t", "uchar", "unsigned char", "uint8", "uint8_t", "short", "short int", "signed short", "signed short int", "int16", "int16_t", "ushort", "unsigned short", "unsigned short int", "uint16", "uint16_t", "int", "signed int", "int32", "int32_t", "uint", "unsigned int", "uint32", "uint32_t", "longlong", "long long", "long long int", "signed long long", "signed long long int", "int64", "int64_t", "ulonglong", "unsigned long long", "unsigned long long int", "uint64", "uint64_t", "float", "double", "block", "" }; static const int _nrrdTypeValEqv[] = { ntCH, ntCH, ntCH, ntUC, ntUC, ntUC, ntUC, ntSH, ntSH, ntSH, ntSH, ntSH, ntSH, ntUS, ntUS, ntUS, ntUS, ntUS, ntIN, ntIN, ntIN, ntIN, ntUI, ntUI, ntUI, ntUI, ntLL, ntLL, ntLL, ntLL, ntLL, ntLL, ntLL, ntUL, ntUL, ntUL, ntUL, ntUL, ntFL, ntDB, ntBL, }; airEnum _nrrdType = { "type", NRRD_TYPE_MAX, _nrrdTypeStr, NULL, _nrrdTypeDesc, _nrrdTypeStrEqv, _nrrdTypeValEqv, AIR_FALSE }; const airEnum *const nrrdType = &_nrrdType; /* ------------------------ nrrdEncodingType ------------------------- */ static const char * _nrrdEncodingTypeStr[NRRD_ENCODING_TYPE_MAX+1] = { "(unknown_encoding)", "raw", "ascii", "hex", "gz", "bz2", }; static const char * _nrrdEncodingTypeDesc[NRRD_ENCODING_TYPE_MAX+1] = { "unknown encoding", "file is byte-for-byte same as memory representation", "values written out in ASCII", "case-insenstive hexadecimal encoding (2 chars / byte)", "gzip compression of binary encoding", "bzip2 compression of binary encoding", }; static const char * _nrrdEncodingTypeStrEqv[] = { "raw", "txt", "text", "ascii", "hex", "gz", "gzip", "bz2", "bzip2", "" }; static const int _nrrdEncodingTypeValEqv[] = { nrrdEncodingTypeRaw, nrrdEncodingTypeAscii, nrrdEncodingTypeAscii, nrrdEncodingTypeAscii, nrrdEncodingTypeHex, nrrdEncodingTypeGzip, nrrdEncodingTypeGzip, nrrdEncodingTypeBzip2, nrrdEncodingTypeBzip2, }; airEnum _nrrdEncodingType = { "encoding", NRRD_ENCODING_TYPE_MAX, _nrrdEncodingTypeStr, NULL, _nrrdEncodingTypeDesc, _nrrdEncodingTypeStrEqv, _nrrdEncodingTypeValEqv, AIR_FALSE }; const airEnum *const nrrdEncodingType = &_nrrdEncodingType; /* ------------------------ nrrdCenter ------------------------- */ static const char * _nrrdCenterStr[NRRD_CENTER_MAX+1] = { "(unknown_center)", "node", "cell", }; static const char * _nrrdCenterDesc[NRRD_CENTER_MAX+1] = { "unknown centering", "samples are at boundaries between elements along axis", "samples are at centers of elements along axis", }; static const airEnum _nrrdCenter_enum = { "centering", NRRD_CENTER_MAX, _nrrdCenterStr, NULL, _nrrdCenterDesc, NULL, NULL, AIR_FALSE }; const airEnum *const nrrdCenter = &_nrrdCenter_enum; /* ------------------------ nrrdKind ------------------------- */ /* nrrdKindUnknown, nrrdKindDomain, * 1: any image domain * nrrdKindSpace, * 2: a spatial domain * nrrdKindTime, * 3: a temporal domain * * -------------------------- end domain kinds * * -------------------------- begin range kinds * nrrdKindList, * 4: any list of values, non-resample-able * nrrdKindPoint, * 5: coords of a point * nrrdKindVector, * 6: coeffs of (contravariant) vector * nrrdKindCovariantVector, * 7: coeffs of covariant vector (eg gradient) * nrrdKindNormal, * 8: coeffs of unit-length covariant vector * * -------------------------- end arbitrary size kinds * * -------------------------- begin size-specific kinds * nrrdKindStub, * 9: axis with one sample (a placeholder) * nrrdKindScalar, * 10: effectively, same as a stub * nrrdKindComplex, * 11: real and imaginary components * nrrdKind2Vector, * 12: 2 component vector * nrrdKind3Color, * 13: ANY 3-component color value * nrrdKindRGBColor, * 14: RGB, no colorimetry * nrrdKindHSVColor, * 15: HSV, no colorimetry * nrrdKindXYZColor, * 16: perceptual primary colors * nrrdKind4Color, * 17: ANY 4-component color value * nrrdKindRGBAColor, * 18: RGBA, no colorimetry * nrrdKind3Vector, * 19: 3-component vector * nrrdKind3Gradient, * 20: 3-component covariant vector * nrrdKind3Normal, * 21: 3-component covector, assumed normalized * nrrdKind4Vector, * 22: 4-component vector * nrrdKindQuaternion, * 23: (w,x,y,z), not necessarily normalized * nrrdKind2DSymMatrix, * 24: Mxx Mxy Myy * nrrdKind2DMaskedSymMatrix, * 25: mask Mxx Mxy Myy * nrrdKind2DMatrix, * 26: Mxx Mxy Myx Myy * nrrdKind2DMaskedMatrix, * 27: mask Mxx Mxy Myx Myy * nrrdKind3DSymMatrix, * 28: Mxx Mxy Mxz Myy Myz Mzz * nrrdKind3DMaskedSymMatrix, * 29: mask Mxx Mxy Mxz Myy Myz Mzz * nrrdKind3DMatrix, * 30: Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz * nrrdKind3DMaskedMatrix, * 31: mask Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz * */ static const char * _nrrdKindStr[NRRD_KIND_MAX+1] = { "(unknown_kind)", "domain", "space", "time", "list", "point", "vector", "covariant-vector", "normal", "stub", "scalar", "complex", "2-vector", "3-color", "RGB-color", "HSV-color", "XYZ-color", "4-color", "RGBA-color", "3-vector", "3-gradient", "3-normal", "4-vector", "quaternion", "2D-symmetric-matrix", "2D-masked-symmetric-matrix", "2D-matrix", "2D-masked-matrix", "3D-symmetric-matrix", "3D-masked-symmetric-matrix", "3D-matrix", "3D-masked-matrix", }; static const char * _nrrdKindDesc[NRRD_KIND_MAX+1] = { "unknown kind", "a domain variable of the function which the nrrd samples", "a spatial domain, like the axes of a measured volume image", "a temporal domain, as from time-varying measurements", "some list of attributes; it makes no sense to resample along these", "coordinates of a point", "coefficients of a (contravariant) vector", "coefficients of a covariant vector, such as a gradient", "coefficients of a normalized covariant vector", "a place-holder axis with a single sample", "axis used to indicate that the nrrd contains a scalar value", "real and imaginary parts of a value", "a 2-component vector", "any 3-component color value", "red-green-blue color", "hue-saturation-value single hexcone color", "perceptual primaries color", "any 4-component color value", "red-green-blue-alpha color", "a 3-element (contravariant) vector", "a 3-element gradient (covariant) vector", "a 3-element (covariant) vector which is assumed normalized", "a 4-element (contravariant) vector", "quaternion: x y z w", "3 elements of 2D symmetric matrix: Mxx Mxy Myy", "mask plus 3 elements of 2D symmetric matrix: mask Mxx Mxy Myy", "4 elements of general 2D matrix: Mxx Mxy Myx Myy", "mask plus 4 elements of general 2D matrix: mask Mxx Mxy Myx Myy", "6 elements of 3D symmetric matrix: Mxx Mxy Mxz Myy Myz Mzz", "mask plus 6 elements of 3D symmetric matrix: mask Mxx Mxy Mxz Myy Myz Mzz", "9 elements of general 3D matrix: Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz", "mask plus 9 elements of general 3D matrix: mask Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz", }; static const char * _nrrdKindStr_Eqv[] = { "domain", "space", "time", "list", "point", "vector", "contravariant-vector", "covariant-vector", "normal", "stub", "scalar", "complex", "2-vector", "3-color", "RGB-color", "RGBcolor", "RGB", "HSV-color", "HSVcolor", "HSV", "XYZ-color", "4-color", "RGBA-color", "RGBAcolor", "RGBA", "3-vector", "3-gradient", "3-normal", "4-vector", "quaternion", "2D-symmetric-matrix", "2D-sym-matrix", "2D-symmetric-tensor", "2D-sym-tensor", "2D-masked-symmetric-matrix", "2D-masked-sym-matrix", "2D-masked-symmetric-tensor", "2D-masked-sym-tensor", "2D-matrix", "2D-tensor", "2D-masked-matrix", "2D-masked-tensor", "3D-symmetric-matrix", "3D-sym-matrix", "3D-symmetric-tensor", "3D-sym-tensor", "3D-masked-symmetric-matrix", "3D-masked-sym-matrix", "3D-masked-symmetric-tensor", "3D-masked-sym-tensor", "3D-matrix", "3D-tensor", "3D-masked-matrix", "3D-masked-tensor", "" }; static int _nrrdKindVal_Eqv[] = { nrrdKindDomain, nrrdKindSpace, nrrdKindTime, nrrdKindList, nrrdKindPoint, nrrdKindVector, nrrdKindVector, nrrdKindCovariantVector, nrrdKindNormal, nrrdKindStub, nrrdKindScalar, nrrdKindComplex, nrrdKind2Vector, nrrdKind3Color, nrrdKindRGBColor, nrrdKindRGBColor, nrrdKindRGBColor, nrrdKindHSVColor, nrrdKindHSVColor, nrrdKindHSVColor, nrrdKindXYZColor, nrrdKind4Color, nrrdKindRGBAColor, nrrdKindRGBAColor, nrrdKindRGBAColor, nrrdKind3Vector, nrrdKind3Gradient, nrrdKind3Normal, nrrdKind4Vector, nrrdKindQuaternion, nrrdKind2DSymMatrix, nrrdKind2DSymMatrix, nrrdKind2DSymMatrix, nrrdKind2DSymMatrix, nrrdKind2DMaskedSymMatrix, nrrdKind2DMaskedSymMatrix, nrrdKind2DMaskedSymMatrix, nrrdKind2DMaskedSymMatrix, nrrdKind2DMatrix, nrrdKind2DMatrix, nrrdKind2DMaskedMatrix, nrrdKind2DMaskedMatrix, nrrdKind3DSymMatrix, nrrdKind3DSymMatrix, nrrdKind3DSymMatrix, nrrdKind3DSymMatrix, nrrdKind3DMaskedSymMatrix, nrrdKind3DMaskedSymMatrix, nrrdKind3DMaskedSymMatrix, nrrdKind3DMaskedSymMatrix, nrrdKind3DMatrix, nrrdKind3DMatrix, nrrdKind3DMaskedMatrix, nrrdKind3DMaskedMatrix, }; static const airEnum _nrrdKind_enum = { "kind", NRRD_KIND_MAX, _nrrdKindStr, NULL, _nrrdKindDesc, _nrrdKindStr_Eqv, _nrrdKindVal_Eqv, AIR_FALSE }; const airEnum *const nrrdKind = &_nrrdKind_enum; /* ------------------------ nrrdField ------------------------- */ static const char * _nrrdFieldStr[NRRD_FIELD_MAX+1] = { "Ernesto \"Che\" Guevara", "#", "content", "number", "type", "block size", "dimension", "space", "space dimension", "sizes", "spacings", "thicknesses", "axis mins", "axis maxs", "space directions", "centerings", "kinds", "labels", "units", "min", "max", "old min", "old max", "endian", "encoding", "line skip", "byte skip", "key/value", /* this is the one field for which the canonical string here is totally different from the field identifier in the NRRD file format (":="). We include nrrdField_keyvalue in the enum because it is very useful to have a consistent way of identifying lines in the format */ "sample units", "space units", "space origin", "measurement frame", "data file", }; static const char * _nrrdFieldDesc[NRRD_FIELD_MAX+1] = { "unknown field identifier", "comment", "short description of whole array and/or its provenance", "total number of samples in array", "type of sample value", "number of bytes in one block (for block-type)", "number of axes in array", "identifier for space in which array grid lies", "dimension of space in which array grid lies", "list of number of samples along each axis, aka \"dimensions\" of the array", "list of sample spacings along each axis", "list of sample thicknesses along each axis", "list of minimum positions associated with each axis", "list of maximum positions associated with each axis", "list of direction inter-sample vectors for each axis", "list of sample centerings for each axis", "list of kinds for each axis", "list of short descriptions for each axis", "list of units in which each axes' spacing and thickness is measured", "supposed minimum array value", "supposed maximum array value", "minimum array value prior to quantization", "maximum array value prior to quantization", "endiannes of data as written in file", "encoding of data written in file", "number of lines to skip prior to byte skip and reading data", "number of bytes to skip after line skip and prior to reading data", "string-based key/value pairs", "units of measurement of (scalar) values inside array itself", "list of units for measuring origin and direct vectors' coefficients", "location in space of center of first (lowest memory address) sample", "maps coords of (non-scalar) values to coords of surrounding space", "with detached headers, where is data to be found", }; static const char * _nrrdFieldStrEqv[] = { "#", "content", "number", "type", "block size", "blocksize", "dimension", "space", "space dimension", "spacedimension", "sizes", "spacings", "thicknesses", "axis mins", "axismins", "axis maxs", "axismaxs", "space directions", "spacedirections", "centers", "centerings", "kinds", "labels", "units", "min", "max", "old min", "oldmin", "old max", "oldmax", "endian", "encoding", "line skip", "lineskip", "byte skip", "byteskip", "key/value", /* bogus, here to keep the airEnum complete */ "sample units", "sampleunits", "space units", "spaceunits", "space origin", "spaceorigin", "measurement frame", "measurementframe", "data file", "datafile", "" }; static const int _nrrdFieldValEqv[] = { nrrdField_comment, nrrdField_content, nrrdField_number, nrrdField_type, nrrdField_block_size, nrrdField_block_size, nrrdField_dimension, nrrdField_space, nrrdField_space_dimension, nrrdField_space_dimension, nrrdField_sizes, nrrdField_spacings, nrrdField_thicknesses, nrrdField_axis_mins, nrrdField_axis_mins, nrrdField_axis_maxs, nrrdField_axis_maxs, nrrdField_space_directions, nrrdField_space_directions, nrrdField_centers, nrrdField_centers, nrrdField_kinds, nrrdField_labels, nrrdField_units, nrrdField_min, nrrdField_max, nrrdField_old_min, nrrdField_old_min, nrrdField_old_max, nrrdField_old_max, nrrdField_endian, nrrdField_encoding, nrrdField_line_skip, nrrdField_line_skip, nrrdField_byte_skip, nrrdField_byte_skip, nrrdField_keyvalue, nrrdField_sample_units, nrrdField_sample_units, nrrdField_space_units, nrrdField_space_units, nrrdField_space_origin, nrrdField_space_origin, nrrdField_measurement_frame, nrrdField_measurement_frame, nrrdField_data_file, nrrdField_data_file, }; static const airEnum _nrrdField = { "nrrd_field", NRRD_FIELD_MAX, _nrrdFieldStr, NULL, _nrrdFieldDesc, _nrrdFieldStrEqv, _nrrdFieldValEqv, AIR_FALSE /* field identifiers not case sensitive */ }; const airEnum *const nrrdField = &_nrrdField; /* ------------------------ nrrdSpace ------------------------- */ /* nrrdSpaceUnknown, nrrdSpaceRightAnteriorSuperior, * 1: NIFTI-1 (right-handed) * nrrdSpaceLeftAnteriorSuperior, * 2: standard Analyze (left-handed) * nrrdSpaceLeftPosteriorSuperior, * 3: DICOM 3.0 (right-handed) * nrrdSpaceRightAnteriorSuperiorTime, * 4: * nrrdSpaceLeftAnteriorSuperiorTime, * 5: * nrrdSpaceLeftPosteriorSuperiorTime, * 6: * nrrdSpaceScannerXYZ, * 7: ACR/NEMA 2.0 (pre-DICOM 3.0) * nrrdSpaceScannerXYZTime, * 8: * nrrdSpace3DRightHanded, * 9: * nrrdSpace3DLeftHanded, * 10: * nrrdSpace3DRightHandedTime, * 11: * nrrdSpace3DLeftHandedTime, * 12: * nrrdSpaceLast */ static const char * _nrrdSpaceStr[NRRD_SPACE_MAX+1] = { "(unknown_space)", "right-anterior-superior", "left-anterior-superior", "left-posterior-superior", "right-anterior-superior-time", "left-anterior-superior-time", "left-posterior-superior-time", "scanner-xyz", "scanner-xyz-time", "3D-right-handed", "3D-left-handed", "3D-right-handed-time", "3D-left-handed-time", }; static const char * _nrrdSpaceDesc[NRRD_SPACE_MAX+1] = { "unknown space", "right-anterior-superior (used in NIFTI-1 and SPL's 3D Slicer)", "left-anterior-superior (used in Analyze 7.5)", "left-posterior-superior (used in DICOM 3)", "right-anterior-superior-time", "left-anterior-superior-time", "left-posterior-superior-time", "scanner-xyz (used in ACR/NEMA 2.0)", "scanner-xyz-time", "3D-right-handed", "3D-left-handed", "3D-right-handed-time", "3D-left-handed-time", }; static const char * _nrrdSpaceStrEqv[] = { "right-anterior-superior", "right anterior superior", "rightanteriorsuperior", "RAS", "left-anterior-superior", "left anterior superior", "leftanteriorsuperior", "LAS", "left-posterior-superior", "left posterior superior", "leftposteriorsuperior", "LPS", "right-anterior-superior-time", "right anterior superior time", "rightanteriorsuperiortime", "RAST", "left-anterior-superior-time", "left anterior superior time", "leftanteriorsuperiortime", "LAST", "left-posterior-superior-time", "left posterior superior time", "leftposteriorsuperiortime", "LPST", "scanner-xyz", "scanner-xyz-time", "scanner-xyzt", "3D-right-handed", "3D right handed", "3Drighthanded", "3D-left-handed", "3D left handed", "3Dlefthanded", "3D-right-handed-time", "3D right handed time", "3Drighthandedtime", "3D-left-handed-time", "3D left handed time", "3Dlefthandedtime", "" }; static const int _nrrdSpaceValEqv[] = { nrrdSpaceRightAnteriorSuperior, nrrdSpaceRightAnteriorSuperior, nrrdSpaceRightAnteriorSuperior, nrrdSpaceRightAnteriorSuperior, nrrdSpaceLeftAnteriorSuperior, nrrdSpaceLeftAnteriorSuperior, nrrdSpaceLeftAnteriorSuperior, nrrdSpaceLeftAnteriorSuperior, nrrdSpaceLeftPosteriorSuperior, nrrdSpaceLeftPosteriorSuperior, nrrdSpaceLeftPosteriorSuperior, nrrdSpaceLeftPosteriorSuperior, nrrdSpaceRightAnteriorSuperiorTime, nrrdSpaceRightAnteriorSuperiorTime, nrrdSpaceRightAnteriorSuperiorTime, nrrdSpaceRightAnteriorSuperiorTime, nrrdSpaceLeftAnteriorSuperiorTime, nrrdSpaceLeftAnteriorSuperiorTime, nrrdSpaceLeftAnteriorSuperiorTime, nrrdSpaceLeftAnteriorSuperiorTime, nrrdSpaceLeftPosteriorSuperiorTime, nrrdSpaceLeftPosteriorSuperiorTime, nrrdSpaceLeftPosteriorSuperiorTime, nrrdSpaceLeftPosteriorSuperiorTime, nrrdSpaceScannerXYZ, nrrdSpaceScannerXYZTime, nrrdSpaceScannerXYZTime, nrrdSpace3DRightHanded, nrrdSpace3DRightHanded, nrrdSpace3DRightHanded, nrrdSpace3DLeftHanded, nrrdSpace3DLeftHanded, nrrdSpace3DLeftHanded, nrrdSpace3DRightHandedTime, nrrdSpace3DRightHandedTime, nrrdSpace3DRightHandedTime, nrrdSpace3DLeftHandedTime, nrrdSpace3DLeftHandedTime, nrrdSpace3DLeftHandedTime }; static const airEnum _nrrdSpace = { "space", NRRD_SPACE_MAX, _nrrdSpaceStr, NULL, _nrrdSpaceDesc, _nrrdSpaceStrEqv, _nrrdSpaceValEqv, AIR_FALSE }; const airEnum *const nrrdSpace = &_nrrdSpace; /* ------------------------ nrrdSpacingStatus ------------------------- */ static const char * _nrrdSpacingStatusStr[NRRD_SPACING_STATUS_MAX+1] = { "(unknown_status)", "none", "scalarNoSpace", "scalarWithSpace", "direction", }; static const char * _nrrdSpacingStatusDesc[NRRD_BOUNDARY_MAX+1] = { "unknown spacing status behavior", "neither axis->spacing nor axis->spaceDirection set", "axis->spacing set normally", "axis->spacing set, with surround space (?)", "axis->spaceDirection set normally", }; static const airEnum _nrrdSpacingStatus = { "spacing status", NRRD_SPACING_STATUS_MAX, _nrrdSpacingStatusStr, NULL, _nrrdSpacingStatusDesc, NULL, NULL, AIR_FALSE }; const airEnum *const nrrdSpacingStatus = &_nrrdSpacingStatus; /* ---- BEGIN non-NrrdIO */ /* ------------------------ nrrdBoundary ------------------------- */ static const char * _nrrdBoundaryStr[NRRD_BOUNDARY_MAX+1] = { "(unknown_boundary)", "pad", "bleed", "wrap", "weight", "mirror" }; static const char * _nrrdBoundaryDesc[NRRD_BOUNDARY_MAX+1] = { "unknown boundary behavior", "pad with some specified value", "copy values from edge outward as needed", "wrap around to other end of axis", "re-weight (by normalization) samples within axis range", "mirror folding" }; static const airEnum _nrrdBoundary = { "boundary behavior", NRRD_BOUNDARY_MAX, _nrrdBoundaryStr, NULL, _nrrdBoundaryDesc, NULL, NULL, AIR_FALSE }; const airEnum *const nrrdBoundary = &_nrrdBoundary; /* ------------------------ nrrdMeasure ------------------------- */ static const char * _nrrdMeasureStr[NRRD_MEASURE_MAX+1] = { "(unknown_measure)", "min", "max", "mean", "median", "mode", "product", "sum", "L1", "L2", "L4", "normalizedL2", "RMS", "Linf", "variance", "stdv", "CoV", "skew", "line-slope", "line-intercept", "line-error", "histo-min", "histo-max", "histo-mean", "histo-median", "histo-mode", "histo-product", "histo-sum", "histo-L2", "histo-variance", "histo-SD", }; static const char * _nrrdMeasureDesc[NRRD_MEASURE_MAX+1] = { "unknown measure", "minimum of values", "maximum of values", "mean of values", "median of values", "mode of values", "product of values", "sum of values", "L1 norm of values", "L2 norm of values", "L4 norm of values", "L2 norm of values divided by # of values", "Root of Mean of Squares", "Linf norm of values", "variance of values", "standard deviation of values", "coefficient of variation of values", "skew of values", "slope of line of best fit", "y-intercept of line of best fit", "error of line fitting", "minimum of histogrammed values", "maximum of histogrammed values", "mean of histogrammed values", "median of histogrammed values", "mode of histogrammed values", "product of histogrammed values", "sum of histogrammed values", "L2 norm of histogrammed values", "variance of histogrammed values", "standard deviation of histogrammed values", }; static const char * _nrrdMeasureStrEqv[] = { "min", "max", "mean", "median", "mode", "product", "prod", "sum", "L1", "L2", "L4", "normalizedL2", "normL2", "nL2", "rootmeansquare", "rms", "Linf", "variance", "var", "SD", "stdv", "cov", "skew", "skewness", "slope", "line-slope", "intc", "intercept", "line-intc", "line-intercept", "error", "line-error", "histo-min", "histo-max", "histo-mean", "histo-median", "histo-mode", "histo-product", "histo-sum", "histo-l2", "histo-variance", "histo-var", "histo-sd", "" }; static const int _nrrdMeasureValEqv[] = { nrrdMeasureMin, nrrdMeasureMax, nrrdMeasureMean, nrrdMeasureMedian, nrrdMeasureMode, nrrdMeasureProduct, nrrdMeasureProduct, nrrdMeasureSum, nrrdMeasureL1, nrrdMeasureL2, nrrdMeasureL4, nrrdMeasureNormalizedL2, nrrdMeasureNormalizedL2, nrrdMeasureNormalizedL2, nrrdMeasureRootMeanSquare, nrrdMeasureRootMeanSquare, nrrdMeasureLinf, nrrdMeasureVariance, nrrdMeasureVariance, nrrdMeasureSD, nrrdMeasureSD, nrrdMeasureCoV, nrrdMeasureSkew, nrrdMeasureSkew, nrrdMeasureLineSlope, nrrdMeasureLineSlope, nrrdMeasureLineIntercept, nrrdMeasureLineIntercept, nrrdMeasureLineIntercept, nrrdMeasureLineIntercept, nrrdMeasureLineError, nrrdMeasureLineError, nrrdMeasureHistoMin, nrrdMeasureHistoMax, nrrdMeasureHistoMean, nrrdMeasureHistoMedian, nrrdMeasureHistoMode, nrrdMeasureHistoProduct, nrrdMeasureHistoSum, nrrdMeasureHistoL2, nrrdMeasureHistoVariance, nrrdMeasureHistoVariance, nrrdMeasureHistoSD, }; static const airEnum _nrrdMeasure = { "measure", NRRD_MEASURE_MAX, _nrrdMeasureStr, NULL, _nrrdMeasureDesc, _nrrdMeasureStrEqv, _nrrdMeasureValEqv, AIR_FALSE }; const airEnum *const nrrdMeasure = &_nrrdMeasure; /* ------------------------ nrrdUnaryOp ---------------------- */ #define nuNeg nrrdUnaryOpNegative #define nuRcp nrrdUnaryOpReciprocal #define nuSin nrrdUnaryOpSin #define nuCos nrrdUnaryOpCos #define nuTan nrrdUnaryOpTan #define nuAsn nrrdUnaryOpAsin #define nuAcs nrrdUnaryOpAcos #define nuAtn nrrdUnaryOpAtan #define nuExp nrrdUnaryOpExp #define nuLge nrrdUnaryOpLog #define nuLg2 nrrdUnaryOpLog2 #define nuLgt nrrdUnaryOpLog10 #define nuL1p nrrdUnaryOpLog1p #define nuEm1 nrrdUnaryOpExpm1 #define nuSqt nrrdUnaryOpSqrt #define nuCbt nrrdUnaryOpCbrt #define nuErf nrrdUnaryOpErf #define nuNerf nrrdUnaryOpNerf #define nuCil nrrdUnaryOpCeil #define nuFlr nrrdUnaryOpFloor #define nuRup nrrdUnaryOpRoundUp #define nuRdn nrrdUnaryOpRoundDown #define nuAbs nrrdUnaryOpAbs #define nuSgn nrrdUnaryOpSgn #define nuExs nrrdUnaryOpExists #define nuRnd nrrdUnaryOpRand #define nuNrn nrrdUnaryOpNormalRand #define nuoIf nrrdUnaryOpIf #define nuZer nrrdUnaryOpZero #define nuOne nrrdUnaryOpOne static const char * _nrrdUnaryOpStr[NRRD_UNARY_OP_MAX+1] = { "(unknown_unary_op)", "-", "r", "sin", "cos", "tan", "asin", "acos", "atan", "exp", "log", "log2", "log10", "log1p", "expm1", "sqrt", "cbrt", "erf", "nerf", "ceil", "floor", "roundup", "rounddown", "abs", "sgn", "exists", "rand", "normrand", "if", "zero", "one" }; static const char * _nrrdUnaryOpDesc[NRRD_UNARY_OP_MAX+1] = { "unknown unary op", "negative; additive inverse", "reciprocal; multiplicative inverse", "sin", "cos", "tan", "arcsin", "arccos", "arctan", "e raised to something", "natural (base e) logarithm", "base 2 logarithm", "base 10 logarithm", "accurate ln(1+x)", "accurate exp(x)-1", "square root", "cube root", "error function (integral of gaussian)", "erf, mapped to range (0,1)", "smallest integer greater than or equal", "largest integer less than or equal", "round to closest integer (0.5 rounded to 1)", "round to closest integer (0.5 rounded to 0)", "absolute value", "sign of value (-1, 0, or 1)", "value is not infinity or NaN", "uniformly distributed random value between 0 and 1", "normally distributed random value, mean 0, stdv 1", "if nonzero, 1, else 0", "always zero", "always one" }; static const char * _nrrdUnaryOpStrEqv[] = { "-", "neg", "negative", "minus", "r", "recip", "sin", "cos", "tan", "asin", "arcsin", "acos", "arccos", "atan", "arctan", "exp", "ln", "log", "log2", "log10", "ln1p", "log1p", "expm1", "sqrt", "cbrt", "erf", "nerf", "ceil", "floor", "roundup", "rup", "rounddown", "rdown", "rdn", "abs", "fabs", "sgn", "sign", "exists", "rand", "normalrand", "normrand", "nrand", "if", "zero", "0", "one", "1", "" }; static const int _nrrdUnaryOpValEqv[] = { nuNeg, nuNeg, nuNeg, nuNeg, nuRcp, nuRcp, nuSin, nuCos, nuTan, nuAsn, nuAsn, nuAcs, nuAcs, nuAtn, nuAtn, nuExp, nuLge, nuLge, nuLg2, nuLgt, nuL1p, nuL1p, nuEm1, nuSqt, nuCbt, nuErf, nuNerf, nuCil, nuFlr, nuRup, nuRup, nuRdn, nuRdn, nuRdn, nuAbs, nuAbs, nuSgn, nuSgn, nuExs, nuRnd, nuNrn, nuNrn, nuNrn, nuoIf, nuZer, nuZer, nuOne, nuOne }; static const airEnum _nrrdUnaryOp_enum = { "unary op", NRRD_UNARY_OP_MAX, _nrrdUnaryOpStr, NULL, _nrrdUnaryOpDesc, _nrrdUnaryOpStrEqv, _nrrdUnaryOpValEqv, AIR_FALSE }; const airEnum *const nrrdUnaryOp = &_nrrdUnaryOp_enum; /* ------------------------ nrrdBinaryOp ---------------------- */ static const char * _nrrdBinaryOpStr[NRRD_BINARY_OP_MAX+1] = { "(unknown_binary_op)", "+", "-", "x", "/", "^", "spow", "fpow", "%", "fmod", "atan2", "min", "max", "lt", "lte", "gt", "gte", "comp", "eq", "neq", "exists", "if", "nrand", "rrand", }; static const char * _nrrdBinaryOpDesc[NRRD_BINARY_OP_MAX+1] = { "unknown binary op", "add", "subtract", "multiply", "divide", "power", "signed power", "one minus power of one minus", "integer modulo", "fractional modulo", "two-argment arctangent based on atan2()", "miniumum", "maximum", "less then", "less then or equal", "greater than", "greater than or equal", "compare (resulting in -1, 0, or 1)", "equal", "not equal", "if exists(a), then a, else b", "if a, then a, else b", "a + b*gaussianNoise", "sample of Rician with mu a and sigma b" }; #define nbAdd nrrdBinaryOpAdd #define nbSub nrrdBinaryOpSubtract #define nbMul nrrdBinaryOpMultiply #define nbDiv nrrdBinaryOpDivide #define nbPow nrrdBinaryOpPow #define nbSpw nrrdBinaryOpSgnPow #define nbFpw nrrdBinaryOpFlippedSgnPow #define nbMod nrrdBinaryOpMod #define nbFmd nrrdBinaryOpFmod #define nbAtn nrrdBinaryOpAtan2 #define nbMin nrrdBinaryOpMin #define nbMax nrrdBinaryOpMax #define nbLt nrrdBinaryOpLT #define nbLte nrrdBinaryOpLTE #define nbGt nrrdBinaryOpGT #define nbGte nrrdBinaryOpGTE #define nbCmp nrrdBinaryOpCompare #define nbEq nrrdBinaryOpEqual #define nbNeq nrrdBinaryOpNotEqual #define nbExt nrrdBinaryOpExists #define nbIf nrrdBinaryOpIf static const char * _nrrdBinaryOpStrEqv[] = { "+", "plus", "add", "-", "minus", "subtract", "sub", "x", "*", "times", "multiply", "product", "/", "divide", "quotient", "^", "pow", "power", "spow", "sgnpow", "sgnpower", "fpow", "%", "mod", "modulo", "fmod", "atan2", "min", "minimum", "max", "maximum", "lt", "<", "less", "lessthan", "lte", "<=", "lessthanorequal", "gt", ">", "greater", "greaterthan", "gte", ">=", "greaterthanorequal", "comp", "compare", "eq", "=", "==", "equal", "neq", "ne", "!=", "notequal", "exists", "if", "nrand", "rrand", "" }; static const int _nrrdBinaryOpValEqv[] = { nbAdd, nbAdd, nbAdd, nbSub, nbSub, nbSub, nbSub, nbMul, nbMul, nbMul, nbMul, nbMul, nbDiv, nbDiv, nbDiv, nbPow, nbPow, nbPow, nbSpw, nbSpw, nbSpw, nbFpw, nbMod, nbMod, nbMod, nbFmd, nbAtn, nbMin, nbMin, nbMax, nbMax, nbLt, nbLt, nbLt, nbLt, nbLte, nbLte, nbLte, nbGt, nbGt, nbGt, nbGt, nbGte, nbGte, nbGte, nbCmp, nbCmp, nbEq, nbEq, nbEq, nbEq, nbNeq, nbNeq, nbNeq, nbNeq, nbExt, nbIf, nrrdBinaryOpNormalRandScaleAdd, nrrdBinaryOpRicianRand, }; static const airEnum _nrrdBinaryOp_enum = { "binary op", NRRD_BINARY_OP_MAX, _nrrdBinaryOpStr, NULL, _nrrdBinaryOpDesc, _nrrdBinaryOpStrEqv, _nrrdBinaryOpValEqv, AIR_FALSE }; const airEnum *const nrrdBinaryOp = &_nrrdBinaryOp_enum; /* ------------------------ nrrdTernaryOp ---------------------- */ static const char * _nrrdTernaryOpStr[NRRD_TERNARY_OP_MAX+1] = { "(unknown_ternary_op)", "add", "multiply", "min", "min_sm", "max", "max_sm", "lt_sm", "gt_sm", "clamp", "ifelse", "lerp", "exists", "in_op", "in_cl", "gauss", "rician" }; static const char * _nrrdTernaryOpDesc[NRRD_TERNARY_OP_MAX+1] = { "unknown ternary op", "add three values", "multiply three values", "minimum of three values", "smooth minimum of 1st and 3rd value, starting at 2nd", "maximum of three values", "smooth maximum of 1st and 3rd value, starting at 2nd", "1st less than 3rd, smoothed by 2nd", "1st greater than 3rd, smoothed by 2nd", "clamp 2nd value to closed interval between 1st and 3rd", "if 1st value is non-zero, then 2nd value, else 3rd value", "linearly interpolate between 2nd value (1st = 0.0) and 3rd (1st = 1.0)", "if 1st value exists, the 2nd value, else the 3rd", "2nd value is inside OPEN interval range between 1st and 3rd", "2nd value is inside CLOSED interval range between 1st and 3rd", "evaluate (at 1st value) Gaussian with mean=2nd and stdv=3rd value", "evaluate (at 1st value) Rician with mean=2nd and stdv=3rd value" }; #define ntAdd nrrdTernaryOpAdd #define ntMul nrrdTernaryOpMultiply static const char * _nrrdTernaryOpStrEqv[] = { "+", "plus", "add", "x", "*", "times", "multiply", "product", "min", "min_sm", "minsm", /* HEY want to deprecate minsm */ "max", "max_sm", "lt_sm", "gt_sm", "clamp", "ifelse", "if", "lerp", "exists", "in_op", "in_cl", "gauss", "rician", "" }; static const int _nrrdTernaryOpValEqv[] = { ntAdd, ntAdd, ntAdd, ntMul, ntMul, ntMul, ntMul, ntMul, nrrdTernaryOpMin, nrrdTernaryOpMinSmooth, nrrdTernaryOpMinSmooth, nrrdTernaryOpMax, nrrdTernaryOpMaxSmooth, nrrdTernaryOpLTSmooth, nrrdTernaryOpGTSmooth, nrrdTernaryOpClamp, nrrdTernaryOpIfElse, nrrdTernaryOpIfElse, nrrdTernaryOpLerp, nrrdTernaryOpExists, nrrdTernaryOpInOpen, nrrdTernaryOpInClosed, nrrdTernaryOpGaussian, nrrdTernaryOpRician, }; static const airEnum _nrrdTernaryOp_enum = { "ternary op", NRRD_TERNARY_OP_MAX, _nrrdTernaryOpStr, NULL, _nrrdTernaryOpDesc, _nrrdTernaryOpStrEqv, _nrrdTernaryOpValEqv, AIR_FALSE }; const airEnum *const nrrdTernaryOp = &_nrrdTernaryOp_enum; /* ------------------------ nrrdFFTWPlanRigor ---------------------- */ static const char * _nrrdFFTWPlanRigorStr[NRRD_FFTW_PLAN_RIGOR_MAX+1] = { "(unknown_rigor)", "estimate", "measure", "patient", "exhaustive" }; static const char * _nrrdFFTWPlanRigorDesc[NRRD_FFTW_PLAN_RIGOR_MAX+1] = { "unknown rigor", "no machine-specific measurements, just estimate a plan", "do several machine-specific measurements to determine plan", "performs more measurements to find \"more optimal\" plan", "considers even wider range of algorithms to find most optimal plan" }; static const char * _nrrdFFTWPlanRigorStrEqv[] = { "e", "est", "estimate", "m", "meas", "measure", "p", "pat", "patient", "x", "ex", "exhaustive", "" }; static const int _nrrdFFTWPlanRigorValEqv[] = { nrrdFFTWPlanRigorEstimate, nrrdFFTWPlanRigorEstimate, nrrdFFTWPlanRigorEstimate, nrrdFFTWPlanRigorMeasure, nrrdFFTWPlanRigorMeasure, nrrdFFTWPlanRigorMeasure, nrrdFFTWPlanRigorPatient, nrrdFFTWPlanRigorPatient, nrrdFFTWPlanRigorPatient, nrrdFFTWPlanRigorExhaustive, nrrdFFTWPlanRigorExhaustive, nrrdFFTWPlanRigorExhaustive }; static const airEnum _nrrdFFTWPlanRigor_enum = { "fftw plan rigor", NRRD_FFTW_PLAN_RIGOR_MAX, _nrrdFFTWPlanRigorStr, NULL, _nrrdFFTWPlanRigorDesc, _nrrdFFTWPlanRigorStrEqv, _nrrdFFTWPlanRigorValEqv, AIR_FALSE }; const airEnum *const nrrdFFTWPlanRigor = &_nrrdFFTWPlanRigor_enum; /* ---------------------- nrrdResampleNonExistent -------------------- */ static const char * _nrrdResampleNonExistentStr[NRRD_RESAMPLE_NON_EXISTENT_MAX+1] = { "(unknown_resample_non_existent)", "noop", "renormalize", "weight" }; static const char * _nrrdResampleNonExistentDesc[NRRD_RESAMPLE_NON_EXISTENT_MAX+1] = { "unknown resample non-existent", "no-op; include non-existent values in convolution sum", "use only existent values in kernel support and renormalize weights", "use only existent values in kernel support and use weights as is" }; static const char * _nrrdResampleNonExistentStrEqv[] = { "noop", "renorm", "renormalize", "wght", "weight", "" }; static const int _nrrdResampleNonExistentValEqv[] = { nrrdResampleNonExistentNoop, nrrdResampleNonExistentRenormalize, nrrdResampleNonExistentRenormalize, nrrdResampleNonExistentWeight, nrrdResampleNonExistentWeight }; static const airEnum _nrrdResampleNonExistent_enum = { "resample non-existent", NRRD_RESAMPLE_NON_EXISTENT_MAX, _nrrdResampleNonExistentStr, NULL, _nrrdResampleNonExistentDesc, _nrrdResampleNonExistentStrEqv, _nrrdResampleNonExistentValEqv, AIR_FALSE }; const airEnum *const nrrdResampleNonExistent = &_nrrdResampleNonExistent_enum; /* ---- END non-NrrdIO */ teem-1.11.0~svn6057/src/nrrd/GNUmakefile0000664000175000017500000000546612165631065017433 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := nrrd #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### $(L).NEED = biff hest air # hest needed for callback declaration $(L).PUBLIC_HEADERS = nrrd.h nrrdDefines.h nrrdMacros.h nrrdEnums.h $(L).PRIVATE_HEADERS = privateNrrd.h $(L).OBJS = \ accessors.o arith.o arraysNrrd.o apply1D.o apply2D.o \ axis.o comment.o convertNrrd.o defaultsNrrd.o \ deringNrrd.o endianNrrd.o enumsNrrd.o filt.o gzio.o \ hestNrrd.o histogram.o iter.o kernel.o \ map.o measure.o methodsNrrd.o parseNrrd.o \ read.o write.o reorder.o resampleNrrd.o \ simple.o subset.o superset.o tmfKernel.o \ winKernel.o bsplKernel.o ccmethods.o cc.o range.o \ encoding.o encodingRaw.o encodingAscii.o encodingHex.o \ encodingGzip.o encodingBzip2.o \ format.o formatNRRD.o formatPNM.o formatPNG.o \ formatVTK.o formatText.o formatEPS.o \ keyvalue.o resampleContext.o fftNrrd.o $(L).TESTS = test/tread test/trand test/ax test/io test/strio test/texp \ test/minmax test/tkernel test/typestest test/tline test/genvol \ test/quadvol test/convo test/kv test/reuse test/histrad test/otsu \ test/dnorm test/morph #### #### #### # boilerplate: declare rules for this library, do other libraries + bins include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/nrrd/resampleNrrd.c0000664000175000017500000010376312165631065020162 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* (this was written before airMopSub ... ) learned: if you start using airMop stuff, and you register a free, but then you free the memory yourself, YOU HAVE GOT TO register a NULL in place of the original free. The next malloc may end up at the same address as what you just freed, and if you want this memory to NOT be mopped up, then you'll be confused with the original registered free goes into effect and mops it up for you, even though YOU NEVER REGISTERED a free for the second malloc. If you want simple stupid tools, you have to treat them accordingly (be extremely careful with fire). learned: well, duh. The reason to use: for (I=0; InumParm; for (ai=0; aidim; ai++) { double axmin, axmax; info->kernel[ai] = kernel; if (samples) { info->samples[ai] = samples[ai]; } else { center = _nrrdCenter(nin->axis[ai].center); if (nrrdCenterCell == center) { info->samples[ai] = (size_t)(nin->axis[ai].size*scalings[ai]); } else { info->samples[ai] = (size_t)((nin->axis[ai].size - 1) *scalings[ai]) + 1; } } for (p=0; pparm[ai][p] = parm[p]; /* set the min/max for this axis if not already set to something */ if (!( AIR_EXISTS(nin->axis[ai].min) && AIR_EXISTS(nin->axis[ai].max) )) { /* HEY: started as copy/paste of body of nrrdAxisInfoMinMaxSet, because we wanted to enable const-correctness, and this function had previously been setting per-axis min/max in *input* when it hasn't been already set */ double spacing; center = _nrrdCenter2(nin->axis[ai].center, nrrdDefaultCenter); spacing = nin->axis[ai].spacing; if (!AIR_EXISTS(spacing)) spacing = nrrdDefaultSpacing; if (nrrdCenterCell == center) { axmin = 0; axmax = spacing*nin->axis[ai].size; } else { axmin = 0; axmax = spacing*(nin->axis[ai].size - 1); } } else { axmin = nin->axis[ai].min; axmax = nin->axis[ai].max; } info->min[ai] = axmin; info->max[ai] = axmax; } /* we go with the defaults (enstated by _nrrdResampleInfoInit()) for all the remaining fields */ if (nrrdSpatialResample(nout, nin, info)) { biffAddf(NRRD, "%s:", me); return 1; } info = nrrdResampleInfoNix(info); return 0; } /* ** _nrrdResampleCheckInfo() ** ** checks validity of given NrrdResampleInfo *info: ** - all required parameters exist ** - both min[d] and max[d] for all axes d */ int _nrrdResampleCheckInfo(const Nrrd *nin, const NrrdResampleInfo *info) { static const char me[] = "_nrrdResampleCheckInfo"; const NrrdKernel *k; int center, p, np; unsigned int ai, minsmp; char stmp[2][AIR_STRLEN_SMALL]; if (nrrdTypeBlock == nin->type || nrrdTypeBlock == info->type) { biffAddf(NRRD, "%s: can't resample to or from type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (nrrdBoundaryUnknown == info->boundary) { biffAddf(NRRD, "%s: didn't set boundary behavior\n", me); return 1; } if (nrrdBoundaryPad == info->boundary && !AIR_EXISTS(info->padValue)) { biffAddf(NRRD, "%s: asked for boundary padding, but no pad value set\n", me); return 1; } for (ai=0; aidim; ai++) { k = info->kernel[ai]; /* we only care about the axes being resampled */ if (!k) continue; if (!(info->samples[ai] > 0)) { biffAddf(NRRD, "%s: axis %d # samples (%s) invalid", me, ai, airSprintSize_t(stmp[0], info->samples[ai])); return 1; } if (!( AIR_EXISTS(nin->axis[ai].min) && AIR_EXISTS(nin->axis[ai].max) )) { biffAddf(NRRD, "%s: input nrrd's axis %d min,max have not " "both been set", me, ai); return 1; } if (!( AIR_EXISTS(info->min[ai]) && AIR_EXISTS(info->max[ai]) )) { biffAddf(NRRD, "%s: info's axis %d min,max not both set", me, ai); return 1; } np = k->numParm; for (p=0; pparm[ai][p])) { biffAddf(NRRD, "%s: didn't set parameter %d (of %d) for axis %d\n", me, p, np, ai); return 1; } } center = _nrrdCenter(nin->axis[ai].center); minsmp = nrrdCenterCell == center ? 1 : 2; if (!( nin->axis[ai].size >= minsmp && info->samples[ai] >= minsmp )) { biffAddf(NRRD, "%s: axis %d # input samples (%s) or output samples (%s) " " invalid for %s centering", me, ai, airSprintSize_t(stmp[0], nin->axis[ai].size), airSprintSize_t(stmp[1], info->samples[ai]), airEnumStr(nrrdCenter, center)); return 1; } } return 0; } /* ** _nrrdResampleComputePermute() ** ** figures out information related to how the axes in a nrrd are ** permuted during resampling: permute, topRax, botRax, passes, ax[][], sz[][] */ void _nrrdResampleComputePermute(unsigned int permute[], unsigned int ax[NRRD_DIM_MAX][NRRD_DIM_MAX], size_t sz[NRRD_DIM_MAX][NRRD_DIM_MAX], int *topRax, int *botRax, unsigned int *passes, const Nrrd *nin, const NrrdResampleInfo *info) { /* char me[]="_nrrdResampleComputePermute"; */ unsigned int bi, ai, pi; /* what are the first (top) and last (bottom) axes being resampled? */ *topRax = *botRax = -1; for (ai=0; aidim; ai++) { if (info->kernel[ai]) { if (*topRax < 0) { *topRax = ai; } *botRax = ai; } } /* figure out total number of passes needed, and construct the permute[] array. permute[i] = j means that the axis in position i of the old array will be in position j of the new one (permute[] answers "where do I put this", not "what do I put here"). */ *passes = bi = 0; for (ai=0; aidim; ai++) { if (info->kernel[ai]) { do { bi = AIR_MOD((int)bi+1, (int)nin->dim); /* HEY scrutinize casts */ } while (!info->kernel[bi]); permute[bi] = ai; *passes += 1; } else { permute[ai] = ai; bi += bi == ai; } } permute[nin->dim] = nin->dim; if (!*passes) { /* none of the kernels was non-NULL */ return; } /* fprintf(stderr, "%s: permute:\n", me); for (d=0; ddim; d++) { fprintf(stderr, " permute[%d] = %d\n", d, permute[ai]); } */ /* create array of how the axes will be arranged in each pass ("ax"), and create array of how big each axes is in each pass ("sz"). The input to pass i will have axis layout described in ax[i] and axis sizes described in sz[i] */ for (ai=0; aidim; ai++) { ax[0][ai] = ai; sz[0][ai] = nin->axis[ai].size; } for (pi=0; pi<*passes; pi++) { for (ai=0; aidim; ai++) { ax[pi+1][permute[ai]] = ax[pi][ai]; if (ai == (unsigned int)*topRax) { /* HEY scrutinize casts */ /* this is the axis which is getting resampled, so the number of samples is potentially changing */ sz[pi+1][permute[ai]] = (info->kernel[ax[pi][ai]] ? info->samples[ax[pi][ai]] : sz[pi][ai]); } else { /* this axis is just a shuffled version of the previous axis; no resampling this pass. Note: this case also includes axes which aren't getting resampled whatsoever */ sz[pi+1][permute[ai]] = sz[pi][ai]; } } } return; } /* ** _nrrdResampleMakeWeightIndex() ** ** _allocate_ and fill the arrays of indices and weights that are ** needed to process all the scanlines along a given axis; also ** be so kind as to set the sampling ratio (<1: downsampling, ** new sample spacing larger, >1: upsampling, new sample spacing smaller) ** ** returns "dotLen", the number of input samples which are required ** for resampling this axis, or 0 if there was an error. Uses biff. */ int _nrrdResampleMakeWeightIndex(nrrdResample_t **weightP, int **indexP, double *ratioP, const Nrrd *nin, const NrrdResampleInfo *info, unsigned int ai) { static const char me[]="_nrrdResampleMakeWeightIndex"; int sizeIn, sizeOut, center, dotLen, halfLen, *indx, base, idx; nrrdResample_t minIn, maxIn, minOut, maxOut, spcIn, spcOut, ratio, support, integral, pos, idxD, wght; nrrdResample_t *weight; double parm[NRRD_KERNEL_PARMS_NUM]; int e, i; if (!(info->kernel[ai])) { biffAddf(NRRD, "%s: don't see a kernel for dimension %d", me, ai); *weightP = NULL; *indexP = NULL; return 0; } center = _nrrdCenter(nin->axis[ai].center); sizeIn = AIR_CAST(int, nin->axis[ai].size); sizeOut = AIR_CAST(int, info->samples[ai]); minIn = AIR_CAST(nrrdResample_t, nin->axis[ai].min); maxIn = AIR_CAST(nrrdResample_t, nin->axis[ai].max); minOut = AIR_CAST(nrrdResample_t, info->min[ai]); maxOut = AIR_CAST(nrrdResample_t, info->max[ai]); spcIn = NRRD_SPACING(center, minIn, maxIn, sizeIn); spcOut = NRRD_SPACING(center, minOut, maxOut, sizeOut); *ratioP = ratio = spcIn/spcOut; support = AIR_CAST(nrrdResample_t, info->kernel[ai]->support(info->parm[ai])); integral = AIR_CAST(nrrdResample_t, info->kernel[ai]->integral(info->parm[ai])); /* fprintf(stderr, "!%s(%d): size{In,Out} = %d, %d, support = %f; ratio = %f\n", me, d, sizeIn, sizeOut, support, ratio); */ if (ratio > 1) { /* if upsampling, we need only as many samples as needed for interpolation with the given kernel */ dotLen = (int)(2*ceil(support)); } else { /* if downsampling, we need to use all the samples covered by the stretched out version of the kernel */ if (info->cheap) { dotLen = (int)(2*ceil(support)); } else { dotLen = (int)(2*ceil(support/ratio)); } } /* fprintf(stderr, "!%s(%d): dotLen = %d\n", me, d, dotLen); */ weight = AIR_CALLOC(sizeOut*dotLen, nrrdResample_t); if (!weight) { biffAddf(NRRD, "%s: can't allocate weight array", me); *weightP = NULL; *indexP = NULL; return 0; } indx = AIR_CALLOC(sizeOut*dotLen, int); if (!indx) { biffAddf(NRRD, "%s: can't allocate index arrays", me); *weightP = NULL; *indexP = NULL; return 0; } /* calculate sample locations and do first pass on indices */ halfLen = dotLen/2; for (i=0; iboundary) { case nrrdBoundaryPad: case nrrdBoundaryWeight: /* this will be further handled later */ idx = sizeIn; break; case nrrdBoundaryBleed: idx = AIR_CLAMP(0, idx, sizeIn-1); break; case nrrdBoundaryWrap: idx = AIR_MOD(idx, sizeIn); break; case nrrdBoundaryMirror: idx = _nrrdMirror_32(sizeIn, idx); break; default: biffAddf(NRRD, "%s: boundary behavior %d unknown/unimplemented", me, info->boundary); *weightP = NULL; *indexP = NULL; return 0; } indx[i] = idx; } } /* run the sample locations through the chosen kernel. We play a sneaky trick on the kernel parameter 0 in case of downsampling to create the blurring of the old index space, but only if !cheap */ memcpy(parm, info->parm[ai], NRRD_KERNEL_PARMS_NUM*sizeof(double)); if (ratio < 1 && !(info->cheap)) { parm[0] /= ratio; } info->kernel[ai]->EVALN(weight, weight, dotLen*sizeOut, parm); /* ******** for (i=0; iboundary) { if (integral) { /* above, we set to sizeIn all the indices that were out of range. We now use that to determine the sum of the weights for the indices that were in-range */ for (i=0; irenormalize && integral) { */ if (info->renormalize && integral) { for (i=0; ikernel[ai]. Where to sample ** on the axis is controlled by info->min[ai] and info->max[ai]; these ** specify a range of "positions" aka "world space" positions, as ** determined by the per-axis min and max of the input nrrd, which must ** be set for every resampled axis. ** ** we cyclically permute those axes being resampled, and never touch ** the position (in axis ordering) of axes along which we are not ** resampling. This strategy is certainly not the most intelligent ** one possible, but it does mean that the axis along which we're ** currently resampling-- the one along which we'll have to look at ** multiple adjecent samples-- is that resampling axis which is ** currently most contiguous in memory. It may make sense to precede ** the resampling with an axis permutation which bubbles all the ** resampled axes to the front (most contiguous) end of the axis list, ** and then puts them back in place afterwards, depending on the cost ** of such axis permutation overhead. */ int nrrdSpatialResample(Nrrd *nout, const Nrrd *nin, const NrrdResampleInfo *info) { static const char me[]="nrrdSpatialResample", func[]="resample"; nrrdResample_t *array[NRRD_DIM_MAX], /* intermediate copies of the input data undergoing resampling; we don't need a full- fledged nrrd for these. Only about two of these arrays will be allocated at a time; intermediate results will be free()d when not needed */ *_inVec, /* current input vector being resampled; not necessarily contiguous in memory (if strideIn != 1) */ *inVec, /* buffer for input vector; contiguous */ *_outVec; /* output vector in context of volume; never contiguous */ double tmpF; double ratio, /* factor by which or up or downsampled */ ratios[NRRD_DIM_MAX]; /* record of "ratio" for all resampled axes, used to compute new spacing in output */ Nrrd *floatNin; /* if the input nrrd type is not nrrdResample_t, then we convert it and keep it here */ unsigned int ai, pi, /* current pass */ topLax, permute[NRRD_DIM_MAX], /* how to permute axes of last pass to get axes for current pass */ ax[NRRD_DIM_MAX+1][NRRD_DIM_MAX], /* axis ordering on each pass */ passes; /* # of passes needed to resample all axes */ int i, s, e, topRax, /* the lowest index of an axis which is resampled. If all axes are being resampled, then this is 0. If for some reason the "x" axis (fastest stride) is not being resampled, but "y" is, then topRax is 1 */ botRax, /* index of highest axis being resampled */ typeIn, typeOut; /* types of input and output of resampling */ size_t sz[NRRD_DIM_MAX+1][NRRD_DIM_MAX]; /* how many samples along each axis, changing on each pass */ /* all these variables have to do with the spacing of elements in memory for the current pass of resampling, and they (except strideIn) are re-set at the beginning of each pass */ nrrdResample_t *weight; /* sample weights */ unsigned int ci[NRRD_DIM_MAX+1], co[NRRD_DIM_MAX+1]; int sizeIn, sizeOut, /* lengths of input and output vectors */ dotLen, /* # input samples to dot with weights to get one output sample */ doRound, /* actually do rounding on output: we DO NOT round when info->round but the output type is not integral */ *indx; /* dotLen*sizeOut 2D array of input indices */ size_t I, /* swiss-army int */ strideIn, /* the stride between samples in the input "scanline" being resampled */ strideOut, /* stride between samples in output "scanline" from resampling */ L, LI, LO, numLines, /* top secret */ numOut; /* # of _samples_, total, in output volume; this is for allocating the output */ airArray *mop; /* for cleaning up */ if (!(nout && nin && info)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdBoundaryUnknown == info->boundary) { biffAddf(NRRD, "%s: need to specify a boundary behavior", me); return 1; } typeIn = nin->type; typeOut = nrrdTypeDefault == info->type ? typeIn : info->type; if (_nrrdResampleCheckInfo(nin, info)) { biffAddf(NRRD, "%s: problem with arguments", me); return 1; } _nrrdResampleComputePermute(permute, ax, sz, &topRax, &botRax, &passes, nin, info); topLax = topRax ? 0 : 1; /* not sure where else to put this: (want to put it before 0 == passes branch) We have to assume some centering when doing resampling, and it would be stupid to not record it in the outgoing nrrd, since the value of nrrdDefaultCenter could always change. */ for (ai=0; aidim; ai++) { if (info->kernel[ai]) { nout->axis[ai].center = _nrrdCenter(nin->axis[ai].center); } } if (0 == passes) { /* actually, no resampling was desired. Copy input to output, but with the clamping that we normally do at the end of resampling */ nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, sz[0]); if (nrrdMaybeAlloc_nva(nout, typeOut, nin->dim, sz[0])) { biffAddf(NRRD, "%s: couldn't allocate output", me); return 1; } numOut = nrrdElementNumber(nout); for (I=0; Itype](nin->data, I); tmpF = nrrdDClamp[typeOut](tmpF); nrrdDInsert[typeOut](nout->data, I, tmpF); } nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE); /* HEY: need to create textual representation of resampling parameters */ if (nrrdContentSet_va(nout, func, nin, "")) { biffAddf(NRRD, "%s:", me); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } mop = airMopNew(); /* convert input nrrd to nrrdResample_t if necessary */ if (nrrdResample_nrrdType != typeIn) { if (nrrdConvert(floatNin = nrrdNew(), nin, nrrdResample_nrrdType)) { biffAddf(NRRD, "%s: couldn't create float copy of input", me); airMopError(mop); return 1; } array[0] = (nrrdResample_t*)floatNin->data; airMopAdd(mop, floatNin, (airMopper)nrrdNuke, airMopAlways); } else { floatNin = NULL; array[0] = (nrrdResample_t*)nin->data; } /* compute strideIn; this is actually the same for every pass because (strictly speaking) in every pass we are resampling the same axis, and axes with lower indices are constant length */ strideIn = 1; for (ai=0; ai<(unsigned int)topRax; ai++) { /* HEY scrutinize casts */ strideIn *= nin->axis[ai].size; } /* printf("%s: strideIn = " _AIR_SIZE_T_CNV "\n", me, strideIn); */ /* go! */ for (pi=0; pidim; ai++) { if (ai < (unsigned int)botRax) { /* HEY scrutinize cast */ strideOut *= sz[pi+1][ai]; } if (ai != (unsigned int)topRax) { /* HEY scrutinize cast */ numLines *= sz[pi][ai]; } } sizeIn = AIR_CAST(int, sz[pi][topRax]); sizeOut = AIR_CAST(int, sz[pi+1][botRax]); numOut = numLines*sizeOut; /* for the rest of the loop body, d is the original "dimension" for the axis being resampled */ ai = ax[pi][topRax]; /* printf("%s(%d): numOut = " _AIR_SIZE_T_CNV "\n", me, pi, numOut); */ /* printf("%s(%d): numLines = " _AIR_SIZE_T_CNV "\n", me, pi, numLines); */ /* printf("%s(%d): stride: In=%d, Out=%d\n", me, pi, */ /* (int)strideIn, (int)strideOut); */ /* printf("%s(%d): sizeIn = %d\n", me, pi, sizeIn); */ /* printf("%s(%d): sizeOut = %d\n", me, pi, sizeOut); */ /* we can free the input to the previous pass (if its not the given data) */ if (pi > 0) { if (pi == 1) { if (array[0] != nin->data) { airMopSub(mop, floatNin, (airMopper)nrrdNuke); floatNin = nrrdNuke(floatNin); array[0] = NULL; /* printf("%s: pi %d: freeing array[0]\n", me, pi); */ } } else { airMopSub(mop, array[pi-1], airFree); array[pi-1] = (nrrdResample_t*)airFree(array[pi-1]); /* printf("%s: pi %d: freeing array[%d]\n", me, pi, pi-1); */ } } /* allocate output volume */ array[pi+1] = (nrrdResample_t*)calloc(numOut, sizeof(nrrdResample_t)); if (!array[pi+1]) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: couldn't create array of %s nrrdResample_t's for " "output of pass %d", me, airSprintSize_t(stmp, numOut), pi); airMopError(mop); return 1; } airMopAdd(mop, array[pi+1], airFree, airMopAlways); /* printf("%s: allocated array[%d]\n", me, pi+1); */ /* allocate contiguous input scanline buffer, we alloc one more than needed to provide a place for the pad value. That is, in fact, the over-riding reason to copy a scanline to a local array: so that there is a simple consistent (non-branchy) way to incorporate the pad values */ inVec = (nrrdResample_t *)calloc(sizeIn+1, sizeof(nrrdResample_t)); airMopAdd(mop, inVec, airFree, airMopAlways); inVec[sizeIn] = AIR_CAST(nrrdResample_t, info->padValue); dotLen = _nrrdResampleMakeWeightIndex(&weight, &indx, &ratio, nin, info, ai); if (!dotLen) { biffAddf(NRRD, "%s: trouble creating weight and index vector arrays", me); airMopError(mop); return 1; } ratios[ai] = ratio; airMopAdd(mop, weight, airFree, airMopAlways); airMopAdd(mop, indx, airFree, airMopAlways); /* the skinny: resample all the scanlines */ _inVec = array[pi]; _outVec = array[pi+1]; memset(ci, 0, (NRRD_DIM_MAX+1)*sizeof(int)); memset(co, 0, (NRRD_DIM_MAX+1)*sizeof(int)); for (L=0; Ldim); NRRD_INDEX_GEN(LO, co, sz[pi+1], nin->dim); _inVec = array[pi] + LI; _outVec = array[pi+1] + LO; /* read input scanline into contiguous array */ for (i=0; i out[%d] = %g\n", i*strideOut, _outVec[i*strideOut]); */ } /* update the coordinates for the scanline starts. We don't use the usual NRRD_COORD macros because we're subject to the unusual constraint that ci[topRax] and co[permute[topRax]] must stay exactly zero */ e = topLax; ci[e]++; co[permute[e]]++; while (L < numLines-1 && ci[e] == sz[pi][e]) { ci[e] = co[permute[e]] = 0; e++; e += e == topRax; ci[e]++; co[permute[e]]++; } } /* pass-specific clean up */ airMopSub(mop, weight, airFree); airMopSub(mop, indx, airFree); airMopSub(mop, inVec, airFree); weight = (nrrdResample_t*)airFree(weight); indx = (int*)airFree(indx); inVec = (nrrdResample_t*)airFree(inVec); } /* clean up second-to-last array and scanline buffers */ if (passes > 1) { airMopSub(mop, array[passes-1], airFree); array[passes-1] = (nrrdResample_t*)airFree(array[passes-1]); /* printf("%s: now freeing array[%d]\n", me, passes-1); */ } else if (array[passes-1] != nin->data) { airMopSub(mop, floatNin, (airMopper)nrrdNuke); floatNin = nrrdNuke(floatNin); } array[passes-1] = NULL; /* create output nrrd and set axis info */ if (nrrdMaybeAlloc_nva(nout, typeOut, nin->dim, sz[passes])) { biffAddf(NRRD, "%s: couldn't allocate final output nrrd", me); airMopError(mop); return 1; } airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopOnError); nrrdAxisInfoCopy(nout, nin, NULL, (NRRD_AXIS_INFO_SIZE_BIT | NRRD_AXIS_INFO_MIN_BIT | NRRD_AXIS_INFO_MAX_BIT | NRRD_AXIS_INFO_SPACING_BIT | NRRD_AXIS_INFO_SPACEDIRECTION_BIT /* see below */ | NRRD_AXIS_INFO_THICKNESS_BIT | NRRD_AXIS_INFO_KIND_BIT)); for (ai=0; aidim; ai++) { if (info->kernel[ai]) { /* we do resample this axis */ nout->axis[ai].spacing = nin->axis[ai].spacing/ratios[ai]; /* no way to usefully update thickness: we could be doing blurring but maintaining the number of samples: thickness increases, or we could be downsampling, in which the relationship between the sampled and the skipped regions of space becomes complicated: no single scalar can represent it, or we could be upsampling, in which the notion of "skip" could be rendered meaningless */ nout->axis[ai].thickness = AIR_NAN; nout->axis[ai].min = info->min[ai]; nout->axis[ai].max = info->max[ai]; /* HEY: this is currently a bug: all this code was written long before there were space directions, so min/max are always set, regardless of whethere there are incoming space directions which then disallows output space directions on the same axes _nrrdSpaceVecScale(nout->axis[ai].spaceDirection, 1.0/ratios[ai], nin->axis[ai].spaceDirection); */ nout->axis[ai].kind = _nrrdKindAltered(nin->axis[ai].kind, AIR_TRUE); } else { /* this axis remains untouched */ nout->axis[ai].min = nin->axis[ai].min; nout->axis[ai].max = nin->axis[ai].max; nout->axis[ai].spacing = nin->axis[ai].spacing; nout->axis[ai].thickness = nin->axis[ai].thickness; nout->axis[ai].kind = nin->axis[ai].kind; } } /* HEY: need to create textual representation of resampling parameters */ if (nrrdContentSet_va(nout, func, nin, "")) { biffAddf(NRRD, "%s:", me); return 1; } /* copy the resampling final result into the output nrrd, maybe rounding as we go to make sure that 254.9999 is saved as 255 in uchar output, and maybe clamping as we go to insure that integral results don't have unexpected wrap-around. */ if (info->round) { if (nrrdTypeInt == typeOut || nrrdTypeUInt == typeOut || nrrdTypeLLong == typeOut || nrrdTypeULLong == typeOut) { fprintf(stderr, "%s: WARNING: possible erroneous output with " "rounding of %s output type due to int-based implementation " "of rounding\n", me, airEnumStr(nrrdType, typeOut)); } doRound = nrrdTypeIsIntegral[typeOut]; } else { doRound = AIR_FALSE; } numOut = nrrdElementNumber(nout); for (I=0; Iclamp) { tmpF = nrrdDClamp[typeOut](tmpF); } nrrdDInsert[typeOut](nout->data, I, tmpF); } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } /* enough already */ airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/nrrd/iter.c0000664000175000017500000000766612165631065016474 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" NrrdIter * nrrdIterNew() { NrrdIter *iter; if ( (iter = (NrrdIter *)calloc(1, sizeof(NrrdIter))) ) { iter->nrrd = NULL; iter->ownNrrd = NULL; iter->val = AIR_NAN; iter->size = 0; iter->data = NULL; iter->left = 0; iter->load = NULL; } return iter; } void nrrdIterSetValue(NrrdIter *iter, double val) { if (iter) { iter->nrrd = NULL; iter->ownNrrd = iter->ownNrrd ? nrrdNuke(iter->ownNrrd) : NULL; iter->val = val; iter->size = nrrdTypeSize[nrrdTypeDouble]; iter->data = (char*)&(iter->val); iter->left = 0; iter->load = nrrdDLoad[nrrdTypeDouble]; } return; } void nrrdIterSetNrrd(NrrdIter *iter, const Nrrd *nrrd) { if (iter && nrrd && nrrd->data) { if (nrrdTypeBlock == nrrd->type) { /* we can't deal */ nrrdIterSetValue(iter, AIR_NAN); return; } iter->nrrd = nrrd; iter->ownNrrd = iter->ownNrrd ? nrrdNuke(iter->ownNrrd) : NULL; iter->val = AIR_NAN; iter->size = nrrdTypeSize[nrrd->type]; iter->data = (char *)nrrd->data; iter->left = nrrdElementNumber(nrrd)-1; iter->load = nrrdDLoad[nrrd->type]; } return; } /* ** formerly known as nrrdIterSetNrrd */ void nrrdIterSetOwnNrrd(NrrdIter *iter, Nrrd *nrrd) { if (iter && nrrd && nrrd->data) { if (nrrdTypeBlock == nrrd->type) { /* we can't deal */ nrrdIterSetValue(iter, AIR_NAN); return; } iter->nrrd = NULL; iter->ownNrrd = iter->ownNrrd ? nrrdNuke(iter->ownNrrd) : NULL; iter->ownNrrd = nrrd; iter->val = AIR_NAN; iter->size = nrrdTypeSize[nrrd->type]; iter->data = (char *)nrrd->data; iter->left = nrrdElementNumber(nrrd)-1; iter->load = nrrdDLoad[nrrd->type]; } return; } double nrrdIterValue(NrrdIter *iter) { double ret = 0.0; if (iter) { ret = iter->load(iter->data); if (iter->nrrd || iter->ownNrrd) { iter->data += iter->size; if (iter->left) { iter->left -= 1; } else { iter->data = (char *)(_NRRD_ITER_NRRD(iter)->data); iter->left = nrrdElementNumber(_NRRD_ITER_NRRD(iter))-1; } } } return ret; } /* ******** nrrdIterContent() ** ** ALLOCATES a string that is either the nrrd's content (or ** nrrdStateUnknownContent) or a string version of the value; useful ** for when you's use the "content" of a nrrd */ char * nrrdIterContent(NrrdIter *iter) { char *ret, buff[AIR_STRLEN_SMALL]; ret = NULL; if (iter) { if (_NRRD_ITER_NRRD(iter)) { ret = _nrrdContentGet(_NRRD_ITER_NRRD(iter)); } else { airSinglePrintf(NULL, buff, "%g", iter->val); ret = airStrdup(buff); } } return ret; } NrrdIter * nrrdIterNix(NrrdIter *iter) { if (iter) { if (iter->ownNrrd) { iter->ownNrrd = nrrdNuke(iter->ownNrrd); } free(iter); } return NULL; } teem-1.11.0~svn6057/src/nrrd/sources.cmake0000664000175000017500000000160511636271162020035 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(NRRD_SOURCES accessors.c apply1D.c apply2D.c arith.c arraysNrrd.c axis.c cc.c ccmethods.c comment.c convertNrrd.c defaultsNrrd.c deringNrrd.c encoding.c encodingAscii.c encodingBzip2.c encodingGzip.c encodingHex.c encodingRaw.c endianNrrd.c enumsNrrd.c filt.c format.c formatEPS.c formatNRRD.c formatPNG.c formatPNM.c formatText.c formatVTK.c gzio.c hestNrrd.c histogram.c iter.c kernel.c keyvalue.c map.c measure.c methodsNrrd.c nrrd.h nrrdDefines.h nrrdEnums.h nrrdMacros.h parseNrrd.c privateNrrd.h range.c read.c reorder.c resampleContext.c fftNrrd.c resampleNrrd.c simple.c subset.c superset.c tmfKernel.c winKernel.c bsplKernel.c write.c ) ADD_TEEM_LIBRARY(nrrd ${NRRD_SOURCES}) teem-1.11.0~svn6057/src/nrrd/bsplKernel.c0000664000175000017500000012231512165631065017617 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" static double returnZero(const double *parm) { AIR_UNUSED(parm); return 0.0; } static double returnOne(const double *parm) { AIR_UNUSED(parm); return 1.0; } /* ** These kernels are the cardinal B-splines of different orders ** Using them with convolution assumes that the data has been pre-filtered ** so that the spline interpolates the original values. */ /* helper macros for doing abs() and remembering sign */ #define ABS_SGN(ax, sgn, x) \ if (x < 0) { \ sgn = -1; \ ax = -x; \ } else { \ sgn = 1; \ ax = x; \ } /* helper macro for listing the various members of the kernel */ #define BSPL_DECL(ord, deriv) \ 0, \ _bspl##ord##_sup, \ (0 == deriv ? returnOne : returnZero), \ _bspl##ord##d##deriv##_1f, \ _bspl##ord##d##deriv##_Nf, \ _bspl##ord##d##deriv##_1d, \ _bspl##ord##d##deriv##_Nd /* (the following preceded some of the function definitions before they were all packed up in the _METHODS macros; but the question about the type of the locals is still relevant) HEY: there is a possibly interesting question to be answered here about whether, to distinguish the float-specific and double-specific versions of the kernel eval functions, if the float-specific versions should actually only use floats for locals, so that no casting to float is needed at return, or, if its too much of a precision loss to do so, at no real economy of speed, so doubles should be used for all intermediate calculations, prior to the final cast to float. The macros below do the casting, whether or not is as actually needed, so this can be experimented with by just changing the type of the locals (without changing the macro definitions) */ #define BSPL_EVEN_METHODS(basename, macro) \ static double \ basename##_1d(double x, const double *parm) { \ double ax, tmp, r; \ AIR_UNUSED(parm); \ \ ax = AIR_ABS(x); \ macro(r, double, tmp, ax); \ return r; \ } \ \ static float \ basename##_1f(float x, const double *parm) { \ float ax, tmp, r; \ AIR_UNUSED(parm); \ \ ax = AIR_ABS(x); \ macro(r, float, tmp, ax); \ return r; \ } \ \ static void \ basename##_Nd(double *f, const double *x, size_t len, \ const double *parm) { \ double ax, tmp, r; \ size_t i; \ AIR_UNUSED(parm); \ \ for (i=0; i nrrd/parseNrrd.c/_nrrdReadNrrdParse_comment() --> nrrd/comment.c/nrrdCommentAdd() --> air/string.c/airOneLinify() On write, output comments processed via: nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> air/string.c/airOneLinify() ==> There is no escaping of anything: white-space is compressed into a single ' '. Probably justified justified given format spec. ** Content: On disk, finished with the end of line. No mention of escaping in the format spec. Input content processed via: nrrd/formatNrrd.c/_nrrdFormatNRRD_read() --> nrrd/parseNrrd.c/_nrrdReadNrrdParse_content() which does NO processing, just airStrdup On write, output content processed via: nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> nrrd/write.c/_nrrdSprintFieldInfo() (maybe via _nrrdFprintFieldInfo()) --> air/string.c/airOneLinify() ==> not only is there no escaping, but there's some assymmetry in the use of airOneLinify. If information is being encoded in the number of contiguous spaces in the content, its preserved on input but not on output. Still, there's no chance of writing a broken file. ** key/value pairs: The keys and values are separated by ":=", and the format spec says (string) "\n" means (character) '\n' and "\\" means '\\'. On input, both keys and values processed via: nrrd/formatNrrd.c/_nrrdFormatNRRD_read() --> nrrd/parseNrrd.c/_nrrdReadNrrdParse_keyvalue() --> air/string.c/airUnescape(), which deals with "\n" and "\\" ONLY (not quotes, not other whitespace), and then --> nrrd/keyvalue.c/nrrdKeyValueAdd(), which only does an airStrdup On output, keys and values processed via nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> nrrd/keyvalue.c/_nrrdKeyValueWrite() --> nrrd/keyvalue.c/_nrrdWriteEscaped() which is invoked to escape \n and \, and (NOTE!) to convert all other whitespace to ' ' Aside from the file format spec, the nrrd *library* does not really have any strictures about the characters that are allowed at run-time in key/values (and indeed nrrdKeyValueAdd just does an airStrdup). But without converting or escaping, say, '\r', you'll generate a broken NRRD file, hence the new handling of converting other whitespace to ' '. ** labels and units: A "-delimited string per axis. Format spec is very specific for labels, and implies units are the same: "Within each label, double quotes may be included by escaping them (\"), but no other form of escaping is supported". On input: nrrd/formatNrrd.c/_nrrdFormatNRRD_read() --> nrrd/parseNrrd.c/_nrrdReadNrrdParse_labels() or _nrrdReadNrrdParse_units() --> nrrd/parseNrrd.c/_nrrdGetQuotedString() which does the work of unescaping \" On output: nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> nrrd/write.c/_nrrdSprintFieldInfo() (maybe via _nrrdFprintFieldInfo()) --> nrrd/keyvalue.c/_nrrdWriteEscaped() which is invoked to escape ", and (NOTE!) to convert all other whitespace to ' ' Same concern above about characters that when written would generate a bad NRRD file, but which are not documented as escape-able in label or unit ** space units: A "-delimited string per axis of *world-space* (NOT the same a per-axis field, like units). Format is sadly silent on issue of escaping for these; so we might as well treat them like labels & units units. On input: nrrd/formatNrrd.c/_nrrdFormatNRRD_read() --> nrrd/parseNrrd.c/_nrrdReadNrrdParse_space_units --> nrrd/parseNrrd.c/_nrrdGetQuotedString() which does the work of unescaping \" On output: nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> nrrd/write.c/_nrrdSprintFieldInfo() (maybe via _nrrdFprintFieldInfo()) --> nrrd/keyvalue.c/_nrrdWriteEscaped() which is invoked to escape ", and (NOTE!) to convert all other whitespace to ' ' ** sample units: like content and comments, not a quoted string. On input: nrrd/formatNrrd.c/_nrrdFormatNRRD_read() --> nrrd/parseNrrd.c/_nrrdReadNrrdParse_sample_units() which does nothing except a strdup On output: nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> nrrd/write.c/_nrrdSprintFieldInfo() (maybe via _nrrdFprintFieldInfo()) --> air/string.c/airOneLinify() **** ***/ #define MAGIC "NRRD" #define MAGIC0 "NRRD00.01" #define MAGIC1 "NRRD0001" #define MAGIC2 "NRRD0002" #define MAGIC3 "NRRD0003" #define MAGIC4 "NRRD0004" #define MAGIC5 "NRRD0005" const char * _nrrdFormatURLLine0 = "Complete NRRD file format specification at:"; const char * _nrrdFormatURLLine1 = "http://teem.sourceforge.net/nrrd/format.html"; void nrrdIoStateDataFileIterBegin(NrrdIoState *nio) { nio->dataFNIndex = 0; return; } /* this macro suggested by Bryan Worthen */ /* if str = '-', strcmp() is 0, && short circuits, return false ** else str != '-' ** if str[1] = ':', its probably a windows full path, != is 0, return false ** else str[1] != ':' ** if str[0] = '/', its a normal full path, return false */ #define _NEED_PATH(str) (strcmp("-", (str)) \ && ':' != (str)[1] \ && '/' != (str)[0]) /* ** this is responsible for the header-relative path processing ** ** NOTE: if the filename is "-", then because it does not start with '/', ** it would normally be prefixed by nio->path, so it needs special handling ** ** NOTE: this should work okay with nio->headerStringRead, I think ... */ int nrrdIoStateDataFileIterNext(FILE **fileP, NrrdIoState *nio, int reading) { static const char me[]="nrrdIoStateDataFileIterNext"; char *fname=NULL; int ii, needPath; unsigned int num, fi; size_t maxl; airArray *mop; mop = airMopNew(); airMopAdd(mop, (void*)fileP, (airMopper)airSetNull, airMopOnError); if (!fileP) { biffAddf(NRRD, "%s: got NULL pointer", me); airMopError(mop); return 1; } if (!_nrrdDataFNNumber(nio)) { biffAddf(NRRD, "%s: there appear to be zero datafiles!", me); airMopError(mop); return 1; } if (nio->dataFNIndex >= _nrrdDataFNNumber(nio)) { /* there is no next data file, but we don't make that an error (though as of Tue Oct 2 22:53:14 CDT 2012, GLK can't remember why this condition would ever occur) */ nio->dataFNIndex = _nrrdDataFNNumber(nio); airMopOkay(mop); *fileP = NULL; return 0; } /* HEY: some of this error checking is done far more often than needed */ if (nio->dataFNFormat || nio->dataFNArr->len) { needPath = AIR_FALSE; maxl = 0; if (nio->dataFNFormat) { needPath = _NEED_PATH(nio->dataFNFormat); /* assuming 10-digit integers is plenty big */ maxl = 10 + strlen(nio->dataFNFormat); } else { for (fi=0; fidataFNArr->len; fi++) { needPath |= _NEED_PATH(nio->dataFN[fi]); maxl = AIR_MAX(maxl, strlen(nio->dataFN[fi])); } } if (needPath && !airStrlen(nio->path)) { biffAddf(NRRD, "%s: need nio->path for header-relative datafiles", me); airMopError(mop); return 1; } fname = (char*)malloc(airStrlen(nio->path) + strlen("/") + maxl + 1); if (!fname) { biffAddf(NRRD, "%s: couldn't allocate filename buffer", me); airMopError(mop); return 1; } airMopAdd(mop, fname, airFree, airMopAlways); } if (nio->dataFNFormat) { /* ---------------------------------------------------------- */ /* --------- base.%d [] ------------- */ /* ---------------------------------------------------------- */ num = 0; for (ii = nio->dataFNMin; ((nio->dataFNStep > 0 && ii <= nio->dataFNMax) || (nio->dataFNStep < 0 && ii >= nio->dataFNMax)); ii += nio->dataFNStep) { if (num == nio->dataFNIndex) { break; } num += 1; } if (_NEED_PATH(nio->dataFNFormat)) { strcpy(fname, nio->path); strcat(fname, "/"); sprintf(fname + strlen(nio->path) + strlen("/"), nio->dataFNFormat, ii); } else { sprintf(fname, nio->dataFNFormat, ii); } } else if (nio->dataFNArr->len) { /* ---------------------------------------------------------- */ /* ------------------- LIST or single ----------------------- */ /* ---------------------------------------------------------- */ if (_NEED_PATH(nio->dataFN[nio->dataFNIndex])) { sprintf(fname, "%s/%s", nio->path, nio->dataFN[nio->dataFNIndex]); } else { strcpy(fname, nio->dataFN[nio->dataFNIndex]); } } /* else data file is attached */ if (nio->dataFNFormat || nio->dataFNArr->len) { *fileP = airFopen(fname, reading ? stdin : stdout, reading ? "rb" : "wb"); if (!(*fileP)) { biffAddf(NRRD, "%s: couldn't open \"%s\" (data file %u of %u) for %s", me, fname, nio->dataFNIndex+1, _nrrdDataFNNumber(nio), reading ? "reading" : "writing"); airMopError(mop); return 1; } } else { /* data file is attached */ if (nio->headerStringRead) { /* except we were never reading from a file to begin with, but this isn't an error */ *fileP = NULL; } else { *fileP = nio->headerFile; } } nio->dataFNIndex++; airMopOkay(mop); return 0; } /* ** we try to use the oldest format that will hold the nrrd */ int _nrrdFormatNRRD_whichVersion(const Nrrd *nrrd, NrrdIoState *nio) { int ret; if (_nrrdFieldInteresting(nrrd, nio, nrrdField_measurement_frame)) { ret = 5; } else if (_nrrdFieldInteresting(nrrd, nio, nrrdField_thicknesses) || _nrrdFieldInteresting(nrrd, nio, nrrdField_space) || _nrrdFieldInteresting(nrrd, nio, nrrdField_space_dimension) || _nrrdFieldInteresting(nrrd, nio, nrrdField_sample_units) || airStrlen(nio->dataFNFormat) || nio->dataFNArr->len > 1) { ret = 4; } else if (_nrrdFieldInteresting(nrrd, nio, nrrdField_kinds)) { ret = 3; } else if (nrrdKeyValueSize(nrrd)) { ret = 2; } else { ret = 1; } return ret; } static int _nrrdFormatNRRD_available(void) { return AIR_TRUE; } static int _nrrdFormatNRRD_nameLooksLike(const char *filename) { return (airEndsWith(filename, NRRD_EXT_NRRD) || airEndsWith(filename, NRRD_EXT_NHDR)); } static int _nrrdFormatNRRD_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { static const char me[]="_nrrdFormatNRRD_fitsInto"; if (!( nrrd && encoding )) { biffMaybeAddf(useBiff, NRRD, "%s: got NULL nrrd (%p) or encoding (%p)", me, AIR_CVOIDP(nrrd), AIR_CVOIDP(encoding)); return AIR_FALSE; } /* everything fits in a nrrd */ return AIR_TRUE; } static int _nrrdFormatNRRD_contentStartsLike(NrrdIoState *nio) { return (!strcmp(MAGIC0, nio->line) || !strcmp(MAGIC1, nio->line) || !strcmp(MAGIC2, nio->line) || !strcmp(MAGIC3, nio->line) || !strcmp(MAGIC4, nio->line) || !strcmp(MAGIC5, nio->line) ); } /* ** _nrrdHeaderCheck() ** ** minimal consistency checks on relationship between fields of nrrd, ** only to be used after the headers is parsed, and before the data is ** read, to make sure that information required for reading data is in ** fact known. ** ** NOTE: this is not the place to do the sort of checking done by ** nrrdCheck(), because it includes I/O-specific stuff ** */ int _nrrdHeaderCheck(Nrrd *nrrd, NrrdIoState *nio, int checkSeen) { static const char me[]="_nrrdHeaderCheck"; int i; if (checkSeen) { for (i=1; i<=NRRD_FIELD_MAX; i++) { if (_nrrdFieldRequired[i] && !nio->seen[i]) { biffAddf(NRRD, "%s: didn't see required field: %s", me, airEnumStr(nrrdField, i)); return 1; } } } if (nrrdTypeBlock == nrrd->type && !nrrd->blockSize) { biffAddf(NRRD, "%s: type is %s, but missing field: %s", me, airEnumStr(nrrdType, nrrdTypeBlock), airEnumStr(nrrdField, nrrdField_block_size)); return 1; } if (!nrrdElementSize(nrrd)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); return 1; } /* _nrrdReadNrrdParse_sizes() checks axis[i].size, which completely determines the return of nrrdElementNumber() */ if (airEndianUnknown == nio->endian && nio->encoding->endianMatters && 1 != nrrdElementSize(nrrd)) { biffAddf(NRRD, "%s: type (%s) and encoding (%s) require %s info", me, airEnumStr(nrrdType, nrrd->type), nio->encoding->name, airEnumStr(nrrdField, nrrdField_endian)); return 1; } /* we don't really try to enforce consistency with the min/max/center/size information on each axis, other than the value checking done by the _nrrdReadNrrdParse_* functions, because we only really care that we know each axis size. Past that, if the user messes it up, its not really our problem ... */ return 0; } /* ** NOTE: currently, this will read, without complaints or errors, ** newer NRRD format features from older NRRD files (as indicated by ** magic), such as key/value pairs from a NRRD0001 file, even though ** strictly speaking these are violations of the format. ** ** NOTE: by giving a NULL "file", you can make this function basically ** do the work of reading in datafiles, without any header parsing */ static int _nrrdFormatNRRD_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatNRRD_read"; /* Dynamically allocated for space reasons. */ /* MWC: These strlen usages look really unsafe. */ int ret; unsigned int llen; size_t valsPerPiece; char *data; FILE *dataFile=NULL; /* record where the header is being read from for the sake of nrrdIoStateDataFileIterNext() */ nio->headerFile = file; /* GLK forgets the context in which file might be reasonably NULL but on Fri Sep 23 09:48:41 EDT 2005 this was "if (file) { ..." */ /* nio->headerStringRead is NULL whenever IO from string is not being done */ if (file || nio->headerStringRead) { if (!_nrrdFormatNRRD_contentStartsLike(nio)) { biffAddf(NRRD, "%s: this doesn't look like a %s file", me, nrrdFormatNRRD->name); return 1; } /* parse all the header lines */ do { nio->pos = 0; if (_nrrdOneLine(&llen, nio, file)) { biffAddf(NRRD, "%s: trouble getting line of header", me); return 1; } if (llen > 1) { ret = _nrrdReadNrrdParseField(nio, AIR_TRUE); if (!ret) { biffAddf(NRRD, "%s: trouble parsing NRRD field identifier from " "in \"%s\"", me, nio->line); return 1; } /* comments and key/values are allowed multiple times */ if (nio->seen[ret] && !(ret == nrrdField_comment || ret == nrrdField_keyvalue)) { biffAddf(NRRD, "%s: already set field %s", me, airEnumStr(nrrdField, ret)); return 1; } if (nrrdFieldInfoParse[ret](file, nrrd, nio, AIR_TRUE)) { biffAddf(NRRD, "%s: trouble parsing %s info |%s|", me, airEnumStr(nrrdField, ret), nio->line + nio->pos); return 1; } nio->seen[ret] = AIR_TRUE; } } while (llen > 1); /* either 0 == llen: we're at EOF (or end of nio->headerStringRead), or 1 == llen: we just read the empty line separating header from data */ if (0 == llen && !nio->headerStringRead && !nio->dataFNFormat && 0 == nio->dataFNArr->len) { /* we're at EOF, we're not reading from a string, but there's apparently no separate data file */ biffAddf(NRRD, "%s: hit end of header, but no \"%s\" given", me, airEnumStr(nrrdField, nrrdField_data_file)); return 1; } } if (_nrrdHeaderCheck(nrrd, nio, !!file)) { biffAddf(NRRD, "%s: %s", me, (llen ? "finished reading header, but there were problems" : "hit EOF before seeing a complete valid header")); return 1; } /* we seemed to have read in a valid header; now allocate the memory. For directIO-compatible allocation we need to get the first datafile */ nrrdIoStateDataFileIterBegin(nio); /* NOTE: if nio->headerStringRead, this may set dataFile to NULL */ if (nrrdIoStateDataFileIterNext(&dataFile, nio, AIR_TRUE)) { biffAddf(NRRD, "%s: couldn't open the first datafile", me); return 1; } if (nio->skipData) { nrrd->data = NULL; data = NULL; } else { if (_nrrdCalloc(nrrd, nio, dataFile)) { biffAddf(NRRD, "%s: couldn't allocate memory for data", me); return 1; } data = (char*)nrrd->data; } /* iterate through datafiles and read them in */ /* NOTE: you have to open dataFile even in the case of skipData, because caller might have set keepNrrdDataFileOpen, in which case you need to do any line or byte skipping if it is specified */ valsPerPiece = nrrdElementNumber(nrrd)/_nrrdDataFNNumber(nio); while (dataFile) { /* ---------------- skip, if need be */ if (nrrdLineSkip(dataFile, nio)) { biffAddf(NRRD, "%s: couldn't skip lines", me); return 1; } if (!nio->encoding->isCompression) { /* bytes are skipped here for non-compression encodings, but are skipped within the decompressed stream for compression encodings */ if (nrrdByteSkip(dataFile, nrrd, nio)) { biffAddf(NRRD, "%s: couldn't skip bytes", me); return 1; } } /* ---------------- read the data itself */ if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: reading %s data ... ", me, nio->encoding->name); fflush(stderr); } if (!nio->skipData) { if (nio->encoding->read(dataFile, data, valsPerPiece, nrrd, nio)) { if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "error!\n"); } biffAddf(NRRD, "%s:", me); return 1; } } if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "done)\n"); } /* ---------------- go to next data file */ if (nio->keepNrrdDataFileOpen && _nrrdDataFNNumber(nio) == 1) { nio->dataFile = dataFile; } else { if (dataFile != nio->headerFile) { dataFile = airFclose(dataFile); } } data += valsPerPiece*nrrdElementSize(nrrd); if (nrrdIoStateDataFileIterNext(&dataFile, nio, AIR_TRUE)) { biffAddf(NRRD, "%s: couldn't get the next datafile", me); return 1; } } if (airEndianUnknown != nio->endian && nrrd->data) { /* we positively know the endianness of data just read */ if (1 < nrrdElementSize(nrrd) && nio->encoding->endianMatters && nio->endian != airMyEndian()) { /* endianness exposed in encoding, and its wrong */ if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: fixing endianness ... ", me); fflush(stderr); } nrrdSwapEndian(nrrd); if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "done)\n"); fflush(stderr); } } } return 0; } static int _nrrdFormatNRRD_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatNRRD_write"; char strbuf[AIR_STRLEN_MED], *strptr, *tmp; int ii; unsigned int jj; airArray *mop; FILE *dataFile=NULL; size_t valsPerPiece; char *data; mop = airMopNew(); if (!(file || nio->headerStringWrite || nio->learningHeaderStrlen)) { biffAddf(NRRD, "%s: have no file or string to write to, nor are " "learning header string length", me); airMopError(mop); return 1; } if (nrrdTypeBlock == nrrd->type && nrrdEncodingAscii == nio->encoding) { biffAddf(NRRD, "%s: can't write nrrd type %s with %s encoding", me, airEnumStr(nrrdType, nrrdTypeBlock), nrrdEncodingAscii->name); airMopError(mop); return 1; } /* record where the header is being written to for the sake of nrrdIoStateDataFileIterNext(). This may be NULL if nio->headerStringWrite is non-NULL */ nio->headerFile = file; /* we have to make sure that the data filename information is set (if needed), so that it can be printed by _nrrdFprintFieldInfo */ if (nio->detachedHeader && !nio->dataFNFormat && 0 == nio->dataFNArr->len) { /* NOTE: this means someone requested a detached header, but we don't already have implicit (via dataFNFormat) or explicit (via dataFN[]) information about the data file */ /* NOTE: whether or not nio->skipData, we have to contrive a filename to say in the "data file" field, which is stored in nio->dataFN[0], because the data filename will be "interesting", according to _nrrdFieldInteresting() */ /* NOTE: Fri Feb 4 01:42:20 EST 2005 the way this is now set up, having a name in dataFN[0] will trump the name implied by nio->{path,base}, which is a useful way for the user to explicitly set the output data filename (as with unu make -od) */ if (!( !!airStrlen(nio->path) && !!airStrlen(nio->base) )) { biffAddf(NRRD, "%s: can't create data file name: nio's " "path and base empty", me); airMopError(mop); return 1; } tmp = (char*)malloc(strlen(nio->base) + strlen(".") + strlen(nio->encoding->suffix) + 1); if (!tmp) { biffAddf(NRRD, "%s: couldn't allocate data filename", me); airMopError(mop); return 1; } airMopAdd(mop, tmp, airFree, airMopOnError); sprintf(tmp, "%s.%s", nio->base, nio->encoding->suffix); jj = airArrayLenIncr(nio->dataFNArr, 1); if (!nio->dataFNArr->data) { biffAddf(NRRD, "%s: can't increase dataFNArr storage", me); airMopError(mop); return 1; } nio->dataFN[jj] = tmp; } /* the magic is in fact the first thing to be written */ if (file) { fprintf(file, "%s%04d\n", MAGIC, _nrrdFormatNRRD_whichVersion(nrrd, nio)); } else if (nio->headerStringWrite) { sprintf(nio->headerStringWrite, "%s%04d\n", MAGIC, _nrrdFormatNRRD_whichVersion(nrrd, nio)); } else { nio->headerStrlen = AIR_CAST(unsigned int, strlen(MAGIC) + strlen("0000")) + 1; } /* write the advertisement about where to get the file format */ if (!nio->skipFormatURL) { if (file) { fprintf(file, "# %s\n", _nrrdFormatURLLine0); fprintf(file, "# %s\n", _nrrdFormatURLLine1); } else if (nio->headerStringWrite) { sprintf(strbuf, "# %s\n", _nrrdFormatURLLine0); strcat(nio->headerStringWrite, strbuf); sprintf(strbuf, "# %s\n", _nrrdFormatURLLine1); strcat(nio->headerStringWrite, strbuf); } else { nio->headerStrlen += sprintf(strbuf, "# %s\n", _nrrdFormatURLLine0); nio->headerStrlen += sprintf(strbuf, "# %s\n", _nrrdFormatURLLine1); } } /* this is where the majority of the header printing happens */ for (ii=1; ii<=NRRD_FIELD_MAX; ii++) { if (_nrrdFieldInteresting(nrrd, nio, ii)) { if (file) { _nrrdFprintFieldInfo(file, "", nrrd, nio, ii); } else if (nio->headerStringWrite) { _nrrdSprintFieldInfo(&strptr, "", nrrd, nio, ii); if (strptr) { strcat(nio->headerStringWrite, strptr); strcat(nio->headerStringWrite, "\n"); free(strptr); strptr = NULL; } } else { _nrrdSprintFieldInfo(&strptr, "", nrrd, nio, ii); if (strptr) { nio->headerStrlen += AIR_CAST(unsigned int, strlen(strptr)); nio->headerStrlen += AIR_CAST(unsigned int, strlen("\n")); free(strptr); strptr = NULL; } } } } /* comments and key/value pairs handled differently */ for (jj=0; jjcmtArr->len; jj++) { char *strtmp; strtmp = airOneLinify(airStrdup(nrrd->cmt[jj])); if (file) { fprintf(file, "%c %s\n", NRRD_COMMENT_CHAR, strtmp); } else if (nio->headerStringWrite) { strptr = (char*)malloc(1 + strlen(" ") + strlen(strtmp) + strlen("\n") + 1); sprintf(strptr, "%c %s\n", NRRD_COMMENT_CHAR, strtmp); strcat(nio->headerStringWrite, strptr); free(strptr); strptr = NULL; } else { nio->headerStrlen += (1 + AIR_CAST(unsigned int, strlen(" ") + strlen(strtmp) + strlen("\n")) + 1); } airFree(strtmp); } for (jj=0; jjkvpArr->len; jj++) { if (file) { _nrrdKeyValueWrite(file, NULL, NULL, nrrd->kvp[0 + 2*jj], nrrd->kvp[1 + 2*jj]); } else if (nio->headerStringWrite) { _nrrdKeyValueWrite(NULL, &strptr, NULL, nrrd->kvp[0 + 2*jj], nrrd->kvp[1 + 2*jj]); if (strptr) { strcat(nio->headerStringWrite, strptr); free(strptr); strptr = NULL; } } else { _nrrdKeyValueWrite(NULL, &strptr, NULL, nrrd->kvp[0 + 2*jj], nrrd->kvp[1 + 2*jj]); if (strptr) { nio->headerStrlen += AIR_CAST(unsigned int, strlen(strptr)); free(strptr); strptr = NULL; } } } if (file) { if (!( nio->detachedHeader || _nrrdDataFNNumber(nio) > 1 )) { fprintf(file, "\n"); } } if (file && !nio->skipData) { nrrdIoStateDataFileIterBegin(nio); if (nrrdIoStateDataFileIterNext(&dataFile, nio, AIR_FALSE)) { biffAddf(NRRD, "%s: couldn't write the first datafile", me); airMopError(mop); return 1; } valsPerPiece = nrrdElementNumber(nrrd)/_nrrdDataFNNumber(nio); data = (char*)nrrd->data; do { /* ---------------- write data */ if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: writing %s data ", me, nio->encoding->name); fflush(stderr); } if (nio->encoding->write(dataFile, data, valsPerPiece, nrrd, nio)) { if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "error!\n"); } biffAddf(NRRD, "%s: couldn't write %s data", me, nio->encoding->name); airMopError(mop); return 1; } if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "done)\n"); } /* ---------------- go to next data file */ if (dataFile != nio->headerFile) { dataFile = airFclose(dataFile); } data += valsPerPiece*nrrdElementSize(nrrd); if (nrrdIoStateDataFileIterNext(&dataFile, nio, AIR_TRUE)) { biffAddf(NRRD, "%s: couldn't get the next datafile", me); airMopError(mop); return 1; } } while (dataFile); } airMopOkay(mop); return 0; } const NrrdFormat _nrrdFormatNRRD = { "NRRD", AIR_FALSE, /* isImage */ AIR_TRUE, /* readable */ AIR_TRUE, /* usesDIO */ _nrrdFormatNRRD_available, _nrrdFormatNRRD_nameLooksLike, _nrrdFormatNRRD_fitsInto, _nrrdFormatNRRD_contentStartsLike, _nrrdFormatNRRD_read, _nrrdFormatNRRD_write }; const NrrdFormat *const nrrdFormatNRRD = &_nrrdFormatNRRD; teem-1.11.0~svn6057/src/nrrd/encodingBzip2.c0000664000175000017500000001722212165631065020213 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" #if TEEM_BZIP2 #include #endif static int _nrrdEncodingBzip2_available(void) { #if TEEM_BZIP2 return AIR_TRUE; #else return AIR_FALSE; #endif } static int _nrrdEncodingBzip2_read(FILE *file, void *_data, size_t elNum, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingBzip2_read"; #if TEEM_BZIP2 size_t bsize, total_read, block_size; int read, bzerror=BZ_OK; long int bi; char *data; BZFILE* bzfin; bsize = nrrdElementSize(nrrd)*elNum; /* Create the BZFILE* for reading in the gzipped data. */ bzfin = BZ2_bzReadOpen(&bzerror, file, 0, 0, NULL, 0); if (bzerror != BZ_OK) { /* there was a problem */ biffAddf(NRRD, "%s: error opening BZFILE: %s", me, BZ2_bzerror(bzfin, &bzerror)); BZ2_bzReadClose(&bzerror, bzfin); return 1; } /* Here is where we do the byte skipping. */ for(bi=0; bibyteSkip; bi++) { unsigned char b; /* Check to see if a single byte was able to be read. */ read = BZ2_bzRead(&bzerror, bzfin, &b, 1); if (read != 1 || bzerror != BZ_OK) { biffAddf(NRRD, "%s: hit an error skipping byte %ld of %ld: %s", me, bi, nio->byteSkip, BZ2_bzerror(bzfin, &bzerror)); return 1; } } /* bzip2 can handle data sizes up to INT_MAX, so we can't just pass in the bsize, because it might be too large for an int. Therefore it must be read in chunks if the size is larger than INT_MAX. */ if (bsize <= INT_MAX) { block_size = bsize; } else { block_size = INT_MAX; } /* This counter will help us to make sure that we read as much data as we think we should. */ total_read = 0; /* Pointer to the blocks as we read them. */ data = (char *)_data; /* Ok, now we can begin reading. */ bzerror = BZ_OK; while ((read = BZ2_bzRead(&bzerror, bzfin, data, block_size)) && (BZ_OK == bzerror || BZ_STREAM_END == bzerror) ) { /* Increment the data pointer to the next available spot. */ data += read; total_read += read; /* We only want to read as much data as we need, so we need to check to make sure that we don't request data that might be there but that we don't want. This will reduce block_size when we get to the last block (which may be smaller than block_size). */ if (bsize >= total_read && bsize - total_read < block_size) block_size = bsize - total_read; } if (!( BZ_OK == bzerror || BZ_STREAM_END == bzerror )) { biffAddf(NRRD, "%s: error reading from BZFILE: %s", me, BZ2_bzerror(bzfin, &bzerror)); return 1; } /* Close the BZFILE. */ BZ2_bzReadClose(&bzerror, bzfin); if (BZ_OK != bzerror) { biffAddf(NRRD, "%s: error closing BZFILE: %s", me, BZ2_bzerror(bzfin, &bzerror)); return 1; } /* Check to see if we got out as much as we thought we should. */ if (total_read != bsize) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: expected %s bytes but received %s", me, airSprintSize_t(stmp1, bsize), airSprintSize_t(stmp2, total_read)); return 1; } return 0; #else AIR_UNUSED(file); AIR_UNUSED(_data); AIR_UNUSED(elNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: sorry, this nrrd not compiled with bzip2 enabled", me); return 1; #endif } static int _nrrdEncodingBzip2_write(FILE *file, const void *_data, size_t elNum, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingBzip2_write"; #if TEEM_BZIP2 size_t bsize, total_written, block_size; int bs, bzerror=BZ_OK; char *data; BZFILE* bzfout; bsize = nrrdElementSize(nrrd)*elNum; /* Set compression block size. */ if (1 <= nio->bzip2BlockSize && nio->bzip2BlockSize <= 9) { bs = nio->bzip2BlockSize; } else { bs = 9; } /* Open bzfile for writing. Verbosity and work factor are set to default values. */ bzfout = BZ2_bzWriteOpen(&bzerror, file, bs, 0, 0); if (BZ_OK != bzerror) { biffAddf(NRRD, "%s: error opening BZFILE: %s", me, BZ2_bzerror(bzfout, &bzerror)); BZ2_bzWriteClose(&bzerror, bzfout, 0, NULL, NULL); return 1; } /* bzip2 can handle data sizes up to INT_MAX, so we can't just pass in the bsize, because it might be too large for an int. Therefore it must be read in chunks if the bsize is larger than INT_MAX. */ if (bsize <= INT_MAX) { block_size = bsize; } else { block_size = INT_MAX; } /* This counter will help us to make sure that we write as much data as we think we should. */ total_written = 0; /* Pointer to the blocks as we write them. */ data = (char *)_data; /* Ok, now we can begin writing. */ bzerror = BZ_OK; while (bsize - total_written > block_size) { BZ2_bzWrite(&bzerror, bzfout, data, block_size); if (BZ_OK != bzerror) break; /* Increment the data pointer to the next available spot. */ data += block_size; total_written += block_size; } /* write the last (possibly smaller) block when its humungous data; write the whole data when its small */ if (BZ_OK == bzerror) { block_size = bsize >= total_written ? bsize - total_written : 0; BZ2_bzWrite(&bzerror, bzfout, data, block_size); total_written += block_size; } if (BZ_OK != bzerror) { biffAddf(NRRD, "%s: error writing to BZFILE: %s", me, BZ2_bzerror(bzfout, &bzerror)); return 1; } /* Close the BZFILE. */ BZ2_bzWriteClose(&bzerror, bzfout, 0, NULL, NULL); if (BZ_OK != bzerror) { biffAddf(NRRD, "%s: error closing BZFILE: %s", me, BZ2_bzerror(bzfout, &bzerror)); return 1; } /* Check to see if we got out as much as we thought we should. */ if (total_written != bsize) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: expected to write %s bytes, but only wrote %s", me, airSprintSize_t(stmp1, bsize), airSprintSize_t(stmp2, total_written)); return 1; } return 0; #else AIR_UNUSED(file); AIR_UNUSED(_data); AIR_UNUSED(elNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: sorry, this nrrd not compiled with bzip2 enabled", me); return 1; #endif } const NrrdEncoding _nrrdEncodingBzip2 = { "bzip2", /* name */ "raw.bz2", /* suffix */ AIR_TRUE, /* endianMatters */ AIR_TRUE, /* isCompression */ _nrrdEncodingBzip2_available, _nrrdEncodingBzip2_read, _nrrdEncodingBzip2_write }; const NrrdEncoding *const nrrdEncodingBzip2 = &_nrrdEncodingBzip2; teem-1.11.0~svn6057/src/nrrd/reorder.c0000664000175000017500000014534512177242206017166 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ******** nrrdInvertPerm() ** ** given an array (p) which represents a permutation of n elements, ** compute the inverse permutation ip. The value of this function ** is not its core functionality, but all the error checking it ** provides. */ int nrrdInvertPerm(unsigned int *invp, const unsigned int *pp, unsigned int nn) { static const char me[]="nrrdInvertPerm"; int problem; unsigned int ii; if (!(invp && pp && nn > 0)) { biffAddf(NRRD, "%s: got NULL pointer or non-positive nn (%d)", me, nn); return 1; } /* use the given array "invp" as a temp buffer for validity checking */ memset(invp, 0, nn*sizeof(unsigned int)); for (ii=0; iidim )) { biffAddf(NRRD, "%s: given axis (%d) outside valid range [0, %d]", me, axis, nin->dim); return 1; } if (NRRD_DIM_MAX == nin->dim) { biffAddf(NRRD, "%s: given nrrd already at NRRD_DIM_MAX (%d)", me, NRRD_DIM_MAX); return 1; } if (nout != nin) { if (_nrrdCopy(nout, nin, (NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT)))) { biffAddf(NRRD, "%s:", me); return 1; } } nout->dim = 1 + nin->dim; for (ai=nin->dim; ai>axis; ai--) { _nrrdAxisInfoCopy(&(nout->axis[ai]), &(nin->axis[ai-1]), NRRD_AXIS_INFO_NONE); } /* the ONLY thing we can say about the new axis is its size */ _nrrdAxisInfoInit(&(nout->axis[axis])); if (!nrrdStateKindNoop) { /* except maybe the kind */ nout->axis[axis].kind = nrrdKindStub; } nout->axis[axis].size = 1; if (nrrdContentSet_va(nout, func, nin, "%d", axis)) { biffAddf(NRRD, "%s:", me); return 1; } /* all basic info has already been copied by nrrdCopy() above */ return 0; } /* ******** nrrdAxesPermute ** ** changes the scanline ordering of the data in a nrrd ** ** The basic means by which data is moved around is with memcpy(). ** The goal is to call memcpy() as few times as possible, on memory ** segments as large as possible. Currently, this is done by ** detecting how many of the low-index axes are left untouched by ** the permutation- this constitutes a "scanline" which can be ** copied around as a unit. For permuting the y and z axes of a ** matrix-x-y-z order matrix volume, this optimization produced a ** factor of 5 speed up (exhaustive multi-platform tests, of course). ** ** The axes[] array determines the permutation of the axes. ** axis[i] = j means: axis i in the output will be the input's axis j ** (axis[i] answers: "what do I put here", from the standpoint of the output, ** not "where do I put this", from the standpoint of the input) */ int nrrdAxesPermute(Nrrd *nout, const Nrrd *nin, const unsigned int *axes) { static const char me[]="nrrdAxesPermute", func[]="permute"; char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_SMALL]; size_t idxOut, idxInA=0, /* indices for input and output scanlines */ lineSize, /* size of block of memory which can be moved contiguously from input to output, thought of as a "scanline" */ numLines, /* how many "scanlines" there are to permute */ szIn[NRRD_DIM_MAX], *lszIn, szOut[NRRD_DIM_MAX], *lszOut, cIn[NRRD_DIM_MAX], cOut[NRRD_DIM_MAX]; char *dataIn, *dataOut; int axmap[NRRD_DIM_MAX]; unsigned int ai, /* running index along dimensions */ lowPax, /* lowest axis which is "p"ermutated */ ldim, /* nin->dim - lowPax */ ip[NRRD_DIM_MAX+1], /* inverse of permutation in "axes" */ laxes[NRRD_DIM_MAX+1]; /* copy of axes[], but shifted down by lowPax elements, to remove i such that i == axes[i] */ airArray *mop; mop = airMopNew(); if (!(nin && nout && axes)) { biffAddf(NRRD, "%s: got NULL pointer", me); airMopError(mop); return 1; } /* we don't actually need ip[], computing it is for error checking */ if (nrrdInvertPerm(ip, axes, nin->dim)) { biffAddf(NRRD, "%s: couldn't compute axis permutation inverse", me); airMopError(mop); return 1; } /* this shouldn't actually be necessary .. */ if (!nrrdElementSize(nin)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); airMopError(mop); return 1; } for (ai=0; aidim && axes[ai] == ai; ai++) ; lowPax = ai; /* allocate output by initial copy */ if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: trouble copying input", me); airMopError(mop); return 1; } dataIn = (char*)nin->data; } else { dataIn = (char*)calloc(nrrdElementNumber(nin), nrrdElementSize(nin)); if (!dataIn) { biffAddf(NRRD, "%s: couldn't create local copy of data", me); airMopError(mop); return 1; } airMopAdd(mop, dataIn, airFree, airMopAlways); memcpy(dataIn, nin->data, nrrdElementNumber(nin)*nrrdElementSize(nin)); } if (lowPax < nin->dim) { /* if lowPax == nin->dim, then we were given the identity permutation, so there's nothing to do other than the copy already done. Otherwise, here we are (actually, lowPax < nin->dim-1) */ for (ai=0; aidim; ai++) { axmap[ai] = AIR_INT(axes[ai]); } nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, szIn); if (nrrdAxisInfoCopy(nout, nin, axmap, NRRD_AXIS_INFO_NONE)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } nrrdAxisInfoGet_nva(nout, nrrdAxisInfoSize, szOut); /* the skinny */ lineSize = 1; for (ai=0; aidim - lowPax; memset(laxes, 0, sizeof(laxes)); for (ai=0; aidata); memset(cIn, 0, sizeof(cIn)); memset(cOut, 0, sizeof(cOut)); for (idxOut=0; idxOutdim; ai++) { sprintf(buff2, "%s%d", (ai ? "," : ""), axes[ai]); strcat(buff1, buff2); } if (nrrdContentSet_va(nout, func, nin, "%s", buff1)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } if (nout != nin) { if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } } } airMopOkay(mop); return 0; } /* ******** nrrdShuffle ** ** rearranges hyperslices of a nrrd along a given axis according to ** given permutation. This could be used to on a 4D array, ** representing a 3D volume of vectors, to re-order the vector ** components. ** ** the given permutation array must allocated for at least as long as ** the input nrrd along the chosen axis. perm[j] = i means that the ** value at position j in the _new_ array should come from position i ** in the _old_array. The standpoint is from the new, looking at ** where to find the values amid the old array (perm answers "what do ** I put here", not "where do I put this"). This allows multiple ** positions in the new array to copy from the same old position, and ** insures that there is an source for all positions along the new ** array. */ int nrrdShuffle(Nrrd *nout, const Nrrd *nin, unsigned int axis, const size_t *perm) { static const char me[]="nrrdShuffle", func[]="shuffle"; char buff2[AIR_STRLEN_SMALL]; /* Sun Feb 8 13:13:58 CST 2009: There was a memory bug here caused by using the same buff1[NRRD_DIM_MAX*30] declaration that had worked fine for nrrdAxesPermute and nrrdReshape, but does NOT work here because now samples along an axes are re-ordered, not axes, so its often not allocated for long enough to hold the string that's printed to it. Ideally there'd be another argument that says whether to document the shuffle in the content string, which would mean an API change. Or, we can use a secret heuristic (or maybe later a nrrdState variable) for determining when an axis is short enough to make documenting the shuffle interesting. This is useful since functions like nrrdFlip() probably do *not* need the shuffle (the sample reversal) to be documented for long axes */ #define LONGEST_INTERESTING_AXIS 42 char buff1[LONGEST_INTERESTING_AXIS*30]; unsigned int ai, ldim, len; size_t idxInB=0, idxOut, lineSize, numLines, size[NRRD_DIM_MAX], *lsize, cIn[NRRD_DIM_MAX+1], cOut[NRRD_DIM_MAX+1]; char *dataIn, *dataOut; if (!(nin && nout && perm)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (!( axis < nin->dim )) { biffAddf(NRRD, "%s: axis %d outside valid range [0,%d]", me, axis, nin->dim-1); return 1; } len = AIR_CAST(unsigned int, nin->axis[axis].size); for (ai=0; aiblockSize = nin->blockSize; nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); if (nrrdMaybeAlloc_nva(nout, nin->type, nin->dim, size)) { biffAddf(NRRD, "%s: failed to allocate output", me); return 1; } if (nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE)) { biffAddf(NRRD, "%s:", me); return 1; } /* the min and max along the shuffled axis are now meaningless */ nout->axis[axis].min = nout->axis[axis].max = AIR_NAN; /* do the safe thing first */ nout->axis[axis].kind = _nrrdKindAltered(nin->axis[axis].kind, AIR_FALSE); /* try cleverness */ if (!nrrdStateKindNoop) { if (0 == nrrdKindSize(nin->axis[axis].kind) || nrrdKindStub == nin->axis[axis].kind || nrrdKindScalar == nin->axis[axis].kind || nrrdKind2Vector == nin->axis[axis].kind || nrrdKind3Color == nin->axis[axis].kind || nrrdKind4Color == nin->axis[axis].kind || nrrdKind3Vector == nin->axis[axis].kind || nrrdKind3Gradient == nin->axis[axis].kind || nrrdKind3Normal == nin->axis[axis].kind || nrrdKind4Vector == nin->axis[axis].kind) { /* these kinds have no intrinsic ordering */ nout->axis[axis].kind = nin->axis[axis].kind; } } /* the skinny */ lineSize = 1; for (ai=0; aiaxis[ai].size; } numLines = nrrdElementNumber(nin)/lineSize; lineSize *= nrrdElementSize(nin); lsize = size + axis; ldim = nin->dim - axis; dataIn = AIR_CAST(char *, nin->data); dataOut = AIR_CAST(char *, nout->data); memset(cIn, 0, sizeof(cIn)); memset(cOut, 0, sizeof(cOut)); for (idxOut=0; idxOutdim && ax2 < nin->dim )) { biffAddf(NRRD, "%s: ax1 (%d) or ax2 (%d) out of bounds [0,%d]", me, ax1, ax2, nin->dim-1); return 1; } for (ai=0; aidim; ai++) { axmap[ai] = ai; } axmap[ax2] = ax1; axmap[ax1] = ax2; if (nrrdAxesPermute(nout, nin, axmap) || nrrdContentSet_va(nout, func, nin, "%d,%d", ax1, ax2)) { biffAddf(NRRD, "%s:", me); return 1; } /* basic info already copied by nrrdAxesPermute */ return 0; } /* ******** nrrdFlip() ** ** reverse the order of slices along the given axis. ** Actually, just a wrapper around nrrdShuffle() (with some ** extra setting of axis info) */ int nrrdFlip(Nrrd *nout, const Nrrd *nin, unsigned int axis) { static const char me[]="nrrdFlip", func[]="flip"; size_t *perm, si; airArray *mop; unsigned int axisIdx; mop = airMopNew(); if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); airMopError(mop); return 1; } if (!( axis < nin->dim )) { biffAddf(NRRD, "%s: given axis (%d) is outside valid range ([0,%d])", me, axis, nin->dim-1); airMopError(mop); return 1; } if (!(perm = (size_t*)calloc(nin->axis[axis].size, sizeof(size_t)))) { biffAddf(NRRD, "%s: couldn't alloc permutation array", me); airMopError(mop); return 1; } airMopAdd(mop, perm, airFree, airMopAlways); for (si=0; siaxis[axis].size; si++) { perm[si] = nin->axis[axis].size-1-si; } /* nrrdBasicInfoCopy called by nrrdShuffle() */ if (nrrdShuffle(nout, nin, axis, perm) || nrrdContentSet_va(nout, func, nin, "%d", axis)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } _nrrdAxisInfoCopy(&(nout->axis[axis]), &(nin->axis[axis]), NRRD_AXIS_INFO_SIZE_BIT | NRRD_AXIS_INFO_KIND_BIT); /* HEY: (Tue Jan 18 00:28:26 EST 2005) there's a basic question to be answered here: do we want to keep the "location" of the samples fixed, while changing their ordering, or do want to flip the location of the samples? In the former, the position information has to be flipped to cancel the flipping of the the sample order, so that samples maintain location. In the latter, the position information is copied verbatim from the original. */ /* (Tue Sep 13 09:59:12 EDT 2005) answer: we keep the "location" of the samples fixed, while changing their ordering. This is the low-level thing to do, so for a nrrd function, its the right thing to do. You don't need a nrrd function to simply manipulate per-axis meta-information */ nout->axis[axis].min = nin->axis[axis].max; nout->axis[axis].max = nin->axis[axis].min; /* HEY: Fri Jan 14 02:53:30 EST 2005: isn't spacing supposed to be the step from one sample to the next? So its a signed quantity. If min and max can be flipped (so min > max), then spacing can be negative, right? */ nout->axis[axis].spacing = -nin->axis[axis].spacing; /* HEY: Fri Jan 14 02:53:30 EST 2005: but not thickness */ nout->axis[axis].thickness = nin->axis[axis].thickness; /* need to set general orientation info too */ for (axisIdx=0; axisIdxaxis[axis].spaceDirection[axisIdx] = -nin->axis[axis].spaceDirection[axisIdx]; } /* modify origin only if we flipped a spatial axis */ if (AIR_EXISTS(nin->axis[axis].spaceDirection[0])) { nrrdSpaceVecScaleAdd2(nout->spaceOrigin, 1.0, nin->spaceOrigin, AIR_CAST(double, nin->axis[axis].size-1), nin->axis[axis].spaceDirection); } else { nrrdSpaceVecCopy(nout->spaceOrigin, nin->spaceOrigin); } airMopOkay(mop); return 0; } /* ** ** NOTE: this seems to destroy all space/orientation info. What ** should be done? */ int nrrdJoin(Nrrd *nout, const Nrrd *const *nin, unsigned int ninNum, unsigned int axis, int incrDim) { static const char me[]="nrrdJoin"; unsigned int ni, ai, mindim, maxdim, outdim, permute[NRRD_DIM_MAX], ipermute[NRRD_DIM_MAX]; int diffdim, axmap[NRRD_DIM_MAX]; size_t outlen, outnum, chunksize, size[NRRD_DIM_MAX]; char *dataPerm; Nrrd *ntmpperm, /* axis-permuted version of output */ **ninperm; airArray *mop; char stmp[2][AIR_STRLEN_SMALL]; /* error checking */ if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!(ninNum >= 1)) { biffAddf(NRRD, "%s: ninNum (%d) must be >= 1", me, ninNum); return 1; } for (ni=0; nidim; for (ni=0; nidim); maxdim = AIR_MAX(maxdim, nin[ni]->dim); } diffdim = maxdim - mindim; if (diffdim > 1) { biffAddf(NRRD, "%s: will only reshape up one dimension (not %d)", me, diffdim); airMopError(mop); return 1; } if (axis > maxdim) { biffAddf(NRRD, "%s: can't join along axis %d with highest input dim = %d", me, axis, maxdim); airMopError(mop); return 1; } /* figure out dimension of output (outdim) */ if (diffdim) { /* case A: (example) 2D slices and 3D slabs are being joined together to make a bigger 3D volume */ outdim = maxdim; } else { /* diffdim == 0 */ if (axis == maxdim) { /* case B: this is like the old "stitch": a bunch of equal-sized slices of dimension N are being stacked together to make an N+1 dimensional volume, which is essentially just the result of concatenating the memory of individual inputs */ outdim = maxdim + 1; } else { /* case C: axis < maxdim; maxdim == mindim */ /* case C1 (!incrDim): a bunch of N-D slabs are being joined together to make a bigger N-D volume. The axis along which they are being joined could be any of existing axes (from 0 to maxdim-1) */ /* case C2 (incrDim): this is also a "stitch", but the new axis created by the stitching is inserted into the existing axes. (ex: stitch 3 PGMs (R, G, B) together into a PPM (with color on axis zero) */ outdim = maxdim + !!incrDim; } } if (outdim > NRRD_DIM_MAX) { biffAddf(NRRD, "%s: output dimension (%d) exceeds NRRD_DIM_MAX (%d)", me, outdim, NRRD_DIM_MAX); airMopError(mop); return 1; } /* do tacit reshaping, and possibly permuting, as needed */ for (ai=0; aidim; /* fprintf(stderr, "!%s: ni = %d ---> diffdim = %d\n", me, ni, diffdim); */ if (diffdim) { /* we do a tacit reshaping, which actually includes a tacit permuting, so we don't have to call permute on the parts that don't actually need it */ /* NB: we register nrrdNix, not nrrdNuke */ /* fprintf(stderr, "!%s: %d: tacit reshape/permute\n", me, ni); */ airMopAdd(mop, ninperm[ni], (airMopper)nrrdNix, airMopAlways); nrrdAxisInfoGet_nva(nin[ni], nrrdAxisInfoSize, size); for (ai=nin[ni]->dim-1; ai>=mindim+1; ai--) { size[ai] = size[ai-1]; } size[mindim] = 1; /* this may be done needlessly often */ for (ai=0; ai<=nin[ni]->dim; ai++) { if (ai < mindim) { axmap[ai] = ai; } else if (ai > mindim) { axmap[ai] = ai-1; } else { axmap[ai] = -1; } } /* we don't have to actually call nrrdReshape(): we just nrrdWrap() the input data with the reshaped size array */ if (nrrdWrap_nva(ninperm[ni], nin[ni]->data, nin[ni]->type, nin[ni]->dim+1, size)) { biffAddf(NRRD, "%s: trouble creating interm. version of nrrd %d", me, ni); airMopError(mop); return 1; } nrrdAxisInfoCopy(ninperm[ni], nin[ni], axmap, (NRRD_AXIS_INFO_SIZE_BIT /* HEY: this is being nixed because I can't think of a sane way of keeping it consistent */ | NRRD_AXIS_INFO_SPACEDIRECTION_BIT)); } else { /* on this part, we permute (no need for a reshape) */ airMopAdd(mop, ninperm[ni], (airMopper)nrrdNuke, airMopAlways); if (nrrdAxesPermute(ninperm[ni], nin[ni], permute)) { biffAddf(NRRD, "%s: trouble permuting input part %d", me, ni); airMopError(mop); return 1; } } } /* make sure all parts are compatible in type and shape, determine length of final output along axis (outlen) */ outlen = 0; for (ni=0; nitype != ninperm[0]->type) { biffAddf(NRRD, "%s: type (%s) of part %d unlike first's (%s)", me, airEnumStr(nrrdType, ninperm[ni]->type), ni, airEnumStr(nrrdType, ninperm[0]->type)); airMopError(mop); return 1; } if (nrrdTypeBlock == ninperm[0]->type) { if (ninperm[ni]->blockSize != ninperm[0]->blockSize) { biffAddf(NRRD, "%s: blockSize (%s) of part %d != first's (%s)", me, airSprintSize_t(stmp[0], ninperm[ni]->blockSize), ni, airSprintSize_t(stmp[1], ninperm[0]->blockSize)); airMopError(mop); return 1; } } if (!nrrdElementSize(ninperm[ni])) { biffAddf(NRRD, "%s: got wacky elements size (%s) for part %d", me, airSprintSize_t(stmp[0], nrrdElementSize(ninperm[ni])), ni); airMopError(mop); return 1; } /* fprintf(stderr, "!%s: part %03d shape: ", me, ni); */ for (ai=0; aiaxis[ai].size);*/ if (ninperm[ni]->axis[ai].size != ninperm[0]->axis[ai].size) { biffAddf(NRRD, "%s: axis %d size (%s) of part %d != first's (%s)", me, ai, airSprintSize_t(stmp[0], ninperm[ni]->axis[ai].size), ni, airSprintSize_t(stmp[1], ninperm[0]->axis[ai].size)); airMopError(mop); return 1; } } /* fprintf(stderr, "%03u\n", (unsigned int)ninperm[ni]->axis[outdim-1].size); */ outlen += ninperm[ni]->axis[outdim-1].size; } /* fprintf(stderr, "!%s: outlen = %u\n", me, (unsigned int)outlen); */ /* allocate temporary nrrd and concat input into it */ outnum = 1; if (outdim > 1) { for (ai=0; aiaxis[ai].size; outnum *= size[ai]; } } size[outdim-1] = outlen; outnum *= size[outdim-1]; if (nrrdMaybeAlloc_nva(ntmpperm = nrrdNew(), ninperm[0]->type, outdim, size)) { biffAddf(NRRD, "%s: trouble allocating permutation nrrd", me); airMopError(mop); return 1; } airMopAdd(mop, ntmpperm, (airMopper)nrrdNuke, airMopAlways); dataPerm = AIR_CAST(char *, ntmpperm->data); for (ni=0; nidata, chunksize); dataPerm += chunksize; } /* copy other axis-specific fields from nin[0] to ntmpperm */ for (ai=0; aiaxis[outdim-1].size = outlen; /* do the permutation required to get output in right order */ if (nrrdInvertPerm(ipermute, permute, outdim) || nrrdAxesPermute(nout, ntmpperm, ipermute)) { biffAddf(NRRD, "%s: error permuting temporary nrrd", me); airMopError(mop); return 1; } /* basic info is either already set or invalidated by joining */ /* HEY: set content on output! */ airMopOkay(mop); return 0; } /* ******** nrrdAxesSplit ** ** like reshape, but only for splitting one axis into a fast and slow part. */ int nrrdAxesSplit(Nrrd *nout, const Nrrd *nin, unsigned int saxi, size_t sizeFast, size_t sizeSlow) { static const char me[]="nrrdAxesSplit", func[]="axsplit"; unsigned int ai; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( saxi <= nin->dim-1 )) { biffAddf(NRRD, "%s: given axis (%d) outside valid range [0, %d]", me, saxi, nin->dim-1); return 1; } if (NRRD_DIM_MAX == nin->dim) { biffAddf(NRRD, "%s: given nrrd already at NRRD_DIM_MAX (%d)", me, NRRD_DIM_MAX); return 1; } if (!(sizeFast*sizeSlow == nin->axis[saxi].size)) { char stmp[4][AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: # samples along axis %d (%s) != " "product of fast and slow sizes (%s * %s = %s)", me, saxi, airSprintSize_t(stmp[0], nin->axis[saxi].size), airSprintSize_t(stmp[1], sizeFast), airSprintSize_t(stmp[2], sizeSlow), airSprintSize_t(stmp[3], sizeFast*sizeSlow)); return 1; } if (nout != nin) { if (_nrrdCopy(nout, nin, (NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT)))) { biffAddf(NRRD, "%s:", me); return 1; } } nout->dim = 1 + nin->dim; for (ai=nin->dim-1; ai>=saxi+1; ai--) { _nrrdAxisInfoCopy(&(nout->axis[ai+1]), &(nin->axis[ai]), NRRD_AXIS_INFO_NONE); } /* the ONLY thing we can say about the new axes are their sizes */ _nrrdAxisInfoInit(&(nout->axis[saxi])); _nrrdAxisInfoInit(&(nout->axis[saxi+1])); nout->axis[saxi].size = sizeFast; nout->axis[saxi+1].size = sizeSlow; if (nrrdContentSet_va(nout, func, nin, "%d,%d,%d", saxi, sizeFast, sizeSlow)) { biffAddf(NRRD, "%s:", me); return 1; } /* all basic information already copied by nrrdCopy */ return 0; } /* ******** nrrdAxesDelete ** ** like reshape, but preserves axis information on old axes, and ** this is only for removing a "stub" axis with length 1. */ int nrrdAxesDelete(Nrrd *nout, const Nrrd *nin, unsigned int daxi) { static const char me[]="nrrdAxesDelete", func[]="axdelete"; unsigned int ai; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( daxi < nin->dim )) { biffAddf(NRRD, "%s: given axis (%d) outside valid range [0, %d]", me, daxi, nin->dim-1); return 1; } if (1 == nin->dim) { biffAddf(NRRD, "%s: given nrrd already at lowest dimension (1)", me); return 1; } if (1 != nin->axis[daxi].size) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: size along axis %d is %s, not 1", me, daxi, airSprintSize_t(stmp, nin->axis[daxi].size)); return 1; } if (nout != nin) { if (_nrrdCopy(nout, nin, (NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT)))) { biffAddf(NRRD, "%s:", me); return 1; } } for (ai=daxi; aidim-1; ai++) { _nrrdAxisInfoCopy(&(nout->axis[ai]), &(nin->axis[ai+1]), NRRD_AXIS_INFO_NONE); } nout->dim = nin->dim - 1; if (nrrdContentSet_va(nout, func, nin, "%d", daxi)) { biffAddf(NRRD, "%s:", me); return 1; } /* all basic information already copied by nrrdCopy */ return 0; } /* ******** nrrdAxesMerge ** ** like reshape, but preserves axis information on old axes ** merges axis ax and ax+1 into one */ int nrrdAxesMerge(Nrrd *nout, const Nrrd *nin, unsigned int maxi) { static const char me[]="nrrdAxesMerge", func[]="axmerge"; unsigned int ai; size_t sizeFast, sizeSlow; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( maxi < nin->dim-1 )) { biffAddf(NRRD, "%s: given axis (%d) outside valid range [0, %d]", me, maxi, nin->dim-2); return 1; } if (1 == nin->dim) { biffAddf(NRRD, "%s: given nrrd already at lowest dimension (1)", me); return 1; } if (nout != nin) { if (_nrrdCopy(nout, nin, (NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT)))) { biffAddf(NRRD, "%s:", me); return 1; } } sizeFast = nin->axis[maxi].size; sizeSlow = nin->axis[maxi+1].size; nout->dim = nin->dim - 1; for (ai=maxi+1; aidim; ai++) { _nrrdAxisInfoCopy(&(nout->axis[ai]), &(nin->axis[ai+1]), NRRD_AXIS_INFO_NONE); } /* the ONLY thing we can say about the new axis is its size */ _nrrdAxisInfoInit(&(nout->axis[maxi])); nout->axis[maxi].size = sizeFast*sizeSlow; if (nrrdContentSet_va(nout, func, nin, "%d", maxi)) { biffAddf(NRRD, "%s:", me); return 1; } /* all basic information already copied by nrrdCopy */ return 0; } /* ******** nrrdReshape_nva() ** */ int nrrdReshape_nva(Nrrd *nout, const Nrrd *nin, unsigned int dim, const size_t *size) { static const char me[]="nrrdReshape_nva", func[]="reshape"; char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_SMALL]; size_t numOut; unsigned int ai; char stmp[2][AIR_STRLEN_SMALL]; if (!(nout && nin && size)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!(AIR_IN_CL(1, dim, NRRD_DIM_MAX))) { biffAddf(NRRD, "%s: given dimension (%d) outside valid range [1,%d]", me, dim, NRRD_DIM_MAX); return 1; } if (_nrrdSizeCheck(size, dim, AIR_TRUE)) { biffAddf(NRRD, "%s:", me); return 1; } numOut = 1; for (ai=0; aidim = dim; for (ai=0; aiaxis[ai])); nout->axis[ai].size = size[ai]; } strcpy(buff1, ""); for (ai=0; aidim) { biffAddf(NRRD, "%s: can't blockify 1-D nrrd", me); return 1; } /* this shouldn't actually be necessary .. */ if (!nrrdElementSize(nin)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); return 1; } numEl = nin->axis[0].size;; nout->blockSize = numEl*nrrdElementSize(nin); /* fprintf(stderr, "!%s: nout->blockSize = %d * %d = %d\n", me, numEl, nrrdElementSize(nin), nout->blockSize); */ for (ai=0; aidim-1; ai++) { map[ai] = ai+1; size[ai] = nin->axis[map[ai]].size; } /* nout->blockSize set above */ if (nrrdMaybeAlloc_nva(nout, nrrdTypeBlock, nin->dim-1, size)) { biffAddf(NRRD, "%s: failed to allocate output", me); return 1; } memcpy(nout->data, nin->data, nrrdElementNumber(nin)*nrrdElementSize(nin)); if (nrrdAxisInfoCopy(nout, nin, map, NRRD_AXIS_INFO_NONE)) { biffAddf(NRRD, "%s: failed to copy axes", me); return 1; } if (nrrdContentSet_va(nout, func, nin, "")) { biffAddf(NRRD, "%s:", me); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } /* ******** nrrdUnblock() ** ** takes a nrrdTypeBlock nrrd and breaks the blocks into elements of ** type "type", and shifts other axis information up by one axis */ int nrrdUnblock(Nrrd *nout, const Nrrd *nin, int type) { static const char me[]="nrrdUnblock", func[]="unblock"; unsigned int dim; size_t size[NRRD_DIM_MAX], outElSz; int map[NRRD_DIM_MAX]; char stmp[2][AIR_STRLEN_SMALL]; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: due to laziness, nout==nin disallowed", me); return 1; } if (nrrdTypeBlock != nin->type) { biffAddf(NRRD, "%s: need input nrrd type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (NRRD_DIM_MAX == nin->dim) { biffAddf(NRRD, "%s: input nrrd already at dimension limit (%d)", me, NRRD_DIM_MAX); return 1; } if (airEnumValCheck(nrrdType, type)) { biffAddf(NRRD, "%s: invalid requested type %d", me, type); return 1; } if (nrrdTypeBlock == type && (!(0 < nout->blockSize))) { biffAddf(NRRD, "%s: for %s type, need nout->blockSize set", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } /* this shouldn't actually be necessary .. */ if (!(nrrdElementSize(nin))) { biffAddf(NRRD, "%s: nin or nout reports zero element size!", me); return 1; } nout->type = type; outElSz = nrrdElementSize(nout); if (nin->blockSize % outElSz) { biffAddf(NRRD, "%s: input blockSize (%s) not multiple of output " "element size (%s)", me, airSprintSize_t(stmp[0], nin->blockSize), airSprintSize_t(stmp[1], outElSz)); return 1; } for (dim=0; dim<=nin->dim; dim++) { map[dim] = !dim ? -1 : (int)dim-1; size[dim] = !dim ? nin->blockSize / outElSz : nin->axis[map[dim]].size; } /* if nout->blockSize is needed, we've checked that its set */ if (nrrdMaybeAlloc_nva(nout, type, nin->dim+1, size)) { biffAddf(NRRD, "%s: failed to allocate output", me); return 1; } memcpy(nout->data, nin->data, nrrdElementNumber(nin)*nrrdElementSize(nin)); if (nrrdAxisInfoCopy(nout, nin, map, NRRD_AXIS_INFO_NONE)) { biffAddf(NRRD, "%s: failed to copy axes", me); return 1; } if (nrrdContentSet_va(nout, func, nin, "")) { biffAddf(NRRD, "%s:", me); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } /* for nrrdTile .. will require that # slices be <= number of images: won't crop for you, but will happy pad with black. This will be handled in another function. Probably unu tile. */ /* ******** nrrdTile2D() ** ** Splits axis axSplit into two pieces of size sizeFast and sizeSlow. ** The data from the fast partition is juxtaposed following ax0, the ** slow after ax1. nrrdAxesMerge is then called to join ax0 and ax1 ** with their respective newly permuted data. There should be one ** fewer dimensions in the output nrrd than in the input nrrd. */ int nrrdTile2D(Nrrd *nout, const Nrrd *nin, unsigned int ax0, unsigned int ax1, unsigned int axSplit, size_t sizeFast, size_t sizeSlow) { static const char me[]="nrrdTile2D"; int E, /* error flag */ insAxis[2*NRRD_DIM_MAX], /* array for inserting the two axes resulting from the initial split amongst the other axes: inserted axes go in odd slots, other axes go in even slots */ mapIdx, /* index for filling map[] */ merge[2], /* two axes to be merged post-permute */ mergeIdx; /* index for filling merge[] */ unsigned int ii, map[NRRD_DIM_MAX]; /* axis map for axis permute */ if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } /* at least for now, axSplit, ax0, and ax1 need to be distinct */ if (!( axSplit != ax0 && axSplit != ax1 && ax0 != ax1 )) { biffAddf(NRRD, "%s: axSplit, ax0, ax1 (%d,%d,%d) must be distinct", me, axSplit, ax0, ax1); return 1; } if (!( ax0 < nin->dim && ax1 < nin->dim && axSplit < nin->dim )) { biffAddf(NRRD, "%s: axSplit, ax0, ax1 (%d,%d,%d) must be in range [0,%d]", me, axSplit, ax0, ax1, nin->dim-1); return 1; } if (nout != nin) { if (_nrrdCopy(nout, nin, (NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT)))) { biffAddf(NRRD, "%s:", me); return 1; } } /* increment ax0 and ax1 if they're above axSplit, since the initial axis split will bump up the corresponding axes */ ax0 += (axSplit < ax0); ax1 += (axSplit < ax1); /* initialize insAxis to all invalid (blank) values */ for (ii=0; ii<2*(nout->dim+1); ii++) { insAxis[ii] = -1; } /* run through post-split axes, inserting axSplit and axSplit+1 into the slots after ax0 and ax1 respectively, otherwise set the identity map */ for (ii=0; ii<(nout->dim+1); ii++) { if (axSplit == ii) { insAxis[2*ax0 + 1] = axSplit; } else if (axSplit+1 == ii) { insAxis[2*ax1 + 1] = axSplit+1; } else { insAxis[2*ii + 0] = ii; } } /* settle the values from insAxis[] into map[] by removing the -1's */ mergeIdx = mapIdx = 0; for (ii=0; ii<2*(nout->dim+1); ii++) { if (insAxis[ii] != -1) { if (1 == ii % 2) { /* its an odd entry in insAxis[], so the previous axis is to be merged. Using mapIdx-1 is legit because we disallow axSplit == ax{0,1} */ merge[mergeIdx++] = mapIdx-1; } map[mapIdx++] = insAxis[ii]; } } E = AIR_FALSE; if (!E) E |= nrrdAxesSplit(nout, nout, axSplit, sizeFast, sizeSlow); if (!E) E |= nrrdAxesPermute(nout, nout, map); if (!E) E |= nrrdAxesMerge(nout, nout, merge[1]); if (!E) E |= nrrdAxesMerge(nout, nout, merge[0]); if (E) { biffAddf(NRRD, "%s: trouble", me); return 1; } /* HEY: set content */ if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } /* ******** nrrdUntile2D() ** ** This will split ax0 into nin->axis[ax0].size/sizeFast and sizeFast ** sizes. ax1 will then be split into nin->axis[ax1].size/sizeSlow ** and sizeSlow sizes. The axes corresponding to sizeFast and ** sizeSlow will be permuted and merged such that ** nout->axis[axMerge].size == sizeFast*sizeSlow. ** ** The thing to be careful of is that axMerge identifies an axis ** in the array set *after* the two axis splits, not before. This ** is in contrast to the axSplit (and ax0 and ax1) argument of nrrdTile2D ** which identifies axes in the original nrrd. */ int nrrdUntile2D(Nrrd *nout, const Nrrd *nin, unsigned int ax0, unsigned int ax1, unsigned int axMerge, size_t sizeFast, size_t sizeSlow) { static const char me[]="nrrdUntile2D"; int E; unsigned int ii, mapIdx, map[NRRD_DIM_MAX]; char stmp[2][AIR_STRLEN_SMALL]; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (ax0 == ax1) { biffAddf(NRRD, "%s: ax0 (%d) and ax1 (%d) must be distinct", me, ax0, ax1); return 1; } if (!( ax0 < nin->dim && ax1 < nin->dim )) { biffAddf(NRRD, "%s: ax0, ax1 (%d,%d) must be in range [0,%d]", me, ax0, ax1, nin->dim-1); return 1; } if (!( axMerge <= nin->dim )) { biffAddf(NRRD, "%s: axMerge (%d) must be in range [0,%d]", me, axMerge, nin->dim); return 1; } if (nin->axis[ax0].size != sizeFast*(nin->axis[ax0].size/sizeFast)) { biffAddf(NRRD, "%s: sizeFast (%s) doesn't divide into axis %d size (%s)", me, airSprintSize_t(stmp[0], sizeFast), ax0, airSprintSize_t(stmp[1], nin->axis[ax0].size)); return 1; } if (nin->axis[ax1].size != sizeSlow*(nin->axis[ax1].size/sizeSlow)) { biffAddf(NRRD, "%s: sizeSlow (%s) doesn't divide into axis %d size (%s)", me, airSprintSize_t(stmp[0], sizeSlow), ax1, airSprintSize_t(stmp[1], nin->axis[ax1].size)); return 1; } if (nout != nin) { if (_nrrdCopy(nout, nin, (NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT)))) { biffAddf(NRRD, "%s:", me); return 1; } } /* Split the larger (slower) axis first. */ E = AIR_FALSE; if (ax0 < ax1) { if (!E) E |= nrrdAxesSplit(nout, nout, ax1, nin->axis[ax1].size/sizeSlow, sizeSlow); if (!E) E |= nrrdAxesSplit(nout, nout, ax0, nin->axis[ax0].size/sizeFast, sizeFast); /* Increment the larger value as it will get shifted by the lower split. */ ax1++; } else { if (!E) E |= nrrdAxesSplit(nout, nout, ax0, nin->axis[ax0].size/sizeFast, sizeFast); if (!E) E |= nrrdAxesSplit(nout, nout, ax1, nin->axis[ax1].size/sizeSlow, sizeSlow); ax0++; } if (E) { biffAddf(NRRD, "%s: trouble with initial splitting", me); return 1; } /* Determine the axis permutation map */ mapIdx = 0; for (ii=0; iidim; ii++) { if (mapIdx == axMerge) { /* Insert the slow parts of the axes that have been split */ map[mapIdx++] = ax0+1; map[mapIdx++] = ax1+1; } if (ii == ax0+1 || ii == ax1+1) { /* These are handled by the logic above */ } else { /* Otherwise use the identity map */ map[mapIdx++] = ii; } } /* fprintf(stderr, "%s: map =", me); for (ii=0; iidim; ii++) { fprintf(stderr, " %d", map[ii]); } fprintf(stderr, "; axMerge = %d\n", axMerge); */ E = AIR_FALSE; if (!E) E |= nrrdAxesPermute(nout, nout, map); if (!E) E |= nrrdAxesMerge(nout, nout, axMerge); if (E) { biffAddf(NRRD, "%s: trouble", me); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } #if 0 int nrrdShift(Nrrd *nout, const Nrrd *nin, const ptrdiff_t *offset, int boundary, double padValue) { static const char me[]="nrrdShift", func[] = "shift"; if (!(nout && nin && offset)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } return 0; } #endif /* ---- END non-NrrdIO */ teem-1.11.0~svn6057/src/nrrd/endianNrrd.c0000664000175000017500000000774212165631065017610 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" static void _nrrdSwap16Endian(void *_data, size_t N) { unsigned short *data, dd, fix, mask; size_t I; if (!_data) { return; } data = AIR_CAST(unsigned short *, _data); mask = AIR_CAST(unsigned short, 0x00FFu); for (I=0; I>= 0x08; fix = (dd & mask) | AIR_CAST(unsigned short, fix << 0x08); data[I] = fix; } } static void _nrrdSwap32Endian(void *_data, size_t N) { unsigned int *data, dd, fix, mask; size_t I; if (!_data) { return; } data = AIR_CAST(unsigned int *, _data); mask = 0x000000FFu; for (I=0; I>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); data[I] = fix; } } static void _nrrdSwap64Endian(void *_data, size_t N) { airULLong *data, dd, fix, mask; size_t I; if (!_data) { return; } data = AIR_CAST(airULLong *, _data); mask = AIR_ULLONG(0x00000000000000FF); for (I=0; I>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); data[I] = fix; } } static void _nrrdNoopEndian(void *data, size_t N) { AIR_UNUSED(data); AIR_UNUSED(N); return; } static void _nrrdBlockEndian(void *data, size_t N) { char me[]="_nrrdBlockEndian"; AIR_UNUSED(data); AIR_UNUSED(N); fprintf(stderr, "%s: WARNING: can't fix endiannes of nrrd type %s\n", me, airEnumStr(nrrdType, nrrdTypeBlock)); } static void (*_nrrdSwapEndian[])(void *, size_t) = { _nrrdNoopEndian, /* 0: nobody knows! */ _nrrdNoopEndian, /* 1: signed 1-byte integer */ _nrrdNoopEndian, /* 2: unsigned 1-byte integer */ _nrrdSwap16Endian, /* 3: signed 2-byte integer */ _nrrdSwap16Endian, /* 4: unsigned 2-byte integer */ _nrrdSwap32Endian, /* 5: signed 4-byte integer */ _nrrdSwap32Endian, /* 6: unsigned 4-byte integer */ _nrrdSwap64Endian, /* 7: signed 8-byte integer */ _nrrdSwap64Endian, /* 8: unsigned 8-byte integer */ _nrrdSwap32Endian, /* 9: 4-byte floating point */ _nrrdSwap64Endian, /* 10: 8-byte floating point */ _nrrdBlockEndian /* 11: size user defined at run time */ }; void nrrdSwapEndian(Nrrd *nrrd) { if (nrrd && nrrd->data && !airEnumValCheck(nrrdType, nrrd->type)) { _nrrdSwapEndian[nrrd->type](nrrd->data, nrrdElementNumber(nrrd)); } return; } teem-1.11.0~svn6057/src/nrrd/formatVTK.c0000664000175000017500000003423712165631065017400 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" #define MAGIC1 "# vtk DataFile Version 1.0" #define MAGIC2 "# vtk DataFile Version 2.0" #define MAGIC3 "# vtk DataFile Version 3.0" int _nrrdFormatVTK_available(void) { return AIR_TRUE; } int _nrrdFormatVTK_nameLooksLike(const char *fname) { return airEndsWith(fname, NRRD_EXT_VTK); } int _nrrdFormatVTK_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { static const char me[]="_nrrdFormatVTK_fitsInto"; if (!( nrrd && encoding )) { biffMaybeAddf(useBiff, NRRD, "%s: got NULL nrrd (%p) or encoding (%p)", me, AIR_CVOIDP(nrrd), AIR_CVOIDP(encoding)); return AIR_FALSE; } if (!( nrrdEncodingRaw == encoding || nrrdEncodingAscii == encoding)) { biffMaybeAddf(useBiff, NRRD, "%s: encoding can only be %s or %s", me, nrrdEncodingRaw->name, nrrdEncodingAscii->name); return AIR_FALSE; } if (!( nrrdTypeUChar == nrrd->type || nrrdTypeChar == nrrd->type || nrrdTypeUShort == nrrd->type || nrrdTypeShort == nrrd->type || nrrdTypeUInt == nrrd->type || nrrdTypeInt == nrrd->type || nrrdTypeFloat == nrrd->type || nrrdTypeDouble == nrrd->type )) { biffMaybeAddf(useBiff, NRRD, "%s: type %s doesn't fit in VTK (as currently implemented)", me, airEnumStr(nrrdType, nrrd->type)); return AIR_FALSE; } if (!( 3 == nrrd->dim || (4 == nrrd->dim && 3 == nrrd->axis[0].size) || (4 == nrrd->dim && 9 == nrrd->axis[0].size) )) { biffMaybeAddf(useBiff, NRRD, "%s: nrrd didn't look like a volume of " "scalars, vectors, or matrices", me); return AIR_FALSE; } return AIR_TRUE; } int _nrrdFormatVTK_contentStartsLike(NrrdIoState *nio) { return (!strcmp(MAGIC1, nio->line) || !strcmp(MAGIC2, nio->line) || !strcmp(MAGIC3, nio->line)); } int _nrrdFormatVTK_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdReadVTK"; char *three[3]; int sx, sy, sz, ret, N; double xm=0.0, ym=0.0, zm=0.0, xs=1.0, ys=1.0, zs=1.0; airArray *mop; unsigned int llen; if (!_nrrdFormatVTK_contentStartsLike(nio)) { biffAddf(NRRD, "%s: this doesn't look like a %s file", me, nrrdFormatVTK->name); return 1; } #define GETLINE(what) \ do { \ ret = _nrrdOneLine(&llen, nio, file); \ } while (!ret && (1 == llen)); \ if (ret || !llen) { \ biffAddf(NRRD, "%s: couldn't get " #what " line", me); \ return 1; \ } /* read in content */ GETLINE(content); if (strcmp(NRRD_UNKNOWN, nio->line)) { if (!(nrrd->content = airStrdup(nio->line))) { biffAddf(NRRD, "%s: couldn't read or copy content string", me); return 1; } } GETLINE(encoding); airToUpper(nio->line); if (!strcmp("ASCII", nio->line)) { nio->encoding = nrrdEncodingAscii; } else if (!strcmp("BINARY", nio->line)) { nio->encoding = nrrdEncodingRaw; } else { biffAddf(NRRD, "%s: encoding \"%s\" wasn't \"ASCII\" or \"BINARY\"", me, nio->line); return 1; } GETLINE(DATASET); airToUpper(nio->line); if (!strstr(nio->line, "STRUCTURED_POINTS")) { biffAddf(NRRD, "%s: sorry, only STRUCTURED_POINTS data is nrrd-ready", me); return 1; } GETLINE(DIMENSIONS); airToUpper(nio->line); if (!strstr(nio->line, "DIMENSIONS") || 3 != sscanf(nio->line, "DIMENSIONS %d %d %d", &sx, &sy, &sz)) { biffAddf(NRRD, "%s: couldn't parse DIMENSIONS line (\"%s\")", me, nio->line); return 1; } GETLINE(next); airToUpper(nio->line); while (!strstr(nio->line, "POINT_DATA")) { if (strstr(nio->line, "ORIGIN")) { if (3 != sscanf(nio->line, "ORIGIN %lf %lf %lf", &xm, &ym, &zm)) { biffAddf(NRRD, "%s: couldn't parse ORIGIN line (\"%s\")", me, nio->line); return 1; } } else if (strstr(nio->line, "SPACING")) { if (3 != sscanf(nio->line, "SPACING %lf %lf %lf", &xs, &ys, &zs)) { biffAddf(NRRD, "%s: couldn't parse SPACING line (\"%s\")", me, nio->line); return 1; } } else if (strstr(nio->line, "ASPECT_RATIO")) { if (3 != sscanf(nio->line, "ASPECT_RATIO %lf %lf %lf", &xs, &ys, &zs)) { biffAddf(NRRD, "%s: couldn't parse ASPECT_RATIO line (\"%s\")", me, nio->line); return 1; } } GETLINE(next); airToUpper(nio->line); } if (1 != sscanf(nio->line, "POINT_DATA %d", &N)) { biffAddf(NRRD, "%s: couldn't parse POINT_DATA line (\"%s\")", me, nio->line); return 1; } if (N != sx*sy*sz) { biffAddf(NRRD, "%s: product of sizes (%d*%d*%d == %d) != # elements (%d)", me, sx, sy, sz, sx*sy*sz, N); return 1; } GETLINE(attribute declaration); mop = airMopNew(); if (3 != airParseStrS(three, nio->line, AIR_WHITESPACE, 3, AIR_FALSE)) { biffAddf(NRRD, "%s: didn't see three words in attribute declaration \"%s\"", me, nio->line); return 1; } airMopAdd(mop, three[0], airFree, airMopAlways); airMopAdd(mop, three[1], airFree, airMopAlways); airMopAdd(mop, three[2], airFree, airMopAlways); airToLower(three[2]); if (!strcmp(three[2], "bit")) { if (nrrdEncodingAscii == nio->encoding) { fprintf(stderr, "%s: WARNING: \"bit\"-type data will be read in as " "unsigned char\n", me); nrrd->type = nrrdTypeUChar; } else { biffAddf(NRRD, "%s: can't read in \"bit\"-type data as BINARY", me); return 1; } } else if (!strcmp(three[2], "unsigned_char")) { nrrd->type = nrrdTypeUChar; } else if (!strcmp(three[2], "char")) { nrrd->type = nrrdTypeChar; } else if (!strcmp(three[2], "unsigned_short")) { nrrd->type = nrrdTypeUShort; } else if (!strcmp(three[2], "short")) { nrrd->type = nrrdTypeShort; } else if (!strcmp(three[2], "unsigned_int")) { nrrd->type = nrrdTypeUInt; } else if (!strcmp(three[2], "int")) { nrrd->type = nrrdTypeInt; } else if (!strcmp(three[2], "float")) { nrrd->type = nrrdTypeFloat; } else if (!strcmp(three[2], "double")) { nrrd->type = nrrdTypeDouble; } else { /* "unsigned_long" and "long" fall in here- I don't know what the VTK people mean by these types, since always mean different things on 32-bit versus 64-bit architectures */ biffAddf(NRRD, "%s: type \"%s\" not recognized", me, three[2]); airMopError(mop); return 1; } airToUpper(three[0]); if (!strncmp("SCALARS", three[0], strlen("SCALARS"))) { GETLINE(LOOKUP_TABLE); airToUpper(nio->line); if (strcmp(nio->line, "LOOKUP_TABLE DEFAULT")) { biffAddf(NRRD, "%s: sorry, can only deal with default LOOKUP_TABLE", me); airMopError(mop); return 1; } nrrd->dim = 3; nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSize, AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, sz)); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSpacing, xs, ys, zs); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoMin, xm, ym, zm); } else if (!strncmp("VECTORS", three[0], strlen("VECTORS"))) { nrrd->dim = 4; nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSize, AIR_CAST(size_t, 3), AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, sz)); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSpacing, AIR_NAN, xs, ys, zs); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoMin, AIR_NAN, xm, ym, zm); nrrd->axis[0].kind = nrrdKind3Vector; } else if (!strncmp("TENSORS", three[0], strlen("TENSORS"))) { nrrd->dim = 4; nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSize, AIR_CAST(size_t, 9), AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, sz)); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSpacing, AIR_NAN, xs, ys, zs); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoMin, AIR_NAN, xm, ym, zm); nrrd->axis[0].kind = nrrdKind3DMatrix; } else { biffAddf(NRRD, "%s: sorry, can only deal with SCALARS, VECTORS, and TENSORS " "currently, so couldn't parse attribute declaration \"%s\"", me, nio->line); airMopError(mop); return 1; } if (!nio->skipData) { if (_nrrdCalloc(nrrd, nio, file)) { biffAddf(NRRD, "%s: couldn't allocate memory for data", me); return 1; } if (nio->encoding->read(file, nrrd->data, nrrdElementNumber(nrrd), nrrd, nio)) { biffAddf(NRRD, "%s:", me); return 1; } if (1 < nrrdElementSize(nrrd) && nio->encoding->endianMatters && airMyEndian() != airEndianBig) { /* encoding exposes endianness, and its big, but we aren't */ nrrdSwapEndian(nrrd); } } else { nrrd->data = NULL; } airMopOkay(mop); return 0; } /* this strongly assumes that nrrdFitsInFormat() was true */ int _nrrdFormatVTK_write(FILE *file, const Nrrd *_nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatVTK_write"; int i, sx, sy, sz, sax; double xs, ys, zs, xm, ym, zm; char type[AIR_STRLEN_MED], name[AIR_STRLEN_SMALL]; Nrrd *nrrd; airArray *mop; /* HEY: should this copy be done more conservatively */ mop = airMopNew(); airMopAdd(mop, nrrd=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nrrd, _nrrd)) { biffAddf(NRRD, "%s: couldn't make private copy", me); airMopError(mop); return 1; } if (!( 3 == nrrd->dim || (4 == nrrd->dim && (3 == nrrd->axis[0].size || 9 == nrrd->axis[0].size)) )) { biffAddf(NRRD, "%s: doesn't seem to be scalar, vector, or matrix", me); airMopError(mop); return 1; } sax = nrrd->dim - 3; xs = nrrd->axis[sax+0].spacing; ys = nrrd->axis[sax+1].spacing; zs = nrrd->axis[sax+2].spacing; if (!( AIR_EXISTS(xs) && AIR_EXISTS(ys) && AIR_EXISTS(zs) )) { xs = ys = zs = 1.0; } xm = nrrd->axis[sax+0].min; ym = nrrd->axis[sax+1].min; zm = nrrd->axis[sax+2].min; if (!( AIR_EXISTS(xm) && AIR_EXISTS(ym) && AIR_EXISTS(zm) )) { xm = ym = zm = 0.0; } sx = AIR_CAST(int, nrrd->axis[sax+0].size); sy = AIR_CAST(int, nrrd->axis[sax+1].size); sz = AIR_CAST(int, nrrd->axis[sax+2].size); switch(nrrd->type) { case nrrdTypeUChar: strcpy(type, "unsigned_char"); break; case nrrdTypeChar: strcpy(type, "char"); break; case nrrdTypeUShort: strcpy(type, "unsigned_short"); break; case nrrdTypeShort: strcpy(type, "short"); break; case nrrdTypeUInt: strcpy(type, "unsigned_int"); break; case nrrdTypeInt: strcpy(type, "int"); break; case nrrdTypeFloat: strcpy(type, "float"); break; case nrrdTypeDouble: strcpy(type, "double"); break; default: biffAddf(NRRD, "%s: can't put %s-type nrrd into VTK", me, airEnumStr(nrrdType, nrrd->type)); airMopError(mop); return 1; } fprintf(file, "%s\n", MAGIC3); /* there is a file-format-imposed limit on the length of the "content" */ if (nrrd->content) { /* when the "250" below was previously "255", vtk didn't deal */ for (i=0; i<=250 && nrrd->content[i]; i++) { fputc(nrrd->content[i], file); } fputc('\n', file); } else { fprintf(file, NRRD_UNKNOWN "\n"); } if (nrrdEncodingRaw == nio->encoding) { fprintf(file, "BINARY\n"); } else { fprintf(file, "ASCII\n"); } fprintf(file, "DATASET STRUCTURED_POINTS\n"); fprintf(file, "DIMENSIONS %d %d %d\n", sx, sy, sz); fprintf(file, "ORIGIN %g %g %g\n", xm, ym, zm); fprintf(file, "SPACING %g %g %g\n", xs, ys, zs); fprintf(file, "POINT_DATA %d\n", sx*sy*sz); airSrandMT(AIR_CAST(unsigned int, airTime())); sprintf(name, "nrrd%05d", airRandInt(100000)); if (3 == nrrd->dim) { fprintf(file, "SCALARS %s %s\n", name, type); fprintf(file, "LOOKUP_TABLE default\n"); } else { /* 4 == nrrd->dim */ if (3 == nrrd->axis[0].size) { fprintf(file, "VECTORS %s %s\n", name, type); } else { fprintf(file, "TENSORS %s %s\n", name, type); } } if (1 < nrrdElementSize(nrrd) && nio->encoding->endianMatters && airMyEndian() != airEndianBig) { /* encoding exposes endianness, and we're not big, as req.d by VTK */ nrrdSwapEndian(nrrd); } if (nio->encoding->write(file, nrrd->data, nrrdElementNumber(nrrd), nrrd, nio)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } const NrrdFormat _nrrdFormatVTK = { "VTK", AIR_FALSE, /* isImage */ AIR_TRUE, /* readable */ AIR_FALSE, /* usesDIO */ _nrrdFormatVTK_available, _nrrdFormatVTK_nameLooksLike, _nrrdFormatVTK_fitsInto, _nrrdFormatVTK_contentStartsLike, _nrrdFormatVTK_read, _nrrdFormatVTK_write }; const NrrdFormat *const nrrdFormatVTK = &_nrrdFormatVTK; teem-1.11.0~svn6057/src/nrrd/arraysNrrd.c0000664000175000017500000002446712165631065017656 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* learned: /usr/bin/c++ on mac (at least) won't actually put a const int blah[] array in an object file if it hasn't been declared as "extern" */ const char nrrdTypePrintfStr[NRRD_TYPE_MAX+1][AIR_STRLEN_SMALL] = { "%*d", /* nrrdTypeUnknown: what else? the effect will be "skip" for sscanf, and "minimum precision" for printf */ "%d", /* nrrdTypeChar: char */ "%u", /* nrrdTypeUChar: unsigned char */ "%hd", /* nrrdTypeShort: short */ "%hu", /* nrrdTypeUShort: unsigned short */ "%d", /* nrrdTypeInt: int */ "%u", /* nrrdTypeUInt: unsigned int */ AIR_LLONG_FMT, /* nrrdTypeLLong: long long */ AIR_ULLONG_FMT, /* nrrdTypeULLong: unsigned long long */ "%f", /* nrrdTypeFloat: float */ "%lf", /* nrrdTypeDouble: double */ "%*d" /* nrrdTypeBlock: what else? */ }; /* ** the setting of NRRD_TYPE_BIGGEST has to be in accordance with this */ const size_t nrrdTypeSize[NRRD_TYPE_MAX+1] = { 0, /* nrrdTypeUnknown: unknown */ 1, /* nrrdTypeChar: char */ 1, /* nrrdTypeUChar: unsigned char */ 2, /* nrrdTypeShort: short */ 2, /* nrrdTypeUShort: unsigned short */ 4, /* nrrdTypeInt: int */ 4, /* nrrdTypeUInt: unsigned int */ 8, /* nrrdTypeLLong: long long */ 8, /* nrrdTypeULLong: unsigned long long */ 4, /* nrrdTypeFloat: float */ 8, /* nrrdTypeDouble: double */ 0 /* nrrdTypeBlock: effectively unknown; user has to set explicitly */ }; const int nrrdTypeIsIntegral[NRRD_TYPE_MAX+1] = { 0, /* nrrdTypeUnknown: unknown */ 1, /* nrrdTypeChar: char */ 1, /* nrrdTypeUChar: unsigned char */ 1, /* nrrdTypeShort: short */ 1, /* nrrdTypeUShort: unsigned short */ 1, /* nrrdTypeInt: int */ 1, /* nrrdTypeUInt: unsigned int */ 1, /* nrrdTypeLLong: long long */ 1, /* nrrdTypeULLong: unsigned long long */ 0, /* nrrdTypeFloat: float */ 0, /* nrrdTypeDouble: double */ 1 /* nrrdTypeBlock: for some reason we pretend that blocks are integers */ }; const int nrrdTypeIsUnsigned[NRRD_TYPE_MAX+1] = { 0, /* nrrdTypeUnknown: unknown */ 0, /* nrrdTypeChar: char */ 1, /* nrrdTypeUChar: unsigned char */ 0, /* nrrdTypeShort: short */ 1, /* nrrdTypeUShort: unsigned short */ 0, /* nrrdTypeInt: int */ 1, /* nrrdTypeUInt: unsigned int */ 0, /* nrrdTypeLLong: long long */ 1, /* nrrdTypeULLong: unsigned long long */ 0, /* nrrdTypeFloat: float */ 0, /* nrrdTypeDouble: double */ 0 /* nrrdTypeBlock: for some reason we pretend that blocks are signed */ }; /* ******** nrrdTypeMin[] ******** nrrdTypeMax[] ** ** only intended for small (<= 32 bits) integral types, ** so that we know how to "unquantize" integral values. ** A 64-bit double can correctly store the 32-bit integral ** mins and maxs, but gets the last few places wrong in the ** 64-bit mins and max. */ const double nrrdTypeMin[NRRD_TYPE_MAX+1] = { 0, /* nrrdTypeUnknown: unknown */ SCHAR_MIN, /* nrrdTypeChar: char */ 0, /* nrrdTypeUChar: unsigned char */ SHRT_MIN, /* nrrdTypeShort: short */ 0, /* nrrdTypeUShort: unsigned short */ INT_MIN, /* nrrdTypeInt: int */ 0, /* nrrdTypeUInt: unsigned int */ (double)NRRD_LLONG_MIN, /* nrrdTypeLLong: long long */ 0, /* nrrdTypeULLong: unsigned long long */ 0, /* nrrdTypeFloat: float */ 0, /* nrrdTypeDouble: double */ 0 /* nrrdTypeBlock: punt */ }, nrrdTypeMax[NRRD_TYPE_MAX+1] = { 0, /* nrrdTypeUnknown: unknown */ SCHAR_MAX, /* nrrdTypeChar: char */ UCHAR_MAX, /* nrrdTypeUChar: unsigned char */ SHRT_MAX, /* nrrdTypeShort: short */ USHRT_MAX, /* nrrdTypeUShort: unsigned short */ INT_MAX, /* nrrdTypeInt: int */ UINT_MAX, /* nrrdTypeUInt: unsigned int */ (double)NRRD_LLONG_MAX, /* nrrdTypeLLong: long long */ (double)NRRD_ULLONG_MAX, /* nrrdTypeULLong: unsigned long long */ 0, /* nrrdTypeFloat: float */ 0, /* nrrdTypeDouble: double */ 0 /* nrrdTypeBlock: punt */ }; /* ** _nrrdFieldValidInImage[] ** ** these fields are valid embedded in PNM and PNG comments ** This does NOT include the fields who's values are constrained ** by the image format (and in the case of PNM, magic) itself. */ const int _nrrdFieldValidInImage[NRRD_FIELD_MAX+1] = { 0, /* nrrdField_unknown */ 1, /* nrrdField_comment */ 1, /* nrrdField_content */ 0, /* nrrdField_number */ 0, /* nrrdField_type */ 0, /* nrrdField_block_size */ 0, /* nrrdField_dimension */ 1, /* nrrdField_space */ 1, /* nrrdField_space_dimension */ 0, /* nrrdField_sizes */ 1, /* nrrdField_spacings */ 1, /* nrrdField_thicknesses */ 1, /* nrrdField_axis_mins */ 1, /* nrrdField_axis_maxs */ 1, /* nrrdField_space_directions */ 1, /* nrrdField_centers */ 1, /* nrrdField_kinds */ 1, /* nrrdField_labels */ 1, /* nrrdField_units */ 0, /* nrrdField_min */ 0, /* nrrdField_max */ 1, /* nrrdField_old_min */ 1, /* nrrdField_old_max */ 0, /* nrrdField_endian */ 0, /* nrrdField_encoding */ 0, /* nrrdField_line_skip */ 0, /* nrrdField_byte_skip */ 1, /* nrrdField_keyvalue */ 1, /* nrrdField_sample_units */ 1, /* nrrdField_space_units */ 1, /* nrrdField_space_origin */ 1, /* nrrdField_measurement_frame */ 0 /* nrrdField_data_file */ }; /* ** _nrrdFieldOnePerAxis ** ** whether or not you need one value per axis, like labels and spacings */ const int _nrrdFieldOnePerAxis[NRRD_FIELD_MAX+1] = { 0, /* nrrdField_unknown */ 0, /* nrrdField_comment */ 0, /* nrrdField_content */ 0, /* nrrdField_number */ 0, /* nrrdField_type */ 0, /* nrrdField_block_size */ 0, /* nrrdField_dimension */ 0, /* nrrdField_space */ 0, /* nrrdField_space_dimension */ 1, /* nrrdField_sizes */ 1, /* nrrdField_spacings */ 1, /* nrrdField_thicknesses */ 1, /* nrrdField_axis_mins */ 1, /* nrrdField_axis_maxs */ 1, /* nrrdField_space_directions */ 1, /* nrrdField_centers */ 1, /* nrrdField_kinds */ 1, /* nrrdField_labels */ 1, /* nrrdField_units */ 0, /* nrrdField_min */ 0, /* nrrdField_max */ 0, /* nrrdField_old_min */ 0, /* nrrdField_old_max */ 0, /* nrrdField_endian */ 0, /* nrrdField_encoding */ 0, /* nrrdField_line_skip */ 0, /* nrrdField_byte_skip */ 0, /* nrrdField_keyvalue */ 0, /* nrrdField_sample_units */ 0, /* nrrdField_space_units */ 0, /* nrrdField_space_origin */ 0, /* nrrdField_measurement_frame */ 0 /* nrrdField_data_file */ }; /* ** _nrrdFieldValidInText[] ** ** these fields are valid embedded in plain text comments ** This does NOT include the fields who's values are constrained ** the plain text format itself. */ const int _nrrdFieldValidInText[NRRD_FIELD_MAX+1] = { 0, /* nrrdField_unknown */ 1, /* nrrdField_comment */ 1, /* nrrdField_content */ 0, /* nrrdField_number */ 0, /* nrrdField_type: decided AGAINST plain text holding general type (but I forget why ...) */ 0, /* nrrdField_block_size */ 1, /* nrrdField_dimension: but can only be 1 or 2 */ 0, /* nrrdField_space */ 0, /* nrrdField_space_dimension */ 0, /* nrrdField_sizes */ 1, /* nrrdField_spacings */ 1, /* nrrdField_thicknesses */ 1, /* nrrdField_axis_mins */ 1, /* nrrdField_axis_maxs */ 1, /* nrrdField_space_directions */ 1, /* nrrdField_centers */ 1, /* nrrdField_kinds */ 1, /* nrrdField_labels */ 1, /* nrrdField_units */ 0, /* nrrdField_min */ 0, /* nrrdField_max */ 1, /* nrrdField_old_min */ 1, /* nrrdField_old_max */ 0, /* nrrdField_endian */ 0, /* nrrdField_encoding */ 0, /* nrrdField_line_skip */ 0, /* nrrdField_byte_skip */ 1, /* nrrdField_keyvalue */ 0, /* nrrdField_sample_units */ 0, /* nrrdField_space_units */ 0, /* nrrdField_space_origin */ 0, /* nrrdField_measurement_frame */ 0 /* nrrdField_data_file */ }; /* ** _nrrdFieldRequired[] ** ** regardless of whether its a nrrd, PNM, or plain text, these things ** need to be conveyed, either explicity or implicitly */ const int _nrrdFieldRequired[NRRD_FIELD_MAX+1] = { 0, /* "Ernesto \"Che\" Guevara" */ 0, /* "#" */ 0, /* nrrdField_content */ 0, /* nrrdField_number */ 1, /* nrrdField_type */ 0, /* nrrdField_block size */ 1, /* nrrdField_dimension */ 0, /* nrrdField_space */ 0, /* nrrdField_space_dimension */ 1, /* nrrdField_sizes */ 0, /* nrrdField_spacings */ 0, /* nrrdField_thicknesses */ 0, /* nrrdField_axis mins */ 0, /* nrrdField_axis maxs */ 0, /* nrrdField_space_directions */ 0, /* nrrdField_centers */ 0, /* nrrdField_kinds */ 0, /* nrrdField_labels */ 0, /* nrrdField_units */ 0, /* nrrdField_min */ 0, /* nrrdField_max */ 0, /* nrrdField_old min */ 0, /* nrrdField_old max */ 0, /* nrrdField_endian */ 1, /* nrrdField_encoding */ 0, /* nrrdField_line_skip */ 0, /* nrrdField_byte_skip */ 0, /* nrrdField_keyvalue */ 0, /* nrrdField_sample_units */ 0, /* nrrdField_space_units */ 0, /* nrrdField_space_origin */ 0, /* nrrdField_measurement_frame */ 0 /* nrrdField_data file */ }; teem-1.11.0~svn6057/src/nrrd/formatPNM.c0000664000175000017500000002447412165631065017370 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" #define MAGIC_P6 "P6" #define MAGIC_P5 "P5" #define MAGIC_P3 "P3" #define MAGIC_P2 "P2" static int _nrrdFormatPNM_available(void) { return AIR_TRUE; } static int _nrrdFormatPNM_nameLooksLike(const char *filename) { return (airEndsWith(filename, NRRD_EXT_PGM) || airEndsWith(filename, NRRD_EXT_PPM)); } static int _nrrdFormatPNM_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { static const char me[]="_nrrdFormatPNM_fitsInto"; int ret; if (!( nrrd && encoding )) { biffMaybeAddf(useBiff, NRRD, "%s: got NULL nrrd (%p) or encoding (%p)", me, AIR_CVOIDP(nrrd), AIR_CVOIDP(encoding)); return AIR_FALSE; } if (nrrdTypeUChar != nrrd->type) { biffMaybeAddf(useBiff, NRRD, "%s: type must be %s (not %s)", me, airEnumStr(nrrdType, nrrdTypeUChar), airEnumStr(nrrdType, nrrd->type)); return AIR_FALSE; } if (!( nrrdEncodingRaw == encoding || nrrdEncodingAscii == encoding)) { biffMaybeAddf(useBiff, NRRD, "%s: encoding can only be %s or %s", me, nrrdEncodingRaw->name, nrrdEncodingAscii->name); return AIR_FALSE; } if (2 == nrrd->dim) { /* its a gray-scale image */ ret = 2; } else if (3 == nrrd->dim) { if (1 == nrrd->axis[0].size) { /* its a faux-3D image, really grayscale */ ret = 2; } else if (3 == nrrd->axis[0].size) { /* its a real color image */ ret = 3; } else { /* else its no good */ char stmp[AIR_STRLEN_SMALL]; biffMaybeAddf(useBiff, NRRD, "%s: dim is 3, but 1st axis size is %s, not 1 or 3", me, airSprintSize_t(stmp, nrrd->axis[0].size)); return AIR_FALSE; } } else { biffMaybeAddf(useBiff, NRRD, "%s: dimension is %d, not 2 or 3", me, nrrd->dim); return AIR_FALSE; } return ret; } int _nrrdFormatPNM_contentStartsLike(NrrdIoState *nio) { return (!strcmp(MAGIC_P6, nio->line) || !strcmp(MAGIC_P5, nio->line) || !strcmp(MAGIC_P3, nio->line) || !strcmp(MAGIC_P2, nio->line)); } static int _nrrdFormatPNM_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatPNM_read"; const char *fs; char *perr; int color, got, want, ret, val[5], sx, sy, max, magic; unsigned int i, llen; if (!_nrrdFormatPNM_contentStartsLike(nio)) { biffAddf(NRRD, "%s: this doesn't look like a %s file", me, nrrdFormatPNM->name); return 1; } nrrd->type = nrrdTypeUChar; if (!strcmp(MAGIC_P6, nio->line)) { magic = 6; } else if (!strcmp(MAGIC_P5, nio->line)) { magic = 5; } else if (!strcmp(MAGIC_P3, nio->line)) { magic = 3; } else if (!strcmp(MAGIC_P2, nio->line)) { magic = 2; } else { fprintf(stderr, "%s: PANIC: magic \"%s\" not handled\n", me, nio->line); biffAddf(NRRD, "%s: PANIC: magic \"%s\" not handled\n", me, nio->line); return 1; } switch(magic) { case 2: color = AIR_FALSE; nio->encoding = nrrdEncodingAscii; nrrd->dim = 2; break; case 3: color = AIR_TRUE; nio->encoding = nrrdEncodingAscii; nrrd->dim = 3; break; case 5: color = AIR_FALSE; nio->encoding = nrrdEncodingRaw; nrrd->dim = 2; break; case 6: color = AIR_TRUE; nio->encoding = nrrdEncodingRaw; nrrd->dim = 3; break; default: biffAddf(NRRD, "%s: sorry, PNM magic %d not implemented", me, magic); return 1; break; } val[0] = val[1] = val[2] = 0; got = 0; want = 3; while (got < want) { nio->pos = 0; if (_nrrdOneLine(&llen, nio, file)) { biffAddf(NRRD, "%s: failed to get line from PNM header", me); return 1; } if (!(0 < llen)) { biffAddf(NRRD, "%s: hit EOF in header with %d of %d ints parsed", me, got, want); return 1; } if ('#' == nio->line[0]) { if (strncmp(nio->line, NRRD_PNM_COMMENT, strlen(NRRD_PNM_COMMENT))) { /* this is a generic comment */ ret = 0; goto plain; } /* else this PNM comment is trying to tell us something */ nio->pos = AIR_CAST(int, strlen(NRRD_PNM_COMMENT)); nio->pos += AIR_CAST(int, strspn(nio->line + nio->pos, _nrrdFieldSep)); ret = _nrrdReadNrrdParseField(nio, AIR_FALSE); if (!ret) { if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: unparsable field \"%s\" --> plain comment)\n", me, nio->line); } goto plain; } if (nrrdField_comment == ret) { ret = 0; goto plain; } fs = airEnumStr(nrrdField, ret); if (!_nrrdFieldValidInImage[ret]) { if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: field \"%s\" (not allowed in PNM) " "--> plain comment)\n", me, fs); } ret = 0; goto plain; } if (!nio->seen[ret] && nrrdFieldInfoParse[ret](file, nrrd, nio, AIR_TRUE)) { perr = biffGetDone(NRRD); if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: unparsable info for field \"%s\" " "--> plain comment:\n%s)\n", me, fs, perr); } free(perr); ret = 0; goto plain; } nio->seen[ret] = AIR_TRUE; plain: if (!ret) { if (nrrdCommentAdd(nrrd, nio->line+1)) { biffAddf(NRRD, "%s: couldn't add comment", me); return 1; } } continue; } if (3 == sscanf(nio->line, "%d%d%d", val+got+0, val+got+1, val+got+2)) { got += 3; continue; } if (2 == sscanf(nio->line, "%d%d", val+got+0, val+got+1)) { got += 2; continue; } if (1 == sscanf(nio->line, "%d", val+got+0)) { got += 1; continue; } /* else, we couldn't parse ANY numbers on this line, which is okay as long as the line contains nothing but white space */ for (i=0; (i<=strlen(nio->line)-1 && isspace(AIR_INT(nio->line[i]))); i++) ; if (i != strlen(nio->line)) { biffAddf(NRRD, "%s: \"%s\" has no integers but isn't just whitespace", me, nio->line); return 1; } } /* this assumes 3 == want */ sx = val[0]; sy = val[1]; max = val[2]; if (!(sx > 0 && sy > 0 && max > 0)) { biffAddf(NRRD, "%s: sx,sy,max of %d,%d,%d has problem", me, sx, sy, max); return 1; } if (255 != max) { biffAddf(NRRD, "%s: sorry, can only deal with max value 255 (not %d)", me, max); return 1; } /* we know what we need in order to set nrrd fields and read data */ if (color) { nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSize, AIR_CAST(size_t, 3), AIR_CAST(size_t, sx), AIR_CAST(size_t, sy)); } else { nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSize, AIR_CAST(size_t, sx), AIR_CAST(size_t, sy)); } if (!nio->skipData) { if (_nrrdCalloc(nrrd, nio, file)) { biffAddf(NRRD, "%s: couldn't allocate memory for data", me); return 1; } if (nio->encoding->read(file, nrrd->data, nrrdElementNumber(nrrd), nrrd, nio)) { biffAddf(NRRD, "%s:", me); return 1; } } else { nrrd->data = NULL; } return 0; } static int _nrrdFormatPNM_write(FILE *file, const Nrrd *_nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatPNM_write"; int color, sx, sy, magic, fi; unsigned int ci; Nrrd *nrrd; airArray *mop; mop = airMopNew(); airMopAdd(mop, nrrd = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nrrd, _nrrd)) { biffAddf(NRRD, "%s: couldn't make private copy", me); airMopError(mop); return 1; } if (3 == nrrd->dim && 1 == nrrd->axis[0].size) { if (nrrdAxesDelete(nrrd, nrrd, 0)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } } color = (3 == nrrd->dim); if (!color) { magic = (nrrdEncodingAscii == nio->encoding ? 2 : 5); sx = AIR_CAST(int, nrrd->axis[0].size); sy = AIR_CAST(int, nrrd->axis[1].size); } else { magic = (nrrdEncodingAscii == nio->encoding ? 3 : 6); sx = AIR_CAST(int, nrrd->axis[1].size); sy = AIR_CAST(int, nrrd->axis[2].size); } fprintf(file, "P%d\n", magic); fprintf(file, "%d %d\n", sx, sy); for (fi=nrrdField_unknown+1; ficmtArr->len; ci++) { fprintf(file, "# %s\n", nrrd->cmt[ci]); } fprintf(file, "255\n"); if (!nio->skipData) { if (nio->encoding->write(file, nrrd->data, nrrdElementNumber(nrrd), nrrd, nio)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } } airMopError(mop); return 0; } const NrrdFormat _nrrdFormatPNM = { "PNM", AIR_TRUE, /* isImage */ AIR_TRUE, /* readable */ AIR_FALSE, /* usesDIO */ _nrrdFormatPNM_available, _nrrdFormatPNM_nameLooksLike, _nrrdFormatPNM_fitsInto, _nrrdFormatPNM_contentStartsLike, _nrrdFormatPNM_read, _nrrdFormatPNM_write }; const NrrdFormat *const nrrdFormatPNM = &_nrrdFormatPNM; teem-1.11.0~svn6057/src/nrrd/formatEPS.c0000664000175000017500000001572512165631065017364 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" static int _nrrdFormatEPS_available(void) { /* but only for writing ... */ return AIR_TRUE; } static int _nrrdFormatEPS_nameLooksLike(const char *filename) { return airEndsWith(filename, NRRD_EXT_EPS); } static int _nrrdFormatEPS_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { static const char me[]="_nrrdFormatEPS_fitsInto"; int ret; AIR_UNUSED(encoding); /* encoding information is ignored- its always going to be hex */ if (!nrrd) { biffMaybeAddf(useBiff, NRRD, "%s: got NULL nrrd (%p)", me, AIR_CVOIDP(nrrd)); return AIR_FALSE; } if (nrrdTypeUChar != nrrd->type) { biffMaybeAddf(useBiff, NRRD, "%s: type must be %s (not %s)", me, airEnumStr(nrrdType, nrrdTypeUChar), airEnumStr(nrrdType, nrrd->type)); return AIR_FALSE; } if (2 == nrrd->dim) { /* its a gray-scale image */ ret = 2; } else if (3 == nrrd->dim) { if (1 == nrrd->axis[0].size) { /* its a faux-3D image, really grayscale */ ret = 2; } else if (3 == nrrd->axis[0].size) { /* its a real RGB color image */ ret = 3; } else if (4 == nrrd->axis[0].size) { /* its a real CMYK (our best guess) color image */ ret = 3; } else { /* else its no good */ char stmp[AIR_STRLEN_SMALL]; biffMaybeAddf(useBiff, NRRD, "%s: dim is 3, but 1st axis size is %s, not 1, 3, or 4", me, airSprintSize_t(stmp, nrrd->axis[0].size)); return AIR_FALSE; } } else { biffMaybeAddf(useBiff, NRRD, "%s: dimension is %d, not 2 or 3", me, nrrd->dim); return AIR_FALSE; } return ret; } static int _nrrdFormatEPS_contentStartsLike(NrrdIoState *nio) { AIR_UNUSED(nio); /* this is a write-only format */ return AIR_FALSE; } static int _nrrdFormatEPS_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatEPS_read"; AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: sorry, this is a write-only format", me); return 1; } static int _nrrdFormatEPS_write(FILE *file, const Nrrd *_nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatEPS_write"; int color, cmyk, sx, sy; Nrrd *nrrd; double aspect, minX, minY, maxX, maxY, scale; airArray *mop; mop = airMopNew(); airMopAdd(mop, nrrd = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nrrd, _nrrd)) { biffAddf(NRRD, "%s: couldn't make private copy", me); airMopError(mop); return 1; } if (3 == nrrd->dim && 1 == nrrd->axis[0].size) { if (nrrdAxesDelete(nrrd, nrrd, 0)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } } color = (3 == nrrd->dim) && (3 == nrrd->axis[0].size || 4 == nrrd->axis[0].size); cmyk = color && 4 == nrrd->axis[0].size; if (color) { sx = AIR_CAST(int, nrrd->axis[1].size); sy = AIR_CAST(int, nrrd->axis[2].size); } else { sx = AIR_CAST(int, nrrd->axis[0].size); sy = AIR_CAST(int, nrrd->axis[1].size); } aspect = AIR_CAST(double, sx)/sy; if (aspect > 7.5/10) { /* image has a wider aspect ratio than safely printable page area */ minX = 0.5; maxX = 8.0; minY = 5.50 - 7.5*sy/sx/2; maxY = 5.50 + 7.5*sy/sx/2; scale = 7.5/sx; } else { /* image is taller ... */ minX = 4.25 - 10.0*sx/sy/2; maxX = 4.25 + 10.0*sx/sy/2; minY = 0.5; maxY = 10.5; scale = 10.0/sy; } minX *= 72; minY *= 72; maxX *= 72; maxY *= 72; scale *= 72; fprintf(file, "%%!PS-Adobe-3.0 EPSF-3.0\n"); fprintf(file, "%%%%Creator: Nrrd Utilities From the " "Great Nation of Deseret\n"); fprintf(file, "%%%%Title: %s\n", nrrd->content ? nrrd->content : "A lovely little image"); fprintf(file, "%%%%Pages: 1\n"); fprintf(file, "%%%%BoundingBox: %d %d %d %d\n", (int)floor(minX), (int)floor(minY), (int)ceil(maxX), (int)ceil(maxY)); fprintf(file, "%%%%HiResBoundingBox: %g %g %g %g\n", minX, minY, maxX, maxY); fprintf(file, "%%%%EndComments\n"); fprintf(file, "%%%%BeginProlog\n"); fprintf(file, "%% linestr creates an empty string to hold " "one scanline\n"); fprintf(file, "/linestr %d string def\n", sx*(color ? (cmyk ? 4 : 3) : 1)); fprintf(file, "%%%%EndProlog\n"); fprintf(file, "%%%%Page: 1 1\n"); fprintf(file, "gsave\n"); fprintf(file, "%g %g moveto\n", minX, minY); fprintf(file, "%g %g lineto\n", maxX, minY); fprintf(file, "%g %g lineto\n", maxX, maxY); fprintf(file, "%g %g lineto\n", minX, maxY); fprintf(file, "closepath\n"); fprintf(file, "clip\n"); fprintf(file, "gsave newpath\n"); fprintf(file, "%g %g translate\n", minX, minY); fprintf(file, "%g %g scale\n", sx*scale, sy*scale); fprintf(file, "%d %d 8\n", sx, sy); fprintf(file, "[%d 0 0 -%d 0 %d]\n", sx, sy, sy); if (color) { fprintf(file, "{currentfile linestr readhexstring pop} " "false %d colorimage\n", cmyk ? 4 : 3); } else { fprintf(file, "{currentfile linestr readhexstring pop} image\n"); } nrrdEncodingHex->write(file, nrrd->data, nrrdElementNumber(nrrd), nrrd, nio); fprintf(file, "\n"); fprintf(file, "grestore\n"); fprintf(file, "grestore\n"); airMopError(mop); return 0; } const NrrdFormat _nrrdFormatEPS = { "EPS", AIR_FALSE, /* isImage */ AIR_FALSE, /* readable */ AIR_FALSE, /* usesDIO */ _nrrdFormatEPS_available, _nrrdFormatEPS_nameLooksLike, _nrrdFormatEPS_fitsInto, _nrrdFormatEPS_contentStartsLike, _nrrdFormatEPS_read, _nrrdFormatEPS_write }; const NrrdFormat *const nrrdFormatEPS = &_nrrdFormatEPS; teem-1.11.0~svn6057/src/nrrd/subset.c0000664000175000017500000006424512165631065017032 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ******** nrrdSlice() ** ** slices a nrrd along a given axis, at a given position. ** ** This is a newer version of the procedure, which is simpler, faster, ** and requires less memory overhead than the first one. It is based ** on the observation that any slice is a periodic square-wave pattern ** in the original data (viewed as a one- dimensional array). The ** characteristics of that periodic pattern are how far from the ** beginning it starts (offset), the length of the "on" part (length), ** the period (period), and the number of periods (numper). */ int nrrdSlice(Nrrd *nout, const Nrrd *cnin, unsigned int saxi, size_t pos) { static const char me[]="nrrdSlice", func[]="slice"; size_t I, rowLen, /* length of segment */ colStep, /* distance between start of each segment */ colLen, /* number of periods */ szOut[NRRD_DIM_MAX]; unsigned int ai, outdim; int map[NRRD_DIM_MAX]; const char *src; char *dest, stmp[2][AIR_STRLEN_SMALL]; airArray *mop; Nrrd *nin; if (!(cnin && nout)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == cnin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (1 == cnin->dim) { if (0 != saxi) { biffAddf(NRRD, "%s: slice axis must be 0, not %u, for 1-D array", me, saxi); return 1; } } else { if (!( saxi < cnin->dim )) { biffAddf(NRRD, "%s: slice axis %d out of bounds (0 to %d)", me, saxi, cnin->dim-1); return 1; } } if (!( pos < cnin->axis[saxi].size )) { biffAddf(NRRD, "%s: position %s out of bounds (0 to %s)", me, airSprintSize_t(stmp[0], pos), airSprintSize_t(stmp[1], cnin->axis[saxi].size-1)); return 1; } /* this shouldn't actually be necessary .. */ if (!nrrdElementSize(cnin)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); return 1; } /* HEY: copy and paste from measure.c/nrrdProject */ mop = airMopNew(); if (1 == cnin->dim) { /* There are more efficient ways of dealing with this case; this way is easy to implement because it leaves most of the established code below only superficially changed; uniformly replacing nin with (nin ? nin : cnin), even if pointlessly so; this expression that can't be assigned to a new variable because of the difference in const. */ nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); if (nrrdAxesInsert(nin, cnin, 1)) { biffAddf(NRRD, "%s: trouble inserting axis on 1-D array", me); airMopError(mop); return 1; } } else { nin = NULL; } /* set up control variables */ rowLen = colLen = 1; for (ai=0; ai<(nin ? nin : cnin)->dim; ai++) { if (ai < saxi) { rowLen *= (nin ? nin : cnin)->axis[ai].size; } else if (ai > saxi) { colLen *= (nin ? nin : cnin)->axis[ai].size; } } rowLen *= nrrdElementSize(nin ? nin : cnin); colStep = rowLen*(nin ? nin : cnin)->axis[saxi].size; outdim = (nin ? nin : cnin)->dim-1; for (ai=0; ai= saxi); szOut[ai] = (nin ? nin : cnin)->axis[map[ai]].size; } nout->blockSize = (nin ? nin : cnin)->blockSize; if (nrrdMaybeAlloc_nva(nout, (nin ? nin : cnin)->type, outdim, szOut)) { biffAddf(NRRD, "%s: failed to create slice", me); airMopError(mop); return 1; } /* the skinny */ src = AIR_CAST(const char *, (nin ? nin : cnin)->data); dest = AIR_CAST(char *, nout->data); src += rowLen*pos; for (I=0; Iaxis[saxi].spaceDirection[0])) { nrrdSpaceVecScaleAdd2(nout->spaceOrigin, 1.0, (nin ? nin : cnin)->spaceOrigin, AIR_CAST(double, pos), (nin ? nin : cnin)->axis[saxi].spaceDirection); } else { nrrdSpaceVecCopy(nout->spaceOrigin, (nin ? nin : cnin)->spaceOrigin); } airMopOkay(mop); return 0; } /* ******** nrrdCrop() ** ** select some sub-volume inside a given nrrd, producing an output ** nrrd with the same dimensions, but with equal or smaller sizes ** along each axis. */ int nrrdCrop(Nrrd *nout, const Nrrd *nin, size_t *min, size_t *max) { static const char me[]="nrrdCrop", func[] = "crop"; char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_SMALL]; unsigned int ai; size_t I, lineSize, /* #bytes in one scanline to be copied */ typeSize, /* size of data type */ cIn[NRRD_DIM_MAX], /* coords for line start, in input */ cOut[NRRD_DIM_MAX], /* coords for line start, in output */ szIn[NRRD_DIM_MAX], szOut[NRRD_DIM_MAX], idxIn, idxOut, /* linear indices for input and output */ numLines; /* number of scanlines in output nrrd */ char *dataIn, *dataOut, stmp[3][AIR_STRLEN_SMALL]; /* errors */ if (!(nout && nin && min && max)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } for (ai=0; aidim; ai++) { if (!(min[ai] <= max[ai])) { biffAddf(NRRD, "%s: axis %d min (%s) not <= max (%s)", me, ai, airSprintSize_t(stmp[0], min[ai]), airSprintSize_t(stmp[1], max[ai])); return 1; } if (!( min[ai] < nin->axis[ai].size && max[ai] < nin->axis[ai].size )) { biffAddf(NRRD, "%s: axis %d min (%s) or max (%s) out of bounds [0,%s]", me, ai, airSprintSize_t(stmp[0], min[ai]), airSprintSize_t(stmp[1], max[ai]), airSprintSize_t(stmp[2], nin->axis[ai].size-1)); return 1; } } /* this shouldn't actually be necessary .. */ if (!nrrdElementSize(nin)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); return 1; } /* allocate */ nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, szIn); numLines = 1; for (ai=0; aidim; ai++) { szOut[ai] = max[ai] - min[ai] + 1; if (ai) { numLines *= szOut[ai]; } } nout->blockSize = nin->blockSize; if (nrrdMaybeAlloc_nva(nout, nin->type, nin->dim, szOut)) { biffAddf(NRRD, "%s:", me); return 1; } lineSize = szOut[0]*nrrdElementSize(nin); /* the skinny */ typeSize = nrrdElementSize(nin); dataIn = (char *)nin->data; dataOut = (char *)nout->data; memset(cOut, 0, NRRD_DIM_MAX*sizeof(*cOut)); /* printf("!%s: nin->dim = %d\n", me, nin->dim); printf("!%s: min = %d %d %d\n", me, min[0], min[1], min[2]); printf("!%s: szIn = %d %d %d\n", me, szIn[0], szIn[1], szIn[2]); printf("!%s: szOut = %d %d %d\n", me, szOut[0], szOut[1], szOut[2]); printf("!%s: lineSize = %d\n", me, lineSize); printf("!%s: typeSize = %d\n", me, typeSize); printf("!%s: numLines = %d\n", me, (int)numLines); */ for (I=0; Idim; ai++) { cIn[ai] = cOut[ai] + min[ai]; } NRRD_INDEX_GEN(idxOut, cOut, szOut, nin->dim); NRRD_INDEX_GEN(idxIn, cIn, szIn, nin->dim); /* printf("!%s: %5d: cOut=(%3d,%3d,%3d) --> idxOut = %5d\n", me, (int)I, cOut[0], cOut[1], cOut[2], (int)idxOut); printf("!%s: %5d: cIn=(%3d,%3d,%3d) --> idxIn = %5d\n", me, (int)I, cIn[0], cIn[1], cIn[2], (int)idxIn); */ memcpy(dataOut + idxOut*typeSize, dataIn + idxIn*typeSize, lineSize); /* the lowest coordinate in cOut[] will stay zero, since we are copying one (1-D) scanline at a time */ NRRD_COORD_INCR(cOut, szOut, nin->dim, 1); } if (nrrdAxisInfoCopy(nout, nin, NULL, (NRRD_AXIS_INFO_SIZE_BIT | NRRD_AXIS_INFO_MIN_BIT | NRRD_AXIS_INFO_MAX_BIT ))) { biffAddf(NRRD, "%s:", me); return 1; } for (ai=0; aidim; ai++) { nrrdAxisInfoPosRange(&(nout->axis[ai].min), &(nout->axis[ai].max), nin, ai, AIR_CAST(double, min[ai]), AIR_CAST(double, max[ai])); /* do the safe thing first */ nout->axis[ai].kind = _nrrdKindAltered(nin->axis[ai].kind, AIR_FALSE); /* try cleverness */ if (!nrrdStateKindNoop) { if (nout->axis[ai].size == nin->axis[ai].size) { /* we can safely copy kind; the samples didn't change */ nout->axis[ai].kind = nin->axis[ai].kind; } else if (nrrdKind4Color == nin->axis[ai].kind && 3 == szOut[ai]) { nout->axis[ai].kind = nrrdKind3Color; } else if (nrrdKind4Vector == nin->axis[ai].kind && 3 == szOut[ai]) { nout->axis[ai].kind = nrrdKind3Vector; } else if ((nrrdKind4Vector == nin->axis[ai].kind || nrrdKind3Vector == nin->axis[ai].kind) && 2 == szOut[ai]) { nout->axis[ai].kind = nrrdKind2Vector; } else if (nrrdKindRGBAColor == nin->axis[ai].kind && 0 == min[ai] && 2 == max[ai]) { nout->axis[ai].kind = nrrdKindRGBColor; } else if (nrrdKind2DMaskedSymMatrix == nin->axis[ai].kind && 1 == min[ai] && max[ai] == szIn[ai]-1) { nout->axis[ai].kind = nrrdKind2DSymMatrix; } else if (nrrdKind2DMaskedMatrix == nin->axis[ai].kind && 1 == min[ai] && max[ai] == szIn[ai]-1) { nout->axis[ai].kind = nrrdKind2DMatrix; } else if (nrrdKind3DMaskedSymMatrix == nin->axis[ai].kind && 1 == min[ai] && max[ai] == szIn[ai]-1) { nout->axis[ai].kind = nrrdKind3DSymMatrix; } else if (nrrdKind3DMaskedMatrix == nin->axis[ai].kind && 1 == min[ai] && max[ai] == szIn[ai]-1) { nout->axis[ai].kind = nrrdKind3DMatrix; } } } strcpy(buff1, ""); for (ai=0; aidim; ai++) { sprintf(buff2, "%s[%s,%s]", (ai ? "x" : ""), airSprintSize_t(stmp[0], min[ai]), airSprintSize_t(stmp[1], max[ai])); strcat(buff1, buff2); } if (nrrdContentSet_va(nout, func, nin, "%s", buff1)) { biffAddf(NRRD, "%s:", me); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_SPACEORIGIN_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } /* copy origin, then shift it along the spatial axes */ nrrdSpaceVecCopy(nout->spaceOrigin, nin->spaceOrigin); for (ai=0; aidim; ai++) { if (AIR_EXISTS(nin->axis[ai].spaceDirection[0])) { nrrdSpaceVecScaleAdd2(nout->spaceOrigin, 1.0, nout->spaceOrigin, AIR_CAST(double, min[ai]), nin->axis[ai].spaceDirection); } } return 0; } /* ---- BEGIN non-NrrdIO */ /* ******** nrrdSliceSelect ** ** selects slices along axis "axi" from "nin", according to whether ** line[i] is above or below thresh: ** ** line[i] >= thresh: slice i goes into noutAbove ** line[i] < thresh: slice i goes into noutBelow ** ** Either noutAbove or noutBelow (but not both) can be passed ** as NULL if you aren't interested in the output. It is a ** biff-able error if the threshold is outside the range of ** errors and a non-Null nrrd was passed for the correspondingly ** empty output. */ int nrrdSliceSelect(Nrrd *noutAbove, Nrrd *noutBelow, const Nrrd *nin, unsigned int saxi, Nrrd *_nline, double thresh) { static const char me[]="nrrdSliceSelect"; airArray *mop; Nrrd *nline, *nslice; NrrdRange *rng; double *line; size_t II, LL, numAbove, numBelow, stride, sizeAbove[NRRD_DIM_MAX], sizeBelow[NRRD_DIM_MAX]; unsigned int aa, bb; int axmap[NRRD_DIM_MAX]; char *above, *below, stmp[2][AIR_STRLEN_SMALL]; if (!( (noutAbove || noutBelow) && nin && _nline )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!AIR_EXISTS(thresh)) { biffAddf(NRRD, "%s: thresh %g doesn't exist", me, thresh); return 1; } if (!(saxi < nin->dim)) { biffAddf(NRRD, "%s: can't select axis %u of a %u-D input nrrd", me, saxi, nin->dim); return 1; } if (nrrdCheck(nin) || nrrdCheck(_nline)) { biffAddf(NRRD, "%s: basic problems with nin or nline", me); return 1; } if (nrrdTypeBlock == nin->type) { /* no good reason for this except that GLK forgets out to set the blocksize in output */ biffAddf(NRRD, "%s: sorry, can't handle type %s input", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!( nrrdTypeBlock != _nline->type && 1 == _nline->dim)) { biffAddf(NRRD, "%s: nline must be 1-D array of scalars (not %u-D of %s)", me, _nline->dim, airEnumStr(nrrdType, _nline->type)); return 1; } if (!( _nline->axis[0].size == nin->axis[saxi].size )) { biffAddf(NRRD, "%s: line length (%s) != axis[%u].size (%s)", me, airSprintSize_t(stmp[0], _nline->axis[0].size), saxi, airSprintSize_t(stmp[1], nin->axis[saxi].size)); return 1; } if (1 == nin->dim) { biffAddf(NRRD, "%s: sorry, slice-based implementation requires input " "dimension > 1", me); return 1; } mop = airMopNew(); rng = nrrdRangeNewSet(_nline, AIR_FALSE); airMopAdd(mop, rng, (airMopper)nrrdRangeNix, airMopAlways); if (rng->hasNonExist) { biffAddf(NRRD, "%s: had non-existent values in line", me); airMopError(mop); return 1; } nslice = nrrdNew(); airMopAdd(mop, nslice, (airMopper)nrrdNix, airMopAlways); nline = nrrdNew(); airMopAdd(mop, nline, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nline, _nline, nrrdTypeDouble)) { biffAddf(NRRD, "%s: problem copying line", me); airMopError(mop); return 1; } line = AIR_CAST(double *, nline->data); LL = nline->axis[0].size; numAbove = numBelow = 0; for (II=0; II= thresh) { numAbove++; } else { numBelow++; } } if (noutAbove && !numAbove) { biffAddf(NRRD, "%s: want slices for val >= thresh %g, " "but highest value is %g < %g\n", me, thresh, rng->max, thresh); airMopError(mop); return 1; } if (noutBelow && !numBelow) { biffAddf(NRRD, "%s: want slices for val < thresh %g, " "but lowest value is %g >= %g\n", me, thresh, rng->min, thresh); airMopError(mop); return 1; } nslice->dim = nin->dim-1; nslice->type = nin->type; bb = 0; stride = nrrdElementSize(nin); for (aa=0; aadim; aa++) { sizeAbove[aa] = sizeBelow[aa] = nin->axis[aa].size; if (aa != saxi) { axmap[aa] = aa; nslice->axis[bb].size = nin->axis[aa].size; if (aa < saxi) { stride *= nin->axis[aa].size; } bb++; } else { axmap[aa] = -1; } } sizeAbove[saxi] = numAbove; sizeBelow[saxi] = numBelow; if ((noutAbove && nrrdMaybeAlloc_nva(noutAbove, nin->type, nin->dim, sizeAbove)) || (noutBelow && nrrdMaybeAlloc_nva(noutBelow, nin->type, nin->dim, sizeBelow))) { biffAddf(NRRD, "%s: trouble allocating output", me); airMopError(mop); return 1; } if (noutAbove) { above = AIR_CAST(char *, noutAbove->data); } else { above = NULL; } if (noutBelow) { below = AIR_CAST(char *, noutBelow->data); } else { below = NULL; } /* the skinny */ for (II=0; II= thresh) { if (noutAbove) { nslice->data = above; if (nrrdSlice(nslice, nin, saxi, II)) { biffAddf(NRRD, "%s: trouble slicing (above) at %s", me, airSprintSize_t(stmp[0], II)); airMopError(mop); return 1; } above += stride; } } else { if (noutBelow) { nslice->data = below; if (nrrdSlice(nslice, nin, saxi, II)) { biffAddf(NRRD, "%s: trouble slicing (below) at %s", me, airSprintSize_t(stmp[0], II)); airMopError(mop); return 1; } below += stride; } } } if (noutAbove) { nrrdAxisInfoCopy(noutAbove, nin, axmap, NRRD_AXIS_INFO_NONE); if (nrrdBasicInfoCopy(noutAbove, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } } if (noutBelow) { nrrdAxisInfoCopy(noutBelow, nin, axmap, NRRD_AXIS_INFO_NONE); if (nrrdBasicInfoCopy(noutBelow, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } } airMopOkay(mop); return 0; } /* ******** nrrdSample_nva() ** ** given coordinates within a nrrd, copies the ** single element into given *val */ int nrrdSample_nva(void *val, const Nrrd *nrrd, const size_t *coord) { static const char me[]="nrrdSample_nva"; size_t I, size[NRRD_DIM_MAX], typeSize; unsigned int ai; char stmp[2][AIR_STRLEN_SMALL]; if (!(nrrd && coord && val)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } /* this shouldn't actually be necessary .. */ if (!nrrdElementSize(nrrd)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); return 1; } typeSize = nrrdElementSize(nrrd); nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSize, size); for (ai=0; aidim; ai++) { if (!( coord[ai] < size[ai] )) { biffAddf(NRRD, "%s: coordinate %s on axis %d out of bounds (0 to %s)", me, airSprintSize_t(stmp[0], coord[ai]), ai, airSprintSize_t(stmp[1], size[ai]-1)); return 1; } } NRRD_INDEX_GEN(I, coord, size, nrrd->dim); memcpy(val, (char*)(nrrd->data) + I*typeSize, typeSize); return 0; } /* ******** nrrdSample_va() ** ** var-args version of nrrdSample_nva() */ int nrrdSample_va(void *val, const Nrrd *nrrd, ...) { static const char me[]="nrrdSample_va"; unsigned int ai; size_t coord[NRRD_DIM_MAX]; va_list ap; if (!(nrrd && val)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } va_start(ap, nrrd); for (ai=0; aidim; ai++) { coord[ai] = va_arg(ap, size_t); } va_end(ap); if (nrrdSample_nva(val, nrrd, coord)) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } /* ******** nrrdSimpleCrop() ** */ int nrrdSimpleCrop(Nrrd *nout, const Nrrd *nin, unsigned int crop) { static const char me[]="nrrdSimpleCrop"; unsigned int ai; size_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX]; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } for (ai=0; aidim; ai++) { min[ai] = crop; max[ai] = nin->axis[ai].size-1 - crop; } if (nrrdCrop(nout, nin, min, max)) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } int nrrdCropAuto(Nrrd *nout, const Nrrd *nin, size_t _min[NRRD_DIM_MAX], size_t _max[NRRD_DIM_MAX], const unsigned int *keep, unsigned int keepNum, int measr, double frac, int offset) { static const char me[]="nrrdCropAuto"; size_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX], NN, II; int cropdo[NRRD_DIM_MAX]; airArray *mop; Nrrd *nperm, *nline; unsigned int axi; double *line; if (!( nout && nin )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (keepNum && !keep) { biffAddf(NRRD, "%s: non-zero keepNum %u but NULL keep", me, keepNum); return 1; } if (airEnumValCheck(nrrdMeasure, measr)) { biffAddf(NRRD, "%s: invalid %s measr %d", me, nrrdMeasure->name, measr); return 1; } if (!( AIR_EXISTS(frac) && frac >= 0.0 && frac < 0.5 )) { biffAddf(NRRD, "%s: frac %g not in interval [0.0,0.5)", me, frac); return 1; } for (axi=0; axidim; axi++) { cropdo[axi] = AIR_TRUE; } for (axi=0; axidim )) { biffAddf(NRRD, "%s: keep[%u] %u out of range [0,%u]", me, axi, keep[axi], nin->dim-1); return 1; } if (!cropdo[keep[axi]]) { biffAddf(NRRD, "%s: keep[%u] %u redundant", me, axi, keep[axi]); return 1; } cropdo[keep[axi]] = AIR_FALSE; } if (keepNum == nin->dim) { /* weird- wanted to keep all axes and crop none; that's easy */ if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: trouble copying for trivial case", me); return 1; } return 0; } /* else there's work to do */ mop = airMopNew(); nperm = nrrdNew(); airMopAdd(mop, nperm, (airMopper)nrrdNuke, airMopAlways); nline = nrrdNew(); airMopAdd(mop, nline, (airMopper)nrrdNuke, airMopAlways); for (axi=0; axidim; axi++) { double wsum, part; min[axi] = 0; max[axi] = nin->axis[axi].size-1; if (!cropdo[axi]) { continue; } /* else some analysis is required for this axis */ /* NN is product of axes NOT being cropped */ NN = nrrdElementNumber(nin)/nin->axis[axi].size; if (nrrdAxesSwap(nperm, nin, axi, nin->dim-1) || nrrdReshape_va(nperm, nperm, 2, NN, nin->axis[axi].size) || nrrdProject(nline, nperm, 0, measr, nrrdTypeDouble)) { biffAddf(NRRD, "%s: trouble forming projection line", me); airMopError(mop); return 1; } /* find sum of array */ line = AIR_CAST(double *, nline->data); wsum = part = 0.0; for (II=0; IIaxis[axi].size; II++) { wsum += line[II]; } /* sum bottom of array until hit fraction */ for (II=0; IIaxis[axi].size; II++) { part += line[II]; if (part/wsum > frac) { min[axi] = II; break; } } if (II == nin->axis[axi].size) { biffAddf(NRRD, "%s: confusion on bottom of axis %u", me, axi); airMopError(mop); return 1; } /* sum top of array until hit fraction */ part = 0.0; for (II=nin->axis[axi].size; II>0; II--) { part += line[II-1]; if (part/wsum > frac) { max[axi] = II-1; break; } } if (II == 0) { biffAddf(NRRD, "%s: confusion on top of axis %u", me, axi); airMopError(mop); return 1; } /* fprintf(stderr, "!%s: axis %u [%u,%u] --> ", me, axi, AIR_CAST(unsigned int, min[axi]), AIR_CAST(unsigned int, max[axi])); */ /* adjust based on offset */ if (offset > 0) { if (min[axi] < AIR_CAST(size_t, offset)) { /* desired outwards offset is more than cropping set */ min[axi] = 0; } else { min[axi] -= offset; } max[axi] += offset; max[axi] = AIR_MIN(max[axi], nin->axis[axi].size-1); } /* fprintf(stderr, "[%u,%u]\n", AIR_CAST(unsigned int, min[axi]), AIR_CAST(unsigned int, max[axi])); */ } /* can now do the crop */ if (nrrdCrop(nout, nin, min, max)) { biffAddf(NRRD, "%s: trouble doing the crop", me); return 1; } /* save the extents */ for (axi=0; axidim; axi++) { if (_min) { _min[axi] = min[axi]; } if (_max) { _max[axi] = max[axi]; } } airMopOkay(mop); return 0; } /* ---- END non-NrrdIO */ teem-1.11.0~svn6057/src/nrrd/formatText.c0000664000175000017500000002472412165631065017660 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" static int _nrrdFormatText_available(void) { return AIR_TRUE; } static int _nrrdFormatText_nameLooksLike(const char *fname) { return (airEndsWith(fname, NRRD_EXT_TEXT) || airEndsWith(fname, ".text") || airEndsWith(fname, ".ascii")); } static int _nrrdFormatText_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { static const char me[]="_nrrdFormatText_fitsInto"; AIR_UNUSED(encoding); /* encoding ignored- always ascii */ if (!(1 == nrrd->dim || 2 == nrrd->dim)) { biffMaybeAddf(useBiff, NRRD, "%s: dimension is %d, not 1 or 2", me, nrrd->dim); return AIR_FALSE; } if (nrrdTypeBlock == nrrd->type) { biffMaybeAddf(useBiff, NRRD, "%s: can't save blocks to plain text", me); return AIR_FALSE; } /* NOTE: type of array not guaranteed to survive */ return AIR_TRUE; } static int _nrrdFormatText_contentStartsLike(NrrdIoState *nio) { float oneFloat; return (NRRD_COMMENT_CHAR == nio->line[0] || airParseStrF(&oneFloat, nio->line, _nrrdTextSep, 1)); } static int _nrrdFormatText_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatText_read"; const char *fs; char *errS; unsigned int plen, llen; size_t line, sx, sy, size[NRRD_DIM_MAX]; int nret, fidx, settwo = 0, gotOnePerAxis = AIR_FALSE; /* fl: first line, al: all lines */ airArray *flArr, *alArr; float *fl, *al, oneFloat; airPtrPtrUnion appu; if (!_nrrdFormatText_contentStartsLike(nio)) { biffAddf(NRRD, "%s: this doesn't look like a %s file", me, nrrdFormatText->name); return 1; } /* this goofiness is just to leave the nrrd as we found it (specifically, nrrd->dim) when we hit an error */ #define UNSETTWO if (settwo) nrrd->dim = settwo /* we only get here with the first line already in nio->line */ line = 1; llen = AIR_CAST(unsigned int, strlen(nio->line)); if (0 == nrrd->dim) { settwo = nrrd->dim; nrrd->dim = 2; } /* first, we get through comments */ while (NRRD_COMMENT_CHAR == nio->line[0]) { nio->pos = 1; nio->pos += AIR_CAST(int, strspn(nio->line + nio->pos, _nrrdFieldSep)); fidx = _nrrdReadNrrdParseField(nio, AIR_FALSE); /* could we parse anything? */ if (!fidx) { /* being unable to parse a comment as a nrrd field is not any kind of error */ goto plain; } if (nrrdField_comment == fidx) { fidx = 0; goto plain; } fs = airEnumStr(nrrdField, fidx); if (!_nrrdFieldValidInText[fidx]) { if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: field \"%s\" not allowed in plain text " "--> plain comment)\n", me, fs); } fidx = 0; goto plain; } /* when reading plain text, we simply ignore repetitions of a field */ if ((nrrdField_keyvalue == fidx || !nio->seen[fidx]) && nrrdFieldInfoParse[fidx](file, nrrd, nio, AIR_TRUE)) { errS = biffGetDone(NRRD); if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "%s: %s", me, errS); fprintf(stderr, "(%s: malformed field \"%s\" --> plain comment)\n", me, fs); } if (nrrdField_dimension == fidx) { /* "# dimension: 0" lead nrrd->dim being set to 0 */ nrrd->dim = 2; } free(errS); fidx = 0; goto plain; } if (nrrdField_dimension == fidx) { if (!(1 == nrrd->dim || 2 == nrrd->dim)) { if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: plain text dimension can only be 1 or 2; " "resetting to 2)\n", me); } nrrd->dim = 2; } if (1 == nrrd->dim && gotOnePerAxis) { fprintf(stderr, "(%s: already parsed per-axis field, can't reset " "dimension to 1; resetting to 2)\n", me); nrrd->dim = 2; } } if (_nrrdFieldOnePerAxis[fidx]) { gotOnePerAxis = AIR_TRUE; } nio->seen[fidx] = AIR_TRUE; plain: if (!fidx) { if (nrrdCommentAdd(nrrd, nio->line + 1)) { biffAddf(NRRD, "%s: couldn't add comment", me); UNSETTWO; return 1; } } if (_nrrdOneLine(&llen, nio, file)) { biffAddf(NRRD, "%s: error getting a line", me); UNSETTWO; return 1; } if (!llen) { biffAddf(NRRD, "%s: hit EOF before any numbers parsed", me); UNSETTWO; return 1; } line++; } /* we supposedly have a line of numbers, see how many there are */ if (!airParseStrF(&oneFloat, nio->line, _nrrdTextSep, 1)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: couldn't parse a single number on line %s", me, airSprintSize_t(stmp, line)); UNSETTWO; return 1; } appu.f = &fl; flArr = airArrayNew(appu.v, NULL, sizeof(float), _NRRD_TEXT_INCR); if (!flArr) { biffAddf(NRRD, "%s: couldn't create array for first line values", me); UNSETTWO; return 1; } for (sx=1; 1; sx++) { /* there is obviously a limit to the number of numbers that can be parsed from a single finite line of input text */ airArrayLenSet(flArr, AIR_CAST(unsigned int, sx)); if (!flArr->data) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: couldn't alloc space for %s values", me, airSprintSize_t(stmp, sx)); UNSETTWO; return 1; } if (sx > airParseStrF(fl, nio->line, _nrrdTextSep, AIR_CAST(unsigned int, sx))) { /* We asked for sx ints and got less. We know that we successfully got one value, so we did succeed in parsing sx-1 values */ sx--; break; } } flArr = airArrayNuke(flArr); if (1 == nrrd->dim && 1 != sx) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: wanted 1-D nrrd, but got %s values on 1st line", me, airSprintSize_t(stmp, sx)); UNSETTWO; return 1; } /* else sx == 1 when nrrd->dim == 1 */ /* now see how many more lines there are */ appu.f = &al; alArr = airArrayNew(appu.v, NULL, sx*sizeof(float), _NRRD_TEXT_INCR); if (!alArr) { biffAddf(NRRD, "%s: couldn't create data buffer", me); UNSETTWO; return 1; } sy = 0; while (llen) { airArrayLenIncr(alArr, 1); if (!alArr->data) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: couldn't create scanline of %s values", me, airSprintSize_t(stmp, sx)); UNSETTWO; return 1; } plen = airParseStrF(al + sy*sx, nio->line, _nrrdTextSep, AIR_CAST(unsigned int, sx)); if (sx > plen) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: could only parse %d values (not %s) on line %s", me, plen, airSprintSize_t(stmp1, sx), airSprintSize_t(stmp2, line)); UNSETTWO; return 1; } sy++; line++; if (_nrrdOneLine(&llen, nio, file)) { biffAddf(NRRD, "%s: error getting a line", me); UNSETTWO; return 1; } } /* fprintf(stderr, "%s: nrrd->dim = %d, sx = %d; sy = %d\n", me, nrrd->dim, sx, sy); */ if (!( 1 == nrrd->dim || 2 == nrrd->dim )) { fprintf(stderr, "%s: PANIC about to save, but dim = %d\n", me, nrrd->dim); exit(1); } if (1 == nrrd->dim) { size[0] = sy; } else { size[0] = sx; size[1] = sy; } if (nio->oldData && nio->oldDataSize == (size_t)(nrrdTypeSize[nrrdTypeFloat]*sx*sy)) { nret = nrrdWrap_nva(nrrd, nio->oldData, nrrdTypeFloat, nrrd->dim, size); } else { nret = nrrdMaybeAlloc_nva(nrrd, nrrdTypeFloat, nrrd->dim, size); } if (nret) { biffAddf(NRRD, "%s: couldn't create nrrd for plain text data", me); UNSETTWO; return 1; } memcpy(nrrd->data, al, sx*sy*sizeof(float)); alArr = airArrayNuke(alArr); return 0; } static int _nrrdFormatText_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { char cmt[AIR_STRLEN_SMALL], buff[AIR_STRLEN_SMALL]; size_t I; int i, x, y, sx, sy; void *data; float val; sprintf(cmt, "%c ", NRRD_COMMENT_CHAR); if (!nio->bareText) { if (1 == nrrd->dim) { _nrrdFprintFieldInfo(file, cmt, nrrd, nio, nrrdField_dimension); } for (i=1; i<=NRRD_FIELD_MAX; i++) { if (_nrrdFieldValidInText[i] && nrrdField_dimension != i /* dimension is handled above */ && _nrrdFieldInteresting(nrrd, nio, i)) { _nrrdFprintFieldInfo(file, cmt, nrrd, nio, i); } } if (nrrdKeyValueSize(nrrd)) { unsigned int kvi; for (kvi=0; kvikvpArr->len; kvi++) { _nrrdKeyValueWrite(file, NULL, NULL, nrrd->kvp[0 + 2*kvi], nrrd->kvp[1 + 2*kvi]); } } } if (1 == nrrd->dim) { sx = 1; sy = AIR_CAST(int, nrrd->axis[0].size); } else { sx = AIR_CAST(int, nrrd->axis[0].size); sy = AIR_CAST(int, nrrd->axis[1].size); } data = nrrd->data; I = 0; for (y=0; ytype](data, I); nrrdSprint[nrrdTypeFloat](buff, &val); if (x) fprintf(file, " "); fprintf(file, "%s", buff); I++; } fprintf(file, "\n"); } return 0; } const NrrdFormat _nrrdFormatText = { "text", AIR_FALSE, /* isImage */ AIR_TRUE, /* readable */ AIR_FALSE, /* usesDIO */ _nrrdFormatText_available, _nrrdFormatText_nameLooksLike, _nrrdFormatText_fitsInto, _nrrdFormatText_contentStartsLike, _nrrdFormatText_read, _nrrdFormatText_write }; const NrrdFormat *const nrrdFormatText = &_nrrdFormatText; teem-1.11.0~svn6057/src/nrrd/accessors.c0000664000175000017500000006064612165631065017513 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" #include "float.h" /* ** making these typedefs here allows us to used one token for both ** constructing function names, and for specifying argument types */ typedef signed char CH; typedef unsigned char UC; typedef signed short SH; typedef unsigned short US; /* Microsoft apparently uses 'IN' as a keyword, so we changed 'IN' to 'JN'. */ typedef signed int JN; typedef unsigned int UI; typedef airLLong LL; /* ui64 to double conversion is not implemented, sorry */ #if _MSC_VER < 1300 typedef airLLong UL; #else typedef airULLong UL; #endif typedef float FL; typedef double DB; #define MAP(F, A) \ F(A, CH) \ F(A, UC) \ F(A, SH) \ F(A, US) \ F(A, JN) \ F(A, UI) \ F(A, LL) \ F(A, UL) \ F(A, FL) \ F(A, DB) /* ** _nrrdLoad( *v) ** ** Dereferences v as TB*, casts it to TA, returns it. */ #define LOAD_DEF(TA, TB) \ static TA \ _nrrdLoad##TA##TB(TB *v) { \ return (TA)(*v); \ } #define LOAD_LIST(TA, TB) \ (TA (*)(const void *))_nrrdLoad##TA##TB, MAP(LOAD_DEF, UI) MAP(LOAD_DEF, JN) MAP(LOAD_DEF, FL) MAP(LOAD_DEF, DB) unsigned int (* nrrdUILoad[NRRD_TYPE_MAX+1])(const void*) = { NULL, MAP(LOAD_LIST, UI) NULL }; int (* nrrdILoad[NRRD_TYPE_MAX+1])(const void*) = { NULL, MAP(LOAD_LIST, JN) NULL }; float (* nrrdFLoad[NRRD_TYPE_MAX+1])(const void*) = { NULL, MAP(LOAD_LIST, FL) NULL }; double (* nrrdDLoad[NRRD_TYPE_MAX+1])(const void*) = { NULL, MAP(LOAD_LIST, DB) NULL }; /* ** _nrrdStore( *v, j) ** ** Takes a TA j, and stores it in *v, thereby implicitly casting it to TB. ** Returns the result of the assignment, which may not be the same as ** the value that was passed in. */ #define STORE_DEF(TA, TB) \ static TA \ _nrrdStore##TA##TB(TB *v, TA j) { \ return (TA)(*v = (TB)j); \ } #define STORE_LIST(TA, TB) \ (TA (*)(void *, TA))_nrrdStore##TA##TB, MAP(STORE_DEF, UI) MAP(STORE_DEF, JN) MAP(STORE_DEF, FL) MAP(STORE_DEF, DB) unsigned int (* nrrdUIStore[NRRD_TYPE_MAX+1])(void *, unsigned int) = { NULL, MAP(STORE_LIST, UI) NULL }; int (* nrrdIStore[NRRD_TYPE_MAX+1])(void *, int) = { NULL, MAP(STORE_LIST, JN) NULL }; float (* nrrdFStore[NRRD_TYPE_MAX+1])(void *, float) = { NULL, MAP(STORE_LIST, FL) NULL }; double (* nrrdDStore[NRRD_TYPE_MAX+1])(void *, double) = { NULL, MAP(STORE_LIST, DB) NULL }; /* ** _nrrdLookup( *v, size_t I) ** ** Looks up element I of TB array v, and returns it cast to a TA. */ #define LOOKUP_DEF(TA, TB) \ static TA \ _nrrdLookup##TA##TB(TB *v, size_t I) { \ return (TA)v[I]; \ } #define LOOKUP_LIST(TA, TB) \ (TA (*)(const void*, size_t))_nrrdLookup##TA##TB, MAP(LOOKUP_DEF, UI) MAP(LOOKUP_DEF, JN) MAP(LOOKUP_DEF, FL) MAP(LOOKUP_DEF, DB) unsigned int (* nrrdUILookup[NRRD_TYPE_MAX+1])(const void *, size_t) = { NULL, MAP(LOOKUP_LIST, UI) NULL }; int (* nrrdILookup[NRRD_TYPE_MAX+1])(const void *, size_t) = { NULL, MAP(LOOKUP_LIST, JN) NULL }; float (* nrrdFLookup[NRRD_TYPE_MAX+1])(const void *, size_t) = { NULL, MAP(LOOKUP_LIST, FL) NULL }; double (* nrrdDLookup[NRRD_TYPE_MAX+1])(const void *, size_t) = { NULL, MAP(LOOKUP_LIST, DB) NULL }; /* ** _nrrdInsert( *v, size_t I, j) ** ** Given TA j, stores it in v[i] (implicitly casting to TB). ** Returns the result of the assignment, which may not be the same as ** the value that was passed in. */ #define INSERT_DEF(TA, TB) \ static TA \ _nrrdInsert##TA##TB(TB *v, size_t I, TA j) { \ return (TA)(v[I] = (TB)j); \ } #define INSERT_LIST(TA, TB) \ (TA (*)(void*, size_t, TA))_nrrdInsert##TA##TB, MAP(INSERT_DEF, UI) MAP(INSERT_DEF, JN) MAP(INSERT_DEF, FL) MAP(INSERT_DEF, DB) unsigned int (* nrrdUIInsert[NRRD_TYPE_MAX+1])(void *, size_t, unsigned int) = { NULL, MAP(INSERT_LIST, UI) NULL }; int (* nrrdIInsert[NRRD_TYPE_MAX+1])(void *, size_t, int) = { NULL, MAP(INSERT_LIST, JN) NULL }; float (* nrrdFInsert[NRRD_TYPE_MAX+1])(void *, size_t, float) = { NULL, MAP(INSERT_LIST, FL) NULL }; double (* nrrdDInsert[NRRD_TYPE_MAX+1])(void *, size_t, double) = { NULL, MAP(INSERT_LIST, DB) NULL }; /* ******** nrrdSprint ** ** Dereferences pointer v and sprintf()s that value into given string s, ** returns the result of sprintf() ** ** There is obviously no provision for ensuring that the sprint'ing ** doesn't overflow the buffer, which is unfortunate... */ static int _nrrdSprintCH(char *s, const CH *v) { return sprintf(s, "%d", *v); } static int _nrrdSprintUC(char *s, const UC *v) { return sprintf(s, "%u", *v); } static int _nrrdSprintSH(char *s, const SH *v) { return sprintf(s, "%d", *v); } static int _nrrdSprintUS(char *s, const US *v) { return sprintf(s, "%u", *v); } static int _nrrdSprintIN(char *s, const JN *v) { return sprintf(s, "%d", *v); } static int _nrrdSprintUI(char *s, const UI *v) { return sprintf(s, "%u", *v); } static int _nrrdSprintLL(char *s, const LL *v) { return sprintf(s, AIR_LLONG_FMT, *v); } static int _nrrdSprintUL(char *s, const UL *v) { return sprintf(s, AIR_ULLONG_FMT, *v); } /* HEY: sizeof(float) and sizeof(double) assumed here, since we're basing "8" and "17" on 6 == FLT_DIG and 15 == DBL_DIG, which are digits of precision for floats and doubles, respectively */ static int _nrrdSprintFL(char *s, const FL *v) { return airSinglePrintf(NULL, s, "%.8g", (double)(*v)); } static int _nrrdSprintDB(char *s, const DB *v) { return airSinglePrintf(NULL, s, "%.17g", *v); } int (* nrrdSprint[NRRD_TYPE_MAX+1])(char *, const void *) = { NULL, (int (*)(char *, const void *))_nrrdSprintCH, (int (*)(char *, const void *))_nrrdSprintUC, (int (*)(char *, const void *))_nrrdSprintSH, (int (*)(char *, const void *))_nrrdSprintUS, (int (*)(char *, const void *))_nrrdSprintIN, (int (*)(char *, const void *))_nrrdSprintUI, (int (*)(char *, const void *))_nrrdSprintLL, (int (*)(char *, const void *))_nrrdSprintUL, (int (*)(char *, const void *))_nrrdSprintFL, (int (*)(char *, const void *))_nrrdSprintDB, NULL}; /* ---- BEGIN non-NrrdIO */ /* ******** nrrdFprint ** ** Dereferences pointer v and fprintf()s that value into given file f; ** returns the result of fprintf() */ static int _nrrdFprintCH(FILE *f, const CH *v) { return fprintf(f, "%d", *v); } static int _nrrdFprintUC(FILE *f, const UC *v) { return fprintf(f, "%u", *v); } static int _nrrdFprintSH(FILE *f, const SH *v) { return fprintf(f, "%d", *v); } static int _nrrdFprintUS(FILE *f, const US *v) { return fprintf(f, "%u", *v); } static int _nrrdFprintIN(FILE *f, const JN *v) { return fprintf(f, "%d", *v); } static int _nrrdFprintUI(FILE *f, const UI *v) { return fprintf(f, "%u", *v); } static int _nrrdFprintLL(FILE *f, const LL *v) { return fprintf(f, AIR_LLONG_FMT, *v); } static int _nrrdFprintUL(FILE *f, const UL *v) { return fprintf(f, AIR_ULLONG_FMT, *v); } static int _nrrdFprintFL(FILE *f, const FL *v) { return airSinglePrintf(f, NULL, "%.8g", (double)(*v)); } static int _nrrdFprintDB(FILE *f, const DB *v) { return airSinglePrintf(f, NULL, "%.17g", *v); } int (* nrrdFprint[NRRD_TYPE_MAX+1])(FILE *, const void *) = { NULL, (int (*)(FILE *, const void *))_nrrdFprintCH, (int (*)(FILE *, const void *))_nrrdFprintUC, (int (*)(FILE *, const void *))_nrrdFprintSH, (int (*)(FILE *, const void *))_nrrdFprintUS, (int (*)(FILE *, const void *))_nrrdFprintIN, (int (*)(FILE *, const void *))_nrrdFprintUI, (int (*)(FILE *, const void *))_nrrdFprintLL, (int (*)(FILE *, const void *))_nrrdFprintUL, (int (*)(FILE *, const void *))_nrrdFprintFL, (int (*)(FILE *, const void *))_nrrdFprintDB, NULL}; /* about here is where Gordon admits he might have some use for C++ */ #define _MMEF_ARGS(type) type *minP, type *maxP, int *hneP, const Nrrd *nrrd #define _MMEF_FIXED(type) \ size_t I, N; \ type a, b, min, max, *v; \ \ if (!(minP && maxP)) \ return; \ \ /* all integral values exist */ \ *hneP = nrrdHasNonExistFalse; \ \ /* set the local data pointer */ \ v = (type*)(nrrd->data); \ \ /* get initial values */ \ N = nrrdElementNumber(nrrd); \ min = max = v[0]; \ \ /* run through array in pairs; by doing a compare on successive \ elements, we can do three compares per pair instead of the naive \ four. In one very unexhaustive test on irix6.64, this resulted \ in a 20% decrease in running time. I learned this trick from \ Numerical Recipes in C, long time ago, but I can't find it \ anywhere in the book now ... */ \ if (N>1) /* size_t is unsigned, so N-2 may overflow */ \ for (I=0; I<=N-2; I+=2) { \ a = v[0 + I]; \ b = v[1 + I]; \ if (a < b) { \ min = AIR_MIN(a, min); \ max = AIR_MAX(b, max); \ } else { \ max = AIR_MAX(a, max); \ min = AIR_MIN(b, min); \ } \ } \ \ /* get the very last one (may be redundant) */ \ a = v[N-1]; \ min = AIR_MIN(a, min); \ max = AIR_MAX(a, max); \ \ /* record results */ \ *minP = min; \ *maxP = max; #define _MMEF_FLOAT(type) \ size_t I, N; \ type a, min, max, *v; \ \ if (!(minP && maxP)) \ return; \ \ /* this may be over-written below */ \ *hneP = nrrdHasNonExistFalse; \ \ /* set the local data pointer */ \ N = nrrdElementNumber(nrrd); \ v = (type*)(nrrd->data); \ \ /* we have to explicitly search for the first non-NaN value */ \ max = min = AIR_NAN; \ for (I=0; I max) { \ max = a; \ } \ } \ } else { \ *hneP = nrrdHasNonExistTrue; \ } \ } \ } \ *minP = min; \ *maxP = max; static void _nrrdMinMaxExactFindCH (_MMEF_ARGS(CH)) {_MMEF_FIXED(CH)} static void _nrrdMinMaxExactFindUC (_MMEF_ARGS(UC)) {_MMEF_FIXED(UC)} static void _nrrdMinMaxExactFindSH (_MMEF_ARGS(SH)) {_MMEF_FIXED(SH)} static void _nrrdMinMaxExactFindUS (_MMEF_ARGS(US)) {_MMEF_FIXED(US)} static void _nrrdMinMaxExactFindIN (_MMEF_ARGS(JN)) {_MMEF_FIXED(JN)} static void _nrrdMinMaxExactFindUI (_MMEF_ARGS(UI)) {_MMEF_FIXED(UI)} static void _nrrdMinMaxExactFindLL (_MMEF_ARGS(LL)) {_MMEF_FIXED(LL)} static void _nrrdMinMaxExactFindUL (_MMEF_ARGS(UL)) {_MMEF_FIXED(UL)} static void _nrrdMinMaxExactFindFL (_MMEF_ARGS(FL)) {_MMEF_FLOAT(FL)} static void _nrrdMinMaxExactFindDB (_MMEF_ARGS(DB)) {_MMEF_FLOAT(DB)} /* ******** nrrdMinMaxExactFind[] ** ** the role of these is to allow finding the EXACT min and max of a nrrd, ** so that one does not have to rely on the potentially lossy storage ** of the min and max values in range->min and range->max, which are doubles. ** ** These also sets *hneP, using a value from the nrrdHasNonExist* enum */ void (* nrrdMinMaxExactFind[NRRD_TYPE_MAX+1])(void *minP, void *maxP, int *hneP, const Nrrd *) = { NULL, (void (*)(void *, void *, int *, const Nrrd *))_nrrdMinMaxExactFindCH, (void (*)(void *, void *, int *, const Nrrd *))_nrrdMinMaxExactFindUC, (void (*)(void *, void *, int *, const Nrrd *))_nrrdMinMaxExactFindSH, (void (*)(void *, void *, int *, const Nrrd *))_nrrdMinMaxExactFindUS, (void (*)(void *, void *, int *, const Nrrd *))_nrrdMinMaxExactFindIN, (void (*)(void *, void *, int *, const Nrrd *))_nrrdMinMaxExactFindUI, (void (*)(void *, void *, int *, const Nrrd *))_nrrdMinMaxExactFindLL, (void (*)(void *, void *, int *, const Nrrd *))_nrrdMinMaxExactFindUL, (void (*)(void *, void *, int *, const Nrrd *))_nrrdMinMaxExactFindFL, (void (*)(void *, void *, int *, const Nrrd *))_nrrdMinMaxExactFindDB, NULL }; /* ******** nrrdValCompare[] ** ** the sort of compare you'd give to qsort() to sort in ascending order: ** return < 0 if A < B, ** 0 if A == B, ** > 0 if A > B ** The non-trivial part of this is that for floating-point values, we ** dictate that all non-existent values are smaller than all existent ** values, regardless of their actual values (so +infinity < -42). This ** is to make sure that we have comparison that won't confuse qsort(), ** which underlies _nrrdMeasureMedian(), and to make it easier to separate ** existent from non-existent values. */ #define _VC_ARGS(type) const type *A, const type *B #define _VC_FIXED (*A < *B ? -1 : (*A > *B ? 1 : 0)) #define _VC_FLOAT \ int ex, ret; \ \ ex = AIR_EXISTS(*A) + AIR_EXISTS(*B); \ switch (ex) { \ case 2: ret = _VC_FIXED; break; \ case 1: ret = AIR_EXISTS(*A) ? 1 : -1; break; \ case 0: default: ret = 0; \ } static int _nrrdValCompareCH (_VC_ARGS(CH)) {return _VC_FIXED;} static int _nrrdValCompareUC (_VC_ARGS(UC)) {return _VC_FIXED;} static int _nrrdValCompareSH (_VC_ARGS(SH)) {return _VC_FIXED;} static int _nrrdValCompareUS (_VC_ARGS(US)) {return _VC_FIXED;} static int _nrrdValCompareIN (_VC_ARGS(JN)) {return _VC_FIXED;} static int _nrrdValCompareUI (_VC_ARGS(UI)) {return _VC_FIXED;} static int _nrrdValCompareLL (_VC_ARGS(LL)) {return _VC_FIXED;} static int _nrrdValCompareUL (_VC_ARGS(UL)) {return _VC_FIXED;} static int _nrrdValCompareFL (_VC_ARGS(FL)) {_VC_FLOAT; return ret;} static int _nrrdValCompareDB (_VC_ARGS(DB)) {_VC_FLOAT; return ret;} int (* nrrdValCompare[NRRD_TYPE_MAX+1])(const void *, const void *) = { NULL, (int (*)(const void *, const void *))_nrrdValCompareCH, (int (*)(const void *, const void *))_nrrdValCompareUC, (int (*)(const void *, const void *))_nrrdValCompareSH, (int (*)(const void *, const void *))_nrrdValCompareUS, (int (*)(const void *, const void *))_nrrdValCompareIN, (int (*)(const void *, const void *))_nrrdValCompareUI, (int (*)(const void *, const void *))_nrrdValCompareLL, (int (*)(const void *, const void *))_nrrdValCompareUL, (int (*)(const void *, const void *))_nrrdValCompareFL, (int (*)(const void *, const void *))_nrrdValCompareDB, NULL }; /* ** ...Inv: for descending order */ static int _nrrdValCompareInvCH (_VC_ARGS(CH)) {return -_VC_FIXED;} static int _nrrdValCompareInvUC (_VC_ARGS(UC)) {return -_VC_FIXED;} static int _nrrdValCompareInvSH (_VC_ARGS(SH)) {return -_VC_FIXED;} static int _nrrdValCompareInvUS (_VC_ARGS(US)) {return -_VC_FIXED;} static int _nrrdValCompareInvIN (_VC_ARGS(JN)) {return -_VC_FIXED;} static int _nrrdValCompareInvUI (_VC_ARGS(UI)) {return -_VC_FIXED;} static int _nrrdValCompareInvLL (_VC_ARGS(LL)) {return -_VC_FIXED;} static int _nrrdValCompareInvUL (_VC_ARGS(UL)) {return -_VC_FIXED;} static int _nrrdValCompareInvFL (_VC_ARGS(FL)) {_VC_FLOAT; return -ret;} static int _nrrdValCompareInvDB (_VC_ARGS(DB)) {_VC_FLOAT; return -ret;} int (* nrrdValCompareInv[NRRD_TYPE_MAX+1])(const void *, const void *) = { NULL, (int (*)(const void *, const void *))_nrrdValCompareInvCH, (int (*)(const void *, const void *))_nrrdValCompareInvUC, (int (*)(const void *, const void *))_nrrdValCompareInvSH, (int (*)(const void *, const void *))_nrrdValCompareInvUS, (int (*)(const void *, const void *))_nrrdValCompareInvIN, (int (*)(const void *, const void *))_nrrdValCompareInvUI, (int (*)(const void *, const void *))_nrrdValCompareInvLL, (int (*)(const void *, const void *))_nrrdValCompareInvUL, (int (*)(const void *, const void *))_nrrdValCompareInvFL, (int (*)(const void *, const void *))_nrrdValCompareInvDB, NULL }; /* ** nrrdArrayCompare ** ** something like strcmp() for arrays of numeric values, except ** that the arrays have to be equal length, and it has to do ** error checking. ** ** See comment about logic of return value above nrrdCompare() ** ** This is a very rare kind of nrrd function that operates on ** a bare array and not a Nrrd itself */ int nrrdArrayCompare(int type, const void *_valA, const void *_valB, size_t valNum, double epsilon, int *differ, char explain[AIR_STRLEN_LARGE]) { static const char me[]="nrrdArrayCompare"; const unsigned char *valA, *valB; int (*compare)(const void *, const void *); size_t ii, sze; char stmp[AIR_STRLEN_SMALL]; if (!(_valA && _valB && differ)) { biffAddf(NRRD, "%s: got NULL pointer (%p, %p, or %p)", me, _valA, _valB, AIR_VOIDP(differ)); return 1; } if (!valNum) { biffAddf(NRRD, "%s: can't work with 0-length arrays", me); return 1; } if (!AIR_EXISTS(epsilon)) { biffAddf(NRRD, "%s: non-existent epsilon %g", me, epsilon); return 1; } if (airEnumValCheck(nrrdType, type)) { biffAddf(NRRD, "%s: invalid nrrd type %d", me, type); return 1; } if (nrrdTypeBlock == type) { biffAddf(NRRD, "%s: can't use type %s", me, airEnumStr(nrrdType, type)); return 1; } if (explain) { strcpy(explain, ""); } if (type == nrrdTypeLLong || type == nrrdTypeULLong) { fprintf(stderr, "%s: WARNING: possible erroneous comparison of " "%s values with %s-based comparison\n", me, airEnumStr(nrrdType, type), airEnumStr(nrrdType, nrrdTypeDouble)); } sze = nrrdTypeSize[type]; compare = nrrdValCompare[type]; valA = AIR_CAST(const unsigned char *, _valA); valB = AIR_CAST(const unsigned char *, _valB); for (ii=0; ii epsilon) { if (explain) { airSprintSize_t(stmp, ii); if (0 == epsilon) { sprintf(explain, "valA[%s]=%.17g %s valB[%s]=%.17g " "by %g", stmp, aa, *differ < 0 ? "<" : ">", stmp, bb, fabs(aa - bb)); } else { sprintf(explain, "valA[%s]=%.17g %s valB[%s]=%.17g " "by %g, more than eps %g", stmp, aa, *differ < 0 ? "<" : ">", stmp, bb, fabs(aa - bb), epsilon); } } break; } else { /* we did detect a difference, but it was not in excess of epsilon, so we reset *differ to 0 */ *differ = 0; } } } return 0; } /* ---- END non-NrrdIO */ teem-1.11.0~svn6057/src/nrrd/encoding.c0000664000175000017500000000667512165631065017316 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ** what a NrrdEncoding can assume: ** -- the given nrrd struct has been filled out for the sake of knowing ** nrrd->dim, nrrd->axis[0].size, nrrd->type, and nrrd->blockSize ** AND NOTHING ELSE. See nrrd.h for why those fields, of all things ** are needed for {en/de}coding ** ** what a NrrdEncoding has to do: ** -- read data from file into the "data" argument (BUT NOT nrrd->data!!), ** or vice versa. ** -- respect nrrdStateVerboseIO with messages to stderr, if possible ** -- in case of error, put text error messages into biff via ** biffAddf(NRRD, ...) ** ** The "unknown" encoding below is intended to serve as a template for ** any new encodings being developed. */ static int _nrrdEncodingUnknown_available(void) { /* insert code here */ return AIR_FALSE; } static int _nrrdEncodingUnknown_read(FILE *file, void *data, size_t elementNum, Nrrd *nrrd, struct NrrdIoState_t *nio) { static const char me[]="_nrrdEncodingUnknown_read"; /* insert code here, and remove error handling below */ AIR_UNUSED(file); AIR_UNUSED(data); AIR_UNUSED(elementNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: ERROR!!! trying to read unknown encoding", me); return 1; } static int _nrrdEncodingUnknown_write(FILE *file, const void *data, size_t elementNum, const Nrrd *nrrd, struct NrrdIoState_t *nio) { static const char me[]="_nrrdEncodingUnknown_write"; /* insert code here, and remove error handling below */ AIR_UNUSED(file); AIR_UNUSED(data); AIR_UNUSED(elementNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: ERROR!!! trying to write unknown encoding", me); return 1; } const NrrdEncoding _nrrdEncodingUnknown = { "unknown", /* name */ "unknown", /* suffix */ AIR_FALSE, /* endianMatters */ AIR_FALSE, /* isCompression */ _nrrdEncodingUnknown_available, _nrrdEncodingUnknown_read, _nrrdEncodingUnknown_write }; const NrrdEncoding *const nrrdEncodingUnknown = &_nrrdEncodingUnknown; const NrrdEncoding *const nrrdEncodingArray[NRRD_ENCODING_TYPE_MAX+1] = { &_nrrdEncodingUnknown, &_nrrdEncodingRaw, &_nrrdEncodingAscii, &_nrrdEncodingHex, &_nrrdEncodingGzip, &_nrrdEncodingBzip2 }; teem-1.11.0~svn6057/src/nrrd/winKernel.c0000664000175000017500000002477412165631065017466 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #define _SINC(x) (sin(AIR_PI*x)/(AIR_PI*x)) static double _nrrdWindSincInt(const double *parm) { AIR_UNUSED(parm); /* This isn't true, but there aren't good accurate, closed-form approximations for these integrals ... */ return 1.0; } static double _nrrdDWindSincInt(const double *parm) { AIR_UNUSED(parm); /* ... or their derivatives */ return 0.0; } static double _nrrdWindSincSup(const double *parm) { double S; S = parm[0]; return parm[1]*S; } #define POW1(S) (S) #define POW2(S) ((S)*(S)) #define POW3(S) ((S)*(S)*(S)) #define WS_1_F(name, mac, spow) \ static float \ _nrrd##name##_1_f(float x, const double *parm) { \ float R, S; \ \ S = AIR_CAST(float, parm[0]); R = AIR_CAST(float, parm[1]); \ x /= S; \ return AIR_CAST(float, mac(x, R)/spow(S)); \ } #define WS_N_F(name, mac, spow) \ static void \ _nrrd##name##_N_f(float *f, const float *x, size_t len, \ const double *parm) { \ float S, R, t; \ size_t i; \ \ S = AIR_CAST(float, parm[0]); R = AIR_CAST(float, parm[1]); \ for (i=0; i R ? 0 : (x < -R ? 0 : (\ (x < R/50000 && x > -R/50000) \ ? 1.1 - x*x*(AIR_PI*AIR_PI*(3 + 2*R*R)/(12*R*R) \ + AIR_PI*AIR_PI*AIR_PI*AIR_PI*(5 + 2*R*R*(5 + 2*R*R))*x*x/(240*R*R*R*R)) \ : (1 + cos(AIR_PI*x/R))*_SINC(x)/2) \ )) WS_1_D(Hann, _HANN, POW1) WS_1_F(Hann, _HANN, POW1) WS_N_F(Hann, _HANN, POW1) WS_N_D(Hann, _HANN, POW1) static NrrdKernel _nrrdKernelHann = { "hann", 2, _nrrdWindSincSup, _nrrdWindSincInt, _nrrdHann_1_f, _nrrdHann_N_f, _nrrdHann_1_d, _nrrdHann_N_d }; NrrdKernel *const nrrdKernelHann = &_nrrdKernelHann; /* ------------------------------------------------------------ */ #define _DHANN(x, R) \ (x > R ? 0.0 : (x < -R ? 0.0 : ( \ (x < R/50000 && x > -R/50000) \ ? -x*AIR_PI*AIR_PI*(3 + 2*R*R)/(6*R*R) \ : ((R*(1 + cos(AIR_PI*x/R))*(AIR_PI*x*cos(AIR_PI*x) - sin(AIR_PI*x)) \ - AIR_PI*x*sin(AIR_PI*x)*sin(AIR_PI*x/R))/(2*R*AIR_PI*x*x)) \ ))) WS_1_D(DHann, _DHANN, POW2) WS_1_F(DHann, _DHANN, POW2) WS_N_F(DHann, _DHANN, POW2) WS_N_D(DHann, _DHANN, POW2) static NrrdKernel _nrrdKernelDHann = { "hannD", 2, _nrrdWindSincSup, _nrrdDWindSincInt, _nrrdDHann_1_f, _nrrdDHann_N_f, _nrrdDHann_1_d, _nrrdDHann_N_d }; NrrdKernel *const nrrdKernelHannD = &_nrrdKernelDHann; /* ------------------------------------------------------------ */ #define _DDHANN_A(x, R) \ (2*AIR_PI*R*cos(AIR_PI*x)*(R + R*cos(AIR_PI*x/R) + AIR_PI*x*sin(AIR_PI*x/R))) #define _DDHANN_B(x, R) \ (cos(AIR_PI*x/R)*(AIR_PI*AIR_PI*x*x + R*R*(AIR_PI*AIR_PI*x*x - 2)) + \ R*(R*(AIR_PI*AIR_PI*x*x - 2) - 2*AIR_PI*x*sin(AIR_PI*x/R))) #define _DDHANN(x, R) \ (x > R ? 0 : (x < -R ? 0 : ( \ (x < R/50000 && x > -R/50000) \ ? (AIR_PI*AIR_PI/(2*R*R))*( -(3 + 2*R*R)/3 \ + AIR_PI*AIR_PI*(5 + 2*R*R*(5 + R*R))*x*x/(10*R*R)) \ : -(_DDHANN_A(x,R) + sin(AIR_PI*x)*_DDHANN_B(x,R)/x)/(2*AIR_PI*R*R*x*x) \ ))) WS_1_D(DDHann, _DDHANN, POW3) WS_1_F(DDHann, _DDHANN, POW3) WS_N_F(DDHann, _DDHANN, POW3) WS_N_D(DDHann, _DDHANN, POW3) static NrrdKernel _nrrdKernelDDHann = { "hannDD", 2, _nrrdWindSincSup, _nrrdDWindSincInt, _nrrdDDHann_1_f, _nrrdDDHann_N_f, _nrrdDDHann_1_d, _nrrdDDHann_N_d }; NrrdKernel *const nrrdKernelHannDD = &_nrrdKernelDDHann; /* ------------------------------------------------------------ */ #define _BLACK(x, R) \ (x > R ? 0 : (x < -R ? 0 : ( \ (x < R/50000 && x > -R/50000) \ ? 1.0 - x*x*(1.6449340668482264 + 4.046537804446637/(R*R)) \ : (0.42 + cos(AIR_PI*x/R)/2 + 0.08*cos(2*AIR_PI*x/R))*_SINC(x) \ ))) WS_1_D(Black, _BLACK, POW1) WS_1_F(Black, _BLACK, POW1) WS_N_F(Black, _BLACK, POW1) WS_N_D(Black, _BLACK, POW1) static NrrdKernel _nrrdKernelBlackman = { "blackman", 2, _nrrdWindSincSup, _nrrdWindSincInt, _nrrdBlack_1_f, _nrrdBlack_N_f, _nrrdBlack_1_d, _nrrdBlack_N_d }; NrrdKernel *const nrrdKernelBlackman = &_nrrdKernelBlackman; /* ------------------------------------------------------------ */ #define _DBLACK_A(x, R) \ R*x*cos(AIR_PI*x)*(2.638937829015426 + AIR_PI*cos(AIR_PI*x/R) \ + 0.5026548245743669*cos(2*AIR_PI*x/R)) #define _DBLACK_B(x, R) \ sin(AIR_PI*x)*(-0.84*R - R*cos(AIR_PI*x/R) - 0.16*R*cos(2*AIR_PI*x/R) - \ AIR_PI*x*sin(AIR_PI*x/R) - 1.0053096491487339*x*sin(2*AIR_PI*x/R)) #define _DBLACK(x, R) \ (x > R ? 0.0 : (x < -R ? 0.0 : ( \ (x < R/50000 && x > -R/50000) \ ? -x*(3.289868133696453 + 8.093075608893272/(R*R)) \ : (_DBLACK_A(x,R) + _DBLACK_B(x,R))/(2*AIR_PI*R*x*x) \ ))) WS_1_D(DBlack, _DBLACK, POW2) WS_1_F(DBlack, _DBLACK, POW2) WS_N_F(DBlack, _DBLACK, POW2) WS_N_D(DBlack, _DBLACK, POW2) static NrrdKernel _nrrdKernelDBlack = { "blackmanD", 2, _nrrdWindSincSup, _nrrdDWindSincInt, _nrrdDBlack_1_f, _nrrdDBlack_N_f, _nrrdDBlack_1_d, _nrrdDBlack_N_d }; NrrdKernel *const nrrdKernelBlackmanD = &_nrrdKernelDBlack; /* ------------------------------------------------------------ */ #define _DDBLACK(x, R) \ (x > R ? 0.0 : (x < -R ? 0.0 : ( \ (x < R/30 && x > -R/30) \ ? (-(3.289868133696453 + 8.093075608893272/(R*R)) \ + x*x*(9.7409091034 + 86.694091020262/(R*R*R*R) + 79.8754546479/(R*R))) \ : ((R*x*cos(AIR_PI*x)*(-2.638937829015426*R - AIR_PI*R*cos((AIR_PI*x)/R) \ - 0.5026548245743669*R*cos((2*AIR_PI*x)/R) \ - AIR_PI*AIR_PI*x*sin((AIR_PI*x)/R) \ - 3.158273408348595*x*sin((2*AIR_PI*x)/R)) \ + sin(AIR_PI*x)*((-4.934802200544679*x*x \ + R*R*(1 - 4.934802200544679*x*x))*cos((AIR_PI*x)/R) \ + (-3.158273408348595*x*x \ + R*R*(0.16 - 0.7895683520871487*x*x))*cos((2*AIR_PI*x)/R) \ + R*(0.84*R - 4.14523384845753*R*x*x \ + AIR_PI*x*sin((AIR_PI*x)/R) \ + 1.0053096491487339*x*sin((2*AIR_PI*x)/R))))/(AIR_PI*R*R*x*x*x)) \ ))) WS_1_D(DDBlack, _DDBLACK, POW3) WS_1_F(DDBlack, _DDBLACK, POW3) WS_N_F(DDBlack, _DDBLACK, POW3) WS_N_D(DDBlack, _DDBLACK, POW3) static NrrdKernel _nrrdKernelDDBlack = { "blackmanDD", 2, _nrrdWindSincSup, _nrrdDWindSincInt, _nrrdDDBlack_1_f, _nrrdDDBlack_N_f, _nrrdDDBlack_1_d, _nrrdDDBlack_N_d }; NrrdKernel *const nrrdKernelBlackmanDD = &_nrrdKernelDDBlack; teem-1.11.0~svn6057/src/nrrd/privateNrrd.h0000664000175000017500000001754512173675674020050 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NRRD_PRIVATE_HAS_BEEN_INCLUDED #define NRRD_PRIVATE_HAS_BEEN_INCLUDED #ifdef _WIN32 #include #include #endif #ifdef __cplusplus extern "C" { #endif #define _NRRD_TEXT_INCR 1024 #define _NRRD_LLONG_MAX_HELP AIR_LLONG(2305843009213693951) #define _NRRD_LLONG_MIN_HELP AIR_LLONG(-2305843009213693952) #define _NRRD_WHITESPACE_NOTAB " \n\r\v\f" /* K+R pg. 157 */ /* ---- BEGIN non-NrrdIO */ #if NRRD_RESAMPLE_FLOAT # define nrrdResample_nrrdType nrrdTypeFloat # define EVALN evalN_f /* NrrdKernel method */ #else # define nrrdResample_nrrdType nrrdTypeDouble # define EVALN evalN_d /* NrrdKernel method */ #endif /* to access whatever nrrd there may be in in a NrrdIter */ #define _NRRD_ITER_NRRD(iter) ((iter)->nrrd ? (iter)->nrrd : (iter)->ownNrrd) /* ---- END non-NrrdIO */ /* ** _NRRD_SPACING ** ** returns nrrdDefSpacing if the argument doesn't exist, otherwise ** returns the argument */ #define _NRRD_SPACING(spc) (AIR_EXISTS(spc) ? spc: nrrdDefSpacing) typedef union { char **CP; int *I; unsigned int *UI; size_t *ST; double *D; const void *P; double (*V)[NRRD_SPACE_DIM_MAX]; } _nrrdAxisInfoSetPtrs; typedef union { char **CP; int *I; unsigned int *UI; size_t *ST; double *D; void *P; double (*V)[NRRD_SPACE_DIM_MAX]; } _nrrdAxisInfoGetPtrs; /* defaultsNrrd.c */ extern airLLong _nrrdLLongMaxHelp(airLLong val); extern airLLong _nrrdLLongMinHelp(airLLong val); extern airULLong _nrrdULLongMaxHelp(airULLong val); /* keyvalue.c */ extern void _nrrdWriteEscaped(FILE *file, char *dst, const char *str, const char *toescape, const char *tospace); extern int _nrrdKeyValueWrite(FILE *file, char **stringP, const char *prefix, const char *key, const char *value); /* formatXXX.c */ extern const char *_nrrdFormatURLLine0; extern const char *_nrrdFormatURLLine1; extern const NrrdFormat _nrrdFormatNRRD; extern const NrrdFormat _nrrdFormatPNM; extern const NrrdFormat _nrrdFormatPNG; extern const NrrdFormat _nrrdFormatVTK; extern const NrrdFormat _nrrdFormatText; extern const NrrdFormat _nrrdFormatEPS; extern int _nrrdHeaderCheck(Nrrd *nrrd, NrrdIoState *nio, int checkSeen); extern int _nrrdFormatNRRD_whichVersion(const Nrrd *nrrd, NrrdIoState *nio); /* encodingXXX.c */ extern const NrrdEncoding _nrrdEncodingRaw; extern const NrrdEncoding _nrrdEncodingAscii; extern const NrrdEncoding _nrrdEncodingHex; extern const NrrdEncoding _nrrdEncodingGzip; extern const NrrdEncoding _nrrdEncodingBzip2; /* read.c */ extern int _nrrdCalloc(Nrrd *nrrd, NrrdIoState *nio, FILE *file); extern char _nrrdFieldSep[]; /* arrays.c */ extern const int _nrrdFieldValidInImage[NRRD_FIELD_MAX+1]; extern const int _nrrdFieldValidInText[NRRD_FIELD_MAX+1]; extern const int _nrrdFieldOnePerAxis[NRRD_FIELD_MAX+1]; extern const char _nrrdEnumFieldStr[NRRD_FIELD_MAX+1][AIR_STRLEN_SMALL]; extern const int _nrrdFieldRequired[NRRD_FIELD_MAX+1]; /* simple.c */ extern char *_nrrdContentGet(const Nrrd *nin); extern int _nrrdContentSet_nva(Nrrd *nout, const char *func, char *content, const char *format, va_list arg); extern int _nrrdContentSet_va(Nrrd *nout, const char *func, char *content, const char *format, ...); extern int (*_nrrdFieldCheck[NRRD_FIELD_MAX+1])(const Nrrd *nrrd, int useBiff); extern void _nrrdSplitSizes(size_t *pieceSize, size_t *pieceNum, Nrrd *nrrd, unsigned int listDim); /* axis.c */ extern int _nrrdKindAltered(int kindIn, int resampling); extern void _nrrdAxisInfoCopy(NrrdAxisInfo *dest, const NrrdAxisInfo *src, int bitflag); extern void _nrrdAxisInfoInit(NrrdAxisInfo *axis); extern void _nrrdAxisInfoNewInit(NrrdAxisInfo *axis); extern int _nrrdCenter(int center); extern int _nrrdCenter2(int center, int def); /* ---- BEGIN non-NrrdIO */ extern int _nrrdDblcmp(double aa, double bb); /* convertNrrd.c */ extern void (*_nrrdConv[][NRRD_TYPE_MAX+1])(void *, const void *, size_t); extern void (*_nrrdClampConv[][NRRD_TYPE_MAX+1])(void *, const void *, size_t); extern void (*_nrrdCastClampRound[][NRRD_TYPE_MAX+1])(void *, const void *, size_t, int doClamp, int roundd); /* ---- END non-NrrdIO */ /* read.c */ extern char _nrrdFieldStr[NRRD_FIELD_MAX+1][AIR_STRLEN_SMALL]; extern char _nrrdRelativePathFlag[]; extern char _nrrdFieldSep[]; extern char _nrrdNoSpaceVector[]; extern char _nrrdTextSep[]; /* ---- BEGIN non-NrrdIO */ extern int _nrrdReshapeUpGrayscale(Nrrd *nimg); /* ---- END non-NrrdIO */ extern void _nrrdSplitName(char **dirP, char **baseP, const char *name); /* write.c */ extern int _nrrdFieldInteresting(const Nrrd *nrrd, NrrdIoState *nio, int field); extern void _nrrdSprintFieldInfo(char **strP, const char *prefix, const Nrrd *nrrd, NrrdIoState *nio, int field); extern void _nrrdFprintFieldInfo(FILE *file, const char *prefix, const Nrrd *nrrd, NrrdIoState *nio, int field); /* ---- BEGIN non-NrrdIO */ extern int _nrrdReshapeDownGrayscale(Nrrd *nimg); /* ---- END non-NrrdIO */ /* parseNrrd.c */ extern int _nrrdReadNrrdParseField(NrrdIoState *nio, int useBiff); /* methodsNrrd.c */ extern void nrrdPeripheralInit(Nrrd *nrrd); extern int nrrdPeripheralCopy(Nrrd *nout, const Nrrd *nin); extern int _nrrdCopy(Nrrd *nout, const Nrrd *nin, int bitflag); extern int _nrrdSizeCheck(const size_t *size, unsigned int dim, int useBiff); extern void _nrrdTraverse(Nrrd *nrrd); extern int _nrrdMaybeAllocMaybeZero_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size, int zeroWhenNoAlloc); #if TEEM_ZLIB #if TEEM_VTK_MANGLE #include "vtk_zlib_mangle.h" #endif #include /* NrrdIO-hack-004 */ /* gzio.c */ extern gzFile _nrrdGzOpen(FILE* fd, const char *mode); extern int _nrrdGzClose(gzFile file); extern int _nrrdGzRead(gzFile file, void* buf, unsigned int len, unsigned int* read); extern int _nrrdGzWrite(gzFile file, const void* buf, unsigned int len, unsigned int* written); #endif /* ---- BEGIN non-NrrdIO */ /* apply1D.c */ extern double _nrrdApplyDomainMin(const Nrrd *nmap, int ramps, int mapAxis); extern double _nrrdApplyDomainMax(const Nrrd *nmap, int ramps, int mapAxis); /* superset.c */ extern size_t _nrrdMirror_64(size_t N, ptrdiff_t I); extern unsigned int _nrrdMirror_32(unsigned int N, int I); /* ---- END non-NrrdIO */ #ifdef __cplusplus } #endif #endif /* NRRD_PRIVATE_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/nrrd/deringNrrd.c0000664000175000017500000006561412165631065017624 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" #define CLAMP_HIST_BINS_MIN 512 #define CLAMP_PERC_MAX 30.0 #define DEBUG 0 /* TODO: * * make sure all outout values exist (test with d/xiao/todering/xing) * * permit controlling the extent of correction (don't subtract out * all the estimated ring signal) * * valgrind * * make it multi-threaded * * try fix for round object boundaries being confused for rings - (relies on properties of discrete gauss for radial blurring) - after initial ptxf - make a few radial blurs (up to scale of radial high-pass) - measuring scale-normalized |df/dr| on each one - do non-maximal suppression along radius, zeroing out edges below some percentile of gradient strength - where the gradients are high on the most-blurring, and had similar grad mag at smaller scales, that's a real edge: make a mask for this - blur this along theta just like the ring map, then multiply w/ ring map * * try fix for low-theta-frequency rings * with high thetaNum, find mode along theta */ NrrdDeringContext * nrrdDeringContextNew(void) { NrrdDeringContext *drc; unsigned int pi; drc = AIR_CALLOC(1, NrrdDeringContext); if (!drc) { return NULL; } drc->verbose = 0; drc->linearInterp = AIR_FALSE; drc->verticalSeam = AIR_FALSE; drc->nin = NULL; drc->center[0] = AIR_NAN; drc->center[1] = AIR_NAN; drc->clampPerc[0] = 0.0; drc->clampPerc[1] = 0.0; drc->radiusScale = 1.0; drc->thetaNum = 0; drc->clampHistoBins = CLAMP_HIST_BINS_MIN*4; drc->rkernel = NULL; drc->tkernel = NULL; for (pi=0; pirkparm[pi] = drc->tkparm[pi] = AIR_NAN; } drc->cdataIn = NULL; drc->cdataOut = NULL; drc->sliceSize = 0; drc->clampDo = AIR_FALSE; drc->clamp[0] = AIR_NAN; drc->clamp[1] = AIR_NAN; drc->ringMagnitude = AIR_NAN; return drc; } NrrdDeringContext * nrrdDeringContextNix(NrrdDeringContext *drc) { if (drc) { free(drc); } return NULL; } int nrrdDeringVerboseSet(NrrdDeringContext *drc, int verbose) { static const char me[]="nrrdDeringVerboseSet"; if (!drc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } drc->verbose = verbose; return 0; } int nrrdDeringLinearInterpSet(NrrdDeringContext *drc, int linterp) { static const char me[]="nrrdDeringLinearInterpSet"; if (!drc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } drc->linearInterp = linterp; return 0; } int nrrdDeringVerticalSeamSet(NrrdDeringContext *drc, int vertSeam) { static const char me[]="nrrdDeringVerticalSeamSet"; if (!drc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } drc->verticalSeam = vertSeam; return 0; } int nrrdDeringInputSet(NrrdDeringContext *drc, const Nrrd *nin) { static const char me[]="nrrdDeringInputSet"; if (!( drc && nin )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdCheck(nin)) { biffAddf(NRRD, "%s: problems with given nrrd", me); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(NRRD, "%s: can't resample from type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!( 2 == nin->dim || 3 == nin->dim )) { biffAddf(NRRD, "%s: need 2 or 3 dim nrrd (not %u)", me, nin->dim); return 1; } if (drc->verbose > 2) { fprintf(stderr, "%s: hi\n", me); } drc->nin = nin; drc->cdataIn = AIR_CAST(const char *, nin->data); drc->sliceSize = (nin->axis[0].size * nin->axis[1].size * nrrdElementSize(nin)); if (drc->verbose > 2) { fprintf(stderr, "%s: sliceSize = %u\n", me, AIR_CAST(unsigned int, drc->sliceSize)); } return 0; } int nrrdDeringCenterSet(NrrdDeringContext *drc, double cx, double cy) { static const char me[]="nrrdDeringCenterSet"; if (!drc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( AIR_EXISTS(cx) && AIR_EXISTS(cy) )) { biffAddf(NRRD, "%s: center (%g,%g) doesn't exist", me, cx, cy); return 1; } drc->center[0] = cx; drc->center[1] = cy; return 0; } int nrrdDeringClampPercSet(NrrdDeringContext *drc, double lo, double hi) { static const char me[]="nrrdDeringClampPercSet"; if (!drc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( AIR_EXISTS(lo) && AIR_EXISTS(hi) && lo >= 0 && lo < CLAMP_PERC_MAX && hi >= 0 && hi < CLAMP_PERC_MAX)) { biffAddf(NRRD, "%s: need finite lo and hi both in [0.0, %g), not %g, %g", me, CLAMP_PERC_MAX, lo, hi); return 1; } drc->clampPerc[0] = lo; drc->clampPerc[1] = hi; return 0; } int nrrdDeringClampHistoBinsSet(NrrdDeringContext *drc, unsigned int bins) { static const char me[]="nrrdDeringClampHistoBinsSet"; if (!drc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( bins >= CLAMP_HIST_BINS_MIN )) { biffAddf(NRRD, "%s: given bins %u not >= reasonable min %u", me, bins, CLAMP_HIST_BINS_MIN); return 1; } drc->clampHistoBins = bins; return 0; } int nrrdDeringRadiusScaleSet(NrrdDeringContext *drc, double rsc) { static const char me[]="nrrdDeringRadiusScaleSet"; if (!drc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( AIR_EXISTS(rsc) && rsc > 0.0 )) { biffAddf(NRRD, "%s: need finite positive radius scale, not %g", me, rsc); return 1; } drc->radiusScale = rsc; return 0; } int nrrdDeringThetaNumSet(NrrdDeringContext *drc, unsigned int thetaNum) { static const char me[]="nrrdDeringThetaNumSet"; if (!drc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!thetaNum) { biffAddf(NRRD, "%s: need non-zero thetaNum", me); return 1; } drc->thetaNum = thetaNum; return 0; } int nrrdDeringRadialKernelSet(NrrdDeringContext *drc, const NrrdKernel *rkernel, const double rkparm[NRRD_KERNEL_PARMS_NUM]) { static const char me[]="nrrdDeringRadialKernelSet"; unsigned int pi; if (!( drc && rkernel && rkparm )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } drc->rkernel = rkernel; for (pi=0; pirkparm[pi] = rkparm[pi]; } return 0; } int nrrdDeringThetaKernelSet(NrrdDeringContext *drc, const NrrdKernel *tkernel, const double tkparm[NRRD_KERNEL_PARMS_NUM]) { static const char me[]="nrrdDeringThetaKernelSet"; unsigned int pi; if (!( drc && tkernel && tkparm )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } drc->tkernel = tkernel; for (pi=0; pitkparm[pi] = tkparm[pi]; } return 0; } /* ** per-thread state for deringing */ #define ORIG 0 #define WGHT 1 #define BLRR 2 #define DIFF 3 #define RSHP 4 #define CROP 5 #define CBLR 6 #define RING 7 #define PTXF_NUM 8 typedef struct { unsigned int zi; double radMax; size_t radNum; airArray *mop; Nrrd *nsliceOrig, /* wrapped slice of nin, sneakily non-const */ *nslice, /* slice of nin, converted to double */ *nsliceOut, /* (may be different type than nslice) */ *nptxf[PTXF_NUM]; double *slice, *ptxf, *wght, *ring; NrrdResampleContext *rsmc[2]; /* rsmc[0]: radial blurring ORIG -> BLRR rsmc[1]: theta blurring DIFF -> RING */ double ringMag; } deringBag; static deringBag * deringBagNew(NrrdDeringContext *drc, double radMax) { deringBag *dbg; unsigned int pi; dbg = AIR_CALLOC(1, deringBag); dbg->radMax = radMax; dbg->radNum = AIR_ROUNDUP(drc->radiusScale*radMax); dbg->mop = airMopNew(); dbg->nsliceOrig = nrrdNew(); airMopAdd(dbg->mop, dbg->nsliceOrig, (airMopper)nrrdNix /* not Nuke! */, airMopAlways); dbg->nslice = nrrdNew(); airMopAdd(dbg->mop, dbg->nslice, (airMopper)nrrdNuke, airMopAlways); dbg->nsliceOut = nrrdNew(); airMopAdd(dbg->mop, dbg->nsliceOut, (airMopper)nrrdNix /* not Nuke! */, airMopAlways); for (pi=0; pinptxf[pi] = nrrdNew(); airMopAdd(dbg->mop, dbg->nptxf[pi], (airMopper)nrrdNuke, airMopAlways); } dbg->rsmc[0] = nrrdResampleContextNew(); airMopAdd(dbg->mop, dbg->rsmc[0], (airMopper)nrrdResampleContextNix, airMopAlways); dbg->rsmc[1] = nrrdResampleContextNew(); airMopAdd(dbg->mop, dbg->rsmc[1], (airMopper)nrrdResampleContextNix, airMopAlways); dbg->ringMag = 0.0; return dbg; } static deringBag * deringBagNix(deringBag *dbg) { airMopOkay(dbg->mop); airFree(dbg); return NULL; } static int deringPtxfAlloc(NrrdDeringContext *drc, deringBag *dbg) { static const char me[]="deringPtxfAlloc"; unsigned int pi, ri; int E; /* polar transform setup */ for (pi=0; piverticalSeam && (CROP == pi || CBLR == pi)) { E = nrrdMaybeAlloc_va(dbg->nptxf[pi], nrrdTypeDouble, 3, dbg->radNum, AIR_CAST(size_t, drc->thetaNum/2 - 1), AIR_CAST(size_t, 2)); } else { E = nrrdMaybeAlloc_va(dbg->nptxf[pi], nrrdTypeDouble, 2, dbg->radNum, AIR_CAST(size_t, drc->thetaNum)); } if (E) { biffAddf(NRRD, "%s: polar transform allocation problem", me); return 1; } } dbg->ptxf = AIR_CAST(double *, dbg->nptxf[ORIG]->data); dbg->wght = AIR_CAST(double *, dbg->nptxf[WGHT]->data); dbg->ring = AIR_CAST(double *, dbg->nptxf[RING]->data); E = AIR_FALSE; for (ri=0; ri<2; ri++) { char kstr[AIR_STRLEN_LARGE]; if (0 == ri) { if (!E) E |= nrrdResampleInputSet(dbg->rsmc[0], dbg->nptxf[ORIG]); nrrdKernelSprint(kstr, drc->rkernel, drc->rkparm); if (drc->verbose > 1) { fprintf(stderr, "%s: rsmc[0] axis 0 kernel: %s\n", me, kstr); } if (!E) E |= nrrdResampleKernelSet(dbg->rsmc[0], 0, drc->rkernel, drc->rkparm); if (!E) E |= nrrdResampleKernelSet(dbg->rsmc[0], 1, NULL, NULL); if (!E) E |= nrrdResampleSamplesSet(dbg->rsmc[0], 1, drc->thetaNum); if (!E) E |= nrrdResampleBoundarySet(dbg->rsmc[0], nrrdBoundaryWrap); } else { if (drc->verticalSeam) { if (!E) E |= nrrdResampleInputSet(dbg->rsmc[1], dbg->nptxf[CROP]); } else { if (!E) E |= nrrdResampleInputSet(dbg->rsmc[1], dbg->nptxf[DIFF]); } nrrdKernelSprint(kstr, drc->tkernel, drc->tkparm); if (drc->verbose > 1) { fprintf(stderr, "%s: rsmc[1] axis 1 kernel: %s\n", me, kstr); } if (!E) E |= nrrdResampleKernelSet(dbg->rsmc[1], 0, NULL, NULL); if (!E) E |= nrrdResampleKernelSet(dbg->rsmc[1], 1, drc->tkernel, drc->tkparm); if (drc->verticalSeam) { if (!E) E |= nrrdResampleSamplesSet(dbg->rsmc[1], 1, drc->thetaNum/2 - 1); if (!E) E |= nrrdResampleKernelSet(dbg->rsmc[1], 2, NULL, NULL); if (!E) E |= nrrdResampleSamplesSet(dbg->rsmc[1], 2, 2); if (!E) E |= nrrdResampleBoundarySet(dbg->rsmc[1], nrrdBoundaryPad); if (!E) E |= nrrdResamplePadValueSet(dbg->rsmc[1], 0.0); } else { if (!E) E |= nrrdResampleSamplesSet(dbg->rsmc[1], 1, drc->thetaNum); if (!E) E |= nrrdResampleBoundarySet(dbg->rsmc[1], nrrdBoundaryWrap); } } if (!E) E |= nrrdResampleDefaultCenterSet(dbg->rsmc[ri], nrrdCenterCell); if (!E) E |= nrrdResampleSamplesSet(dbg->rsmc[ri], 0, dbg->radNum); if (!E) E |= nrrdResampleTypeOutSet(dbg->rsmc[ri], nrrdTypeDefault); if (!E) E |= nrrdResampleRenormalizeSet(dbg->rsmc[ri], AIR_TRUE); if (!E) E |= nrrdResampleNonExistentSet(dbg->rsmc[ri], nrrdResampleNonExistentRenormalize); if (!E) E |= nrrdResampleRangeFullSet(dbg->rsmc[ri], 0); if (!E) E |= nrrdResampleRangeFullSet(dbg->rsmc[ri], 1); } if (E) { biffAddf(NRRD, "%s: couldn't set up resampler", me); return 1; } return 0; } static int deringSliceGet(NrrdDeringContext *drc, deringBag *dbg, unsigned int zi) { static const char me[]="deringSliceGet"; void *nonconstdata; const char *cdata; /* HEY: bypass of const-ness of drc->cdataIn; should think about how to do this without breaking const-correctness... */ cdata = drc->cdataIn + zi*(drc->sliceSize); memcpy(&nonconstdata, &cdata, sizeof(void*)); /* slice setup */ if (nrrdWrap_va(dbg->nsliceOrig, nonconstdata, drc->nin->type, 2, drc->nin->axis[0].size, drc->nin->axis[1].size) || (nrrdTypeDouble == drc->nin->type ? nrrdCopy(dbg->nslice, dbg->nsliceOrig) : nrrdConvert(dbg->nslice, dbg->nsliceOrig, nrrdTypeDouble))) { biffAddf(NRRD, "%s: slice setup trouble", me); return 1; } dbg->slice = AIR_CAST(double *, dbg->nslice->data); dbg->zi = zi; return 0; } static int deringSliceSet(NrrdDeringContext *drc, deringBag *dbg, Nrrd *nout, unsigned int zi) { static const char me[]="deringSliceSet"; if (nrrdWrap_va(dbg->nsliceOut, AIR_CAST(void *, drc->cdataOut + zi*(drc->sliceSize)), nout->type, 2, nout->axis[0].size, nout->axis[1].size) || (nrrdTypeDouble == nout->type ? nrrdCopy(dbg->nsliceOut, dbg->nslice) : nrrdConvert(dbg->nsliceOut, dbg->nslice, nout->type))) { biffAddf(NRRD, "%s: slice output trouble", me); return 1; } return 0; } #define EPS 0.000001 static void deringXYtoRT(NrrdDeringContext *drc, deringBag *dbg, unsigned int xi, unsigned int yi, unsigned int *rrIdx, unsigned int *thIdx, double *rrFrc, double *thFrc) { double dx, dy, rr, th, rrScl, thScl; dx = xi - drc->center[0]; dy = yi - drc->center[1]; rr = sqrt(dx*dx + dy*dy); th = atan2(-dx, dy); rrScl = AIR_AFFINE(-EPS, rr, dbg->radMax+EPS, 0.0, dbg->radNum-1); *rrIdx = AIR_CAST(unsigned int, 0.5 + rrScl); thScl = AIR_AFFINE(-AIR_PI-EPS, th, AIR_PI+EPS, 0.0, drc->thetaNum); *thIdx = AIR_CAST(unsigned int, 0.5 + thScl); if (rrFrc && thFrc) { *rrFrc = rrScl - *rrIdx; *thFrc = thScl - *thIdx; if (*rrFrc < 0) { *rrIdx -= 1; *rrFrc += 1; } if (*thFrc < 0) { *thIdx -= 1; *thFrc += 1; } } return; } static int deringPtxfDo(NrrdDeringContext *drc, deringBag *dbg) { /* static const char me[]="deringPtxfDo"; */ unsigned int sx, sy, xi, yi, rrIdx, thIdx; nrrdZeroSet(dbg->nptxf[ORIG]); nrrdZeroSet(dbg->nptxf[WGHT]); sx = AIR_CAST(unsigned int, drc->nin->axis[0].size); sy = AIR_CAST(unsigned int, drc->nin->axis[1].size); for (yi=0; yilinearInterp) { unsigned int bidx, bidxPlus; deringXYtoRT(drc, dbg, xi, yi, &rrIdx, &thIdx, &rrFrc, &thFrc); bidx = AIR_UINT(rrIdx + dbg->radNum*thIdx); bidxPlus = AIR_UINT(thIdx < drc->thetaNum-1 ? bidx + dbg->radNum : rrIdx); val = dbg->slice[xi + sx*yi]; if (drc->clampDo) { val = AIR_CLAMP(drc->clamp[0], val, drc->clamp[1]); } dbg->ptxf[bidx ] += (1-rrFrc)*(1-thFrc)*val; dbg->ptxf[bidx + 1] += rrFrc*(1-thFrc)*val; dbg->ptxf[bidxPlus ] += (1-rrFrc)*thFrc*val; dbg->ptxf[bidxPlus + 1] += rrFrc*thFrc*val; dbg->wght[bidx ] += (1-rrFrc)*(1-thFrc); dbg->wght[bidx + 1] += rrFrc*(1-thFrc); dbg->wght[bidxPlus ] += (1-rrFrc)*thFrc; dbg->wght[bidxPlus + 1] += rrFrc*thFrc; } else { deringXYtoRT(drc, dbg, xi, yi, &rrIdx, &thIdx, NULL, NULL); thIdx = thIdx % drc->thetaNum; dbg->ptxf[rrIdx + dbg->radNum*thIdx] += dbg->slice[xi + sx*yi]; dbg->wght[rrIdx + dbg->radNum*thIdx] += 1; } } } for (thIdx=0; thIdxthetaNum; thIdx++) { for (rrIdx=0; rrIdxradNum; rrIdx++) { double tmpW; tmpW = dbg->wght[rrIdx + dbg->radNum*thIdx]; if (tmpW) { dbg->ptxf[rrIdx + dbg->radNum*thIdx] /= tmpW; } else { dbg->ptxf[rrIdx + dbg->radNum*thIdx] = AIR_NAN; } } } if (DEBUG) { char fname[AIR_STRLEN_SMALL]; sprintf(fname, "wght-%02u.nrrd", dbg->zi); nrrdSave(fname, dbg->nptxf[WGHT], NULL); } return 0; } static int deringPtxfFilter(NrrdDeringContext *drc, deringBag *dbg, unsigned int zi) { static const char me[]="deringPtxfFilter"; if ((!zi ? 0 : nrrdResampleInputSet(dbg->rsmc[0], dbg->nptxf[ORIG])) || nrrdResampleExecute(dbg->rsmc[0], dbg->nptxf[BLRR]) || nrrdArithBinaryOp(dbg->nptxf[DIFF], nrrdBinaryOpSubtract, dbg->nptxf[ORIG], dbg->nptxf[BLRR])) { biffAddf(NRRD, "%s: trouble with radial blur", me); return 1; } if (!drc->verticalSeam) { if ((!zi ? 0 : nrrdResampleInputSet(dbg->rsmc[1], dbg->nptxf[DIFF])) || nrrdResampleExecute(dbg->rsmc[1], dbg->nptxf[RING])) { biffAddf(NRRD, "%s: trouble", me); return 1; } } else { size_t cmin[3], cmax[3]; ptrdiff_t pmin[3], pmax[3]; cmin[0] = 0; cmin[1] = 1; cmin[2] = 0; pmin[0] = 0; pmin[1] = -1; pmin[2] = 0; pmax[0] = cmax[0] = dbg->radNum - 1; cmax[1] = drc->thetaNum/2 - 1; pmax[1] = drc->thetaNum/2 - 2; /* max sample # of cropped axis 1 */ pmax[2] = cmax[2] = 1; if (nrrdAxesSplit(dbg->nptxf[RSHP], dbg->nptxf[DIFF], 1, drc->thetaNum/2, 2) || nrrdCrop(dbg->nptxf[CROP], dbg->nptxf[RSHP], cmin, cmax) || (!zi ? 0 : nrrdResampleInputSet(dbg->rsmc[1], dbg->nptxf[CROP])) || nrrdResampleExecute(dbg->rsmc[1], dbg->nptxf[CBLR]) || nrrdPad_nva(dbg->nptxf[RSHP], dbg->nptxf[CBLR], pmin, pmax, nrrdBoundaryPad, 0) || nrrdAxesMerge(dbg->nptxf[RING], dbg->nptxf[RSHP], 1)) { biffAddf(NRRD, "%s: trouble with vertical seam", me); return 1; } if (DEBUG) { char fn[AIR_STRLEN_SMALL]; sprintf(fn, "rshp-%02u.nrrd", dbg->zi); nrrdSave(fn, dbg->nptxf[RSHP],NULL); sprintf(fn, "crop-%02u.nrrd", dbg->zi); nrrdSave(fn, dbg->nptxf[CROP],NULL); } } if (DEBUG) { char fn[AIR_STRLEN_SMALL]; sprintf(fn, "orig-%02u.nrrd", dbg->zi); nrrdSave(fn, dbg->nptxf[ORIG],NULL); sprintf(fn, "blrr-%02u.nrrd", dbg->zi); nrrdSave(fn, dbg->nptxf[BLRR],NULL); sprintf(fn, "diff-%02u.nrrd", dbg->zi); nrrdSave(fn, dbg->nptxf[DIFF],NULL); sprintf(fn, "ring-%02u.nrrd", dbg->zi); nrrdSave(fn, dbg->nptxf[RING],NULL); } return 0; } static int deringRingMagMeasure(NrrdDeringContext *drc, deringBag *dbg) { static const char me[]="deringRingMagMeasure"; airArray *mop; Nrrd *ntmp[2]; AIR_UNUSED(drc); mop = airMopNew(); ntmp[0] = nrrdNew(); airMopAdd(mop, ntmp[0], (airMopper)nrrdNuke, airMopAlways); ntmp[1] = nrrdNew(); airMopAdd(mop, ntmp[1], (airMopper)nrrdNuke, airMopAlways); if (nrrdReshape_va(ntmp[0], dbg->nptxf[RING], 2, (dbg->nptxf[RING]->axis[0].size * dbg->nptxf[RING]->axis[1].size), AIR_CAST(size_t, 1)) || nrrdProject(ntmp[1], ntmp[0], 0, nrrdMeasureL2, nrrdTypeDouble)) { biffAddf(NRRD, "%s: trouble", me); airMopError(mop); return 1; } dbg->ringMag = *(AIR_CAST(double *, ntmp[1]->data)); airMopOkay(mop); return 0; } static int deringSubtract(NrrdDeringContext *drc, deringBag *dbg) { /* static const char me[]="deringSubtract"; */ unsigned int sx, sy, xi, yi, rrIdx, thIdx; sx = AIR_CAST(unsigned int, drc->nin->axis[0].size); sy = AIR_CAST(unsigned int, drc->nin->axis[1].size); for (yi=0; yilinearInterp) { unsigned int bidx, bidxPlus; deringXYtoRT(drc, dbg, xi, yi, &rrIdx, &thIdx, &rrFrc, &thFrc); bidx = AIR_UINT(rrIdx + dbg->radNum*thIdx); bidxPlus = AIR_UINT(thIdx < drc->thetaNum-1 ? bidx + dbg->radNum : rrIdx); val = (dbg->ring[bidx ]*(1-rrFrc)*(1-thFrc) + dbg->ring[bidx + 1]*rrFrc*(1-thFrc) + dbg->ring[bidxPlus ]*(1-rrFrc)*thFrc + dbg->ring[bidxPlus + 1]*rrFrc*thFrc); dbg->slice[xi + sx*yi] -= val; } else { deringXYtoRT(drc, dbg, xi, yi, &rrIdx, &thIdx, NULL, NULL); thIdx = thIdx % drc->thetaNum; dbg->slice[xi + sx*yi] -= dbg->ring[rrIdx + dbg->radNum*thIdx]; } } } if (DEBUG) { char fname[AIR_STRLEN_SMALL]; sprintf(fname, "ring2-%02u.nrrd", dbg->zi); nrrdSave(fname, dbg->nptxf[RING], NULL); sprintf(fname, "drng-%02u.nrrd", dbg->zi); nrrdSave(fname, dbg->nslice, NULL); } return 0; } static int deringDo(NrrdDeringContext *drc, deringBag *dbg, Nrrd *nout, unsigned int zi) { static const char me[]="deringDo"; if (deringSliceGet(drc, dbg, zi) || deringPtxfDo(drc, dbg) || deringPtxfFilter(drc, dbg, zi) || deringRingMagMeasure(drc, dbg) || deringSubtract(drc, dbg) || deringSliceSet(drc, dbg, nout, zi)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } static int deringCheck(NrrdDeringContext *drc) { static const char me[]="deringCheck"; if (!(drc->nin)) { biffAddf(NRRD, "%s: no input set", me); return 1; } if (!( AIR_EXISTS(drc->center[0]) && AIR_EXISTS(drc->center[1]) )) { biffAddf(NRRD, "%s: no center set", me); return 1; } if (!(drc->thetaNum)) { biffAddf(NRRD, "%s: no thetaNum set", me); return 1; } if (!( drc->rkernel && drc->tkernel )) { biffAddf(NRRD, "%s: R and T kernels not both set", me); return 1; } if (drc->verticalSeam && !(0 == (drc->thetaNum % 2))) { biffAddf(NRRD, "%s: need even thetaNum (not %u) if wanting verticalSeam", me, drc->thetaNum); return 1; } return 0; } int nrrdDeringExecute(NrrdDeringContext *drc, Nrrd *nout) { static const char me[]="nrrdDeringExecute"; unsigned int sx, sy, sz, zi; double dx, dy, radLen, len; deringBag *dbg; airArray *mop; if (!( drc && nout )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (deringCheck(drc)) { biffAddf(NRRD, "%s: trouble with setup", me); return 1; } /* initialize output */ if (nrrdCopy(nout, drc->nin)) { biffAddf(NRRD, "%s: trouble initializing output with input", me); return 1; } drc->cdataOut = AIR_CAST(char *, nout->data); mop = airMopNew(); /* set radLen: radial length of polar transform of data */ radLen = 0; sx = AIR_CAST(unsigned int, drc->nin->axis[0].size); sy = AIR_CAST(unsigned int, drc->nin->axis[1].size); dx = 0 - drc->center[0]; dy = 0 - drc->center[1]; len = sqrt(dx*dx + dy*dy); radLen = AIR_MAX(radLen, len); dx = sx-1 - drc->center[0]; dy = 0 - drc->center[1]; len = sqrt(dx*dx + dy*dy); radLen = AIR_MAX(radLen, len); dx = sx-1 - drc->center[0]; dy = sy-1 - drc->center[1]; len = sqrt(dx*dx + dy*dy); radLen = AIR_MAX(radLen, len); dx = 0 - drc->center[0]; dy = sy-1 - drc->center[1]; len = sqrt(dx*dx + dy*dy); radLen = AIR_MAX(radLen, len); if (drc->verbose) { fprintf(stderr, "%s: radLen = %g\n", me, radLen); } /* determine clamping, if any */ if (drc->clampPerc[0] > 0.0 || drc->clampPerc[1] > 0.0) { Nrrd *nhist; double *hist, total, sum; unsigned int hi; nhist = nrrdNew(); airMopAdd(mop, nhist, (airMopper)nrrdNuke, airMopAlways); if (nrrdHisto(nhist, drc->nin, NULL, NULL, drc->clampHistoBins, nrrdTypeDouble)) { biffAddf(NRRD, "%s: trouble making histogram", me); return 1; } hist = AIR_CAST(double *, nhist->data); total = AIR_CAST(double, nrrdElementNumber(drc->nin)); sum = 0; for (hi=0; hiclampHistoBins; hi++) { sum += hist[hi]; if (sum >= drc->clampPerc[0]*total/100.0) { drc->clamp[0] = AIR_AFFINE(0, hi, drc->clampHistoBins-1, nhist->axis[0].min, nhist->axis[0].max); break; } } if (hi == drc->clampHistoBins) { biffAddf(NRRD, "%s: failed to find lower %g-percentile value", me, drc->clampPerc[0]); return 1; } sum = 0; for (hi=drc->clampHistoBins; hi; hi--) { sum += hist[hi-1]; if (sum >= drc->clampPerc[1]*total/100.0) { drc->clamp[1] = AIR_AFFINE(0, hi-1, drc->clampHistoBins-1, nhist->axis[0].min, nhist->axis[0].max); break; } } if (!hi) { biffAddf(NRRD, "%s: failed to find upper %g-percentile value", me, drc->clampPerc[1]); return 1; } if (drc->verbose) { fprintf(stderr, "%s: [%g,%g]-%%ile clamping of [%g,%g] --> [%g,%g]\n", me, drc->clampPerc[0], drc->clampPerc[1], nhist->axis[0].min, nhist->axis[0].max, drc->clamp[0], drc->clamp[1]); } drc->clampDo = AIR_TRUE; } else { drc->clamp[0] = drc->clamp[1] = AIR_NAN; drc->clampDo = AIR_FALSE; } /* create deringBag(s) */ dbg = deringBagNew(drc, radLen); airMopAdd(mop, dbg, (airMopper)deringBagNix, airMopAlways); if (deringPtxfAlloc(drc, dbg)) { biffAddf(NRRD, "%s: trouble on setup", me); return 1; } sz = (2 == drc->nin->dim ? 1 : AIR_CAST(unsigned int, drc->nin->axis[2].size)); drc->ringMagnitude = 0.0; for (zi=0; ziverbose) { fprintf(stderr, "%s: slice %u of %u ...\n", me, zi, sz); } if (deringDo(drc, dbg, nout, zi)) { biffAddf(NRRD, "%s: trouble on slice %u", me, zi); return 1; } drc->ringMagnitude += dbg->ringMag; if (drc->verbose) { fprintf(stderr, "%s: ... %u done\n", me, zi); } } if (drc->verbose) { fprintf(stderr, "%s: ring magnitude = %g\n", me, drc->ringMagnitude); } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/nrrd/resampleContext.c0000664000175000017500000015017312174423545020700 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ** This is a largely a re-write of the functionality in ** nrrdSpatialResample(), but with some improvements. The big API ** change is that everything happens in a nrrdResampleContext, and no ** fields in this need to be directly set (except for rsmc->verbose). ** ** The big behavior/API change is that the range along the axis that ** is resampled is now defined in terms of index space, instead of ** axis-aligned scaled index space (what used to be called "world ** space", prior to general orientation). This means that if you ** want the whole range resampled, you use [-0.5,size-0.5] for cell- ** centered, and [0,size-1] for node-centered. One function helpful ** dealing with this is nrrdResampleRangeFullSet(). ** ** Other improvements: ** -- ability to quickly resample a different nrrd with the same ** sizes and kernels as with a previous (all useful state and ** allocations of intermediate resampling results is preserved ** in the nrrdResampleContext). To resample a second nrrd, ** you'd only need: ** nrrdResampleInputSet(rsmc, nin2); ** nrrdResampleExecute(rsmc, nout2); ** -- correct handling general orientation (space directions and ** space origin). This was impossible in the old resampler ** because of how its logic was hard-wired to the axis-aligned ** world space defined by the per-axis min and max. ** -- correct handling of "cheap" downsampling, with the use of ** the new nrrdKernelCheap ** -- smaller memory footprint (smarter about freeing intermediate ** resampling results) */ enum { flagUnknown, /* 0 */ flagDefaultCenter, /* 1 */ flagInput, /* 2 */ flagOverrideCenters, /* 3 */ flagInputDimension, /* 4 */ flagInputCenters, /* 5 */ flagInputSizes, /* 6 */ flagKernels, /* 7 */ flagSamples, /* 8 */ flagRanges, /* 9 */ flagBoundary, /* 10 */ flagLineAllocate, /* 11 */ flagLineFill, /* 12 */ flagVectorAllocate, /* 13 */ flagPermutation, /* 14 */ flagVectorFill, /* 15 */ flagClamp, /* 16 */ flagRound, /* 17 */ flagTypeOut, /* 18 */ flagPadValue, /* 19 */ flagRenormalize, /* 20 */ flagNonExistent, /* 21 */ flagLast }; #define FLAG_MAX 21 void nrrdResampleContextInit(NrrdResampleContext *rsmc) { unsigned int axIdx, axJdx, kpIdx, flagIdx; NrrdResampleAxis *axis; if (rsmc) { rsmc->nin = NULL; rsmc->boundary = nrrdDefaultResampleBoundary; rsmc->typeOut = nrrdDefaultResampleType; rsmc->renormalize = nrrdDefaultResampleRenormalize; rsmc->roundlast = nrrdDefaultResampleRound; rsmc->clamp = nrrdDefaultResampleClamp; rsmc->defaultCenter = nrrdDefaultCenter; rsmc->nonExistent = nrrdDefaultResampleNonExistent; rsmc->padValue = nrrdDefaultResamplePadValue; rsmc->dim = 0; rsmc->passNum = AIR_CAST(unsigned int, -1); /* 4294967295 */ rsmc->topRax = AIR_CAST(unsigned int, -1); rsmc->botRax = AIR_CAST(unsigned int, -1); for (axIdx=0; axIdxpermute[axIdx] = AIR_CAST(unsigned int, -1); rsmc->passAxis[axIdx] = AIR_CAST(unsigned int, -1); } for (axIdx=0; axIdxaxis + axIdx; axis->kernel = NULL; axis->kparm[0] = nrrdDefaultKernelParm0; for (kpIdx=1; kpIdxkparm[kpIdx] = AIR_NAN; } axis->min = axis->max = AIR_NAN; axis->samples = AIR_CAST(unsigned int, -1); axis->overrideCenter = nrrdCenterUnknown; axis->center = nrrdCenterUnknown; axis->sizeIn = AIR_CAST(unsigned int, -1); axis->axIdx = axIdx; /* never changes */ axis->passIdx = AIR_CAST(unsigned int, -1); for (axJdx=0; axJdxsizePerm[axJdx] = AIR_CAST(size_t, -1); axis->axisPerm[axJdx] = AIR_CAST(unsigned int, -1); } axis->ratio = AIR_NAN; axis->nrsmp = NULL; /* these are nrrdNew()'d as needed */ axis->nline = nrrdNew(); axis->nindex = nrrdNew(); axis->nweight = nrrdNew(); } /* initialize flags to all true */ for (flagIdx=0; flagIdx<=FLAG_MAX; flagIdx++) { rsmc->flag[flagIdx] = AIR_TRUE; } rsmc->time = 0.0; } return; } NrrdResampleContext * nrrdResampleContextNew() { NrrdResampleContext *rsmc; rsmc = AIR_CALLOC(1, NrrdResampleContext); if (rsmc) { rsmc->flag = AIR_CALLOC(1+FLAG_MAX, int); nrrdResampleContextInit(rsmc); } return rsmc; } NrrdResampleContext * nrrdResampleContextNix(NrrdResampleContext *rsmc) { unsigned int axIdx; if (rsmc) { for (axIdx=0; axIdxaxis[axIdx].nline); nrrdNuke(rsmc->axis[axIdx].nindex); nrrdNuke(rsmc->axis[axIdx].nweight); } airFree(rsmc->flag); airFree(rsmc); } return NULL; } int nrrdResampleDefaultCenterSet(NrrdResampleContext *rsmc, int center) { static const char me[]="nrrdResampleDefaultCenterSet"; if (!( rsmc )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!(nrrdCenterNode == center || nrrdCenterCell == center)) { biffAddf(NRRD, "%s: got invalid center (%d)", me, center); return 1; } if (center != rsmc->defaultCenter) { rsmc->defaultCenter = center; rsmc->flag[flagDefaultCenter] = AIR_TRUE; } return 0; } int nrrdResampleNonExistentSet(NrrdResampleContext *rsmc, int nonExist) { static const char me[]="nrrdResampleNonExistentSet"; if (!( rsmc )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(nrrdResampleNonExistent, nonExist)) { biffAddf(NRRD, "%s: didn't get valid non-existent behavior (%d)", me, nonExist); return 1; } if (nonExist != rsmc->nonExistent) { rsmc->nonExistent = nonExist; rsmc->flag[flagNonExistent] = AIR_TRUE; } return 0; } #define NRRD_RESAMPLE_INPUT_SET_BODY \ unsigned int axIdx, kpIdx; \ \ if (!( rsmc && nin )) { \ biffAddf(NRRD, "%s: got NULL pointer", me); \ return 1; \ } \ if (nrrdCheck(nin)) { \ biffAddf(NRRD, "%s: problems with given nrrd", me); \ return 1; \ } \ if (nrrdTypeBlock == nin->type) { \ biffAddf(NRRD, "%s: can't resample from type %s", me, \ airEnumStr(nrrdType, nrrdTypeBlock)); \ return 1; \ } \ \ rsmc->nin = nin; \ rsmc->flag[flagInput] = AIR_TRUE; \ \ /* per-axis information should be invalidated at this point, because \ if we defer the invalidation to later ...Update() calls, it will \ clobber the effects of intervening calls to the likes of \ ...KernelSet(), ...SampleSet(), and so on */ \ if (rsmc->dim != nin->dim) { \ for (axIdx=0; axIdxaxis[axIdx].center = nrrdCenterUnknown; \ rsmc->axis[axIdx].sizeIn = 0; \ rsmc->axis[axIdx].kernel = NULL; \ rsmc->axis[axIdx].kparm[0] = nrrdDefaultKernelParm0; \ for (kpIdx=1; kpIdxaxis[axIdx].kparm[kpIdx] = AIR_NAN; \ } \ rsmc->axis[axIdx].samples = 0; \ rsmc->axis[axIdx].min = AIR_NAN; \ rsmc->axis[axIdx].max = AIR_NAN; \ } \ } \ \ return 0 int nrrdResampleNrrdSet(NrrdResampleContext *rsmc, const Nrrd *nin) { static const char me[]="nrrdResampleNrrdSet"; NRRD_RESAMPLE_INPUT_SET_BODY; } int nrrdResampleInputSet(NrrdResampleContext *rsmc, const Nrrd *nin) { static const char me[]="nrrdResampleInputSet"; NRRD_RESAMPLE_INPUT_SET_BODY; } #define PER_AXIS_ERROR_CHECK \ if (!rsmc) { \ biffAddf(NRRD, "%s: got NULL pointer", me); \ return 1; \ } \ if (!rsmc->nin) { \ biffAddf(NRRD, "%s: haven't set input nrrd yet", me); \ return 1; \ } \ if (!( axIdx < rsmc->nin->dim )) { \ biffAddf(NRRD, "%s: axis %u >= nin->dim %u", \ me, axIdx, rsmc->nin->dim); \ return 1; \ } int nrrdResampleKernelSet(NrrdResampleContext *rsmc, unsigned int axIdx, const NrrdKernel *kernel, double kparm[NRRD_KERNEL_PARMS_NUM]) { static const char me[]="nrrdResampleKernelSet"; unsigned int kpIdx; PER_AXIS_ERROR_CHECK; rsmc->axis[axIdx].kernel = kernel; if (kernel) { for (kpIdx=0; kpIdxnumParm; kpIdx++) { rsmc->axis[axIdx].kparm[kpIdx] = kparm[kpIdx]; } if (rsmc->verbose) { char kstr[AIR_STRLEN_LARGE]; NrrdKernelSpec ksp; nrrdKernelSpecSet(&ksp, rsmc->axis[axIdx].kernel, rsmc->axis[axIdx].kparm); nrrdKernelSpecSprint(kstr, &ksp); fprintf(stderr, "%s: axis %u kernel %s\n", me, axIdx, kstr); } } rsmc->flag[flagKernels] = AIR_TRUE; return 0; } int nrrdResampleSamplesSet(NrrdResampleContext *rsmc, unsigned int axIdx, size_t samples) { static const char me[]="nrrdResampleSamplesSet"; PER_AXIS_ERROR_CHECK; if (rsmc->axis[axIdx].samples != samples) { if (rsmc->verbose) { fprintf(stderr, "%s: axis %u samples %u --> %u\n", me, axIdx, AIR_CAST(unsigned int, rsmc->axis[axIdx].samples), AIR_CAST(unsigned int, samples)); } rsmc->axis[axIdx].samples = samples; rsmc->flag[flagSamples] = AIR_TRUE; } return 0; } int nrrdResampleRangeSet(NrrdResampleContext *rsmc, unsigned int axIdx, double min, double max) { static const char me[]="nrrdResampleRangeSet"; PER_AXIS_ERROR_CHECK; if (!(AIR_EXISTS(min) && AIR_EXISTS(max) && min != max)) { biffAddf(NRRD, "%s: need min != max and both to exist", me); return 1; } if (!(rsmc->axis[axIdx].min == min && rsmc->axis[axIdx].max == max)) { rsmc->axis[axIdx].min = min; rsmc->axis[axIdx].max = max; rsmc->flag[flagRanges] = AIR_TRUE; } return 0; } int nrrdResampleOverrideCenterSet(NrrdResampleContext *rsmc, unsigned int axIdx, int center) { static const char me[]="nrrdResampleOverrideCenterSet"; PER_AXIS_ERROR_CHECK; if (center) { /* we do allow passing nrrdCenterUnknown, to turn off override */ if (airEnumValCheck(nrrdCenter, center)) { biffAddf(NRRD, "%s: didn't get valid centering (%d)", me, center); return 1; } } if (center != rsmc->axis[axIdx].overrideCenter) { rsmc->axis[axIdx].overrideCenter = center; rsmc->flag[flagOverrideCenters] = AIR_TRUE; } return 0; } void _nrrdResampleMinMaxFull(double *minP, double *maxP, int center, size_t size) { if (nrrdCenterCell == center) { *minP = -0.5; *maxP = size - 0.5; } else { *minP = 0.0; *maxP = size - 1.0; } } int nrrdResampleRangeFullSet(NrrdResampleContext *rsmc, unsigned int axIdx) { static const char me[]="nrrdResampleRangeFullSet"; double min, max; int center; PER_AXIS_ERROR_CHECK; /* HEY trick is to figure out the axis's centering, and to make sure its the same code as used elsewhere */ center = (rsmc->axis[axIdx].overrideCenter ? rsmc->axis[axIdx].overrideCenter : (rsmc->nin->axis[axIdx].center ? rsmc->nin->axis[axIdx].center : rsmc->defaultCenter)); _nrrdResampleMinMaxFull(&min, &max, center, rsmc->nin->axis[axIdx].size); if (!(rsmc->axis[axIdx].min == min && rsmc->axis[axIdx].max == max)) { rsmc->axis[axIdx].min = min; rsmc->axis[axIdx].max = max; rsmc->flag[flagRanges] = AIR_TRUE; } return 0; } int nrrdResampleBoundarySet(NrrdResampleContext *rsmc, int boundary) { static const char me[]="nrrdResampleBoundarySet"; if (!rsmc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(nrrdBoundary, boundary)) { biffAddf(NRRD, "%s: invalid boundary %d", me, boundary); return 1; } if (rsmc->boundary != boundary) { rsmc->boundary = boundary; rsmc->flag[flagBoundary] = AIR_TRUE; } return 0; } int nrrdResamplePadValueSet(NrrdResampleContext *rsmc, double padValue) { static const char me[]="nrrdResamplePadValueSet"; if (!rsmc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (rsmc->padValue != padValue) { rsmc->padValue = padValue; rsmc->flag[flagPadValue] = AIR_TRUE; } return 0; } int nrrdResampleRenormalizeSet(NrrdResampleContext *rsmc, int renormalize) { static const char me[]="nrrdResampleRenormalizeSet"; if (!rsmc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (rsmc->renormalize != renormalize) { rsmc->renormalize = renormalize; rsmc->flag[flagRenormalize] = AIR_TRUE; } return 0; } int nrrdResampleTypeOutSet(NrrdResampleContext *rsmc, int type) { static const char me[]="nrrdResampleTypeOutSet"; if (!rsmc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdTypeDefault != type && airEnumValCheck(nrrdType, type)) { biffAddf(NRRD, "%s: invalid type %d", me, type); return 1; } if (nrrdTypeBlock == type) { biffAddf(NRRD, "%s: can't output %s type", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (rsmc->typeOut != type) { rsmc->typeOut = type; rsmc->flag[flagTypeOut] = AIR_TRUE; } return 0; } int nrrdResampleRoundSet(NrrdResampleContext *rsmc, int roundlast) { static const char me[]="nrrdResampleRoundSet"; if (!rsmc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (rsmc->roundlast != roundlast) { rsmc->roundlast = roundlast; rsmc->flag[flagRound] = AIR_TRUE; } return 0; } int nrrdResampleClampSet(NrrdResampleContext *rsmc, int clamp) { static const char me[]="nrrdResampleClampSet"; if (!rsmc) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (rsmc->clamp != clamp) { rsmc->clamp = clamp; rsmc->flag[flagClamp] = AIR_TRUE; } return 0; } int _nrrdResampleInputDimensionUpdate(NrrdResampleContext *rsmc) { if (rsmc->flag[flagInput]) { if (rsmc->dim != rsmc->nin->dim) { rsmc->dim = rsmc->nin->dim; rsmc->flag[flagInputDimension] = AIR_TRUE; } } return 0; } int _nrrdResampleInputCentersUpdate(NrrdResampleContext *rsmc) { unsigned int axIdx; int center; if (rsmc->flag[flagOverrideCenters] || rsmc->flag[flagDefaultCenter] || rsmc->flag[flagInputDimension] || rsmc->flag[flagInput]) { for (axIdx=0; axIdxaxis[axIdx].overrideCenter ? rsmc->axis[axIdx].overrideCenter : (rsmc->nin->axis[axIdx].center ? rsmc->nin->axis[axIdx].center : rsmc->defaultCenter)); if (rsmc->axis[axIdx].center != center) { rsmc->axis[axIdx].center = center; rsmc->flag[flagInputCenters] = AIR_TRUE; } } rsmc->flag[flagOverrideCenters] = AIR_FALSE; rsmc->flag[flagDefaultCenter] = AIR_FALSE; } return 0; } int _nrrdResampleInputSizesUpdate(NrrdResampleContext *rsmc) { unsigned int axIdx; if (rsmc->flag[flagInputDimension] || rsmc->flag[flagInput]) { for (axIdx=0; axIdxdim; axIdx++) { if (rsmc->axis[axIdx].sizeIn != rsmc->nin->axis[axIdx].size) { rsmc->axis[axIdx].sizeIn = rsmc->nin->axis[axIdx].size; rsmc->flag[flagInputSizes] = AIR_TRUE; } } rsmc->flag[flagInputDimension] = AIR_FALSE; } return 0; } int _nrrdResampleLineAllocateUpdate(NrrdResampleContext *rsmc) { static const char me[]="_nrrdResampleLineAllocateUpdate"; unsigned int axIdx; NrrdResampleAxis *axis; if (rsmc->flag[flagInputSizes] || rsmc->flag[flagKernels]) { for (axIdx=0; axIdxdim; axIdx++) { axis = rsmc->axis + axIdx; if (!axis->kernel) { nrrdEmpty(axis->nline); } else { if (nrrdMaybeAlloc_va(axis->nline, nrrdResample_nt, 1, AIR_CAST(size_t, 1 + axis->sizeIn))) { biffAddf(NRRD, "%s: couldn't allocate scanline buffer", me); return 1; } } } rsmc->flag[flagLineAllocate] = AIR_TRUE; } return 0; } int _nrrdResampleVectorAllocateUpdate(NrrdResampleContext *rsmc) { static const char me[]="_nrrdResampleVectorAllocateUpdate"; unsigned int axIdx, kpIdx, dotLen, minSamples; nrrdResample_t spacingOut, support; NrrdResampleAxis *axis; if (rsmc->flag[flagKernels] || rsmc->flag[flagSamples] || rsmc->flag[flagRanges]) { for (axIdx=0; axIdxdim; axIdx++) { axis = rsmc->axis + axIdx; if (!axis->kernel) { /* no resampling on this axis */ continue; } /* check user-set parameters */ if (!( AIR_EXISTS(axis->min) && AIR_EXISTS(axis->max) )) { biffAddf(NRRD, "%s: don't have min, max set on axis %u", me, axIdx); return 1; } for (kpIdx=0; kpIdxkernel->numParm; kpIdx++) { if (!AIR_EXISTS(axis->kparm[kpIdx])) { biffAddf(NRRD, "%s: didn't set kernel parm %u on axis %u", me, kpIdx, axIdx); return 1; } } minSamples = (nrrdCenterCell == axis->center ? 1 : 2); if (!( axis->samples >= minSamples )) { biffAddf(NRRD, "%s: need at least %u output samples (not %u) for " "%s-centered sampling along axis %u", me, minSamples, AIR_CAST(unsigned int, axis->samples), airEnumStr(nrrdCenter, axis->center), axIdx); return 1; } /* compute support (spacingIn == 1.0 by definition) */ spacingOut = AIR_CAST(nrrdResample_t, ((axis->max - axis->min) / (nrrdCenterCell == axis->center ? axis->samples : axis->samples - 1))); axis->ratio = 1.0/spacingOut; support = AIR_CAST(nrrdResample_t, axis->kernel->support(axis->kparm)); if (axis->ratio > 1) { /* if upsampling, we need only as many samples as needed for interpolation with the given kernel */ dotLen = (int)(2*ceil(support)); } else { /* if downsampling, we need to use all the samples covered by the stretched out version of the kernel */ dotLen = (int)(2*ceil(support/axis->ratio)); } /* some kernels can report zero support when they're basically delta functions */ dotLen = AIR_MAX(2, dotLen); if (nrrdMaybeAlloc_va(axis->nweight, nrrdResample_nt, 2, AIR_CAST(size_t, dotLen), AIR_CAST(size_t, axis->samples)) || nrrdMaybeAlloc_va(axis->nindex, nrrdTypeInt, 2, AIR_CAST(size_t, dotLen), AIR_CAST(size_t, axis->samples))) { biffAddf(NRRD, "%s: trouble allocating index and weighting vectors", me); return 1; } } rsmc->flag[flagRanges] = AIR_FALSE; rsmc->flag[flagVectorAllocate] = AIR_TRUE; } return 0; } int _nrrdResampleLineFillUpdate(NrrdResampleContext *rsmc) { unsigned int axIdx; NrrdResampleAxis *axis; nrrdResample_t *line; if (rsmc->flag[flagPadValue] || rsmc->flag[flagLineAllocate]) { for (axIdx=0; axIdxdim; axIdx++) { axis = rsmc->axis + axIdx; if (axis->kernel) { line = (nrrdResample_t*)(axis->nline->data); line[axis->sizeIn] = AIR_CAST(nrrdResample_t, rsmc->padValue); } } rsmc->flag[flagPadValue] = AIR_FALSE; rsmc->flag[flagLineAllocate] = AIR_FALSE; rsmc->flag[flagLineFill] = AIR_TRUE; } return 0; } int _nrrdResampleVectorFillUpdate(NrrdResampleContext *rsmc) { static const char me[]="_nrrdResampleVectorFillUpdate"; unsigned int axIdx, dotIdx, dotLen, halfLen, smpIdx, kpIdx; int *indexData, tmp, base, rawIdx; nrrdResample_t *weightData, idx, integral; NrrdResampleAxis *axis; double kparm[NRRD_KERNEL_PARMS_NUM]; if (rsmc->flag[flagRenormalize] || rsmc->flag[flagBoundary] || rsmc->flag[flagInputCenters] || rsmc->flag[flagInputSizes] || rsmc->flag[flagVectorAllocate]) { if (rsmc->verbose) { for (axIdx=0; axIdxdim; axIdx++) { if (rsmc->axis[axIdx].kernel) { fprintf(stderr, "%s: axis %u: %s-centering\n", me, axIdx, airEnumStr(nrrdCenter, rsmc->axis[axIdx].center)); } } } for (axIdx=0; axIdxdim; axIdx++) { axis = rsmc->axis + axIdx; if (!axis->kernel) { /* no resampling on this axis */ continue; } /* calculate sample locations and do first pass on indices */ indexData = (int *)axis->nindex->data; weightData = (nrrdResample_t *)axis->nweight->data; dotLen = AIR_CAST(unsigned int, axis->nweight->axis[0].size); halfLen = dotLen/2; for (smpIdx=0; smpIdxsamples; smpIdx++) { idx = AIR_CAST(nrrdResample_t, (nrrdCenterCell == axis->center ? AIR_AFFINE(-0.5, smpIdx, axis->samples-0.5, axis->min, axis->max) : AIR_AFFINE(0.0, smpIdx, axis->samples-1.0, axis->min, axis->max))); base = (int)floor(idx) - halfLen + 1; for (dotIdx=0; dotIdxverbose) { if (!smpIdx) { fprintf(stderr, "%s: smpIdx=%u -> idx=%g -> base=%d\n", me, smpIdx, idx, base); fprintf(stderr, "%s: sample locations:\n", me); } fprintf(stderr, "%s: %d (sample locations)\n ", me, smpIdx); for (dotIdx=0; dotIdxsamples; dotIdx++) { rawIdx = indexData[dotIdx]; if (!AIR_IN_CL(0, rawIdx, AIR_CAST(int, axis->sizeIn)-1)) { switch(rsmc->boundary) { case nrrdBoundaryPad: case nrrdBoundaryWeight: /* this will be further handled later */ rawIdx = AIR_CAST(int, axis->sizeIn); break; case nrrdBoundaryBleed: rawIdx = AIR_CLAMP(0, rawIdx, AIR_CAST(int, axis->sizeIn)-1); break; case nrrdBoundaryWrap: rawIdx = AIR_MOD(rawIdx, AIR_CAST(int, axis->sizeIn)); break; case nrrdBoundaryMirror: rawIdx = _nrrdMirror_32(AIR_CAST(unsigned int, axis->sizeIn), rawIdx); break; default: biffAddf(NRRD, "%s: boundary behavior %d unknown/unimplemented", me, rsmc->boundary); return 0; } indexData[dotIdx] = rawIdx; } } /* Wow - there is a big rift here between old conventions for how NrrdKernels were defined, versus the newer practice of creating parameter-free kernels. The "sneaky trick" code below for changing parm[0] only works if the kernel actually looks at parm[0]! So at least for the parameter-free kernels (and maybe other kernels, but there's no principled way of knowing!) we have to do what we probably should have been done all along: simulating the kernel scaling by pre-processing the evaluation locations and post-processing the kernel weights */ if (0 == axis->kernel->numParm) { size_t nn, ii; double ratio; nn = dotLen*axis->samples; ratio = axis->ratio; if (ratio < 1) { for (ii=0; iikernel->EVALN(weightData, weightData, nn, axis->kparm); if (ratio < 1) { for (ii=0; iiratio < 1 ? axis->kparm[0] / axis->ratio : axis->kparm[0]); for (kpIdx=1; kpIdxkparm[kpIdx]; } axis->kernel->EVALN(weightData, weightData, dotLen*axis->samples, kparm); /* special handling of "cheap" kernel */ if (nrrdKernelCheap == axis->kernel) { for (smpIdx=0; smpIdxsamples; smpIdx++) { nrrdResample_t dist, minDist; int minIdx, minSet; minIdx = indexData[0 + dotLen*smpIdx]; minDist = weightData[0 + dotLen*smpIdx]; /* find sample closest to origin */ for (dotIdx=1; dotIdxverbose) { fprintf(stderr, "%s: axis %u sample weights:\n", me, axIdx); for (smpIdx=0; smpIdxsamples; smpIdx++) { fprintf(stderr, "%s: %d (sample weights)\n ", me, smpIdx); for (dotIdx=0; dotIdxkernel->integral(axis->kparm)); if (nrrdBoundaryWeight == rsmc->boundary) { if (integral) { /* above, we set to axis->sizeIn all the indices that were out of range. We now use that to determine the sum of the weights for the indices that were in-range */ for (smpIdx=0; smpIdxsamples; smpIdx++) { nrrdResample_t wght = 0; for (dotIdx=0; dotIdxsizeIn) != indexData[dotIdx + dotLen*smpIdx]) { wght += weightData[dotIdx + dotLen*smpIdx]; } } for (dotIdx=0; dotIdxsizeIn) != indexData[dotIdx + dotLen*smpIdx]) { weightData[dotIdx + dotLen*smpIdx] *= integral/wght; } else { weightData[dotIdx + dotLen*smpIdx] = 0; } } } } } else { /* try to remove ripple/grating on downsampling, and errors in weighting on upsampling when using kernels that are not first-order accurate */ if (rsmc->renormalize && integral) { for (smpIdx=0; smpIdxsamples; smpIdx++) { nrrdResample_t wght = 0; for (dotIdx=0; dotIdxverbose) { fprintf(stderr, "%s: axis %u post-correction sample weights:\n", me, axIdx); for (smpIdx=0; smpIdxsamples; smpIdx++) { fprintf(stderr, "%s: %d (sample weights)\n ", me, smpIdx); for (dotIdx=0; dotIdxflag[flagRenormalize] = AIR_FALSE; rsmc->flag[flagBoundary] = AIR_FALSE; rsmc->flag[flagInputCenters] = AIR_FALSE; rsmc->flag[flagVectorAllocate] = AIR_FALSE; rsmc->flag[flagVectorFill] = AIR_TRUE; } return 0; } int _nrrdResamplePermutationUpdate(NrrdResampleContext *rsmc) { static const char me[]="_nrrdResamplePermutationUpdate"; unsigned int axIdx, passIdx, currTop, lastTop, fromTop, toTop; int bi; if (rsmc->flag[flagInputSizes] || rsmc->flag[flagKernels] || rsmc->flag[flagSamples]) { rsmc->topRax = rsmc->botRax = AIR_CAST(unsigned int, -1); for (axIdx=0; axIdxdim; axIdx++) { if (rsmc->axis[axIdx].kernel) { if (AIR_CAST(unsigned int, -1) == rsmc->topRax) { rsmc->topRax = axIdx; } rsmc->botRax = axIdx; } } if (rsmc->verbose) { fprintf(stderr, "%s: topRax = %u (%d); botRax = %u (%d)\n", me, rsmc->topRax, AIR_CAST(int, rsmc->topRax), rsmc->botRax, AIR_CAST(int, rsmc->botRax)); } /* figure out total number of passes needed, and construct the permute[] array. permute[i] = j means that the axis in position i of the old array will be in position j of the new one (permute[] answers "where do I put this", not "what got put here"). */ rsmc->passNum = 0; bi = 0; for (axIdx=0; axIdxdim; axIdx++) { if (rsmc->axis[axIdx].kernel) { do { bi = AIR_MOD(bi+1, AIR_CAST(int, rsmc->dim)); } while (!rsmc->axis[bi].kernel); rsmc->permute[bi] = axIdx; rsmc->passNum += 1; } else { rsmc->permute[axIdx] = axIdx; bi += bi == AIR_CAST(int, axIdx); } } rsmc->permute[rsmc->dim] = rsmc->dim; /* HEY: what is this for? */ if (rsmc->passNum) { toTop = AIR_CAST(unsigned int, -1); for (axIdx=0; axIdxdim; axIdx++) { /* this will always "break" somewhere */ if (rsmc->topRax == rsmc->permute[axIdx]) { toTop = axIdx; break; } } fromTop = rsmc->permute[rsmc->topRax]; if (rsmc->verbose) { fprintf(stderr, "%s: passNum = %u; permute =\n ", me, rsmc->passNum); for (axIdx=0; axIdxdim; axIdx++) { fprintf(stderr, "%u ", rsmc->permute[axIdx]); } fprintf(stderr, "\n"); fprintf(stderr, "%s: toTop = %u; fromTop = %u\n", me, toTop, fromTop); } /* create array of how the axes will be arranged in each pass ("ax"), and create array of how big each axes is in each pass ("sz"). The input to pass i will have axis layout described in ax[i] and axis sizes described in sz[i] */ passIdx = 0; currTop = rsmc->topRax; rsmc->passAxis[passIdx] = currTop; rsmc->axis[currTop].passIdx = passIdx; for (axIdx=0; axIdxdim; axIdx++) { rsmc->axis[currTop].axisPerm[axIdx] = axIdx; rsmc->axis[currTop].sizePerm[axIdx] = rsmc->axis[axIdx].sizeIn; } for (passIdx=1; passIdxpassNum+1; passIdx++) { lastTop = currTop; currTop = (passIdxpassNum ? rsmc->axis[currTop].axisPerm[toTop] : NRRD_DIM_MAX); rsmc->passAxis[passIdx] = currTop; rsmc->axis[currTop].passIdx = passIdx; for (axIdx=0; axIdxdim; axIdx++) { rsmc->axis[currTop].axisPerm[rsmc->permute[axIdx]] = rsmc->axis[lastTop].axisPerm[axIdx]; rsmc->axis[currTop].sizePerm[rsmc->permute[axIdx]] = rsmc->axis[lastTop].sizePerm[axIdx]; /* modify the one size corresponding to the resampled axis */ rsmc->axis[currTop].sizePerm[fromTop] = rsmc->axis[lastTop].samples; } } if (rsmc->verbose) { NrrdResampleAxis *axis; fprintf(stderr, "%s: axis and size permutations:\n", me); for (passIdx=0; passIdxpassNum+1; passIdx++) { axis = rsmc->axis + rsmc->passAxis[passIdx]; fprintf(stderr, "----- pass[%u=?=%u] @ %u %s:\n", passIdx, axis->passIdx, rsmc->passAxis[passIdx], (passIdxpassNum ? "" : "(output of final pass)")); if (!passIdx) { fprintf(stderr, "resampling: "); for (axIdx=0; axIdxdim; axIdx++) { fprintf(stderr, "%s ", rsmc->axis[axIdx].kernel ? " XX" : " "); } fprintf(stderr, "\n"); } fprintf(stderr, " axes: "); for (axIdx=0; axIdxdim; axIdx++) { fprintf(stderr, "%3u ", axis->axisPerm[axIdx]); } fprintf(stderr, "\n"); fprintf(stderr, " sizes: "); for (axIdx=0; axIdxdim; axIdx++) { fprintf(stderr, "%3u ", AIR_CAST(unsigned int, axis->sizePerm[axIdx])); } fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } } rsmc->flag[flagInputSizes] = AIR_FALSE; rsmc->flag[flagKernels] = AIR_FALSE; rsmc->flag[flagSamples] = AIR_FALSE; rsmc->flag[flagPermutation] = AIR_TRUE; } return 0; } /* Copy input to output, but with the optional clamping and rounding */ int _nrrdResampleTrivial(NrrdResampleContext *rsmc, Nrrd *nout, int typeOut, int doRound, nrrdResample_t (*lup)(const void *, size_t), nrrdResample_t (*clamp)(nrrdResample_t), nrrdResample_t (*ins)(void *, size_t, nrrdResample_t)) { static const char me[]="_nrrdResampleTrivial"; size_t size[NRRD_DIM_MAX], valNum, valIdx; nrrdResample_t val; const void *dataIn; void *dataOut; nrrdAxisInfoGet_nva(rsmc->nin, nrrdAxisInfoSize, size); if (nrrdMaybeAlloc_nva(nout, typeOut, rsmc->nin->dim, size)) { biffAddf(NRRD, "%s: couldn't allocate output", me); return 1; } valNum = nrrdElementNumber(rsmc->nin); dataIn = rsmc->nin->data; dataOut = nout->data; for (valIdx=0; valIdxclamp) { val = clamp(val); } ins(dataOut, valIdx, val); } return 0; } int _nrrdResampleCore(NrrdResampleContext *rsmc, Nrrd *nout, int typeOut, int doRound, nrrdResample_t (*lup)(const void *, size_t), nrrdResample_t (*clamp)(nrrdResample_t), nrrdResample_t (*ins)(void *, size_t, nrrdResample_t)) { static const char me[]="_nrrdResampleCore"; unsigned int axIdx, passIdx; size_t strideIn, strideOut, lineNum, lineIdx, coordIn[NRRD_DIM_MAX], coordOut[NRRD_DIM_MAX]; nrrdResample_t *line, *weight, *rsmpIn, *rsmpOut; int *indx; const void *dataIn; void *dataOut; NrrdResampleAxis *axisIn, *axisOut; airArray *mop; /* NOTE: there was an odd memory leak here with normal operation (no errors), because the final airMopOkay() was missing, but quick attempts at resolving it pre-Teem-1.9 release were not successful (surprisingly, commenting out the airMopSub's led to a segfault). So, the airMopAdd which is supposed to manage the per-axis resampling result is commented out, and there are no leaks and no segfaults with normal operation, which is good enough for now */ /* compute strideIn; this is constant across passes because all passes resample topRax, and axes with lower indices have constant length. */ strideIn = 1; for (axIdx=0; axIdxtopRax; axIdx++) { strideIn *= rsmc->axis[axIdx].sizeIn; } mop = airMopNew(); for (passIdx=0; passIdxpassNum; passIdx++) { if (rsmc->verbose) { fprintf(stderr, "%s: -------------- pass %u/%u \n", me, passIdx, rsmc->passNum); } /* calculate pass-specific size, stride, and number info */ axisIn = rsmc->axis + rsmc->passAxis[passIdx]; axisOut = rsmc->axis + rsmc->passAxis[passIdx+1]; lineNum = strideOut = 1; for (axIdx=0; axIdxdim; axIdx++) { if (axIdx < rsmc->botRax) { strideOut *= axisOut->sizePerm[axIdx]; } if (axIdx != rsmc->topRax) { lineNum *= axisIn->sizePerm[axIdx]; } } if (rsmc->verbose) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; fprintf(stderr, "%s(%u): lineNum = %s\n", me, passIdx, airSprintSize_t(stmp1, lineNum)); fprintf(stderr, "%s(%u): strideIn = %s, strideOut = %s\n", me, passIdx, airSprintSize_t(stmp1, strideIn), airSprintSize_t(stmp2, strideOut)); } /* allocate output for this pass */ if (passIdx < rsmc->passNum-1) { axisOut->nrsmp = nrrdNew(); /* see NOTE above! airMopAdd(mop, axisOut->nrsmp, (airMopper)nrrdNuke, airMopAlways); */ if (nrrdMaybeAlloc_nva(axisOut->nrsmp, nrrdResample_nt, rsmc->dim, axisOut->sizePerm)) { biffAddf(NRRD, "%s: trouble allocating output of pass %u", me, passIdx); airMopError(mop); return 1; } if (rsmc->verbose) { fprintf(stderr, "%s: allocated pass %u/%u output nrrd @ %p/%p " "(on axis %u)\n", me, passIdx, axisIn->passIdx, AIR_CAST(void*, axisOut->nrsmp), AIR_CAST(void*, axisOut->nrsmp->data), axisOut->axIdx); } } else { if (nrrdMaybeAlloc_nva(nout, typeOut, rsmc->dim, axisOut->sizePerm)) { biffAddf(NRRD, "%s: trouble allocating final output", me); airMopError(mop); return 1; } if (rsmc->verbose) { fprintf(stderr, "%s: allocated final pass %u output nrrd @ %p/%p\n", me, passIdx, AIR_CAST(void*, nout), AIR_CAST(void*, nout->data)); } } /* set up data pointers */ if (0 == passIdx) { rsmpIn = NULL; dataIn = rsmc->nin->data; } else { rsmpIn = (nrrdResample_t *)(axisIn->nrsmp->data); dataIn = NULL; } if (passIdx < rsmc->passNum-1) { rsmpOut = (nrrdResample_t *)(axisOut->nrsmp->data); dataOut = NULL; } else { rsmpOut = NULL; dataOut = nout->data; } line = (nrrdResample_t *)(axisIn->nline->data); indx = (int *)(axisIn->nindex->data); weight = (nrrdResample_t *)(axisIn->nweight->data); if (rsmc->verbose) { fprintf(stderr, "%s: {rsmp,data}In = %p/%p; {rsmp,data}Out = %p/%p\n", me, rsmpIn, dataIn, rsmpOut, dataOut); fprintf(stderr, "%s: line = %p; indx = %p; weight = %p\n", me, line, indx, weight); } /* the skinny */ for (axIdx=0; axIdxdim; axIdx++) { coordIn[axIdx] = 0; coordOut[axIdx] = 0; } for (lineIdx=0; lineIdxsizePerm, rsmc->dim); NRRD_INDEX_GEN(indexOut, coordOut, axisOut->sizePerm, rsmc->dim); /* read input scanline into scanline buffer */ if (0 == passIdx) { for (smpIdx=0; smpIdxsizeIn; smpIdx++) { line[smpIdx] = lup(dataIn, smpIdx*strideIn + indexIn); } } else { for (smpIdx=0; smpIdxsizeIn; smpIdx++) { line[smpIdx] = rsmpIn[smpIdx*strideIn + indexIn]; } } /* do the bloody convolution and save the output value */ dotLen = axisIn->nweight->axis[0].size; for (smpIdx=0; smpIdxsamples; smpIdx++) { double val; val = 0.0; if (nrrdResampleNonExistentNoop != rsmc->nonExistent) { double wsum; wsum = 0.0; for (dotIdx=0; dotIdxnonExistent) { val /= wsum; } /* else nrrdResampleNonExistentWeight: leave as is */ } else { val = AIR_NAN; } } else { /* nrrdResampleNonExistentNoop: do convolution sum w/out worries about value existance */ for (dotIdx=0; dotIdxpassNum-1) { rsmpOut[smpIdx*strideOut + indexOut] = val; } else { if (doRound) { val = AIR_CAST(nrrdResample_t, AIR_ROUNDUP(val)); } if (rsmc->clamp) { val = clamp(val); } ins(dataOut, smpIdx*strideOut + indexOut, val); } } /* as long as there's another line to be processed, increment the coordinates for the scanline starts. We don't use the usual NRRD_COORD macros because we're subject to the unusual constraint that coordIn[topRax] and coordOut[permute[topRax]] must stay == 0 */ if (lineIdx < lineNum-1) { axIdx = rsmc->topRax ? 0 : 1; coordIn[axIdx]++; coordOut[rsmc->permute[axIdx]]++; while (coordIn[axIdx] == axisIn->sizePerm[axIdx]) { coordIn[axIdx] = coordOut[rsmc->permute[axIdx]] = 0; axIdx++; axIdx += axIdx == rsmc->topRax; coordIn[axIdx]++; coordOut[rsmc->permute[axIdx]]++; } } } /* (maybe) free input to this pass, now that we're done with it */ if (axisIn->nrsmp) { if (rsmc->verbose) { fprintf(stderr, "%s: nrrdNuke(%p) pass %u input (on axis %u)\n", me, AIR_CAST(void*, axisIn->nrsmp), axisIn->passIdx, axisIn->axIdx); } axisIn->nrsmp = nrrdNuke(axisIn->nrsmp); /* airMopSub(mop, axisIn->nrsmp, (airMopper)nrrdNuke); */ } } /* for passIdx */ airMopOkay(mop); return 0; } int _nrrdResampleOutputUpdate(NrrdResampleContext *rsmc, Nrrd *nout, const char *func) { static const char me[]="_nrrdResampleOutputUpdate"; #if NRRD_RESAMPLE_FLOAT float (*lup)(const void *, size_t), (*clamp)(float), (*ins)(void *, size_t, float); #else double (*lup)(const void *, size_t), (*clamp)(double), (*ins)(void *, size_t, double); #endif unsigned int axIdx; int typeOut, doRound; if (rsmc->flag[flagClamp] || rsmc->flag[flagNonExistent] || rsmc->flag[flagRound] || rsmc->flag[flagTypeOut] || rsmc->flag[flagLineFill] || rsmc->flag[flagVectorFill] || rsmc->flag[flagPermutation] || rsmc->flag[flagInput]) { typeOut = (nrrdTypeDefault == rsmc->typeOut ? rsmc->nin->type : rsmc->typeOut); doRound = rsmc->roundlast && nrrdTypeIsIntegral[typeOut]; if (doRound && (nrrdTypeInt == typeOut || nrrdTypeUInt == typeOut || nrrdTypeLLong == typeOut || nrrdTypeULLong == typeOut)) { fprintf(stderr, "%s: WARNING: possible erroneous output with " "rounding of %s output type due to int-based implementation " "of rounding\n", me, airEnumStr(nrrdType, typeOut)); } #if NRRD_RESAMPLE_FLOAT lup = nrrdFLookup[rsmc->nin->type]; clamp = nrrdFClamp[typeOut]; ins = nrrdFInsert[typeOut]; #else lup = nrrdDLookup[rsmc->nin->type]; clamp = nrrdDClamp[typeOut]; ins = nrrdDInsert[typeOut]; #endif if (0 == rsmc->passNum) { if (_nrrdResampleTrivial(rsmc, nout, typeOut, doRound, lup, clamp, ins)) { biffAddf(NRRD, "%s: trouble", me); return 1; } } else { if (_nrrdResampleCore(rsmc, nout, typeOut, doRound, lup, clamp, ins)) { biffAddf(NRRD, "%s: trouble", me); return 1; } } /* HEY: need to create textual representation of resampling parameters */ if (nrrdContentSet_va(nout, func, rsmc->nin, "")) { biffAddf(NRRD, "%s:", me); return 1; } /* start work of updating space origin */ nrrdSpaceVecCopy(nout->spaceOrigin, rsmc->nin->spaceOrigin); for (axIdx=0; axIdxdim; axIdx++) { if (rsmc->axis[axIdx].kernel) { /* this axis was resampled */ double minIdxFull, maxIdxFull, zeroPos; /* actually its in continuous index space ... */ _nrrdAxisInfoCopy(nout->axis + axIdx, rsmc->nin->axis + axIdx, (NRRD_AXIS_INFO_SIZE_BIT | NRRD_AXIS_INFO_SPACING_BIT | NRRD_AXIS_INFO_THICKNESS_BIT | NRRD_AXIS_INFO_MIN_BIT | NRRD_AXIS_INFO_MAX_BIT | NRRD_AXIS_INFO_SPACEDIRECTION_BIT | NRRD_AXIS_INFO_CENTER_BIT | NRRD_AXIS_INFO_KIND_BIT)); /* now set all the per-axis fields we just abstained from copying */ /* size was already set */ nout->axis[axIdx].spacing = (rsmc->nin->axis[axIdx].spacing / rsmc->axis[axIdx].ratio); /* for now, we don't attempt to modify thickness */ nout->axis[axIdx].thickness = AIR_NAN; /* We had to assume a specific centering when doing resampling */ nout->axis[axIdx].center = rsmc->axis[axIdx].center; _nrrdResampleMinMaxFull(&minIdxFull, &maxIdxFull, rsmc->axis[axIdx].center, rsmc->nin->axis[axIdx].size); nout->axis[axIdx].min = AIR_AFFINE(minIdxFull, rsmc->axis[axIdx].min, maxIdxFull, rsmc->nin->axis[axIdx].min, rsmc->nin->axis[axIdx].max); nout->axis[axIdx].max = AIR_AFFINE(minIdxFull, rsmc->axis[axIdx].max, maxIdxFull, rsmc->nin->axis[axIdx].min, rsmc->nin->axis[axIdx].max); nrrdSpaceVecScale(nout->axis[axIdx].spaceDirection, 1.0/rsmc->axis[axIdx].ratio, rsmc->nin->axis[axIdx].spaceDirection); nout->axis[axIdx].kind = _nrrdKindAltered(rsmc->nin->axis[axIdx].kind, AIR_TRUE); /* space origin may have translated along this axis; only do this if the axis was already spatial */ if (AIR_EXISTS(rsmc->nin->axis[axIdx].spaceDirection[0])) { zeroPos = NRRD_POS(nout->axis[axIdx].center, rsmc->axis[axIdx].min, rsmc->axis[axIdx].max, rsmc->axis[axIdx].samples, 0); nrrdSpaceVecScaleAdd2(nout->spaceOrigin, 1.0, nout->spaceOrigin, zeroPos, rsmc->nin->axis[axIdx].spaceDirection); } } else { /* no resampling; this axis totally unchanged */ _nrrdAxisInfoCopy(nout->axis + axIdx, rsmc->nin->axis + axIdx, NRRD_AXIS_INFO_NONE); /* also: the space origin has not translated along this axis */ } } if (nrrdBasicInfoCopy(nout, rsmc->nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_SPACEORIGIN_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } rsmc->flag[flagClamp] = AIR_FALSE; rsmc->flag[flagNonExistent] = AIR_FALSE; rsmc->flag[flagRound] = AIR_FALSE; rsmc->flag[flagTypeOut] = AIR_FALSE; rsmc->flag[flagLineFill] = AIR_FALSE; rsmc->flag[flagVectorFill] = AIR_FALSE; rsmc->flag[flagPermutation] = AIR_FALSE; rsmc->flag[flagInput] = AIR_FALSE; } return 0; } int nrrdResampleExecute(NrrdResampleContext *rsmc, Nrrd *nout) { static const char me[]="nrrdResampleExecute", func[]="resample"; double time0; if (!(rsmc && nout)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } /* any other error checking? Do we need a _nrrdResampleContextCheck() ? */ if (nrrdBoundaryPad == rsmc->boundary && !AIR_EXISTS(rsmc->padValue)) { biffAddf(NRRD, "%s: asked for boundary padding, " "but no pad value set", me); return 1; } time0 = airTime(); if (_nrrdResampleInputDimensionUpdate(rsmc) || _nrrdResampleInputCentersUpdate(rsmc) || _nrrdResampleInputSizesUpdate(rsmc) || _nrrdResampleLineAllocateUpdate(rsmc) || _nrrdResampleVectorAllocateUpdate(rsmc) || _nrrdResampleLineFillUpdate(rsmc) || _nrrdResampleVectorFillUpdate(rsmc) || _nrrdResamplePermutationUpdate(rsmc) || _nrrdResampleOutputUpdate(rsmc, nout, func)) { biffAddf(NRRD, "%s: trouble resampling", me); return 1; } rsmc->time = airTime() - time0; return 0; } teem-1.11.0~svn6057/src/nrrd/formatPNG.c0000664000175000017500000004421312165631065017353 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" #include #if TEEM_PNG #include #endif #define MAGIC "\211PNG" static int _nrrdFormatPNG_available(void) { #if TEEM_PNG return AIR_TRUE; #else return AIR_FALSE; #endif } static int _nrrdFormatPNG_nameLooksLike(const char *filename) { return airEndsWith(filename, NRRD_EXT_PNG); } static int _nrrdFormatPNG_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { static const char me[]="_nrrdFormatPNG_fitsInto"; #if !TEEM_PNG /* ------------------------------------------- */ AIR_UNUSED(nrrd); AIR_UNUSED(encoding); biffMaybeAddf(useBiff, NRRD, "%s: %s format not available in this Teem build", me, nrrdFormatPNG->name); return AIR_FALSE; #else /* ------------------------------------------- */ int ret; if (!( nrrd && encoding )) { biffMaybeAddf(useBiff, NRRD, "%s: got NULL nrrd (%p) or encoding (%p)", me, AIR_CVOIDP(nrrd), AIR_CVOIDP(encoding)); return AIR_FALSE; } if (!( nrrdTypeUChar == nrrd->type || nrrdTypeUShort == nrrd->type )) { biffMaybeAddf(useBiff, NRRD, "%s: type must be %s or %s (not %s)", me, airEnumStr(nrrdType, nrrdTypeUChar), airEnumStr(nrrdType, nrrdTypeUShort), airEnumStr(nrrdType, nrrd->type)); return AIR_FALSE; } /* else */ /* encoding ignored- always gzip */ if (2 == nrrd->dim) { /* its a gray-scale image */ ret = AIR_TRUE; } else if (3 == nrrd->dim) { if (!( 1 == nrrd->axis[0].size || 2 == nrrd->axis[0].size || 3 == nrrd->axis[0].size || 4 == nrrd->axis[0].size )) { char stmp[AIR_STRLEN_SMALL]; biffMaybeAddf(useBiff, NRRD, "%s: 1st axis size is %s, not 1, 2, 3, or 4", me, airSprintSize_t(stmp, nrrd->axis[0].size)); return AIR_FALSE; } /* else */ ret = AIR_TRUE; } else { biffMaybeAddf(useBiff, NRRD, "%s: dimension is %d, not 2 or 3", me, nrrd->dim); return AIR_FALSE; } return ret; #endif /* ------------------------------------------- */ } static int _nrrdFormatPNG_contentStartsLike(NrrdIoState *nio) { return !strcmp(MAGIC, nio->line); } #if TEEM_PNG static void _nrrdErrorHandlerPNG (png_structp png, png_const_charp message) { static const char me[]="_nrrdErrorHandlerPNG"; /* add PNG error message to biff */ biffAddf(NRRD, "%s: PNG error: %s", me, message); /* longjmp back to the setjmp, return 1 */ longjmp(png_jmpbuf(png), 1); } static void _nrrdWarningHandlerPNG (png_structp png, png_const_charp message) { static const char me[]="_nrrdWarningHandlerPNG"; AIR_UNUSED(png); /* add the png warning message to biff */ biffAddf(NRRD, "%s: PNG warning: %s", me, message); /* no longjump, execution continues */ } /* we need to use the file I/O callbacks on windows to make sure we can mix VC6 libpng with VC7 Teem */ #ifdef _WIN32 static void _nrrdReadDataPNG (png_structp png, png_bytep data, png_size_t len) { png_size_t read; read = (png_size_t)fread(data, (png_size_t)1, len, (FILE*)png_get_io_ptr(png)); if (read != len) png_error(png, "file read error"); } static void _nrrdWriteDataPNG (png_structp png, png_bytep data, png_size_t len) { png_size_t written; written = fwrite(data, 1, len, (FILE*)png_get_io_ptr(png)); if (written != len) png_error(png, "file write error"); } static void _nrrdFlushDataPNG (png_structp png) { FILE *io_ptr = png_get_io_ptr(png); if (io_ptr != NULL) fflush(io_ptr); } #endif /* _WIN32 */ #endif /* TEEM_PNG */ static int _nrrdFormatPNG_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatPNG_read"; #if TEEM_PNG png_structp png; png_infop info; png_bytep *row; png_uint_32 width, height, rowsize, hi; png_text* txt; int depth, type, i, channels, numtxt, ret; int ntype, ndim; size_t nsize[3]; #endif /* TEEM_PNG */ AIR_UNUSED(file); AIR_UNUSED(nrrd); if (!_nrrdFormatPNG_contentStartsLike(nio)) { biffAddf(NRRD, "%s: this doesn't look like a %s file", me, nrrdFormatPNG->name); return 1; } #if TEEM_PNG /* create png struct with the error handlers above */ png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, _nrrdErrorHandlerPNG, _nrrdWarningHandlerPNG); if (png == NULL) { biffAddf(NRRD, "%s: failed to create PNG read struct", me); return 1; } /* create image info struct */ info = png_create_info_struct(png); if (info == NULL) { png_destroy_read_struct(&png, NULL, NULL); biffAddf(NRRD, "%s: failed to create PNG image info struct", me); return 1; } /* set up png style error handling */ if (setjmp(png_jmpbuf(png))) { /* the error is reported inside the handler, but we still need to clean up and return */ png_destroy_read_struct(&png, &info, NULL); return 1; } /* initialize png I/O */ #ifdef _WIN32 png_set_read_fn(png, (png_voidp)file, _nrrdReadDataPNG); #else png_init_io(png, file); #endif /* if we are here, we have already read 6 bytes from the file */ png_set_sig_bytes(png, 6); /* png_read_info() returns all information from the file before the first data chunk */ png_read_info(png, info); png_get_IHDR(png, info, &width, &height, &depth, &type, NULL, NULL, NULL); /* expand paletted colors into rgb triplets */ if (type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); /* expand grayscale images to 8 bits from 1, 2, or 4 bits */ if (type == PNG_COLOR_TYPE_GRAY && depth < 8) png_set_expand_gray_1_2_4_to_8(png); /* expand paletted or rgb images with transparency to full alpha channels so the data will be available as rgba quartets */ if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png); /* fix endianness for 16 bit formats */ if (depth > 8 && airMyEndian() == airEndianLittle) png_set_swap(png); #if 0 /* HEY GLK asks why is this commented out? */ /* set up gamma correction */ /* NOTE: screen_gamma is platform dependent, it can hardwired or set from a parameter/environment variable */ if (png_get_sRGB(png_ptr, info_ptr, &intent)) { /* if the image has sRGB info, pass in standard nrrd file gamma 1.0 */ png_set_gamma(png_ptr, screen_gamma, 1.0); } else { double gamma; /* set image gamma if present */ if (png_get_gAMA(png, info, &gamma)) png_set_gamma(png, screen_gamma, gamma); else png_set_gamma(png, screen_gamma, 1.0); } #endif /* update reader */ png_read_update_info(png, info); /* allocate memory for the image data */ ntype = depth > 8 ? nrrdTypeUShort : nrrdTypeUChar; switch (type) { case PNG_COLOR_TYPE_GRAY: ndim = 2; nsize[0] = width; nsize[1] = height; nsize[2] = 1; /* to simplify code below */ break; case PNG_COLOR_TYPE_GRAY_ALPHA: ndim = 3; nsize[0] = 2; nsize[1] = width; nsize[2] = height; break; case PNG_COLOR_TYPE_RGB: ndim = 3; nsize[0] = 3; nsize[1] = width; nsize[2] = height; break; case PNG_COLOR_TYPE_RGB_ALPHA: ndim = 3; nsize[0] = 4; nsize[1] = width; nsize[2] = height; break; case PNG_COLOR_TYPE_PALETTE: /* TODO: merge this with the outer switch, needs to be tested */ channels = png_get_channels(png, info); if (channels < 2) { ndim = 2; nsize[0] = width; nsize[1] = height; } else { ndim = 3; nsize[0] = channels; nsize[1] = width; nsize[2] = height; } break; default: png_destroy_read_struct(&png, &info, NULL); biffAddf(NRRD, "%s: unknown png type: %d", me, type); return 1; break; } if (nio->oldData && (nio->oldDataSize == (size_t)(nrrdTypeSize[ntype]*nsize[0]*nsize[1]*nsize[2]))) { ret = nrrdWrap_nva(nrrd, nio->oldData, ntype, ndim, nsize); } else { ret = nrrdMaybeAlloc_nva(nrrd, ntype, ndim, nsize); } if (ret) { png_destroy_read_struct(&png, &info, NULL); biffAddf(NRRD, "%s: failed to allocate nrrd", me); return 1; } /* query row size */ rowsize = png_get_rowbytes(png, info); /* check byte size */ if (nrrdElementNumber(nrrd)*nrrdElementSize(nrrd) != height*rowsize) { png_destroy_read_struct(&png, &info, NULL); biffAddf(NRRD, "%s: size mismatch", me); return 1; } /* set up row pointers */ row = (png_bytep*)malloc(sizeof(png_bytep)*height); for (hi=0; hidata)[hi*rowsize]; } /* read the entire image in one pass */ png_read_image(png, row); /* read all text fields from the text chunk */ numtxt = png_get_text(png, info, &txt, NULL); for (i=0; ipos = 0; /* Reading PNGs teaches Gordon that his scheme for parsing nrrd header information is inappropriately specific to reading PNMs and NRRDs, since in this case the text from which we parse a nrrd field descriptor did NOT come from a line of text as read by _nrrdOneLine */ nio->line = (char *)airFree(nio->line); nio->line = airStrdup(txt[i].text); ret = _nrrdReadNrrdParseField(nio, AIR_FALSE); if (ret) { const char* fs = airEnumStr(nrrdField, ret); if (nrrdField_comment == ret) { ret = 0; goto plain; } if (!_nrrdFieldValidInImage[ret]) { if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: field \"%s\" (not allowed in PNG) " "--> plain comment)\n", me, fs); } ret = 0; goto plain; } if (!nio->seen[ret] && nrrdFieldInfoParse[ret](file, nrrd, nio, AIR_FALSE)) { if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: unparsable info for field \"%s\" " "--> plain comment)\n", me, fs); } ret = 0; goto plain; } nio->seen[ret] = AIR_TRUE; plain: if (!ret) { if (nrrdCommentAdd(nrrd, nio->line)) { png_destroy_read_struct(&png, &info, NULL); biffAddf(NRRD, "%s: couldn't add comment", me); return 1; } } } } else if (!strcmp(txt[i].key, NRRD_PNG_COMMENT_KEY)) { char *p, *c; c = airStrtok(txt[i].text, "\n", &p); while (c) { if (nrrdCommentAdd(nrrd, c)) { png_destroy_read_struct(&png, &info, NULL); biffAddf(NRRD, "%s: couldn't add comment", me); return 1; } c = airStrtok(NULL, "\n", &p); } } else { if (nrrdKeyValueAdd(nrrd, txt[i].key, txt[i].text)) { png_destroy_read_struct(&png, &info, NULL); biffAddf(NRRD, "%s: couldn't add key/value pair", me); return 1; } } } /* finish reading */ png_read_end(png, info); /* clean up */ row = (png_byte**)airFree(row); png_destroy_read_struct(&png, &info, NULL); return 0; #else biffAddf(NRRD, "%s: Sorry, this nrrd not compiled with PNG enabled", me); return 1; #endif } static int _nrrdFormatPNG_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatPNG_write"; #if TEEM_PNG int fi, depth, type, csize; unsigned int jj, numtxt, txtidx; png_structp png; png_infop info; png_bytep *row; png_uint_32 width, height, rowsize, hi; png_text *txt; char *key, *value; /* no need to check type and format, done in FitsInFormat */ /* create png struct with the error handlers above */ png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, _nrrdErrorHandlerPNG, _nrrdWarningHandlerPNG); if (png == NULL) { biffAddf(NRRD, "%s: failed to create PNG write struct", me); return 1; } /* create image info struct */ info = png_create_info_struct(png); if (info == NULL) { png_destroy_write_struct(&png, NULL); biffAddf(NRRD, "%s: failed to create PNG image info struct", me); return 1; } /* set up error png style error handling */ if (setjmp(png_jmpbuf(png))) { /* the error is reported inside the error handler, but we still need to clean up an return with an error */ png_destroy_write_struct(&png, &info); return 1; } /* initialize png I/O */ #ifdef _WIN32 png_set_write_fn(png, file, _nrrdWriteDataPNG, _nrrdFlushDataPNG); #else png_init_io(png, file); #endif /* calculate depth, width, height, and row size */ depth = nrrd->type == nrrdTypeUChar ? 8 : 16; switch (nrrd->dim) { char stmp[AIR_STRLEN_SMALL]; case 2: /* g only */ width = nrrd->axis[0].size; height = nrrd->axis[1].size; type = PNG_COLOR_TYPE_GRAY; rowsize = width*nrrdElementSize(nrrd); break; case 3: /* g, ga, rgb, rgba */ width = nrrd->axis[1].size; height = nrrd->axis[2].size; rowsize = width*nrrd->axis[0].size*nrrdElementSize(nrrd); switch (nrrd->axis[0].size) { case 1: type = PNG_COLOR_TYPE_GRAY; break; case 2: type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 3: type = PNG_COLOR_TYPE_RGB; break; case 4: type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: png_destroy_write_struct(&png, &info); biffAddf(NRRD, "%s: nrrd->axis[0].size (%s) not compatible with PNG", me, airSprintSize_t(stmp, nrrd->axis[0].size)); return 1; break; } break; default: png_destroy_write_struct(&png, &info); biffAddf(NRRD, "%s: dimension (%d) not compatible with PNG", me, nrrd->dim); return 1; break; } /* set image header info */ png_set_IHDR(png, info, width, height, depth, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* calculate numtxt and allocate txt[] array */ numtxt = 0; for (fi=nrrdField_unknown+1; ficmtArr->len > 0) { /* all comments are put into single text field */ numtxt += 1; } if (0 == numtxt) { txt = NULL; } else { txt = AIR_CAST(png_text *, calloc(numtxt, sizeof(png_text))); /* add nrrd fields to the text chunk */ csize = 0; txtidx = 0; for (fi=nrrdField_unknown+1; ficmtArr->len > 0) { txt[txtidx].key = airStrdup(NRRD_PNG_COMMENT_KEY); txt[txtidx].compression = PNG_TEXT_COMPRESSION_NONE; for (jj=0; jjcmtArr->len; jj++) { csize += airStrlen(nrrd->cmt[jj]) + 1; } txt[txtidx].text = (png_charp)malloc(csize + 1); txt[txtidx].text[0] = 0; for (jj=0; jjcmtArr->len; jj++) { strcat(txt[txtidx].text, nrrd->cmt[jj]); strcat(txt[txtidx].text, "\n"); } txtidx++; } png_set_text(png, info, txt, numtxt); } /* write header */ png_write_info(png, info); /* fix endianness for 16 bit formats */ if (depth > 8 && airMyEndian() == airEndianLittle) { png_set_swap(png); } /* set up row pointers */ row = (png_bytep*)malloc(sizeof(png_bytep)*height); for (hi=0; hidata)[hi*rowsize]; } png_set_rows(png, info, row); /* write the entire image in one pass */ png_write_image(png, row); /* finish writing */ png_write_end(png, info); /* clean up */ if (txt) { for (jj=0; jj *val NOT set ** AIR_TRUE: set in a valid way ==> *val set (to something) ** AIR_FALSE: set in an invalid way ==> *val NOT set */ int nrrdGetenvBool(int *val, char **envStr, const char *envVar) { char *env; int tmp; if (!(val && envVar)) { return -1; } env = getenv(envVar); if (envStr) { *envStr = env; } if (!env) { return -1; } if (!strlen(env)) { /* for bools, being merely set (but not to any string) means "true" */ *val = AIR_TRUE; return AIR_TRUE; } tmp = airEnumVal(airBool, env); if (airEnumUnknown(airBool) == tmp) { return AIR_FALSE; } else { *val = tmp; return AIR_TRUE; } } int nrrdGetenvEnum(int *val, char **envStr, const airEnum *enm, const char *envVar) { char *env; int tmp; if (!(val && envVar)) { return -1; } env = getenv(envVar); if (envStr) { *envStr = env; } if (!env) { return -1; } tmp = airEnumVal(enm, env); if (airEnumUnknown(enm) == tmp) { return AIR_FALSE; } else { *val = tmp; return AIR_TRUE; } } int nrrdGetenvUInt(unsigned int *val, char **envStr, const char *envVar) { char *env; unsigned int tmp; if (!(val && envVar)) { return -1; } env = getenv(envVar); if (envStr) { *envStr = env; } if (!env) { return -1; } if (1 != sscanf(env, "%u", &tmp)) { return AIR_FALSE; } else { *val = tmp; return AIR_TRUE; } } int nrrdGetenvInt(int *val, char **envStr, const char *envVar) { char *env; int tmp; if (!(val && envVar)) { return -1; } env = getenv(envVar); if (envStr) { *envStr = env; } if (!env) { return -1; } if (1 != sscanf(env, "%d", &tmp)) { return AIR_FALSE; } else { *val = tmp; return AIR_TRUE; } } int nrrdGetenvDouble(double *val, char **envStr, const char *envVar) { char *env; double tmp; if (!(val && envVar)) { return -1; } env = getenv(envVar); if (envStr) { *envStr = env; } if (!env) { return -1; } if (1 != sscanf(env, "%lf", &tmp)) { return AIR_FALSE; } else { *val = tmp; return AIR_TRUE; } } /* ** This function is not used in the same way within nrrd the same way ** as the other nrrdGetenv functions; it was added just to have a more ** convenient wrapper around getenv for strings. */ int nrrdGetenvString(char **envStr, const char *envVar) { if (!(envStr && envVar)) { return -1; } *envStr = getenv(envVar); if (!(*envStr)) { return AIR_FALSE; } else { return AIR_TRUE; } } void nrrdDefaultGetenv(void) { /* these two pre-date Def --> Default rename */ if (-1 == nrrdGetenvBool(/**/ &nrrdDefaultWriteBareText, NULL, nrrdEnvVarDefaultWriteBareTextOld)) { nrrdGetenvBool(/**/ &nrrdDefaultWriteBareText, NULL, nrrdEnvVarDefaultWriteBareText); } if (-1 == nrrdGetenvEnum(/**/ &nrrdDefaultCenter, NULL, nrrdCenter, nrrdEnvVarDefaultCenterOld)) { nrrdGetenvEnum(/**/ &nrrdDefaultCenter, NULL, nrrdCenter, nrrdEnvVarDefaultCenter); } /* these post-date the Def --> Default rename */ nrrdGetenvEnum(/**/ &nrrdDefaultWriteEncodingType, NULL, nrrdEncodingType, nrrdEnvVarDefaultWriteEncodingType); nrrdGetenvUInt(/**/ &nrrdDefaultWriteCharsPerLine, NULL, nrrdEnvVarDefaultWriteCharsPerLine); nrrdGetenvUInt(/**/ &nrrdDefaultWriteValsPerLine, NULL, nrrdEnvVarDefaultWriteValsPerLine); nrrdGetenvDouble(/**/ &nrrdDefaultKernelParm0, NULL, nrrdEnvVarDefaultKernelParm0); nrrdGetenvDouble(/**/ &nrrdDefaultSpacing, NULL, nrrdEnvVarDefaultSpacing); return; } void nrrdStateGetenv(void) { nrrdGetenvBool(/**/ &nrrdStateKindNoop, NULL, nrrdEnvVarStateKindNoop); nrrdGetenvInt(/**/ &nrrdStateVerboseIO, NULL, nrrdEnvVarStateVerboseIO); nrrdGetenvBool(/**/ &nrrdStateKeyValuePairsPropagate, NULL, nrrdEnvVarStateKeyValuePairsPropagate); nrrdGetenvBool(/**/ &nrrdStateBlind8BitRange, NULL, nrrdEnvVarStateBlind8BitRange); nrrdGetenvBool(/**/ &nrrdStateAlwaysSetContent, NULL, nrrdEnvVarStateAlwaysSetContent); nrrdGetenvBool(/**/ &nrrdStateDisableContent, NULL, nrrdEnvVarStateDisableContent); nrrdGetenvEnum(/**/ &nrrdStateMeasureType, NULL, nrrdType, nrrdEnvVarStateMeasureType); nrrdGetenvInt(/**/ &nrrdStateMeasureModeBins, NULL, nrrdEnvVarStateMeasureModeBins); nrrdGetenvEnum(/**/ &nrrdStateMeasureHistoType, NULL, nrrdType, nrrdEnvVarStateMeasureHistoType); nrrdGetenvBool(/**/ &nrrdStateGrayscaleImage3D, NULL, nrrdEnvVarStateGrayscaleImage3D); return; } /* ---- END non-NrrdIO */ teem-1.11.0~svn6057/src/nrrd/measure.c0000664000175000017500000010356012177242445017163 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* the non-histogram measures assume that there will be no NaNs in data */ void _nrrdMeasureUnknown(void *ans, int ansType, const void *line, int lineType, size_t len, double axmin, double axmax) { static const char me[]="_nrrdMeasureUnknown"; AIR_UNUSED(line); AIR_UNUSED(lineType); AIR_UNUSED(len); AIR_UNUSED(axmin); AIR_UNUSED(axmax); fprintf(stderr, "%s: Need To Specify A Measure !!! \n", me); nrrdDStore[ansType](ans, AIR_NAN); } void _nrrdMeasureMin(void *ans, int ansType, const void *line, int lineType, size_t len, double axmin, double axmax) { double val, M, (*lup)(const void*, size_t); size_t ii; AIR_UNUSED(axmin); AIR_UNUSED(axmax); lup = nrrdDLookup[lineType]; if (nrrdTypeIsIntegral[lineType]) { M = lup(line, 0); for (ii=1; iidata, nrrdTypeInt, nrrdStateMeasureModeBins, nhist->axis[0].min, nhist->axis[0].max); nrrdNuke(nhist); nrrdNix(nline); } else { nrrdDStore[ansType](ans, 0); } return; } void _nrrdMeasureMedian(void *ans, int ansType, const void *_line, int lineType, size_t len, double axmin, double axmax) { double M=0, (*lup)(const void*, size_t); size_t ii, mid; void *line; AIR_UNUSED(axmin); AIR_UNUSED(axmax); lup = nrrdDLookup[lineType]; line = calloc(len, nrrdTypeSize[lineType]); if (line) { memcpy(line, _line, len*nrrdTypeSize[lineType]); /* yes, I know, this is not the fastest median. I'll get to it ... */ qsort(line, len, nrrdTypeSize[lineType], nrrdValCompare[lineType]); M = AIR_NAN; for (ii=0; !AIR_EXISTS(M) && ii 0 ? tmp : 0); } if (!sum) { nrrdDStore[ansType](ans, AIR_NAN); return; } /* else there was something in the histogram */ half = sum/2; sum = 0; for (ii=0; ii 0 ? tmp : 0); if (sum >= half) { break; } } ansD = AIR_CAST(double, ii); if (AIR_EXISTS(axmin) && AIR_EXISTS(axmax)) { ansD = NRRD_CELL_POS(axmin, axmax, len, ansD); } nrrdDStore[ansType](ans, ansD); } void _nrrdMeasureHistoMode(void *ans, int ansType, const void *line, int lineType, size_t len, double axmin, double axmax) { double val, max, idxsum, ansD, (*lup)(const void*, size_t); size_t ii, idxcount; lup = nrrdDLookup[lineType]; max = -DBL_MAX; for (ii=0; ii ansD = %g --> ", (float)idxsum, idxcount, ansD); */ if (AIR_EXISTS(axmin) && AIR_EXISTS(axmax)) { ansD = NRRD_CELL_POS(axmin, axmax, len, ansD); } /* printf("%g\n", ansD); */ nrrdDStore[ansType](ans, ansD); } void _nrrdMeasureHistoMean(void *ans, int ansType, const void *line, int lineType, size_t len, double axmin, double axmax) { double count, hits, ansD, (*lup)(const void*, size_t); size_t ii; lup = nrrdDLookup[lineType]; ansD = count = 0; for (ii=0; ii0; ii--) { if (lup(line, ii-1) > 0) { break; } } if (ii==0) { nrrdDStore[ansType](ans, AIR_NAN); return; } val = NRRD_CELL_POS(axmin, axmax, len, ii-1); nrrdDStore[ansType](ans, val); } void _nrrdMeasureHistoMin(void *ans, int ansType, const void *line, int lineType, size_t len, double axmin, double axmax) { double val, (*lup)(const void*, size_t); size_t ii; if (!(AIR_EXISTS(axmin) && AIR_EXISTS(axmax))) { axmin = -0.5; axmax = len-0.5; } lup = nrrdDLookup[lineType]; for (ii=0; ii 0) { break; } } if (ii==len) { nrrdDStore[ansType](ans, AIR_NAN); return; } val = NRRD_CELL_POS(axmin, axmax, len, ii); nrrdDStore[ansType](ans, val); } void (* nrrdMeasureLine[NRRD_MEASURE_MAX+1])(void *, int, const void *, int, size_t, double, double) = { _nrrdMeasureUnknown, _nrrdMeasureMin, _nrrdMeasureMax, _nrrdMeasureMean, _nrrdMeasureMedian, _nrrdMeasureMode, _nrrdMeasureProduct, _nrrdMeasureSum, _nrrdMeasureL1, _nrrdMeasureL2, _nrrdMeasureL4, _nrrdMeasureNormalizedL2, _nrrdMeasureRootMeanSquare, _nrrdMeasureLinf, _nrrdMeasureVariance, _nrrdMeasureSD, _nrrdMeasureCoV, _nrrdMeasureSkew, _nrrdMeasureLineSlope, _nrrdMeasureLineIntercept, _nrrdMeasureLineError, _nrrdMeasureHistoMin, _nrrdMeasureHistoMax, _nrrdMeasureHistoMean, _nrrdMeasureHistoMedian, _nrrdMeasureHistoMode, _nrrdMeasureHistoProduct, _nrrdMeasureHistoSum, _nrrdMeasureHistoL2, _nrrdMeasureHistoVariance, _nrrdMeasureHistoSD }; int _nrrdMeasureType(const Nrrd *nin, int measr) { static const char me[]="_nrrdMeasureType"; int type=nrrdTypeUnknown; switch(measr) { case nrrdMeasureMin: case nrrdMeasureMax: case nrrdMeasureMedian: case nrrdMeasureMode: type = nin->type; break; case nrrdMeasureMean: /* the rational for this is that if you're after the average value along a scanline, you probably want it in the same format as what you started with, and if you really want an exact answer than you can always use nrrdMeasureSum and then divide. This may well be bone-headed, so is subject to change */ type = nin->type; break; case nrrdMeasureProduct: case nrrdMeasureSum: case nrrdMeasureL1: case nrrdMeasureL2: case nrrdMeasureL4: case nrrdMeasureNormalizedL2: case nrrdMeasureRootMeanSquare: case nrrdMeasureLinf: case nrrdMeasureVariance: case nrrdMeasureSD: case nrrdMeasureCoV: case nrrdMeasureSkew: case nrrdMeasureLineSlope: case nrrdMeasureLineIntercept: case nrrdMeasureLineError: type = nrrdStateMeasureType; break; case nrrdMeasureHistoMin: case nrrdMeasureHistoMax: case nrrdMeasureHistoProduct: case nrrdMeasureHistoSum: case nrrdMeasureHistoL2: case nrrdMeasureHistoMean: case nrrdMeasureHistoMedian: case nrrdMeasureHistoMode: case nrrdMeasureHistoVariance: case nrrdMeasureHistoSD: /* We (currently) don't keep track of the type of the original values which generated the histogram, and we may not even have access to that information. So we end up choosing one type for all these histogram-based measures */ type = nrrdStateMeasureHistoType; break; default: fprintf(stderr, "%s: PANIC: measr %d not handled\n", me, measr); exit(1); } return type; } int nrrdProject(Nrrd *nout, const Nrrd *cnin, unsigned int axis, int measr, int type) { static const char me[]="nrrdProject", func[]="project"; int iType, oType, axmap[NRRD_DIM_MAX]; unsigned int ai, ei; size_t iElSz, oElSz, iSize[NRRD_DIM_MAX], oSize[NRRD_DIM_MAX], linLen, rowIdx, rowNum, colIdx, colNum, colStep; const char *ptr, *iData; char *oData, *line; double axmin, axmax; Nrrd *nin; airArray *mop; if (!(cnin && nout)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == cnin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (nrrdTypeBlock == cnin->type) { biffAddf(NRRD, "%s: can't project nrrd type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!AIR_IN_OP(nrrdMeasureUnknown, measr, nrrdMeasureLast)) { biffAddf(NRRD, "%s: measure %d not recognized", me, measr); return 1; } if (1 == cnin->dim) { if (0 != axis) { biffAddf(NRRD, "%s: axis must be 0, not %u, for 1-D array", me, axis); return 1; } } else { if (!( axis <= cnin->dim-1 )) { biffAddf(NRRD, "%s: axis %u not in range [0,%d]", me, axis, cnin->dim-1); return 1; } } if (nrrdTypeDefault != type) { if (!( AIR_IN_OP(nrrdTypeUnknown, type, nrrdTypeLast) )) { biffAddf(NRRD, "%s: got invalid target type %d", me, type); return 1; } } mop = airMopNew(); if (1 == cnin->dim) { /* There are more efficient ways of dealing with this case; this way is easy to implement because it leaves most of the established code below only superficially changed; uniformly replacing nin with (nin ? nin : cnin), even if pointlessly so; this expression that can't be assigned to a new variable because of the difference in const. */ nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); if (nrrdAxesInsert(nin, cnin, 1)) { biffAddf(NRRD, "%s: trouble inserting axis on 1-D array", me); airMopError(mop); return 1; } } else { nin = NULL; } iType = (nin ? nin : cnin)->type; oType = (nrrdTypeDefault != type ? type : _nrrdMeasureType((nin ? nin : cnin), measr)); iElSz = nrrdTypeSize[iType]; oElSz = nrrdTypeSize[oType]; nrrdAxisInfoGet_nva((nin ? nin : cnin), nrrdAxisInfoSize, iSize); colNum = rowNum = 1; for (ai=0; ai<(nin ? nin : cnin)->dim; ai++) { if (ai < axis) { colNum *= iSize[ai]; } else if (ai > axis) { rowNum *= iSize[ai]; } } linLen = iSize[axis]; colStep = linLen*colNum; for (ai=0; ai<=(nin ? nin : cnin)->dim-2; ai++) { axmap[ai] = ai + (ai >= axis); } for (ai=0; ai<=(nin ? nin : cnin)->dim-2; ai++) { oSize[ai] = iSize[axmap[ai]]; } if (nrrdMaybeAlloc_nva(nout, oType, (nin ? nin : cnin)->dim-1, oSize)) { biffAddf(NRRD, "%s: failed to create output", me); airMopError(mop); return 1; } /* allocate a scanline buffer */ if (!(line = AIR_CALLOC(linLen*iElSz, char))) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: couldn't calloc(%s,%s) scanline buffer", me, airSprintSize_t(stmp1, linLen), airSprintSize_t(stmp2, iElSz)); airMopError(mop); return 1; } airMopAdd(mop, line, airFree, airMopAlways); /* the skinny */ axmin = (nin ? nin : cnin)->axis[axis].min; axmax = (nin ? nin : cnin)->axis[axis].max; iData = AIR_CAST(char *, (nin ? nin : cnin)->data); oData = AIR_CAST(char *, nout->data); for (rowIdx=0; rowIdx #include /* for ptrdiff_t */ /* ---- BEGIN non-NrrdIO */ #include #include #include #include #include #include #include #include #include #include #include "nrrdDefines.h" #include "nrrdMacros.h" #include "nrrdEnums.h" #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(nrrd_EXPORTS) || defined(teem_EXPORTS) # define NRRD_EXPORT extern __declspec(dllexport) # else # define NRRD_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define NRRD_EXPORT extern #endif /* ---- END non-NrrdIO */ #ifdef __cplusplus extern "C" { #endif #define NRRD nrrdBiffKey /* ******** NrrdAxisInfo struct ** ** all the information which can sensibly be associated with ** one axis of a nrrd. The only member which MUST be explicitly ** set to something meaningful is "size". ** ** If an axis lies conceptually along some direction in an enclosing ** space of dimension nrrd->spaceDim, then the first nrrd->spaceDim ** entries of spaceDirection[] must be non-NaN, and min, max, spacing, ** and units must NOT be set; thickness, center, and label can still ** be used. The mutual exclusion between axis-aligned and general ** direction information is enforced per-axis, not per-array. ** ** The min and max values give the range of positions "represented" ** by the samples along this axis. In node-centering, "min" IS the ** position at the lowest index. In cell-centering, the position at ** the lowest index is between min and max (a touch bigger than min, ** assuming min < max). ** ** There needs to be a one-to-one correspondence between these variables ** and the nrrdAxisInfo* enum (nrrdEnums.h), the per-axis header fields ** (see nrrdField* enum in nrrdEnums.h), and the various methods in axis.c */ typedef struct { size_t size; /* number of elements along each axis */ double spacing; /* if non-NaN, distance between samples */ double thickness; /* if non-NaN, nominal thickness of region represented by one sample along the axis. No semantics relative to spacing are assumed or imposed, and unlike spacing, there is no sensible way to alter thickness- it is either copied (as with cropping and slicing) or set to NaN (when resampled). */ double min, max; /* if non-NaN, range of positions spanned by the samples on this axis. Obviously, one can set "spacing" to something incompatible with min and max: the idea is that only one (min and max, or spacing) should be taken to be significant at any time. */ double spaceDirection[NRRD_SPACE_DIM_MAX]; /* the vector, in "space" (as described by nrrd->space and/or nrrd->spaceDim), from one sample to the next sample along this axis. It is the column vector of the transform from index space to "space" space */ int center; /* cell vs. node centering (value should be one of nrrdCenter{Unknown,Node,Cell} */ int kind; /* what kind of information is along this axis (from the nrrdKind* enum) */ char *label, /* short info string for each axis */ *units; /* string identifying the unit */ } NrrdAxisInfo; /* ******** Nrrd struct ** ** The struct used to wrap around the raw data array */ typedef struct { /* ** NECESSARY information describing the main array. This is ** generally set at the same time that either the nrrd is created, ** or at the time that the nrrd is wrapped around an existing array */ void *data; /* the data in memory */ int type; /* a value from the nrrdType enum */ unsigned int dim; /* the dimension (rank) of the array */ /* ** All per-axis specific information */ NrrdAxisInfo axis[NRRD_DIM_MAX]; /* axis[0] is the fastest axis in the scan- line ordering, the one who's coordinates change the fastest as the elements are accessed in the order in which they appear in memory */ /* ** Optional information descriptive of whole array, some of which is ** meaningfuly for only some uses of a nrrd */ char *content; /* brief account of what this data is */ char *sampleUnits; /* units of measurement of the values stored in the array itself (not the array axes and not space coordinates). The logical name might be "dataUnits", but that's perhaps ambiguous. Note that these units may apply to non-scalar kinds (e.g. coefficients of a vector have the same units) */ int space; /* from nrrdSpace* enum, and often implies the value of spaceDim */ unsigned int spaceDim; /* if non-zero, the dimension of the space in which the regular sampling grid conceptually lies. This is a separate variable because this dimension can be different than the array dimension. The non-zero-ness of this value is in fact the primary indicator that space and orientation information is set. This identifies the number of entries in "origin" and the per-axis "direction" vectors that are taken as meaningful */ char *spaceUnits[NRRD_SPACE_DIM_MAX]; /* units for coordinates of space */ double spaceOrigin[NRRD_SPACE_DIM_MAX]; /* the location of the center the first (lowest memory address) array sample, regardless of node-vs-cell centering */ double measurementFrame[NRRD_SPACE_DIM_MAX][NRRD_SPACE_DIM_MAX]; /* if spaceDim is non-zero, this may store a spaceDim-by-spaceDim matrix which transforms vector/matrix coefficients in the "measurement frame" to those in the world space described by spaceDim (and hopefully space). Coeff [i][j] is *column* i & *row* j, which is probably the *transpose* of what you expect. There are no semantics linking this to the "kind" of any axis, for a variety of reasons */ size_t blockSize; /* for nrrdTypeBlock, block byte size */ double oldMin, oldMax; /* if non-NaN, and if nrrd is of integral type, extremal values for the array BEFORE it was quantized */ void *ptr; /* never read or set by nrrd; use/abuse as you see fit */ /* ** Comments. Read from, and written to, header. ** The comment array "cmt" is NOT NULL-terminated. ** The number of comments is cmtArr->len. */ char **cmt; airArray *cmtArr; /* ** Key-value pairs. */ char **kvp; airArray *kvpArr; } Nrrd; struct NrrdIoState_t; struct NrrdEncoding_t; /* ******** NrrdFormat ** ** All information and behavior relevent to one datafile format */ typedef struct { char name[AIR_STRLEN_SMALL]; /* short identifying string */ int isImage, /* this format is intended solely for "2D" images, which controls the invocation of _nrrdReshapeUpGrayscale() if nrrdStateGrayscaleImage3D */ readable, /* we can read as well as write this format */ usesDIO; /* this format can use Direct IO */ /* tests if this format is currently available in this build */ int (*available)(void); /* (for writing) returns non-zero if a given filename could likely be represented by this format */ int (*nameLooksLike)(const char *filename); /* (for writing) returns non-zero if a given nrrd/encoding pair will fit in this format */ int (*fitsInto)(const Nrrd *nrrd, const struct NrrdEncoding_t *encoding, int useBiff); /* (for reading) returns non-zero if what has been read in so far is recognized as the beginning of this format */ int (*contentStartsLike)(struct NrrdIoState_t *nio); /* reader and writer */ int (*read)(FILE *file, Nrrd *nrrd, struct NrrdIoState_t *nio); int (*write)(FILE *file, const Nrrd *nrrd, struct NrrdIoState_t *nio); } NrrdFormat; /* ******** NrrdEncoding ** ** All information and behavior relevent to one way of encoding data ** ** The data readers are responsible for memory allocation. ** This is necessitated by the memory restrictions of direct I/O */ typedef struct NrrdEncoding_t { char name[AIR_STRLEN_SMALL], /* short identifying string */ suffix[AIR_STRLEN_SMALL]; /* costumary filename suffix */ int endianMatters, isCompression; int (*available)(void); /* The "data" and "elementNum" values have to be passed explicitly to read/wrote because they will be different from nrrd->data and nrrdElementNumber(nrrd) in the case of multiple data files. You might think that the only other thing required to be passed is nrrdElementSize(nrrd), but no, it is in fact best to pass the whole Nrrd, instead of just certain attributes. The stupid details: nrrd->dim: needed to know whether to put one value per line in case of 1-D nrrdEncodingAscii nrrd->axis[0].size: need for proper formatting of nrrdEncodingAscii nrrd->type: needed for nrrdEncodingAscii, since its action is entirely parameterized by type nrrd->blockSize: needed for nrrdElementSize in case of nrrdTypeBlock */ int (*read)(FILE *file, void *data, size_t elementNum, Nrrd *nrrd, struct NrrdIoState_t *nio); int (*write)(FILE *file, const void *data, size_t elementNum, const Nrrd *nrrd, struct NrrdIoState_t *nio); } NrrdEncoding; /* ******** NrrdIoState struct ** ** Everything relating to how the nrrd is read and written. ** Multiple parameters for writing are set here (like format, encoding, ** zlib parameters). Also, this is the place where those few parameters ** of reading are stored (like skipData and keepNrrdDataFileOpen). Also, ** after the nrrd has been read, it is a potentially useful record of what ** it took to read it in. */ typedef struct NrrdIoState_t { char *path, /* allows us to remember the directory from whence this nrrd was "load"ed, or to whence this nrrd is "save"ed, MINUS the trailing "/", so as to facilitate games with header-relative data files */ *base, /* when "save"ing a nrrd into separate header and data, the name of the header file (e.g. "output.nhdr") MINUS the ".nhdr". This is massaged to produce a header- relative data filename. */ *line, /* buffer for saving one line from file */ *dataFNFormat, /* if non-NULL, the format string (containing something like "%d" as a substring) to be used to identify multiple detached datafiles. NB: This is "format" in the sense of a printf- style format string, not in the sense of a file format. This may need header-relative path processing. */ **dataFN, /* ON READ + WRITE: array of data filenames. These are not passed directly to fopen, they may need header-relative path processing. Like the cmtArr in the Nrrd, this array is not NULL- terminated */ *headerStringWrite; /* ON WRITE: string from to which the header can be written. On write, it is assumed allocated for as long as it needs to be (probably via a first pass with learningHeaderStrlen). NOTE: It is the non-NULL-ity of this which signifies the intent to do string-based writing */ const char *headerStringRead; /* ON READ: like headerStringWrite, but for reading the header from. NOTE: It is the non-NULL-ity of this which signifies the intent to do string-based reading */ airArray *dataFNArr; /* for managing the above */ FILE *headerFile, /* if non-NULL, the file from which the NRRD header is being read */ *dataFile; /* this used to be a central part of how the I/O code worked, but now it is simply the place to store the dataFile in the case of keepNrrdDataFileOpen */ unsigned int dataFileDim, /* The dimension of the data in each data file. Together with dataFNArr->len, this determines how many bytes should be in each data file */ lineLen, /* allocated size of line, including the last character for \0 */ charsPerLine, /* when writing ASCII data in which we intend only to write a huge long list of numbers whose text formatting implies nothing, then how many characters do we limit ourselves to per line */ valsPerLine, /* when writing ASCII data in which we DO intend to sigify (or at least hint at) something with the formatting, then what is the max number of values to write on a line */ lineSkip, /* if dataFile non-NULL, the number of lines in dataFile that should be skipped over (so as to bypass another form of ASCII header preceeding raw data) */ headerStrlen, /* ON WRITE, for NRRDs, if learningHeaderStrlen, the learned strlen of the header so far */ headerStrpos; /* ON READ, for NRRDs, if headerStringRead is non-NULL, the current location of reading in the header */ long int byteSkip; /* exactly like lineSkip, but bytes instead of lines. First the lines are skipped, then the bytes */ /* Note that the NRRD0004 and NRRD0005 file formats indicate that a numbered sequence of data filenames should be indexed via a "%d" format specification, and that the format doc says nothing about the "min" and "max" fields of "data file" being only positive. So the following three dataFN* fields are appropriately (signed) ints, even if all normal usage could also be represented with unsigned ints. Nonetheless, the return from _nrrdDataFNNumber(), which gives the total number of file names, is still appropriately an unsigned int. This may be revisited if the file format itself is adjusted. */ int dataFNMin, /* used with dataFNFormat to identify .. */ dataFNMax, /* .. all the multiple detached datafiles */ dataFNStep; /* how to step from max to min */ /* On the other hand, dataFNIndex ranges from 0 to (#datafiles-1), and not dataFNMin to dataFNMax, so it really should be unsigned */ unsigned int dataFNIndex; /* which of the data files are being read */ int pos, /* line[pos] is beginning of stuff which still has yet to be parsed */ endian, /* endian-ness of the data in file, for those encoding/type combinations for which it matters (from nrrdEndian) */ seen[NRRD_FIELD_MAX+1], /* for error checking in header parsing */ detachedHeader, /* ON WRITE: request for file (NRRD format only) to be split into distinct header and data. This only has an effect if detaching the header is not already necessary, as it is with multiple data files */ bareText, /* when writing a plain text file, is there any effort made to record the nrrd struct info in the text file */ skipData, /* if non-zero (all formats): ON READ: don't allocate memory for, and don't read in, the data portion of the file (but we do verify that for nrrds, detached datafiles can be opened). Note: Does NOT imply keepNrrdDataFileOpen. Warning: resulting nrrd struct will have "data" pointer NULL. ON WRITE: don't write data portion of file (for nrrds, don't even try to open detached datafiles). Warning: can result in broken noncomformant files. (be careful with this) */ skipFormatURL, /* if non-zero for NRRD format ON WRITE: skip the comment lines that document where to find the NRRD file format specs */ keepNrrdDataFileOpen, /* ON READ: when there is only a single dataFile, don't close nio->dataFile when you otherwise would, when reading the nrrd format. Probably used in conjunction with skipData. (currently for "unu data") ON WRITE: no semantics */ zlibLevel, /* zlib compression level (0-9, -1 for default[6], 0 for no compression). */ zlibStrategy, /* zlib compression strategy, can be one of the nrrdZlibStrategy enums, default is nrrdZlibStrategyDefault. */ bzip2BlockSize, /* block size used for compression, roughly equivalent to better but slower (1-9, -1 for default[9]). */ learningHeaderStrlen; /* ON WRITE, for nrrds, learn and save the total length of header into headerStrlen. This is used to allocate a buffer for header */ void *oldData; /* ON READ: if non-NULL, pointer to space that has already been allocated for oldDataSize */ size_t oldDataSize; /* ON READ: size of mem pointed to by oldData */ /* The format and encoding. These are initialized to nrrdFormatUnknown and nrrdEncodingUnknown, respectively. USE THESE VALUES for any kind of initialization or flagging; DO NOT USE NULL */ const NrrdFormat *format; const NrrdEncoding *encoding; } NrrdIoState; /* ---- BEGIN non-NrrdIO */ /* ******** NrrdRange ** ** information about a range of values, used as both a description ** of an existing nrrd, or as input to functions like nrrdQuantize ** (in which case the given min,max may not correspond to the actual ** min,max of the nrrd in question). ** ** This information has been removed from the Nrrd struct (as of Teem1.6) ** and put into this separate entity because: ** 1) when intended to be descriptive of a nrrd, it can't be guaranteed ** to be true across nrrd calls ** 2) when used as input parameters (e.g. to nrrdQuantize), its not ** data-flow friendly (you can't modify input) */ typedef struct { double min, max; /* if non-NaN, nominally: extremal values for array, but practically: the min and max values to use for nrrd calls for which a min and max values are used */ int hasNonExist; /* from the nrrdHasNonExist* enum values */ } NrrdRange; /* ******** NrrdKernel struct ** ** these are essentially the methods of the various kernels implemented. ** ** Nrrd's use of this sort of kernel always assumes support symmetric ** around zero, but does not assume anything about even- or oddness ** ** It is a strong but very simplifying assumption that the parameter ** array ("parm") is always type double. There is essentially no ** value in allowing flexibility between float and double, and much ** Teem code assumes that it will always be type double. */ typedef struct { /* terse string representation of kernel function, irrespective of the parameter vector */ char name[AIR_STRLEN_SMALL]; /* number of parameters needed (# elements in parm[] used) */ unsigned int numParm; /* HEY: should be "parmNum" in standard convention */ /* smallest x (x > 0) such that k(y) = 0 for all y > x, y < -x */ double (*support)(const double *parm); /* integral of kernel from -support to +support */ double (*integral)(const double *parm); /* evaluate once, single precision */ float (*eval1_f)(float x, const double *parm); /* evaluate N times, single precision */ void (*evalN_f)(float *f, const float *x, size_t N, const double *parm); /* evaluate once, double precision */ double (*eval1_d)(double x, const double *parm); /* evaluate N times, double precision */ void (*evalN_d)(double *f, const double *x, size_t N, const double *parm); } NrrdKernel; /* ******** NrrdKernelSpec struct ** ** for those times when it makes most sense to directly associate a ** NrrdKernel with its parameter vector (that is, a full kernel ** "spec"ification), basically: using hest. */ typedef struct { const NrrdKernel *kernel; double parm[NRRD_KERNEL_PARMS_NUM]; } NrrdKernelSpec; /* ******** NrrdResampleInfo struct ** ** a struct to contain the many parameters needed for nrrdSpatialResample() */ typedef struct { const NrrdKernel *kernel[NRRD_DIM_MAX]; /* which kernel to use on each axis; use NULL to say no resampling whatsoever on this axis */ size_t samples[NRRD_DIM_MAX]; /* number of samples per axis */ double parm[NRRD_DIM_MAX][NRRD_KERNEL_PARMS_NUM], /* kernel arguments */ min[NRRD_DIM_MAX], max[NRRD_DIM_MAX]; /* min[i] and max[i] are the range, in WORLD space, along which to resample axis i. axis mins and maxs are required on resampled axes. */ int boundary, /* value from the nrrdBoundary enum */ type, /* desired type of output, use nrrdTypeUnknown for "same as input" */ renormalize, /* when downsampling with a kernel with non-zero integral, should we renormalize the weights to match the kernel integral so as to remove annoying ripple */ round, /* when copying from the last intermediate (floating point) result to the output nrrd, for integer outputs, do we round to the nearest integer first, before clamping and assigning. Enabling this fixed the mystery of downsampling large constant regions of 255 (uchar), and ending up with 254 */ clamp, /* when copying from the last intermediate (floating point) result to the output nrrd, should we clamp the values to the range of values for the output type, a concern only for integer outputs */ cheap; /* when *downsampling* (reducing the number of samples), don't bother expanding the kernel to achieve filtering in the old index space; with nrrdKernelBox this can lead to subsampling by picking using every other value */ double padValue; /* if padding, what value to pad with */ } NrrdResampleInfo; /* ******** NrrdResampleAxis struct ** ** specific to one pass (one pass per axis) of resampling process */ typedef struct { /* ----------- input ---------- */ const NrrdKernel *kernel; /* which kernel to use on this axis; use NULL to say no resampling whatsoever on this axis */ double kparm[NRRD_KERNEL_PARMS_NUM]; /* kernel arguments */ double min, max; /* range in INDEX space of resampling */ size_t samples; /* number output samples on this axis (sizeOut) */ int overrideCenter; /* possible explicit setting of centering */ /* ----------- internal ---------- */ int center; /* centering for this axis */ size_t sizeIn, /* number input samples on this axis */ sizePerm[NRRD_DIM_MAX]; /* permutation of axis sizes for this pass */ unsigned int axIdx, /* what axis are we (redundant with other info) */ passIdx, /* exactly which pass are we on */ axisPerm[NRRD_DIM_MAX]; /* permutation of axis indices for this pass */ double ratio; /* > 1: upsampling; < 1: downsampling */ Nrrd *nrsmp, /* intermediate resampling result; input to this pass */ *nline, /* input scanline buffer (includes extra sample at end for storing pad value) */ *nindex, /* row of input indices for each output sample */ *nweight; /* row of input weights for each output sample */ } NrrdResampleAxis; /* ******** NrrdResampleContext struct ** ** contains the parameters and state associated with resampling, ** geared towards quickly resampling multiple different nrrds with the ** same shape. */ typedef struct { /* ----------- input ---------- */ const Nrrd *nin; /* the nrrd being resampled */ int verbose, /* blah blah blah */ boundary, /* value from the nrrdBoundary enum */ typeOut, /* desired type of output, use nrrdTypeDefault for "same as input" */ renormalize, /* when downsampling with a kernel with non-zero integral, should we renormalize the weights to match the kernel integral so as to remove annoying ripple */ roundlast, /* when copying from the last intermediate (floating point) result to the output nrrd, for integer outputs, do we round to the nearest integer first, before clamping and assigning. Enabling this fixed the mystery of downsampling large constant regions of 255 (uchar), and ending up with 254 (renamed from "round" to avoid shadowing) */ clamp, /* when copying from the last intermediate (floating point) result to the output nrrd, should we clamp the values to the range of values for the output type, a concern only for integer outputs */ defaultCenter, /* lacking known centering on input axis, what centering to use when resampling */ nonExistent; /* from nrrdResampleNonExistent enum */ double padValue; /* if padding, what value to pad with */ /* ----------- input/internal ---------- */ unsigned int dim, /* dimension of nin (saved here to help manage state in NrrdResampleAxis[]) */ passNum, /* number of passes needed */ topRax, botRax, /* fastest, slowest axes undergoing resampling */ permute[NRRD_DIM_MAX+1], /* how each pass permutes axes */ passAxis[NRRD_DIM_MAX]; /* mapping from pass index to axis index */ NrrdResampleAxis axis[NRRD_DIM_MAX+1]; /* axis[j] stores information for input to pass which is resampling nin->axis[j]; axis[NRRD_DIM_MAX] stores info about the final output of all passes */ int *flag; /* flags for managing state */ /* ----------- output ---------- */ double time; /* time required for resampling */ } NrrdResampleContext; /* ******** NrrdIter struct ** ** To hold values: either a single value, or a whole nrrd of values. ** Also, this facilitates iterating through those values */ typedef struct { const Nrrd *nrrd; /* read-only nrrd to get values from */ Nrrd *ownNrrd; /* another nrrd to get values from, which we do "own", and do delete on nrrdIterNix */ double val; /* single fixed value */ size_t size; /* type size */ char *data; /* where to get the next value */ size_t left; /* number of values beyond what "data" currently points to */ double (*load)(const void*); /* how to get a value out of "data" */ } NrrdIter; /* ---- END non-NrrdIO */ /******** defaults (nrrdDefault..) and state (nrrdState..) */ /* defaultsNrrd.c */ NRRD_EXPORT int nrrdDefaultWriteEncodingType; NRRD_EXPORT int nrrdDefaultWriteBareText; NRRD_EXPORT unsigned int nrrdDefaultWriteCharsPerLine; NRRD_EXPORT unsigned int nrrdDefaultWriteValsPerLine; /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT int nrrdDefaultResampleBoundary; NRRD_EXPORT int nrrdDefaultResampleType; NRRD_EXPORT int nrrdDefaultResampleRenormalize; NRRD_EXPORT int nrrdDefaultResampleRound; NRRD_EXPORT int nrrdDefaultResampleClamp; NRRD_EXPORT int nrrdDefaultResampleCheap; NRRD_EXPORT double nrrdDefaultResamplePadValue; NRRD_EXPORT int nrrdDefaultResampleNonExistent; NRRD_EXPORT double nrrdDefaultKernelParm0; /* ---- END non-NrrdIO */ NRRD_EXPORT int nrrdDefaultCenter; NRRD_EXPORT double nrrdDefaultSpacing; NRRD_EXPORT int nrrdStateVerboseIO; NRRD_EXPORT int nrrdStateKeyValuePairsPropagate; /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT int nrrdStateBlind8BitRange; NRRD_EXPORT int nrrdStateMeasureType; NRRD_EXPORT int nrrdStateMeasureModeBins; NRRD_EXPORT int nrrdStateMeasureHistoType; NRRD_EXPORT int nrrdStateDisallowIntegerNonExist; /* ---- END non-NrrdIO */ NRRD_EXPORT int nrrdStateAlwaysSetContent; NRRD_EXPORT int nrrdStateDisableContent; NRRD_EXPORT const char *nrrdStateUnknownContent; NRRD_EXPORT int nrrdStateGrayscaleImage3D; NRRD_EXPORT int nrrdStateKeyValueReturnInternalPointers; NRRD_EXPORT int nrrdStateKindNoop; /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT const char *const nrrdEnvVarDefaultWriteEncodingType; NRRD_EXPORT const char *const nrrdEnvVarDefaultWriteBareText; NRRD_EXPORT const char *const nrrdEnvVarDefaultWriteBareTextOld; NRRD_EXPORT const char *const nrrdEnvVarDefaultCenter; NRRD_EXPORT const char *const nrrdEnvVarDefaultCenterOld; NRRD_EXPORT const char *const nrrdEnvVarDefaultWriteCharsPerLine; NRRD_EXPORT const char *const nrrdEnvVarDefaultWriteValsPerLine; NRRD_EXPORT const char *const nrrdEnvVarDefaultKernelParm0; NRRD_EXPORT const char *const nrrdEnvVarDefaultSpacing; NRRD_EXPORT const char *const nrrdEnvVarStateKindNoop; NRRD_EXPORT const char *const nrrdEnvVarStateVerboseIO; NRRD_EXPORT const char *const nrrdEnvVarStateKeyValuePairsPropagate; NRRD_EXPORT const char *const nrrdEnvVarStateBlind8BitRange; NRRD_EXPORT const char *const nrrdEnvVarStateAlwaysSetContent; NRRD_EXPORT const char *const nrrdEnvVarStateDisableContent; NRRD_EXPORT const char *const nrrdEnvVarStateMeasureType; NRRD_EXPORT const char *const nrrdEnvVarStateMeasureModeBins; NRRD_EXPORT const char *const nrrdEnvVarStateMeasureHistoType; NRRD_EXPORT const char *const nrrdEnvVarStateGrayscaleImage3D; NRRD_EXPORT int nrrdGetenvBool(int *val, char **envStr, const char *envVar); NRRD_EXPORT int nrrdGetenvEnum(int *val, char **envStr, const airEnum *enm, const char *envVar); NRRD_EXPORT int nrrdGetenvInt(int *val, char **envStr, const char *envVar); NRRD_EXPORT int nrrdGetenvUInt(unsigned int *val, char **envStr, const char *envVar); NRRD_EXPORT int nrrdGetenvDouble(double *val, char **envStr, const char *envVar); NRRD_EXPORT int nrrdGetenvString(char **envStr, const char *envVar); NRRD_EXPORT void nrrdDefaultGetenv(void); NRRD_EXPORT void nrrdStateGetenv(void); /* ---- END non-NrrdIO */ /******** all the airEnums used through-out nrrd */ /* ** the actual C enums are in nrrdEnums.h; experience has shown that it ** is not particularly useful to name those enums, since the shortest ** name is best used for the airEnums here */ /* enumsNrrd.c */ NRRD_EXPORT const airEnum *const nrrdFormatType; NRRD_EXPORT const airEnum *const nrrdType; NRRD_EXPORT const airEnum *const nrrdEncodingType; NRRD_EXPORT const airEnum *const nrrdCenter; NRRD_EXPORT const airEnum *const nrrdKind; NRRD_EXPORT const airEnum *const nrrdField; NRRD_EXPORT const airEnum *const nrrdSpace; NRRD_EXPORT const airEnum *const nrrdSpacingStatus; /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT const airEnum *const nrrdBoundary; NRRD_EXPORT const airEnum *const nrrdMeasure; NRRD_EXPORT const airEnum *const nrrdUnaryOp; NRRD_EXPORT const airEnum *const nrrdBinaryOp; NRRD_EXPORT const airEnum *const nrrdTernaryOp; NRRD_EXPORT const airEnum *const nrrdFFTWPlanRigor; NRRD_EXPORT const airEnum *const nrrdResampleNonExistent; /* ---- END non-NrrdIO */ /******** arrays of things (poor-man's functions/predicates) */ /* arraysNrrd.c */ NRRD_EXPORT const char nrrdTypePrintfStr[NRRD_TYPE_MAX+1][AIR_STRLEN_SMALL]; NRRD_EXPORT const size_t nrrdTypeSize[NRRD_TYPE_MAX+1]; NRRD_EXPORT const double nrrdTypeMin[NRRD_TYPE_MAX+1]; NRRD_EXPORT const double nrrdTypeMax[NRRD_TYPE_MAX+1]; NRRD_EXPORT const int nrrdTypeIsIntegral[NRRD_TYPE_MAX+1]; NRRD_EXPORT const int nrrdTypeIsUnsigned[NRRD_TYPE_MAX+1]; /******** pseudo-constructors, pseudo-destructors, and such */ /* methodsNrrd.c */ /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT const int nrrdPresent; /* ---- END non-NrrdIO */ NRRD_EXPORT NrrdIoState *nrrdIoStateNew(void); NRRD_EXPORT void nrrdIoStateInit(NrrdIoState *nio); NRRD_EXPORT NrrdIoState *nrrdIoStateNix(NrrdIoState *nio); /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT NrrdResampleInfo *nrrdResampleInfoNew(void); NRRD_EXPORT NrrdResampleInfo *nrrdResampleInfoNix(NrrdResampleInfo *info); NRRD_EXPORT NrrdKernelSpec *nrrdKernelSpecNew(void); NRRD_EXPORT NrrdKernelSpec *nrrdKernelSpecCopy(const NrrdKernelSpec *ksp); NRRD_EXPORT void nrrdKernelSpecSet(NrrdKernelSpec *ksp, const NrrdKernel *k, const double kparm[NRRD_KERNEL_PARMS_NUM]); NRRD_EXPORT void nrrdKernelParmSet(const NrrdKernel **kP, double kparm[NRRD_KERNEL_PARMS_NUM], NrrdKernelSpec *ksp); NRRD_EXPORT NrrdKernelSpec *nrrdKernelSpecNix(NrrdKernelSpec *ksp); /* ---- END non-NrrdIO */ NRRD_EXPORT void nrrdInit(Nrrd *nrrd); NRRD_EXPORT Nrrd *nrrdNew(void); NRRD_EXPORT Nrrd *nrrdNix(Nrrd *nrrd); NRRD_EXPORT Nrrd *nrrdEmpty(Nrrd *nrrd); NRRD_EXPORT Nrrd *nrrdNuke(Nrrd *nrrd); NRRD_EXPORT int nrrdWrap_nva(Nrrd *nrrd, void *data, int type, unsigned int dim, const size_t *size); NRRD_EXPORT int nrrdWrap_va(Nrrd *nrrd, void *data, int type, unsigned int dim, ... /* size_t sx, sy, .., axis(dim-1) size */); NRRD_EXPORT void nrrdBasicInfoInit(Nrrd *nrrd, int excludeBitflag); NRRD_EXPORT int nrrdBasicInfoCopy(Nrrd *nout, const Nrrd *nin, int excludeBitflag); NRRD_EXPORT int nrrdCopy(Nrrd *nout, const Nrrd *nin); NRRD_EXPORT int nrrdAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size); NRRD_EXPORT int nrrdAlloc_va(Nrrd *nrrd, int type, unsigned int dim, ... /* size_t sx, sy, .., axis(dim-1) size */); NRRD_EXPORT int nrrdMaybeAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size); NRRD_EXPORT int nrrdMaybeAlloc_va(Nrrd *nrrd, int type, unsigned int dim, ... /* size_t sx, sy, .., ax(dim-1) size */); /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT int nrrdCompare(const Nrrd *ninA, const Nrrd *ninB, int onlyData, double epsilon, int *differ, char explain[AIR_STRLEN_LARGE]); NRRD_EXPORT int nrrdPPM(Nrrd *, size_t sx, size_t sy); NRRD_EXPORT int nrrdPGM(Nrrd *, size_t sx, size_t sy); /* ---- END non-NrrdIO */ /******** axis info related */ /* axis.c */ NRRD_EXPORT int nrrdKindIsDomain(int kind); NRRD_EXPORT unsigned int nrrdKindSize(int kind); NRRD_EXPORT int nrrdAxisInfoCopy(Nrrd *nout, const Nrrd *nin, const int *axmap, int excludeBitflag); NRRD_EXPORT void nrrdAxisInfoSet_nva(Nrrd *nin, int axInfo, const void *info); NRRD_EXPORT void nrrdAxisInfoSet_va(Nrrd *nin, int axInfo, ... /* const void* */); NRRD_EXPORT void nrrdAxisInfoGet_nva(const Nrrd *nrrd, int axInfo, void *info); NRRD_EXPORT void nrrdAxisInfoGet_va(const Nrrd *nrrd, int axInfo, ... /* ??? */); NRRD_EXPORT double nrrdAxisInfoPos(const Nrrd *nrrd, unsigned int ax, double idx); NRRD_EXPORT double nrrdAxisInfoIdx(const Nrrd *nrrd, unsigned int ax, double pos); NRRD_EXPORT void nrrdAxisInfoPosRange(double *loP, double *hiP, const Nrrd *nrrd, unsigned int ax, double loIdx, double hiIdx); NRRD_EXPORT void nrrdAxisInfoIdxRange(double *loP, double *hiP, const Nrrd *nrrd, unsigned int ax, double loPos, double hiPos); NRRD_EXPORT void nrrdAxisInfoSpacingSet(Nrrd *nrrd, unsigned int ax); NRRD_EXPORT void nrrdAxisInfoMinMaxSet(Nrrd *nrrd, unsigned int ax, int defCenter); /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT int nrrdAxisInfoCompare(const NrrdAxisInfo *axisA, const NrrdAxisInfo *axisB, int *differ, char explain[AIR_STRLEN_LARGE]); /* ---- END non-NrrdIO */ NRRD_EXPORT unsigned int nrrdDomainAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]); NRRD_EXPORT unsigned int nrrdRangeAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]); NRRD_EXPORT unsigned int nrrdSpatialAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]); NRRD_EXPORT unsigned int nrrdNonSpatialAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]); NRRD_EXPORT int nrrdSpacingCalculate(const Nrrd *nrrd, unsigned int ax, double *spacing, double vector[NRRD_SPACE_DIM_MAX]); NRRD_EXPORT int nrrdOrientationReduce(Nrrd *nout, const Nrrd *nin, int setMinsFromOrigin); /******** simple things */ /* simple.c */ NRRD_EXPORT const char *nrrdBiffKey; NRRD_EXPORT unsigned int nrrdSpaceDimension(int space); NRRD_EXPORT int nrrdSpaceSet(Nrrd *nrrd, int space); NRRD_EXPORT int nrrdSpaceDimensionSet(Nrrd *nrrd, unsigned int spaceDim); NRRD_EXPORT unsigned int nrrdSpaceOriginGet(const Nrrd *nrrd, double vector[NRRD_SPACE_DIM_MAX]); NRRD_EXPORT int nrrdSpaceOriginSet(Nrrd *nrrd, const double *vector); NRRD_EXPORT int nrrdOriginCalculate(const Nrrd *nrrd, unsigned int *axisIdx, unsigned int axisIdxNum, int defaultCenter, double *origin); NRRD_EXPORT int nrrdContentSet_va(Nrrd *nout, const char *func, const Nrrd *nin, const char *format, ... /* printf-style arg list */ ); NRRD_EXPORT void nrrdDescribe(FILE *file, const Nrrd *nrrd); NRRD_EXPORT int nrrdCheck(const Nrrd *nrrd); NRRD_EXPORT int _nrrdCheck(const Nrrd *nrrd, int checkData, int useBiff); NRRD_EXPORT size_t nrrdElementSize(const Nrrd *nrrd); NRRD_EXPORT size_t nrrdElementNumber(const Nrrd *nrrd); NRRD_EXPORT int nrrdSanity(void); NRRD_EXPORT int nrrdSameSize(const Nrrd *n1, const Nrrd *n2, int useBiff); NRRD_EXPORT void nrrdSpaceVecCopy(double dst[NRRD_SPACE_DIM_MAX], const double src[NRRD_SPACE_DIM_MAX]); NRRD_EXPORT void nrrdSpaceVecScaleAdd2(double sum[NRRD_SPACE_DIM_MAX], double sclA, const double vecA[NRRD_SPACE_DIM_MAX], double sclB, const double vecB[NRRD_SPACE_DIM_MAX]); NRRD_EXPORT void nrrdSpaceVecScale(double out[NRRD_SPACE_DIM_MAX], double scl, const double vec[NRRD_SPACE_DIM_MAX]); NRRD_EXPORT double nrrdSpaceVecNorm(unsigned int sdim, const double vec[NRRD_SPACE_DIM_MAX]); NRRD_EXPORT int nrrdSpaceVecExists(unsigned int sdim, double vec[NRRD_SPACE_DIM_MAX]); NRRD_EXPORT void nrrdSpaceVecSetNaN(double vec[NRRD_SPACE_DIM_MAX]); /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT void nrrdSanityOrDie(const char *me); NRRD_EXPORT void nrrdSpaceVecSetZero(double vec[NRRD_SPACE_DIM_MAX]); NRRD_EXPORT void nrrdZeroSet(Nrrd *nout); /* ---- END non-NrrdIO */ /******** comments related */ /* comment.c */ NRRD_EXPORT int nrrdCommentAdd(Nrrd *nrrd, const char *str); NRRD_EXPORT void nrrdCommentClear(Nrrd *nrrd); NRRD_EXPORT int nrrdCommentCopy(Nrrd *nout, const Nrrd *nin); /******** key/value pairs */ /* keyvalue.c */ NRRD_EXPORT unsigned int nrrdKeyValueSize(const Nrrd *nrrd); NRRD_EXPORT int nrrdKeyValueAdd(Nrrd *nrrd, const char *key, const char *value); NRRD_EXPORT char *nrrdKeyValueGet(const Nrrd *nrrd, const char *key); NRRD_EXPORT void nrrdKeyValueIndex(const Nrrd *nrrd, char **keyP, char **valueP, unsigned int ki); NRRD_EXPORT int nrrdKeyValueErase(Nrrd *nrrd, const char *key); NRRD_EXPORT void nrrdKeyValueClear(Nrrd *nrrd); NRRD_EXPORT int nrrdKeyValueCopy(Nrrd *nout, const Nrrd *nin); /******** endian related */ /* endianNrrd.c */ NRRD_EXPORT void nrrdSwapEndian(Nrrd *nrrd); /******** getting information to and from files */ /* formatXXX.c */ NRRD_EXPORT const NrrdFormat *const nrrdFormatNRRD; NRRD_EXPORT const NrrdFormat *const nrrdFormatPNM; NRRD_EXPORT const NrrdFormat *const nrrdFormatPNG; NRRD_EXPORT const NrrdFormat *const nrrdFormatVTK; NRRD_EXPORT const NrrdFormat *const nrrdFormatText; NRRD_EXPORT const NrrdFormat *const nrrdFormatEPS; /* format.c */ NRRD_EXPORT const NrrdFormat *const nrrdFormatUnknown; NRRD_EXPORT const NrrdFormat * const nrrdFormatArray[NRRD_FORMAT_TYPE_MAX+1]; /* encodingXXX.c */ NRRD_EXPORT const NrrdEncoding *const nrrdEncodingRaw; NRRD_EXPORT const NrrdEncoding *const nrrdEncodingAscii; NRRD_EXPORT const NrrdEncoding *const nrrdEncodingHex; NRRD_EXPORT const NrrdEncoding *const nrrdEncodingGzip; NRRD_EXPORT const NrrdEncoding *const nrrdEncodingBzip2; /* encoding.c */ NRRD_EXPORT const NrrdEncoding *const nrrdEncodingUnknown; NRRD_EXPORT const NrrdEncoding * const nrrdEncodingArray[NRRD_ENCODING_TYPE_MAX+1]; /* parseNrrd.c */ /* this needs the "FILE *file" first arg for the sole reason that parsing a "data file: " field which identifies a LIST must then read in all the data filenames from the same file */ NRRD_EXPORT int (*nrrdFieldInfoParse[NRRD_FIELD_MAX+1])(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff); NRRD_EXPORT unsigned int _nrrdDataFNNumber(NrrdIoState *nio); NRRD_EXPORT int _nrrdContainsPercentThisAndMore(const char *str, char thss); NRRD_EXPORT int _nrrdDataFNCheck(NrrdIoState *nio, Nrrd *nrrd, int useBiff); /* read.c */ NRRD_EXPORT int _nrrdOneLine(unsigned int *lenP, NrrdIoState *nio, FILE *file); NRRD_EXPORT int nrrdLineSkip(FILE *dataFile, NrrdIoState *nio); NRRD_EXPORT int nrrdByteSkip(FILE *dataFile, Nrrd *nrrd, NrrdIoState *nio); NRRD_EXPORT int nrrdLoad(Nrrd *nrrd, const char *filename, NrrdIoState *nio); NRRD_EXPORT int nrrdLoadMulti(Nrrd *const *nin, unsigned int ninLen, const char *fnameFormat, unsigned int numStart, NrrdIoState *nio); NRRD_EXPORT int nrrdRead(Nrrd *nrrd, FILE *file, NrrdIoState *nio); NRRD_EXPORT int nrrdStringRead(Nrrd *nrrd, const char *string, NrrdIoState *nio); /* write.c */ NRRD_EXPORT int nrrdIoStateSet(NrrdIoState *nio, int parm, int value); NRRD_EXPORT int nrrdIoStateEncodingSet(NrrdIoState *nio, const NrrdEncoding *encoding); NRRD_EXPORT int nrrdIoStateFormatSet(NrrdIoState *nio, const NrrdFormat *format); NRRD_EXPORT int nrrdIoStateGet(NrrdIoState *nio, int parm); NRRD_EXPORT const NrrdEncoding *nrrdIoStateEncodingGet(NrrdIoState *nio); NRRD_EXPORT const NrrdFormat *nrrdIoStateFormatGet(NrrdIoState *nio); NRRD_EXPORT int nrrdSave(const char *filename, const Nrrd *nrrd, NrrdIoState *nio); NRRD_EXPORT int nrrdSaveMulti(const char *fnameFormat, const Nrrd *const *nin, unsigned int ninLen, unsigned int numStart, NrrdIoState *nio); NRRD_EXPORT int nrrdWrite(FILE *file, const Nrrd *nrrd, NrrdIoState *nio); NRRD_EXPORT int nrrdStringWrite(char **stringP, const Nrrd *nrrd, NrrdIoState *nio); /******** getting value into and out of an array of general type, and all other simplistic functionality pseudo-parameterized by type */ /* accessors.c */ NRRD_EXPORT double (*nrrdDLoad[NRRD_TYPE_MAX+1])(const void *v); NRRD_EXPORT float (*nrrdFLoad[NRRD_TYPE_MAX+1])(const void *v); NRRD_EXPORT int (*nrrdILoad[NRRD_TYPE_MAX+1])(const void *v); NRRD_EXPORT unsigned int (*nrrdUILoad[NRRD_TYPE_MAX+1])(const void *v); NRRD_EXPORT double (*nrrdDStore[NRRD_TYPE_MAX+1])(void *v, double d); NRRD_EXPORT float (*nrrdFStore[NRRD_TYPE_MAX+1])(void *v, float f); NRRD_EXPORT int (*nrrdIStore[NRRD_TYPE_MAX+1])(void *v, int j); NRRD_EXPORT unsigned int (*nrrdUIStore[NRRD_TYPE_MAX+1])(void *v, unsigned int j); NRRD_EXPORT double (*nrrdDLookup[NRRD_TYPE_MAX+1])(const void *v, size_t I); NRRD_EXPORT float (*nrrdFLookup[NRRD_TYPE_MAX+1])(const void *v, size_t I); NRRD_EXPORT int (*nrrdILookup[NRRD_TYPE_MAX+1])(const void *v, size_t I); NRRD_EXPORT unsigned int (*nrrdUILookup[NRRD_TYPE_MAX+1])(const void *v, size_t I); NRRD_EXPORT double (*nrrdDInsert[NRRD_TYPE_MAX+1])(void *v, size_t I, double d); NRRD_EXPORT float (*nrrdFInsert[NRRD_TYPE_MAX+1])(void *v, size_t I, float f); NRRD_EXPORT int (*nrrdIInsert[NRRD_TYPE_MAX+1])(void *v, size_t I, int j); NRRD_EXPORT unsigned int (*nrrdUIInsert[NRRD_TYPE_MAX+1])(void *v, size_t I, unsigned int j); NRRD_EXPORT int (*nrrdSprint[NRRD_TYPE_MAX+1])(char *, const void *); /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT int (*nrrdFprint[NRRD_TYPE_MAX+1])(FILE *, const void *); NRRD_EXPORT void (*nrrdMinMaxExactFind[NRRD_TYPE_MAX+1])(void *minP, void *maxP, int *hasNonExistP, const Nrrd *nrrd); NRRD_EXPORT int (*nrrdValCompare[NRRD_TYPE_MAX+1])(const void *, const void *); NRRD_EXPORT int (*nrrdValCompareInv[NRRD_TYPE_MAX+1])(const void *, const void *); NRRD_EXPORT int nrrdArrayCompare(int type, const void *valA, const void *valB, size_t valNum, double epsilon, int *differ, char explain[AIR_STRLEN_LARGE]); /* ---- END non-NrrdIO */ /******** permuting, shuffling, and all flavors of reshaping */ /* reorder.c */ NRRD_EXPORT int nrrdAxesInsert(Nrrd *nout, const Nrrd *nin, unsigned int ax); NRRD_EXPORT int nrrdInvertPerm(unsigned int *invp, const unsigned int *perm, unsigned int n); NRRD_EXPORT int nrrdAxesPermute(Nrrd *nout, const Nrrd *nin, const unsigned int *axes); NRRD_EXPORT int nrrdShuffle(Nrrd *nout, const Nrrd *nin, unsigned int axis, const size_t *perm); /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT int nrrdAxesSwap(Nrrd *nout, const Nrrd *nin, unsigned int ax1, unsigned int ax2); NRRD_EXPORT int nrrdFlip(Nrrd *nout, const Nrrd *nin, unsigned int axis); NRRD_EXPORT int nrrdJoin(Nrrd *nout, const Nrrd *const *nin, unsigned int numNin, unsigned int axis, int incrDim); NRRD_EXPORT int nrrdReshape_va(Nrrd *nout, const Nrrd *nin, unsigned int dim, ... /* sx, sy, .., axis(dim-1) size */ ); NRRD_EXPORT int nrrdReshape_nva(Nrrd *nout, const Nrrd *nin, unsigned int dim, const size_t *size); NRRD_EXPORT int nrrdAxesSplit(Nrrd *nout, const Nrrd *nin, unsigned int ax, size_t sizeFast, size_t sizeSlow); NRRD_EXPORT int nrrdAxesDelete(Nrrd *nout, const Nrrd *nin, unsigned int ax); NRRD_EXPORT int nrrdAxesMerge(Nrrd *nout, const Nrrd *nin, unsigned int ax); NRRD_EXPORT int nrrdBlock(Nrrd *nout, const Nrrd *nin); NRRD_EXPORT int nrrdUnblock(Nrrd *nout, const Nrrd *nin, int type); NRRD_EXPORT int nrrdTile2D(Nrrd *nout, const Nrrd *nin, unsigned int ax0, unsigned int ax1, unsigned int axSplit, size_t sizeFast, size_t sizeSlow); NRRD_EXPORT int nrrdUntile2D(Nrrd *nout, const Nrrd *nin, unsigned int ax0, unsigned int ax1, unsigned int axMerge, size_t sizeFast, size_t sizeSlow); /******** things useful with hest */ /* hestNrrd.c */ NRRD_EXPORT hestCB *nrrdHestNrrd; NRRD_EXPORT hestCB *nrrdHestKernelSpec; NRRD_EXPORT hestCB *nrrdHestIter; /******** nrrd value iterator gadget */ /* iter.c */ NRRD_EXPORT NrrdIter *nrrdIterNew(void); NRRD_EXPORT void nrrdIterSetValue(NrrdIter *iter, double val); NRRD_EXPORT void nrrdIterSetNrrd(NrrdIter *iter, const Nrrd *nrrd); NRRD_EXPORT void nrrdIterSetOwnNrrd(NrrdIter *iter, Nrrd *nrrd); NRRD_EXPORT double nrrdIterValue(NrrdIter *iter); NRRD_EXPORT char *nrrdIterContent(NrrdIter *iter); NRRD_EXPORT NrrdIter *nrrdIterNix(NrrdIter *iter); /******** expressing the range of values in a nrrd */ /* range.c */ NRRD_EXPORT NrrdRange *nrrdRangeNew(double min, double max); NRRD_EXPORT NrrdRange *nrrdRangeCopy(const NrrdRange *range); NRRD_EXPORT NrrdRange *nrrdRangeNix(NrrdRange *range); NRRD_EXPORT void nrrdRangeReset(NrrdRange *range); NRRD_EXPORT void nrrdRangeSet(NrrdRange *range, const Nrrd *nrrd, int blind8BitRange); NRRD_EXPORT int nrrdRangePercentileSet(NrrdRange *range, const Nrrd *nrrd, double minPerc, double maxPerc, unsigned int hbins, int blind8BitRange); NRRD_EXPORT int nrrdRangePercentileFromStringSet(NrrdRange *range, const Nrrd *nrrd, const char *minStr, const char *maxStr, unsigned int hbins, int blind8BitRange); NRRD_EXPORT void nrrdRangeSafeSet(NrrdRange *range, const Nrrd *nrrd, int blind8BitRange); NRRD_EXPORT NrrdRange *nrrdRangeNewSet(const Nrrd *nrrd, int blind8BitRange); NRRD_EXPORT int nrrdHasNonExist(const Nrrd *nrrd); /******** some of the point-wise value remapping, conversion, and such */ /* map.c */ NRRD_EXPORT float (*nrrdFClamp[NRRD_TYPE_MAX+1])(float); NRRD_EXPORT double (*nrrdDClamp[NRRD_TYPE_MAX+1])(double); NRRD_EXPORT int nrrdConvert(Nrrd *nout, const Nrrd *nin, int type); NRRD_EXPORT int nrrdClampConvert(Nrrd *nout, const Nrrd *nin, int type); NRRD_EXPORT int nrrdCastClampRound(Nrrd *nout, const Nrrd *nin, int type, int doClamp, int roundDir); NRRD_EXPORT int nrrdQuantize(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, unsigned int bits); NRRD_EXPORT int nrrdUnquantize(Nrrd *nout, const Nrrd *nin, int type); NRRD_EXPORT int nrrdHistoEq(Nrrd *nout, const Nrrd *nin, Nrrd **nhistP, unsigned int bins, unsigned int smart, float amount); /******** rest of point-wise value remapping, and "color"mapping */ /* apply1D.c */ NRRD_EXPORT int nrrdApply1DLut(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, const Nrrd *nlut, int typeOut, int rescale); NRRD_EXPORT int nrrdApplyMulti1DLut(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, const Nrrd *nmlut, int typeOut, int rescale); NRRD_EXPORT int nrrdApply1DRegMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, const Nrrd *nmap, int typeOut, int rescale); NRRD_EXPORT int nrrdApplyMulti1DRegMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, const Nrrd *nmmap, int typeOut, int rescale); NRRD_EXPORT int nrrd1DIrregMapCheck(const Nrrd *nmap); NRRD_EXPORT int nrrd1DIrregAclGenerate(Nrrd *nacl, const Nrrd *nmap, size_t aclLen); NRRD_EXPORT int nrrd1DIrregAclCheck(const Nrrd *nacl); NRRD_EXPORT int nrrdApply1DIrregMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, const Nrrd *nmap, const Nrrd *nacl, int typeOut, int rescale); NRRD_EXPORT int nrrdApply1DSubstitution(Nrrd *nout, const Nrrd *nin, const Nrrd *nsubst); /* apply2D.c */ NRRD_EXPORT int nrrdApply2DLut(Nrrd *nout, const Nrrd *nin, unsigned int domainAxis, const NrrdRange *range0, const NrrdRange *range1, const Nrrd *nlut, int typeOut, int rescale0, int rescale1); /* ---- END non-NrrdIO */ /******** sampling, slicing, cropping */ /* subset.c */ NRRD_EXPORT int nrrdSlice(Nrrd *nout, const Nrrd *nin, unsigned int axis, size_t pos); NRRD_EXPORT int nrrdCrop(Nrrd *nout, const Nrrd *nin, size_t *min, size_t *max); /* ---- BEGIN non-NrrdIO */ NRRD_EXPORT int nrrdSliceSelect(Nrrd *noutAbove, Nrrd *noutBelow, const Nrrd *nin, unsigned int axi, Nrrd *nline, double thresh); NRRD_EXPORT int nrrdSample_nva(void *val, const Nrrd *nin, const size_t *coord); NRRD_EXPORT int nrrdSample_va(void *val, const Nrrd *nin, ... /* size_t idx0, idx1, .., idx(dim-1) */ ); NRRD_EXPORT int nrrdSimpleCrop(Nrrd *nout, const Nrrd *nin, unsigned int crop); NRRD_EXPORT int nrrdCropAuto(Nrrd *nout, const Nrrd *nin, size_t _min[NRRD_DIM_MAX], size_t _max[NRRD_DIM_MAX], const unsigned int *keep, unsigned int keepNum, int measr, double frac, int offset); /******** padding */ /* superset.c */ NRRD_EXPORT int nrrdSplice(Nrrd *nout, const Nrrd *nin, const Nrrd *nslice, unsigned int axis, size_t pos); NRRD_EXPORT int nrrdPad_nva(Nrrd *nout, const Nrrd *nin, const ptrdiff_t *min, const ptrdiff_t *max, int boundary, double padValue); NRRD_EXPORT int nrrdPad_va(Nrrd *nout, const Nrrd *nin, const ptrdiff_t *min, const ptrdiff_t *max, int boundary, ... /* double value (if nrrdBoundaryPad) */); NRRD_EXPORT int nrrdSimplePad_nva(Nrrd *nout, const Nrrd *nin, unsigned int pad, int boundary, double padValue); NRRD_EXPORT int nrrdSimplePad_va(Nrrd *nout, const Nrrd *nin, unsigned int pad, int boundary, ... /* double value (if nrrdBoundaryPad) */); NRRD_EXPORT int nrrdInset(Nrrd *nout, const Nrrd *nin, const Nrrd *nsub, const size_t *min); /******** measuring and projecting */ /* measure.c */ NRRD_EXPORT void (*nrrdMeasureLine[NRRD_MEASURE_MAX+1])(void *ans, int ansType, const void *line, int lineType, size_t lineLen, double axMin, double axMax); NRRD_EXPORT int nrrdProject(Nrrd *nout, const Nrrd *nin, unsigned int axis, int measr, int type); /********* various kinds of histograms and their analysis */ /* histogram.c */ NRRD_EXPORT int nrrdHisto(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, const Nrrd *nwght, size_t bins, int type); NRRD_EXPORT int nrrdHistoCheck(const Nrrd *nhist); NRRD_EXPORT int nrrdHistoDraw(Nrrd *nout, const Nrrd *nin, size_t sy, int showLog, double max); NRRD_EXPORT int nrrdHistoAxis(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, unsigned int axis, size_t bins, int type); NRRD_EXPORT int nrrdHistoJoint(Nrrd *nout, const Nrrd *const *nin, const NrrdRange *const *range, unsigned int ninNum, const Nrrd *nwght, const size_t *bins, int type, const int *clamp); NRRD_EXPORT int nrrdHistoThresholdOtsu(double *threshP, const Nrrd *nhist, double expo); /******** arithmetic and math on nrrds */ /* arith.c */ NRRD_EXPORT int nrrdArithGamma(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, double gamma); NRRD_EXPORT int nrrdArithUnaryOp(Nrrd *nout, int op, const Nrrd *nin); NRRD_EXPORT int nrrdArithBinaryOp(Nrrd *nout, int op, const Nrrd *ninA, const Nrrd *ninB); NRRD_EXPORT int nrrdArithTernaryOp(Nrrd *nout, int op, const Nrrd *ninA, const Nrrd *ninB, const Nrrd *ninC); NRRD_EXPORT int nrrdArithAffine(Nrrd *nout, double minIn, const Nrrd *nin, double maxIn, double minOut, double maxOut, int clamp); NRRD_EXPORT int nrrdArithIterBinaryOp(Nrrd *nout, int op, NrrdIter *inA, NrrdIter *inB); NRRD_EXPORT int nrrdArithIterBinaryOpSelect(Nrrd *nout, int op, NrrdIter *inA, NrrdIter *inB, unsigned int which); NRRD_EXPORT int nrrdArithIterTernaryOp(Nrrd *nout, int op, NrrdIter *inA, NrrdIter *inB, NrrdIter *inC); NRRD_EXPORT int nrrdArithIterTernaryOpSelect(Nrrd *nout, int op, NrrdIter *inA, NrrdIter *inB, NrrdIter *inC, unsigned int which); NRRD_EXPORT int nrrdArithIterAffine(Nrrd *nout, NrrdIter *minIn, NrrdIter *in, NrrdIter *maxIn, NrrdIter *minOut, NrrdIter *maxOut, int clamp); NRRD_EXPORT unsigned int nrrdCRC32(const Nrrd *nin, int endian); /******** filtering and re-sampling */ /* filt.c */ NRRD_EXPORT int nrrdCheapMedian(Nrrd *nout, const Nrrd *nin, int pad, int mode, unsigned int radius, float wght, unsigned int bins); NRRD_EXPORT int nrrdDistanceL2(Nrrd *nout, const Nrrd *nin, int typeOut, const int *axisDo, double thresh, int insideHigher); NRRD_EXPORT int nrrdDistanceL2Biased(Nrrd *nout, const Nrrd *nin, int typeOut, const int *axisDo, double thresh, double bias, int insideHigher); NRRD_EXPORT int nrrdDistanceL2Signed(Nrrd *nout, const Nrrd *nin, int typeOut, const int *axisDo, double thresh, int insideHigher); /******** deringNrrd.c: deringing CT */ typedef struct { /* -------- INPUT */ int verbose, /* blah blah blah */ linearInterp, /* instead of nearest neighbor in polar txf */ verticalSeam; /* dering left and right sides of image separately, asserting that there are no rings along vertical line through center */ const Nrrd *nin; /* array to dering */ double center[2], /* location of recon center in index space of fastest two axes */ clampPerc[2], /* percentiles above 0.0 and below 1.0 at which to clamp values (for ring estimation) from below and above, respectively. Setting both to zero means no such clamping */ radiusScale; /* radius scaling to polar txf */ unsigned int thetaNum, /* number of samples in theta in polar txf */ clampHistoBins; /* number of bins in histogram for computing clamping in terms of percentiles */ const NrrdKernel *rkernel; /* kernel for radial high-pass filtering */ double rkparm[NRRD_KERNEL_PARMS_NUM]; const NrrdKernel *tkernel; /* kernel for blurring along theta */ double tkparm[NRRD_KERNEL_PARMS_NUM]; /* -------- INTERNAL */ const char *cdataIn; /* nin->data as char* */ char *cdataOut; /* nout->data as char* */ size_t sliceSize; /* sizeof slice */ int clampDo; /* is there really is clamping to be done */ double clamp[2]; /* clamping values implied by clampPerc */ /* -------- OUTPUT */ double ringMagnitude; /* L2 norm of ring map; may be useful for optimizing an (unknown) center location */ } NrrdDeringContext; NRRD_EXPORT NrrdDeringContext *nrrdDeringContextNew(void); NRRD_EXPORT NrrdDeringContext *nrrdDeringContextNix(NrrdDeringContext *drc); NRRD_EXPORT int nrrdDeringVerboseSet(NrrdDeringContext *drc, int verbose); NRRD_EXPORT int nrrdDeringLinearInterpSet(NrrdDeringContext *drc, int linterp); NRRD_EXPORT int nrrdDeringVerticalSeamSet(NrrdDeringContext *drc, int vertSeam); NRRD_EXPORT int nrrdDeringInputSet(NrrdDeringContext *drc, const Nrrd *nin); NRRD_EXPORT int nrrdDeringCenterSet(NrrdDeringContext *drc, double cx, double cy); NRRD_EXPORT int nrrdDeringClampPercSet(NrrdDeringContext *drc, double lo, double hi); NRRD_EXPORT int nrrdDeringClampHistoBinsSet(NrrdDeringContext *drc, unsigned int bins); NRRD_EXPORT int nrrdDeringRadiusScaleSet(NrrdDeringContext *drc, double rsc); NRRD_EXPORT int nrrdDeringThetaNumSet(NrrdDeringContext *drc, unsigned int thetaNum); NRRD_EXPORT int nrrdDeringRadialKernelSet(NrrdDeringContext *drc, const NrrdKernel *rkernel, const double rkparm[NRRD_KERNEL_PARMS_NUM]); NRRD_EXPORT int nrrdDeringThetaKernelSet(NrrdDeringContext *drc, const NrrdKernel *tkernel, const double tkparm[NRRD_KERNEL_PARMS_NUM]); NRRD_EXPORT int nrrdDeringExecute(NrrdDeringContext *drc, Nrrd *nout); /* ******** nrrdResample_t typedef ** ** type used to hold filter sample locations and weights in ** nrrdSpatialResample(), and to hold the intermediate sampling ** results. Not as good as templating, but better than hard-coding ** float versus double. Actually, the difference between float and ** double is not exposed in any functions or objects declared in this ** header; it is entirely internal to the operation of ** nrrdSpatialResample() and nrrdResampleExecute() and things ** based on those. ** ** Choose by setting "#if" arg to 1 (for float) or 0 (for double) */ #if 0 typedef float nrrdResample_t; # define nrrdResample_nt nrrdTypeFloat # define NRRD_RESAMPLE_FLOAT 1 #else typedef double nrrdResample_t; # define nrrdResample_nt nrrdTypeDouble # define NRRD_RESAMPLE_FLOAT 0 #endif /* resampleContext.c */ NRRD_EXPORT NrrdResampleContext *nrrdResampleContextNew(void); NRRD_EXPORT NrrdResampleContext *nrrdResampleContextNix(NrrdResampleContext *); NRRD_EXPORT int nrrdResampleDefaultCenterSet(NrrdResampleContext *rsmc, int center); NRRD_EXPORT int nrrdResampleNonExistentSet(NrrdResampleContext *rsmc, int nonExistent); NRRD_EXPORT int nrrdResampleNrrdSet(NrrdResampleContext *rsmc, const Nrrd *nin); NRRD_EXPORT int nrrdResampleInputSet(NrrdResampleContext *rsmc, const Nrrd *nin); NRRD_EXPORT int nrrdResampleKernelSet(NrrdResampleContext *rsmc, unsigned int axIdx, const NrrdKernel *kernel, double kparm[NRRD_KERNEL_PARMS_NUM]); NRRD_EXPORT int nrrdResampleSamplesSet(NrrdResampleContext *rsmc, unsigned int axIdx, size_t samples); NRRD_EXPORT int nrrdResampleRangeSet(NrrdResampleContext *rsmc, unsigned int axIdx, double min, double max); NRRD_EXPORT int nrrdResampleOverrideCenterSet(NrrdResampleContext *rsmc, unsigned int axIdx, int center); NRRD_EXPORT int nrrdResampleRangeFullSet(NrrdResampleContext *rsmc, unsigned int axIdx); NRRD_EXPORT int nrrdResampleBoundarySet(NrrdResampleContext *rsmc, int boundary); NRRD_EXPORT int nrrdResamplePadValueSet(NrrdResampleContext *rsmc, double padValue); NRRD_EXPORT int nrrdResampleTypeOutSet(NrrdResampleContext *rsmc, int typeOut); NRRD_EXPORT int nrrdResampleRenormalizeSet(NrrdResampleContext *rsmc, int renormalize); NRRD_EXPORT int nrrdResampleRoundSet(NrrdResampleContext *rsmc, int round); NRRD_EXPORT int nrrdResampleClampSet(NrrdResampleContext *rsmc, int clamp); NRRD_EXPORT int nrrdResampleExecute(NrrdResampleContext *rsmc, Nrrd *nout); /* resampleNrrd.c */ NRRD_EXPORT int nrrdSpatialResample(Nrrd *nout, const Nrrd *nin, const NrrdResampleInfo *info); NRRD_EXPORT int nrrdSimpleResample(Nrrd *nout, const Nrrd *nin, const NrrdKernel *kernel, const double *parm, const size_t *samples, const double *scalings); /******** connected component extraction and manipulation */ /* ccmethods.c */ NRRD_EXPORT int nrrdCCValid(const Nrrd *nin); NRRD_EXPORT unsigned int nrrdCCSize(Nrrd *nout, const Nrrd *nin); NRRD_EXPORT unsigned int nrrdCCMax(const Nrrd *nin); NRRD_EXPORT unsigned int nrrdCCNum(const Nrrd *nin); /* cc.c */ NRRD_EXPORT int nrrdCCFind(Nrrd *nout, Nrrd **nvalP, const Nrrd *nin, int type, unsigned int conny); NRRD_EXPORT int nrrdCCAdjacency(Nrrd *nout, const Nrrd *nin, unsigned int conny); NRRD_EXPORT int nrrdCCMerge(Nrrd *nout, const Nrrd *nin, Nrrd *nval, int dir, unsigned int maxSize, unsigned int maxNeighbor, unsigned int conny); NRRD_EXPORT int nrrdCCRevalue (Nrrd *nout, const Nrrd *nin, const Nrrd *nval); NRRD_EXPORT int nrrdCCSettle(Nrrd *nout, Nrrd **nvalP, const Nrrd *nin); /******** FFT */ /* fftNrrd.c */ NRRD_EXPORT const int nrrdFFTWEnabled; NRRD_EXPORT int nrrdFFTWWisdomRead(FILE *file); NRRD_EXPORT int nrrdFFT(Nrrd *nout, const Nrrd *nin, unsigned int *axes, unsigned int axesLen, int sign, int rescale, int preCompLevel); NRRD_EXPORT int nrrdFFTWWisdomWrite(FILE *file); /******** kernels (interpolation, 1st and 2nd derivatives) */ /* new kernels should also be registered with meet/meetNrrd.c/meetNrrdKernelAll() */ /* tmfKernel.c nrrdKernelTMF[D+1][C+1][A] is d_c_ef: Dth-derivative, C-order continuous ("smooth"), A-order accurate (for D and C, index 0 accesses the function for -1) */ NRRD_EXPORT NrrdKernel *const nrrdKernelTMF[4][5][5]; NRRD_EXPORT const unsigned int nrrdKernelTMF_maxD; NRRD_EXPORT const unsigned int nrrdKernelTMF_maxC; NRRD_EXPORT const unsigned int nrrdKernelTMF_maxA; /* winKernel.c : various kinds of windowed sincs */ NRRD_EXPORT NrrdKernel *const nrrdKernelHann, /* Hann (cosine-bell) windowed sinc */ *const nrrdKernelHannD, /* 1st derivative of Hann windowed since */ *const nrrdKernelHannDD, /* 2nd derivative */ *const nrrdKernelBlackman, /* Blackman windowed sinc */ *const nrrdKernelBlackmanD, /* 1st derivative of Blackman windowed sinc */ *const nrrdKernelBlackmanDD; /* 2nd derivative */ /* bsplKernel.c: b-splines of various orders; these do not interpolate, but the ApproxInverse kernels are ok for pre-filtering so that they do */ NRRD_EXPORT NrrdKernel *const nrrdKernelBSpline1, /* 1st order B-spline */ *const nrrdKernelBSpline1D, *const nrrdKernelBSpline2, /* 2nd order (quadratic) B-spline */ *const nrrdKernelBSpline2D, *const nrrdKernelBSpline2DD, *const nrrdKernelBSpline3, /* 3rd order (cubic) B-spline */ *const nrrdKernelBSpline3D, *const nrrdKernelBSpline3DD, *const nrrdKernelBSpline3DDD, *const nrrdKernelBSpline3ApproxInverse, *const nrrdKernelBSpline4, /* 4rd order B-spline */ *const nrrdKernelBSpline4D, *const nrrdKernelBSpline4DD, *const nrrdKernelBSpline4DDD, *const nrrdKernelBSpline5, /* 5th order B-spline */ *const nrrdKernelBSpline5D, *const nrrdKernelBSpline5DD, *const nrrdKernelBSpline5DDD, *const nrrdKernelBSpline5ApproxInverse, *const nrrdKernelBSpline6, /* 6th order B-spline */ *const nrrdKernelBSpline6D, *const nrrdKernelBSpline6DD, *const nrrdKernelBSpline6DDD, *const nrrdKernelBSpline7, /* 7th order B-spline */ *const nrrdKernelBSpline7D, *const nrrdKernelBSpline7DD, *const nrrdKernelBSpline7DDD, *const nrrdKernelBSpline7ApproxInverse; /* kernel.c: the rest of the kernels and kernel utility functions */ NRRD_EXPORT NrrdKernel *const nrrdKernelZero, /* zero everywhere (though still takes a single "support" parm[0]) */ *const nrrdKernelBox, /* box filter (nearest neighbor) */ *const nrrdKernelBoxSupportDebug, /* box kernel but with an adjustable support, not for changing the shape of the kernel (as with nrrdKernelBox), but for exercising functions (like nrrd's resampling or gage's probing) that depend on kernel support */ *const nrrdKernelCos4SupportDebug, /* like BoxSupportDebug but instead of box function, using cos(pi*x)^4 within [-0.5,0.5] and 0.0 outside */ *const nrrdKernelCos4SupportDebugD, *const nrrdKernelCos4SupportDebugDD, *const nrrdKernelCos4SupportDebugDDD, *const nrrdKernelCatmullRomSupportDebug, *const nrrdKernelCatmullRomSupportDebugD, *const nrrdKernelCatmullRomSupportDebugDD, *const nrrdKernelCheap, /* an unusual and specially-handled kernel that evaluates to abs(x), for the singular purpose of enabling nearest neighbor downsampling */ *const nrrdKernelHermiteScaleSpaceFlag, /* a kernel that looks like tent, but which exists as a flag for particular gage behavior in the context of doing interpolation along scale in scale-space */ *const nrrdKernelTent, /* tent filter (linear interpolation) */ *const nrrdKernelForwDiff, /* forward-difference-ish 1st deriv. */ *const nrrdKernelCentDiff, /* central-difference-ish 1st deriv. */ *const nrrdKernelBCCubic, /* BC family of cubic polynomial splines */ *const nrrdKernelBCCubicD, /* 1st deriv. of BC cubic family */ *const nrrdKernelBCCubicDD, /* 2nd deriv. of BC cubic family */ *const nrrdKernelCatmullRom, /* aka cubic:0,0.5 */ *const nrrdKernelCatmullRomD, /* aka cubicd:0,0.5 */ *const nrrdKernelCatmullRomDD, /* aka cubicdd:0,0.5 */ *const nrrdKernelAQuartic, /* A family of quartic C2 interp. splines */ *const nrrdKernelAQuarticD, /* 1st deriv. of A quartic family */ *const nrrdKernelAQuarticDD, /* 2nd deriv. of A quartic family */ *const nrrdKernelC3Quintic, /* 0-parm C3 quintic, support [-2,2] */ *const nrrdKernelC3QuinticD, /* 1st deriv of C3Quintic */ *const nrrdKernelC3QuinticDD, /* 2nd deriv of C3Quintic */ *const nrrdKernelC4Hexic, /* 0-parm C4 hex, support [-3,3] */ *const nrrdKernelC4HexicD, /* 1st deriv of C4Hexic */ *const nrrdKernelC4HexicDD, /* 2nd deriv of C4Hexic */ *const nrrdKernelC4HexicDDD, /* 3rd deriv of C4Hexic */ *const nrrdKernelC4HexicApproxInverse, *const nrrdKernelC5Septic, /* 0-parm C5 sept, support [-4,4] */ *const nrrdKernelC5SepticD, /* 1st deriv of C5Septic */ *const nrrdKernelC5SepticDD, /* 2nd deriv of C5Septic */ *const nrrdKernelC5SepticDDD, /* 3rd deriv of C5Septic */ *const nrrdKernelC5SepticApproxInverse, *const nrrdKernelGaussian, /* Gaussian */ *const nrrdKernelGaussianD, /* 1st derivative of Gaussian */ *const nrrdKernelGaussianDD, /* 2nd derivative of Gaussian */ *const nrrdKernelDiscreteGaussian; /* Discrete Gaussian-like kernel for scale-space analysis */ NRRD_EXPORT int nrrdKernelParse(const NrrdKernel **kernelP, double *parm, const char *str); NRRD_EXPORT int nrrdKernelSpecParse(NrrdKernelSpec *ksp, const char *str); NRRD_EXPORT int nrrdKernelSpecSprint(char str[AIR_STRLEN_LARGE], const NrrdKernelSpec *ksp); NRRD_EXPORT int nrrdKernelSprint(char str[AIR_STRLEN_LARGE], const NrrdKernel *kernel, const double *kparm); NRRD_EXPORT int nrrdKernelCompare(const NrrdKernel *kernA, const double parmA[NRRD_KERNEL_PARMS_NUM], const NrrdKernel *kernB, const double parmB[NRRD_KERNEL_PARMS_NUM], int *differ, char explain[AIR_STRLEN_LARGE]); NRRD_EXPORT int nrrdKernelCheck(const NrrdKernel *kern, const double parm[NRRD_KERNEL_PARMS_NUM], size_t evalNum, double epsilon, unsigned int diffOkEvalMax, unsigned int diffOkIntglMax, const NrrdKernel *dkern, const double dparm[NRRD_KERNEL_PARMS_NUM]); /* ---- END non-NrrdIO */ #ifdef __cplusplus } #endif #endif /* NRRD_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/nrrd/apply2D.c0000664000175000017500000003455512165631065017041 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" enum { kindLut=0, kindRmap=1 }; /* ** _nrrdApply2DSetUp() ** ** some error checking and initializing needed for 2D LUTS and regular ** maps. The intent is that if this succeeds, then there is no need ** for any further error checking. ** ** The only thing this function DOES is allocate the output nrrd, and ** set meta information. The rest is just error checking. ** ** The given NrrdRange has to be fleshed out by the caller: it can't ** be NULL, and both range->min and range->max must exist. */ int _nrrdApply2DSetUp(Nrrd *nout, const Nrrd *nin, const NrrdRange *range0, const NrrdRange *range1, const Nrrd *nmap, int kind, int typeOut, int rescale0, int rescale1) { static const char me[]="_nrrdApply2DSetUp"; char *mapcnt; char nounStr[][AIR_STRLEN_SMALL]={"2D lut", "2D regular map"}; char verbStr[][AIR_STRLEN_SMALL]={"lut2", "rmap2"}; int mapAxis, copyMapAxis0=AIR_FALSE, axisMap[NRRD_DIM_MAX]; unsigned int dim, entLen; size_t size[NRRD_DIM_MAX]; double domMin, domMax; if (nout == nin) { biffAddf(NRRD, "%s: due to laziness, nout==nin always disallowed", me); return 1; } if (airEnumValCheck(nrrdType, typeOut)) { biffAddf(NRRD, "%s: invalid requested output type %d", me, typeOut); return 1; } if (nrrdTypeBlock == nin->type || nrrdTypeBlock == typeOut) { biffAddf(NRRD, "%s: input or requested output type is %s, need scalar", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!(2 == nin->axis[0].size)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: input axis[0] must have size 2 (not %s)", me, airSprintSize_t(stmp, nin->axis[0].size)); return 1; } if (!(nin->dim > 1)) { biffAddf(NRRD, "%s: input dimension must be > 1 (not %u)", me, nin->dim); return 1; } if (rescale0 && !(range0 && AIR_EXISTS(range0->min) && AIR_EXISTS(range0->max))) { biffAddf(NRRD, "%s: want axis 0 rescaling but didn't get range, or " "not both range->{min,max} exist", me); return 1; } if (rescale1 && !(range1 && AIR_EXISTS(range1->min) && AIR_EXISTS(range1->max))) { biffAddf(NRRD, "%s: want axis 1 rescaling but didn't get range, or " "not both range->{min,max} exist", me); return 1; } mapAxis = nmap->dim - 2; if (!(0 == mapAxis || 1 == mapAxis)) { biffAddf(NRRD, "%s: dimension of %s should be 2 or 3, not %d", me, nounStr[kind], nmap->dim); return 1; } copyMapAxis0 = (1 == mapAxis); domMin = _nrrdApplyDomainMin(nmap, AIR_FALSE, mapAxis); domMax = _nrrdApplyDomainMax(nmap, AIR_FALSE, mapAxis); if (!( domMin < domMax )) { biffAddf(NRRD, "%s: (axis %d) domain min (%g) not less than max (%g)", me, mapAxis, domMin, domMax); return 1; } domMin = _nrrdApplyDomainMin(nmap, AIR_FALSE, mapAxis+1); domMax = _nrrdApplyDomainMax(nmap, AIR_FALSE, mapAxis+1); if (!( domMin < domMax )) { biffAddf(NRRD, "%s: (axis %d) domain min (%g) not less than max (%g)", me, mapAxis+1, domMin, domMax); return 1; } if (nrrdHasNonExist(nmap)) { biffAddf(NRRD, "%s: %s nrrd has non-existent values", me, nounStr[kind]); return 1; } entLen = mapAxis ? AIR_CAST(unsigned int, nmap->axis[0].size) : 1; if (mapAxis + nin->dim - 1 > NRRD_DIM_MAX) { biffAddf(NRRD, "%s: input nrrd dim %d through non-scalar %s exceeds " "NRRD_DIM_MAX %d", me, nin->dim, nounStr[kind], NRRD_DIM_MAX); return 1; } if (mapAxis) { size[0] = entLen; axisMap[0] = -1; } for (dim=1; dimdim; dim++) { size[dim-1+mapAxis] = nin->axis[dim].size; axisMap[dim-1+mapAxis] = dim; } /* fprintf(stderr, "##%s: pre maybe alloc: nout->data = %p\n", me, nout->data); for (dim=0; dimdim - 1; dim++) { fprintf(stderr, " size[%d] = %d\n", dim, (int)size[dim]); } fprintf(stderr, " nout->dim = %u; nout->type = %d = %s; sizes = %u,%u\n", nout->dim, nout->type, airEnumStr(nrrdType, nout->type), AIR_CAST(unsigned int, nout->axis[0].size), AIR_CAST(unsigned int, nout->axis[1].size)); fprintf(stderr, " typeOut = %d = %s\n", typeOut, airEnumStr(nrrdType, typeOut)); */ if (nrrdMaybeAlloc_nva(nout, typeOut, nin->dim - 1 + mapAxis, size)) { biffAddf(NRRD, "%s: couldn't allocate output nrrd", me); return 1; } /* fprintf(stderr, " nout->dim = %d; nout->type = %d = %s\n", nout->dim, nout->type, airEnumStr(nrrdType, nout->type)); for (dim=0; dimdim; dim++) { fprintf(stderr, " size[%d] = %d\n", dim, (int)nout->axis[dim].size); } fprintf(stderr, "##%s: post maybe alloc: nout->data = %p\n", me, nout->data); */ if (nrrdAxisInfoCopy(nout, nin, axisMap, NRRD_AXIS_INFO_NONE)) { biffAddf(NRRD, "%s: trouble copying axis info", me); return 1; } if (copyMapAxis0) { _nrrdAxisInfoCopy(nout->axis + 0, nmap->axis + 0, NRRD_AXIS_INFO_SIZE_BIT); } mapcnt = _nrrdContentGet(nmap); if (nrrdContentSet_va(nout, verbStr[kind], nin, "%s", mapcnt)) { biffAddf(NRRD, "%s:", me); free(mapcnt); return 1; } free(mapcnt); nrrdBasicInfoInit(nout, (NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT)); return 0; } /* ** _nrrdApply2DLutOrRegMap() ** ** the guts of nrrdApply2DLut and nrrdApply2DRegMap ** ** yikes, does NOT use biff, since we're only supposed to be called ** after copious error checking. ** ** FOR INSTANCE, this allows nout == nin, which could be a big ** problem if mapAxis == 1. ** ** we don't need a typeOut arg because nout has already been allocated ** as some specific type; we'll look at that. ** ** NOTE: non-existent values get passed through regular maps and luts ** "unchanged". However, if the output type is integral, the results ** are probaby undefined. HEY: there is currently no warning message ** or error handling based on nrrdStateDisallowIntegerNonExist, but ** there really should be. */ int _nrrdApply2DLutOrRegMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *range0, const NrrdRange *range1, const Nrrd *nmap, int ramps, int rescale0, int rescale1) { static const char me[]="_nrrdApply2DLutOrRegMap"; char *inData, *outData, *mapData, *entData; size_t N, I; double (*inLoad)(const void *v), (*mapLup)(const void *v, size_t I), (*outInsert)(void *v, size_t I, double d), val0, val1, domMin0, domMax0, domMin1, domMax1; unsigned int i, mapAxis, mapLen0, mapLen1, mapIdx0, mapIdx1, entSize, entLen, inSize, outSize; mapAxis = nmap->dim - 2; /* axis of nmap containing entries */ mapData = (char *)nmap->data; /* map data, as char* */ /* low end of map domain */ domMin0 = _nrrdApplyDomainMin(nmap, ramps, mapAxis + 0); domMin1 = _nrrdApplyDomainMin(nmap, ramps, mapAxis + 1); /* high end of map domain */ domMax0 = _nrrdApplyDomainMax(nmap, ramps, mapAxis + 0); domMax1 = _nrrdApplyDomainMax(nmap, ramps, mapAxis + 1); mapLen0 = AIR_CAST(unsigned int, nmap->axis[mapAxis+0].size); /* number of entries in map axis 0 */ mapLen1 = AIR_CAST(unsigned int, nmap->axis[mapAxis+1].size); /* number of entries in map axis 1 */ mapLup = nrrdDLookup[nmap->type]; /* how to get doubles out of map */ inData = (char *)nin->data; /* input data, as char* */ inLoad = nrrdDLoad[nin->type]; /* how to get doubles out of nin */ inSize = AIR_CAST(unsigned int, nrrdElementSize(nin)); /* size of one input value */ outData = (char *)nout->data; /* output data, as char* */ outInsert = nrrdDInsert[nout->type]; /* putting doubles into output */ entLen = (mapAxis /* number of elements in one entry */ ? AIR_CAST(unsigned int, nmap->axis[0].size) : 1); outSize = entLen*AIR_CAST(unsigned int, nrrdElementSize(nout)); /* size of entry in output */ entSize = entLen*AIR_CAST(unsigned int, nrrdElementSize(nmap)); /* size of entry in map */ /* fprintf(stderr, "!%s: entLen = %u, mapLen = %u,%u\n", me, entLen, mapLen0, mapLen1); */ N = nrrdElementNumber(nin)/2; /* number of value pairs to be mapped */ /* _VV = 1; */ if (ramps) { fprintf(stderr, "%s: PANIC: unimplemented\n", me); exit(1); } else { /* lookup table */ for (I=0; Imin, val0, range0->max, domMin0, domMax0); } if (rescale1) { val1 = AIR_AFFINE(range1->min, val1, range1->max, domMin1, domMax1); } if (AIR_EXISTS(val0) && AIR_EXISTS(val1)) { mapIdx0 = airIndexClamp(domMin0, val0, domMax0, mapLen0); mapIdx1 = airIndexClamp(domMin1, val1, domMax1, mapLen1); entData = mapData + entSize*(mapIdx0 + mapLen0*mapIdx1); for (i=0; iaxis[mapAxis].min; ret = AIR_EXISTS(ret) ? ret : 0; return ret; } double _nrrdApplyDomainMax(const Nrrd *nmap, int ramps, int mapAxis) { double ret; ret = nmap->axis[mapAxis].max; if (!AIR_EXISTS(ret)) { ret = AIR_CAST(double, nmap->axis[mapAxis].size); ret = ramps ? ret-1 : ret; } return ret; } /* ** _nrrdApply1DSetUp() ** ** some error checking and initializing needed for 1D LUTS, regular, ** and irregular maps. The intent is that if this succeeds, then ** there is no need for any further error checking. ** ** The only thing this function DOES is allocate the output nrrd, and ** set meta information. The rest is just error checking. ** ** The given NrrdRange has to be fleshed out by the caller: it can't ** be NULL, and both range->min and range->max must exist. */ int _nrrdApply1DSetUp(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, const Nrrd *nmap, int kind, int typeOut, int rescale, int multi) { static const char me[]="_nrrdApply1DSetUp"; char *mapcnt; char nounStr[][AIR_STRLEN_SMALL]={"lut", "regular map", "irregular map"}; char mnounStr[][AIR_STRLEN_SMALL]={"multi lut", "multi regular map", "multi irregular map"}; /* wishful thinking */ char verbStr[][AIR_STRLEN_SMALL]={"lut", "rmap", "imap"}; char mverbStr[][AIR_STRLEN_SMALL]={"mlut", "mrmap", "mimap"}; /* wishful thinking */ int mapAxis, copyMapAxis0=AIR_FALSE, axisMap[NRRD_DIM_MAX]; unsigned int ax, dim, entLen; size_t size[NRRD_DIM_MAX]; double domMin, domMax; if (nout == nin) { biffAddf(NRRD, "%s: due to laziness, nout==nin always disallowed", me); return 1; } if (airEnumValCheck(nrrdType, typeOut)) { biffAddf(NRRD, "%s: invalid requested output type %d", me, typeOut); return 1; } if (nrrdTypeBlock == nin->type || nrrdTypeBlock == typeOut) { biffAddf(NRRD, "%s: input or requested output type is %s, need scalar", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (rescale) { if (!range) { biffAddf(NRRD, "%s: want rescaling but didn't get a range", me); return 1; } if (!( AIR_EXISTS(range->min) && AIR_EXISTS(range->max) )) { biffAddf(NRRD, "%s: want rescaling but not both " "range->{min,max} %g %g exist", me, range->min, range->max); return 1; } /* HEY: consider adding an error check for range->min == range->max here; the code below now guards AIR_AFFINE(range->min, val, range->max, domMin, domMax) against producing NaNs, but maybe that's too clever */ } if (kindLut == kind || kindRmap == kind) { if (!multi) { mapAxis = nmap->dim - 1; if (!(0 == mapAxis || 1 == mapAxis)) { biffAddf(NRRD, "%s: dimension of %s should be 1 or 2, not %d", me, nounStr[kind], nmap->dim); return 1; } copyMapAxis0 = (1 == mapAxis); } else { mapAxis = nmap->dim - nin->dim - 1; if (!(0 == mapAxis || 1 == mapAxis)) { biffAddf(NRRD, "%s: dimension of %s should be %d or %d, not %d", me, mnounStr[kind], nin->dim + 1, nin->dim + 2, nmap->dim); return 1; } copyMapAxis0 = (1 == mapAxis); /* need to make sure the relevant sizes match */ for (ax=0; axdim; ax++) { unsigned int taxi = mapAxis + 1 + ax; if (taxi > NRRD_DIM_MAX-1) { biffAddf(NRRD, "%s: test axis index %u exceeds NRRD_DIM_MAX-1 %u", me, taxi, NRRD_DIM_MAX-1); return 1; } if (nin->axis[ax].size != nmap->axis[taxi].size) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: input and mmap don't have compatible sizes: " "nin->axis[%d].size (%s) " "!= nmap->axis[%d].size (%s): ", me, ax, airSprintSize_t(stmp1, nin->axis[ax].size), mapAxis + 1 + ax, airSprintSize_t(stmp2, nmap->axis[taxi].size)); return 1; } } } domMin = _nrrdApplyDomainMin(nmap, AIR_FALSE, mapAxis); domMax = _nrrdApplyDomainMax(nmap, AIR_FALSE, mapAxis); if (!( domMin < domMax )) { biffAddf(NRRD, "%s: (axis %d) domain min (%g) not less than max (%g)", me, mapAxis, domMin, domMax); return 1; } if (nrrdHasNonExist(nmap)) { biffAddf(NRRD, "%s: %s nrrd has non-existent values", me, multi ? mnounStr[kind] : nounStr[kind]); return 1; } entLen = mapAxis ? AIR_CAST(unsigned int, nmap->axis[0].size) : 1; } else { if (multi) { biffAddf(NRRD, "%s: sorry, multi irregular maps not implemented", me); return 1; } /* its an irregular map */ if (nrrd1DIrregMapCheck(nmap)) { biffAddf(NRRD, "%s: problem with irregular map", me); return 1; } /* mapAxis has no meaning for irregular maps, but we'll pretend ... */ mapAxis = nmap->axis[0].size == 2 ? 0 : 1; copyMapAxis0 = AIR_TRUE; entLen = AIR_CAST(unsigned int, nmap->axis[0].size-1); } if (mapAxis + nin->dim > NRRD_DIM_MAX) { biffAddf(NRRD, "%s: input nrrd dim %d through non-scalar %s exceeds " "NRRD_DIM_MAX %d", me, nin->dim, multi ? mnounStr[kind] : nounStr[kind], NRRD_DIM_MAX); return 1; } nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size+mapAxis); if (mapAxis) { size[0] = entLen; axisMap[0] = -1; } for (dim=0; dimdim; dim++) { axisMap[dim+mapAxis] = dim; } /* fprintf(stderr, "##%s: pre maybe alloc: nout->data = %p\n", me, nout->data); for (dim=0; dimdim; dim++) { fprintf(stderr, " size[%d] = %d\n", d, (int)size[d]); } fprintf(stderr, " nout->dim = %d; nout->type = %d = %s; sizes = %d,%d\n", nout->dim, nout->type, airEnumStr(nrrdType, nout->type)); fprintf(stderr, " typeOut = %d = %s\n", typeOut, airEnumStr(nrrdType, typeOut)); */ if (nrrdMaybeAlloc_nva(nout, typeOut, mapAxis + nin->dim, size)) { biffAddf(NRRD, "%s: couldn't allocate output nrrd", me); return 1; } /* fprintf(stderr, " nout->dim = %d; nout->type = %d = %s\n", nout->dim, nout->type, airEnumStr(nrrdType, nout->type), nout->axis[0].size, nout->axis[1].size); for (d=0; ddim; d++) { fprintf(stderr, " size[%d] = %d\n", d, (int)nout->axis[d].size); } fprintf(stderr, "##%s: post maybe alloc: nout->data = %p\n", me, nout->data); */ if (nrrdAxisInfoCopy(nout, nin, axisMap, NRRD_AXIS_INFO_NONE)) { biffAddf(NRRD, "%s: trouble copying axis info", me); return 1; } if (copyMapAxis0) { _nrrdAxisInfoCopy(nout->axis + 0, nmap->axis + 0, NRRD_AXIS_INFO_SIZE_BIT); } mapcnt = _nrrdContentGet(nmap); if (nrrdContentSet_va(nout, multi ? mverbStr[kind] : verbStr[kind], nin, "%s", mapcnt)) { biffAddf(NRRD, "%s:", me); free(mapcnt); return 1; } free(mapcnt); if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } /* ** _nrrdApply1DLutOrRegMap() ** ** the guts of nrrdApply1DLut and nrrdApply1DRegMap ** ** yikes, does NOT use biff, since we're only supposed to be called ** after copious error checking. ** ** FOR INSTANCE, this allows nout == nin, which could be a big ** problem if mapAxis == 1. ** ** we don't need a typeOut arg because nout has already been allocated ** as some specific type; we'll look at that. ** ** NOTE: non-existent values get passed through regular maps and luts ** "unchanged". However, if the output type is integral, the results ** are probaby undefined. HEY: there is currently no warning message ** or error handling based on nrrdStateDisallowIntegerNonExist, but ** there really should be. */ int _nrrdApply1DLutOrRegMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, const Nrrd *nmap, int ramps, int rescale, int multi) { /* static const char me[]="_nrrdApply1DLutOrRegMap"; */ char *inData, *outData, *mapData, *entData0, *entData1; size_t N, I; double (*inLoad)(const void *v), (*mapLup)(const void *v, size_t I), (*outInsert)(void *v, size_t I, double d), val, mapIdxFrac, domMin, domMax; unsigned int i, mapAxis, mapLen, mapIdx, entSize, entLen, inSize, outSize; if (!multi) { mapAxis = nmap->dim - 1; /* axis of nmap containing entries */ } else { mapAxis = nmap->dim - nin->dim - 1; } mapData = (char *)nmap->data; /* map data, as char* */ /* low end of map domain */ domMin = _nrrdApplyDomainMin(nmap, ramps, mapAxis); /* high end of map domain */ domMax = _nrrdApplyDomainMax(nmap, ramps, mapAxis); mapLen = AIR_CAST(unsigned int, nmap->axis[mapAxis].size); /* number of entries in map */ mapLup = nrrdDLookup[nmap->type]; /* how to get doubles out of map */ inData = (char *)nin->data; /* input data, as char* */ inLoad = nrrdDLoad[nin->type]; /* how to get doubles out of nin */ inSize = AIR_CAST(unsigned int, nrrdElementSize(nin)); /* size of one input value */ outData = (char *)nout->data; /* output data, as char* */ outInsert = nrrdDInsert[nout->type]; /* putting doubles into output */ entLen = (mapAxis /* number of elements in one entry */ ? AIR_CAST(unsigned int, nmap->axis[0].size) : 1); outSize = entLen*AIR_CAST(unsigned int, nrrdElementSize(nout)); /* size of entry in output */ entSize = entLen*AIR_CAST(unsigned int, nrrdElementSize(nmap)); /* size of entry in map */ N = nrrdElementNumber(nin); /* the number of values to be mapped */ if (ramps) { /* regular map */ for (I=0; I ", me, val); ... */ if (rescale) { val = (range->min != range->max ? AIR_AFFINE(range->min, val, range->max, domMin, domMax) : domMin); /* ... fprintf(stderr, "\nb% 31.15f (min,max = %g,%g)--> ", val, range->min, range->max); ... */ } /* ... fprintf(stderr, "\nc% 31.15f --> clamp(%g,%g), %d --> ", val, domMin, domMax, mapLen); ... */ if (AIR_EXISTS(val)) { val = AIR_CLAMP(domMin, val, domMax); mapIdxFrac = AIR_AFFINE(domMin, val, domMax, 0, mapLen-1); /* ... fprintf(stderr, "mapIdxFrac = \nd% 31.15f --> ", mapIdxFrac); ... */ mapIdx = (unsigned int)mapIdxFrac; mapIdx -= mapIdx == mapLen-1; mapIdxFrac -= mapIdx; /* ... fprintf(stderr, "%s: (%d,\ne% 31.15f) --> \n", me, mapIdx, mapIdxFrac); ... */ entData0 = mapData + mapIdx*entSize; entData1 = mapData + (mapIdx+1)*entSize; for (i=0; imin != range->max ? AIR_AFFINE(range->min, val, range->max, domMin, domMax) : domMin); } if (AIR_EXISTS(val)) { mapIdx = airIndexClamp(domMin, val, domMax, mapLen); entData0 = mapData + mapIdx*entSize; for (i=0; i= 2, non-block-type, no non-existent ** values in range. If the first point's position is non-existent, ** than the first three points positions must be -inf, NaN, and +inf, ** and none of the other points locations can be non-existent, and ** they must increase monotonically. There must be at least two ** points with existent positions. */ int nrrd1DIrregMapCheck(const Nrrd *nmap) { static const char me[]="nrrd1DIrregMapCheck"; double (*mapLup)(const void *v, size_t I); int i, entLen, mapLen, baseI; size_t min[2], max[2]; Nrrd *nrange; if (!nmap) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdCheck(nmap)) { biffAddf(NRRD, "%s: ", me); return 1; } if (nrrdTypeBlock == nmap->type) { biffAddf(NRRD, "%s: map is %s type, need scalar", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (2 != nmap->dim) { biffAddf(NRRD, "%s: map needs to have dimension 2, not %d", me, nmap->dim); return 1; } entLen = AIR_CAST(unsigned int,nmap->axis[0].size); mapLen = AIR_CAST(unsigned int,nmap->axis[1].size); if (!( entLen >= 2 && mapLen >= 2 )) { biffAddf(NRRD, "%s: both map's axes sizes should be >= 2 (not %d,%d)", me, entLen, mapLen); return 1; } min[0] = 1; max[0] = nmap->axis[0].size-1; min[1] = 0; max[1] = nmap->axis[1].size-1; if (nrrdCrop(nrange=nrrdNew(), nmap, min, max)) { biffAddf(NRRD, "%s: couldn't crop to isolate range of map", me); nrrdNuke(nrange); return 1; } if (nrrdHasNonExist(nrange)) { biffAddf(NRRD, "%s: map has non-existent values in its range", me); nrrdNuke(nrange); return 1; } nrrdNuke(nrange); mapLup = nrrdDLookup[nmap->type]; if (AIR_EXISTS(mapLup(nmap->data, 0))) { baseI = 0; } else { baseI = 3; if (!( mapLen >= 5 )) { biffAddf(NRRD, "%s: length of map w/ non-existent locations must " "be >= 5 (not %d)", me, mapLen); return 1; } if (!( airFP_NEG_INF == airFPClass_d(mapLup(nmap->data, 0*entLen)) && airFP_QNAN == airFPClass_d(mapLup(nmap->data, 1*entLen)) && airFP_POS_INF == airFPClass_d(mapLup(nmap->data, 2*entLen)) )) { biffAddf(NRRD, "%s: 1st entry's position non-existent, but position " "of 1st three entries (%g:%d,%g:%d,%g:%d) not " "-inf, NaN, and +inf", me, mapLup(nmap->data, 0*entLen), airFPClass_d(mapLup(nmap->data, 0*entLen)), mapLup(nmap->data, 1*entLen), airFPClass_d(mapLup(nmap->data, 1*entLen)), mapLup(nmap->data, 2*entLen), airFPClass_d(mapLup(nmap->data, 2*entLen))); return 1; } } for (i=baseI; idata, i*entLen))) { biffAddf(NRRD, "%s: entry %d has non-existent position", me, i); return 1; } } for (i=baseI; idata, i*entLen) < mapLup(nmap->data, (i+1)*entLen) )) { biffAddf(NRRD, "%s: map entry %d pos (%g) not < entry %d pos (%g)", me, i, mapLup(nmap->data, i*entLen), i+1, mapLup(nmap->data, (i+1)*entLen)); return 1; } } return 0; } /* ******** nrrd1DIrregAclCheck() ** ** returns zero only on valid accelerators for 1D irregular mappings */ int nrrd1DIrregAclCheck(const Nrrd *nacl) { static const char me[]="nrrd1DIrregAclCheck"; if (!nacl) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdCheck(nacl)) { biffAddf(NRRD, "%s: ", me); return 1; } if (nrrdTypeUShort != nacl->type) { biffAddf(NRRD, "%s: type should be %s, not %s", me, airEnumStr(nrrdType, nrrdTypeUShort), airEnumStr(nrrdType, nacl->type)); return 1; } if (2 != nacl->dim) { biffAddf(NRRD, "%s: dimension should be 2, not %d", me, nacl->dim); return 1; } if (!( nacl->axis[0].size == 2 && nacl->axis[1].size >= 2 )) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: sizes (%s,%s) not (2,>=2)", me, airSprintSize_t(stmp1, nacl->axis[0].size), airSprintSize_t(stmp2, nacl->axis[1].size)); return 1; } return 0; } /* ** _nrrd1DIrregMapDomain() ** ** ALLOCATES an array of doubles storing the existent control point ** locations, and sets its length in *poslenP. If there are the three ** points with non-existent locations, these are ignored. ** ** Assumes that nrrd1DIrregMapCheck has been called on "nmap". */ double * _nrrd1DIrregMapDomain(int *posLenP, int *baseIP, const Nrrd *nmap) { static const char me[]="_nrrd1DIrregMapDomain"; int i, entLen, baseI, posLen; double *pos, (*mapLup)(const void *v, size_t I); mapLup = nrrdDLookup[nmap->type]; baseI = AIR_EXISTS(mapLup(nmap->data, 0)) ? 0 : 3; if (baseIP) { *baseIP = baseI; } entLen = AIR_CAST(unsigned int,nmap->axis[0].size); posLen = AIR_CAST(unsigned int,nmap->axis[1].size) - baseI; if (posLenP) { *posLenP = posLen; } pos = (double*)malloc(posLen * sizeof(double)); if (!pos) { biffAddf(NRRD, "%s: couldn't allocate %d doubles\n", me, posLen); return NULL; } for (i=0; idata, (baseI+i)*entLen); } return pos; } /* ** _nrrd1DIrregFindInterval() ** ** The hard part of doing 1D irregular mapping: given an array of ** control point locations, and a value, find which interval the value ** lies in. The lowest and highest possible indices are given in ** "loI" and "hiI". Results are undefined if these do not in fact ** bound the location of correct interval, or if loI > hiI, or if the ** query positon "p" is not in the domain vector "pos". Intervals are ** identified by the integral index of the LOWER of the two control ** points spanning the interval. ** ** This imposes the same structure of half-open intervals that ** is done by airIndex(). That is, a value p is in interval i ** if pos[i] <= p < pos[i+1] for all but the last interval, and ** pos[i] <= p <= pos[i+1] for the last interval (in which case ** i == hiI) */ int _nrrd1DIrregFindInterval(const double *pos, double p, int loI, int hiI) { int midI; /* fprintf(stderr, "##%s(%g): hi: %d/%g-%g | %d/%g-%g\n", "_nrrd1DIrregFindInterval", p, loI, pos[loI], pos[loI+1], hiI, pos[hiI], pos[hiI+1]); */ while (loI < hiI) { midI = (loI + hiI)/2; if ( pos[midI] <= p && ((midI < hiI && p < pos[midI+1]) || (midI == hiI && p <= pos[midI+1])) ) { /* p is between (pos[midI],pos[midI+1]): we're done */ loI = hiI = midI; } else if (pos[midI] > p) { /* p is below interval midI: midI-1 is valid upper bound */ hiI = midI-1; } else { /* p is above interval midI: midI+1 is valid lower bound */ loI = midI+1; } /* fprintf(stderr, "##%s(%g): %d/%g-%g | %d/%g-%g | %d/%g-%g\n", "_nrrd1DIrregFindInterval", p, loI, pos[loI], pos[loI+1], midI, pos[midI], pos[midI+1], hiI, pos[hiI], pos[hiI+1]); */ } return loI; } /* ******** nrrd1DIrregAclGenerate() ** ** Generates the "acl" that is used to speed up the action of ** nrrdApply1DIrregMap(). Basically, the domain of the map ** is quantized into "acllen" bins, and for each bin, the ** lowest and highest possible map interval is stored. This ** either obviates or speeds up the task of finding which ** interval contains a given value. ** ** Assumes that nrrd1DIrregMapCheck has been called on "nmap". */ int nrrd1DIrregAclGenerate(Nrrd *nacl, const Nrrd *nmap, size_t aclLen) { static const char me[]="nrrd1DIrregAclGenerate"; int posLen; unsigned int ii; unsigned short *acl; double lo, hi, min, max, *pos; if (!(nacl && nmap)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!(aclLen >= 2)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: given acl length (%s) is too small", me, airSprintSize_t(stmp, aclLen)); return 1; } if (nrrdMaybeAlloc_va(nacl, nrrdTypeUShort, 2, AIR_CAST(size_t, 2), AIR_CAST(size_t, aclLen))) { biffAddf(NRRD, "%s: ", me); return 1; } acl = (unsigned short *)nacl->data; pos = _nrrd1DIrregMapDomain(&posLen, NULL, nmap); if (!pos) { biffAddf(NRRD, "%s: couldn't determine domain", me); return 1; } nacl->axis[1].min = min = pos[0]; nacl->axis[1].max = max = pos[posLen-1]; for (ii=0; ii<=aclLen-1; ii++) { lo = AIR_AFFINE(0, ii, aclLen, min, max); hi = AIR_AFFINE(0, ii+1, aclLen, min, max); acl[0 + 2*ii] = _nrrd1DIrregFindInterval(pos, lo, 0, posLen-2); acl[1 + 2*ii] = _nrrd1DIrregFindInterval(pos, hi, 0, posLen-2); } free(pos); return 0; } /* ******** nrrdApply1DIrregMap() ** ** Linear interpolation between irregularly spaced control points. ** Obviously, the location of the control point has to be given ** explicitly. The map nrrd must have dimension 2, and each ** control point is represented by a scanline along axis 0. The ** first value is the position of the control point, and the remaining ** value(s) are linearly weighted according to the position of the ** input value among the control point locations. ** ** To allow "coloring" of non-existent values -inf, NaN, and +inf, if ** the very first value of the map (the location of the first control ** point) is non-existent, then the first three control point locations ** must be -inf, NaN, and +inf, in that order, and the information ** about these points will be used for corresponding input values. ** Doing this makes everything slower, however, because airFPClass_f() ** is called on every single value. ** ** This assumes that nrrd1DIrregMapCheck has been called on "nmap", ** and that nrrd1DIrregAclCheck has been called on "nacl" (if it is ** non-NULL). */ int nrrdApply1DIrregMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range, const Nrrd *nmap, const Nrrd *nacl, int typeOut, int rescale) { static const char me[]="nrrdApply1DIrregMap"; size_t N, I; int i, *acl, entLen, posLen, aclLen, mapIdx, aclIdx, entSize, colSize, inSize, lo, hi, baseI; double val, *pos, domMin, domMax, mapIdxFrac, (*mapLup)(const void *v, size_t I), (*inLoad)(const void *v), (*outInsert)(void *v, size_t I, double d); char *inData, *outData, *entData0, *entData1; NrrdRange *range; airArray *mop; /* fprintf(stderr, "!%s: rescale = %d\n", me, rescale); */ if (!(nout && nmap && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); if (_range) { range = nrrdRangeCopy(_range); nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); } else { range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); } airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); if (_nrrdApply1DSetUp(nout, nin, range, nmap, kindImap, typeOut, rescale, AIR_FALSE /* multi */)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } if (nacl && nrrd1DIrregAclCheck(nacl)) { biffAddf(NRRD, "%s: given acl isn't valid", me); airMopError(mop); return 1; } if (nacl) { acl = (int *)nacl->data; aclLen = AIR_CAST(unsigned int,nacl->axis[1].size); } else { acl = NULL; aclLen = 0; } pos = _nrrd1DIrregMapDomain(&posLen, &baseI, nmap); if (!pos) { biffAddf(NRRD, "%s: couldn't determine domain", me); airMopError(mop); return 1; } airMopAdd(mop, pos, airFree, airMopAlways); mapLup = nrrdDLookup[nmap->type]; inData = (char *)nin->data; inLoad = nrrdDLoad[nin->type]; inSize = AIR_CAST(unsigned int,nrrdElementSize(nin)); mapLup = nrrdDLookup[nmap->type]; entLen = AIR_CAST(unsigned int,nmap->axis[0].size); /* entLen is really 1 + entry length */ entSize = entLen*AIR_CAST(unsigned int,nrrdElementSize(nmap)); colSize = (entLen-1)*AIR_CAST(unsigned int,nrrdTypeSize[typeOut]); outData = (char *)nout->data; outInsert = nrrdDInsert[nout->type]; domMin = pos[0]; domMax = pos[posLen-1]; /* fprintf(stderr, "!%s: domMin, domMax = %g, %g\n", me, domMin, domMax); */ N = nrrdElementNumber(nin); for (I=0; Idata) + mapIdx*entSize; for (i=1; imin != range->max ? AIR_AFFINE(range->min, val, range->max, domMin, domMax) : domMin); } val = AIR_CLAMP(domMin, val, domMax); if (acl) { aclIdx = airIndex(domMin, val, domMax, aclLen); lo = acl[0 + 2*aclIdx]; hi = acl[1 + 2*aclIdx]; } else { lo = 0; hi = posLen-2; } if (lo < hi) { mapIdx = _nrrd1DIrregFindInterval(pos, val, lo, hi); } else { /* acl did its job ==> lo == hi */ mapIdx = lo; } /* fprintf(stderr, "!%s: --> val = %g; lo,hi = %d,%d, mapIdx = %d\n", me, val, lo, hi, mapIdx); */ } mapIdxFrac = AIR_AFFINE(pos[mapIdx], val, pos[mapIdx+1], 0.0, 1.0); /* fprintf(stderr, "!%s: mapIdxFrac = %g\n", me, mapIdxFrac); */ entData0 = (char*)(nmap->data) + (baseI+mapIdx)*entSize; entData1 = (char*)(nmap->data) + (baseI+mapIdx+1)*entSize; for (i=1; itype || nrrdTypeBlock == _nsubst->type) { biffAddf(NRRD, "%s: input or substitution type is %s, need scalar", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (2 != _nsubst->dim) { biffAddf(NRRD, "%s: substitution table has to be 2-D, not %d-D", me, _nsubst->dim); return 1; } nrrdAxisInfoGet_va(_nsubst, nrrdAxisInfoSize, &asize0, &asize1); if (2 != asize0) { biffAddf(NRRD, "%s: substitution table has to be 2xN, not %dxN", me, asize0); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: couldn't initialize by copy to output", me); return 1; } } mop = airMopNew(); nsubst = nrrdNew(); airMopAdd(mop, nsubst, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nsubst, _nsubst, nrrdTypeDouble)) { biffAddf(NRRD, "%s: couldn't create double copy of substitution table", me); airMopError(mop); return 1; } lup = nrrdDLookup[nout->type]; ins = nrrdDInsert[nout->type]; subst = (double *)nsubst->data; num = nrrdElementNumber(nout); for (ii=0; iidata, ii); changed = AIR_FALSE; for (jj=0; jjdata, ii, val); } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/nrrd/test/0000775000175000017500000000000012203513757016325 5ustar domibeldomibelteem-1.11.0~svn6057/src/nrrd/test/histrad.c0000664000175000017500000001164712165631065020140 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" char *histradInfo = ("like unu histax, but for circles"); int main(int argc, const char *argv[]) { const char *me; char *outS; hestOpt *hopt; hestParm *hparm; airArray *mop; char *err; Nrrd *nin, *nhist; double vmin, vmax, rmax, val, cent[2], rad; int bins[2], sx, sy, xi, yi, ridx, hidx, rbins, hbins; NrrdRange *range; double (*lup)(const void *v, size_t I), *hist; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "b", "rbins hbins", airTypeInt, 2, 2, bins, NULL, "# of histogram bins: radial and value"); hestOptAdd(&hopt, "min", "value", airTypeDouble, 1, 1, &vmin, "nan", "Value at low end of histogram. Defaults to lowest value " "found in input nrrd."); hestOptAdd(&hopt, "max", "value", airTypeDouble, 1, 1, &vmax, "nan", "Value at high end of histogram. Defaults to highest value " "found in input nrrd."); hestOptAdd(&hopt, "rmax", "max radius", airTypeDouble, 1, 1, &rmax, "nan", "largest radius to include in histogram"); hestOptAdd(&hopt, "c", "center x, y", airTypeDouble, 2, 2, cent, NULL, "The center point around which to build radial histogram"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "file to write histogram to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, histradInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (2 != nin->dim) { fprintf(stderr, "%s: need 2-D input (not %d-D)\n", me, nin->dim); airMopError(mop); return 1; } rbins = bins[0]; hbins = bins[1]; nhist = nrrdNew(); airMopAdd(mop, nhist, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(nhist, nrrdTypeDouble, 2, AIR_CAST(size_t, rbins), AIR_CAST(size_t, hbins))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate histogram:\n%s", me, err); airMopError(mop); return 1; } if (!( AIR_EXISTS(vmin) && AIR_EXISTS(vmax) )) { range = nrrdRangeNewSet(nin, nrrdStateBlind8BitRange); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); vmin = AIR_EXISTS(vmin) ? vmin : range->min; vmax = AIR_EXISTS(vmax) ? vmax : range->max; } #define DIST(x0, y0, x1, y1) (sqrt((x0-x1)*(x0-x1) + (y0-y1)*(y0-y1))) sx = nin->axis[0].size; sy = nin->axis[1].size; if (!AIR_EXISTS(rmax)) { rmax = 0; rmax = AIR_MAX(rmax, DIST(cent[0], cent[1], 0, 0)); rmax = AIR_MAX(rmax, DIST(cent[0], cent[1], sx-1, 0)); rmax = AIR_MAX(rmax, DIST(cent[0], cent[1], 0, sy-1)); rmax = AIR_MAX(rmax, DIST(cent[0], cent[1], sx-1, sy-1)); } lup = nrrdDLookup[nin->type]; hist = (double*)(nhist->data); for (xi=0; xidata, xi + sx*yi); if (!AIR_IN_OP(vmin, val, vmax)) { continue; } ridx = airIndex(0, rad, rmax, rbins); hidx = airIndex(vmin, val, vmax, hbins); hist[ridx + rbins*hidx] += 1; } } nhist->axis[0].min = 0; nhist->axis[0].max = rmax; nhist->axis[1].min = vmin; nhist->axis[1].max = vmax; if (nrrdSave(outS, nhist, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/tline.c0000664000175000017500000000453012165631065017606 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" /* learned: C++ name mangling means that you can't simply declare the function as extern, you need to do the same extern "C" wrapping as is done in the header file */ #ifdef __cplusplus extern "C" { #endif extern int _nrrdOneLine(unsigned int *lenP, NrrdIoState *io, FILE *file); #ifdef __cplusplus } #endif FILE * myopen(char *name) { if (!strcmp(name, "-")) { return stdin; } else { return fopen(name, "r"); } } void myclose(FILE *file) { if (file != stdin) { fclose(file); } return; } int main(int argc, char *argv[]) { char *me, *fileS; FILE *file; unsigned int llen; NrrdIoState *io; me = argv[0]; if (2 != argc) { /* 0 1 (2) */ fprintf(stderr, "usage: %s \n", me); exit(1); } fileS = argv[1]; if (!( file = myopen(fileS) )) { fprintf(stderr, "%s: couldn't open \"%s\" for reading\n", me, fileS); exit(1); } io = nrrdIoStateNew(); do { if (_nrrdOneLine(&llen, io, file)) { fprintf(stderr, "%s: trouble:\n%s", me, biffGet(NRRD)); exit(1); } if (llen) { printf("%2u |%s|\n", llen, io->line); } } while(llen > 0); nrrdIoStateNix(io); myclose(file); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/minmax.c0000664000175000017500000000347012165631065017766 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" void usage(char *me) { /* 0 1 (2) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char **argv) { char *me, *err; Nrrd *nrrd; NrrdRange *range; me = argv[0]; if (2 != argc) usage(me); nrrdStateVerboseIO = 10; if (nrrdLoad(nrrd=nrrdNew(), argv[1], NULL)) { fprintf(stderr, "%s: trouble loading \"%s\":\n%s", me, argv[1], err = biffGet(NRRD)); free(err); exit(1); } range = nrrdRangeNewSet(nrrd, nrrdBlind8BitRangeState); printf("%s: min = %g; max = %g, hasNonExist = %d\n", me, range->min, range->max, range->hasNonExist); nrrdNuke(nrrd); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/morph.c0000664000175000017500000001205112165631065017615 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" char *morphInfo = ("testing. "); int morph(Nrrd *nout, Nrrd *_nin, Nrrd *_nkern, float scl) { static const char me[]="morph"; airArray *mop; Nrrd *nin, *nkern; int sx, sy, sz, kd, kr, xx, yy, zz, ii, jj, kk; float *in, *out, *kern; mop = airMopNew(); if (nrrdTypeFloat == _nin->type) { nin = _nin; } else { nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nin, _nin, nrrdTypeFloat)) { biffAddf(NRRD, "%s: trouble 1", me); airMopError(mop); return 1; } } if (nrrdTypeFloat == _nkern->type) { nkern = _nkern; } else { nkern = nrrdNew(); airMopAdd(mop, nkern, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nkern, _nkern, nrrdTypeFloat)) { biffAddf(NRRD, "%s: trouble 2", me); airMopError(mop); return 1; } } if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: trouble allocating output", me); airMopError(mop); return 1; } in = AIR_CAST(float *, nin->data); out = AIR_CAST(float *, nout->data); kern = AIR_CAST(float *, nkern->data); sx = AIR_CAST(int, nin->axis[0].size); sy = AIR_CAST(int, nin->axis[1].size); sz = AIR_CAST(int, nin->axis[2].size); kd = AIR_CAST(int, nkern->axis[0].size); kr = kd/2; /* 5 -> 2 */ /* min_i (f(x+i)- k(i)) */ for (zz=kr; zzdim && nkern->axis[0].size == nkern->axis[1].size && nkern->axis[0].size == nkern->axis[2].size && 1 == nkern->axis[0].size % 2)) { fprintf(stderr, "%s: need cubical kernel w/ odd # sample on edge", me); airMopError(mop); exit(1); } if (!( 3 == nin->dim )) { fprintf(stderr, "%s: need 3D input", me); airMopError(mop); exit(1); } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (morph(nout, nin, nkern, scl)) { airMopAdd(mop, err = biffGet(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s", me, err); airMopError(mop); exit(1); } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGet(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving \"%s\":\n%s", me, outS, err); airMopError(mop); exit(1); } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/tread.c0000664000175000017500000000347712165631065017603 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" int main(int argc, char *argv[]) { char *me, *ninName, *noutName, *err; Nrrd *nin; me = argv[0]; if (3 != argc) { /* 0 1 2 (3) */ fprintf(stderr, "usage: %s \n", me); exit(1); } ninName = argv[1]; noutName = argv[2]; if (nrrdLoad(nin=nrrdNew(), ninName, NULL)) { fprintf(stderr, "%s: couldn't open nrrd \"%s\":\n%s", me, ninName, err = biffGetDone(NRRD)); free(err); exit(1); } if (nrrdSave(noutName, nin, NULL)) { fprintf(stderr, "%s: trouble saving nrrd to \"%s\":\n%s", me, noutName, err = biffGetDone(NRRD)); free(err); exit(1); } nrrdNuke(nin); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/genvol.c0000664000175000017500000001324312165631065017766 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "../nrrd.h" char *genvolInfo = ("generates test volumes. Not very flexible as long as " "the \"funk\" library doesn't exist"); double rho(double r) { return cos(2*AIR_PI*6.0*cos(AIR_PI*r/2)); } double genvolFunc(double x, double y, double z) { double mask, phi, R2, R3, Rbig, Rlit, sig0=0.17, sig1=0.04, a, b, w, ret; /* three bladed thing */ R3 = sqrt(x*x + y*y + z*z); mask = AIR_AFFINE(-1.0, erf((R3 - 0.75)*15), 1.0, 1.0, 0.0); R2 = sqrt(x*x + y*y); phi = atan2(y+0.001,x+0.001) + z*1.2; w = pow((1+cos(3*phi))/2, R2*R2*90); return w*mask; #if 0 /* ridge surface is a Mobius aka Moebius strip */ Rbig = sqrt(x*x + y*y); Rlit = sqrt(z*z + (Rbig-0.5)*(Rbig-0.5)); phi = atan2(Rbig-0.5, z) - atan2(x, y)/2; a = Rlit*cos(phi); b = Rlit*sin(phi); /* ret = airGaussian(a, 0, sig0)*airGaussian(b, 0, sig1); */ a = (a > sig0 ? a - sig0 : (a < -sig0 ? a + sig0 : 0)); ret = airGaussian(a, 0, sig1)*airGaussian(b, 0, sig1); return ret; #endif /* double A, B; A = 1; B = 1; */ /* marschner-lobb, the real thing return ((1 - sin(AIR_PI*z/2)) + 0.25*(1 + rho(sqrt(x*x + y*y))))/(2*(1 + 0.25)); */ /* marschner-lobb, linear variation in Z return (1 - (AIR_PI*z + 3)/5 + 0.25*(1 + rho(sqrt(x*x + y*y)))/(2*(1 + 0.25))); */ /* cone return z - 2*sqrt(x*x + y*y) + 0.5; */ /* pin-cushion return x*x + y*y + z*z - x*x*x*x - y*y*y*y - z*z*z*z; */ /* quadratic surface (moved to quadvol.c) return A*x*x + B*y*y - z; */ /* torus A = sqrt(x*x + y*y) - 0.5; return 2 - sqrt(A*A + z*z); */ /* return sqrt(x*x + y*y + z*z); */ /* return sqrt(x*x + y*y); */ } int main(int argc, const char *argv[]) { const char *me; char *err, *out; int size[3], xi, yi, zi; hestOpt *hopt; hestParm *hparm; airArray *mop; double min[3], max[3], x, y, z, *data; Nrrd *nout; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "s", "sx sy sz", airTypeInt, 3, 3, size, "128 128 128", "dimensions of output volume"); hestOptAdd(&hopt, "min", "x y z", airTypeDouble, 3, 3, min, "-1 -1 -1", "lower bounding corner of volume"); hestOptAdd(&hopt, "max", "x y z", airTypeDouble, 3, 3, max, "1 1 1", "upper bounding corner of volume"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &out, "-", "file to write output nrrd to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, genvolInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdAlloc_va(nout, nrrdTypeDouble, 3, AIR_CAST(size_t, size[0]), AIR_CAST(size_t, size[1]), AIR_CAST(size_t, size[2]))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem allocating volume:\n%s\n", me, err); airMopError(mop); return 1; } data = (double *)nout->data; for (zi=0; zispace = nrrdSpaceLeftPosteriorSuperior; nout->spaceDim = 3; ELL_3V_SET(nout->spaceOrigin, 0, 0, 0); ELL_3V_SET(nout->axis[0].spaceDirection, 1, 0, 0); ELL_3V_SET(nout->axis[1].spaceDirection, 0, 1, 0); ELL_3V_SET(nout->axis[2].spaceDirection, 0, 0, 1); #endif if (nrrdSave(out, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem saving output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/otsu.c0000664000175000017500000000442412165631065017467 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" char *otsuInfo = ("demonstrates nrrd's Otsu thresholding"); int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt; hestParm *hparm; airArray *mop; char *err; Nrrd *nhist; double thresh, expo; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "i", "nhist", airTypeOther, 1, 1, &nhist, NULL, "input histogram", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "e", "expo", airTypeDouble, 1, 1, &expo, "2.0", "exponent to use for variance calculation"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, otsuInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (nrrdHistoThresholdOtsu(&thresh, nhist, expo)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "%s: threshold = %g\n", me, thresh); airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/texp.c0000664000175000017500000000526612165631065017462 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../nrrd.h" void usage(char *me) { /* 0 1 2 3 4 (5) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char *argv[]) { char *me; unsigned int ii, NN; double min, max, *out; Nrrd *nout; me = argv[0]; if (5 != argc) { usage(me); } if (3 != (sscanf(argv[2], "%u", &NN) + sscanf(argv[1], "%lf", &min) + sscanf(argv[3], "%lf", &max))) { fprintf(stderr, "%s: couldn't parse %s %s %s double uint double", me, argv[1], argv[2], argv[3]); usage(me); } nout = nrrdNew(); if (nrrdAlloc_va(nout, nrrdTypeDouble, 2, AIR_CAST(size_t, 5), AIR_CAST(size_t, NN))) { fprintf(stderr, "%s: trouble allocating:\n%s", me, biffGetDone(NRRD)); exit(1); } out = AIR_CAST(double *, nout->data); for (ii=0; ii real %f, approx %f %f\n", me, xx, rr, ff, gg); exit(1); } out[0] = rr; out[1] = ff; out[2] = (ff-rr)/rr; out[3] = gg; out[4] = (gg-rr)/rr; out += 5; } if (nrrdSave(argv[4], nout, NULL)) { fprintf(stderr, "%s: trouble saving:\n%s", me, biffGetDone(NRRD)); exit(1); } exit(0); } teem-1.11.0~svn6057/src/nrrd/test/ax.c0000664000175000017500000002061712165631065017107 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "../nrrd.h" float frand(float min, float max) { return (min + airDrandMT() * (max - min)); } int main(int argc, char *argv[]) { int i; Nrrd *nrrd; double diff, idx, idx2, idx3, idx4, lo, hi, pos, pos2, pos3, pos4; AIR_UNUSED(argc); AIR_UNUSED(argv); if (nrrdAlloc_va(nrrd=nrrdNew(), nrrdTypeFloat, 2, AIR_CAST(size_t, 4), AIR_CAST(size_t, 4))) { printf("trouble:\n%s\n", biffGet(NRRD)); exit(1); } nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoMin, 10.0, 10.0); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoMax, 12.0, 12.0); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoCenter, nrrdCenterNode, nrrdCenterCell); idx = 0; printf("\n"); pos = nrrdAxisInfoPos(nrrd, 0, idx); printf("pos(0, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 0, pos)); pos = nrrdAxisInfoPos(nrrd, 1, idx); printf("pos(1, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 1, pos)); idx = 1; printf("\n"); pos = nrrdAxisInfoPos(nrrd, 0, idx); printf("pos(0, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 0, pos)); pos = nrrdAxisInfoPos(nrrd, 1, idx); printf("pos(1, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 1, pos)); idx = 2; printf("\n"); pos = nrrdAxisInfoPos(nrrd, 0, idx); printf("pos(0, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 0, pos)); pos = nrrdAxisInfoPos(nrrd, 1, idx); printf("pos(1, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 1, pos)); idx = 0; idx2 = 0; printf("\n"); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 0, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 0, lo, hi); printf("range(0, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 1, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 1, lo, hi); printf("range(1, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); idx = 0; idx2 = 1; printf("\n"); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 0, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 0, lo, hi); printf("range(0, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 1, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 1, lo, hi); printf("range(1, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); idx = 1; idx2 = 0; printf("\n"); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 0, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 0, lo, hi); printf("range(0, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 1, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 1, lo, hi); printf("range(1, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoMin, 12.0, 12.0); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoMax, 10.0, 10.0); printf("\n(axis min,max flipped)\n"); idx = 0; printf("\n"); pos = nrrdAxisInfoPos(nrrd, 0, idx); printf("pos(0, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 0, pos)); pos = nrrdAxisInfoPos(nrrd, 1, idx); printf("pos(1, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 1, pos)); idx = 1; printf("\n"); pos = nrrdAxisInfoPos(nrrd, 0, idx); printf("pos(0, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 0, pos)); pos = nrrdAxisInfoPos(nrrd, 1, idx); printf("pos(1, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 1, pos)); idx = 2; printf("\n"); pos = nrrdAxisInfoPos(nrrd, 0, idx); printf("pos(0, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 0, pos)); pos = nrrdAxisInfoPos(nrrd, 1, idx); printf("pos(1, %g) == %g --> %g\n", idx, pos, nrrdAxisInfoIdx(nrrd, 1, pos)); idx = 0; idx2 = 0; printf("\n"); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 0, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 0, lo, hi); printf("range(0, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 1, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 1, lo, hi); printf("range(1, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); idx = 0; idx2 = 2; printf("\n"); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 0, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 0, lo, hi); printf("range(0, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 1, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 1, lo, hi); printf("range(1, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); idx = 2; idx2 = 0; printf("\n"); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 0, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 0, lo, hi); printf("range(0, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); nrrdAxisInfoPosRange(&lo, &hi, nrrd, 1, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 1, lo, hi); printf("range(1, %g -- %g) == (%g -- %g) --> (%g -- %g)\n", idx, idx2, lo, hi, idx3, idx4); nrrd->axis[0].center = nrrdCenterCell; nrrd->axis[0].size = 4; nrrd->axis[0].min = -4; nrrd->axis[0].max = 4; pos = 0; pos2 = 1; nrrdAxisInfoIdxRange(&idx, &idx2, nrrd, 0, pos, pos2); nrrdAxisInfoPosRange(&pos3, &pos4, nrrd, 0, idx, idx2); printf("min, max = %g, %g\n", nrrd->axis[0].min, nrrd->axis[0].max); printf("pos, pos2 = %g, %g\n", pos, pos2); printf("idx, idx2 = %g, %g\n", idx, idx2); printf("pos3, pos4 = %g, %g\n", pos3, pos4); exit(1); /* and now for random-ness */ airSrandMT((int)airTime()); nrrd->axis[0].center = nrrdCenterNode; nrrd->axis[0].center = nrrdCenterCell; for (i=0; i<=1000000; i++) { nrrd->axis[0].min = frand(-3.0, 3.0); nrrd->axis[0].max = frand(-3.0, 3.0); idx = frand(-3.0, 3.0); pos = nrrdAxisInfoPos(nrrd, 0, idx); diff = idx - nrrdAxisInfoIdx(nrrd, 0, pos); if (AIR_ABS(diff) > 0.00000001) { printf("PANIC 0\n"); exit(2); } pos = frand(-3.0, 3.0); idx = nrrdAxisInfoIdx(nrrd, 0, pos); diff = pos - nrrdAxisInfoPos(nrrd, 0, idx); if (AIR_ABS(diff) > 0.00000001) { printf("PANIC 1\n"); exit(2); } nrrd->axis[0].min = (int)frand(-3.0, 3.0); nrrd->axis[0].max = (int)frand(-3.0, 3.0); idx = (int)frand(-10.0, 10.0); idx2 = (int)frand(-10.0, 10.0); nrrdAxisInfoPosRange(&pos, &pos2, nrrd, 0, idx, idx2); nrrdAxisInfoIdxRange(&idx3, &idx4, nrrd, 0, pos, pos2); diff = AIR_ABS(idx - idx3) + AIR_ABS(idx2 - idx4); if (AIR_ABS(diff) > 0.00000001) { printf("PANIC 2\n"); exit(2); } pos = (int)frand(-3.0, 3.0); pos2 = (int)frand(-3.0, 3.0); nrrdAxisInfoIdxRange(&idx, &idx2, nrrd, 0, pos, pos2); nrrdAxisInfoPosRange(&pos3, &pos4, nrrd, 0, idx, idx2); diff = AIR_ABS(pos - pos3) + AIR_ABS(pos2 - pos4); if (AIR_ABS(diff) > 0.00000001) { printf("min, max = %g, %g\n", nrrd->axis[0].min, nrrd->axis[0].max); printf("pos, pos2 = %g, %g\n", pos, pos2); printf("idx, idx2 = %g, %g\n", idx, idx2); printf("pos3, pos4 = %g, %g\n", pos3, pos4); printf("PANIC (%d) 3 %g\n", (int)nrrd->axis[0].size, diff); exit(2); } } exit(0); } teem-1.11.0~svn6057/src/nrrd/test/typestest.c0000664000175000017500000000557112165631065020545 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" int main(int argc, char *argv[]) { int tt; /* char str1[AIR_STRLEN_MED], str2[AIR_STRLEN_MED], str3[AIR_STRLEN_MED]; */ AIR_UNUSED(argc); AIR_UNUSED(argv); for (tt=nrrdTypeChar; tt<=nrrdTypeDouble; tt++) { printf(" ----- %s -----\n", airEnumStr(nrrdType, tt)); printf("nrrdTypeSize: %d\n", (int)nrrdTypeSize[tt]); printf("nrrdTypeIsUnsigned: %d\n", nrrdTypeIsUnsigned[tt]); printf("nrrdTypeIsIntegral: %d\n", nrrdTypeIsIntegral[tt]); printf("nrrdTypeMin: % 31.15f\n", nrrdTypeMin[tt]); printf("nrrdTypeMax: % 31.15f\n", nrrdTypeMax[tt]); } printf("sizeof(size_t) = %d\n", (int)sizeof(size_t)); /* c = -10; uc = 10; s = -10; us = 10; i = -10; ui = 10; lli = -10; ulli = 10; f = 3.14159324234098320948172304987123; d = 3.14159324234098320948172304987123; printf("c: %d\n", c); printf("uc: %u\n", uc); printf("s: %hd\n", s); printf("us: %hu\n", us); printf("i: %d\n", i); printf("ui: %u\n", ui); printf("lli: %lld\n", lli); printf("ulli: %llu\n", ulli); printf("f: %f\n", f); printf("d: %lf\n", d); sprintf(str1, "-10"); sprintf(str2, "10"); sprintf(str3, "3.14159324234098320948172304987123"); sscanf(str1, "%d", &c); sscanf(str2, "%u", &uc); sscanf(str1, "%hd", &s); sscanf(str2, "%hu", &us); sscanf(str1, "%d", &i); sscanf(str2, "%u", &ui); sscanf(str1, "%lld", &lli); sscanf(str2, "%llu", &ulli); sscanf(str3, "%f", &f); sscanf(str3, "%lf", &d); printf("\n"); printf("c: %d\n", c); printf("uc: %u\n", uc); printf("s: %hd\n", s); printf("us: %hu\n", us); printf("i: %d\n", i); printf("ui: %u\n", ui); printf("lli: %lld\n", lli); printf("ulli: %llu\n", ulli); printf("f: %f\n", f); printf("d: %lf\n", d); */ exit(0); } teem-1.11.0~svn6057/src/nrrd/test/io.c0000664000175000017500000001117712165631065017107 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" void usage(char *me) { /* 0 1 2 (3) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char **argv) { char *me, *err; Nrrd *nrrd; NrrdIoState *io; unsigned int domNum, domAxi[NRRD_DIM_MAX], rngNum, rngAxi[NRRD_DIM_MAX], axi; double val[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX]; me = argv[0]; if (3 != argc) { usage(me); } io = nrrdIoStateNew(); nrrdStateVerboseIO = 10; if (nrrdLoad(nrrd=nrrdNew(), argv[1], NULL)) { fprintf(stderr, "%s: trouble loading \"%s\":\n%s", me, argv[1], err = biffGet(NRRD)); free(err); exit(1); } domNum = nrrdDomainAxesGet(nrrd, domAxi); rngNum = nrrdRangeAxesGet(nrrd, rngAxi); fprintf(stderr, "%s domain axes (%u):", me, domNum); for (axi=0; axiaxis[domAxi[axi]].kind)); } fprintf(stderr, "\n"); fprintf(stderr, "%s range naxes (%u):", me, rngNum); for (axi=0; axiaxis[rngAxi[axi]].kind)); } fprintf(stderr, "\n"); fprintf(stderr, "0 --------------------------------------\n"); nrrdDescribe(stderr, nrrd); fprintf(stderr, "1 --------------------------------------\n"); nrrd->spaceDim = 3; val[0][0] = 1.11; val[0][1] = 2.22; val[0][2] = 3.33; val[1][0] = 4.11; val[1][1] = 4.22; val[1][2] = 4.33; val[2][0] = 5.11; val[2][1] = 6.11; val[2][2] = 7.11; fprintf(stderr, "%s: val[0,1,2] = %lu %lu %lu\n", me, (unsigned long)(val[0]), (unsigned long)(val[1]), (unsigned long)(val[2])); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSpaceDirection, val[0], val[1], val[2]); fprintf(stderr, "2 --------------------------------------\n"); nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSpaceDirection, val); fprintf(stderr, "%s: val[0] = %g %g %g\n", me, val[0][0], val[0][1], val[0][2]); fprintf(stderr, "%s: val[1] = %g %g %g\n", me, val[1][0], val[1][1], val[1][2]); fprintf(stderr, "%s: val[2] = %g %g %g\n", me, val[2][0], val[2][1], val[2][2]); fprintf(stderr, "3 --------------------------------------\n"); nrrdAxisInfoGet_va(nrrd, nrrdAxisInfoSpaceDirection, val[0], val[1], val[2]); fprintf(stderr, "4 --------------------------------------\n"); fprintf(stderr, "%s: val[0] = %g %g %g\n", me, val[0][0], val[0][1], val[0][2]); fprintf(stderr, "%s: val[1] = %g %g %g\n", me, val[1][0], val[1][1], val[1][2]); fprintf(stderr, "%s: val[2] = %g %g %g\n", me, val[2][0], val[2][1], val[2][2]); fprintf(stderr, "5 --------------------------------------\n"); if (nrrdSave("out.nrrd", nrrd, io)) { fprintf(stderr, "%s: trouble saving \"%s\":\n%s", me, argv[1], err = biffGet(NRRD)); free(err); exit(1); } nrrdIoStateInit(io); if (nrrdSave(argv[2], nrrd, io)) { fprintf(stderr, "%s: trouble saving \"%s\":\n%s", me, argv[1], err = biffGet(NRRD)); free(err); exit(1); } nrrdIoStateInit(io); if (nrrdSave(argv[2], nrrd, io)) { fprintf(stderr, "%s: trouble saving \"%s\":\n%s", me, argv[1], err = biffGet(NRRD)); free(err); exit(1); } nrrdIoStateInit(io); if (nrrdSave(argv[2], nrrd, io)) { fprintf(stderr, "%s: trouble saving \"%s\":\n%s", me, argv[1], err = biffGet(NRRD)); free(err); exit(1); } nrrdIoStateNix(io); nrrdNuke(nrrd); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/reuse.c0000664000175000017500000000464212165631065017622 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" void usage(char *me) { /* 0 1 2 (3) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char **argv) { char *me, *err; Nrrd *nrrd, *n2; size_t size[NRRD_DIM_MAX]; me = argv[0]; if (3 != argc) { usage(me); } nrrdStateVerboseIO = 10; if (nrrdLoad(nrrd=nrrdNew(), argv[1], NULL)) { fprintf(stderr, "%s: trouble loading \"%s\":\n%s", me, argv[1], err = biffGet(NRRD)); free(err); exit(1); } fprintf(stderr, "%s: data for \"%s\" at %p\n", me, argv[1], nrrd->data); if (nrrdLoad(nrrd, argv[2], NULL)) { fprintf(stderr, "%s: trouble loading \"%s\":\n%s", me, argv[2], err = biffGet(NRRD)); free(err); exit(1); } fprintf(stderr, "%s: data for \"%s\" at %p\n", me, argv[2], nrrd->data); n2 = nrrdNew(); nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSize, size); if (nrrdWrap_nva(n2, nrrd->data, nrrd->type, nrrd->dim, size) || nrrdAxesMerge(n2, nrrd, 0)) { fprintf(stderr, "%s: trouble wrapping or merging \"%s\":\n%s", me, argv[2], err = biffGet(NRRD)); free(err); exit(1); } fprintf(stderr, "%s: data for axmerge(\"%s\",0) at %p\n", me, argv[2], n2->data); nrrdNuke(nrrd); nrrdNix(n2); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/dnorm.c0000664000175000017500000002020512165631065017607 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef DIDEROT # include # include # include #else # include "../nrrd.h" #endif char *dnormInfo = ("Normalizes nrrd representation for Diderot. " "Forces information about kind and orientation into " "a consistent form, and nixes various other fields. "); int main(int argc, const char **argv) { const char *me; char *outS; hestOpt *hopt; hestParm *hparm; airArray *mop; char *err; Nrrd *nin, *nout; NrrdIoState *nio; int kindIn, kindOut, headerOnly; unsigned int kindAxis, axi, si, sj; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "h,header", NULL, airTypeInt, 0, 0, &headerOnly, NULL, "output header of nrrd file only, not the data itself"); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output filename", NULL); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, dnormInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); /* can't deal with block type */ if (nrrdTypeBlock == nin->type) { fprintf(stderr, "%s: can only have scalar kinds (not %s)\n", me, airEnumStr(nrrdType, nrrdTypeBlock)); airMopError(mop); exit(1); } /* make sure all kinds are set to something */ /* see if there's a range kind, verify that there's only one */ kindIn = nrrdKindUnknown; kindAxis = 0; for (axi=0; axidim; axi++) { if (nrrdKindUnknown == nin->axis[axi].kind || nrrdKindIsDomain(nin->axis[axi].kind)) { continue; } if (nrrdKindUnknown != kindIn) { fprintf(stderr, "%s: got non-domain kind %s on axis %u, but already " "have %s from axis %u\n", me, airEnumStr(nrrdKind, nin->axis[axi].kind), axi, airEnumStr(nrrdKind, kindIn), kindAxis); airMopError(mop); exit(1); } kindIn = nin->axis[axi].kind; kindAxis = axi; } /* see if the non-domain kind is something we can interpret as a tensor */ if (nrrdKindUnknown != kindIn) { switch (kindIn) { /* ======= THESE are the kinds that we can possibly output ======= */ case nrrdKind2Vector: case nrrdKind3Vector: case nrrdKind4Vector: case nrrdKind2DSymMatrix: case nrrdKind2DMatrix: case nrrdKind3DSymMatrix: case nrrdKind3DMatrix: /* =============================================================== */ kindOut = kindIn; break; case nrrdKind3Color: case nrrdKindRGBColor: kindOut = nrrdKind3Vector; break; case nrrdKind4Color: case nrrdKindRGBAColor: kindOut = nrrdKind4Vector; break; default: fprintf(stderr, "%s: got non-conforming kind %s on axis %u\n", me, airEnumStr(nrrdKind, kindIn), kindAxis); airMopError(mop); exit(1); break; } } else { kindOut = nrrdKindUnknown; } /* initialize output by copying */ nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, nin)) { airMopAdd(mop, err = biffGet(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble copying:\n%s", me, err); airMopError(mop); exit(1); } /* no comments, either advertising the format URL or anything else */ nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); nio->skipFormatURL = AIR_TRUE; if (headerOnly) { nio->skipData = AIR_TRUE; } nrrdCommentClear(nout); /* no measurement frame */ for (si=0; simeasurementFrame[si][sj] = AIR_NAN; } } /* no key/value pairs */ nrrdKeyValueClear(nout); /* no content field */ nout->content = airFree(nout->content); /* normalize domain kinds to "space" */ /* turn off centers (perhaps Diderot should assume cell-centered) */ /* turn off thickness */ /* turn off labels and units */ for (axi=0; axidim; axi++) { if (nrrdKindUnknown == kindOut) { nout->axis[axi].kind = nrrdKindSpace; } else { nout->axis[axi].kind = (kindAxis == axi ? kindOut : nrrdKindSpace); } nout->axis[axi].center = nrrdCenterUnknown; nout->axis[axi].thickness = AIR_NAN; nout->axis[axi].label = airFree(nout->axis[axi].label); nout->axis[axi].units = airFree(nout->axis[axi].units); nout->axis[axi].min = AIR_NAN; nout->axis[axi].max = AIR_NAN; nout->axis[axi].spacing = AIR_NAN; } /* logic of orientation definition: if space dimension is known: set origin to zero if not already set set space direction to unit vector if not already set else: set origin to zero and all space directions to units might be nice to use gage's logic for mapping from world to index, but we have to accept a greater variety of kinds and dimensions than gage ever has to process. */ if (nout->spaceDim) { int saxi = 0; /* we use only the space dimension, not any named space */ nout->space = nrrdSpaceUnknown; if (!nrrdSpaceVecExists(nout->spaceDim, nout->spaceOrigin)) { nrrdSpaceVecSetZero(nout->spaceOrigin); } for (axi=0; axidim; axi++) { if (nrrdKindUnknown == kindOut || kindAxis != axi) { if (!nrrdSpaceVecExists(nout->spaceDim, nout->axis[axi].spaceDirection)) { nrrdSpaceVecSetZero(nout->axis[axi].spaceDirection); nout->axis[axi].spaceDirection[saxi] = 1.0; } saxi++; } else { nrrdSpaceVecSetNaN(nout->axis[axi].spaceDirection); } } } else { int saxi = 0; nrrdSpaceVecSetZero(nout->spaceOrigin); for (axi=0; axidim; axi++) { if (nrrdKindUnknown == kindOut || kindAxis != axi) { nrrdSpaceVecSetZero(nout->axis[axi].spaceDirection); nout->axis[axi].spaceDirection[saxi] = (AIR_EXISTS(nin->axis[axi].spacing) ? nin->axis[axi].spacing : 1.0); saxi++; } else { nrrdSpaceVecSetNaN(nout->axis[axi].spaceDirection); } } nout->spaceDim = saxi; } /* probably should be asserted earlier */ if (nout->dim != nout->spaceDim + !!kindOut) { fprintf(stderr, "%s: output dim %d != spaceDim %d + %d %s%s%s\n", me, nout->dim, nout->spaceDim, !!kindOut, kindOut ? "for non-scalar (" : "(scalar data)", kindOut ? airEnumStr(nrrdKind, kindOut) : "", kindOut ? ") data" : ""); airMopError(mop); exit(1); } if (nrrdSave(outS, nout, nio)) { airMopAdd(mop, err = biffGet(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving \"%s\":\n%s", me, outS, err); airMopError(mop); exit(1); } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/kv.c0000664000175000017500000000366412165631065017122 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" void usage(char *me) { /* 0 1 */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char **argv) { char *me, *err, *key="strong bad", *value; Nrrd *nrrd; NrrdIoState *io; me = argv[0]; if (2 != argc) usage(me); io = nrrdIoStateNew(); nrrdStateVerboseIO = 10; if (nrrdLoad(nrrd=nrrdNew(), argv[1], NULL)) { fprintf(stderr, "%s: trouble loading \"%s\":\n%s", me, argv[1], err = biffGetDone(NRRD)); free(err); exit(1); } if ((value = nrrdKeyValueGet(nrrd, key))) { fprintf(stderr, "%s: '%s':='%s' (%d)\n", me, key, value, (int)strlen(value)); } else { fprintf(stderr, "%s: value not found for key: %s\n", me, key); } nrrdIoStateNix(io); nrrdNuke(nrrd); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/tkernel.c0000664000175000017500000001514512165631065020143 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" /* ** this program demonstrates parsing a string into a kernel with parms, ** verifies that all the kernel methods are consistent and/or equal, ** and produces a text file of the kernel evaluated many times. ** The output can be "plotted" with unu jhisto, as with: ** tkernel ctmr -2 0.0001 2 - | unu jhisto -b 800 300 | unu flip -a 1 | unu quantize -b 8 -o tmp.png */ void usage(char *me) { /* 0 1 2 3 4 5 (6) (7)*/ fprintf(stderr, "usage: %s [dkern]\n", me); exit(1); } #define CLOSE(a,b, eps) (fabs((a)-(b)) < eps) int main(int argc, char *argv[]) { char *me, *kernS[2], *minS, *stepS, *maxS, *outS, *err, kstr[AIR_STRLEN_LARGE]; const NrrdKernel *kern[2]; NrrdKernelSpec *ksp[2]; double parm[NRRD_KERNEL_PARMS_NUM], min, step, max, integral, *dom_d, *ran_d; float *dom_f, *ran_f, val, r_f, r_d; FILE *fout; int i, len; airArray *mop; unsigned int kii; me = argv[0]; if (!( 6 == argc || 7 == argc )) { usage(me); } kernS[0] = argv[1]; minS = argv[2]; stepS = argv[3]; maxS = argv[4]; outS = argv[5]; if (7 == argc) { kernS[1] = argv[6]; } else { kernS[1] = NULL; } if (3 != (sscanf(minS, "%lf", &min) + sscanf(stepS, "%lf", &step) + sscanf(maxS, "%lf", &max))) { fprintf(stderr, "%s: couldn't parse \"%s\", \"%s\", \"%s\" as 3 doubles\n", me, minS, stepS, maxS); exit(1); } mop = airMopNew(); for (kii=0; kii<=(kernS[1] ? 1 : 0); kii++) { if (nrrdKernelParse(&(kern[kii]), parm, kernS[kii])) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: (kii %u) trouble:\n%s\n", me, kii, err); airMopError(mop); exit(1); } ksp[kii] = nrrdKernelSpecNew(); airMopAdd(mop, ksp[kii], (airMopper)nrrdKernelSpecNix, airMopAlways); nrrdKernelSpecSet(ksp[kii], kern[kii], parm); if (nrrdKernelSpecSprint(kstr, ksp[kii])) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); exit(1); } fprintf(stderr, "%s: printed kernel as \"%s\"\n", me, kstr); if (!( min <= -kern[kii]->support(parm) && max >= kern[kii]->support(parm) )) { fprintf(stderr, "%s: WARNING: support=%g => lower min (%g) or raise max (%g)\n", me, kern[kii]->support(parm), min, max); } fprintf(stderr, "%s: support(%s) = %g\n", me, kstr, kern[kii]->support(parm)); } /* see how many values are in the interval */ len = 0; for (val=min; val<=max; val+=step) { len++; } /* allocate domain and range for both float and double */ if (!( (dom_d = (double *)calloc(len, sizeof(double))) && (ran_d = (double *)calloc(len, sizeof(double))) && (dom_f = (float *)calloc(len, sizeof(float))) && (ran_f = (float *)calloc(len, sizeof(float))) )) { fprintf(stderr, "%s: PANIC: couldn't allocate buffers\n", me); exit(1); } airMopAdd(mop, dom_d, airFree, airMopAlways); airMopAdd(mop, ran_d, airFree, airMopAlways); airMopAdd(mop, dom_f, airFree, airMopAlways); airMopAdd(mop, ran_f, airFree, airMopAlways); /* set values in both domains */ i=0; for (val=min; val<=max; val+=step) { /* note that the value stored in dom_d[i] is only a single-precision float, so that it is really equal to dom_f[i] */ dom_d[i] = val; dom_f[i] = val; i++; } /* do the vector evaluations */ kern[0]->evalN_f(ran_f, dom_f, len, parm); kern[0]->evalN_d(ran_d, dom_d, len, parm); /* do the single evaluations, and make sure everything agrees */ i = 0; integral = 0; for (val=min; val<=max; val+=step) { /* compare two single evaluations */ r_f = kern[0]->eval1_f(val, parm); r_d = kern[0]->eval1_d(val, parm); if (!CLOSE(r_f,r_d, 0.00001)) { fprintf(stderr, "%s: (eval1_f(%g)== %f) != (eval1_d(%g)== %f)\n", me, val, r_f, val, r_d); } /* compare single float with vector float */ if (!CLOSE(r_f,ran_f[i], 0.00001)) { fprintf(stderr, "%s: (eval1_f(%g)== %f) != (evalN_f[%d]== %f)\n", me, val, r_f, i, ran_f[i]); } /* compare single float with vector double */ r_d = ran_d[i]; if (!CLOSE(r_f,r_d, 0.00001)) { fprintf(stderr, "%s: (eval1_f(%g)== %f) != (evalN_d[%d]== %f)\n", me, val, r_f, i, r_d); } integral += step*ran_d[i]; /* possibly check on given derivatives */ if (kern[1]) { double numd; numd = (kern[0]->eval1_d(val+step/2, parm) - kern[0]->eval1_d(val-step/2, parm))/step; if (!CLOSE(numd, kern[1]->eval1_d(val+step, parm), 0.005)) { fprintf(stderr, "%s: |numerical f'(%g) %g - true %g| = %g > 0.005\n", me, val, numd, kern[1]->eval1_d(val+step, parm), fabs(numd - kern[1]->eval1_d(val+step, parm))); /* exit(1); */ } } i++; } if (!CLOSE(integral, kern[0]->integral(parm), 0.0005)) { fprintf(stderr, "%s: HEY HEY HEY HEY HEY HEY!\n", me); fprintf(stderr, "%s: discrete integral %f != %f\n", me, integral, kern[0]->integral(parm)); /* exit(1); */ } /* it all checks out; write the file */ if (!(fout = airFopen(outS, stdout, "w"))) { fprintf(stderr, "%s: couldn't open \"%s\" for writing\n", me, outS); exit(1); } for (i=0; i<=len-1; i++) { fprintf(fout, "%g %g\n", dom_f[i], ran_f[i]); } fclose(fout); airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/strio.c0000664000175000017500000000454112165631065017635 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" int main(int argc, char **argv) { char *me, *err; Nrrd *nrrd; NrrdIoState *nio; char hstr[] = "NRRD0001\n" "# Complete NRRD file format specification at:\n" "# http://teem.sourceforge.net/nrrd/format.html\n" "# one comment\n" "# two comment\n" "# three comment\n" "type: float\n" "dimension: 2\n" "sizes: 91 114\n" "centerings: node node\n" "endian: big\n" "encoding: raw\n" "bingo:=bob\n" "foo:=super duper fancy bar with corona\n" /* "data file: tmp.raw\n" */; char *wstr; AIR_UNUSED(argc); me = argv[0]; nrrdStateVerboseIO = 10; nio = nrrdIoStateNew(); nrrd = nrrdNew(); nio->path = airStrdup("."); if (nrrdStringRead(nrrd, hstr, nio)) { fprintf(stderr, "%s: trouble reading string:\n%s", me, err = biffGet(NRRD)); free(err); exit(1); } fprintf(stderr, "%s: nrrd->data = %p\n", me, nrrd->data); nrrdSave("out.nrrd", nrrd, NULL); if (nrrdStringWrite(&wstr, nrrd, NULL)) { fprintf(stderr, "%s: trouble writing string:\n%s", me, err = biffGet(NRRD)); free(err); exit(1); } fprintf(stderr, "%s: |%s|\n", me, wstr); free(wstr); nrrdIoStateNix(nio); nrrdNuke(nrrd); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/convo.c0000664000175000017500000001543612165631065017626 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "../nrrd.h" char *convoInfo = ("Good for convolution of two 2-D nrrd, and nothing else."); #define CONVO "convo" int convoFunc(Nrrd *nout, Nrrd *_nimg, Nrrd *_nmask, int renorm, int crop) { char me[]="convoFunc", err[AIR_STRLEN_MED]; Nrrd *nimg, *nmask, *npad; airArray *mop; int E; unsigned int i; int x, y, ix, iy, /* image coordinates */ isx, isy, /* input image size */ osx, osy, /* output image size */ mx, my, /* mask coordinates */ msx, msy, /* mask size */ lox, hix, /* amount of padding along X caused by mask */ loy, hiy; /* amount of padding along Y caused by mask */ size_t cmin[2], cmax[2]; /* cropping bounds */ ptrdiff_t pmin[2], pmax[2]; /* cropping bounds */ float *ohere, *idata, *mdata, *odata, sum; isx = _nimg->axis[0].size; isy = _nimg->axis[1].size; msx = _nmask->axis[0].size; msy = _nmask->axis[1].size; lox = (msx-1)/2; hix = msx/2; loy = (msy-1)/2; hiy = msy/2; osx = isx + lox + hix; osy = isy + loy + hiy; pmin[0] = -lox; pmin[1] = -loy; pmax[0] = isx - 1 + hix; pmax[1] = isy - 1 + hiy; /* fprintf(stderr, "%s: lox,loy = %d,%d hix,hiy = %d,%d\n", me, lox, loy, hix, hiy); fprintf(stderr, "%s: min[] = %d,%d max[] = %d,%d\n", me, min[0], min[1], max[0], max[1]); */ mop = airMopNew(); airMopAdd(mop, nimg=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nmask=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, npad=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); E = 0; if (!E) E |= nrrdConvert(nimg, _nimg, nrrdTypeFloat); if (!E) E |= nrrdConvert(nmask, _nmask, nrrdTypeFloat); /* we will blow away the values values generated by this; padding here is just a simple way of creating a nrrd with the correct dimensions */ if (!E) E |= nrrdPad_va(npad, nimg, pmin, pmax, nrrdBoundaryPad, 0.0); if (E) { sprintf(err, "%s: set-up problem", me); biffMove(CONVO, me, NRRD); airMopError(mop); return 1; } idata = (float *)nimg->data; mdata = (float *)nmask->data; odata = (float *)npad->data; for (i=0; idim && 2 == nmask->dim )) { fprintf(stderr, "%s: dimension of image (%d) and mask (%d) not both 2\n", me, nimg->dim, nmask->dim); airMopError(mop); return 1; } if (convoFunc(nout, nimg, nmask, !renorm, !uncrop)) { airMopAdd(mop, err = biffGetDone(CONVO), airFree, airMopAlways); fprintf(stderr, "%s: problem save image:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(out, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem save image:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/nrrd/test/trand.c0000664000175000017500000000340012165631065017576 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../nrrd.h" #define BINS 1024 #define HGHT 800 int main(int argc, char **argv) { Nrrd *nval, *nhist, *npgm; double *val; int i; AIR_UNUSED(argc); AIR_UNUSED(argv); nrrdAlloc_va(nval=nrrdNew(), nrrdTypeDouble, 1, AIR_CAST(size_t, BINS*BINS)); val = (double *)nval->data; airSrandMT((int)airTime()); for (i=0; i #include "../nrrd.h" char *quadInfo = ("generates quadratic test volumes, with isosurfaces " "which should resemble z = A*x^2 + B*y^2"); float quadFunc(float x, float y, float z, float A, float B, float off) { return A*x*x + B*y*y - z + off; } int main(int argc, const char *argv[]) { const char *me; char *err, *out; int size[3], xi, yi, zi; hestOpt *hopt; hestParm *hparm; airArray *mop; float min[3], max[3], AB[2], x, y, z, *data, off; Nrrd *nout; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "s", "sx sy sz", airTypeInt, 3, 3, size, "128 128 128", "dimensions of output volume"); hestOptAdd(&hopt, "min", "x y z", airTypeFloat, 3, 3, min, "-1 -1 -1", "lower bounding corner of volume"); hestOptAdd(&hopt, "max", "x y z", airTypeFloat, 3, 3, max, "1 1 1", "upper bounding corner of volume"); hestOptAdd(&hopt, "c", "A B", airTypeFloat, 2, 2, AB, NULL, "A and B quadratic coefficients"); hestOptAdd(&hopt, "off", "z offset", airTypeFloat, 1, 1, &off, "0.0", "vertical offset"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &out, "-", "file to write output nrrd to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, quadInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdAlloc_va(nout, nrrdTypeFloat, 3, AIR_CAST(size_t, size[0]), AIR_CAST(size_t, size[1]), AIR_CAST(size_t, size[2]))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem allocating volume:\n%s\n", me, err); airMopError(mop); return 1; } data = (float *)nout->data; for (zi=0; zi= 8 # define _NRRD_DEF_MEM_LEVEL 8 #else # define _NRRD_DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* stream buffer size */ #define _NRRD_Z_BUFSIZE 16 * 1024 /* gzip flag byte */ #define _NRRD_ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ #define _NRRD_HEAD_CRC 0x02 /* bit 1 set: header CRC present */ #define _NRRD_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ #define _NRRD_ORIG_NAME 0x08 /* bit 3 set: original file name present */ #define _NRRD_COMMENT 0x10 /* bit 4 set: file comment present */ #define _NRRD_RESERVED 0xE0 /* bits 5..7: reserved */ typedef struct _NrrdGzStream { z_stream stream; int z_err; /* error code for last stream operation */ int z_eof; /* set if end of input file */ FILE *file; /* .gz file */ Byte *inbuf; /* input buffer */ Byte *outbuf; /* output buffer */ uLong crc; /* crc32 of uncompressed data */ char *msg; /* error message */ int transparent; /* 1 if input file is not a .gz file */ char mode; /* 'w' or 'r' */ long startpos; /* start of compressed data in file (header skipped) */ } _NrrdGzStream; static int _nrrdGzMagic[2] = {0x1f, 0x8b}; /* gzip magic header */ /* zlib error messages */ static const char *_nrrdGzErrMsg[10] = { "need dictionary", /* Z_NEED_DICT 2 */ "stream end", /* Z_STREAM_END 1 */ "", /* Z_OK 0 */ "file error", /* Z_ERRNO (-1) */ "stream error", /* Z_STREAM_ERROR (-2) */ "data error", /* Z_DATA_ERROR (-3) */ "insufficient memory", /* Z_MEM_ERROR (-4) */ "buffer error", /* Z_BUF_ERROR (-5) */ "incompatible version",/* Z_VERSION_ERROR (-6) */ ""}; #define _NRRD_GZ_ERR_MSG(err) _nrrdGzErrMsg[Z_NEED_DICT-(err)] /* some forward declarations for things in this file */ static void _nrrdGzCheckHeader(_NrrdGzStream *s); static int _nrrdGzDestroy(_NrrdGzStream *s); static int _nrrdGzDoFlush(gzFile file, int flush); static void _nrrdGzPutLong(FILE *file, uLong x); static uLong _nrrdGzGetLong(_NrrdGzStream *s); /* ** _nrrdGzOpen() ** ** Opens a gzip (.gz) file for reading or writing. The mode parameter ** is like in fopen ("rb" or "wb"). The file represented by the FILE* pointer ** should be open already with the same mode. The mode parameter can also be ** used to specify the compression level "[0-9]" and strategy "[f|h]". ** ** The compression level must be between 0 and 9: 1 gives best speed, ** 9 gives best compression, 0 gives no compression at all (the input data ** is simply copied a block at a time). The default level is 6. ** ** The strategy parameter is used to tune the compression algorithm. Use ** "f" for data produced by a filter (or predictor), or "h" to force Huffman ** encoding only (no string match). Filtered data consists mostly of small ** values with a somewhat random distribution. In this case, the compression ** algorithm is tuned to compress them better. The effect of "f" is to force ** more Huffman coding and less string matching; it is somewhat intermediate ** between the default and Huffman. The strategy parameter only affects the ** compression ratio but not the correctness of the compressed output even ** if it is not set appropriately. ** ** The complete syntax for the mode parameter is: "(r|w[a])[0-9][f|h]". ** ** Returns Z_NULL if the file could not be opened or if there was ** insufficient memory to allocate the (de)compression state; errno ** can be checked to distinguish the two cases (if errno is zero, the ** zlib error is Z_MEM_ERROR). */ gzFile _nrrdGzOpen(FILE* fd, const char* mode) { static const char me[]="_nrrdGzOpen"; int error; int level = Z_DEFAULT_COMPRESSION; /* compression level */ int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ const char *p = mode; _NrrdGzStream *s; char fmode[AIR_STRLEN_MED]; /* copy of mode, without the compression level */ char *m = fmode; if (!mode) { biffAddf(NRRD, "%s: no file mode specified", me); return Z_NULL; } /* allocate stream struct */ s = (_NrrdGzStream *)calloc(1, sizeof(_NrrdGzStream)); if (!s) { biffAddf(NRRD, "%s: failed to allocate stream buffer", me); return Z_NULL; } /* initialize stream struct */ s->stream.zalloc = (alloc_func)0; s->stream.zfree = (free_func)0; s->stream.opaque = (voidpf)0; s->stream.next_in = s->inbuf = Z_NULL; s->stream.next_out = s->outbuf = Z_NULL; s->stream.avail_in = s->stream.avail_out = 0; s->file = NULL; s->z_err = Z_OK; s->z_eof = 0; s->crc = crc32(0L, Z_NULL, 0); s->msg = NULL; s->transparent = 0; /* parse mode flag */ s->mode = '\0'; do { if (*p == 'r') s->mode = 'r'; if (*p == 'w' || *p == 'a') s->mode = 'w'; if (*p >= '0' && *p <= '9') { level = *p - '0'; } else if (*p == 'f') { strategy = Z_FILTERED; } else if (*p == 'h') { strategy = Z_HUFFMAN_ONLY; } else { *m++ = *p; /* copy the mode */ } } while (*p++ && m != fmode + sizeof(fmode)); if (s->mode == '\0') { biffAddf(NRRD, "%s: invalid file mode", me); return _nrrdGzDestroy(s), (gzFile)Z_NULL; } if (s->mode == 'w') { error = deflateInit2(&(s->stream), level, Z_DEFLATED, -MAX_WBITS, _NRRD_DEF_MEM_LEVEL, strategy); /* windowBits is passed < 0 to suppress zlib header */ s->stream.next_out = s->outbuf = (Byte*)calloc(1, _NRRD_Z_BUFSIZE); if (error != Z_OK || s->outbuf == Z_NULL) { biffAddf(NRRD, "%s: stream init failed", me); return _nrrdGzDestroy(s), (gzFile)Z_NULL; } } else { s->stream.next_in = s->inbuf = (Byte*)calloc(1, _NRRD_Z_BUFSIZE); error = inflateInit2(&(s->stream), -MAX_WBITS); /* windowBits is passed < 0 to tell that there is no zlib header. * Note that in this case inflate *requires* an extra "dummy" byte * after the compressed stream in order to complete decompression and * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are * present after the compressed stream. */ if (error != Z_OK || s->inbuf == Z_NULL) { biffAddf(NRRD, "%s: stream init failed", me); return _nrrdGzDestroy(s), (gzFile)Z_NULL; } } s->stream.avail_out = _NRRD_Z_BUFSIZE; errno = 0; s->file = fd; if (s->file == NULL) { biffAddf(NRRD, "%s: null file pointer", me); return _nrrdGzDestroy(s), (gzFile)Z_NULL; } if (s->mode == 'w') { /* Write a very simple .gz header: */ fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", _nrrdGzMagic[0], _nrrdGzMagic[1], Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, _NRRD_OS_CODE); s->startpos = 10L; /* We use 10L instead of ftell(s->file) to because ftell causes an * fflush on some systems. This version of the library doesn't use * startpos anyway in write mode, so this initialization is not * necessary. */ } else { _nrrdGzCheckHeader(s); /* skip the .gz header */ s->startpos = (ftell(s->file) - s->stream.avail_in); } return (gzFile)s; } /* ** _nrrdGzClose() ** ** Flushes all pending output if necessary, closes the compressed file ** and deallocates the (de)compression state. */ int _nrrdGzClose (gzFile file) { static const char me[]="_nrrdGzClose"; int error; _NrrdGzStream *s = (_NrrdGzStream*)file; if (s == NULL) { biffAddf(NRRD, "%s: invalid stream", me); return 1; } if (s->mode == 'w') { error = _nrrdGzDoFlush(file, Z_FINISH); if (error != Z_OK) { biffAddf(NRRD, "%s: failed to flush pending data", me); return _nrrdGzDestroy((_NrrdGzStream*)file); } _nrrdGzPutLong(s->file, s->crc); _nrrdGzPutLong(s->file, s->stream.total_in); } return _nrrdGzDestroy((_NrrdGzStream*)file); } /* ** _nrrdGzRead() ** ** Reads the given number of uncompressed bytes from the compressed file. ** Returns the number of bytes actually read (0 for end of file). */ int _nrrdGzRead(gzFile file, void* buf, unsigned int len, unsigned int* didread) { static const char me[]="_nrrdGzRead"; _NrrdGzStream *s = (_NrrdGzStream*)file; Bytef *start = (Bytef*)buf; /* starting point for crc computation */ Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ if (s == NULL || s->mode != 'r') { biffAddf(NRRD, "%s: invalid stream or file mode", me); *didread = 0; return 1; } if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) { biffAddf(NRRD, "%s: data read error", me); *didread = 0; return 1; } if (s->z_err == Z_STREAM_END) { *didread = 0; return 0; /* EOF */ } next_out = (Byte*)buf; s->stream.next_out = (Bytef*)buf; s->stream.avail_out = len; while (s->stream.avail_out != 0) { if (s->transparent) { /* Copy first the lookahead bytes: */ uInt n = s->stream.avail_in; if (n > s->stream.avail_out) n = s->stream.avail_out; if (n > 0) { memcpy(s->stream.next_out, s->stream.next_in, n); next_out += n; s->stream.next_out = next_out; s->stream.next_in += n; s->stream.avail_out -= n; s->stream.avail_in -= n; } if (s->stream.avail_out > 0) { s->stream.avail_out -= (uInt)fread(next_out, 1, s->stream.avail_out, s->file); } len -= s->stream.avail_out; s->stream.total_in += len; s->stream.total_out += len; if (len == 0) s->z_eof = 1; *didread = len; return 0; } if (s->stream.avail_in == 0 && !s->z_eof) { errno = 0; s->stream.avail_in = (uInt)fread(s->inbuf, 1, _NRRD_Z_BUFSIZE, s->file); if (s->stream.avail_in == 0) { s->z_eof = 1; if (ferror(s->file)) { s->z_err = Z_ERRNO; break; } } s->stream.next_in = s->inbuf; } s->z_err = inflate(&(s->stream), Z_NO_FLUSH); if (s->z_err == Z_STREAM_END) { /* Check CRC and original size */ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); start = s->stream.next_out; if (_nrrdGzGetLong(s) != s->crc) { s->z_err = Z_DATA_ERROR; } else { (void)_nrrdGzGetLong(s); /* The uncompressed length returned by above getlong() may * be different from s->stream.total_out) in case of * concatenated .gz files. Check for such files: */ _nrrdGzCheckHeader(s); if (s->z_err == Z_OK) { uLong total_in = s->stream.total_in; uLong total_out = s->stream.total_out; inflateReset(&(s->stream)); s->stream.total_in = total_in; s->stream.total_out = total_out; s->crc = crc32(0L, Z_NULL, 0); } } } if (s->z_err != Z_OK || s->z_eof) break; } s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); *didread = len - s->stream.avail_out; return 0; } /* ** _nrrdGzWrite() ** ** Writes the given number of uncompressed bytes into the compressed file. ** Returns the number of bytes actually written (0 in case of error). */ int _nrrdGzWrite(gzFile file, const void* buf, unsigned int len, unsigned int* written) { static const char me[]="_nrrdGzWrite"; _NrrdGzStream *s = (_NrrdGzStream*)file; void *nonconstbuf; if (s == NULL || s->mode != 'w') { biffAddf(NRRD, "%s: invalid stream or file mode", me); *written = 0; return 1; } /* If you google for "const correct zlib" or "zlib.h is not const-correct" you'll find zlib mailing list discussions of how zlib doesn't have all the consts that it should, and various code examples of using multiple casts to hide the problem. Here's a slow way that doesn't use mere casting to make the const go away */ memcpy(&nonconstbuf, &buf, sizeof(void*)); s->stream.next_in = (Bytef*)nonconstbuf; s->stream.avail_in = len; while (s->stream.avail_in != 0) { if (s->stream.avail_out == 0) { s->stream.next_out = s->outbuf; if (fwrite(s->outbuf, 1, _NRRD_Z_BUFSIZE, s->file) != _NRRD_Z_BUFSIZE) { s->z_err = Z_ERRNO; biffAddf(NRRD, "%s: failed to write to file", me); break; } s->stream.avail_out = _NRRD_Z_BUFSIZE; } s->z_err = deflate(&(s->stream), Z_NO_FLUSH); if (s->z_err != Z_OK) break; } s->crc = crc32(s->crc, (const Bytef *)buf, len); *written = len - s->stream.avail_in; return 0; } /* ** _nrrdGzGetByte() ** ** Reads a byte from a _NrrdGzStream. Updates next_in and avail_in. ** Returns EOF for end of file. ** IN assertion: the stream s has been sucessfully opened for reading. */ static int _nrrdGzGetByte(_NrrdGzStream *s) { static const char me[]="_nrrdGzGetByte"; if (s->z_eof) return EOF; if (s->stream.avail_in == 0) { errno = 0; s->stream.avail_in = (uInt)fread(s->inbuf, 1, _NRRD_Z_BUFSIZE, s->file); if (s->stream.avail_in == 0) { s->z_eof = 1; if (ferror(s->file)) { biffAddf(NRRD, "%s: failed to read from file", me); s->z_err = Z_ERRNO; } return EOF; } s->stream.next_in = s->inbuf; } s->stream.avail_in--; return *(s->stream.next_in)++; } /* ******** _nrrdGzCheckHeader() ** ** Checks the gzip header of a _NrrdGzStream opened for reading. Sets ** the stream mode to transparent if the gzip magic header is not ** present; sets s->err to Z_DATA_ERROR if the magic header is present ** but the rest of the header is incorrect. ** IN assertion: the stream s has already been created sucessfully; ** s->stream.avail_in is zero for the first time, but may be non-zero ** for concatenated .gz files. */ static void _nrrdGzCheckHeader(_NrrdGzStream *s) { static const char me[]="_nrrdGzCheckHeader"; int method; /* method byte */ int flags; /* flags byte */ uInt len; int c; /* Check the gzip magic header */ for (len = 0; len < 2; len++) { c = _nrrdGzGetByte(s); if (c != _nrrdGzMagic[len]) { if (len != 0) s->stream.avail_in++, s->stream.next_in--; if (c != EOF) { s->stream.avail_in++, s->stream.next_in--; s->transparent = 1; } s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END; return; } } method = _nrrdGzGetByte(s); flags = _nrrdGzGetByte(s); if (method != Z_DEFLATED || (flags & _NRRD_RESERVED) != 0) { biffAddf(NRRD, "%s: gzip compression method is not deflate", me); s->z_err = Z_DATA_ERROR; return; } /* Discard time, xflags and OS code: */ for (len = 0; len < 6; len++) (void)_nrrdGzGetByte(s); if ((flags & _NRRD_EXTRA_FIELD) != 0) { /* skip the extra field */ len = (uInt)_nrrdGzGetByte(s); len += ((uInt)_nrrdGzGetByte(s))<<8; /* len is garbage if EOF but the loop below will quit anyway */ while (len-- != 0 && _nrrdGzGetByte(s) != EOF) ; } if ((flags & _NRRD_ORIG_NAME) != 0) { /* skip the original file name */ while ((c = _nrrdGzGetByte(s)) != 0 && c != EOF) ; } if ((flags & _NRRD_COMMENT) != 0) { /* skip the .gz file comment */ while ((c = _nrrdGzGetByte(s)) != 0 && c != EOF) ; } if ((flags & _NRRD_HEAD_CRC) != 0) { /* skip the header crc */ for (len = 0; len < 2; len++) (void)_nrrdGzGetByte(s); } s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; } /* ** _nrrdGzDestroy() ** ** Cleans up then free the given _NrrdGzStream. Returns a zlib error code. ** Try freeing in the reverse order of allocations. FILE* s->file is not ** closed. Because we didn't allocate it, we shouldn't delete it. */ static int _nrrdGzDestroy(_NrrdGzStream *s) { static const char me[]="_nrrdGzDestroy"; int error = Z_OK; if (s == NULL) { biffAddf(NRRD, "%s: invalid stream", me); return 1; } s->msg = (char *)airFree(s->msg); if (s->stream.state != NULL) { if (s->mode == 'w') { error = deflateEnd(&(s->stream)); } else if (s->mode == 'r') { error = inflateEnd(&(s->stream)); } } if (error != Z_OK) { biffAddf(NRRD, "%s: %s", me, _NRRD_GZ_ERR_MSG(error)); } if (s->z_err < 0) error = s->z_err; if (error != Z_OK) { biffAddf(NRRD, "%s: %s", me, _NRRD_GZ_ERR_MSG(error)); } s->inbuf = (Byte *)airFree(s->inbuf); s->outbuf = (Byte *)airFree(s->outbuf); airFree(s); /* avoiding unused value warnings, no NULL set */ return error != Z_OK; } /* ** _nrrdGzDoFlush() ** ** Flushes all pending output into the compressed file. The parameter ** flush is the same as in the deflate() function. */ static int _nrrdGzDoFlush(gzFile file, int flush) { static const char me[]="_nrrdGzDoFlush"; uInt len; int done = 0; _NrrdGzStream *s = (_NrrdGzStream*)file; if (s == NULL || s->mode != 'w') { biffAddf(NRRD, "%s: invalid stream or file mode", me); return Z_STREAM_ERROR; } s->stream.avail_in = 0; /* should be zero already anyway */ for (;;) { len = _NRRD_Z_BUFSIZE - s->stream.avail_out; if (len != 0) { if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { s->z_err = Z_ERRNO; return Z_ERRNO; } s->stream.next_out = s->outbuf; s->stream.avail_out = _NRRD_Z_BUFSIZE; } if (done) break; s->z_err = deflate(&(s->stream), flush); /* Ignore the second of two consecutive flushes: */ if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; /* deflate has finished flushing only when it hasn't used up * all the available space in the output buffer: */ done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; } return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; } /* ** _nrrdGzPutLong() ** ** Outputs a long in LSB order to the given file. */ static void _nrrdGzPutLong(FILE* file, uLong x) { int n; for (n = 0; n < 4; n++) { fputc((int)(x & 0xff), file); x >>= 8; } } /* ** _nrrdGzGetLong() ** ** Reads a long in LSB order from the given _NrrdGzStream. ** Sets z_err in case of error. */ static uLong _nrrdGzGetLong(_NrrdGzStream *s) { uLong x = (uLong)_nrrdGzGetByte(s); int c; x += ((uLong)_nrrdGzGetByte(s))<<8; x += ((uLong)_nrrdGzGetByte(s))<<16; c = _nrrdGzGetByte(s); if (c == EOF) s->z_err = Z_DATA_ERROR; x += ((uLong)c)<<24; return x; } #endif /* TEEM_ZLIB */ /* ** random symbol to have in object file, even when Zlib not enabled */ int _nrrdGzDummySymbol(void) { return 42; } teem-1.11.0~svn6057/src/nrrd/tmf/0000775000175000017500000000000012203513757016134 5ustar domibeldomibelteem-1.11.0~svn6057/src/nrrd/tmf/fix3.pl0000775000175000017500000000352712165627520017355 0ustar domibeldomibel#!/usr/bin/perl -w # # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # for most of the TMF filters, the parm vector is not used, and # fix2.pl assumes as much by putting in an AIR_UNUSED declaration. # This takes out the AIR_UNUSED for the filters that do actually # use the parm vector (thanks to do the nearby kernel identification) while (<>) { s|AIR_UNUSED\(parm\); /\* TMF_dn_cn_3ef \*/||g; s|AIR_UNUSED\(parm\); /\* TMF_dn_c1_4ef \*/||g; s|AIR_UNUSED\(parm\); /\* TMF_d1_cn_2ef \*/||g; s|AIR_UNUSED\(parm\); /\* TMF_d1_cn_4ef \*/||g; s|AIR_UNUSED\(parm\); /\* TMF_d1_c0_3ef \*/||g; s|AIR_UNUSED\(parm\); /\* TMF_d1_c0_4ef \*/||g; s|AIR_UNUSED\(parm\); /\* TMF_d2_cn_3ef \*/||g; s|AIR_UNUSED\(parm\); /\* TMF_d2_c1_4ef \*/||g; print; } teem-1.11.0~svn6057/src/nrrd/tmf/fix2.pl0000775000175000017500000002314412165627520017351 0ustar domibeldomibel#!/usr/bin/perl -w # # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # print "\n\#include \"nrrd.h\"\n\n"; print " /* ************************************************* */\n"; print " /* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */\n"; print " /* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */\n"; print " /* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */\n"; print " /* */\n"; print " /* */\n"; print " /* THIS FILE AUTOMATICALLY GENERATED FROM */\n"; print " /* PERL SCRIPTS IN THE tmf SUBDIRECTORY */\n"; print " /* EDIT THOSE SCRIPTS, NOT THIS FILE! */\n"; print " /* */\n"; print " /* */\n"; print " /* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */\n"; print " /* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */\n"; print " /* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */\n"; print " /* ************************************************* */\n\n"; # generate a stub kernel for when the user incorrectly indexes # into ef == 0, which is undefined; ef must be >= 1 print "static double\n"; print "_nrrd_TMFBAD_Int(const double *parm) {\n"; print " AIR_UNUSED(parm);\n"; print " fprintf\(stderr, \"_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\\n\"\);\n"; print " return 0.0;\n"; print "}\n\n"; print "static double\n"; print "_nrrd_TMFBAD_Sup(const double *parm) {\n"; print " AIR_UNUSED(parm);\n"; print " fprintf\(stderr, \"_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\\n\"\);\n"; print " return 0.0;\n"; print "}\n\n"; print "static double\n"; print "_nrrd_TMFBAD_1_d(double x, const double *parm) {\n"; print " AIR_UNUSED(x);\n"; print " AIR_UNUSED(parm);\n"; print " fprintf\(stderr, \"_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\\n\"\);\n"; print " return 0.0;\n"; print "}\n\n"; print "static float\n"; print "_nrrd_TMFBAD_1_f(float x, const double *parm) {\n"; print " AIR_UNUSED(x);\n"; print " AIR_UNUSED(parm);\n"; print " fprintf\(stderr, \"_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\\n\"\);\n"; print " return 0.0;\n"; print "}\n\n"; print "static void\n"; print "_nrrd_TMFBAD_N_d(double *f, const double *x, size_t len, const double *parm) {\n"; print " AIR_UNUSED(f);\n"; print " AIR_UNUSED(x);\n"; print " AIR_UNUSED(len);\n"; print " AIR_UNUSED(parm);\n"; print " fprintf\(stderr, \"_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\\n\"\);\n"; print "}\n\n"; print "static void\n"; print "_nrrd_TMFBAD_N_f(float *f, const float *x, size_t len, const double *parm) {\n"; print " AIR_UNUSED(f);\n"; print " AIR_UNUSED(x);\n"; print " AIR_UNUSED(len);\n"; print " AIR_UNUSED(parm);\n"; print " fprintf\(stderr, \"_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\\n\"\);\n"; print "}\n\n"; print "static NrrdKernel\n"; print "_nrrdKernel_TMFBAD = {\n"; print " \"TMFBAD\",\n"; print " 1, _nrrd_TMFBAD_Sup, _nrrd_TMFBAD_Int,\n"; print " _nrrd_TMFBAD_1_f, _nrrd_TMFBAD_N_f,\n"; print " _nrrd_TMFBAD_1_d, _nrrd_TMFBAD_N_d\n"; print "};\n"; %needk = (); %support = (); $maxD = 0; $maxC = 0; $maxA = 0; # process body of file while (<>) { if (/\#define/) { print; next; } if (/float (d[n012]_c[n0123]_[1234]ef)\(float a/) { $kern = "TMF_$1"; if ($_ =~ m/d([012])_c([0123])_([1234])ef/) { $maxD = $1 > $maxD ? $1 : $maxD; $maxC = $2 > $maxC ? $2 : $maxC; $maxA = $3 > $maxA ? $3 : $maxA; } print "\n/* ------------------------ $kern --------------------- */\n\n"; print "\#define ${kern}(a, i, t) ( \\\n"; while (<>) { # for when $kern is just a wrapper around $_needk if (/return (d[n012]_c[n0123]_[1234]ef)\((.+), *t\)/) { $_needk = "TMF_$1"; # finish the #define, and pass in the correct first arg print " ${_needk}((double)($2), i, t))\n\n"; # remember that this kernel needs another in order to know the support $needk{$kern} = $_needk; last; } # process the switch cases s/case ([0-9]): +result *= *(.+); +break;$/\(i == $1 ? $2 : \\/g; $n = $1; $sup = ($n + 1)/2; # when we've reached the end of the switch cases if (/default: result = 0;/) { # the default case print " 0"; # print one end paren for each of the cases for ($i=0; $i <= $n; $i++) { print ")"; } # and one more end paren to finish the #define print ")\n\n"; # remember what the support is $support{$kern} = $sup; last; } print; } } } # generate 3-D array of all TMFs if (2 != $maxD) { print "teem/src/nrrd/tmf/fix2.pl error: maxD = $maxD, not 2\n"; exit; } if (3 != $maxC) { print "teem/src/nrrd/tmf/fix2.pl error: maxC = $maxC, not 3\n"; exit; } if (4 != $maxA) { print "teem/src/nrrd/tmf/fix2.pl error: maxA = $maxA, not 4\n"; exit; } # print out code for all kernels for ($_d=0; $_d<=3; $_d++) { $d = (($_d > 0) ? $_d-1 : "n"); for ($_c=0; $_c<=4; $_c++) { $c = (($_c > 0) ? $_c-1 : "n"); for ($ef=1; $ef<=4; $ef++) { $kern = "TMF_d${d}_c${c}_${ef}ef"; print "\n/* ------------------------ $kern --------------------- */\n\n"; print blah($kern); } } } # create master array of all kernels print "\nNrrdKernel *const\n"; print "nrrdKernelTMF[4][5][5] = {\n"; for ($_d=0; $_d<=3; $_d++) { $d = (($_d > 0) ? $_d-1 : "n"); print " { /* d = $d */ \n"; for ($_c=0; $_c<=4; $_c++) { $c = (($_c > 0) ? $_c-1 : "n"); print " {\n"; print " &_nrrdKernel_TMFBAD,\n"; for ($ef=1; $ef<=4; $ef++) { $kern = "TMF_d${d}_c${c}_${ef}ef"; print " &_nrrdKernel_${kern},\n"; } print " },\n"; } print " },\n"; } print "};\n\n"; print "const unsigned int nrrdKernelTMF_maxD = $maxD;\n"; print "const unsigned int nrrdKernelTMF_maxC = $maxC;\n"; print "const unsigned int nrrdKernelTMF_maxA = $maxA;\n"; sub blah { $kern = $_[0]; # there seems to be at most two levels of indirection $sup = (exists $support{$kern} ? $support{$kern} : (exists $support{$needk{$kern}} ? $support{$needk{$kern}} : $support{$needk{$needk{$kern}}})); $integral = ("static double " . "_nrrd_${kern}_Int\(const double *parm\) {\n" . " AIR_UNUSED(parm);\n" . (($kern =~ m/_d0_/ || $kern =~ m/_dn_/) ? " return 1.0;\n" : " return 0.0;\n") . "}\n\n"); $support = ("static double " . "_nrrd_${kern}_Sup\(const double *parm\) {\n" . " AIR_UNUSED(parm);\n" . " return ${sup};\n" . "}\n\n"); $_1_d = ("static double\n" . "_nrrd_${kern}_1_d\(double x, const double *parm\) {\n" . " int i;\n\n" . " AIR_UNUSED(parm); /* ${kern} */\n" . " x += $sup;\n" . " i = (int)((x<0) ? x-1 : x); /* HEY scrutinize cast */\n" . " x -= i;\n" . " return ${kern}\(parm[0], i, x\);\n" . "}\n\n"); $_1_f = ("static float\n" . "_nrrd_${kern}_1_f\(float x, const double *parm\) {\n" . " int i;\n\n" . " AIR_UNUSED(parm); /* ${kern} */\n" . " x += $sup;\n" . " i = (int)((x<0) ? x-1 : x); /* HEY scrutinize cast */\n" . " x -= AIR_CAST(float, i);\n" . " return AIR_CAST(float, ${kern}\(parm[0], i, x\));\n" . "}\n\n"); $_N_d = ("static void\n" . "_nrrd_${kern}_N_d(double *f, const double *x, size_t len, const double *parm) {\n" . " double t;\n" . " size_t I;\n" . " int i;\n\n" . " AIR_UNUSED(parm); /* ${kern} */\n" . " for \(I=0; I! ../tmfKernel.c teem-1.11.0~svn6057/src/nrrd/tmf/tmFilters_raw.c0000664000175000017500000006541012165631065021130 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* ### Written by Torsten Moeller ### sometime in March 1998 ### email me if you find any errors ### ### March 2003 - modified to remove coeff part */ #pragma warning(disable:4244) #pragma warning(disable:4305) #define OVER_3 0.33333333 #define OVER_6 0.16666666 #define OVER_12 0.0833333333 #define OVER_2_3 0.6666666666 #include "tmFilters_raw.h" /***************************************************************************/ /* Approximation Filters */ /***************************************************************************/ float dn_cn_1ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = 0.5; break; case 1: result = 0.5; break; default: result = 0; } return result; } float dn_cn_2ef(float a, float t) { return d0_c0_2ef(a, t); } float dn_cn_3ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ( 0.25*t +(2*a-0.25))*t - a ; break; case 1: result = (-0.25*t -(6*a-1.25))*t + 3*a ; break; case 2: result = (-0.25*t +(6*a-0.75))*t - 3*a+1; break; case 3: result = ( 0.25*t -(2*a+0.25))*t + a ; break; default: result = 0; } return result; } float dn_cn_4ef(float a, float t) { return d0_c0_4ef(a, t); } float dn_c0_1ef(float a, float t) { return d0_c0_2ef(a, t); } float dn_c0_2ef(float a, float t) { return d0_c0_2ef(a, t); } float dn_c0_3ef(float a, float t) { return d0_c0_3ef(a, t); } float dn_c0_4ef(float a, float t) { return d0_c0_4ef(a,t); } float dn_c1_1ef(float a, float t) { return d0_c1_1ef(a, t); } float dn_c1_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ( t )*t/4; break; case 1: result = (-t +2)*t/4 +0.25; break; case 2: result = (-t )*t/4 +0.5; break; case 3: result = ( t -2)*t/4 +0.25; break; default: result = 0; } return result; } float dn_c1_3ef(float a, float t) { return d0_c1_3ef(a, t); } float dn_c1_4ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((-( a )*t +(1.5*a- 1./24))*t + 0)*t + 0; break; case 1: result = (( ( 5*a+OVER_6)*t -(7.5*a- 1./8 ))*t - (OVER_12))*t +(0.5*a-1./24 ); break; case 2: result = ((-(10*a+ 0.5)*t +( 15*a+ 5./12))*t +(OVER_2_3))*t -( 2*a-OVER_6); break; case 3: result = (( (10*a+ 0.5)*t -( 15*a+13./12))*t + 0)*t +( 3*a+0.75 ); break; case 4: result = ((-( 5*a+OVER_6)*t +(7.5*a+ 5./8 ))*t -(OVER_2_3))*t -( 2*a-OVER_6); break; case 5: result = (( ( a )*t -(1.5*a+ 1./24))*t + (OVER_12))*t +(0.5*a-1./24 ); break; default: result = 0; } return result; } float dn_c2_1ef(float a, float t) { return d0_c2_1ef(a, t); } float dn_c2_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (( OVER_6*t + 0)*t + 0)*t + 0; break; case 1: result = (( -0.5*t +0.5)*t +0.5)*t + OVER_6; break; case 2: result = (( 0.5*t - 1)*t + 0)*t +OVER_2_3; break; case 3: result = ((-OVER_6*t +0.5)*t -0.5)*t + OVER_6; break; default: result = 0; } return result; } float dn_c2_3ef(float a, float t) { return d0_c2_3ef(a, t); } float dn_c2_4ef(float a, float t) { return dn_c1_4ef(1./36, t); } float dn_c3_1ef(float a, float t) { return d0_c3_1ef(a, t); } float dn_c3_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (-0.10*t +0.25)*t*t*t*t; break; case 1: result = ((( 0.30*t -0.75)*t*t +0.5)*t +0.5)*t +0.15; break; case 2: result = (((-0.30*t +0.75)*t*t -1 )*t +0 )*t +0.70; break; case 3: result = ((( 0.10*t -0.25)*t*t +0.5)*t -0.5)*t +0.15; break; default: result = 0; } return result; } float dn_c3_3ef(float a, float t) { return d0_c3_3ef(a, t); } float dn_c3_4ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (((( 1./30*t - 1./16)*t + 0)*t + 0)*t + 0)*t + 0; break; case 1: result = ((((-OVER_6*t +17./48)*t +OVER_12)*t - 1./24)*t - OVER_12)*t - 7./240; break; case 2: result = (((( OVER_3*t -19./24)*t -OVER_6 )*t +OVER_2_3)*t +OVER_2_3)*t + 7./60; break; case 3: result = ((((-OVER_3*t + 7./8 )*t + 0)*t - 1.25)*t + 0)*t +33./40; break; case 4: result = (((( OVER_6*t -23./48)*t +OVER_6 )*t +OVER_2_3)*t -OVER_2_3)*t + 7./60; break; case 5: result = ((((- 1./30*t + 5./48)*t -OVER_12)*t - 1./24)*t + OVER_12)*t - 7./240; break; default: result = 0; } return result; } /***************************************************************************/ /* Interpolation Filters */ /***************************************************************************/ float d0_cn_1ef(float a, float t) { return d0_c0_2ef(a, t); } float d0_cn_2ef(float a, float t) { return d0_c0_2ef(a, t); } float d0_cn_3ef(float a, float t) { return d0_c0_3ef(a, t); } float d0_cn_4ef(float a, float t) { return d0_c0_4ef(a, t); } float d0_c0_1ef(float a, float t) { return d0_c0_2ef(a, t); } float d0_c0_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = t; break; case 1: result = 1-t; break; default: result = 0; } return result; } float d0_c0_3ef(float a, float t) { return dn_cn_3ef(0, t); } float d0_c0_4ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (( OVER_6*t +0 )*t -OVER_6)*t ; break; case 1: result = (( -0.5*t +0.5)*t +1 )*t ; break; case 2: result = (( 0.5*t -1 )*t -0.5 )*t+1; break; case 3: result = ((-OVER_6*t +0.5)*t -OVER_3)*t ; break; default: result = 0; } return result; } float d0_c1_1ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (-2*t +3)*t*t; break; case 1: result = ( 2*t -3)*t*t +1; break; default: result = 0; } return result; } float d0_c1_2ef(float a, float t) { return d0_c1_3ef(a, t); } float d0_c1_3ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (( 0.5*t -0.5)*t +0 )*t; break; case 1: result = ((-1.5*t +2 )*t +0.5)*t; break; case 2: result = (( 1.5*t -2.5)*t +0 )*t +1; break; case 3: result = ((-0.5*t +1 )*t -0.5)*t; break; default: result = 0; } return result; } float d0_c1_4ef(float a, float t) { return dn_c1_4ef(1./12, t); } float d0_c2_1ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (( 6*t -15)*t +10)*t*t*t; break; case 1: result = ((-6*t +15)*t -10)*t*t*t +1; break; default: result = 0; } return result; } float d0_c2_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((( 0.5*t -0.5)*t + 0)*t + 0)*t; break; case 1: result = (((-0.5*t -0.5)*t +1.5)*t +0.5)*t; break; case 2: result = (((-0.5*t +2.5)*t - 3)*t + 0)*t +1; break; case 3: result = ((( 0.5*t -1.5)*t +1.5)*t -0.5)*t; break; default: result = 0; } return result; } float d0_c2_3ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((((-1*t +2.5)*t -1.5)*t +0 )*t +0 )*t ; break; case 1: result = (((( 3*t -7.5)*t +4.5)*t +0.5)*t +0.5)*t ; break; case 2: result = ((((-3*t +7.5)*t -4.5)*t -1 )*t +0 )*t +1; break; case 3: result = (((( 1*t -2.5)*t +1.5)*t +0.5)*t -0.5)*t ; break; default: result = 0; } return result; } float d0_c2_4ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (((-1./12*t + 1./12)*t + 0)*t + 0)*t; break; case 1: result = ((( 1./4 *t + 1./12)*t -1./4)*t - OVER_12)*t; break; case 2: result = (((-1./6 *t - 1 )*t +3./2)*t +OVER_2_3)*t; break; case 3: result = (((-1./6 *t + 5./3 )*t -5./2)*t + 0)*t +1; break; case 4: result = ((( 1./4 *t -13./12)*t +3./2)*t -OVER_2_3)*t; break; case 5: result = (((-1./12*t + 1./4 )*t -1./4)*t + OVER_12)*t; break; default: result = 0; } return result; } float d0_c3_1ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (((-20*t +70)*t -84)*t +35)*t*t*t*t; break; case 1: result = ((( 20*t -70)*t +84)*t -35)*t*t*t*t +1; break; default: result = 0; } return result; } float d0_c3_2ef(float a, float t) { return d0_c3_3ef(a, t); float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((((-0.75*t +2 )*t -1.25)*t*t +0 )*t +0 )*t ; break; case 1: result = (((( 0.75*t -1.5)*t +0 )*t*t +1.25)*t +0.5)*t ; break; case 2: result = (((( 0.75*t -3 )*t +3.75)*t*t -2.5 )*t +0 )*t +1; break; case 3: result = ((((-0.75*t +2.5)*t -2.5 )*t*t +1.25)*t -0.5)*t ; break; default: result = 0; } return result; } float d0_c3_3ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((((( 3*t -10.5)*t +12.5)*t - 5)*t*t +0 )*t +0 )*t ; break; case 1: result = (((((-9*t +31.5)*t -37.5)*t +15)*t*t +0.5)*t +0.5)*t ; break; case 2: result = ((((( 9*t -31.5)*t +37.5)*t -15)*t*t -1 )*t +0 )*t +1; break; case 3: result = (((((-3*t +10.5)*t -12.5)*t + 5)*t*t +0.5)*t -0.5)*t ; break; default: result = 0; } return result; } float d0_c3_4ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((((( 7./48*t - 3./8)*t +11./48)*t +0 )*t + 0 )*t + 0)*t; break; case 1: result = (((((-7./16*t + 1 )*t - 3./8 )*t +1./12)*t - 3./16)*t - OVER_12)*t; break; case 2: result = ((((( 7./24*t - 1./4)*t -19./24)*t -1./6 )*t + 5./4 )*t +OVER_2_3)*t; break; case 3: result = ((((( 7./24*t - 3./2)*t + 7./3 )*t +0 )*t -17./8 )*t + 0)*t +1; break; case 4: result = (((((-7./16*t +13./8)*t -31./16)*t +1./6 )*t + 5./4 )*t -OVER_2_3)*t; break; case 5: result = ((((( 7./48*t - 1./2)*t +13./24)*t -1./12)*t - 3./16)*t + OVER_12)*t; break; default: result = 0; } return result; } /***************************************************************************/ /* First Derivative Filters */ /***************************************************************************/ float d1_cn_1ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = 1; break; case 1: result = -1; break; default: result = 0; } return result; } float d1_cn_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = 0.5*t +( a ); break; case 1: result = -0.5*t -(3*a-0.5); break; case 2: result = -0.5*t +(3*a ); break; case 3: result = 0.5*t -( a+0.5); break; default: result = 0; } return result; } float d1_cn_3ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ( 0.5*t +0)*t -OVER_6; break; case 1: result = (-1.5*t +1)*t +1; break; case 2: result = ( 1.5*t -2)*t -0.5; break; case 3: result = (-0.5*t +1)*t -OVER_3; break; default: result = 0; } return result; } float d1_cn_4ef(float a, float t) { return d1_c0_4ef(a, t); } float d1_c0_1ef(float a, float t) { return d1_c0_2ef(a, t); } float d1_c0_2ef(float a, float t) { return d1_cn_2ef(0, t); } float d1_c0_3ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ( ( a )*t -( a+OVER_12))*t +0; break; case 1: result = (-( 5*a-0.5)*t +( 5*a+ 0.25))*t -OVER_12; break; case 2: result = ( (10*a-1.5)*t -(10*a- 5./6))*t +OVER_2_3; break; case 3: result = (-(10*a-1.5)*t +(10*a- 13./6))*t +0; break; case 4: result = ( ( 5*a-0.5)*t -( 5*a- 1.25))*t -OVER_2_3; break; case 5: result = (-( a )*t +( a-OVER_12))*t +OVER_12; break; default: result = 0; } return result; } float d1_c0_4ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((OVER_12*t +( a ))*t -( a+ OVER_6))*t +0; break; case 1: result = (( -0.25*t -( 5*a-0.25))*t +( 5*a+ 0.75))*t -OVER_12; break; case 2: result = (( OVER_6*t +(10*a- 0.5))*t -(10*a+ OVER_3))*t +OVER_2_3; break; case 3: result = (( OVER_6*t -(10*a ))*t +(10*a- 5./6))*t +0; break; case 4: result = (( -0.25*t +( 5*a+ 0.5))*t -( 5*a- 0.5))*t -OVER_2_3; break; case 5: result = ((OVER_12*t -( a+0.25))*t +( a+OVER_12))*t +OVER_12; break; default: result = 0; } return result; } float d1_c1_1ef(float a, float t) { return d1_c1_2ef(a, t); } float d1_c1_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ( 0.5*t +0)*t +0; break; case 1: result = (-1.5*t +1)*t +0.5; break; case 2: result = ( 1.5*t -2)*t +0; break; case 3: result = (-0.5*t +1)*t -0.5; break; default: result = 0; } return result; } float d1_c1_3ef(float a, float t) { return d1_c0_3ef(-1./12, t); } float d1_c1_4ef(float a, float t) { return d1_c0_4ef(-1./6, t); } float d1_c2_1ef(float a, float t) { return d1_c2_2ef(a, t); } float d1_c2_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((-0.5*t +1)*t*t +0)*t +0; break; case 1: result = (( 1.5*t -3)*t*t +1)*t +0.5; break; case 2: result = ((-1.5*t +3)*t*t -2)*t +0; break; case 3: result = (( 0.5*t -1)*t*t +1)*t -0.5; break; default: result = 0; } return result; } float d1_c2_3ef(float a, float t) { return d1_c2_4ef(a, t); } float d1_c2_4ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((( OVER_6*t - 0.25)*t + 0)*t + 0)*t +0; break; case 1: result = (((- 5./6*t +17./12)*t +0.25)*t -OVER_12)*t -OVER_12; break; case 2: result = ((( 5./3*t - 19./6)*t -0.5 )*t + 4./3)*t +OVER_2_3; break; case 3: result = (((- 5./3*t + 3.5)*t +0 )*t - 2.5)*t +0; break; case 4: result = ((( 5./6*t -23./12)*t +0.5 )*t + 4./3)*t -OVER_2_3; break; case 5: result = (((-OVER_6*t + 5./12)*t -0.25)*t -OVER_12)*t +OVER_12; break; default: result = 0; } return result; } float d1_c3_1ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (((-0.75*t +1.25)*t + 0)*t*t + 0)*t +0; break; case 1: result = ((( 0.75*t + 0)*t -2.5)*t*t +1.25)*t +0.5; break; case 2: result = ((( 0.75*t -3.75)*t +5 )*t*t -2.5 )*t +0; break; case 3: result = (((-0.75*t +2.5 )*t -2.5)*t*t +1.25)*t -0.5; break; default: result = 0; } return result; } float d1_c3_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((( 1*t -3)*t +2.5)*t*t*t +0)*t +0; break; case 1: result = (((-3*t +9)*t -7.5)*t*t*t +1)*t +0.5; break; case 2: result = ((( 3*t -9)*t +7.5)*t*t*t -2)*t +0; break; case 3: result = (((-1*t +3)*t -2.5)*t*t*t +1)*t -0.5; break; default: result = 0; } return result; } float d1_c3_3ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (((( 3./16*t - 13./48)*t + 0)*t + 0)*t + 0)*t +0; break; case 1: result = ((((-9./16*t + 5./12)*t +19./24)*t +0.25)*t - 7./48)*t -OVER_12; break; case 2: result = (((( 3./8 *t + 25./24)*t -19./6 )*t - 0.5)*t +19./12)*t +OVER_2_3; break; case 3: result = (((( 3./8 *t - 35./12)*t +19./4 )*t + 0)*t -23./8 )*t +0; break; case 4: result = ((((-9./16*t + 115./48)*t -19./6 )*t + 0.5)*t +19./12)*t -OVER_2_3; break; case 5: result = (((( 3./16*t -OVER_2_3)*t +19./24)*t -0.25)*t - 7./48)*t +OVER_12; break; default: result = 0; } return result; } float d1_c3_4ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (((((-0.25*t +0.75)*t - 7./12)*t + 0)*t + 0)*t + 0)*t +0; break; case 1: result = ((((( 1.25*t -3.75)*t +35./12)*t + OVER_6)*t +0.25)*t -OVER_12)*t -OVER_12; break; case 2: result = ((((( -2.5*t + 7.5)*t -35./6 )*t -OVER_2_3)*t - 0.5)*t + 4./3)*t +OVER_2_3; break; case 3: result = ((((( 2.5*t - 7.5)*t +35./6 )*t + 1)*t + 0)*t - 5./2)*t +0; break; case 4: result = (((((-1.25*t +3.75)*t -35./12)*t -OVER_2_3)*t + 0.5)*t + 4./3)*t -OVER_2_3; break; case 5: result = ((((( 0.25*t -0.75)*t + 7./12)*t + OVER_6)*t -0.25)*t -OVER_12)*t +OVER_12; break; default: result = 0; } return result; } /***************************************************************************/ /* Second Derivative Filters */ /***************************************************************************/ float d2_cn_1ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = 0.5; break; case 1: result = -0.5; break; case 2: result = -0.5; break; case 3: result = 0.5; break; default: result = 0; } return result; } float d2_cn_2ef(float a, float t) { return d2_c0_2ef(a, t); } float d2_cn_3ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ( 0.25*t +(a-30)/120)*t -(a+10)/240; break; case 1: result = (-0.75*t -(a-42)/24 )*t +(a+ 6)/48; break; case 2: result = ( 0.5 *t +(a-42)/12 )*t -(a-22)/24; break; case 3: result = ( 0.5 *t -(a-30)/12 )*t +(a-50)/24; break; case 4: result = (-0.75*t +(a- 6)/24 )*t -(a-54)/48; break; case 5: result = ( 0.25*t -(a+30)/120)*t +(a-10)/240; break; default: result = 0; } return result; } float d2_cn_4ef(float a, float t) { return d2_c0_4ef(a, t); } float d2_c0_1ef(float a, float t) { return d2_c0_2ef(a, t); } float d2_c0_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = t; break; case 1: result = -3*t +1; break; case 2: result = 3*t -2; break; case 3: result = - t +1; break; default: result = 0; } return result; } float d2_c0_3ef(float a, float t) { return d2_cn_3ef(-10, t); } float d2_c0_4ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (( 1./6*t +0 )*t -0.25)*t + 0; break; case 1: result = ((-5./6*t +0.5)*t +1.75)*t -1./12; break; case 2: result = (( 5./3*t -2 )*t -3.5 )*t + 4./3; break; case 3: result = ((-5./3*t +3 )*t +2.5 )*t - 2.5; break; case 4: result = (( 5./6*t -2 )*t -0.25)*t + 4./3; break; case 5: result = ((-1./6*t +0.5)*t -0.25)*t -1./12; break; default: result = 0; } return result; } float d2_c1_1ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (-2*t +3)*t*t +0; break; case 1: result = ( 6*t -9)*t*t +1; break; case 2: result = (-6*t +9)*t*t -2; break; case 3: result = ( 2*t -3)*t*t +1; break; default: result = 0; } return result; } float d2_c1_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ( 0.25*t +0 )*t; break; case 1: result = (-0.75*t +0.5)*t +0.25; break; case 2: result = ( 0.5 *t -1 )*t; break; case 3: result = ( 0.5 *t +0 )*t -0.5; break; case 4: result = (-0.75*t +1 )*t; break; case 5: result = ( 0.25*t -0.5)*t +0.25; break; default: result = 0; } return result; } float d2_c1_3ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (( 2./3*t - 0.75)*t +0 )*t; break; case 1: result = ((-10./3*t + 4.25)*t +0.5)*t -1./12; break; case 2: result = (( 20./3*t - 9.5 )*t -1 )*t +4./3; break; case 3: result = ((-20./3*t +10.5 )*t +0 )*t -2.5; break; case 4: result = (( 10./3*t - 5.75)*t +1 )*t +4./3; break; case 5: result = ((- 2./3*t + 1.25)*t -0.5)*t -1./12; break; default: result = 0; } return result; } float d2_c1_4ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((-( a+ 53)/360*t +( a+ 38)/240)*t +0 )*t; break; case 1: result = (( (7*a+431)/360*t -(7*a+296)/240)*t - 1./8)*t +(a+ 8)/720; break; case 2: result = ((-(7*a+471)/120*t +(7*a+366)/80 )*t +1 )*t -(a+18)/120; break; case 3: result = (( (7*a+491)/72 *t -(7*a+452)/48 )*t -13./8)*t +(a+72)/48 ; break; case 4: result = ((-(7*a+491)/72 *t +(7*a+530)/48 )*t +0 )*t -(a+98)/36 ; break; case 5: result = (( (7*a+471)/120*t -(7*a+576)/80 )*t +13./8)*t +(a+72)/48 ; break; case 6: result = ((-(7*a+431)/360*t +(7*a+566)/240)*t -1 )*t -(a+18)/120; break; case 7: result = (( ( a+ 53)/360*t -( a+ 68)/240)*t + 1./8)*t +(a+ 8)/720; break; default: result = 0; } return result; } float d2_c2_1ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (( 6*t -15)*t +10)*t*t*t; break; case 1: result = ((-18*t +45)*t -30)*t*t*t +1; break; case 2: result = (( 18*t -45)*t +30)*t*t*t -2; break; case 3: result = ((- 6*t +15)*t -10)*t*t*t +1; break; default: result = 0; } return result; } float d2_c2_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (( 1./6*t +0 )*t +0 )*t; break; case 1: result = ((-5./6*t +0.5)*t +0.5)*t +1./6; break; case 2: result = (( 5./3*t -2 )*t -1 )*t +1./3; break; case 3: result = ((-5./3*t +3 )*t +0 )*t -1; break; case 4: result = (( 5./6*t -2 )*t +1 )*t +1./3; break; case 5: result = ((-1./6*t +0.5)*t -0.5)*t +1./6; break; default: result = 0; } return result; } float d2_c2_3ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((((-1.5*t + 3.75)*t - 7./3)*t +0 )*t +0 )*t; break; case 1: result = (((( 7.5*t -18.75)*t +35./3)*t +0.5)*t +0.5)*t -1./12; break; case 2: result = ((((- 15*t +37.5 )*t -70./3)*t -2 )*t -1 )*t +4./3 ; break; case 3: result = (((( 15*t -37.5 )*t +70./3)*t +3 )*t +0 )*t -2.5 ; break; case 4: result = ((((-7.5*t +18.75)*t -35./3)*t -2 )*t +1 )*t +4./3 ; break; case 5: result = (((( 1.5*t - 3.75)*t + 7./3)*t +0.5)*t -0.5)*t -1./12; break; default: result = 0; } return result; } float d2_c2_4ef(float a, float t) { return d2_c1_4ef(-38, t); } float d2_c3_1ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (((-20*t + 70)*t - 84)*t + 35)*t*t*t*t; break; case 1: result = ((( 60*t -210)*t +252)*t -105)*t*t*t*t +1; break; case 2: result = (((-60*t +210)*t -252)*t +105)*t*t*t*t -2; break; case 3: result = ((( 20*t - 70)*t + 84)*t - 35)*t*t*t*t +1; break; default: result = 0; } return result; } float d2_c3_2ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (((-0.1*t +0.25)*t*t + 0)*t +0 )*t; break; case 1: result = ((( 0.5*t -1.25)*t*t +0.5)*t +0.5)*t +3./20; break; case 2: result = (((-1 *t +2.5 )*t*t -2 )*t -1 )*t +2./5; break; case 3: result = ((( 1 *t -2.5 )*t*t +3 )*t +0 )*t -11./10; break; case 4: result = (((-0.5*t +1.25)*t*t -2 )*t +1 )*t +2./5; break; case 5: result = ((( 0.1*t -0.25)*t*t +0.5)*t -0.5)*t +3./20; break; default: result = 0; } return result; } float d2_c3_3ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = ((((( 14./3*t - 49./3)*t + 39./2)*t - 95./12)*t*t +0 )*t +0 )*t; break; case 1: result = (((((- 70./3*t +245./3)*t -195./2)*t +475./12)*t*t +0.5)*t +0.5)*t -1./12; break; case 2: result = ((((( 140./3*t -490./3)*t +195 )*t -475./6 )*t*t -2 )*t -1 )*t +4./3; break; case 3: result = (((((-140./3*t +490./3)*t -195 )*t +475./6 )*t*t +3 )*t +0 )*t -5./2; break; case 4: result = ((((( 70./3*t -245./3)*t +195./2)*t -475./12)*t*t -2 )*t +1 )*t +4./3; break; case 5: result = (((((- 14./3*t + 49./3)*t - 39./2)*t + 95./12)*t*t +0.5)*t -0.5)*t -1./12; break; default: result = 0; } return result; } float d2_c3_4ef(float a, float t) { float result; int i; i = (t<0) ? (int)t-1:(int)t; t = t - i; switch (i) { case 0: result = (((( 1./24*t - 1./12)*t +0 )*t +0 )*t +0 )*t; break; case 1: result = ((((- 7./24*t + 5./8 )*t +1./12)*t -1./12)*t - 1./8)*t - 1./24; break; case 2: result = (((( 7./8 *t - 2 )*t -1./3 )*t +1 )*t +1 )*t + 1./6; break; case 3: result = ((((-35./24*t +85./24)*t +5./12)*t -13./4)*t -13./8)*t +17./24; break; case 4: result = (((( 35./24*t -15./4 )*t +0 )*t +14./3)*t +0 )*t - 5./3; break; case 5: result = ((((- 7./8 *t +19./8 )*t -5./12)*t -13./4)*t +13./8)*t +17./24; break; case 6: result = (((( 7./24*t - 5./6 )*t +1./3 )*t +1 )*t -1 )*t + 1./6; break; case 7: result = ((((- 1./24*t + 1./8 )*t -1./12)*t -1./12)*t + 1./8)*t - 1./24; break; default: result = 0; } return result; } teem-1.11.0~svn6057/src/nrrd/tmf/fix1.pl0000775000175000017500000000351112165627520017344 0ustar domibeldomibel#!/usr/bin/perl # # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # $emit = 1; while (<>) { # nix carraige returns s/ //g; # nix pragmas s/\#pragma warning.+$//g; if (/w[pm][0-9][a-z] = /) { chomp; # nix newline s/ //g; # nix spaces s/;$//g; # nix last semicolon foreach $pair (split /;/) { ($var, $val) = split /=/, $pair; $$var = $val; } next; } # nix the minimal function body before and after the switch lines s/ float result;//g; s/ int i;//g; s/ i = \(t\<0\) \? \(int\)t-1:\(int\)t;//g; s/ t = t - i;//g; s/ switch \(i\) \{//g; s/ \}//g; s/ return result;//g; # print lines that aren't all whitespace s/^ +$//g; if (!m/^$/) { print; } } teem-1.11.0~svn6057/src/nrrd/hestNrrd.c0000664000175000017500000001641312165631065017310 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ---------------------------- Nrrd ----------------------------- */ /* ** _nrrdHestNrrdParse() ** ** Converts a filename into a nrrd for the sake of hest. ** There is no HestMaybeNrrdParse because this already does that: ** when we get an empty string, we give back a NULL pointer, and ** that is just fine */ int _nrrdHestNrrdParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[] = "_nrrdHestNrrdParse", *nerr; Nrrd **nrrdP; airArray *mop; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } nrrdP = (Nrrd **)ptr; if (airStrlen(str)) { mop = airMopNew(); *nrrdP = nrrdNew(); airMopAdd(mop, *nrrdP, (airMopper)nrrdNuke, airMopOnError); if (nrrdLoad(*nrrdP, str, NULL)) { airMopAdd(mop, nerr = biffGetDone(NRRD), airFree, airMopOnError); airStrcpy(err, AIR_STRLEN_HUGE, nerr); airMopError(mop); return (strstr(err, "EOF") ? 2 : 1); } airMopOkay(mop); } else { /* they gave us an empty string, we give back no nrrd, but its not an error condition */ *nrrdP = NULL; } return 0; } hestCB _nrrdHestNrrd = { sizeof(Nrrd *), "nrrd", _nrrdHestNrrdParse, (airMopper)nrrdNuke }; hestCB * nrrdHestNrrd = &_nrrdHestNrrd; /* ------------------------ NrrdKernelSpec -------------------------- */ int _nrrdHestKernelSpecParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { NrrdKernelSpec **ksP; char me[]="_nrrdHestKernelSpecParse", *nerr; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } ksP = (NrrdKernelSpec **)ptr; *ksP = nrrdKernelSpecNew(); if (nrrdKernelParse(&((*ksP)->kernel), (*ksP)->parm, str)) { nerr = biffGetDone(NRRD); airStrcpy(err, AIR_STRLEN_HUGE, nerr); free(nerr); return 1; } return 0; } hestCB _nrrdHestKernelSpec = { sizeof(NrrdKernelSpec*), "kernel specification", _nrrdHestKernelSpecParse, (airMopper)nrrdKernelSpecNix }; hestCB * nrrdHestKernelSpec = &_nrrdHestKernelSpec; /* --------------------------- NrrdIter ----------------------------- */ int _nrrdLooksLikeANumber(char *str) { /* 0: -+ (no restriction, but that's a little daft) 1: 0123456789 n > 0 2: . 0 <= n <= 1 3: eE 0 <= n <= 1 4: everything else 0 == n */ int count[5]; count[0] = count[1] = count[2] = count[3] = count[4] = 0; while (*str) { int lwc, cc = *str; lwc = tolower(cc); switch (lwc) { case '-': case '+': count[0]++; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': count[1]++; break; case '.': count[2]++; break; case 'e': count[3]++; break; default: count[4]++; break; } str++; } if (count[1] > 0 && AIR_IN_CL(0, count[2], 1) && AIR_IN_CL(0, count[3], 1) && count[4] == 0) { return AIR_TRUE; } else { return AIR_FALSE; } } int _nrrdHestIterParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { char me[]="_nrrdHestIterParse", *nerr; Nrrd *nrrd; NrrdIter **iterP; airArray *mop; double val; int ret; if (!(ptr && str)) { sprintf(err, "%s: got NULL pointer", me); return 1; } iterP = (NrrdIter **)ptr; mop = airMopNew(); *iterP = nrrdIterNew(); airMopAdd(mop, *iterP, (airMopper)nrrdIterNix, airMopOnError); /* the challenge here is determining if a given string represents a filename or a number. Obviously there are cases where it could be both, so we'll assume its a filename first. Because: there are different ways of writing the same number, such as "3" --> "+3", "3.1" --> "3.10", so someone accidently using the file when they mean to use the number has easy ways of changing the number representation, since these trivial transformations will probably not all result in valid filenames. Another problem is that one really wants a general robust test to see if a given string is a valid number representation AND NOTHING BUT THAT, and sscanf() is not that test. In any case, if there are to be improved smarts about this matter, they need to be implemented below and nowhere else. */ nrrd = nrrdNew(); ret = nrrdLoad(nrrd, str, NULL); if (!ret) { /* first attempt at nrrdLoad() was SUCCESSFUL */ nrrdIterSetOwnNrrd(*iterP, nrrd); } else { /* so it didn't load as a nrrd- if its because fopen() failed, then we'll try it as a number. If its for another reason, then we complain */ nrrdNuke(nrrd); if (2 != ret) { /* it failed because of something besides the fopen(), so complain */ nerr = biffGetDone(NRRD); airStrcpy(err, AIR_STRLEN_HUGE, nerr); airMopError(mop); return 1; } else { /* fopen() failed, so it probably wasn't meant to be a filename */ free(biffGetDone(NRRD)); ret = airSingleSscanf(str, "%lf", &val); if (_nrrdLooksLikeANumber(str) || (1 == ret && (!AIR_EXISTS(val) || AIR_ABS(AIR_PI - val) < 0.0001))) { /* either it patently looks like a number, or, it already parsed as a number and it is a special value */ if (1 == ret) { nrrdIterSetValue(*iterP, val); } else { /* oh, this is bad. */ fprintf(stderr, "%s: PANIC, is it a number or not?", me); exit(1); } } else { /* it doesn't look like a number, but the fopen failed, so we'll let it fail again and pass back the error messages */ if (nrrdLoad(nrrd = nrrdNew(), str, NULL)) { nerr = biffGetDone(NRRD); airStrcpy(err, AIR_STRLEN_HUGE, nerr); airMopError(mop); return 1; } else { /* what the hell? */ fprintf(stderr, "%s: PANIC, is it a nrrd or not?", me); exit(1); } } } } airMopAdd(mop, iterP, (airMopper)airSetNull, airMopOnError); airMopOkay(mop); return 0; } hestCB _nrrdHestIter = { sizeof(NrrdIter *), "nrrd/value", _nrrdHestIterParse, (airMopper)nrrdIterNix }; hestCB * nrrdHestIter = &_nrrdHestIter; teem-1.11.0~svn6057/src/nrrd/newformat.txt0000664000175000017500000000072607574130504020116 0ustar domibeldomibelto add VTK format to nrrd: nrrdEnums.h: added entries to nrrdFormat* and nrrdMagic* enums nrrdDefines.h: added #define NRRD_EXT_VTK ".vtk" arraysNrrd.c: augmented nrrdFormatIsAvailable[], nrrdFormatIsImage[], and _nrrdFormatUsesDIO[] enumsNrrd.c: augmented _nrrdFormatStr[], _nrrdFormatStrEqv[], _nrrdFormatValEqv[], and _nrrdMagicStr[] simple.c: augmented nrrdFitsInFormat() read.c/write.c: wrote reader and writer (also augmented usage info in unu save) teem-1.11.0~svn6057/src/nrrd/map.c0000664000175000017500000006761412173672004016302 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* ** RETIRED: nrrdMinMaxSet() ** ** Sets nrrd->min and nrrd->max to the extremal (existent) values in ** the given nrrd, by calling the appropriate member of nrrdMinMaxExactFind[] ** ** calling this function will result in nrrd->hasNonExist being set ** (because of the nrrdFindMinMax[] functions) ** ** decided NOT to use biff, so that this is a more distinct alternative to ** nrrdMinMaxCleverSet(). void nrrdMinMaxSet(Nrrd *nrrd) { NRRD_TYPE_BIGGEST _min, _max; if (nrrd) { if (!airEnumValCheck(nrrdType, nrrd->type) && nrrdTypeBlock != nrrd->type) { nrrdFindMinMax[nrrd->type](&_min, &_max, nrrd); nrrd->min = nrrdDLoad[nrrd->type](&_min); nrrd->max = nrrdDLoad[nrrd->type](&_max); } else { nrrd->min = nrrd->max = AIR_NAN; } } return; } */ /* ** RETIRED: nrrdMinMaxCleverSet() ** ** basically a wrapper around nrrdMinMaxSet(), with bells + whistles: ** 1) will call nrrdMinMaxSet only when one of nrrd->min and nrrd->max ** are non-existent, with the end result that only the non-existent ** values are over-written ** 2) obeys the nrrdStateClever8BitMinMax global state to short-cut ** finding min and max for 8-bit data. Values for nrrd->min or ** nrrd->max which were existent to start with are untouched. ** 3) reports error if there are no existent values in nrrd (AIR_EXISTS() ** fails on every value) ** ** Like nrrdMinMaxSet(), this will always set nrrd->hasNonExist. ** ** Uses biff. int nrrdMinMaxCleverSet(Nrrd *nrrd) { static const char me[]="nrrdMinMaxCleverSet"; double min, max; if (!nrrd) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(nrrdType, nrrd->type)) { biffAddf(NRRD, "%s: input nrrd has invalid type (%d)", me, nrrd->type); return 1; } if (nrrdTypeBlock == nrrd->type) { sprintf(err, "%s: can't find min/max of type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); } if (AIR_EXISTS(nrrd->min) && AIR_EXISTS(nrrd->max)) { / * both of min and max already set, so we won't look for those, but we have to comply with stated behavior of always setting hasNonExist * / nrrdHasNonExistSet(nrrd); return 0; } if (nrrdStateClever8BitMinMax && (nrrdTypeChar == nrrd->type || nrrdTypeUChar == nrrd->type)) { if (nrrdTypeChar == nrrd->type) { if (!AIR_EXISTS(nrrd->min)) nrrd->min = SCHAR_MIN; if (!AIR_EXISTS(nrrd->max)) nrrd->max = SCHAR_MAX; } else { if (!AIR_EXISTS(nrrd->min)) nrrd->min = 0; if (!AIR_EXISTS(nrrd->max)) nrrd->max = UCHAR_MAX; } nrrdHasNonExistSet(nrrd); return 0; } / * at this point we need to find either min and/or max (at least one of them was non-existent on the way in) * / / * save incoming values in case they exist * / min = nrrd->min; max = nrrd->max; / * this will set nrrd->min, nrrd->max, and hasNonExist * / nrrdMinMaxSet(nrrd); if (!( AIR_EXISTS(nrrd->min) && AIR_EXISTS(nrrd->max) )) { biffAddf(NRRD, "%s: no existent values!", me); return 1; } / * re-enstate the existent incoming min and/or max values * / if (AIR_EXISTS(min)) nrrd->min = min; if (AIR_EXISTS(max)) nrrd->max = max; return 0; } */ static int clampRoundConvert(Nrrd *nout, const Nrrd *nin, int type, int doClamp, int roundDir) { static const char me[]="clampRoundConvert"; char typeS[AIR_STRLEN_SMALL]; size_t num, size[NRRD_DIM_MAX]; if (!( nin && nout && !nrrdCheck(nin) && !airEnumValCheck(nrrdType, type) )) { biffAddf(NRRD, "%s: invalid args", me); return 1; } if (nin->type == nrrdTypeBlock || type == nrrdTypeBlock) { biffAddf(NRRD, "%s: can't convert to or from nrrd type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (nout == nin && nrrdTypeSize[type] != nrrdTypeSize[nin->type]) { biffAddf(NRRD, "%s: nout==nin but input,output type sizes unequal", me); return 1; } if (nrrdStateDisallowIntegerNonExist && !nrrdTypeIsIntegral[nin->type] && nrrdTypeIsIntegral[type]) { /* there's a risk of non-existent values getting converted to non-sensical integral values */ if (nrrdHasNonExist(nin)) { biffAddf(NRRD, "%s: can't convert to integral values (%s) with " "non-existent values in input", me, airEnumStr(nrrdType, type)); return 1; } } /* if we're actually converting to the same type, just do a copy */ if (type == nin->type) { if (nout == nin) { /* nout == nin is allowed if the input and output type are of the same size, which will certainly be the case if the input and output types are identical, so there's actually no work to do */ } else { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: couldn't copy input to output", me); return 1; } } } else { /* allocate space if necessary */ nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); /* MUST be nrrdMaybeAlloc_nva (not nrrd Alloc_nva) because we allow nout==nin if type sizes match */ if (nrrdMaybeAlloc_nva(nout, type, nin->dim, size)) { biffAddf(NRRD, "%s: failed to allocate output", me); return 1; } /* call the appropriate converter */ num = nrrdElementNumber(nin); if (roundDir) { _nrrdCastClampRound[nout->type][nin->type](nout->data, nin->data, num, doClamp, roundDir); } else if (doClamp) { _nrrdClampConv[nout->type][nin->type](nout->data, nin->data, num); } else { _nrrdConv[nout->type][nin->type](nout->data, nin->data, num); } nout->blockSize = 0; /* copy peripheral information */ nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE); sprintf(typeS, "(%s)", airEnumStr(nrrdType, nout->type)); if (nrrdContentSet_va(nout, typeS, nin, "")) { biffAddf(NRRD, "%s:", me); return 1; } /* the min and max have probably changed if there was a conversion to integral values, or to a lower precision representation */ if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } } return 0; } /* ******** nrrdConvert() ** ** copies values from one type of nrrd to another, without any ** transformation, except what you get with a cast. The point is to ** make available on Nrrds the exact same behavior as you have in C ** with casts and assignments. */ int nrrdConvert(Nrrd *nout, const Nrrd *nin, int type) { static const char me[]="nrrdConvert"; if (clampRoundConvert(nout, nin, type, AIR_FALSE /* clamp */, 0 /* round */)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ******** nrrdClampConvert() ** ** same as nrrdConvert, but with clamping to output value representation range ** so as to avoid wrap-around artifacts ** ** HEY: WARNING: may have loss of data when processing long long ** (either signed or unsigned) */ int nrrdClampConvert(Nrrd *nout, const Nrrd *nin, int type) { static const char me[]="nrrdClampConvert"; if (clampRoundConvert(nout, nin, type, AIR_TRUE /* clamp */, 0 /* round */)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ******** nrrdCastClampRound() ** ** Named in a way that will likely be regretted, this is for changing ** the type, in a controllable way (with doClamp and or doRound). ** And same as above: ** HEY: WARNING: may have loss of data when processing long long ** (either signed or unsigned) */ int nrrdCastClampRound(Nrrd *nout, const Nrrd *nin, int type, int doClamp, int roundDir) { static const char me[]="nrrdCastClampRound"; if (clampRoundConvert(nout, nin, type, doClamp, roundDir)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ******** nrrdQuantize() ** ** convert values to 8, 16, or 32 bit unsigned quantities ** by mapping the value range delimited by the nrrd's min ** and max to the representable range ** ** NOTE: for the time being, this uses a "double" as the intermediate ** value holder, which may mean needless loss of precision */ int nrrdQuantize(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range, unsigned int bits) { static const char me[]="nrrdQuantize", func[]="quantize"; double valIn, minIn, maxIn, eps; int type=nrrdTypeUnknown; size_t I, num, size[NRRD_DIM_MAX]; unsigned char *outUC; unsigned short *outUS; unsigned int *outUI; airArray *mop; NrrdRange *range; if (!(nin && nout)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(NRRD, "%s: can't quantize type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } /* determine nrrd type from number of bits */ switch (bits) { case 8: type = nrrdTypeUChar; break; case 16: type = nrrdTypeUShort; break; case 32: type = nrrdTypeUInt; break; default: biffAddf(NRRD, "%s: bits has to be 8, 16, or 32 (not %d)", me, bits); return 1; } if (nout == nin && nrrdTypeSize[type] != nrrdTypeSize[nin->type]) { biffAddf(NRRD, "%s: nout==nin but input,output type sizes unequal", me); return 1; } mop = airMopNew(); if (_range) { range = nrrdRangeCopy(_range); nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); } else { range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); } airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); if (nrrdStateDisallowIntegerNonExist && range->hasNonExist) { biffAddf(NRRD, "%s: can't quantize non-existent values " "(NaN, +/-inf)", me); airMopError(mop); return 1; } /* allocate space if necessary */ nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); /* MUST be nrrdMaybeAlloc_nva (not nrrd Alloc_nva) because we allow nout==nin if type sizes match */ if (nrrdMaybeAlloc_nva(nout, type, nin->dim, size)) { biffAddf(NRRD, "%s: failed to create output", me); airMopError(mop); return 1; } /* the skinny */ num = nrrdElementNumber(nin); minIn = range->min; maxIn = range->max; eps = (minIn == maxIn ? 1.0 : 0.0); outUC = (unsigned char*)nout->data; outUS = (unsigned short*)nout->data; outUI = (unsigned int*)nout->data; switch(bits) { case 8: for (I=0; Itype](nin->data, I); valIn = AIR_CLAMP(minIn, valIn, maxIn); outUC[I] = airIndex(minIn, valIn, maxIn+eps, 1 << 8); } break; case 16: for (I=0; Itype](nin->data, I); valIn = AIR_CLAMP(minIn, valIn, maxIn); outUS[I] = airIndex(minIn, valIn, maxIn+eps, 1 << 16); } break; case 32: for (I=0; Itype](nin->data, I); valIn = AIR_CLAMP(minIn, valIn, maxIn); outUI[I] = AIR_CAST(unsigned int, airIndexULL(minIn, valIn, maxIn+eps, AIR_ULLONG(1) << 32)); } break; } /* set information in new volume */ if (nout != nin) { nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE); } if (nrrdContentSet_va(nout, func, nin, "%d", bits)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_OLDMIN_BIT | NRRD_BASIC_INFO_OLDMAX_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } nout->oldMin = minIn; nout->oldMax = maxIn; nout->blockSize = 0; airMopOkay(mop); return 0; } /* ** _nrrdTypeNumberOfValues[] ** ** only meaningful for integral values, and only correct for ** 32-bit values; tells the number of different integral values that ** can be represented by the type ** ** HEY: this used to be public, but that was stopped when it was clear ** that only the following function was using the array. The reason ** for the array is questionable, and the implementation below ** should be re-evaluated. */ static const double _nrrdTypeNumberOfValues[NRRD_TYPE_MAX+1] = { 0, /* unknown */ UCHAR_MAX+1, /* char */ UCHAR_MAX+1, /* unsigned char */ USHRT_MAX+1, /* short */ USHRT_MAX+1, /* unsigned short */ (double)UINT_MAX+1, /* int */ (double)UINT_MAX+1, /* unsigned int */ (double)NRRD_ULLONG_MAX+1, /* long long */ (double)NRRD_ULLONG_MAX+1, /* unsigned long long */ 0, /* float */ 0, /* double */ 0 /* punt */ }; /* ******** nrrdUnquantize() ** ** try to recover floating point values from a quantized nrrd, ** using the oldMin and oldMax values, if they exist. If they ** don't exist, the output range will be 0.0 to 1.0. However, ** because we're using NRRD_CELL_POS to recover values, ** the output values will never be exactly 0.0 to 1.0 (or oldMin ** to oldMax). In unsigned char data, for instance, the value ** V will be mapped to: ** NRRD_CELL_POS(0.0, 1.0, 256, V) == ** AIR_AFFINE(0, V + 0.5, 256, 0.0, 1.0) == ** ((double)(1.0)-(0.0))*((double)(V+0.5)-(0))/((double)(256)-(0)) + (0.0) == ** (1.0)*(V+0.5) / (256.0) + (0.0) == ** (V+0.5)/256 ** so a 0 will be mapped to 1/512 = 0.00195 */ int nrrdUnquantize(Nrrd *nout, const Nrrd *nin, int type) { static const char me[]="nrrdUnquantize", func[]="unquantize"; float *outF; double *outD, minIn, numValIn, minOut, maxOut, valIn; size_t NN, II, size[NRRD_DIM_MAX]; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(nrrdType, type)) { biffAddf(NRRD, "%s: don't recognize type %d\n", me, type); return 1; } if (!( type == nrrdTypeFloat || type == nrrdTypeDouble )) { biffAddf(NRRD, "%s: output type must be %s or %s (not %s)", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeDouble), airEnumStr(nrrdType, type)); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(NRRD, "%s: can't unquantize type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!nrrdTypeIsIntegral[nin->type]) { biffAddf(NRRD, "%s: can only unquantize integral types, not %s", me, airEnumStr(nrrdType, nin->type)); return 1; } if (nout == nin && nrrdTypeSize[type] != nrrdTypeSize[nin->type]) { biffAddf(NRRD, "%s: nout==nin but input,output type sizes unequal", me); return 1; } nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); if (nrrdMaybeAlloc_nva(nout, type, nin->dim, size)) { biffAddf(NRRD, "%s: failed to create output", me); return 1; } minIn = nrrdTypeMin[nin->type]; numValIn = _nrrdTypeNumberOfValues[nin->type]; if (AIR_EXISTS(nin->oldMin) && AIR_EXISTS(nin->oldMax)) { minOut = nin->oldMin; maxOut = nin->oldMax; } else { minOut = 0.0; maxOut = 1.0; } outF = (float*)nout->data; outD = (double*)nout->data; NN = nrrdElementNumber(nin); switch(type) { case nrrdTypeFloat: for (II=0; IItype](nin->data, II); outF[II] = AIR_CAST(float, NRRD_CELL_POS(minOut, maxOut, numValIn, valIn)); } break; case nrrdTypeDouble: for (II=0; IItype](nin->data, II); outD[II] = NRRD_CELL_POS(minOut, maxOut, numValIn, valIn); } break; } /* set information in new volume */ if (nout != nin) { nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE); } if (nrrdContentSet_va(nout, func, nin, "")) { biffAddf(NRRD, "%s:", me); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_OLDMIN_BIT | NRRD_BASIC_INFO_OLDMAX_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } nout->oldMin = nout->oldMax = AIR_NAN; nout->blockSize = 0; return 0; } /* ** _nrrdHistoEqCompare() ** ** used by nrrdHistoEq in smart mode to sort the "steady" array ** in _descending_ order */ int _nrrdHistoEqCompare(const void *a, const void *b) { return *((const unsigned int*)b) - *((const unsigned int*)a); } /* ******** nrrdHistoEq() ** ** performs histogram equalization on given nrrd, treating it as a ** big one-dimensional array. The procedure is as follows: ** - create a histogram of nrrd (using "bins" bins) ** - integrate the histogram, and normalize and shift this so it is ** a monotonically increasing function from min to max, where ** (min,max) is the range of values in the nrrd ** - map the values in the nrrd through the adjusted histogram integral ** ** If the histogram of the given nrrd is already as flat as can be, ** the histogram integral will increase linearly, and the adjusted ** histogram integral should be close to the identity function, so ** the values shouldn't change much. ** ** If the nhistP arg is non-NULL, then it is set to point to ** the histogram that was used for calculation. Otherwise this ** histogram is deleted on return. ** ** This is all that is done normally, when "smart" is == 0. In ** "smart" mode (activated by setting "smart" to something greater ** than 0), the histogram is analyzed during its creation to detect if ** there are a few bins which keep getting hit with the same value ** over and over. It may be desirable to ignore these bins in the ** histogram integral because they may not contain any useful ** information, and so they should not effect how values are ** re-mapped. The value of "smart" is the number of bins that will be ** ignored. For instance, use the value 1 if the problem with naive ** histogram equalization is a large amount of background (which is ** exactly one fixed value). */ int nrrdHistoEq(Nrrd *nout, const Nrrd *nin, Nrrd **nmapP, unsigned int bins, unsigned int smart, float amount) { static const char me[]="nrrdHistoEq", func[]="heq"; Nrrd *nhist, *nmap; double val, min, max, *last = NULL, *ycoord = NULL; int *respect = NULL, lort; unsigned int *hist, *steady = NULL, idx, hirt; size_t I, num; airArray *mop; NrrdRange *range; unsigned bii; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(NRRD, "%s: can't histogram equalize type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!(bins > 4)) { biffAddf(NRRD, "%s: need # bins > 4 (not %d)", me, bins); return 1; } /* we start by simply copying, because the only thing we're changing is the values themselves, and all peripheral information is unchanged by this value remapping */ if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s:", me); return 1; } } mop = airMopNew(); if (nmapP) { airMopAdd(mop, nmapP, (airMopper)airSetNull, airMopOnError); } num = nrrdElementNumber(nin); if (smart <= 0) { nhist = nrrdNew(); if (nrrdHisto(nhist, nin, NULL, NULL, bins, nrrdTypeUInt)) { biffAddf(NRRD, "%s: failed to create histogram", me); airMopError(mop); return 1; } airMopAdd(mop, nhist, (airMopper)nrrdNuke, airMopAlways); hist = (unsigned int*)nhist->data; min = nhist->axis[0].min; max = nhist->axis[0].max; } else { /* for "smart" mode, we have to some extra work while creating the histogram to look for bins incessantly hit with the exact same value */ if (nrrdMaybeAlloc_va(nhist=nrrdNew(), nrrdTypeUInt, 1, AIR_CAST(size_t, bins))) { biffAddf(NRRD, "%s: failed to allocate histogram", me); airMopError(mop); return 1; } airMopAdd(mop, nhist, (airMopper)nrrdNuke, airMopAlways); hist = (unsigned int*)nhist->data; nhist->axis[0].size = bins; /* allocate the respect, steady, and last arrays */ respect = (int*)calloc(bins, sizeof(int)); steady = (unsigned int*)calloc(2*bins, sizeof(unsigned int)); last = (double*)calloc(bins, sizeof(double)); airMopMem(mop, &respect, airMopAlways); airMopMem(mop, &steady, airMopAlways); airMopMem(mop, &last, airMopAlways); if (!(respect && steady && last)) { biffAddf(NRRD, "%s: couldn't allocate smart arrays", me); airMopError(mop); return 1; } /* steady[0 + 2*bii] == how many times has bin bii seen the same value steady[1 + 2*bii] == bii (steady will be rearranged by qsort()) */ for (bii=0; biimin == range->max) { biffAddf(NRRD, "%s: invalid min and max in nrrd. " "Min and max are equivalent (min,max = %g).", me, range->min); airMopError(mop); return 1; } min = range->min; max = range->max; for (I=0; Itype](nin->data, I); if (AIR_EXISTS(val)) { idx = airIndex(min, val, max, bins); ++hist[idx]; if (AIR_EXISTS(last[idx])) { steady[0 + 2*idx] = (last[idx] == val ? 1 + steady[0 + 2*idx] : 0); } last[idx] = val; } } /* now sort the steady array */ qsort(steady, bins, 2*sizeof(unsigned int), _nrrdHistoEqCompare); /* we ignore some of the bins according to "smart" arg */ for (bii=0; biidata); nmap->axis[0].min = min; nmap->axis[0].max = max; /* integrate the histogram then normalize it */ for (bii=0; bii<=bins; bii++) { if (bii == 0) { ycoord[bii] = 0; } else { ycoord[bii] = ycoord[bii-1] + hist[bii-1]*(smart ? respect[bii-1] : 1); } } /* if we've done smart, the integral will have little flat spots where we ignored hits in the histogram. That means the mapping will actually be lower at that value than it should be. In truth, we should be using an irregular mapping for this, and the control points at the ignored bins should just be missing. So we have to do this silliness to raise those control points in the regular map. */ if (smart) { /* there are bins+1 control points, with indices 0 to bins. We'll fix control points 1 to bins-1. ycoord[bii] is too low if hist[bii-1] was not respected (!respect[bii-1]) */ for (bii=1; bii<=bins-1; bii++) { if (!respect[bii-1]) { /* lort and hirt will bracket the index of the bad control point with points corresponding either to respected bins or the endpoints of the histogram */ for (lort=bii; lort>=1 && !respect[lort-1]; lort--) ; for (hirt=bii; hirttype, AIR_FALSE)) { biffAddf(NRRD, "%s: problem remapping", me); airMopError(mop); return 1; } /* for (I=0; Itype](nin->data, I); if (AIR_EXISTS(val)) { AIR_INDEX(min, val, max, bins, idx); val = AIR_AFFINE(xcoord[idx], val, xcoord[idx+1], ycoord[idx], ycoord[idx+1]); } nrrdDInsert[nout->type](nout->data, I, val); } */ /* if user is interested, set pointer to map nrrd, otherwise it will be nixed by airMop */ if (nmapP) { *nmapP = nmap; } /* fiddling with content is the only thing we'll do */ if (nrrdContentSet_va(nout, func, nin, "%d,%d", bins, smart)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/nrrd/write.c0000664000175000017500000010253312165631065016650 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "nrrd.h" #include "privateNrrd.h" /* #include #include */ int nrrdIoStateSet(NrrdIoState *nio, int parm, int value) { static const char me[]="nrrdIoStateSet"; if (!nio) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( AIR_IN_OP(nrrdIoStateUnknown, parm, nrrdIoStateLast) )) { biffAddf(NRRD, "%s: identifier %d not in valid range [%d,%d]", me, parm, nrrdIoStateUnknown+1, nrrdIoStateLast-1); return 1; } switch (parm) { case nrrdIoStateDetachedHeader: nio->detachedHeader = !!value; break; case nrrdIoStateBareText: nio->bareText = !!value; break; case nrrdIoStateCharsPerLine: if (value < 40) { biffAddf(NRRD, "%s: %d charsPerLine is awfully small", me, value); return 1; } /* cast won't lose info because "value" must be positive */ nio->charsPerLine = AIR_CAST(unsigned int, value); break; case nrrdIoStateValsPerLine: if (value < 4) { biffAddf(NRRD, "%s: %d valsPerLine is awfully small", me, value); return 1; } /* cast won't lose info because "value" must be positive */ nio->valsPerLine = AIR_CAST(unsigned int, value); break; case nrrdIoStateSkipData: nio->skipData = !!value; break; case nrrdIoStateKeepNrrdDataFileOpen: nio->keepNrrdDataFileOpen = !!value; break; case nrrdIoStateZlibLevel: if (!( AIR_IN_CL(-1, value, 9) )) { biffAddf(NRRD, "%s: zlibLevel %d invalid", me, value); return 1; } nio->zlibLevel = value; break; case nrrdIoStateZlibStrategy: if (!( AIR_IN_OP(nrrdZlibStrategyUnknown, value, nrrdZlibStrategyLast) )) { biffAddf(NRRD, "%s: zlibStrategy %d invalid", me, value); return 1; } nio->zlibStrategy = value; break; case nrrdIoStateBzip2BlockSize: if (!( AIR_IN_CL(-1, value, 9) )) { biffAddf(NRRD, "%s: bzip2BlockSize %d invalid", me, value); return 1; } nio->bzip2BlockSize = value; break; default: fprintf(stderr, "!%s: PANIC: didn't recognize parm %d\n", me, parm); return 1; } return 0; } int nrrdIoStateEncodingSet(NrrdIoState *nio, const NrrdEncoding *encoding) { static const char me[]="nrrdIoStateEncodingSet"; if (!( nio && encoding )) { if (nio) { nio->encoding = nrrdEncodingUnknown; } biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!encoding->available()) { nio->encoding = nrrdEncodingUnknown; biffAddf(NRRD, "%s: %s encoding isn't actually available", me, encoding->name); return 1; } nio->encoding = encoding; return 0; } int nrrdIoStateFormatSet(NrrdIoState *nio, const NrrdFormat *format) { static const char me[]="nrrdIoStateFormatSet"; if (!( nio && format )) { if (nio) { nio->format = nrrdFormatUnknown; } biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!format->available()) { nio->format = nrrdFormatUnknown; biffAddf(NRRD, "%s: %s format isn't actually available", me, format->name); return 1; } nio->format = format; return 0; } /* ** no biff */ int nrrdIoStateGet(NrrdIoState *nio, int parm) { static const char me[]="nrrdIoStateGet"; int value; if (!nio) { /* got NULL pointer */ return -1; } if (!( AIR_IN_OP(nrrdIoStateUnknown, parm, nrrdIoStateLast) )) { /* got bogus parameter identifier */ return -1; } switch (parm) { case nrrdIoStateDetachedHeader: value = !!nio->detachedHeader; break; case nrrdIoStateBareText: value = !!nio->bareText; break; case nrrdIoStateCharsPerLine: /* HEY: this cast is a bad because nio->charsPerLine is unsigned */ value = AIR_CAST(int, nio->charsPerLine); break; case nrrdIoStateValsPerLine: /* HEY: this cast is a bad because nio->valsPerLine is unsigned */ value = AIR_CAST(int, nio->valsPerLine); break; case nrrdIoStateSkipData: value = !!nio->skipData; break; case nrrdIoStateKeepNrrdDataFileOpen: value = !!nio->keepNrrdDataFileOpen; break; case nrrdIoStateZlibLevel: value = nio->zlibLevel; break; case nrrdIoStateZlibStrategy: value = nio->zlibStrategy; break; case nrrdIoStateBzip2BlockSize: value = nio->bzip2BlockSize; break; default: fprintf(stderr, "!%s: PANIC: didn't recognize parm %d\n", me, parm); return -1; } return value; } /* ** no biff */ const NrrdEncoding * nrrdIoStateEncodingGet(NrrdIoState *nio) { return nio ? nio->encoding : nrrdEncodingUnknown; } /* ** no biff */ const NrrdFormat * nrrdIoStateFormatGet(NrrdIoState *nio) { return nio ? nio->format : nrrdFormatUnknown; } void _nrrdStrcatSpaceVector(char *str, unsigned int spaceDim, const double val[NRRD_SPACE_DIM_MAX]) { char buff[AIR_STRLEN_MED]; /* bad Gordon */ unsigned int dd; if (AIR_EXISTS(val[0])) { strcat(str, "("); for (dd=0; dddim, NRRD_DIM_MAX) && nio && nio->encoding && AIR_IN_OP(nrrdField_unknown, field, nrrdField_last) )) { return 0; } ret = 0; switch (field) { case nrrdField_comment: /* comments and key/value pairs are always handled differently (by being printed explicity), so they are never "interesting" */ break; case nrrdField_content: ret = !!(airStrlen(nrrd->content)); break; case nrrdField_number: /* "number" is entirely redundant with "sizes", which is a required field. Absolutely nothing is lost in eliding "number" from the header, so "number" is NEVER interesting. Should this judgement later be found in error, this is the one place where the policy change can be implemented */ break; case nrrdField_type: /* this is vital */ ret = 1; break; case nrrdField_block_size: ret = (nrrdTypeBlock == nrrd->type); break; case nrrdField_dimension: /* this is vital */ ret = 1; break; case nrrdField_space: /* its interesting if its known */ ret = (nrrdSpaceUnknown != nrrd->space); break; case nrrdField_space_dimension: /* its interesting if its non-zero and if space is not known */ ret = (nrrd->spaceDim > 0 && nrrdSpaceUnknown == nrrd->space); break; case nrrdField_sizes: /* this is vital */ ret = 1; break; case nrrdField_spacings: for (ai=0; aidim; ai++) { ret |= AIR_EXISTS(nrrd->axis[ai].spacing); } break; case nrrdField_thicknesses: for (ai=0; aidim; ai++) { ret |= AIR_EXISTS(nrrd->axis[ai].thickness); } break; case nrrdField_axis_mins: for (ai=0; aidim; ai++) { ret |= AIR_EXISTS(nrrd->axis[ai].min); } break; case nrrdField_axis_maxs: for (ai=0; aidim; ai++) { ret |= AIR_EXISTS(nrrd->axis[ai].max); } break; case nrrdField_space_directions: ret = nrrd->spaceDim > 0; break; case nrrdField_centers: for (ai=0; aidim; ai++) { ret |= (nrrdCenterUnknown != nrrd->axis[ai].center); } break; case nrrdField_kinds: for (ai=0; aidim; ai++) { ret |= (nrrdKindUnknown != nrrd->axis[ai].kind); } break; case nrrdField_labels: for (ai=0; aidim; ai++) { ret |= !!(airStrlen(nrrd->axis[ai].label)); } break; case nrrdField_units: for (ai=0; aidim; ai++) { ret |= !!(airStrlen(nrrd->axis[ai].units)); } break; case nrrdField_min: case nrrdField_max: /* these no longer exist in the Nrrd struct; we never write them */ ret = AIR_FALSE; break; case nrrdField_old_min: ret = AIR_EXISTS(nrrd->oldMin); break; case nrrdField_old_max: ret = AIR_EXISTS(nrrd->oldMax); break; case nrrdField_endian: ret = nio->encoding->endianMatters && 1 < nrrdElementSize(nrrd); break; case nrrdField_encoding: /* this is vital */ ret = 1; break; case nrrdField_line_skip: ret = nio->lineSkip > 0; break; case nrrdField_byte_skip: ret = nio->byteSkip != 0; break; case nrrdField_keyvalue: /* comments and key/value pairs are always handled differently (by being printed explicity), so they are never "interesting" */ break; case nrrdField_sample_units: ret = !!airStrlen(nrrd->sampleUnits); break; case nrrdField_space_units: for (ai=0; aispaceDim; ai++) { ret |= !!(airStrlen(nrrd->spaceUnits[ai])); } break; case nrrdField_space_origin: /* we're trusting other validity checks to ensure that all the coeffs exist or not, together */ ret = (nrrd->spaceDim > 0 && AIR_EXISTS(nrrd->spaceOrigin[0])); break; case nrrdField_measurement_frame: /* we're trusting other validity checks to ensure that all the coeffs exist or not, together */ ret = (nrrd->spaceDim > 0 && AIR_EXISTS(nrrd->measurementFrame[0][0])); break; case nrrdField_data_file: /* detached header was either requested or is required */ ret = (nio->detachedHeader || nio->dataFNFormat || nio->dataFNArr->len > 1); break; } return ret; } /* ** _nrrdSprintFieldInfo ** ** this prints ": " into *strP (after allocating it for ** big enough, usually with a stupidly big margin of error), in a form ** suitable to be written to NRRD or other image headers. This will always ** print something (for valid inputs), even stupid s like ** "(unknown endian)". It is up to the caller to decide which fields ** are worth writing, via _nrrdFieldInteresting(). ** ** NOTE: some of these fields make sense in non-NRRD files (e.g. all ** the per-axis information), but many only make sense in NRRD files. ** This is just one example of NRRD-format-specific stuff that is not ** in formatNRRD.c */ void _nrrdSprintFieldInfo(char **strP, const char *prefix, const Nrrd *nrrd, NrrdIoState *nio, int field) { static const char me[]="_nrrdSprintFieldInfo"; char buff[AIR_STRLEN_MED], *fnb, stmp[AIR_STRLEN_SMALL], *strtmp=NULL; double colvec[NRRD_SPACE_DIM_MAX]; const char *fs; unsigned int ii, dd, uintStrlen = 11, size_tStrlen = 33, doubleStrlen = 513; size_t fslen, fdlen, maxl; int endi; if (!( strP && prefix && nrrd && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdField_unknown, field, nrrdField_last) )) { return; } /* As of Sun Dec 2 01:57:48 CST 2012 (revision 5832) the only places where this function is called is when it has been guarded by "if (_nrrdFieldInteresting())" (except for in formatText.c when its called on the dimension field, which is always interesting). So, the following: if (!_nrrdFieldInteresting(nrrd, nio, field)) { *strP = airStrdup(""); } was redundant and confusingly created the appearance of a memory leak waiting to happen. We now let the default switch statement set *strP to NULL (all the other cases set it), to smoke out errors in how this function is called */ fs = airEnumStr(nrrdField, field); fslen = strlen(prefix) + strlen(fs) + strlen(": ") + 1; switch (field) { case nrrdField_comment: case nrrdField_keyvalue: fprintf(stderr, "%s: CONFUSION: why are you calling me on \"%s\"?\n", me, airEnumStr(nrrdField, nrrdField_comment)); *strP = airStrdup(""); break; case nrrdField_content: strtmp = airOneLinify(airStrdup(nrrd->content)); *strP = AIR_CALLOC(fslen + strlen(strtmp), char); sprintf(*strP, "%s%s: %s", prefix, fs, strtmp); airFree(strtmp); strtmp = NULL; break; case nrrdField_number: *strP = AIR_CALLOC(fslen + size_tStrlen, char); sprintf(*strP, "%s%s: %s", prefix, fs, airSprintSize_t(stmp, nrrdElementNumber(nrrd))); break; case nrrdField_type: *strP = AIR_CALLOC(fslen + strlen(airEnumStr(nrrdType, nrrd->type)), char); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdType, nrrd->type)); break; case nrrdField_block_size: *strP = AIR_CALLOC(fslen + size_tStrlen, char); sprintf(*strP, "%s%s: %s", prefix, fs, airSprintSize_t(stmp, nrrd->blockSize)); break; case nrrdField_dimension: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->dim); break; case nrrdField_space: *strP = AIR_CALLOC(fslen + strlen(airEnumStr(nrrdSpace, nrrd->space)), char); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdSpace, nrrd->space)); break; case nrrdField_space_dimension: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->spaceDim); break; /* ---- begin per-axis fields ---- */ case nrrdField_sizes: *strP = AIR_CALLOC(fslen + nrrd->dim*(size_tStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { sprintf(buff, " %s", airSprintSize_t(stmp, nrrd->axis[ii].size)); strcat(*strP, buff); } break; case nrrdField_spacings: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].spacing); strcat(*strP, buff); } break; case nrrdField_thicknesses: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].thickness); strcat(*strP, buff); } break; case nrrdField_axis_mins: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].min); strcat(*strP, buff); } break; case nrrdField_axis_maxs: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].max); strcat(*strP, buff); } break; case nrrdField_space_directions: *strP = AIR_CALLOC(fslen + nrrd->dim*nrrd->spaceDim*(doubleStrlen + strlen("(,) ")), char); sprintf(*strP, "%s%s: ", prefix, fs); for (ii=0; iidim; ii++) { _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, nrrd->axis[ii].spaceDirection); if (ii < nrrd->dim-1) { strcat(*strP, " "); } } break; case nrrdField_centers: fdlen = 0; for (ii=0; iidim; ii++) { fdlen += 1 + airStrlen(nrrd->axis[ii].center ? airEnumStr(nrrdCenter, nrrd->axis[ii].center) : NRRD_UNKNOWN); } *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { sprintf(buff, " %s", (nrrd->axis[ii].center ? airEnumStr(nrrdCenter, nrrd->axis[ii].center) : NRRD_UNKNOWN)); strcat(*strP, buff); } break; case nrrdField_kinds: fdlen = 0; for (ii=0; iidim; ii++) { fdlen += 1 + airStrlen(nrrd->axis[ii].kind ? airEnumStr(nrrdKind, nrrd->axis[ii].kind) : NRRD_UNKNOWN); } *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { sprintf(buff, " %s", (nrrd->axis[ii].kind ? airEnumStr(nrrdKind, nrrd->axis[ii].kind) : NRRD_UNKNOWN)); strcat(*strP, buff); } break; case nrrdField_labels: case nrrdField_units: #define LABEL_OR_UNITS (nrrdField_labels == field \ ? nrrd->axis[ii].label \ : nrrd->axis[ii].units) fdlen = 0; for (ii=0; iidim; ii++) { /* The "2*" is because at worst every character needs escaping. The "+ 3" for the |" "| between each part */ fdlen += 2*airStrlen(LABEL_OR_UNITS) + 3; } fdlen += 1; /* for '\0' */ *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->axis[ii].label)) { _nrrdWriteEscaped(NULL, *strP, LABEL_OR_UNITS, "\"", _NRRD_WHITESPACE_NOTAB); } strcat(*strP, "\""); } #undef LABEL_OR_UNITS break; /* ---- end per-axis fields ---- */ case nrrdField_min: case nrrdField_max: /* we're basically a no-op, now that these fields became meaningless */ *strP = AIR_CALLOC(fslen + doubleStrlen, char); sprintf(*strP, "%s%s: 0.0", prefix, fs); strcat(*strP, buff); break; case nrrdField_old_min: *strP = AIR_CALLOC(fslen + doubleStrlen, char); sprintf(*strP, "%s%s: ", prefix, fs); airSinglePrintf(NULL, buff, "%.17g", nrrd->oldMin); strcat(*strP, buff); break; case nrrdField_old_max: *strP = AIR_CALLOC(fslen + doubleStrlen, char); sprintf(*strP, "%s%s: ", prefix, fs); airSinglePrintf(NULL, buff, "%.17g", nrrd->oldMax); strcat(*strP, buff); break; case nrrdField_endian: if (airEndianUnknown != nio->endian) { /* we know a specific endianness because either it was recorded as part of "unu make -h", or it was set (and data was possibly altered) as part of "unu save" */ endi = nio->endian; } else { /* we record our current architecture's endian because we're going to writing out data */ endi = airMyEndian(); } *strP = AIR_CALLOC(fslen + strlen(airEnumStr(airEndian, endi)), char); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(airEndian, endi)); break; case nrrdField_encoding: *strP = AIR_CALLOC(fslen + strlen(nio->encoding->name), char); sprintf(*strP, "%s%s: %s", prefix, fs, nio->encoding->name); break; case nrrdField_line_skip: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %d", prefix, fs, nio->lineSkip); break; case nrrdField_byte_skip: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %ld", prefix, fs, nio->byteSkip); break; case nrrdField_sample_units: strtmp = airOneLinify(airStrdup(nrrd->sampleUnits)); *strP = AIR_CALLOC(fslen + strlen(strtmp), char); sprintf(*strP, "%s%s: \"%s\"", prefix, fs, strtmp); airFree(strtmp); strtmp = NULL; break; case nrrdField_space_units: fdlen = 0; for (ii=0; iispaceDim; ii++) { /* The "2*" is because at worst every character needs escaping. See note in formatNRRD.c about how even though its not part of the format, we have worst-case scenario of having to escape a space units which is nothing but ". The "+ 3" for the |" "| between each part */ fdlen += 2*airStrlen(nrrd->spaceUnits[ii]) + 3; } fdlen += 1; /* for '\0' */ *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iispaceDim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->spaceUnits[ii])) { _nrrdWriteEscaped(NULL, *strP, nrrd->spaceUnits[ii], "\"", _NRRD_WHITESPACE_NOTAB); } strcat(*strP, "\""); } break; case nrrdField_space_origin: *strP = AIR_CALLOC(fslen + nrrd->spaceDim*(doubleStrlen + strlen("(,) ")), char); sprintf(*strP, "%s%s: ", prefix, fs); _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, nrrd->spaceOrigin); break; case nrrdField_measurement_frame: *strP = AIR_CALLOC(fslen + (nrrd->spaceDim* nrrd->spaceDim*(doubleStrlen + strlen("(,) "))), char); sprintf(*strP, "%s%s: ", prefix, fs); for (dd=0; ddspaceDim; dd++) { for (ii=0; iispaceDim; ii++) { colvec[ii] = nrrd->measurementFrame[dd][ii]; } _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, colvec); if (dd < nrrd->spaceDim-1) { strcat(*strP, " "); } } break; case nrrdField_data_file: /* NOTE: this comes last (nrrdField_data_file is the highest-valued member of the nrrdField* enum) because the "LIST" form of the data file specification requires that the following lines be the filenames */ /* error checking elsewhere: assumes there is data file info */ if (nio->dataFNFormat) { *strP = AIR_CALLOC(fslen + strlen(nio->dataFNFormat) + 4*uintStrlen, char); if (nio->dataFileDim == nrrd->dim-1) { sprintf(*strP, "%s%s: %s %d %d %d", prefix, fs, nio->dataFNFormat, nio->dataFNMin, nio->dataFNMax, nio->dataFNStep); } else { sprintf(*strP, "%s%s: %s %d %d %d %u", prefix, fs, nio->dataFNFormat, nio->dataFNMin, nio->dataFNMax, nio->dataFNStep, nio->dataFileDim); } } else if (nio->dataFNArr->len > 1) { maxl = 0; for (ii=0; iidataFNArr->len; ii++) { maxl = AIR_MAX(maxl, strlen(nio->dataFN[ii])); } *strP = AIR_CALLOC(fslen + strlen(NRRD_LIST_FLAG) + uintStrlen + nio->dataFNArr->len * (maxl + 1), char); fnb = AIR_CALLOC(fslen + strlen(NRRD_LIST_FLAG) + uintStrlen + maxl + 1, char); if (nio->dataFileDim == nrrd->dim-1) { sprintf(*strP, "%s%s: LIST\n", prefix, fs); } else { sprintf(*strP, "%s%s: LIST %u\n", prefix, fs, nio->dataFileDim); } for (ii=0; iidataFNArr->len; ii++) { sprintf(fnb, "%s%s", nio->dataFN[ii], iidataFNArr->len-1 ? "\n" : ""); strcat(*strP, fnb); } free(fnb); } else { /* there is some ambiguity between a "LIST" of length one, and a single explicit data filename, but that's harmless */ *strP = AIR_CALLOC(fslen + strlen("./") + strlen(nio->dataFN[0]) + 1, char); sprintf(*strP, "%s%s: %s%s", prefix, fs, /* this is a favor to older readers that can deal with this NRRD file because its being saved in a NRRD0003 (or below) version, so we don't want to confuse them by not having the old explicit header-relative flag */ (_nrrdFormatNRRD_whichVersion(nrrd, nio) < 4 ? "./" : ""), nio->dataFN[0]); } break; default: fprintf(stderr, "%s: CONFUSION: field %d unrecognized\n", me, field); *strP = NULL; break; } return; } /* ** _nrrdFprintFieldInfo ** ** convenience wrapper around _nrrdSprintFieldInfo, for writing into ** a file. Same caveats here: use _nrrdFieldInteresting */ void _nrrdFprintFieldInfo(FILE *file, const char *prefix, const Nrrd *nrrd, NrrdIoState *nio, int field) { char *line=NULL; _nrrdSprintFieldInfo(&line, prefix, nrrd, nio, field); if (line) { fprintf(file, "%s\n", line); free(line); } return; } int _nrrdEncodingMaybeSet(NrrdIoState *nio) { static const char me[]="_nrrdEncodingMaybeSet"; if (!nio) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!nio->encoding) { biffAddf(NRRD, "%s: invalid (NULL) encoding", me); return 1; } if (nrrdEncodingUnknown == nio->encoding) { nio->encoding = nrrdEncodingArray[nrrdDefaultWriteEncodingType]; } if (!nio->encoding->available()) { biffAddf(NRRD, "%s: %s encoding not available in this Teem build", me, nio->encoding->name); return 1; } return 0; } /* ** we can assume (via action of caller nrrdSave) that nio->encoding ** has been set ** ** we must set nio->format to something useful/non-trivial */ int _nrrdFormatMaybeGuess(const Nrrd *nrrd, NrrdIoState *nio, const char *filename) { static const char me[]="_nrrdFormatMaybeGuess"; char mesg[AIR_STRLEN_MED]; int fi, guessed, available, fits; if (!nio->format) { biffAddf(NRRD, "%s: got invalid (NULL) format", me); return 1; } if (nrrdFormatUnknown == nio->format) { for (fi = nrrdFormatTypeUnknown+1; fi < nrrdFormatTypeLast; fi++) { if (nrrdFormatArray[fi]->nameLooksLike(filename)) { nio->format = nrrdFormatArray[fi]; break; } } if (nrrdFormatUnknown == nio->format) { /* no nameLooksLike() returned non-zero, punt */ nio->format = nrrdFormatNRRD; } guessed = AIR_TRUE; } else { guessed = AIR_FALSE; } available = nio->format->available(); fits = nio->format->fitsInto(nrrd, nio->encoding, AIR_FALSE); /* !available ==> !fits, by the nature of fitsInto() */ if (!( available && fits )) { sprintf(mesg, "can not use %s format: %s", nio->format->name, (!available ? "not available in this Teem build" : "array doesn\'t fit")); if (guessed) { if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: %s --> saving to NRRD format)\n", me, mesg); } nio->format = nrrdFormatNRRD; } else { /* problem: this was the format someone explicitly requested */ biffAddf(NRRD, "%s: %s", me, mesg); return 1; } } return 0; } int _nrrdFormatMaybeSet(NrrdIoState *nio) { static const char me[]="_nrrdFormatMaybeSet"; if (!nio->format) { biffAddf(NRRD, "%s: invalid (NULL) format", me); return 1; } if (nrrdFormatUnknown == nio->format) { nio->format = nrrdFormatNRRD; } if (!nio->format->available()) { biffAddf(NRRD, "%s: %s format not available in this Teem build", me, nio->format->name); return 1; } return 0; } /* ** _nrrdWrite ** ** Write a nrrd to given file or string (allocated by nrrd), using the ** format and and encoding indicated in nio. Cleverness should be ** isolated and collected here: by the time nio->format->write() is ** called, all writing parameters must be given explicitly, and their ** appropriateness is explicitly tested */ int _nrrdWrite(FILE *file, char **stringP, const Nrrd *nrrd, NrrdIoState *_nio) { static const char me[]="_nrrdWrite"; NrrdIoState *nio; airArray *mop; if (!((file || stringP) && nrrd)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (file && stringP) { biffAddf(NRRD, "%s: can't write to both file and string", me); return 1; } if (nrrdCheck(nrrd)) { biffAddf(NRRD, "%s:", me); return 1; } mop = airMopNew(); if (_nio) { nio = _nio; } else { nio = nrrdIoStateNew(); if (!nio) { biffAddf(NRRD, "%s: couldn't alloc local NrrdIoState", me); airMopError(mop); return 1; } airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); } if (_nrrdEncodingMaybeSet(nio) || _nrrdFormatMaybeSet(nio)) { biffAddf(NRRD, "%s: ", me); airMopError(mop); return 1; } if (nio->byteSkip || nio->lineSkip) { /* NOTE: unu make bypasses this by calling nrrdFormatNRRD->write() directly */ biffAddf(NRRD, "%s: can't generate line or byte skips on data write", me); airMopError(mop); return 1; } if (stringP) { if (nrrdFormatNRRD != nio->format) { biffAddf(NRRD, "%s: sorry, can only write %s files to strings (not %s)", me, nrrdFormatNRRD->name, nio->format->name); airMopError(mop); return 1; } /* we do this in two passes; first see how much room is needed for the header, then allocate, then write the header */ nio->learningHeaderStrlen = AIR_TRUE; if (nio->format->write(NULL, nrrd, nio)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } *stringP = AIR_MALLOC(nio->headerStrlen + 1, char); if (!*stringP) { biffAddf(NRRD, "%s: couldn't allocate header string (%u len )", me, nio->headerStrlen); airMopError(mop); return 1; } nio->learningHeaderStrlen = AIR_FALSE; nio->headerStringWrite = *stringP; if (nio->format->write(NULL, nrrd, nio)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } } else { /* call the writer appropriate for the format */ if (nio->format->write(file, nrrd, nio)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } /* ******** nrrdWrite ** ** wrapper around _nrrdWrite; writes to a FILE* */ int nrrdWrite(FILE *file, const Nrrd *nrrd, NrrdIoState *_nio) { static const char me[]="nrrdWrite"; if (_nrrdWrite(file, NULL, nrrd, _nio)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ******** nrrdStringWrite ** ** wrapper around _nrrdWrite; *allocates* and writes to a string */ int nrrdStringWrite(char **stringP, const Nrrd *nrrd, NrrdIoState *_nio) { static const char me[]="nrrdStringWrite"; if (_nrrdWrite(NULL, stringP, nrrd, _nio)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ******** nrrdSave ** ** save a given nrrd to a given filename, with cleverness to guess ** format if not specified by the caller ** ** currently, for NRRD format files, we play the detached header game ** whenever the filename ends in NRRD_EXT_NHDR, and when we play this ** game, the data file is ALWAYS header relative. */ int nrrdSave(const char *filename, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="nrrdSave"; FILE *file; airArray *mop; if (!(nrrd && filename)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); if (!nio) { nio = nrrdIoStateNew(); if (!nio) { biffAddf(NRRD, "%s: couldn't alloc local NrrdIoState", me); return 1; } airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); } if (_nrrdEncodingMaybeSet(nio) || _nrrdFormatMaybeGuess(nrrd, nio, filename)) { biffAddf(NRRD, "%s: ", me); airMopError(mop); return 1; } if (nrrdFormatNRRD == nio->format && airEndsWith(filename, NRRD_EXT_NHDR)) { nio->detachedHeader = AIR_TRUE; _nrrdSplitName(&(nio->path), &(nio->base), filename); /* nix the ".nhdr" suffix */ nio->base[strlen(nio->base) - strlen(NRRD_EXT_NHDR)] = 0; /* nrrdFormatNRRD->write will do the rest */ } else { nio->detachedHeader = AIR_FALSE; } if (!( file = airFopen(filename, stdout, "wb") )) { biffAddf(NRRD, "%s: couldn't fopen(\"%s\",\"wb\"): %s", me, filename, strerror(errno)); airMopError(mop); return 1; } airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); if (nrrdWrite(file, nrrd, nio)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } int nrrdSaveMulti(const char *fnameFormat, const Nrrd *const *nin, unsigned int ninLen, unsigned int numStart, NrrdIoState *nio) { static const char me[]="nrrdSaveMulti"; char *fname; airArray *mop; unsigned int nii; if (!( fnameFormat && nin )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( _nrrdContainsPercentThisAndMore(fnameFormat, 'u') )) { biffAddf(NRRD, "%s: given format \"%s\" doesn't seem to " "have the \"%%u\" conversion specification to sprintf " "an unsigned int\n", me, fnameFormat); return 1; } mop = airMopNew(); /* should be big enough for the number replacing the format sequence */ fname = AIR_CALLOC(strlen(fnameFormat) + 128, char); if (!(fname)) { biffAddf(NRRD, "%s: couldn't allocate local fname buffer", me); airMopError(mop); return 1; } airMopAdd(mop, fname, airFree, airMopAlways); for (nii=0; nii #endif /* Routines for estimating the ball-and-multi-stick model from single-shell * DWI data. Implements the ideas from * * T. Schultz, C.-F. Westin, G. Kindlmann: Multi-Diffusion-Tensor * Fitting via Spherical Deconvolution: A Unifying Framework. MICCAI * 2010, Part I, LNCS 6361, pp. 673-680. Springer, 2010 */ /* elfKernelStick: * * Computes the deconvolution kernel corresponding to a single stick, for * desired ESH order, bd (b value times diffusivity) and b0 (non-DWI signal) * If delta!=0, deconvolves to delta peak, else to rank-1 peak * * returns 0 on success, 1 if order is not supported */ int elfKernelStick_f(float *kernel, unsigned int order, float bd, float b0, int delta) { double ebd=exp(bd); double embd=exp(-bd); double sbd=sqrt(bd); double erfsbd=airErf(sbd); double spi=sqrt(AIR_PI); kernel[0]=AIR_CAST(float, b0*AIR_PI*erfsbd/sbd); if (order>=2) { kernel[1]=AIR_CAST(float, -b0/(4.0*bd*sbd)*embd*sqrt(5*AIR_PI)* (6*sbd+(-3+2*bd)*ebd*spi*erfsbd)); if (order>=4) { kernel[2]=AIR_CAST(float, b0/(32.0*bd*bd*sbd)*embd*spi* (-30*sbd*(21+2*bd)+9*(35+4*bd*(-5+bd))* ebd*spi*erfsbd)); if (order>=6) { /* At order 6, noise starts to take over! */ kernel[3]=AIR_CAST(float, b0/(128*bd*bd*bd*sbd)*embd*sqrt(13.0)* (-42*sbd*(165+4*bd*(5+bd))*spi- 5*(-693+378*bd-84*bd*bd+8*bd*bd*bd)* ebd*AIR_PI*erfsbd)); if (order>=8) { kernel[4]=AIR_CAST(float, b0/(2048*bd*bd*bd*bd*sbd)*embd*sqrt(17.0)* (-6*sbd*(225225+2*bd*(15015+2*bd*(1925+62*bd)))*spi+ 35*(19305+8*bd*(-1287+bd*(297+2*(-18+bd)*bd)))* ebd*AIR_PI*erfsbd)); if (order>8) return 1; } } } } if (delta) { return tijk_esh_make_kernel_delta_f(kernel,kernel,order); } return tijk_esh_make_kernel_rank1_f(kernel,kernel,order); } /* elfBallStickODF: * * Deconvolves single-shell DWI measurements to an ODF, using a kernel derived * from the ball-and-stick model * * Output: * odf - ESH coefficients of computed ODF * fiso - if non-NULL, estimated isotropic volume fraction * d - if non-NULL, estimated ADC * Input: * dwi - DWI measurement and parameters * T - matrix computed by elfESHEstimMatrix * order - desired ESH order of odf * delta - whether to use delta peak (set to 0 when using elfBallStickPredict) * * Returns 0 on success * 1 if order is not supported * 2 if all DWIs were larger than the B0 image */ int elfBallStickODF_f(float *odf, float *fiso, float *d, const elfSingleShellDWI *dwi, const float *T, unsigned int order, int delta) { unsigned int C = tijk_esh_len[order/2], k, l; float mean=0, _origd=1e-20f, _d=_origd; float *odfp = odf; float isovf0, isovf1, _fiso; float kernel[10]; /* should be tijk_max_esh_order/2+1, * but ANSI C doesn't allow that :/ */ if (order>tijk_max_esh_order || order%2!=0) return 1; /* use T to estimate ESH coefficients for given signal values */ for (k=0; kdwino; l++) *odfp += T[k*dwi->dwino+l]*dwi->dwis[l]; odfp++; } /* guess d and fiso based on the data */ for (k=0; kdwino; k++) { float thisd = AIR_CAST(float, -log(dwi->dwis[k]/dwi->b0)/dwi->b); if (dwi->dwis[k]!=0 && thisd>_d) _d=thisd; mean += dwi->dwis[k]; } mean /= dwi->dwino; isovf0 = AIR_CAST(float, 0.5*sqrt(AIR_PI/(dwi->b*_d)) *airErf(sqrt(dwi->b*_d))); isovf1 = AIR_CAST(float, exp(-dwi->b*_d)); _fiso = AIR_CAST(float, AIR_AFFINE(isovf0,mean/dwi->b0,isovf1, 0.0, 1.0)); _fiso=AIR_CLAMP(0.01f,_fiso,0.99f); if (fiso!=NULL) *fiso=_fiso; if (d!=NULL) *d=_d; /* create deconvolution kernel */ elfKernelStick_f(kernel, order, dwi->b*_d, dwi->b0, delta); /* remove estimated isotropic part from the signal */ odf[0] -= AIR_CAST(float, dwi->b0 * _fiso * 2*sqrt(AIR_PI)*exp(-dwi->b*_d)); /* deconvolve */ tijk_esh_deconvolve_f(odf, odf, kernel, order); if (_d==_origd) return 2; else return 0; } /* elfBallStickPredict: * * Based on a rank-k decomposition of the given odf (given as a * symmetric tensor of type "type"), sets vs and fs[1..k] of parms. d * (ADC) and fiso are used to set d and fs[0]. * Returns 0 upon success, 1 upon error */ int elfBallStickPredict_f(elfBallStickParms *parms, float *odf, const tijk_type *type, unsigned int k, float d, float fiso) { tijk_refine_rankk_parm *rparm; unsigned int i; float totalfs=0; if (k>3) return 1; rparm=tijk_refine_rankk_parm_new(); rparm->pos=1; if (tijk_approx_rankk_3d_f(parms->fs+1, parms->vs, NULL, odf, type, k, rparm)) { rparm=tijk_refine_rankk_parm_nix(rparm); return 1; } rparm=tijk_refine_rankk_parm_nix(rparm); parms->d = d; parms->fiberct = k; parms->fs[0]=fiso; /* normalize fs[1..3] */ for (i=1; i<=k; i++) totalfs+=parms->fs[i]; for (i=1; i<=k; i++) parms->fs[i]*=(1.0f-parms->fs[0])/totalfs; return 0; } #if TEEM_LEVMAR /* Callbacks for levmar-based optimization of ball-and-stick model */ /* pp[0]: log(d) pp[1]: VF1 pp[2]: theta1 pp[3]: phi1 * pp[4]: VF2 pp[5]: theta2 pp[6]: phi2 * pp[7]: VF3 pp[8]: theta3 pp[9]: phi3 * one-/two-stick-models may only use a subset of these */ static void _levmarBallStickCB(double *pp, double *xx, int mm, int nn, void *_data) { int ii, maxk=(mm-1)/3; elfSingleShellDWI *data = (elfSingleShellDWI *) _data; float *egrad; double adc=exp(pp[0]); double dirs[3][3]; int k; double vfs[4]={0.0,0.0,0.0,0.0}, sumvfs; for (k=0; k=0) vfs[k+1]=1.0-0.5*exp(-pp[1+3*k]); else vfs[k+1]=0.5*exp(pp[1+3*k]); } sumvfs=vfs[1]+vfs[2]+vfs[3]; if (sumvfs>1.0) { vfs[1]/=sumvfs; vfs[2]/=sumvfs; vfs[3]/=sumvfs; } else { vfs[0]=1.0-sumvfs; } egrad = data->grads; for (ii=0; iib*adc); /* ball compartment */ for (k=0; kb*adc*dp*dp); } xx[ii]*=data->b0; egrad+=3; } } static void _levmarBallStickJacCB(double *p, double *jac, int m, int n, void *_data) { elfSingleShellDWI *data = (elfSingleShellDWI *) _data; float *egrad; double *j; int i,k, maxk=(m-1)/3; double adc=exp(p[0]); double dirs[3][3]; double stheta[3], ctheta[3], sphi[3], cphi[3]; /* pre-compute volume fractions */ double _vfs[4]={0.0,0.0,0.0,0.0}, vfs[4], vfssum, svfssum; for (k=0; k=0) _vfs[k+1]=1.0-0.5*exp(-p[1+3*k]); else _vfs[k+1]=0.5*exp(p[1+3*k]); } vfssum=_vfs[1]+_vfs[2]+_vfs[3]; svfssum=vfssum*vfssum; if (vfssum>1.0) { vfs[0]=0; vfs[1]=_vfs[1]/vfssum; vfs[2]=_vfs[2]/vfssum; vfs[3]=_vfs[3]/vfssum; } else { vfs[0]=1.0-vfssum; ELL_3V_COPY(vfs+1,_vfs+1); } /* directions */ for (k=0; kgrads; j=jac; for (i=0; ib0*exp(-data->b*adc); for (k=0; kb0*exp(-data->b*adc*dps[k]*dps[k]); } j[0]=vfs[0]*pred[0]*(-data->b*adc); /* ball contribution */ for (k=0; kb*adc*dps[k]*dps[k]); if (vfssum<1.0) { if (_vfs[k+1]>=0.5) j[1+3*k]=(1.0-_vfs[k+1])*(-pred[0]+pred[k+1]); else j[1+3*k]=_vfs[k+1]*(-pred[0]+pred[k+1]); } else { j[1+3*k]=0; for (ik=0; ik=0.5) j[1+3*k]*=(1.0-_vfs[k+1])/svfssum; else j[1+3*k]*=_vfs[k+1]/svfssum; } j[2+3*k]=vfs[k+1]*pred[k+1]* (-2*data->b*adc*dps[k]*(egrad[0]*ctheta[k]*cphi[k]+ egrad[1]*ctheta[k]*sphi[k]- egrad[2]*stheta[k])); j[3+3*k]=vfs[k+1]*pred[k+1]* (-2*data->b*adc*dps[k]*(-egrad[0]*stheta[k]*sphi[k]+ egrad[1]*stheta[k]*cphi[k])); } egrad+=3; j+=m; } } #endif /* levmar support */ /* elfBallStickOptimize: * * Based on an initial guess of parms, use Levenberg-Marquardt optimization * to improve the fit w.r.t. the given DWI data. * Requires teem to be compiled with levmar support * Returns 0 upon success * 1 if fiberct outside {1,2,3} * 2 if levmar returned an error * 3 if levmar support is missing in this version of teem */ int elfBallStickOptimize_f(elfBallStickParms *parms, const elfSingleShellDWI *dwi) { #if TEEM_LEVMAR double lmparms[10], *dwis; int lmret=0; unsigned int k; const int parmct=3*parms->fiberct+1; const int maxitr=200; double opts[LM_OPTS_SZ]={LM_INIT_MU,1e-2,1e-8,1e-8,1e-8}; double info[9]; double sumfs; double mind=1e-5, minvf=1e-3; /* some minimal values for stability */ if (parms->fiberct==0 || parms->fiberct>3) return 1; /* we need a double precision version of the dwis */ dwis = (double*) malloc(sizeof(double)*dwi->dwino); for (k=0; kdwino; k++) dwis[k]=dwi->dwis[k]; /* set parameters for optimization */ lmparms[0]=log(AIR_MAX(mind,parms->d)); parms->fs[1]=AIR_MAX(minvf,parms->fs[1]); if (parms->fs[1]<0.5) lmparms[1]=log(2*parms->fs[1]); else lmparms[1]=-log(2.0-2.0*parms->fs[1]); lmparms[2]=acos(parms->vs[2]); lmparms[3]=atan2(parms->vs[1],parms->vs[0]); if (parms->fiberct>1) { parms->fs[2]=AIR_MAX(minvf,parms->fs[2]); if (parms->fs[2]<0.5) lmparms[4]=log(2*parms->fs[2]); else lmparms[4]=-log(2.0-2.0*parms->fs[2]); lmparms[5]=acos(parms->vs[5]); lmparms[6]=atan2(parms->vs[4],parms->vs[3]); if (parms->fiberct>2) { parms->fs[3]=AIR_MAX(minvf,parms->fs[3]); if (parms->fs[3]<0.5) lmparms[7]=log(2*parms->fs[3]); else lmparms[7]=-log(2.0-2.0*parms->fs[3]); lmparms[8]=acos(parms->vs[8]); lmparms[9]=atan2(parms->vs[7],parms->vs[6]); } } lmret = dlevmar_der(_levmarBallStickCB,_levmarBallStickJacCB, lmparms, dwis, parmct, dwi->dwino, maxitr, opts, info, NULL, NULL, (void*)dwi); if (lmret==-1 && (int)info[6]==4) { /* try again with larger mu */ opts[0]*=10; lmret = dlevmar_der(_levmarBallStickCB,_levmarBallStickJacCB, lmparms, dwis, parmct, dwi->dwino, maxitr, opts, info, NULL, NULL, (void*)dwi); } free(dwis); /* no longer needed */ /* output the results (whether or not levmar signalled an error) */ parms->d = exp(lmparms[0]); parms->fs[0] = 0.0; if (lmparms[1]>=0) parms->fs[1]=1.0-0.5*exp(-lmparms[1]); else parms->fs[1]=0.5*exp(lmparms[1]); if (parms->fiberct>1) { if (lmparms[4]>=0) parms->fs[2]=1.0-0.5*exp(-lmparms[4]); else parms->fs[2]=0.5*exp(lmparms[4]); } else { parms->fs[2]=0.0; } if (parms->fiberct>2) { if (lmparms[7]>=0) parms->fs[3]=1.0-0.5*exp(-lmparms[7]); else parms->fs[3]=0.5*exp(lmparms[7]); } else { parms->fs[3]=0.0; } sumfs = parms->fs[1] + parms->fs[2] + parms->fs[3]; if (sumfs>1.0) { ELL_4V_SCALE(parms->fs, 1.0/sumfs, parms->fs); } else { parms->fs[0] = 1.0-sumfs; } for (k=0; kfiberct; k++) { double stheta = sin(lmparms[2+3*k]); ELL_3V_SET(parms->vs+3*k, stheta*cos(lmparms[3+3*k]), stheta*sin(lmparms[3+3*k]), cos(lmparms[2+3*k])); } /* record statistical information */ parms->stopreason = (int) (info[6])-1; parms->sqrerr = info[1]; parms->itr = info[5]; if (lmret==-1) return 2; return 0; #else /* no levmar support, out of luck */ (void) parms; (void) dwi; /* not using the parameters in this case */ return 3; #endif } teem-1.11.0~svn6057/src/elf/glyphElf.c0000664000175000017500000004400112174651611017063 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2010, 2009 Thomas Schultz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "elf.h" const int elfPresent = 42; /* Glyphs for higher-order tensors */ /* Helper routine to estimate normals in a triangle soup in which antipodal * vertices are subsequent */ static void estimateNormalsAntipodal (limnPolyData *glyph, const char normalize) { unsigned int faceno=glyph->indxNum/3; unsigned int *faces=glyph->indx; unsigned int f; memset(glyph->norm,0,sizeof(float)*3*glyph->xyzwNum); for (f=0; fxyzw+4*faces[3*f+1], glyph->xyzw+4*faces[3*f]); ELL_3V_SUB(diff2,glyph->xyzw+4*faces[3*f+2], glyph->xyzw+4*faces[3*f]); ELL_3V_CROSS(cross,diff1,diff2); ELL_3V_INCR(glyph->norm+3*faces[3*f],cross); ELL_3V_INCR(glyph->norm+3*faces[3*f+1],cross); ELL_3V_INCR(glyph->norm+3*faces[3*f+2],cross); /* same for anti-face */ if (faces[3*f]%2==0) ELL_3V_SUB(glyph->norm+3*faces[3*f]+3, glyph->norm+3*faces[3*f]+3,cross); else ELL_3V_SUB(glyph->norm+3*faces[3*f]-3, glyph->norm+3*faces[3*f]-3,cross); if (faces[3*f+1]%2==0) ELL_3V_SUB(glyph->norm+3*faces[3*f+1]+3, glyph->norm+3*faces[3*f+1]+3,cross); else ELL_3V_SUB(glyph->norm+3*faces[3*f+1]-3, glyph->norm+3*faces[3*f+1]-3,cross); if (faces[3*f+2]%2==0) ELL_3V_SUB(glyph->norm+3*faces[3*f+2]+3, glyph->norm+3*faces[3*f+2]+3,cross); else ELL_3V_SUB(glyph->norm+3*faces[3*f+2]-3, glyph->norm+3*faces[3*f+2]-3,cross); } if (normalize) { float len; unsigned int i; for (i=0; inormNum; i++) { ELL_3V_NORM_TT(glyph->norm + 3*i, float, glyph->norm + 3*i, len); } } } /* ******** elfGlyphPolar ** ** Turns a unit sphere into a polar plot that depicts a given tensor. ** ** Input: ** glyph is expected to represent the unit sphere ** antipodal can be set to a non-zero value if antipodal points on the sphere ** are subsequent in the input. It will lead to faster processing. ** (this can be used together with limnPolyDataIcoSphere) ** ten is an input tensor of type type ** clamp - if nonzero, negative values will be clamped to zero ** normalize - if nonzero, surface normals will be rescaled to unit length ** posColor[4] - RGBA color code for positive values (if desired, else NULL) ** negColor[4] - assumed to be non-NULL if posColor is non-NULL ** ** Output: ** glyph is the polar plot that corresponds to ten. ** If the input shape was anything other than a unit sphere, the output ** shape is undefined ** Normals are only updated when they were allocated in the input ** When colors were present in the input, they are replaced by a color ** coding of sign (if posColor!=NULL) or a pointwise XYZ-RGB map (else) ** When isdef!=NULL, *isdef is set to 0 if we found evidence that the given ** input tensor is not positive definite ** The return value is the radius of the glyph's bounding sphere */ float elfGlyphPolar(limnPolyData *glyph, const char antipodal, const float *ten, const tijk_type *type, char *isdef, const char clamp, const char normalize, const unsigned char *posColor, const unsigned char *negColor) { float *verts=glyph->xyzw; float max=0; unsigned int i, infoBitFlag; char def=1; infoBitFlag = limnPolyDataInfoBitFlag(glyph); for (i=0; ixyzwNum; i++) { float val=(*type->sym->s_form_f)(ten,verts); /* if RGBA is allocated, take care of coloring */ if (infoBitFlag & (1 << limnPolyDataInfoRGBA)) { if (posColor!=NULL) { /* color by sign */ if (val<0) { ELL_4V_COPY(glyph->rgba+4*i, negColor); if (antipodal) ELL_4V_COPY(glyph->rgba+4*i+4, negColor); } else { ELL_4V_COPY(glyph->rgba+4*i, posColor); if (antipodal) ELL_4V_COPY(glyph->rgba+4*i+4, posColor); } } else { /* RGB encode the vertex coordinates */ ELL_4V_SET_TT(glyph->rgba+4*i, unsigned char, 255*fabs(verts[0]), 255*fabs(verts[1]), 255*fabs(verts[2]), 255); if (antipodal) { ELL_4V_COPY(glyph->rgba+4*i+4,glyph->rgba+4*i); } } } if (val<0) { def=0; if (clamp) val=0; else val=-val; } if (val>max) max=val; ELL_3V_SCALE(verts,val,verts); if (antipodal) { ELL_3V_SCALE(verts+4,-1.0f,verts); verts+=4; i++; } verts+=4; } if (isdef!=NULL) *isdef=def; if (infoBitFlag & (1 << limnPolyDataInfoNorm)) { /* take care of normals */ if (antipodal && glyph->primNum==1 && glyph->type[0]==limnPrimitiveTriangles) { /* we can use our specialized, more efficient code */ estimateNormalsAntipodal(glyph, normalize); } else { /* use standard limn routine */ limnPolyDataVertexNormals(glyph); } } return max; } /* ******** elfGlyphHOME ** ** Turns a unit sphere into a HOME glyph that depicts a given tensor. ** ** Input: ** glyph is expected to represent the unit sphere ** antipodal can be set to a non-zero value if antipodal points on the sphere ** are subsequent in the input. It will lead to faster processing. ** (this can be used together with limnPolyDataIcoSphere) ** ten is an input tensor of type type ** normalize - if nonzero, normals will be rescaled to unit length ** ** Output: ** glyph is the HOME glyph that corresponds to ten. ** If the input shape was anything other than a unit sphere, the output ** shape is undefined ** If the input tensor does not have a rank-k decomposition with positive ** coefficients, the output shape may self-intersect ** Normals are only updated when they were allocated in the input ** When colors were present in the input, they are replaced by a ** pointwise XYZ-RGB map ** When isdef!=NULL, *isdef is set to 0 if we found evidence that the given ** input tensor is not positive definite ** The return value is the radius of the glyph's bounding sphere, or -1 ** upon error (odd tensor order; HOME glyph is only defined for even orders) */ float elfGlyphHOME(limnPolyData *glyph, const char antipodal, const float *ten, const tijk_type *type, char *isdef, const char normalize) { float *verts=glyph->xyzw; float max=0; unsigned int i, infoBitFlag; char def=1; if (type->order%2==1) /* sanity check */ return -1; infoBitFlag = limnPolyDataInfoBitFlag(glyph); for (i=0; ixyzwNum; i++) { float HOMEpos[3], len; (*type->sym->v_form_f)(HOMEpos,ten,verts); if (ELL_3V_DOT(HOMEpos,verts)<0) def=0; ELL_3V_COPY(verts,HOMEpos); len=AIR_CAST(float, ELL_3V_LEN(HOMEpos)); if (len>max) max=len; /* if RGBA is allocated, take care of coloring */ if (infoBitFlag & (1 << limnPolyDataInfoRGBA)) { float c[3]; if (len>1e-18) ELL_3V_SET_TT(c,float,fabs(verts[0]/len), fabs(verts[1]/len),fabs(verts[2]/len)); else ELL_3V_SET(c,0,0,0); /* RGB encode the vertex coordinates */ ELL_4V_SET_TT(glyph->rgba+4*i, unsigned char, 255*c[0], 255*c[1], 255*c[2], 255); if (antipodal) { ELL_4V_COPY(glyph->rgba+4*i+4,glyph->rgba+4*i); } } if (antipodal) { ELL_3V_SCALE(verts+4,-1.0f,verts); verts+=4; i++; } verts+=4; } if (isdef!=NULL) *isdef=def; if (infoBitFlag & (1 << limnPolyDataInfoNorm)) { /* take care of normals */ if (antipodal && glyph->primNum==1 && glyph->type[0]==limnPrimitiveTriangles) { /* we can use our specialized faster code */ estimateNormalsAntipodal(glyph, normalize); } else { /* use standard limn routine */ limnPolyDataVertexNormals(glyph); } } return max; } /* ******** elfGlyphKDE ** ** Turns a unit sphere into a polar plot that depicts Kernel Density ** Estimate (KDE) of input vectors ** ** Input: ** glyph is expected to represent the unit sphere ** antipodal can be set to a non-zero value if antipodal points on the sphere ** are subsequent in the input, and gamma is even. It will lead to ** faster processing. ** (this can be used together with limnPolyDataIcoSphere) ** vecs is a set of n_vecs 3D input vectors (PDF samples) ** KDE uses dp kernel with exponent gamma ** normalize - if nonzero, surface normals will be rescaled to unit length ** ** Output: ** glyph is the polar plot that corresponds to KDE of vecs. ** If the input shape was anything other than a unit sphere, the output ** shape is undefined ** ** NOTE ON NORMALIZATION: ** Normalization is such that the maximum radius of the glyph (when all ** samples agree) is one. To instead normalize the KDE such that it ** integrates to unity, multiply by (2*gamma+1)/(4*pi) ** ** Normals are only updated when they were allocated in the input ** When colors were present in the input, they are replaced by a pointwise ** XYZ-RGB map ** The return value is the radius of the glyph's bounding sphere */ float elfGlyphKDE(limnPolyData *glyph, const char antipodal, const float *vecs, const size_t n_vecs, const float gamma, const char normalize) { float *verts=glyph->xyzw; float max=0; unsigned int i, j, infoBitFlag; infoBitFlag = limnPolyDataInfoBitFlag(glyph); for (i=0; ixyzwNum; i++) { /* compute value by looping over all vecs */ double val=0; for (j=0; jrgba+4*i, unsigned char, 255*fabs(verts[0]), 255*fabs(verts[1]), 255*fabs(verts[2]), 255); if (antipodal) { ELL_4V_COPY(glyph->rgba+4*i+4,glyph->rgba+4*i); } } if (val>max) max=AIR_CAST(float,val); ELL_3V_SCALE_TT(verts,float,val,verts); if (antipodal) { ELL_3V_SCALE(verts+4,-1.0f,verts); verts+=4; i++; } verts+=4; } if (infoBitFlag & (1 << limnPolyDataInfoNorm)) { /* take care of normals */ if (antipodal && glyph->primNum==1 && glyph->type[0]==limnPrimitiveTriangles) { /* we can use our specialized, more efficient code */ estimateNormalsAntipodal(glyph, normalize); } else { /* use standard limn routine */ limnPolyDataVertexNormals(glyph); } } return max; } /* ******** elfColorGlyphMaxima ** ** Maximum-based coloring of tensor glyphs, as described in Section 4 of ** Schultz/Kindlmann, EuroVis 2010 ** ** Input: ** glyph is a tensor glyph (generated by elfGlyph*) ** antipodal can be set to a non-zero value if antipodal points on the sphere ** are subsequent in the input. It will lead to faster processing. ** neighbors is an array that, for each vertex in glyph, holds the indices ** of its neighbors (adjacent through an edge). Each vertex has ** nbstride entries, padded with -1s if it really has less neighbors ** ten is the tensor depicted by the glyph ** type is the tensor type ** modulate - non-zero values lead to modulation by peak sharpness ** gamma - allows you to "bump up" the peak sharpness by the power of gamma ** ** Output: ** glyph is colored according to its maxima ** returns zero upon success (fails if memory cannot be allocated) */ int elfColorGlyphMaxima(limnPolyData *glyph, const char antipodal, const int *neighbors, unsigned int nbstride, const float *ten, const tijk_type *type, const char modulate, const float gamma) { float *sqrdist, *verts, *newcol; char *processed, *id_ct, *diff_ct; int *path; unsigned int infoBitFlag, i, j; airArray *mop; infoBitFlag = limnPolyDataInfoBitFlag(glyph); if (limnPolyDataAlloc(glyph, /* make sure we have a color buffer */ infoBitFlag | (1 << limnPolyDataInfoRGBA), glyph->xyzwNum, glyph->indxNum, glyph->primNum)) { return 1; } /* do the memory allocation */ mop = airMopNew(); if (mop==NULL) return 1; sqrdist = (float *) malloc(sizeof(float)*glyph->xyzwNum); airMopAdd(mop, sqrdist, airFree, airMopAlways); processed = (char *) malloc(sizeof(char)*glyph->xyzwNum); airMopAdd(mop, processed, airFree, airMopAlways); path = (int *) malloc(sizeof(int)*glyph->xyzwNum); airMopAdd(mop, path, airFree, airMopAlways); newcol = (float *) malloc(sizeof(float)*3*glyph->xyzwNum); airMopAdd(mop, newcol, airFree, airMopAlways); id_ct = (char *) malloc(sizeof(char)*glyph->xyzwNum); airMopAdd(mop, id_ct, airFree, airMopAlways); diff_ct = (char *) malloc(sizeof(char)*glyph->xyzwNum); airMopAdd(mop, diff_ct, airFree, airMopAlways); if (sqrdist==NULL || processed==NULL || path==NULL || newcol==NULL || id_ct==NULL || diff_ct==NULL) { airMopError(mop); return 1; } /* initialize sqrdist / processed / path */ verts=glyph->xyzw; if (antipodal) { for (i=0; ixyzwNum; i+=2) { sqrdist[i]=sqrdist[i+1]=ELL_3V_DOT(verts,verts); verts+=8; } } else { for (i=0; ixyzwNum; i++) { sqrdist[i]=ELL_3V_DOT(verts,verts); verts+=4; } } memset(processed,0,sizeof(char)*glyph->xyzwNum); memset(path, -1, sizeof(int)*glyph->xyzwNum); /* go through all vertices; ascend until we get to a maximum or a * previously processed vertex */ for (i=0; ixyzwNum; i++) { unsigned int pathno=0; int vert=i; char foundmax=0; unsigned char color[3]; if (processed[i]) continue; path[pathno++]=vert; while (!foundmax) { int bestnb=-1; float maxdist=0; for (j=0; jmaxdist) { maxdist=dist; bestnb=nb; } } if (bestnb==-1) { /* we are at an unprocessed maximum */ /* find appropriate color */ float vertdir[3]; float norm; float modfactor=1.0; ELL_3V_COPY(vertdir,glyph->xyzw+4*vert); norm=AIR_CAST(float, ELL_3V_LEN(vertdir)); if (norm>1e-18) { ELL_3V_SCALE(vertdir,1.0f/norm,vertdir); if (modulate) { /* modulate by peak strength */ float hess[7], val, evals[3]; /* compute second derivatives */ (*type->sym->hess_f)(hess+1,ten,vertdir); val=(*type->sym->s_form_f)(ten,vertdir); tenEigensolve_f(evals, NULL, hess); if (evals[1]>=0 || val<1e-10) { modfactor=0.0; } else { modfactor=-evals[1]/(type->order*val); if (modfactor>1.0) modfactor=1.0; else modfactor=AIR_CAST(float, pow(modfactor,gamma)); } } } else { ELL_3V_SET(vertdir,0,0,0); } ELL_3V_SET(color, (unsigned char) (AIR_LERP(modfactor, 1.0, fabs(vertdir[0]))*255), (unsigned char) (AIR_LERP(modfactor, 1.0, fabs(vertdir[1]))*255), (unsigned char) (AIR_LERP(modfactor, 1.0, fabs(vertdir[2]))*255)); foundmax=1; } else { if (processed[bestnb]) { ELL_3V_COPY(color, glyph->rgba+4*bestnb); foundmax=1; } else { /* add bestnb to the path and proceed */ path[pathno++]=bestnb; vert=bestnb; } } } /* end looking for maximum */ /* copy color to all vertices on the path */ for (j=0; jrgba+4*path[j], color[0], color[1], color[2], 255); if (antipodal) { int antip=path[j]+1; if (antip%2==0) antip-=2; processed[antip]=1; ELL_4V_COPY(glyph->rgba+4*antip, glyph->rgba+4*path[j]); } } if (antipodal) i++; /* we can skip over the next one */ } /* make coloring smoother by averaging */ memset(newcol,0,sizeof(float)*3*glyph->xyzwNum); memset(id_ct,0,sizeof(char)*glyph->xyzwNum); memset(diff_ct,0,sizeof(char)*glyph->xyzwNum); for (i=0; ixyzwNum; i++) { for (j=0; jrgba+4*i, glyph->rgba+4*nb)) { id_ct[i]++; id_ct[nb]++; } else { ELL_3V_INCR(newcol+3*i, glyph->rgba+4*nb); diff_ct[i]++; ELL_3V_INCR(newcol+3*nb, glyph->rgba+4*i); diff_ct[nb]++; } } } for (i=0; ixyzwNum; i++) { if (diff_ct[i]>0) { ELL_3V_SCALE_INCR_TT(newcol+3*i, float, 1.0+id_ct[i],glyph->rgba+4*i); ELL_3V_SCALE_TT(glyph->rgba+4*i, unsigned char, 1.0/(1.0+id_ct[i]+diff_ct[i]), newcol+3*i); if (antipodal) { ELL_3V_COPY(glyph->rgba+4*i+4,glyph->rgba+4*i); i++; } } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/src/elf/GNUmakefile0000664000175000017500000000346712165631065017233 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := elf #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### $(L).NEED = ten limn tijk ell biff air $(L).PUBLIC_HEADERS = elf.h $(L).PRIVATE_HEADERS = $(L).OBJS = ballStickElf.o ESHEstimElf.o glyphElf.o maximaElf.o $(L).TESTS = #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/elf/sources.cmake0000664000175000017500000000033711600224337017630 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(ELF_SOURCES ballStickElf.c ESHEstimElf.c glyphElf.c maximaElf.c elf.h ) ADD_TEEM_LIBRARY(elf ${ELF_SOURCES}) teem-1.11.0~svn6057/src/elf/elf.h0000664000175000017500000001465512107721646016102 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2011, 2010, 2009 Thomas Schultz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ELF_HAS_BEEN_INCLUDED #define ELF_HAS_BEEN_INCLUDED #include #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(tijk_EXPORTS) || defined(teem_EXPORTS) # define ELF_EXPORT extern __declspec(dllexport) # else # define ELF_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define ELF_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif /* glyphElf.c */ ELF_EXPORT const int elfPresent; ELF_EXPORT float elfGlyphHOME(limnPolyData *glyph, const char antipodal, const float *ten, const tijk_type *type, char *isdef, const char normalize); ELF_EXPORT float elfGlyphPolar(limnPolyData *glyph, const char antipodal, const float *ten, const tijk_type *type, char *isdef, const char clamp, const char normalize, const unsigned char *posColor, const unsigned char *negColor); ELF_EXPORT float elfGlyphKDE(limnPolyData *glyph, const char antipodal, const float *vecs, const size_t n_vecs, const float gamma, const char normalize); ELF_EXPORT int elfColorGlyphMaxima(limnPolyData *glyph, const char antipodal, const int *neighbors, unsigned int nbstride, const float *ten, const tijk_type *type, const char modulate, const float gamma); /* ********** elfMaximaContext ** ** Allows us to precompute and store information needed to find all maxima ** of a given symmetric 3D tensor type. Should only be used through elfMaxima* */ typedef struct { unsigned int num; const tijk_type *type; tijk_refine_rank1_parm *parm; int refine; int *neighbors; unsigned int nbstride; float *vertices_f; /* we're only storing the non-redundant ones */ double *vertices_d; /* only filled when needed */ } elfMaximaContext; /* maximaElf.c */ ELF_EXPORT elfMaximaContext *elfMaximaContextNew(const tijk_type *type, unsigned int level); ELF_EXPORT elfMaximaContext *elfMaximaContextNix(elfMaximaContext *emc); ELF_EXPORT void elfMaximaParmSet(elfMaximaContext *emc, tijk_refine_rank1_parm *parm); ELF_EXPORT void elfMaximaRefineSet(elfMaximaContext *emc, int refine); ELF_EXPORT int elfMaximaFind_d(double **ls, double **vs, const double *ten, elfMaximaContext *emc); ELF_EXPORT int elfMaximaFind_f(float **ls, float **vs, const float *ten, elfMaximaContext *emc); /* ESHEstimElf.c */ ELF_EXPORT void elfCart2Thetaphi_d(double *thetaphi, const double *dirs, unsigned int ct); ELF_EXPORT void elfCart2Thetaphi_f(float *thetaphi, const float *dirs, unsigned int ct); ELF_EXPORT int elfESHEstimMatrix_d(double *T, double *H, unsigned int order, const double *thetaphi, unsigned int ct, double lambda, double *w); ELF_EXPORT int elfESHEstimMatrix_f(float *T, float *H, unsigned int order, const float *thetaphi, unsigned int ct, float lambda, float *w); ELF_EXPORT int elfTenEstimMatrix_d(double *T, double *H, const tijk_type *type, const double *vecs, unsigned int ct, double *w); ELF_EXPORT int elfTenEstimMatrix_f(float *T, float *H, const tijk_type *type, const float *vecs, unsigned int ct, float *w); /* ballStickElf.c */ /* elfSingleShellDWI: * * Collects the parameters and measurements of a single-shell DWI experiment */ typedef struct { float b0; /* unweighted measurement */ float b; /* b value */ float *dwis; /* diffusion-weighted measurements */ float *grads; /* normalized gradient vectors (Cartesian 3D) */ unsigned int dwino; /* number of dwis */ } elfSingleShellDWI; ELF_EXPORT int elfKernelStick_f(float *kernel, unsigned int order, float bd, float b0, int delta); ELF_EXPORT int elfBallStickODF_f(float *odf, float *fiso, float *d, const elfSingleShellDWI *dwi, const float *T, unsigned int order, int delta); /* elfBallStickParms: * * Collects the parameters associated with a ball-and-multi-stick model * (up to three sticks), as well as debugging information */ typedef struct { float d; /* ADC */ unsigned int fiberct; float fs[4]; /* fs[0]==fiso */ float vs[9]; /* remaining fields are for statistics/debugging */ int stopreason; double sqrerr; double itr; } elfBallStickParms; ELF_EXPORT int elfBallStickPredict_f(elfBallStickParms *parms, float *odf, const tijk_type *type, unsigned int k, float d, float fiso); ELF_EXPORT int elfBallStickOptimize_f(elfBallStickParms *parms, const elfSingleShellDWI *dwi); #ifdef __cplusplus } #endif #endif /* ELF_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/elf/maximaElf.c0000664000175000017500000001527312042367142017222 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2011, 2010, 2009 Thomas Schultz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "elf.h" /* Creates an elfMaximaContext, which can then be used to find all * maxima of a symmetric even-order 3D tensor of the given type. * Returns NULL if type is not even-order 3D symmetric. * * The initial maximum sampling is at the vertices of a subdivided * icosahedron - level=3 (321 unique directions) should be * sufficient. Larger levels reduce the risk of missing one of two (or * more) very close maxima, at increased computational cost. */ elfMaximaContext *elfMaximaContextNew(const tijk_type *type, unsigned int level) { elfMaximaContext *retval; limnPolyData *sphere; unsigned int vert; if (type==NULL || type->dim!=3 || type->sym==NULL || type->order%2!=0) return NULL; /* elfMaxima cannot be used with this tensor type */ sphere = limnPolyDataNew(); limnPolyDataIcoSphere(sphere, 0, level); retval = (elfMaximaContext*) malloc(sizeof(elfMaximaContext)); retval->num = sphere->xyzwNum; retval->type = type; retval->parm = NULL; retval->refine = 1; /* extract neighborhood info (needed to take discrete maxima) */ limnPolyDataNeighborArray(&(retval->neighbors), &(retval->nbstride), sphere); /* copy over vertices in single and double precision */ retval->vertices_f = (float*) malloc(sizeof(float)*3*(sphere->xyzwNum/2)); for (vert=0; vertxyzwNum; vert+=2) { ELL_3V_COPY(retval->vertices_f+3*(vert/2), sphere->xyzw+4*vert); } retval->vertices_d = NULL; sphere=limnPolyDataNix(sphere); return retval; } elfMaximaContext *elfMaximaContextNix(elfMaximaContext *emc) { if (emc!=NULL) { free(emc->neighbors); free(emc->vertices_f); if (emc->vertices_d!=NULL) free(emc->vertices_d); if (emc->parm!=NULL) tijk_refine_rank1_parm_nix(emc->parm); free(emc); } return NULL; } /* can be used to set the parameters for refining the found local * maxima. Note that the elfMaximaContext will "take over possession" * of the parm struct, i.e., it will be nix'ed along with the context * or when setting another parm */ void elfMaximaParmSet(elfMaximaContext *emc, tijk_refine_rank1_parm *parm) { if (emc!=NULL) { if (emc->parm!=NULL) tijk_refine_rank1_parm_nix(emc->parm); emc->parm=parm; } } /* By default, discrete maxima are refined via optimization on the sphere. * Set this to zero for faster, but less accurate results. */ void elfMaximaRefineSet(elfMaximaContext *emc, int refine) { if (emc!=NULL) { emc->refine = refine; } } /* Finds discrete maxima of the tensor (based on emc) and refines them, * storing magnitudes in (malloc'ed) *ls and *vs. Returns the number of * distinct maxima, or -1 on error. ls are sorted in descending order. */ int elfMaximaFind_d(double **ls, double **vs, const double *ten, elfMaximaContext *emc) { unsigned int i; int retval; double *vals; airHeap *heap; if (ls==NULL || vs==NULL || ten==NULL || emc==NULL) return -1; if (emc->vertices_d==NULL) { /* we need to allocate these */ emc->vertices_d = (double*) malloc(sizeof(double)*3*(emc->num/2)); for (i=0; inum/2; i++) { ELL_3V_COPY(emc->vertices_d+3*i, emc->vertices_f+3*i); } } /* evaluate all unique directions */ vals = (double*) malloc(sizeof(double)*(emc->num/2)); for (i=0; inum/2; i++) { vals[i]=(*emc->type->sym->s_form_d)(ten, emc->vertices_d+3*i); } heap = airHeapNew(sizeof(double)*3, 20); /* identify discrete maxima */ for (i=0; inum/2; i++) { unsigned int ni=0; int ismax=1, nb; while (ninbstride && (nb=emc->neighbors[emc->nbstride*2*i+ni])!=-1) { if (vals[i]<=vals[nb/2]) { ismax=0; break; } ni++; } if (ismax) { double s=vals[i], v[3]; ELL_3V_COPY(v,emc->vertices_d+3*i); if (emc->refine) /* refine further */ tijk_refine_max_3d_d(&s, v, ten, emc->type, emc->parm); /* add to heap */ airHeapInsert(heap, -s, v); } } /* allocate arrays and return what we have found */ retval=airHeapLength(heap); if (retval>0) { *ls = (double*) malloc(sizeof(double)*retval); *vs = (double*) malloc(sizeof(double)*3*retval); for (i=0; i<(unsigned int)retval; i++) { (*ls)[i]=-airHeapFrontPop(heap, (*vs)+3*i); } } heap=airHeapNix(heap); free(vals); return retval; } /* Mostly copy-pasted from above :-/ */ int elfMaximaFind_f(float **ls, float **vs, const float *ten, elfMaximaContext *emc) { unsigned int i; int retval; float *vals; airHeap *heap; if (ls==NULL || vs==NULL || ten==NULL || emc==NULL) return -1; /* evaluate all unique directions */ vals = (float*) malloc(sizeof(float)*(emc->num/2)); for (i=0; inum/2; i++) { vals[i]=(*emc->type->sym->s_form_f)(ten, emc->vertices_f+3*i); } heap = airHeapNew(sizeof(float)*3, 20); /* identify discrete maxima */ for (i=0; inum/2; i++) { unsigned int ni=0; int ismax=1, nb; while (ninbstride && (nb=emc->neighbors[emc->nbstride*2*i+ni])!=-1) { if (vals[i]<=vals[nb/2]) { ismax=0; break; } ni++; } if (ismax) { float s=vals[i], v[3]; ELL_3V_COPY(v,emc->vertices_f+3*i); if (emc->refine) /* refine further */ tijk_refine_max_3d_f(&s, v, ten, emc->type, emc->parm); /* add to heap */ airHeapInsert(heap, -s, v); } } /* allocate arrays and return what we have found */ retval=airHeapLength(heap); if (retval>0) { *ls = (float*) malloc(sizeof(float)*retval); *vs = (float*) malloc(sizeof(float)*3*retval); for (i=0; i<(unsigned int)retval; i++) { (*ls)[i]=AIR_CAST(float,-airHeapFrontPop(heap, (*vs)+3*i)); } } heap=airHeapNix(heap); free(vals); return retval; } teem-1.11.0~svn6057/src/elf/ESHEstimElf.c0000664000175000017500000004544312104250015017356 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009, 2008 Thomas Schultz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "elf.h" /* Routines for estimating even-order spherical harmonics or tensor * coefficients. */ /* elfCart2Thetaphi: * * Helper function to transform ct 3D Cartesian coordinates into * theta (polar angle from positive z) and phi (azimuth from positive x) * Input vectors should be non-zero, but do not need to be normalized * thetaphi needs to be pre-allocated to length 2*ct */ #define ELFCART2THETAPHI(TYPE, SUF) \ void elfCart2Thetaphi_##SUF(TYPE *thetaphi, const TYPE *dirs, \ unsigned int ct) { \ unsigned int i; \ for (i=0; i1) thetaphi[2*i]=0; \ else if (z<-1) thetaphi[2*i]=AIR_CAST(TYPE, AIR_PI); \ else thetaphi[2*i]=AIR_CAST(TYPE, acos(z)); \ thetaphi[2*i+1]=AIR_CAST(TYPE, atan2(dirs[3*i+1],dirs[3*i])); \ } \ } \ ELFCART2THETAPHI(double, d) ELFCART2THETAPHI(float, f) /* elfESHEstimMatrix: * * Computes a matrix T that can be used to transform a measurement vector v * of ct values on the sphere, at positions given by thetaphi, into a vector * c of ESH coefficients of the desired order (c=Tv) such that the ESH * series approximates the given values in a least squares sense. * * T needs to be pre-allocated to length len(c)*ct * If H is non-NULL, it should be pre-allocated to length ct*ct * In this case, the "hat" matrix will be written to H, which maps the * measurement vector v to the corresponding model predictions * If lambda>0, Laplace-Beltrami regularization is employed * If w is non-NULL, it is assumed to be a weight vector with len(w)=ct * * Returns 0 on success * 1 if order is unsupported * 2 if len(v)tijk_max_esh_order || order%2!=0) \ return 1; \ N=tijk_esh_len[order/2]; \ if (ct all SHs that belong to the same \ * thetaphi are stored sequentially */ \ for (i=0; i0) { \ unsigned int idx=0, o; \ for (o=0; o<=order; o+=2) { /* order */ \ while (idxdim=2; \ nmat->type=nrrdTypeDouble; \ nmat->axis[0].size=nmat->axis[1].size=N; \ nmat->data=M; \ ell_Nm_inv(ninv, nmat); \ Minv = (double*) ninv->data; \ \ /* create final transformation matrix */ \ if (w!=NULL) { /* weighted */ \ for (i=0; isym==NULL || (type->dim!=2 && type->dim!=3)) \ return 1; \ N=type->num; \ if (ct all coeffs that belong to the same \ * vec are stored sequentially */ \ for (i=0; idim==2) { \ ELL_2V_COPY(vec, vecs+2*i); \ } else { \ ELL_3V_COPY(vec, vecs+3*i); \ } \ (*type->sym->make_rank1_d)(B+N*i, 1.0, vec); \ } \ for (j=0; jmult[j]); \ for (i=0; idim=2; \ nmat->type=nrrdTypeDouble; \ nmat->axis[0].size=nmat->axis[1].size=N; \ nmat->data=M; \ ell_Nm_inv(ninv, nmat); \ Minv = (double*) ninv->data; \ \ /* create final transformation matrix */ \ if (w!=NULL) { /* weighted */ \ for (i=0; imult[i]); \ for (j=0; j LUV conversion; u'_n = 4X_n / (X_n + 15Y_n + 3Z_n) v'_n = 9Y_n / (X_n + 15Y_n + 3Z_n) */ float dyeWhiteuvp_n[2] = {0.197839f, 0.468342f}; void dyeRGBtoHSV(float *H, float *S, float *V, float R, float G, float B) { float max, min, delta; max = AIR_MAX(R,G); max = AIR_MAX(B,max); min = AIR_MIN(R,G); min = AIR_MIN(B,min); *V = max; if (max != 0) *S = (max - min)/max; else *S = 0; if (0 == *S) { *H = 0; return; } /* else there is hue */ delta = max - min; if (R == max) *H = (G - B)/delta; else if (G == max) *H = 2 + (B - R)/delta; else *H = 4 + (R - G)/delta; *H /= 6; if (*H < 0) *H += 1; return; } /* ******** dyeHSVtoRGB() ** ** conversion from HSV single hexcone to RGB ** ** input and ouput are all floats in interval [0,1] ** DOES NO RANGE CHECKING WHATSOEVER ** ** From Foley + vanDam, 2nd Ed., p. 593 */ void dyeHSVtoRGB(float *R, float *G, float *B, float H, float S, float V) { float min, fract, vsf, mid1, mid2; int sextant; if (0 == S) { *R = *G = *B = V; return; } /* else there is hue */ if (1 == H) H = 0; H *= 6; sextant = (int) floor(H); fract = H - sextant; vsf = V*S*fract; min = V*(1 - S); mid1 = min + vsf; mid2 = V - vsf; switch (sextant) { case 0: { *R = V; *G = mid1; *B = min; break; } case 1: { *R = mid2; *G = V; *B = min; break; } case 2: { *R = min; *G = V; *B = mid1; break; } case 3: { *R = min; *G = mid2; *B = V; break; } case 4: { *R = mid1; *G = min; *B = V; break; } case 5: { *R = V; *G = min; *B = mid2; break; } } } /* ******** dyeRGBtoHSL() ** ** converts from RGB to HSL double hexcone ** L: "lightness" = (max(R,G,B)+min(R,G,B))/2 ** note that saturation (S) is different than the saturation in HSV ** hue (H) is the same in HSL and HSV ** ** r,g,b input and *h,*s,*l output are all floats in [0, 1] ** DOES NO RANGE CHECKING WHATSOEVER ** ** From Foley + vanDam, 2nd Ed., p. 595 */ void dyeRGBtoHSL(float *H, float *S, float *L, float R, float G, float B) { float min, max, lev, delta; max = AIR_MAX(R,G); max = AIR_MAX(max,B); min = AIR_MIN(R,G); min = AIR_MIN(min,B); *L = lev = (max + min)/2.0f; if (max == min) { *S = 0; *H = 0; /* actually, undefined */ return; } /* else there is hue */ delta = max - min; if (lev <= 0.5) *S = delta/(max + min); else *S = delta/(2-(max + min)); if (R == max) *H = (G - B)/delta; else if (G == max) *H = 2 + (B - R)/delta; else *H = 4 + (R - G)/delta; *H /= 6; if (*H < 0) *H += 1; return; } /* ******** dyeHSLtoRGB() ** ** converts from HSL double hexcone back to RGB ** ** input and ouput are all floats in interval [0,1] ** DOES NO RANGE CHECKING WHATSOEVER ** ** From Foley + vanDam, 2nd Ed., p. 596 */ void dyeHSLtoRGB(float *R, float *G, float *B, float H, float S, float L) { float m1, m2, fract, mid1, mid2; int sextant; if (S == 0) { *R = *G = *B = L; return; } /* else there is hue */ if (L <= 0.5) m2 = L*(1+S); /* the book says L*(L+S) which is ?? wrong ?? */ else m2 = L + S - L*S; m1 = 2*L - m2; if (1 == H) H = 0; H *= 6; sextant = (int) floor(H); fract = H - sextant; mid1 = m1 + fract*(m2 - m1); mid2 = m2 + fract*(m1 - m2); /* compared to HSVtoRGB: V -> m2, min -> m1 */ switch (sextant) { case 0: { *R = m2; *G = mid1; *B = m1; break; } case 1: { *R = mid2; *G = m2; *B = m1; break; } case 2: { *R = m1; *G = m2; *B = mid1; break; } case 3: { *R = m1; *G = mid2; *B = m2; break; } case 4: { *R = mid1; *G = m1; *B = m2; break; } case 5: { *R = m2; *G = m1; *B = mid2; break; } } } void dyeRGBtoXYZ(float *X, float *Y, float *Z, float R, float G, float B) { float in[3], out[3]; ELL_3V_SET(in, R, G, B); ELL_3MV_MUL(out, dyeRGBtoXYZMatx, in); ELL_3V_GET(*X, *Y, *Z, out); return; } void dyeXYZtoRGB(float *R, float *G, float *B, float X, float Y, float Z) { float in[3], out[3]; ELL_3V_SET(in, X, Y, Z); ELL_3MV_MUL(out, dyeXYZtoRGBMatx, in); ELL_3V_GET(*R, *G, *B, out); return; } float dyeLcbrt(float t) { return AIR_CAST(float, (t > 0.008856 ? airCbrt(t) : 7.787*t + 16.0/116.0)); } float dyeLcubed(float t) { return(t > 0.206893 ? t*t*t : (t - 16.0f/116.0f)/7.787f); } void dyeXYZtoLAB(float *L, float *A, float *B, float X, float Y, float Z) { float Xnorm, Ynorm, Znorm; Xnorm = X/dyeWhiteXYZ_n[0]; Ynorm = Y/dyeWhiteXYZ_n[1]; Znorm = Z/dyeWhiteXYZ_n[2]; *L = 116.0f*dyeLcbrt(Ynorm) - 16.0f; *A = 500.0f*(dyeLcbrt(Xnorm) - dyeLcbrt(Ynorm)); *B = 200.0f*(dyeLcbrt(Ynorm) - dyeLcbrt(Znorm)); } void dyeXYZtoLUV(float *L, float *U, float *V, float X, float Y, float Z) { float Ynorm, up, vp; Ynorm = Y/dyeWhiteXYZ_n[1]; *L = 116.0f*dyeLcbrt(Ynorm) - 16.0f; up = 4.0f*X/(X + 15.0f*Y + 3.0f*Z); vp = 9.0f*Y/(X + 15.0f*Y + 3.0f*Z); *U = 13.0f*(*L)*(up - dyeWhiteuvp_n[0]); *V = 13.0f*(*L)*(vp - dyeWhiteuvp_n[1]); } void dyeLABtoXYZ(float *X, float *Y, float *Z, float L, float A, float B) { float YnormCbrt; YnormCbrt = (16 + L)/116; *X = dyeWhiteXYZ_n[0]*dyeLcubed(A/500 + YnormCbrt); *Y = dyeWhiteXYZ_n[1]*dyeLcubed(YnormCbrt); *Z = dyeWhiteXYZ_n[2]*dyeLcubed(YnormCbrt - B/200); return; } void dyeLUVtoXYZ(float *X, float *Y, float *Z, float L, float U, float V) { float up, vp, YnormCbrt; YnormCbrt = (16 + L)/116; up = U/(13*L) + dyeWhiteuvp_n[0]; vp = V/(13*L) + dyeWhiteuvp_n[1]; *Y = dyeWhiteXYZ_n[1]*dyeLcubed(YnormCbrt); *X = -9*(*Y)*up/((up - 4)*vp - up*vp); *Z = (9*(*Y) - 15*vp*(*Y) - vp*(*X))/(3*vp); return; } void dyeIdentity(float *A, float *B, float *C, float a, float b, float c) { *A = a; *B = b; *C = c; return; } dyeConverter dyeSimpleConvert[DYE_MAX_SPACE+1][DYE_MAX_SPACE+1] = { {NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {NULL, dyeIdentity, NULL, dyeHSVtoRGB, NULL, NULL, NULL}, {NULL, NULL, dyeIdentity, dyeHSLtoRGB, NULL, NULL, NULL}, {NULL, dyeRGBtoHSV, dyeRGBtoHSL, dyeIdentity, dyeRGBtoXYZ, NULL, NULL}, {NULL, NULL, NULL, dyeXYZtoRGB, dyeIdentity, dyeXYZtoLAB, dyeXYZtoLUV}, {NULL, NULL, NULL, NULL, dyeLABtoXYZ, dyeIdentity, NULL}, {NULL, NULL, NULL, NULL, dyeLUVtoXYZ, NULL, dyeIdentity} }; /* ******** dyeConvert() ** ** master color conversion function. Can convert between any two ** types by recursive calls and calls to the simple converters above. */ int dyeConvert(dyeColor *col, int outSpace) { static const char me[] = "dyeConvert"; float i0, i1, i2, o0, o1, o2; dyeConverter simple; int inSpace, E; E = 0; if (!col) { biffAddf(DYE, "%s: got NULL pointer", me); return 1; } inSpace = dyeColorGet(&i0, &i1, &i2, col); if (!DYE_VALID_SPACE(inSpace)) { biffAddf(DYE, "%s: invalid input space #%d\n", me, inSpace); return 1; } if (!DYE_VALID_SPACE(outSpace)) { biffAddf(DYE, "%s: invalid output space #%d\n", me, outSpace); return 1; } if ( (simple = dyeSimpleConvert[inSpace][outSpace]) ) { (*simple)(&o0, &o1, &o2, i0, i1, i2); dyeColorSet(col, outSpace, o0, o1, o2); } else { /* we have some work to do . . . */ if (inSpace < dyeSpaceRGB && outSpace < dyeSpaceRGB) { /* its an easy HSV <-- RGB --> HSL conversion */ if (!E) E |= dyeConvert(col, dyeSpaceRGB); if (!E) E |= dyeConvert(col, outSpace); } else if (inSpace > dyeSpaceXYZ && outSpace > dyeSpaceXYZ) { /* its an easy LAB <-- XYZ --> LUV conversion */ if (!E) E |= dyeConvert(col, dyeSpaceXYZ); if (!E) E |= dyeConvert(col, outSpace); } else { /* the start and end spaces are at different stages */ if (inSpace < outSpace) { /* we are going towards higher stages */ if (inSpace < dyeSpaceRGB) { if (!E) E |= dyeConvert(col, dyeSpaceRGB); if (!E) E |= dyeConvert(col, outSpace); } else if (inSpace == dyeSpaceRGB) { if (!E) E |= dyeConvert(col, dyeSpaceXYZ); if (!E) E |= dyeConvert(col, outSpace); } else { biffAddf(DYE, "%s: CONFUSED! can't go %s -> %s\n", me, dyeSpaceToStr[inSpace], dyeSpaceToStr[outSpace]); E = 1; } } else { /* we are going towards lower stages */ if (outSpace < dyeSpaceRGB) { if (!E) E |= dyeConvert(col, dyeSpaceRGB); if (!E) E |= dyeConvert(col, outSpace); } else if (outSpace == dyeSpaceRGB) { if (!E) E |= dyeConvert(col, dyeSpaceXYZ); if (!E) E |= dyeConvert(col, dyeSpaceRGB); } else { biffAddf(DYE, "%s: CONFUSED! can't go %s -> %s\n", me, dyeSpaceToStr[inSpace], dyeSpaceToStr[outSpace]); E = 1; } } } } return E; } teem-1.11.0~svn6057/src/dye/dye.h0000664000175000017500000001135012165631065016114 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DYE_HAS_BEEN_INCLUDED #define DYE_HAS_BEEN_INCLUDED #include #include #include #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(dye_EXPORTS) || defined(teem_EXPORTS) # define DYE_EXPORT extern __declspec(dllexport) # else # define DYE_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define DYE_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define DYE dyeBiffKey enum { dyeSpaceUnknown, /* 0: nobody knows */ dyeSpaceHSV, /* 1: single hexcone */ dyeSpaceHSL, /* 2: double hexcone */ dyeSpaceRGB, /* 3: obscure, deprecated */ dyeSpaceXYZ, /* 4: perceptual primaries */ dyeSpaceLAB, /* 5: 1976 CIE (L*a*b*) (based on Munsell) */ dyeSpaceLUV, /* 6: 1976 CIE (L*u*v*) */ dyeSpaceLast }; #define DYE_MAX_SPACE 6 #define DYE_VALID_SPACE(spc) \ (AIR_IN_OP(dyeSpaceUnknown, (spc), dyeSpaceLast)) typedef struct { float val[2][3]; /* room for two colors: two triples of floats */ float xWhite, yWhite; /* chromaticity for white point */ signed char spc[2], /* the spaces the two colors belong to */ ii; /* which (0 or 1) of the two values is current */ } dyeColor; /* methodsDye.c */ DYE_EXPORT const int dyePresent; DYE_EXPORT const char *dyeBiffKey; DYE_EXPORT char dyeSpaceToStr[][AIR_STRLEN_SMALL]; DYE_EXPORT int dyeStrToSpace(char *str); DYE_EXPORT dyeColor *dyeColorInit(dyeColor *col); DYE_EXPORT dyeColor *dyeColorSet(dyeColor *col, int space, float v0, float v1, float v2); DYE_EXPORT int dyeColorGet(float *v0P, float *v1P, float *v2P, dyeColor *col); DYE_EXPORT int dyeColorGetAs(float *v0P, float *v1P, float *v2P, dyeColor *col, int space); DYE_EXPORT dyeColor *dyeColorNew(void); DYE_EXPORT dyeColor *dyeColorCopy(dyeColor *c1, dyeColor *c0); DYE_EXPORT dyeColor *dyeColorNix(dyeColor *col); DYE_EXPORT int dyeColorParse(dyeColor *col, char *str); DYE_EXPORT char *dyeColorSprintf(char *str, dyeColor *col); /* convertDye.c */ typedef void (*dyeConverter)(float*, float*, float*, float, float, float); DYE_EXPORT void dyeRGBtoHSV(float *H, float *S, float *V, float R, float G, float B); DYE_EXPORT void dyeHSVtoRGB(float *R, float *G, float *B, float H, float S, float V); DYE_EXPORT void dyeRGBtoHSL(float *H, float *S, float *L, float R, float G, float B); DYE_EXPORT void dyeHSLtoRGB(float *R, float *G, float *B, float H, float S, float L); DYE_EXPORT void dyeRGBtoXYZ(float *X, float *Y, float *Z, float R, float G, float B); DYE_EXPORT void dyeXYZtoRGB(float *R, float *G, float *B, float X, float Y, float Z); DYE_EXPORT void dyeXYZtoLAB(float *L, float *A, float *B, float X, float Y, float Z); DYE_EXPORT void dyeXYZtoLUV(float *L, float *U, float *V, float X, float Y, float Z); DYE_EXPORT void dyeLABtoXYZ(float *X, float *Y, float *Z, float L, float A, float B); DYE_EXPORT void dyeLUVtoXYZ(float *X, float *Y, float *Z, float L, float U, float V); DYE_EXPORT dyeConverter dyeSimpleConvert[DYE_MAX_SPACE+1][DYE_MAX_SPACE+1]; DYE_EXPORT int dyeConvert(dyeColor *col, int space); #ifdef __cplusplus } #endif #endif /* DYE_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/dye/GNUmakefile0000664000175000017500000000350312165631065017235 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := dye #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### (needs ell for macros) #### $(L).NEED = ell biff air $(L).PUBLIC_HEADERS = dye.h $(L).PRIVATE_HEADERS = $(L).OBJS = methodsDye.o convertDye.o $(L).TESTS = test/conv test/bow test/mchist #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/dye/methodsDye.c0000664000175000017500000001125612165631065017440 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "dye.h" const int dyePresent = 42; const char * dyeBiffKey = "dye"; char dyeSpaceToStr[][AIR_STRLEN_SMALL] = { "(unknown)", "HSV", "HSL", "RGB", "XYZ", "LAB", "LUV" }; int dyeStrToSpace(char *_str) { int spc; char *str; spc = dyeSpaceUnknown; if ( (str = airStrdup(_str)) ) { airToUpper(str); for (spc=0; spcval[0], AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(col->val[1], AIR_NAN, AIR_NAN, AIR_NAN); col->xWhite = col->yWhite = AIR_NAN; col->spc[0] = dyeSpaceUnknown; col->spc[1] = dyeSpaceUnknown; col->ii = 0; } return col; } dyeColor * dyeColorSet(dyeColor *col, int space, float v0, float v1, float v2) { if (col && DYE_VALID_SPACE(space)) { col->ii = AIR_CLAMP(0, col->ii, 1); /* We switch to the other one if the current one seems to be used, but we don't switch if new and current colorspaces are the same. If the other one is being used too, oh well. */ if (dyeSpaceUnknown != col->spc[col->ii] && AIR_EXISTS(col->val[col->ii][0]) && col->spc[col->ii] != space) { col->ii = 1 - col->ii; } ELL_3V_SET(col->val[col->ii], v0, v1, v2); col->spc[col->ii] = space; } return col; } int dyeColorGet(float *v0P, float *v1P, float *v2P, dyeColor *col) { int spc; spc = dyeSpaceUnknown; if (v0P && v1P && v2P && col) { col->ii = AIR_CLAMP(0, col->ii, 1); spc = col->spc[col->ii]; ELL_3V_GET(*v0P, *v1P, *v2P, col->val[col->ii]); } return spc; } int dyeColorGetAs(float *v0P, float *v1P, float *v2P, dyeColor *colIn, int space) { dyeColor _col, *col; col = &_col; dyeColorCopy(col, colIn); /* hope for no error */ dyeConvert(col, space); return dyeColorGet(v0P, v1P, v2P, col); } dyeColor * dyeColorNew() { dyeColor *col; col = (dyeColor *)calloc(1, sizeof(dyeColor)); col = dyeColorInit(col); return col; } dyeColor * dyeColorCopy(dyeColor *c1, dyeColor *c0) { if (c1 && c0) { memcpy(c1, c0, sizeof(dyeColor)); } return c1; } dyeColor * dyeColorNix(dyeColor *col) { if (col) { col = (dyeColor *)airFree(col); } return NULL; } int dyeColorParse(dyeColor *col, char *_str) { static const char me[]="dyeColorParse"; char *str; char *colon, *valS; float v0, v1, v2; int spc; if (!(col && _str)) { biffAddf(DYE, "%s: got NULL pointer", me); return 1; } if (!(str = airStrdup(_str))) { biffAddf(DYE, "%s: couldn't strdup!", me); return 1; } if (!(colon = strchr(str, ':'))) { biffAddf(DYE, "%s: given string \"%s\" didn't contain colon", me, str); return 1; } *colon = '\0'; valS = colon+1; if (3 != sscanf(valS, "%g,%g,%g", &v0, &v1, &v2)) { biffAddf(DYE, "%s: couldn't parse three floats from \"%s\"", me, valS); return 1; } spc = dyeStrToSpace(str); if (dyeSpaceUnknown == spc) { biffAddf(DYE, "%s: couldn't parse colorspace from \"%s\"", me, str); return 1; } str = (char *)airFree(str); dyeColorSet(col, spc, v0, v1, v2); return 0; } char * dyeColorSprintf(char *str, dyeColor *col) { if (str && col) { col->ii = AIR_CLAMP(0, col->ii, 1); sprintf(str, "%s:%g,%g,%g", dyeSpaceToStr[col->spc[col->ii]], col->val[col->ii][0], col->val[col->ii][1], col->val[col->ii][2]); } return str; } teem-1.11.0~svn6057/src/dye/sources.cmake0000664000175000017500000000030111113047450017630 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(DYE_SOURCES convertDye.c dye.h methodsDye.c ) ADD_TEEM_LIBRARY(dye ${DYE_SOURCES}) teem-1.11.0~svn6057/src/dye/test/0000775000175000017500000000000012203513755016137 5ustar domibeldomibelteem-1.11.0~svn6057/src/dye/test/conv.c0000664000175000017500000000365112042367142017253 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../dye.h" char *me; void usage() { /* 0 1 2 (3) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char *argv[]) { char *inS, *spcS, buff[512]; dyeColor *col; int spc; me = argv[0]; if (3 != argc) usage(); inS = argv[1]; spcS = argv[2]; if (dyeColorParse(col = dyeColorNew(), inS)) { fprintf(stderr, "%s: trouble parsing \"%s\":\n%s", me, inS, biffGet(DYE)); exit(1); } spc = dyeStrToSpace(spcS); if (dyeSpaceUnknown == spc) { fprintf(stderr, "%s: couldn't parse \"%s\" as colorspace\n", me, spcS); exit(1); } if (dyeConvert(col, spc)) { fprintf(stderr, "%s: trouble converting to %s:\n%s", me, spcS, biffGet(DYE)); exit(1); } printf("%s\n", dyeColorSprintf(buff, col)); col = dyeColorNix(col); exit(0); } teem-1.11.0~svn6057/src/dye/test/bow.c0000664000175000017500000000406212042367142017072 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../dye.h" char *me; void usage() { /* 0 1 2 3 (4) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char *argv[]) { char *resS, *scS, *outS; int i, res; FILE *out; float hue, R, G, B, sc; me = argv[0]; if (4 != argc) usage(); resS = argv[1]; scS = argv[2]; outS = argv[3]; if (1 != sscanf(resS, "%d", &res)) { fprintf(stderr, "%s: couldn't parse \"%s\" as int\n", me, resS); exit(1); } if (1 != sscanf(scS, "%f", &sc)) { fprintf(stderr, "%s: couldn't parse \"%s\" as float\n", me, scS); exit(1); } if (!(out = fopen(outS, "wa"))) { fprintf(stderr, "%s: couldn't open \"%s\" for writing\n", me, outS); exit(1); } fprintf(out, "# space: RGB\n"); for (i=0; i<=res-1; i++) { hue = AIR_AFFINE(0, i, res, 0.0, 1.0); dyeHSVtoRGB(&R, &G, &B, hue, 1, 1); fprintf(out, "%g %g %g %g\n", hue, sc*R, sc*G, sc*B); } fclose(out); exit(0); } teem-1.11.0~svn6057/src/dye/test/mchist.c0000664000175000017500000002602612160500366017574 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../dye.h" /* ** hist[0]: hue vs sat ** hist[1]: hue vs val */ void imageProc(Nrrd *nhproj[3], Nrrd *nhist[2], unsigned int sH, float *rgb, unsigned int size0, unsigned int sXY, unsigned int overSampleNum, float overSampleScale) { unsigned int xyi, hi, si, vi, oi; float rr, gg, bb, hh, ss, vv, *hist[2]; double rndA, rndB; nrrdZeroSet(nhist[0]); nrrdZeroSet(nhist[1]); hist[0] = AIR_CAST(float *, nhist[0]->data); hist[1] = AIR_CAST(float *, nhist[1]->data); for (xyi=0; xyirespFileEnable = AIR_TRUE; hestOptAdd(&hopt, "i", "images", airTypeString, 1, -1, &ninStr, NULL, "input image sequence", &ninLen, NULL, NULL); hestOptAdd(&hopt, "sh", "histo size", airTypeUInt, 1, 1, &sH, "500", "histogram size"); hestOptAdd(&hopt, "k", "kern", airTypeOther, 1, 1, &ksp, "tent", "kernel for upsampling images", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "us", "upsampling", airTypeFloat, 1, 1, &upSample, "1", "amount of upsampling of image"); hestOptAdd(&hopt, "osn", "# oversmp", airTypeUInt, 1, 1, &overSampleNum, "1", "number of sample per (upsampled) pixel"); hestOptAdd(&hopt, "osc", "scaling", airTypeFloat, 1, 1, &overSampleScale, "1", "scaling with oversampling"); hestOptAdd(&hopt, "ms", "max sum", airTypeFloat, 1, 1, &maxSum, "10", "per-hue histogram summation is non-linearly and " "asymptotically clamped to this maximum"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output filename", NULL); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, mchistInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (0 == overSampleNum) { fprintf(stderr, "%s: overSampleNum must be > 0\n", me); airMopError(mop); return 1; } nin0 = nrrdNew(); airMopAdd(mop, nin0, (airMopper)nrrdNuke, airMopAlways); if (nrrdLoad(nin0, ninStr[0], NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't load first image:\n%s", me, err); airMopError(mop); return 1; } if (!( (3 == nin0->axis[0].size || 4 == nin0->axis[0].size) && 3 == nin0->dim && nrrdTypeUChar == nin0->type )) { fprintf(stderr, "%s: 1st image not 3D (3-or-4)-by-X-by-Y %s array " "(got %u-D %s array)\n", me, airEnumStr(nrrdType, nrrdTypeUChar), nin0->dim, airEnumStr(nrrdType, nin0->type)); airMopError(mop); return 1; } rsmc = nrrdResampleContextNew(); airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways); size0 = AIR_CAST(unsigned int, nin0->axis[0].size); sX = AIR_CAST(unsigned int, upSample*nin0->axis[1].size); sY = AIR_CAST(unsigned int, upSample*nin0->axis[2].size); nrgb = nrrdNew(); airMopAdd(mop, nrgb, (airMopper)nrrdNuke, airMopAlways); if (nrrdResampleDefaultCenterSet(rsmc, nrrdCenterCell) || nrrdResampleInputSet(rsmc, nin0) || nrrdResampleKernelSet(rsmc, 1, ksp->kernel, ksp->parm) || nrrdResampleKernelSet(rsmc, 2, ksp->kernel, ksp->parm) || nrrdResampleSamplesSet(rsmc, 1, sX) || nrrdResampleSamplesSet(rsmc, 2, sY) || nrrdResampleRangeFullSet(rsmc, 1) || nrrdResampleRangeFullSet(rsmc, 2) || nrrdResampleTypeOutSet(rsmc, nrrdTypeFloat) || nrrdResampleRenormalizeSet(rsmc, AIR_TRUE) || nrrdResampleExecute(rsmc, nrgb)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error resampling slice:\n%s", me, err); airMopError(mop); return 1; } nhist[0] = nrrdNew(); airMopAdd(mop, nhist[0], (airMopper)nrrdNuke, airMopAlways); nhist[1] = nrrdNew(); airMopAdd(mop, nhist[1], (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(nhist[0], nrrdTypeFloat, 2, AIR_CAST(size_t, sH), AIR_CAST(size_t, sH)) || nrrdMaybeAlloc_va(nhist[1], nrrdTypeFloat, 2, AIR_CAST(size_t, sH), AIR_CAST(size_t, sH))) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error allocating histos:\n%s", me, err); airMopError(mop); return 1; } nhist[0]->axis[0].min = nhist[0]->axis[1].min = 0.0; nhist[0]->axis[0].max = nhist[0]->axis[1].max = 1.0; nhist[1]->axis[0].min = nhist[1]->axis[1].min = 0.0; nhist[1]->axis[0].max = nhist[1]->axis[1].max = 1.0; nhproj[0] = nrrdNew(); airMopAdd(mop, nhproj[0], (airMopper)nrrdNix, airMopAlways); nhproj[1] = nrrdNew(); airMopAdd(mop, nhproj[1], (airMopper)nrrdNix, airMopAlways); nhproj[2] = nrrdNew(); airMopAdd(mop, nhproj[2], (airMopper)nrrdNix, airMopAlways); printf("working ... "); hist[0] = AIR_CAST(float *, nhist[0]->data); hist[1] = AIR_CAST(float *, nhist[1]->data); nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); npreout = nrrdNew(); airMopAdd(mop, npreout, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(npreout, nrrdTypeFloat, 3, AIR_CAST(size_t, 3), AIR_CAST(size_t, sH), AIR_CAST(size_t, ninLen))) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error allocating pre-output:\n%s", me, err); airMopError(mop); return 1; } preout = AIR_CAST(float *, npreout->data); for (ti=0; tidata); imageProc(nhproj, nhist, sH, rgb, size0, sX*sY, overSampleNum, overSampleScale); preout += 3*sH; } printf("%s\n", airDoneStr(0, ti, ninLen, doneStr)); fflush(stdout); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 3, AIR_CAST(size_t, 3), AIR_CAST(size_t, sH), AIR_CAST(size_t, ninLen))) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error allocating output:\n%s", me, err); airMopError(mop); return 1; } out = AIR_CAST(float *, nout->data); preout = AIR_CAST(float *, npreout->data); for (ti=0; tispaceDim; sdi++) { vector[sdi] = nrrd->spaceOrigin[sdi]; } for (sdi=nrrd->spaceDim; sdispaceDim; } else { ret = 0; } return ret; } Note: * No tabs, ever. (why: no two editors show them the same) * Two spaces per level of indentation * In function definition (and only in definition), the function name starts a new line (why: makes it trivial to use grep or ack to identify which source file contains the function definition, as with "ack ^nrrdLoad") * opening brace *not* on its own line * closing brace gets its own line, except for if/else * braces around a branch even if there is only one statement (why: there will likely eventually be more than one statement, even if only for debugging) * single blank line between function variable declaration/initialiation and function code. Blank lines between distinct stages of a function computation are nice too (like a paragraph break). Other code formatting points: * If at all possible please keep lines to 79 characters, like the current line. A reference for this is the following line appearing in the source headers: Teem: Tools to process and visualize scientific data and images . with a period at the end to fill out to 79 characters. The exceptions to this are things like setting up a gageKind, or an airEnum, where long lines help preserve logical structure in fragile things. Some code in teem/Testing also exceeds this line length to increase the clarity of its repetitive structure. (why: limiting to 80 characters is a line in the sand, but it has more historical precedent than larger values. But the 80th character ends up being wrapped around to the next line in some displays, hence the limit at 79). * No trailing white-space. Changes in it are easy to make, and generate only noise in diffs (though removing it also generates confusing diffs, but that's already been done). Google "trailing white space" for further info and tips. * In header files, when using conditionals in the C pre-processor, put spaces between the "#" (in the first column) and (for example) the "define", so that the logical structure is clearer, e.g: #if 0 typedef double coil_t; # define coil_nrrdType nrrdTypeDouble # define COIL_TYPE_FLOAT 0 #else typedef float coil_t; # define coil_nrrdType nrrdTypeFloat # define COIL_TYPE_FLOAT 1 #endif This is actually consequential; the current teem/python/ctypes/gen-teem.py (automated ctypes wrapper generator) will skip over "# defines" and only reflect the setting of "#defines" in the python interface. (It would obviously be better to generate python wrappings that reflected the correct one of the two options, but the current tools aren't smart enough, so the current approach is better than having two sets of contradictory assignments). ============ Libraries * Teem is organized as a set of libraries (current 23 of them), with a well-defined dependency order. New libraries are thoughtfully placed within this ordering. * New libraries should be added only when it is very clear that no existing library can contain the intended functionality. By convention adopted in 2002, all Teem library names must be a WORD, not an acronym. The language of the word need not be English (e.g. tijk, Dutch for ticking, the fabric of pillow and mattress covering) * The "air" library is used by all other libraries, and is the source for low-level utility (e.e. airEnum, airArray) and math (e.g. airErf, airBesselI0) functions. * The "meet" library depends on all other libraries, is the place for functions that provide a uniform interface for things spread across different libraries (e.g meetGageKindParse, which parses strings to a gageKind*) * In any place where you have list all Teem libraries (which might happen in make files, documentation, symbol enumeration, etc), then always include the word "TEEM_LIB_LIST" nearby (to facilitate consistent maintenance), and always list the libraries in this dependency order (or its opposite): air hest biff nrrd ell unrrdu alan moss tijk gage dye bane limn echo hoover seek ten elf pull coil push mite meet * All libraries "lib" contain a const int "libPresent", defined in one of the first .c files the header refers to (which .c file is not fixed). The presence of this symbol provides a way to test which libraries are part of a given Teem shared library. Tradition dictates that the libPresent variable is set to 42, but this is not necessary. * Every library's .h file has a LIB_EXPORT macro which serves the purpose of "extern" in a normal C header, but which also handles the Windows __declspec weirdness. These must be used and used consistently. * air.h supplies TEEM_VERSION #defines that allow other software to know what version of Teem a given install is supplying. These need to be updated with each release and patch. Also, air/miscAir.c supplies const strings airTeemVersion (which is just set to TEEM_VERSION_STRING), and airTeemReleaseDate; airTeemReleaseDate has to be updated upon release or patch. The "meet" library would be a more logical place for all these, but that would complicate their use in NrrdIO ("meet" is not part of NrrdIO). * Despite the established dependency ordering of the libraries, there are rare instances when something in one library A is really for library B, even though A doesn't depend on B (in fact B depends on A). Examples of this include: - nrrdKernelHermiteScaleSpaceFlag, a nrrd kernel a flag for a kind of behavior that is implemented in gage (Hermite-spline interpolation between pre-blurred volumes) - gage has some specialization for length-7 values in probed volumes, this is really for the ten library that uses length-7 tensors These kinds of implicit dependency reversals should be minimized. ============ NrrdIO * NrrdIO is a stand-alone library for reading and writing NRRD files, for those programs that don't want or need all of Teem, but need NRRD IO functionality. * NrrdIO is *not* a library in Teem; is created from a Teem source tree by means of some scripts (which are part of the NrrdIO distribution). To facilitate that transformation, there are some special comments that delimit code sections and particular lines. These appear only the air, biff, and nrrd libraries; these are the source of all the NrrdIO source. Sections of code *not* needed for NrrdIO (in files that do supply other things needed for NrrdIO) are delimited by: /* ---- BEGIN non-NrrdIO */ /* ---- END non-NrrdIO */ Special lines that need different processing are tagged with /* NrrdIO-hack-001 */ /* NrrdIO-hack-002 */ . . . Respecting the placement and content of these comments is not important for Teem itself, but is essential for regenerating NrrdIO. ============ Identifiers and their suffixes * All public symbols (functions and globals) must begin with "lib" or "_lib", where "lib" is the library name (air, hest, biff, etc). All #defines must begin with "LIB" or "_LIB" (the only exceptions are the TEEM_VERSION #defines described below). The leading underscore should only be used in exceptional circumstances, where there is compelling value in making accessible in the API some additional functionality or set of function arguments that would otherwise be hidden inside an implementation. * In most libraries, function names, struct names, enum value names, and variables are in camel case, starting with a lower-case letter (e.g. tenExperSpecGradBValSet). One exception is the nrrd library, in which the N is capitalize in Nrrd, NrrdIOState, NrrdKernel, etc. The other exceptions are the ell and tijk library, in which everything is lower case, with _ separations (e.g. ell_cubic_root_triple). In all libraries #defines and macros are all upper case, with _ separations (e.g. NRRD_INDEX_GEN, TIJK_TYPE_MAX_NUM) * All functions that use var-args must have a name that ends with "_va". There are currently some functions ending with "_nva" (for "no var-args"), but in a future version of Teem the "_nva" may be removed. * Two versions of a function or variable that represent the same thing, except on floats vs doubles, should end with "_f" and "_d" * Much of Teem is effectively object-oriented, even though not in C++, with various structs playing the role of objects. For struct foo, there will be a fooNew() which allocates and initializes in, a fooCopy() which allocates a foo to be a copy of an existing foo, fooNix() which destroys it For container-like objects (airArray, Nrrd), there is also a fooNuke() which additionally frees the contained data, where fooNix() does not. There may also be fooInit() and fooDone() which set up and take down state within a struct without allocating or freeing the container. All foo-related functions should start with "foo", and most likely should take a foo pointer as their first argument. * All identifier names should be set up so that whatever is logically common between two functions appears as a common prefix, with only the suffix changing (e.g. tenInterpTypeQuatGeoLoxK, tenInterpTypeQuatGeoLoxR). One effect of this is that verbs (Set, Get, Slice, etc), end up at the very *end* of the function name (e.g. tenFiberMultiNew(), tenFiberMultiTrace(), tenFiberMultiPolyData()). This may seem awkward, but it is a blessing in interactive environments (like ipython) where you want to be doing tab-completion on identifiers. The extreme consistency of Teem function names also facilitates automated methods (still under development) for wrapping Teem struct foo in a real object-oriented language, with methods wrapping foo-related functions. ============ Coding functions * Following the point about "object-oriented" code above, make an effort to logically associate functions with existing structs in a way that reflects how you would attach methods to objects. * In general, Teem functions put the *output* in the first (or first few) function arguments. The remaining arguments are either parms then input, or input then parms. For example: int nrrdAxisInfoCopy(Nrrd *nout, const Nrrd *nin, const int *axmap, int excludeBitflag); copies per-axis information TO nout FROM nin, subject to the other parms. The rationale is that having the output first is like having the destination of an assignment on the left-hand side e.g. "nout = copy(nin)" * When passing an array to a Teem function with length known only at run-time (the array is passed as a pointer), the array and the array length must be subsequent arguments, in that order. The array length identifier should ideally be the array identifier, suffixed with "Num". For example, "nin" and "ninNum" here: int nrrdJoin(Nrrd *nout, const Nrrd *const *nin, unsigned int ninNum, unsigned int axis, int incrDim); * In public headers, ONLY use "..." to indicate var-args arguments. "..." should not be used in comments (in public headers). Use ".." instead of "..." to refer to a range of things, and ". . ." for regular ellipses in text. (why: to make it trivial to see which functions are var-args; they can be the hardest to debug). * Any functions that are not intending to be public should be "static" in their source files, so that nothing accidentally links against them. Static functions can use whatever name they want. * "parm" means "parameter", "parms" means "parameters" ============ Practices to keep Teem coherent and effective * Teem has a dashboard at: http://my.cdash.org/index.php?project=Teem The dashboard results represent the state of the svn trunk at midnight Chicago time (UTC-5). The dashboard must be monitored during development, in order to avoid creating new warnings or new errors. Adding new machines to the dashboard is a priority. * The tests in teem/Testing are slowly accumulating. Much of the effort in ongoing Teem development will be in creating and strengthening tests. * Teem is 99.9% ANSI C89. The known ways in which it isn't conformant are: - using the "long long" type qualifier to generate 64-bit ints (on *nix, and __int64 on Windows). - various strings created at compile time, often via the automatic "foo" "bar" --> "foobar" string concatenation, exceed the length 509 guaranteed to be supported by the standard - using snprintf() and fileno(), which are POSIX but not ANSI C Note that automatic conversion from a non-void* to a void*, in the specific case of supplying the value for a %p argument to a printf-like function, is not actually in C89: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26542 The AIR_VOIDP(x) and AIR_CVOIDP(x) macros are used to cast x to void* or const void * in this context. * Compiling with various warnings can help reveal bugs and potential problems. The advice here is relevant for Teem: http://www.gnu.org/software/gsl/manual/html_node/GCC-warning-options-for-numerical-programs.html However, -Wmissing-prototypes complains about a missing prototype even for local (static) functions that aren't used outside a given file, which is very common in Teem, and adding their protoypes would be onerous (and for similar reasons -Wmissing-declarations is not helpful). So what has proven useful for development is: gcc -O2 -std=c89 -pedantic \ -W -Wall -Wextra -Wno-long-long -Wno-overlength-strings \ -fstrict-aliasing -Wstrict-aliasing=2 -fstrict-overflow -Wstrict-overflow=5 \ -ftrapv -fshort-enums -fno-common \ -Wpointer-arith -Wcast-qual -Wcast-align \ -Wstrict-prototypes -Wshadow -Wwrite-strings -Wnested-externs \ -Wmissing-field-initializers -Wconversion * The use of -Wconversion *only* makes for gcc version >= 4.3 (before which its definition and action were useless). It reveals lots of places where there may be unexpected change of value as part of assignment or function arguments. This includes warnings about conversions from size_t that Visual Studio is good at noticing, as well as many other numerous sitautions! It will be some time before every last one of these is cleared up. * gcc -Wshadow shows when a new symbol name clashes with an existing one; this is certainly unintentional within a function, but there are various math and system calls with names that conflict with their use as variables or parameters, whether or not the existing names are true blue ANSI C. Here are some that have been removed from Teem, starting with the most suprising: index: looks to be same as strchr, but in strings.h instead of string.h round: round double to integer y0, y1: along with j0, j1, jn, yn, these are for Bessel functions signal: basic unix process communication read: read from file descriptors; a system call exp, log2: math functions, oops * It would be nice if Teem can compile without warnings as C++ code; There are two main issues: - void* can't be implicitly cast to some other pointer type as part of an assignment, so an explicit cast (via AIR_CAST or something else) is needed. This decreases the utility of airFree returning NULL for the "x = airFree(x)" idiom, but its usage was decreased anyway with compiler warnings about "x set but not used". - "this" is no longer available as a variable name * Any externally visible changes in Teem must be accompanied by regenerating its python ctypes wrappers teem/python/ctypes/teem.py. The current tools for doing this are not very slick, including the drawback that the information from ctypeslib is apparently randomly ordered: without changes in Teem, the generated teem.py will differ. Nonetheless, keeping teem.py up-to-date will avoid creating problems for users. There is a new script for this in teem/python/ctypes/go-gen-teem.sh, which is pretty hacky right now (since it presumes the directory structure on GLK's laptop!); ideas for improvement are welcome. * Make casts grep-able. This usually means using AIR_CAST, but can also mean using macros AIR_VOIDP, AIR_CVOIDP, AIR_UINT, AIR_INT, and the ell macros ending in "_TT". The only exception is casting things to airMopper to pass to airMopAdd(). (why: casts are inherently tricky, and sources of bugs, so their use should be grep-able) * Use AIR_CALLOC (preferably) or AIR_MALLOC to allocate things (why: both explicitly indicate the number of elements, and the size of each, and both include the cast from void* to the required pointer type) * Use airMop functions to manage dynamically-allocated resources to avoid memory leaks. The idea is that as soon as something is allocated, the pointer to the something is passed to airMopAdd, along with the callback function that should be called on that pointer to free the thing. You also tell airMopAdd the circumstances under which to call the callback: airMopOnError, airMopOnOkay (rarely used), airMopAlways. Using airMop allows one to centralize and simplify the management of a number of things, especially when the set of things being managed can vary depending on parameters or execution path. In C++, you'd use smart pointers. An example: airArray *mop; double *tmpbuff, *toreturn; . . . mop = airMopNew(); tmpbuff = AIR_CALLOC(100, double); airMopAdd(mop, tmpbuff, airFree, airMopAlways); toreturn = AIR_CALLOC(100, double); airMopAdd(mop, tmpbuff, airFree, airMopOnError); if (!( tmpbuff && toreturn )) { biffAddf(KEY, "%s: couldn't allocate things", me); airMopError(mop); return NULL; } . . . airMopOkay(mop); return toreturn; Note that you can safely pass NULL-pointers (pointers set to NULL because of unsuccessful allocations) to airMopAdd, because airFree() can be safely called on NULL. A sequence of allocations might look like this: buffA = AIR_CALLOC(sizeA, double); buffB = AIR_CALLOC(sizeB, double); buffC = AIR_CALLOC(sizeC, double); airMopAdd(mop, buffA, airFree, airMopAlways); airMopAdd(mop, buffB, airFree, airMopAlways); airMopAdd(mop, buffC, airFree, airMopAlways); or the airMopAdds can be interleaved, depending on clarity. * All Teem destructor-like functions (nrrdNuke, nrrdNix, limnPolyDataNix, gageContextNix, . . .) should be no-ops on when passed NULL, if for no other reason that it facilitates the practice that you airMopAdd things as soon as they are created, even in a sequence of allocations, wherein some of them might fail (as in the example above). * The airMop is built on top of Teem's simple dynamic array re-allocation helper, the airArray. Depending on requests for growing or shrinking the array, an airArray will change the value of a pointer to the (changing size) memory region. See teem/src/air/air.h for more details. * Because the first argument to airArrayNew is a void**, helpful compilers can generate many warnings about "dereferencing type-punned pointer will break strict-aliasing rules" when you cast the pointer (which you want to have managed by the airArray) to void**. Seeking to avoid those warnings, there is an airPtrPtrUnion which provides a union for many of the common pointer-to-pointer types that come up in Teem code. Variables of type airPtrPtrUnion are typically named "appu", and grepping through the Teem code shows examples of their use. Creating new unions to side-step aliasing warnings should be created only when dealing with pointers to objects defined by Teem libraries other than air (e.g. gagePerVolumes). * Teem relies heavily on C enums to give human-readable names to integer values (e.g. nrrdTypeChar, nrrdTypeShort, nrrdTypeFloat, . . .). There are strict conventions for setting these up: ** All enums start with an "unknown" value, with a name ending in "Unknown" or "_unknown", the numeric value of which is 0 ** The numeric values of the enum values are set implicitly by the compiler to successive integers (which are commonly documented in the header files for debugging purposes), rather than explicitly set by hand in the code (with a small fixed set of special exceptions, like airEndian) ** All enums end with a flag "last" value, with a name ending in "Last" or "_last"; (why: simple way to test bounds on valid values) ** None of the Teem enums are typecast to something distinct- the values are still just of type "int". Thus, to collectively refer to the {fooBarUnknown, fooBarA, fooBarB, fooBarC . . .} enum values, we use "fooBar*". Not using a typecast is a loss of opportunity for some kinds of type checking, but is otherwise a big win for simplicity- airEnums (also widely used in Teem) map between strings and ints, and can do so generally because the ints are not typed as something that will be different for every enum. Also, in many cases, which enum is in play is known only at run-time (e.g. the listing of of gageItems in a gage query in a volume for which the gageKind is known only at run-time). * All public airEnums are registered in the "meet" library. If you add a new airEnum, you need to also register it in the meetAirEnumAll() function in teem/src/meet/enumall.c. Also, all public airEnums should be declared as const airEnum *const enumName; * NrrdKernels are structs that package information about a convolution kernel and its properties. So far all the NrrdKernels are supplied by the Nrrd library, but that could change. All NrrdKernels are registered in the "meet" library, in the meetNrrdKernelAll() function in teem/src/meet/meetNrrd.c. * Whenever there is an lookup-table array (created at compile time) to accompany the values in a C enum (e.g. nrrdTypeIsIntegral[] array indexed by values from nrrdType* enum): ** initialize only one value per line ** include with each line a comments that names the enum value (e.g. nrrdTypeFloat) for which this is the value (why: then it becomes trivial to use grep/ack to find things that need to be updated when the set of values in the enum is changed) * Anything that behaves and is used like a bool should be type "int". Teem coders are expected to know that in C non-zero IS true, and zero IS false, so for example "if (!ptr) { errorhandle(); }" is the preferred way to call errorhandle() if pointer ptr is NULL. Thanks to the convention of starting enums with an "unknown" value, the unknown value is 0 (false), and all legit values are non-zero (true), and Teem code often assumes this. * A slightly unusual idiom that is used in Teem is using an int "E" (or preferrably "EE") to store error code, and using "if (!E) E |= " to stop execution as soon as there is an error, e.g.: E = 0; if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, nrrdDefaultCenter); if (!E) E |= nrrdResampleNrrdSet(rsmc, nin); if (!E) E |= nrrdResampleBoundarySet(rsmc, sbp->boundary); if (!E) E |= nrrdResampleTypeOutSet(rsmc, nrrdTypeDefault); if (!E) E |= nrrdResampleRenormalizeSet(rsmc, sbp->renormalize); if (E) { . . . biff handling; } Another idiom to accomplish the same thing in this case is: if (nrrdResampleDefaultCenterSet(rsmc, nrrdDefaultCenter) || nrrdResampleNrrdSet(rsmc, nin) || nrrdResampleBoundarySet(rsmc, sbp->boundary) || nrrdResampleTypeOutSet(rsmc, nrrdTypeDefault) || nrrdResampleRenormalizeSet(rsmc, sbp->renormalize)) { . . . biff handling; } The first idiom is more useful in situations where there are other statements (which may not have a return value, or have a different way of indicating error) that have to be interleaved in, which can also be prefixed with "if (!E)", as in bin/pprobe.c if (!E) E |= !(pvlSS = AIR_CAST(gagePerVolume **, calloc(numSS, sizeof(gagePerVolume *)))); if (!E) airMopAdd(mop, pvlSS, (airMopper)airFree, airMopAlways); if (!E) E |= gageStackPerVolumeNew(ctx, pvlSS, AIR_CAST(const Nrrd**, ninSS), numSS, kind); if (!E) E |= gageStackPerVolumeAttach(ctx, pvl, pvlSS, sbp->scale, numSS); if (!E) E |= gageKernelSet(ctx, gageKernelStack, kSS->kernel, kSS->parm); * Everywhere except air and hest, use the biff library for registering error messages. The annoyances of biff (currently not thread-safe to use, limited to textual descriptions with no additional numeric codes) are outweighed by the value of providing rich information about the context and circumstances of errors. The main function to use is biffAddf(LIBNAME, "%s: error message", me); where me identifies what function you're in: static const char me[]="functionName"; The error messages passed to biff shouldn't contain newlines. It is standard, at the beginning of a function, to have a long list of checks and tests, all of which might fail with a biff error message, before getting on with the main purpose of the function. Think of these as asserts() but more graceful and more informative. It not a problem for the error checking to take up more space than the core functionality. * Teem uses size_t-type variables quite a bit, and also ptrdiff_t (at least in nrrdPad_nva and nrrdPad_va). Sometimes these values have to passed {s,f,}printf, but ANSI C89 has no "format specifiers" for that (as with %d for int, %u for unsigned int). Teem has previously used macros _AIR_SIZE_T_CNV and _AIR_PTRDIFF_T_CNV for this, but setting these is a complicated compile-time task that is completely architecture-dependent, and which also relied on TEEM_32BIT, which has been removed. Ultimately, the nicest thing to do (but which will require significant work) would be to re-implement {s,f,}printf (or copy an implementation with suitable licensing). Until then, we use airSprintSize_t and airSprintPtrdiff_t to sprint a single size_t or ptrdiff_t variable to a buffer string (typically char stmp[AIR_STRLEN_SMALL]) which is then handled via %s. This is admittedly clumsy; it will hopefully be one of the first things to fix in Teem 2.0. ============ Other coding practices * Avoid single-letter variable names (why: too hard to search for usage) "uu" is better than "u". * In equality tests between a variable and a constant, the constant goes first, e.g. use "if (42 == blah) " instead of "if (blah == 42)" (why: eliminates chance of assignment (by =) instead of equality test). * If a pointer argument to a function can be const, make it const. In upcoming versions this may be applied to non-pointer arguments as well. * If something is logically an unsigned quantity (mainly, indices into arrays, or quantities of things), then make it unsigned. Use size_t for anything that might be generated by sizeof(), or is logically equivalent to what sizeof() is describing * Avoid magic constants embedded in code, use instead either a #define or one of the many enum values * Spell check comments * In the common circumstance that there are two variables, a float or double which represents a continuous quantity, and an int or unsigned int that represents a discretized version of the same, the two variable names should start the same, but the discrete one ends with "i" or "Idx", e.g. "uu" vs. "uuIdx", or "uu" vs. "uui" * Try to avoid making local copies of variables in structs, just for the sake of code brevity when those variables won't change during the scope of the function. Hopefully the struct is const, so the compiler can do the right thing. * Use "return" correctly: no parens! "exit()" does have parens. * If a pointer should be initialized to NULL, then set it to NULL; Don't assume that a pointer in a struct will be NULL following a calloc. * Parts of the GNU style (http://www.gnu.org/prep/standards.html) which have been more or less followed in Teem: - avoid arbitrary limits on (memory) sizes of things (this is very hard) - be robust about handling of non-ASCII input where ASCII is expected - be super careful about handling of erroneous system call return, and always err on the side of being paranoid/defensive in matters of error detection and reporting. - check every single malloc/calloc for NULL return - for expressions split on multiple lines, split before an operator, not after - use parens on multi-line expressions to make them tidy - Don't over-use assignments inside if-conditional, if doing so be sure to put an extra set of parens around it to highlight what's going on. teem-1.11.0~svn6057/src/hex/0000775000175000017500000000000012203513755015163 5ustar domibeldomibelteem-1.11.0~svn6057/src/hex/enhex.c0000664000175000017500000000616412165631065016447 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* ** enhex: simple stand-alone hex encoder ** ** Compile with: ** cc -o enhex enhex.c */ #include #include #include #include #ifdef _WIN32 #include #include #endif int enhexColumns = 70; /* number of characters per line */ void enhexUsage(char *me) { /* 0 1 2 (2/3) */ fprintf(stderr, "usage: %s []\n", me); fprintf(stderr, " : file to read raw data from\n"); fprintf(stderr, ": file to write hex data to; " "uses stdout by default\n"); fprintf(stderr, " \"-\" can be used to refer to stdin/stdout\n"); exit(1); } void enhexFclose(FILE *file) { if (!( stdin == file || stdout == file )) { fclose(file); } } int enhexTable[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; int main(int argc, char *argv[]) { char *me, *inS, *outS; FILE *fin, *fout; int car=0, col; me = argv[0]; if (!( 2 == argc || 3 == argc )) enhexUsage(me); inS = argv[1]; if (!strcmp("-", inS)) { fin = stdin; #ifdef _WIN32 _setmode(_fileno(fin), _O_BINARY); #endif } else { fin = fopen(inS, "rb"); if (!fin) { fprintf(stderr, "\n%s: couldn't fopen(\"%s\",\"rb\"): %s\n\n", me, inS, strerror(errno)); enhexUsage(me); } } if (2 == argc) { fout = stdout; } else { outS = argv[2]; if (!strcmp("-", outS)) { fout = stdout; } else { fout = fopen(outS, "w"); if (!fout) { fprintf(stderr, "\n%s: couldn't fopen(\"%s\",\"w\"): %s\n\n", me, outS, strerror(errno)); enhexUsage(me); } } } col = 0; car = fgetc(fin); while (EOF != car) { if (col > enhexColumns) { fprintf(fout, "\n"); col = 0; } fprintf(fout, "%c%c", enhexTable[car>>4], enhexTable[car&15]); col += 2; car = fgetc(fin); } if (2 != col) { fprintf(fout, "\n"); } enhexFclose(fin); enhexFclose(fout); exit(0); } teem-1.11.0~svn6057/src/hex/dehex.c0000664000175000017500000001002312165631065016422 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* ** dehex: simple stand-alone hex decoder ** ** Compile with: ** cc -o dehex dehex.c */ #include #include #include #include #ifdef _WIN32 #include #include #endif void dehexUsage(char *me) { /* 0 1 2 (2/3) */ fprintf(stderr, "usage: %s []\n", me); fprintf(stderr, " : file to read hex data from\n"); fprintf(stderr, ": file to write raw data to; " "uses stdout by default\n"); fprintf(stderr, " \"-\" can be used to refer to stdin/stdout\n"); exit(1); } void dehexFclose(FILE *file) { if (!( stdin == file || stdout == file )) { fclose(file); } } int dehexTable[128] = { /* 0 1 2 3 4 5 6 7 8 9 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, /* 0 */ -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, /* 10 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 20 */ -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, /* 30 */ -2, -2, -2, -2, -2, -2, -2, -2, 0, 1, /* 40 */ 2, 3, 4, 5, 6, 7, 8, 9, -2, -2, /* 50 */ -2, -2, -2, -2, -2, 10, 11, 12, 13, 14, /* 60 */ 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 70 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 80 */ -2, -2, -2, -2, -2, -2, -2, 10, 11, 12, /* 90 */ 13, 14, 15, -2, -2, -2, -2, -2, -2, -2, /* 100 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 110 */ -2, -2, -2, -2, -2, -2, -2, -2 /* 120 */ }; int main(int argc, char *argv[]) { char *me, *inS, *outS; FILE *fin, *fout; int car=0, byte, nibble, even; me = argv[0]; if (!( 2 == argc || 3 == argc )) dehexUsage(me); inS = argv[1]; if (!strcmp("-", inS)) { fin = stdin; } else { fin = fopen(inS, "r"); if (!fin) { fprintf(stderr, "\n%s: couldn't fopen(\"%s\",\"rb\"): %s\n\n", me, inS, strerror(errno)); dehexUsage(me); } } if (2 == argc) { fout = stdout; } else { outS = argv[2]; if (!strcmp("-", outS)) { fout = stdout; #ifdef _WIN32 _setmode(_fileno(fout), _O_BINARY); #endif } else { fout = fopen(outS, "w"); if (!fout) { fprintf(stderr, "\n%s: couldn't fopen(\"%s\",\"w\"): %s\n\n", me, outS, strerror(errno)); dehexUsage(me); } } } byte = 0; even = 1; for (car=fgetc(fin); EOF != car; car=fgetc(fin)) { nibble = dehexTable[car & 127]; if (-2 == nibble) { /* its an invalid character */ break; } if (-1 == nibble) { /* its white space */ continue; } if (even) { byte = nibble << 4; } else { byte += nibble; if (EOF == fputc(byte, fout)) { fprintf(stderr, "%s: error writing!!!\n", me); exit(1); } } even = 1 - even; } if (EOF != car) { fprintf(stderr, "\n%s: got invalid character '%c'\n\n", me, car); dehexUsage(me); } dehexFclose(fin); dehexFclose(fout); exit(0); } teem-1.11.0~svn6057/src/hex/README.txt0000664000175000017500000000361612165631065016671 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # ----------- enhex/dehex ----------- enhex and dehex are a stand-alone hex encoder/decoder pair. They convert between data in raw form (paying no regard to the endianness), and convert it to hexadecimal form. To compile: cc -o enhex enhex.c cc -o dehex dehex.c I wrote these programs for doing the hex encoding which is an optional encoding in the nrrd library in Teem. The (convoluted) reason is that the only encodings that are *required* of non-Teem nrrd readers and writers are raw and ascii. All other encodings should be able to be handled by stand-alone tools (such as gzip/gunzip for zlib compression, and bzip/bunzip2 for bzip compress). Yet, a google search didn't reveal simple tools for raw--hex conversion, so I wrote them. Nothing fancy. Gordon Kindlmann teem-1.11.0~svn6057/src/hex/CMakeLists.txt0000664000175000017500000000230312165631065017723 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # ADD_EXECUTABLE(dehex dehex.c ) ADD_EXECUTABLE(enhex enhex.c ) teem-1.11.0~svn6057/src/matlab/0000775000175000017500000000000012203513756015640 5ustar domibeldomibelteem-1.11.0~svn6057/src/matlab/nrrdLoad.c0000664000175000017500000000754612165631065017566 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mex.h" #include mxClassID typeNtoM(int ntype) { mxClassID mtype; switch(ntype) { case nrrdTypeChar: mtype = mxINT8_CLASS; break; case nrrdTypeUChar: mtype = mxUINT8_CLASS; break; case nrrdTypeShort: mtype = mxINT16_CLASS; break; case nrrdTypeUShort: mtype = mxUINT16_CLASS; break; case nrrdTypeInt: mtype = mxINT32_CLASS; break; case nrrdTypeUInt: mtype = mxUINT32_CLASS; break; case nrrdTypeLLong: mtype = mxINT64_CLASS; break; case nrrdTypeULLong: mtype = mxUINT64_CLASS; break; case nrrdTypeFloat: mtype = mxSINGLE_CLASS; break; case nrrdTypeDouble: mtype = mxDOUBLE_CLASS; break; default: mtype = mxUNKNOWN_CLASS; break; } return mtype; } void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char me[]="nrrdLoad", *filename, *errPtr, errBuff[AIR_STRLEN_MED]; int filenameLen, sizeI[NRRD_DIM_MAX]; mxClassID mtype; size_t sizeZ[NRRD_DIM_MAX]; unsigned int axIdx; Nrrd *nrrd; NrrdIoState *nio; airArray *mop; if (!(1 == nrhs && mxIsChar(prhs[0]))) { sprintf(errBuff, "%s: requires one string argument (the name of the file)", me); mexErrMsgTxt(errBuff); } mop = airMopNew(); filenameLen = mxGetM(prhs[0])*mxGetN(prhs[0])+1; filename = mxCalloc(filenameLen, sizeof(mxChar)); /* managed by Matlab */ mxGetString(prhs[0], filename, filenameLen); nrrd = nrrdNew(); airMopAdd(mop, nrrd, (airMopper)nrrdNix, airMopAlways); nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); nrrdIoStateSet(nio, nrrdIoStateSkipData, AIR_TRUE); /* read header, but no data */ if (nrrdLoad(nrrd, filename, nio)) { errPtr = biffGetDone(NRRD); airMopAdd(mop, errPtr, airFree, airMopAlways); sprintf(errBuff, "%s: trouble reading NRRD header:\n%s", me, errPtr); airMopError(mop); mexErrMsgTxt(errBuff); } mtype = typeNtoM(nrrd->type); if (mxUNKNOWN_CLASS == mtype) { sprintf(errBuff, "%s: sorry, can't handle type %s (%d)", me, airEnumStr(nrrdType, nrrd->type), nrrd->type); airMopError(mop); mexErrMsgTxt(errBuff); } /* allocate matlab array based on nrrd struct */ for (axIdx=0; axIdxdim; axIdx++) { sizeI[axIdx] = nrrd->axis[axIdx].size; } plhs[0]=mxCreateNumericArray(nrrd->dim,sizeI,mtype,mxREAL); /* copy data pointer */ nrrd->data = mxGetPr(plhs[0]); /* read second time, now loading data */ if (nrrdLoad(nrrd, filename, NULL)) { errPtr = biffGetDone(NRRD); airMopAdd(mop, errPtr, airFree, airMopAlways); sprintf(errBuff, "%s: trouble reading NRRD:\n%s", me, errPtr); airMopError(mop); mexErrMsgTxt(errBuff); } airMopOkay(mop); return; } teem-1.11.0~svn6057/src/matlab/compilethis.m0000664000175000017500000000053310314412101020316 0ustar domibeldomibelmex nrrdLoad.c -I/projects/lmi/people/gk/teem/include -L/projects/lmi/people/gk/teem/solaris/lib -lteem -lz -lm mex nrrdLoadOrientation.c -I/projects/lmi/people/gk/teem/include -L/projects/lmi/people/gk/teem/solaris/lib -lteem -lz -lm mex nrrdSave.c -I/projects/lmi/people/gk/teem/include -L/projects/lmi/people/gk/teem/solaris/lib -lteem -lz -lm teem-1.11.0~svn6057/src/matlab/nrrdSave.c0000664000175000017500000000717612165631065017604 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mex.h" #include int typeMtoN(mxClassID mtype) { int ntype; switch(mtype) { case mxINT8_CLASS: ntype = nrrdTypeChar; break; case mxUINT8_CLASS: ntype = nrrdTypeUChar; break; case mxINT16_CLASS: ntype = nrrdTypeShort; break; case mxUINT16_CLASS: ntype = nrrdTypeUShort; break; case mxINT32_CLASS: ntype = nrrdTypeInt; break; case mxUINT32_CLASS: ntype = nrrdTypeUInt; break; case mxINT64_CLASS: ntype = nrrdTypeLLong; break; case mxUINT64_CLASS: ntype = nrrdTypeULLong; break; case mxSINGLE_CLASS: ntype = nrrdTypeFloat; break; case mxDOUBLE_CLASS: ntype = nrrdTypeDouble; break; default: ntype = nrrdTypeUnknown; break; } return ntype; } void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char me[]="nrrdSave", *filename, *errPtr, errBuff[AIR_STRLEN_MED]; int filenameLen, ntype; size_t sizeZ[NRRD_DIM_MAX]; unsigned int dim, axIdx; Nrrd *nrrd; airArray *mop; const mxArray *filenameMx, *arrayMx; if (!(2 == nrhs && mxIsChar(prhs[0]) )) { sprintf(errBuff, "%s: requires two args: one string, one array", me); mexErrMsgTxt(errBuff); } filenameMx = prhs[0]; arrayMx = prhs[1]; if (mxIsComplex(arrayMx)) { sprintf(errBuff, "%s: sorry, array must be real", me); mexErrMsgTxt(errBuff); } ntype = typeMtoN(mxGetClassID(arrayMx)); if (nrrdTypeUnknown == ntype) { sprintf(errBuff, "%s: sorry, can't handle type %s", me, mxGetClassName(arrayMx)); mexErrMsgTxt(errBuff); } dim = mxGetNumberOfDimensions(arrayMx); if (!( 1 <= dim && dim <= NRRD_DIM_MAX )) { sprintf(errBuff, "%s: number of array dimensions %d outside range [1,%d]", me, dim, NRRD_DIM_MAX); mexErrMsgTxt(errBuff); } filenameLen = mxGetM(filenameMx)*mxGetN(filenameMx)+1; filename = mxCalloc(filenameLen, sizeof(mxChar)); /* managed by Matlab */ mxGetString(filenameMx, filename, filenameLen); for (axIdx=0; axIdx void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char me[]="nrrdLoadOrientation", *filename, *errPtr, errBuff[AIR_STRLEN_MED]; int filenameLen, sizeI[NRRD_DIM_MAX]; mxClassID mtype; size_t sizeZ[NRRD_DIM_MAX]; unsigned int axIdx; Nrrd *nrrd; NrrdIoState *nio; airArray *mop; unsigned int domainAxisNum, domainAxisIdx[NRRD_DIM_MAX], axisIdx, rowIdx, colIdx; double spacing, spaceDir[NRRD_SPACE_DIM_MAX]; int spacingStatus; if (!(1 == nrhs && mxIsChar(prhs[0]))) { sprintf(errBuff, "%s: requires one string argument (the name of the file)", me); mexErrMsgTxt(errBuff); } mop = airMopNew(); filenameLen = mxGetM(prhs[0])*mxGetN(prhs[0])+1; filename = mxCalloc(filenameLen, sizeof(mxChar)); /* managed by Matlab */ mxGetString(prhs[0], filename, filenameLen); nrrd = nrrdNew(); airMopAdd(mop, nrrd, (airMopper)nrrdNix, airMopAlways); nio = nrrdIoStateNew(); airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); nrrdIoStateSet(nio, nrrdIoStateSkipData, AIR_TRUE); /* read header, but no data */ if (nrrdLoad(nrrd, filename, nio)) { errPtr = biffGetDone(NRRD); airMopAdd(mop, errPtr, airFree, airMopAlways); sprintf(errBuff, "%s: trouble reading NRRD header:\n%s", me, errPtr); airMopError(mop); mexErrMsgTxt(errBuff); } domainAxisNum = nrrdDomainAxesGet(nrrd, domainAxisIdx); plhs[0] = mxCreateDoubleMatrix(domainAxisNum /* # rows */, nrrd->dim /* # cols */, mxREAL); for (colIdx=0; colIdxdim; colIdx++) { spacingStatus = nrrdSpacingCalculate(nrrd, domainAxisIdx[colIdx], &spacing, spaceDir); switch(spacingStatus) { case nrrdSpacingStatusNone: for (rowIdx=0; rowIdxaxis[colIdx].spaceDirection[rowIdx]; } break; case nrrdSpacingStatusUnknown: sprintf(errBuff, "%s: error interpreting axis %u spacing " "(nrrdSpacingStatusUnknown)", me, colIdx); airMopError(mop); mexErrMsgTxt(errBuff); break; case nrrdSpacingStatusScalarWithSpace: sprintf(errBuff, "%s: error interpreting axis %u spacing " "(nrrdSpacingScalarWithSpace)", me, colIdx); airMopError(mop); mexErrMsgTxt(errBuff); break; } } airMopOkay(mop); return; } teem-1.11.0~svn6057/src/matlab/README.txt0000664000175000017500000000126110314413103017320 0ustar domibeldomibelThis is an initial implementation of a matlab reader and writer for NRRD files, using the mex compiler to link against the Teem library, written by Steven Haker and Gordon Kindlmann. The .c files are compiled by mex according to the commands in compilethis.m, except that you have to edit the -I and -L lines according to wherever your Teem got built. Suggestions welcome. Once these are compiled, you should be able to (in matlab) >> x = nrrdLoad('blah.nrrd'); >> nrrdSave('out.nrrd', x); Work continues on a matlab-idiomatic way of representing all the other meta-information that makes a nrrd something more than a matlab array. Gordon Kindlmann gk at bwh punto harvard punto edu teem-1.11.0~svn6057/src/TODO.txt0000664000175000017500000003257412202620747015717 0ustar domibeldomibel ======== for Teem 2.0: decide on a uniform way of indicating if a given external is available (e.g. airThreadCapable vs nrrdFFTWEnabled vs #if TEEM_LEVMAR: not uniform). "meet" is the logical place for collecting this info, and would be useful for Teem-using apps to have an API for this. Libraries within Teem can't use "meet", but there should at least be consistent naming conventions. Consider doing away with "experimental" libraries and applications: its annoying to have two totally different kinds of Teem installs, especially when it means that it would mean two different python wrappings. Consider inserting SVN revision numbers in code itself: http://addisu.taddese.com/blog/inserting-svn-revision-number-in-your-cc-code/ Consider extending const-correctness of function input not just to pointers, but to all values as well. all: * scrutinize use of strncpy, consider using airStrcpy * remove '_'s from enum value names in non-elf non-tijk libraries * decide if _EXPORT should just be TEEM_EXPORT * enforce consistent use of AIR_CALLOC/AIR_MALLOC air: * consider nixing AIR_ENDIAN, AIR_QNANHIBIT, and AIR_DIO * airArray->len should probably be size_t, not unsigned int. Along with this is biffMsgErrNum and nrrdKeyValueSize. * consider changing airIndexULL and airIndexClampULL to work with size_t instead of unsigned long long, or at least adding versions that work with size_t * either change AIR_STRLEN_* to AIR_BUFFSIZE_* (or something similar, or take out the +1 from their definition and it into usage: the STRLEN is misleading as is. * important: change airArray implementation to re-allocate the array size multiplicatively (by some adjustable factor, default around 1.5 or 2.0), rather than the linear increments used now. References: http://en.wikipedia.org/wiki/Dynamic_array http://hg.python.org/cpython/file/e3be2941c834/Objects/listobject.c * consider terminating airEnum->strEqv with NULL (just like argv) instead of with empty string "" * reconcile names of "unsigned int airUIrandMT_r()" vs "unsigned int airRandInt()" and "unsigned int airRandInt_r()" * consider having something like a teemContext, which might be: - the destination of thread-safe generating of error messages - a place for doing progress indication in multi-threaded callers - uniform place for indicating desired verbosity of complex functions - storage for other things that are current globals. The problem is that such a thing would have to be an argument to a great many functions (or else it itself would be a global), and then how do you know which functions should take it? hest: * hest NEEDS a way to learn if a given option was learned from the command-line, or from the given default string. Various tricks have been used to work around this limitation, but its gotten too annoying. * consider renaming hestParmFree --> hestParmNix * all the hest defaults (hestVerbosity, hestRespFileEnable), etc, should start with hestDef, not just hest * air/hest: remove the airType* enum. The following sort of code looks correct but is completely wrong if (nrrdAlloc(nout, airTypeFloat, 3, size[0], size[1], size[2])) { ... because "airTypeFloat" is there instead of "nrrdTypeFloat". All things airType were created for the sake of hest. Instead of taking an airType enum value for hestOptAdd, hestOptAdd can take a string (e.g. "uchar", "float", "enum", "callback" for airTypeOther). biff: * biffMove(destKey, err, srcKey) --> biffMove(destKey, srcKey, err) so that it matches biffMovef * rename biffCheck --> biffErrNum * consider biffGetStrlen --> biffStrlenGet, biffSetStr --> biffStrSet * a few biff functions are never used in Teem, is that ok? nrrd: * ponder utility of having "const void *constdata" field in the Nrrd struct, to allow a nrrd to wrap around data it doesn't own. Would this supersede the nrrdNuke vs nrrdNix distinction? * nrrdKeyValueGet() is problematic: it either returns a pointer internal to nrrd, which the user had better not free, or something newly allocated. This completely goes against (at least the spirit of) const-correctness, even though nrrdKeyValueGet() takes a const Nrrd *nrrd. Consider breaking this into const-returning nrrdKeyValuePointer() and allocate-on-return nrrdKeyValueDup(), or some other way of making that distinction * int nrrdAxisInfoCopy(Nrrd *nout, const Nrrd *nin, const int *axmap, int excludeBitflag) presents a problem for the principle that axis indices should be unsigned, because axmap[i]==-1 means something special. Should this be changed to axmap[i]==UINT_MAX? * resolve basic confusion between unsigned and signed int with return of nrrdIoStateGet (now int, including for learning nio->charsPerLine and nio->valsPerLine, which are unsigned int, just like nrrdDefaultWriteCharsPerLine and nrrdDefaultWriteValsPerLine) and argument to nrrdIoStateSet. * with NrrdKernel: - debug apparent precision problems worked-around in nrrdKernelCheck - add fields for "what is my derivative" or "what is my integral", but how to do that at compile time? - add field for "parm[0] controls the scaling that the old resampling code (e.g. unu resample) has always assumed" vs "parm[0] isn't used". The assumption by the resampling code that all kernels use parm[0] was a big longstanding bug; "bspln3" doesn't work that way - consider nixing nrrdDefaultKernelParm0, nrrdEnvVarDefaultKernelParm0 - "when parsing from string, parm[0] is needed" vs "parm[0] defaults to 1.0" OR, maybe simplify things by saying that all parameters are always needed - add method for answering "do I interpolate?" - add fields for "if I don't interpolate, what's my approximate inverse?" - add statement of accuracy (in a Taylor series sense) - add: am I an odd or even function - rename numParm --> parmNum - fix the kernels that meetNrrdKernelAllCheck revealed to be broken * with NrrdFormat: - There is now an available() method, but this is incomplete - EPS is something that can be written for images, but not read in general. Should there be more ways of describing its support? - supposing a general airBuffer utility is not implemented, there may be formats that can be read from disk (fseek-able) but not from stdin. Is this something else that NrrdFormat should represent? * should re-evaluate the need for all the nrrdDefault and nrrdState global variables, especially nrrdStateGrayscaleImage3D, nrrdEnvVarStateGrayscaleImage3D, nrrdDefaultSpacing, nrrdEnvVarDefaultSpacing, which seem antiquated * the percentile-based bounds specification now supported in e.g "unu histo", "unu quantize", and "unu jhisto" should be migrated down to the nrrd library. Consider doing this with a sorting of highest/lowest values rather than a histogram (one less parameter, and often more accurate) * nrrdSpaceVecNorm(unsigned int sdim, const double vec[]) should have its two args switch order in order to conform to Teem convention of "array first, length second" * revisit how FFTW is called and used. How have others dealt with the weird global nature of reading/writing wisdom? Should "rigor" be an argument to nrrdFFT? Is there really no error reporting on fftw_execute, fftw_export_wisdom_to_file? * make the verbosity level a field in NrrdIoState, remove nrrdStateVerboseIO * add support for 16-bit float "half" (start with Milan's patch) * rename nrrd_nva() --> nrrd() * rename nrrdResampleNrrdSet --> nrrdResampleInputSet ? * permit replacements for malloc/free (e.g. fftw_malloc, fftw_free) of data * rename NRRD_KERNEL_PARMS_NUM --> NRRD_KERNEL_PARM_MAXNUM * remove "minsm" as synonym for nrrdTernaryOpMinSmooth * resampler: why can't you set a kernel prior to setting input? [nrrd] nrrdDeringExecute: trouble on setup [nrrd] deringPtxfAlloc: couldn't set up resampler [nrrd] nrrdResampleKernelSet: haven't set input nrrd yet * for nrrdCropAuto: consider adding some minimum remaining size requirement, in addition to offset * all of the code in apply1D.c is ancient, and has not been brought into the world of using unsigned int and size_t. Also very confusing. Needs to be walked through and tested thoroughly. * why is _nrrdCheck (different than nrrdCheck) public? Should be renamed? unu: * the UNRRDU_QUIET_QUIT functionality should be enabled by default instead of having to set an environment variable to get it. Or maybe not: when this goes wrong (and unfortunately GLK can't find a good test case now) it is REALLY mysterious, and hard to debug. * unu dice -ff should change from using %d to %u * unu resample: -- consider supporting more sophisticated expressions e.g. "x2" --> "x2+1" -- reconsider whether normalization should be turned on by default (always a surprise when using purposely small kernels like cos4sup or a small Gaussian) gage: * clean up gageVolumeCheck vs gageKindVolumeCheck mess * in a gageKinds, the airEnum for the items, and the associated table of pre-requisites, should really be compiled from some other description which is more reliably written and upated (updating a gageKind now is EXTREMELY error-prone). More radically, perhaps the entire thing can be created at run-time, and perhaps there could be per-item function. Should investigate if calling per-item functions is faster than the bizarrely-long chain of if{}s that currently control item computation. ten: * tend helix and tend satin should not generate physically implausible diffusivity values (i.e. not exceeding 0.003 mm^2/sec) dye: * remove it, assuming its smarts have been copied into nrrd push: * remove it, assuming its smarts have been moved into pull ======== ** The items below have accumulated over time, but the importance of them ** for Teem 2.0 or any other release should be re-evaluated. large-scale fix: when using sizeof with memcpy or memset, should be using the variable name itself instead of type as argument to sizeof() for cmake: - build pv on windows - see if wild-card expansion works as expected on windows - make cmd-line utilities link with static libs unrrdu: standardize hest framework for doing unu/gkms/tend style programs [portable54-250:~/d/parepi/6] gk% tend estim -new -sigma 0.01 -est wls -i 6crop-dwi.nrrd \ -B kvp -knownB0 false -t 250 -o tmp.nrrd 0.0%tend estim: trouble doing estimation: [ten] tenEstimate1TensorVolume4D: failed at sample 40 [ten] tenEstimate1TensorSingle_d: [ten] _tenEstimate1TensorSingle: estimation failed [ten] _tenEstimate1Tensor_WLS: trying to improve on first WLS [ten] _tenEstimate1TensorSimulateSingle gage: re-modularize to facilitate probing bricked data make system really botched: change the size of the tenFiberContext (added a field or two), do a make ten/install, then cd ../push; rm -f test/pusher air: make airOneLine return number of bytes read; current return of string length is entirely redundant with return of strlen() and then enable the ftell() check after PNG magic read bin/unu: valgrid parsing of encoding stuff gage changes into bane: - try gkms hvol with three explicit ranges - remove excess NULL pointer checks between answer and answer wrapper - valgrind - ADD gkms back into teem bins ell: debug SVD of things based on negative eigenvalues - add flag to say: always positive sv's, or always right-handed rotations leaf: do it hest: add commenting via # or something else bane: finish updating tutorial dye: see if dyeColorParse should allocate the thing and return it image registration tool for small translational errors limn: make it smarter- so that joining parts together is possible in a way that facilitates transformations and correct drawing. This really requires general data structures for 2-D graphics primatives... limn: either debug or remove limnQN16border1 dye: colormaps dye: hest callbacks air: think about implementing a fabs() and dabs() with bitmasking write a paper about nrrd! include a list of published papers using teem: Kindlmann: Superquadric Tensor Glyphs Kindlmann Vis03: Curvature-Based Transfer Functions Lefohn Vis03: Interactive Deformation and Visualization of Level Set Surfaces Using Graphics Hardware Lefohn TVCG July/August 04: A Streaming Narrow-Band Algorithm: Interactive Computation and Visualization of Level Sets Kniss VisSym04: Medical Applications of Multi-field Volume Rendering and VR Techniques Deschamps TVCG04: Fast Evolution of Image Manifolds and Application to Filtering and Segmentation in 3D Medical Images ikits Vis03: A Constraint-based Technique for Haptic Volume Exploration fout Eurovis 05: High-Quality Rendering of Compressed Volume Data Formats callahan TVCG May/June 05: Hardware-Assisted Visibility Sorting for Unstructured Volume Rendering jorik blaas vis05 fiber paper (less of an issue now that CMake is in use) make install; make; and you'll STILL get memory errors due to seeing library/object files which use the older context size. Do a top-level make clobber, and then things work. This is crap. I think the problem is that development object files of push were not recompiled when they should have been- doing a make clobber; make in push solved the problem... Examples of this: - change limn.h, cd ../ten, make, nothing to be done WRONG - make a change in nrrdEnums.h, which unrrdu/project.c uses directly. recompile, and nrrd gets compiled, but not unrrdu, so unu doesn't get updated correctly! - add a macro in ELL, make install in ell, cd limn, make ../limn/test/tiso, doesn't see new header and fails at link-time with " symbol undefined" - want seperate directories for static and shared libraries - want bin/dev and bin/install targets teem-1.11.0~svn6057/src/ell/0000775000175000017500000000000012203513760015147 5ustar domibeldomibelteem-1.11.0~svn6057/src/ell/miscEll.c0000664000175000017500000000765612165631065016727 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ell.h" /* ** we use the name ellPresent (even though ell_present would be ** properly consistent with the ell library name convention) because ** we want to facilitate systematic testing of all libraries */ const int ellPresent = 42; const char * ell_biff_key = "ell"; /* ******** ell_debug ** ** some functions may use this value to control printing of ** verbose debugging information */ int ell_debug = 0; const char * _ell_cubic_root_str[] = { "(unknown ell_cubic_root)", "single", "triple", "single and double", "three distinct" }; const char * _ell_cubic_root_desc[] = { "(unknown ell_cubic_root)", "one single root", "one triple root", "a single and a double root", "three distinct roots" }; airEnum _ell_cubic_root = { "cubic root solutions", ELL_CUBIC_ROOT_MAX, _ell_cubic_root_str, NULL, _ell_cubic_root_desc, NULL, NULL, AIR_FALSE }; const airEnum *const ell_cubic_root = &_ell_cubic_root; void ell_3m_print_f(FILE *f, const float s[9]) { fprintf(f, "% 15.7f % 15.7f % 15.7f\n", s[0], s[1], s[2]); fprintf(f, "% 15.7f % 15.7f % 15.7f\n", s[3], s[4], s[5]); fprintf(f, "% 15.7f % 15.7f % 15.7f\n", s[6], s[7], s[8]); } void ell_3v_print_f(FILE *f, const float s[3]) { fprintf(f, "% 15.7f % 15.7f % 15.7f\n", s[0], s[1], s[2]); } void ell_3m_print_d(FILE *f, const double s[9]) { fprintf(f, "% 31.15f % 31.15f % 31.15f\n", s[0], s[1], s[2]); fprintf(f, "% 31.15f % 31.15f % 31.15f\n", s[3], s[4], s[5]); fprintf(f, "% 31.15f % 31.15f % 31.15f\n", s[6], s[7], s[8]); } void ell_3v_print_d(FILE *f, const double s[3]) { fprintf(f, "% 31.15f % 31.15f % 31.15f\n", s[0], s[1], s[2]); } void ell_4m_print_f(FILE *f, const float s[16]) { fprintf(f, "% 15.7f % 15.7f % 15.7f % 15.7f\n", s[ 0], s[ 1], s[ 2], s[ 3]); fprintf(f, "% 15.7f % 15.7f % 15.7f % 15.7f\n", s[ 4], s[ 5], s[ 6], s[ 7]); fprintf(f, "% 15.7f % 15.7f % 15.7f % 15.7f\n", s[ 8], s[ 9], s[10], s[11]); fprintf(f, "% 15.7f % 15.7f % 15.7f % 15.7f\n", s[12], s[13], s[14], s[15]); } void ell_4v_print_f(FILE *f, const float s[4]) { fprintf(f, "% 15.7f % 15.7f % 15.7f % 15.7f\n", s[0], s[1], s[2], s[3]); } void ell_4m_print_d(FILE *f, const double s[16]) { fprintf(f, "% 31.15f % 31.15f % 31.15f % 31.15f\n", s[ 0], s[ 1], s[ 2], s[ 3]); fprintf(f, "% 31.15f % 31.15f % 31.15f % 31.15f\n", s[ 4], s[ 5], s[ 6], s[ 7]); fprintf(f, "% 31.15f % 31.15f % 31.15f % 31.15f\n", s[ 8], s[ 9], s[10], s[11]); fprintf(f, "% 31.15f % 31.15f % 31.15f % 31.15f\n", s[12], s[13], s[14], s[15]); } void ell_4v_print_d(FILE *f, const double s[4]) { fprintf(f, "% 31.15f % 31.15f % 31.15f % 31.15f\n", s[0], s[1], s[2], s[3]); } teem-1.11.0~svn6057/src/ell/genmat.c0000664000175000017500000002613112165631065016577 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ell.h" int ell_Nm_check(Nrrd *mat, int doNrrdCheck) { static const char me[]="ell_Nm_check"; if (doNrrdCheck) { if (nrrdCheck(mat)) { biffMovef(ELL, NRRD, "%s: basic nrrd validity check failed", me); return 1; } } else { if (!mat) { biffAddf(ELL, "%s: got NULL pointer", me); return 1; } } if (!( 2 == mat->dim )) { biffAddf(ELL, "%s: nrrd must be 2-D (not %d-D)", me, mat->dim); return 1; } if (!( nrrdTypeDouble == mat->type )) { biffAddf(ELL, "%s: nrrd must be type %s (not %s)", me, airEnumStr(nrrdType, nrrdTypeDouble), airEnumStr(nrrdType, mat->type)); return 1; } return 0; } /* ******** ell_Nm_tran ** ** M N ** N [trn] <-- M [mat] */ int ell_Nm_tran(Nrrd *ntrn, Nrrd *nmat) { static const char me[]="ell_Nm_tran"; double *mat, *trn; size_t MM, NN, mm, nn; if (!( ntrn && !ell_Nm_check(nmat, AIR_FALSE) )) { biffAddf(ELL, "%s: NULL or invalid args", me); return 1; } if (ntrn == nmat) { biffAddf(ELL, "%s: sorry, can't work in-place yet", me); return 1; } /* if (nrrdAxesSwap(ntrn, nmat, 0, 1)) { biffMovef(ELL, NRRD, "%s: trouble", me); return 1; } */ NN = nmat->axis[0].size; MM = nmat->axis[1].size; if (nrrdMaybeAlloc_va(ntrn, nrrdTypeDouble, 2, MM, NN)) { biffMovef(ELL, NRRD, "%s: trouble", me); return 1; } mat = AIR_CAST(double *, nmat->data); trn = AIR_CAST(double *, ntrn->data); for (nn=0; nnaxis[1].size; MM = nA->axis[0].size; NN = nB->axis[0].size; if (MM != nB->axis[1].size) { biffAddf(ELL, "%s: size mismatch: %s-by-%s times %s-by-%s", me, airSprintSize_t(stmp[0], LL), airSprintSize_t(stmp[1], MM), airSprintSize_t(stmp[2], nB->axis[1].size), airSprintSize_t(stmp[3], NN)); return 1; } if (nrrdMaybeAlloc_va(nAB, nrrdTypeDouble, 2, NN, LL)) { biffMovef(ELL, NRRD, "%s: trouble", me); return 1; } A = (double*)(nA->data); B = (double*)(nB->data); AB = (double*)(nAB->data); for (ll=0; ll big) { big = tmp; } } if (!big) { char stmp[AIR_STRLEN_SMALL]; biffAddf(ELL, "%s: singular matrix since column %s all zero", me, airSprintSize_t(stmp, ii)); ret = 1; goto seeya; } vv[ii] = big; } for (jj=0; jj= big) { big = tmp; imax = ii; } } /* unless we're on the imax column, swap this column the with imax column, and permute vv[] accordingly */ if (jj != imax) { /* could record parity # of permutes here */ for (kk=0; kk0; ii--) { sum = bb[ii-1]; for (jj=ii; jjaxis[0].size; if (!( NN == nmat->axis[1].size )) { char stmp[2][AIR_STRLEN_SMALL]; biffAddf(ELL, "%s: need a square matrix, not %s-by-%s", me, airSprintSize_t(stmp[0], nmat->axis[1].size), airSprintSize_t(stmp[1], NN)); return 1; } if (nrrdMaybeAlloc_va(ninv, nrrdTypeDouble, 2, NN, NN)) { biffMovef(ELL, NRRD, "%s: trouble", me); return 1; } inv = (double*)(ninv->data); mat = (double*)(nmat->data); if (_ell_inv(inv, mat, NN)) { biffAddf(ELL, "%s: trouble", me); return 1; } return 0; } /* ******** ell_Nm_pseudo_inv() ** ** determines the pseudoinverse of the given matrix M by using the formula ** P = (M^T * M)^(-1) * M^T ** ** I'll get an SVD-based solution working later, since that gives a more ** general solution */ int ell_Nm_pseudo_inv(Nrrd *ninv, Nrrd *nA) { static const char me[]="ell_Nm_pseudo_inv"; Nrrd *nAt, *nAtA, *nAtAi; int ret=0; if (!( ninv && !ell_Nm_check(nA, AIR_FALSE) )) { biffAddf(ELL, "%s: NULL or invalid args", me); return 1; } nAt = nrrdNew(); nAtA = nrrdNew(); nAtAi = nrrdNew(); if (ell_Nm_tran(nAt, nA) || ell_Nm_mul(nAtA, nAt, nA) || ell_Nm_inv(nAtAi, nAtA) || ell_Nm_mul(ninv, nAtAi, nAt)) { biffAddf(ELL, "%s: trouble", me); ret = 1; goto seeya; } seeya: nrrdNuke(nAt); nrrdNuke(nAtA); nrrdNuke(nAtAi); return ret; } /* ******** ell_Nm_wght_pseudo_inv() ** ** determines a weighted least squares solution via ** P = (A^T * W * A)^(-1) * A^T * W */ int ell_Nm_wght_pseudo_inv(Nrrd *ninv, Nrrd *nA, Nrrd *nW) { static const char me[]="ell_Nm_wght_pseudo_inv"; Nrrd *nAt, *nAtW, *nAtWA, *nAtWAi; int ret=0; if (!( ninv && !ell_Nm_check(nA, AIR_FALSE) && !ell_Nm_check(nW, AIR_FALSE) )) { biffAddf(ELL, "%s: NULL or invalid args", me); return 1; } nAt = nrrdNew(); nAtW = nrrdNew(); nAtWA = nrrdNew(); nAtWAi = nrrdNew(); if (ell_Nm_tran(nAt, nA) || ell_Nm_mul(nAtW, nAt, nW) || ell_Nm_mul(nAtWA, nAtW, nA) || ell_Nm_inv(nAtWAi, nAtWA) || ell_Nm_mul(ninv, nAtWAi, nAtW)) { biffAddf(ELL, "%s: trouble", me); ret = 1; goto seeya; } seeya: nrrdNuke(nAt); nrrdNuke(nAtW); nrrdNuke(nAtWA); nrrdNuke(nAtWAi); return ret; } teem-1.11.0~svn6057/src/ell/vecEll.c0000664000175000017500000001135512165631065016540 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ell.h" void ell_4v_norm_f(float bv[4], const float av[4]) { float len; len = AIR_CAST(float, ELL_4V_LEN(av)); ELL_4V_SCALE(bv, 1.0f/len, av); return; } #define PERP \ idx = 0; \ if (b[0]*b[0] < b[1]*b[1]) \ idx = 1; \ if (b[idx]*b[idx] < b[2]*b[2]) \ idx = 2; \ switch (idx) { \ case 0: \ ELL_3V_SET(a, b[1] - b[2], -b[0], b[0]); \ break; \ case 1: \ ELL_3V_SET(a, -b[1], b[0] - b[2], b[1]); \ break; \ case 2: \ ELL_3V_SET(a, -b[2], b[2], b[0] - b[1]); \ break; \ } /* ******** ell_3v_perp_f() ** ** Given a 3-vector, produce one which is perpendicular. ** Output length won't be same as input length, but it will always ** be non-zero, if input length is non-zero. This does NOT try to ** produce a unit-length vector, regardless of the length of the input. */ void ell_3v_perp_f(float a[3], const float b[3]) { int idx; PERP; } /* ******** ell_3v_perp_d() ** ** same as above, but for doubles */ void ell_3v_perp_d(double a[3], const double b[3]) { int idx; PERP; } void ell_3mv_mul_f(float v2[3], const float m[9], const float v1[3]) { float tmp[3]; ELL_3MV_MUL(tmp, m, v1); ELL_3V_COPY(v2, tmp); } void ell_3mv_mul_d(double v2[3], const double m[9], const double v1[3]) { double tmp[3]; ELL_3MV_MUL(tmp, m, v1); ELL_3V_COPY(v2, tmp); } void ell_4mv_mul_f(float v2[4], const float m[16], const float v1[4]) { float tmp[4]; ELL_4MV_MUL(tmp, m, v1); ELL_4V_COPY(v2, tmp); } void ell_4mv_mul_d(double v2[4], const double m[16], const double v1[4]) { double tmp[4]; ELL_4MV_MUL(tmp, m, v1); ELL_4V_COPY(v2, tmp); } /* ** hat tip to http://www.plunk.org/~hatch/rightway.php */ float ell_3v_angle_f(const float _uu[3], const float _vv[3]) { float tmp[3], len, uu[3], vv[3], ret; ELL_3V_NORM_TT(uu, float, _uu, len); ELL_3V_NORM_TT(vv, float, _vv, len); if (ELL_3V_DOT(uu, vv) < 0.0) { ELL_3V_ADD2(tmp, uu, vv); ret = AIR_CAST(float, AIR_PI - 2*asin(ELL_3V_LEN(tmp)/2.0)); } else { ELL_3V_SUB(tmp, uu, vv); ret = AIR_CAST(float, 2*asin(ELL_3V_LEN(tmp)/2.0)); } return ret; } /* HEY: copy and paste */ double ell_3v_angle_d(const double _uu[3], const double _vv[3]) { double tmp[3], len, uu[3], vv[3], ret; ELL_3V_NORM(uu, _uu, len); ELL_3V_NORM(vv, _vv, len); if (ELL_3V_DOT(uu, vv) < 0.0) { ELL_3V_ADD2(tmp, uu, vv); ret = AIR_PI - 2*asin(ELL_3V_LEN(tmp)/2.0); } else { ELL_3V_SUB(tmp, uu, vv); ret = 2*asin(ELL_3V_LEN(tmp)/2.0); } return ret; } /* ** input vectors have to be normalized! */ double ell_3v_area_spherical_d(const double avec[3], const double bvec[3], const double cvec[3]) { double axb[3], bxc[3], cxa[3], A, B, C, tmp; ELL_3V_CROSS(axb, avec, bvec); ELL_3V_CROSS(bxc, bvec, cvec); ELL_3V_CROSS(cxa, cvec, avec); ELL_3V_NORM(axb, axb, tmp); ELL_3V_NORM(bxc, bxc, tmp); ELL_3V_NORM(cxa, cxa, tmp); A = AIR_PI - ell_3v_angle_d(axb, cxa); B = AIR_PI - ell_3v_angle_d(bxc, axb); C = AIR_PI - ell_3v_angle_d(cxa, bxc); return A + B + C - AIR_PI; } /* ** all input vectors {a,b,c}vec, dir must be normalized */ void ell_3v_barycentric_spherical_d(double bary[3], const double av[3], const double bv[3], const double cv[3], const double vv[3]) { double sum; bary[0] = ell_3v_area_spherical_d(vv, bv, cv); bary[1] = ell_3v_area_spherical_d(vv, cv, av); bary[2] = ell_3v_area_spherical_d(vv, av, bv); sum = bary[0] + bary[1] + bary[2]; if (sum) { ELL_3V_SCALE(bary, 1.0/sum, bary); } return; } teem-1.11.0~svn6057/src/ell/ell.h0000664000175000017500000002733712174426262016117 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ELL_HAS_BEEN_INCLUDED #define ELL_HAS_BEEN_INCLUDED #include #include #include #include #include "ellMacros.h" #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(ell_EXPORTS) || defined(teem_EXPORTS) # define ELL_EXPORT extern __declspec(dllexport) # else # define ELL_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define ELL_EXPORT extern #endif #ifdef __cplusplus extern "C" { #endif #define ELL ell_biff_key #define ELL_EPS 1.0e-10 /* ******** ell_cubic_root* enum ** ** return values for ell_cubic */ enum { ell_cubic_root_unknown, /* 0 */ ell_cubic_root_single, /* 1 */ ell_cubic_root_triple, /* 2 */ ell_cubic_root_single_double, /* 3 */ ell_cubic_root_three, /* 4 */ ell_cubic_root_last }; #define ELL_CUBIC_ROOT_MAX 4 /* ** Note: *** PRE-TEEM 1.7 *** matrix element ordering was: ** ** 0 3 6 ** 1 4 7 for 3x3 ** 2 5 8 ** ** 0 4 8 12 ** 1 5 9 13 for 4x4 ** 2 6 10 14 ** 3 7 11 15 ** ** as of TEEM 1.7, matrix element ordering is: ** ** 0 1 2 ** 3 4 5 for 3x3 ** 6 7 8 ** ** 0 1 2 3 ** 4 5 6 7 for 4x4 ** 8 9 10 11 ** 12 13 14 15 ** ** all vectors are still, logically, COLUMN vectors */ /* miscEll.c */ ELL_EXPORT const int ellPresent; ELL_EXPORT const char *ell_biff_key; ELL_EXPORT const airEnum *const ell_cubic_root; ELL_EXPORT int ell_debug; ELL_EXPORT void ell_3m_print_f(FILE *f, const float s[9]); ELL_EXPORT void ell_3v_print_f(FILE *f, const float s[3]); ELL_EXPORT void ell_3m_print_d(FILE *f, const double s[9]); ELL_EXPORT void ell_3v_print_d(FILE *f, const double s[3]); ELL_EXPORT void ell_4m_print_f(FILE *f, const float s[16]); ELL_EXPORT void ell_4v_print_f(FILE *f, const float s[4]); ELL_EXPORT void ell_4m_print_d(FILE *f, const double s[16]); ELL_EXPORT void ell_4v_print_d(FILE *f, const double s[4]); /* vecEll.c */ ELL_EXPORT void ell_4v_norm_f(float bv[4], const float av[4]); ELL_EXPORT void ell_3v_perp_f(float p[3], const float v[3]); ELL_EXPORT void ell_3v_perp_d(double p[3], const double v[3]); ELL_EXPORT void ell_3mv_mul_f(float v2[3], const float m[9], const float v1[3]); ELL_EXPORT void ell_3mv_mul_d(double v2[3], const double m[9], const double v1[3]); ELL_EXPORT void ell_4mv_mul_f(float v2[4], const float m[16], const float v1[4]); ELL_EXPORT void ell_4mv_mul_d(double v2[4], const double m[16], const double v1[4]); ELL_EXPORT float ell_3v_angle_f(const float u[3], const float v[3]); ELL_EXPORT double ell_3v_angle_d(const double u[3], const double v[3]); ELL_EXPORT double ell_3v_area_spherical_d(const double avec[3], const double bvec[3], const double cvec[3]); ELL_EXPORT void ell_3v_barycentric_spherical_d(double bary[3], const double av[3], const double bv[3], const double cv[3], const double vv[3]); /* mat.c */ ELL_EXPORT void ell_3m_mul_f(float m3[9], const float m1[9], const float m2[9]); ELL_EXPORT void ell_3m_mul_d(double m3[9], const double m1[9], const double m2[9]); ELL_EXPORT void ell_3m_pre_mul_f(float m[9], const float x[9]); ELL_EXPORT void ell_3m_pre_mul_d(double m[9], const double x[9]); ELL_EXPORT void ell_3m_post_mul_f(float m[9], const float x[9]); ELL_EXPORT void ell_3m_post_mul_d(double m[9], const double x[9]); ELL_EXPORT float ell_3m_det_f(float m[9]); ELL_EXPORT double ell_3m_det_d(double m[9]); ELL_EXPORT void ell_3m_inv_f(float i[9], const float m[9]); ELL_EXPORT void ell_3m_inv_d(double i[9], const double m[9]); ELL_EXPORT void ell_4m_mul_f(float m3[16], const float m1[16], const float m2[16]); ELL_EXPORT void ell_4m_mul_d(double m3[16], const double m1[16], const double m2[16]); ELL_EXPORT void ell_4m_pre_mul_f(float m[16], const float x[16]); ELL_EXPORT void ell_4m_pre_mul_d(double m[16], const double x[16]); ELL_EXPORT void ell_4m_post_mul_f(float m[16], const float x[16]); ELL_EXPORT void ell_4m_post_mul_d(double m[16], const double x[16]); ELL_EXPORT float ell_4m_det_f(float m[16]); ELL_EXPORT double ell_4m_det_d(double m[16]); ELL_EXPORT void ell_4m_inv_f(float i[16], const float m[16]); ELL_EXPORT void ell_4m_inv_d(double i[16], const double m[16]); ELL_EXPORT void ell_6m_mul_d(double AB[36], const double A[36], const double B[36]); ELL_EXPORT void ell_3m_rotate_between_d(double rot[9], double from[3], double to[3]); /* ** Note: quaternion element ordering is: ** ** w x y z ** 0 1 2 3 ** ** where w is the real component and (x,y,z) is the imaginary component ** ** Nowhere in ell is there the assumption that any given quaternion is ** a unit-length quaternion. Sometimes returned quaternions will be ** unit-length, and sometimes given quaternions must be normalized internally ** prior to doing some operation, but its not something that ell users ** need worry about. */ /* quat.c */ ELL_EXPORT void ell_3m_to_q_f( float q[4], const float m[9]); ELL_EXPORT void ell_3m_to_q_d(double q[4], const double m[9]); ELL_EXPORT void ell_4m_to_q_f( float q[4], const float m[16]); ELL_EXPORT void ell_4m_to_q_d(double q[4], const double m[16]); ELL_EXPORT void ell_q_to_3m_f( float m[9], const float q[4]); ELL_EXPORT void ell_q_to_3m_d(double m[9], const double q[4]); ELL_EXPORT void ell_q_to_4m_f( float m[16], const float q[4]); ELL_EXPORT void ell_q_to_4m_d(double m[16], const double q[4]); ELL_EXPORT float ell_q_to_aa_f( float axis[3], const float q[4]); ELL_EXPORT double ell_q_to_aa_d(double axis[3], const double q[4]); ELL_EXPORT void ell_aa_to_q_f(float q[4], const float angle, const float axis[3]); ELL_EXPORT void ell_aa_to_q_d(double q[4], const double angle, const double axis[3]); ELL_EXPORT void ell_aa_to_3m_f(float m[9], const float angle, const float axis[3]); ELL_EXPORT void ell_aa_to_3m_d(double m[9], const double angle, const double axis[3]); ELL_EXPORT void ell_aa_to_4m_f(float m[16], const float angle, const float axis[3]); ELL_EXPORT void ell_aa_to_4m_d(double m[16], const double angle, const double axis[3]); ELL_EXPORT float ell_3m_to_aa_f( float axis[3], const float m[9]); ELL_EXPORT double ell_3m_to_aa_d(double axis[3], const double m[9]); ELL_EXPORT float ell_4m_to_aa_f( float axis[3], const float m[16]); ELL_EXPORT double ell_4m_to_aa_d(double axis[3], const double m[16]); ELL_EXPORT void ell_q_mul_f(float q3[4], const float q1[4], const float q2[4]); ELL_EXPORT void ell_q_mul_d(double q3[4], const double q1[4], const double q2[4]); ELL_EXPORT void ell_q_inv_f( float qi[4], const float q[4]); ELL_EXPORT void ell_q_inv_d(double qi[4], const double q[4]); ELL_EXPORT void ell_q_pow_f( float q2[4], const float q1[4], const float p); ELL_EXPORT void ell_q_pow_d(double q2[4], const double q1[4], const double p); ELL_EXPORT void ell_q_div_f(float q3[4], const float q1[4], const float q2[4]); ELL_EXPORT void ell_q_div_d(double q3[4], const double q1[4], const double q2[4]); ELL_EXPORT void ell_q_exp_f( float q2[4], const float q1[4]); ELL_EXPORT void ell_q_exp_d(double q2[4], const double q1[4]); ELL_EXPORT void ell_q_log_f( float q2[4], const float q1[4]); ELL_EXPORT void ell_q_log_d(double q2[4], const double q1[4]); ELL_EXPORT void ell_q_3v_rotate_f(float v2[3], const float q[4], const float v1[3]); ELL_EXPORT void ell_q_3v_rotate_d(double v2[3], const double q[4], const double v1[3]); ELL_EXPORT void ell_q_4v_rotate_f(float v2[4], const float q[4], const float v1[4]); ELL_EXPORT void ell_q_4v_rotate_d(double v2[4], const double q[4], const double v1[4]); ELL_EXPORT int ell_q_avg4_d(double m[4], unsigned int *iterP, const double _q1[4], const double _q2[4], const double _q3[4], const double _q4[4], const double _wght[4], const double eps, const unsigned int maxIter); ELL_EXPORT int ell_q_avgN_d(double mm[4], unsigned int *iterP, const double *qq, double *qbuff, const double *wght, const unsigned int NN, const double eps, const unsigned int maxIter); /* genmat.c */ ELL_EXPORT int ell_Nm_check(Nrrd *mat, int doNrrdCheck); ELL_EXPORT int ell_Nm_tran(Nrrd *dest, Nrrd *src); ELL_EXPORT int ell_Nm_mul(Nrrd *dest, Nrrd *A, Nrrd *B); ELL_EXPORT int ell_Nm_inv(Nrrd *dest, Nrrd *src); ELL_EXPORT int ell_Nm_pseudo_inv(Nrrd *dest, Nrrd *src); ELL_EXPORT int ell_Nm_wght_pseudo_inv(Nrrd *ninv, Nrrd *nA, Nrrd *nW); /* cubic.c */ ELL_EXPORT int ell_cubic(double root[3], double A, double B, double C, int newton); /* eigen.c */ ELL_EXPORT void ell_2m_1d_nullspace_d(double ans[2], const double n[4]); ELL_EXPORT void ell_3m_1d_nullspace_d(double ans[3], const double n[9]); ELL_EXPORT void ell_3m_2d_nullspace_d(double ans0[3], double ans1[3], const double n[9]); ELL_EXPORT int ell_3m_eigenvalues_d(double eval[3], const double m[9], const int newton); ELL_EXPORT int ell_3m_eigensolve_d(double eval[3], double evec[9], const double m[9], const int newton); ELL_EXPORT int ell_3m2sub_eigenvalues_d(double eval[3], const double m[9]); ELL_EXPORT int ell_3m2sub_eigensolve_d(double eval[3], double evec[9], const double m[9]); ELL_EXPORT int ell_3m_svd_d(double uu[9], double sval[3], double vv[9], const double mat[9], const int newton); ELL_EXPORT int ell_6ms_eigensolve_d(double eval[6], double evec[36], const double mat[21], const double eps); #ifdef __cplusplus } #endif #endif /* ELL_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/ell/quat.c0000664000175000017500000004166112165631065016303 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ell.h" #define W 0 #define X 1 #define Y 2 #define Z 3 /* 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ /* ** note: this will always produce a unit length quaternion, by the ** ELL_4V_NORM(q, q, len) at the end (NOTE: actually that's been ** expanded out to deal with warnings about precision loss with ** double->float conversion). However, for proper rotation matrices, ** that normalization should be far from a divide by zero, so it ** should be stable. It *IS* necessary, since it accomplishes ** division by w, x, y, or z, whichever's squared magnitude is biggest */ #define _ELL_M_TO_Q(type, i0, i1, i2, i3, i4, i5, i6, i7, i8) \ type s[4], wx, wy, wz, xy, xz, yz, len; \ int mi; \ \ s[W] = 1 + m[i0] + m[i4] + m[i8]; \ s[X] = 1 + m[i0] - m[i4] - m[i8]; \ s[Y] = 1 - m[i0] + m[i4] - m[i8]; \ s[Z] = 1 - m[i0] - m[i4] + m[i8]; \ wx = m[i7] - m[i5]; \ wy = m[i2] - m[i6]; \ wz = m[i3] - m[i1]; \ xy = m[i3] + m[i1]; \ xz = m[i6] + m[i2]; \ yz = m[i7] + m[i5]; \ mi = s[W] > s[X] ? W : X; \ mi = s[mi] > s[Y] ? mi : Y; \ mi = s[mi] > s[Z] ? mi : Z; \ switch (mi) { \ case W: \ ELL_4V_SET(q, s[W], wx, wy, wz); \ break; \ case X: \ ELL_4V_SET(q, wx, s[X], xy, xz); \ break; \ case Y: \ ELL_4V_SET(q, wy, xy, s[Y], yz); \ break; \ case Z: \ ELL_4V_SET(q, wz, xz, yz, s[Z]); \ break; \ } void ell_3m_to_q_f(float q[4], const float m[9]) { _ELL_M_TO_Q( float, 0, 1, 2, 3, 4, 5, 6, 7, 8); len = AIR_CAST(float, ELL_4V_LEN(q)); ELL_4V_SCALE(q, 1.0f/len, q); } void ell_3m_to_q_d(double q[4], const double m[9]) { _ELL_M_TO_Q(double, 0, 1, 2, 3, 4, 5, 6, 7, 8); ELL_4V_NORM(q, q, len); } void ell_4m_to_q_f(float q[4], const float m[16]) { _ELL_M_TO_Q( float, 0, 1, 2, 4, 5, 6, 8, 9, 10); len = AIR_CAST(float, ELL_4V_LEN(q)); ELL_4V_SCALE(q, 1.0f/len, q); } void ell_4m_to_q_d(double q[4], const double m[16]) { _ELL_M_TO_Q(double, 0, 1, 2, 4, 5, 6, 8, 9, 10); ELL_4V_NORM(q, q, len); } /* ** note: normalizes the quaternion on the way in, to insure ** creation of a proper rotation matrix. Without the normalization ** the coefficients in the matrix would be off by a factor of ** w*w + x*x + y*y + z*z ** ** See NOTE below about the non-use of ELL_4V_NORM(u, q, w) */ #define _ELL_Q_TO_3M(type) \ ELL_4V_GET(w, x, y, z, u); \ ELL_3V_SET(m+0, \ w*w + x*x - y*y - z*z, \ 2*(x*y - w*z), \ 2*(x*z + w*y)); \ ELL_3V_SET(m+3, \ 2*(x*y + w*z), \ w*w - x*x + y*y - z*z, \ 2*(y*z - w*x)); \ ELL_3V_SET(m+6, \ 2*(x*z - w*y), \ 2*(y*z + w*x), \ w*w - x*x - y*y + z*z) void ell_q_to_3m_f(float m[9], const float q[4]) { float u[4], w=0.0, x=0.0, y=0.0, z=0.0; w = AIR_CAST(float, ELL_4V_LEN(q)); ELL_4V_SCALE(u, 1.0f/w, q); _ELL_Q_TO_3M(float); } void ell_q_to_3m_d(double m[9], const double q[4]) { double u[4], w=0.0, x=0.0, y=0.0, z=0.0; ELL_4V_NORM(u, q, w); _ELL_Q_TO_3M(double); } /* ** HEY: the first two lines of this replace ELL_4V_NORM(u, q, w). The ** replacement was needed to avoid warnings about precision loss with ** double->float converstion. Macros are indeed problematic . . . */ #define _ELL_Q_TO_4M(type) \ ELL_4V_GET(w, x, y, z, u); \ ELL_4V_SET(m+0, \ w*w + x*x - y*y - z*z, \ 2*(x*y - w*z), \ 2*(x*z + w*y), \ 0); \ ELL_4V_SET(m+4, \ 2*(x*y + w*z), \ w*w - x*x + y*y - z*z, \ 2*(y*z - w*x), \ 0); \ ELL_4V_SET(m+8, \ 2*(x*z - w*y), \ 2*(y*z + w*x), \ w*w - x*x - y*y + z*z, \ 0); \ ELL_4V_SET(m+12, 0, 0, 0, 1) void ell_q_to_4m_f(float m[16], const float q[4]) { float u[4], w=0.0, x=0.0, y=0.0, z=0.0; w = AIR_CAST(float, ELL_3V_LEN(q)); ELL_4V_SCALE(u, 1.0f/w, q); _ELL_Q_TO_4M(float); } void ell_q_to_4m_d(double m[16], const double q[4]) { double u[4], w=0.0, x=0.0, y=0.0, z=0.0; ELL_4V_NORM(u, q, w); _ELL_Q_TO_4M(double); } /* ** note: by the use of atan2, this does NOT assume a ** a unit-length quaternion. The axis output, however, ** will always be unit length, even if the quaternion was ** purely real (rotation angle is zero) ** ** HEY: there are two instances here of non-use of ELL_3V_NORM ** to avoid warnings about precision loss with type conversion */ #define _ELL_Q_TO_AA(type) \ type len, angle; \ \ len = AIR_CAST(type, ELL_3V_LEN(q+1)); \ angle = AIR_CAST(type, atan2(len, q[0])); \ if (len) { \ ELL_3V_SCALE(axis, 1.0f/len, q+1); \ len = AIR_CAST(type, ELL_3V_LEN(axis)); \ ELL_3V_SCALE(axis, 1.0f/len, axis); \ } else { \ ELL_3V_SET(axis, 1, 0, 0); \ } \ return 2*angle float ell_q_to_aa_f(float axis[3], const float q[4]) { _ELL_Q_TO_AA(float); } double ell_q_to_aa_d(double axis[3], const double q[4]) { _ELL_Q_TO_AA(double); } /* ** note: assuming that axis is unit length, this produces a ** a unit length quaternion */ #define _ELL_AA_TO_Q(type) \ type sa; \ \ sa = AIR_CAST(type, sin(angle/2)); \ ELL_4V_SET(q, \ AIR_CAST(type, cos(angle/2)), AIR_CAST(type, sa*axis[0]), \ AIR_CAST(type, sa*axis[1]), AIR_CAST(type, sa*axis[2])) void ell_aa_to_q_f(float q[4], const float angle, const float axis[3]) { _ELL_AA_TO_Q(float); } void ell_aa_to_q_d(double q[4], const double angle, const double axis[3]) { _ELL_AA_TO_Q(double); } float ell_3m_to_aa_f( float axis[3], const float m[9]) { float q[4]; ell_3m_to_q_f(q, m); return ell_q_to_aa_f(axis, q); } double ell_3m_to_aa_d(double axis[3], const double m[9]) { double q[4]; ell_3m_to_q_d(q, m); return ell_q_to_aa_d(axis, q); } float ell_4m_to_aa_f( float axis[3], const float m[16]) { float q[4]; ell_4m_to_q_f(q, m); return ell_q_to_aa_f(axis, q); } double ell_4m_to_aa_d(double axis[3], const double m[16]) { double q[4]; ell_4m_to_q_d(q, m); return ell_q_to_aa_d(axis, q); } void ell_aa_to_3m_f( float m[9], const float angle, const float axis[3]) { float q[4]; ell_aa_to_q_f(q, angle, axis); ell_q_to_3m_f(m, q); } void ell_aa_to_3m_d(double m[9], const double angle, const double axis[3]) { double q[4]; ell_aa_to_q_d(q, angle, axis); ell_q_to_3m_d(m, q); } void ell_aa_to_4m_f( float m[16], const float angle, const float axis[3]) { float q[4]; ell_aa_to_q_f(q, angle, axis); ell_q_to_4m_f(m, q); } void ell_aa_to_4m_d(double m[16], const double angle, const double axis[3]) { double q[4]; ell_aa_to_q_d(q, angle, axis); ell_q_to_4m_d(m, q); } void ell_q_mul_f(float q3[4], const float q1[4], const float q2[4]) { ELL_Q_MUL(q3, q1, q2); } void ell_q_mul_d(double q3[4], const double q1[4], const double q2[4]) { ELL_Q_MUL(q3, q1, q2); } void ell_q_inv_f(float qi[4], const float q[4]) { float N; ELL_Q_INV(qi, q, N); } void ell_q_inv_d(double qi[4], const double q[4]) { double N; ELL_Q_INV(qi, q, N); } /* ** div(a, b) = a^-1 * b */ void ell_q_div_f(float q3[4], const float q1[4], const float q2[4]) { float N, q1i[4]; ELL_Q_INV(q1i, q1, N); ELL_Q_MUL(q3, q1i, q2); } void ell_q_div_d(double q3[4], const double q1[4], const double q2[4]) { double N, q1i[4]; ELL_Q_INV(q1i, q1, N); ELL_Q_MUL(q3, q1i, q2); } /* ** this is good for *ALL* quaternions, any length, including zero. ** the behavior on the zero quaternion is governed by the behavior ** of the log() and atan2() functions in the math library ** ** the basic insight is that doing conversion to angle/axis, ** and doing the atan2(l2(x,y,z),w), ** and that doing a logarithm, are all basically the same thing */ void ell_q_log_f(float q2[4], const float q1[4]) { float a, b, axis[3]; a = AIR_CAST(float, log(ELL_4V_LEN(q1))); b = ell_q_to_aa_f(axis, q1)/2.0f; ELL_4V_SET(q2, a, b*axis[0], b*axis[1], b*axis[2]); } void ell_q_log_d(double q2[4], const double q1[4]) { double a, b, axis[3]; a = log(ELL_4V_LEN(q1)); b = ell_q_to_aa_d(axis, q1)/2.0; ELL_4V_SET(q2, a, b*axis[0], b*axis[1], b*axis[2]); } /* ** this is good for *ALL* quaternions, any length, including zero ** NOTE: one non-use of ELL_3V_NORM to avoid warnings about ** precision loss from type conversion */ #define _ELL_Q_EXP(type) \ type ea, b, sb, axis[3], tmp; \ \ ea = AIR_CAST(type, exp(q1[0])); \ b = AIR_CAST(type, ELL_3V_LEN(q1+1)); \ if (b) { \ ELL_3V_SCALE(axis, 1.0f/b, q1+1); \ tmp = AIR_CAST(type, ELL_3V_LEN(axis)); \ ELL_3V_SCALE(axis, 1.0f/tmp, axis); \ } else { \ ELL_3V_SET(axis, 1.0f, 0.0f, 0.0f); \ } \ sb = AIR_CAST(type, sin(b)); \ ELL_4V_SET(q2, AIR_CAST(type, ea*cos(b)), ea*sb*axis[0], \ ea*sb*axis[1], ea*sb*axis[2]) void ell_q_exp_f(float q2[4], const float q1[4]) { _ELL_Q_EXP(float); } void ell_q_exp_d(double q2[4], const double q1[4]) { _ELL_Q_EXP(double); } void ell_q_pow_f(float q2[4], const float q1[4], const float p) { float len, angle, axis[3]; len = AIR_CAST(float, pow(ELL_4V_LEN(q1), p)); angle = ell_q_to_aa_f(axis, q1); ell_aa_to_q_f(q2, p*angle, axis); ELL_4V_SCALE(q2, len, q2); } void ell_q_pow_d(double q2[4], const double q1[4], const double p) { double len, angle, axis[3]; len = pow(ELL_4V_LEN(q1), p); angle = ell_q_to_aa_d(axis, q1); ell_aa_to_q_d(q2, p*angle, axis); ELL_4V_SCALE(q2, len, q2); } /* ** by the wonders of quaternions, this rotation will be the ** same regardless of the quaternion length. This is in ** contrast to doing rotation by first converting to matrix, ** in which an explicit normalization is required. There is ** a divide here (in ELL_Q_INV), but there's no sqrt(). */ #define _ELL_Q3V_ROT(type) \ type n, a[4], b[4], c[4]; \ \ ELL_4V_SET(a, 0, v1[0], v1[1], v1[2]); \ ELL_Q_INV(b, q, n); \ ELL_Q_MUL(c, a, b); \ ELL_Q_MUL(a, q, c); \ ELL_3V_COPY(v2, a+1) void ell_q_3v_rotate_f( float v2[3], const float q[4], const float v1[3]) { _ELL_Q3V_ROT(float); } void ell_q_3v_rotate_d(double v2[3], const double q[4], const double v1[3]) { _ELL_Q3V_ROT(double); } /* ** we start by ignoring the last (homogenous) coordinate of ** the vector, but then we copy it to the output */ void ell_q_4v_rotate_f( float v2[4], const float q[4], const float v1[4]) { _ELL_Q3V_ROT(float); v2[3] = v1[3]; } void ell_q_4v_rotate_d(double v2[4], const double q[4], const double v1[4]) { _ELL_Q3V_ROT(double); v2[3] = v1[3]; } /* #define _ELL_Q_AVG_ITER_MAX 30 */ int ell_q_avg4_d(double m[4], unsigned int *iterP, const double _q1[4], const double _q2[4], const double _q3[4], const double _q4[4], const double _wght[4], const double eps, const unsigned int maxIter) { static const char me[]="ell_q_avg4_d"; double N, elen, a[4], b[4], c[4], d[4], tmp[4], la[4], lb[4], lc[4], ld[4], u[4], wght[4]; unsigned int iter; /* *iterP optional */ if (!( m && _q1 && _q2 && _q3 && _q4 && _wght )) { biffAddf(ELL, "%s: got NULL pointer", me); return 1; } if (!( eps >= 0 )) { biffAddf(ELL, "%s: need eps >= 0 (not %g)", me, eps); return 1; } /* normalize (wrt L2) all given quaternions */ ELL_4V_NORM(a, _q1, N); ELL_4V_NORM(b, _q2, N); ELL_4V_NORM(c, _q3, N); ELL_4V_NORM(d, _q4, N); /* normalize (wrt L1) the given weights */ ELL_4V_COPY(wght, _wght); N = wght[0] + wght[1] + wght[2] + wght[3]; ELL_4V_SCALE(wght, 1.0/N, wght); /* initialize mean to normalized euclidean mean */ ELL_4V_SCALE_ADD4(m, wght[0], a, wght[1], b, wght[2], c, wght[3], d); ELL_4V_NORM(m, m, N); iter = 0; do { /* take log of everyone */ ell_q_div_d(tmp, m, a); ell_q_log_d(la, tmp); ell_q_div_d(tmp, m, b); ell_q_log_d(lb, tmp); ell_q_div_d(tmp, m, c); ell_q_log_d(lc, tmp); ell_q_div_d(tmp, m, d); ell_q_log_d(ld, tmp); /* average, and find length */ ELL_4V_SCALE_ADD4(u, wght[0], la, wght[1], lb, wght[2], lc, wght[3], ld); elen = ELL_4V_LEN(u); /* use exp to put it back on S^3 */ ell_q_exp_d(tmp, u); ell_q_mul_d(m, m, tmp); iter++; } while ((!maxIter || iter < maxIter) && elen > eps); if (elen > eps) { biffAddf(ELL, "%s: still have error %g after max %d iters", me, elen, maxIter); return 1; } if (iterP) { *iterP = iter; } return 0; } /* ** unlike the thing above, this one assumes that the quaternions in qq ** have already been normalized, and, that the sum of wght[] is 1.0 */ int ell_q_avgN_d(double mm[4], unsigned int *iterP, const double *qq, double *qlog, const double *wght, const unsigned int NN, const double eps, const unsigned int maxIter) { static const char me[]="ell_q_avgN_d"; double tmp, qdiv[4], elen; unsigned int ii, iter; /* iterP optional */ /* wght optional, to signify equal 1/NN weighting for all */ if (!( mm && qq )) { biffAddf(ELL, "%s: got NULL pointer", me); return 1; } if (!( eps >= 0 )) { biffAddf(ELL, "%s: need eps >= 0 (not %g)", me, eps); return 1; } /* initialize with euclidean mean */ ELL_4V_SET(mm, 0, 0, 0, 0); for (ii=0; ii eps); if (elen > eps) { biffAddf(ELL, "%s: still have error %g (> eps %g) after max %d iters", me, elen, eps, maxIter); return 1; } if (iterP) { *iterP = iter; } return 0; } teem-1.11.0~svn6057/src/ell/ellMacros.h0000664000175000017500000013454712165631065017265 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ELLMACROS_HAS_BEEN_INCLUDED #define ELLMACROS_HAS_BEEN_INCLUDED #ifdef __cplusplus extern "C" { #endif /* ******** ELL_SWAP2, ELL_SWAP3 ** ** used to interchange 2 or 3 values, using the given temp variable ** SWAP2: a <=> b ** SWAP3: a <= b, b <= c, c <= a */ #define ELL_SWAP2(a, b, t) ((t)=(a),(a)=(b),(b)=(t)) #define ELL_SWAP3(a, b, c, t) ((t)=(a),(a)=(b),(b)=(c),(c)=(t)) /* ******** ELL_SORT3 ** ** sorts v0, v1, v2 in descending order, using given temp variable t, */ #define ELL_SORT3(v0, v1, v2, t) \ if (v0 > v1) { \ if (v1 < v2) { \ if (v0 > v2) { ELL_SWAP2(v1, v2, t); } \ else { ELL_SWAP3(v0, v2, v1, t); } \ } \ } \ else { \ if (v1 > v2) { \ if (v0 > v2) { ELL_SWAP2(v0, v1, t); } \ else { ELL_SWAP3(v0, v1, v2, t); } \ } \ else { \ ELL_SWAP2(v0, v2, t); \ } \ } /* ******** ELL_MAX3_IDX ** ** returns 0, 1, 2, to indicate which of the three arguments is largest */ #define ELL_MAX3_IDX(v0, v1, v2) \ (v0 > v1 \ ? (v1 > v2 \ ? 0 \ : (v0 > v2 \ ? 0 \ : 2)) \ : (v2 > v1 \ ? 2 \ : 1)) /* ******** ELL_MIN3_IDX ** ** returns 0, 1, 2, to indicate which of the three arguments is smallest */ #define ELL_MIN3_IDX(v0, v1, v2) \ (v0 < v1 \ ? (v1 < v2 \ ? 0 \ : (v0 < v2 \ ? 0 \ : 2)) \ : (v2 < v1 \ ? 2 \ : 1)) #define ELL_2V_SET(v, a, b) \ ((v)[0]=(a), (v)[1]=(b)) #define ELL_2V_SET_TT(v, TT, a, b) \ ((v)[0] = AIR_CAST(TT, (a)), \ (v)[1] = AIR_CAST(TT, (b))) #define ELL_2V_COPY(v2, v1) \ ((v2)[0] = (v1)[0], (v2)[1] = (v1)[1]) #define ELL_2V_INCR(v2, v1) \ ((v2)[0] += (v1)[0], \ (v2)[1] += (v1)[1]) #define ELL_2V_LERP(v3, w, v1, v2) \ ((v3)[0] = AIR_LERP((w), (v1)[0], (v2)[0]), \ (v3)[1] = AIR_LERP((w), (v1)[1], (v2)[1])) #define ELL_2V_LERP_TT(v3, TT, w, v1, v2) \ ((v3)[0] = AIR_CAST(TT, AIR_LERP((w), (v1)[0], (v2)[0])), \ (v3)[1] = AIR_CAST(TT, AIR_LERP((w), (v1)[1], (v2)[1]))) #define ELL_2V_ADD2(v3, v1, v2) \ ((v3)[0] = (v1)[0] + (v2)[0], \ (v3)[1] = (v1)[1] + (v2)[1]) #define ELL_2V_ADD3(v4, v1, v2, v3) \ ((v4)[0] = (v1)[0] + (v2)[0] + (v3)[0], \ (v4)[1] = (v1)[1] + (v2)[1] + (v3)[1]) #define ELL_2V_ADD4(v5, v1, v2, v3, v4) \ ((v5)[0] = (v1)[0] + (v2)[0] + (v3)[0] + (v4)[0], \ (v5)[1] = (v1)[1] + (v2)[1] + (v3)[1] + (v4)[1]) #define ELL_2V_SUB(v3, v1, v2) \ ((v3)[0] = (v1)[0] - (v2)[0], \ (v3)[1] = (v1)[1] - (v2)[1]) #define ELL_2V_COPY(v2, v1) \ ((v2)[0] = (v1)[0], (v2)[1] = (v1)[1]) #define ELL_2V_COPY_TT(v2, TYPE, v1) \ ((v2)[0] = AIR_CAST(TYPE, (v1)[0]), \ (v2)[1] = AIR_CAST(TYPE, (v1)[1])) #define ELL_2V_DOT(v1, v2) ((v1)[0]*(v2)[0] + (v1)[1]*(v2)[1]) #define ELL_2V_LEN(v) (sqrt(ELL_2V_DOT((v),(v)))) #define ELL_2V_SCALE(v2, a, v1) \ ((v2)[0] = (a)*(v1)[0], \ (v2)[1] = (a)*(v1)[1]) #define ELL_2V_SCALE_ADD2(v2, s0, v0, s1, v1) \ ((v2)[0] = (s0)*(v0)[0] + (s1)*(v1)[0], \ (v2)[1] = (s0)*(v0)[1] + (s1)*(v1)[1]) #define ELL_2V_SCALE_ADD3(vd, s0, v0, s1, v1, s2, v2) \ ((vd)[0] = (s0)*(v0)[0] + (s1)*(v1)[0] + (s2)*(v2)[0], \ (vd)[1] = (s0)*(v0)[1] + (s1)*(v1)[1] + (s2)*(v2)[1]) #define ELL_2V_NORM(v2, v1, length) \ (length = ELL_2V_LEN(v1), ELL_2V_SCALE(v2, 1.0/length, v1)) /* ** the 2x2 matrix-related macros assume that the matrix indexing is: ** 0 1 ** 2 3 */ #define _ELL_2M_DET(a,b,c,d) ((a)*(d) - (b)*(c)) #define ELL_2M_DET(m) _ELL_2M_DET((m)[0],(m)[1],(m)[2],(m)[3]) #define ELL_2M_TRANSPOSE(m2, m1) \ ((m2)[0] = (m1)[0], \ (m2)[1] = (m1)[2], \ (m2)[2] = (m1)[1], \ (m2)[3] = (m1)[3]) #define ELL_2M_MUL(m3, m1, m2) \ ((m3)[0] = (m1)[0]*(m2)[0] + (m1)[1]*(m2)[2], \ (m3)[1] = (m1)[0]*(m2)[1] + (m1)[1]*(m2)[3], \ \ (m3)[2] = (m1)[2]*(m2)[0] + (m1)[3]*(m2)[2], \ (m3)[3] = (m1)[2]*(m2)[1] + (m1)[3]*(m2)[3]) #define ELL_2M_ROTATE_SET(m, th) \ (ELL_2V_SET((m)+ 0, cos(th) , -sin(th)), \ ELL_2V_SET((m)+ 2, +sin(th) , cos(th))) #define ELL_2MV_MUL(v2, m, v1) \ ((v2)[0] = (m)[0]*(v1)[0] + (m)[1]*(v1)[1], \ (v2)[1] = (m)[2]*(v1)[0] + (m)[3]*(v1)[1]) /* ** the 3x3 matrix-related macros assume that the matrix indexing is: ** 0 1 2 ** 3 4 5 ** 6 7 8 */ #define ELL_3V_SET(v, a, b, c) \ ((v)[0] = (a), (v)[1] = (b), (v)[2] = (c)) #define ELL_3V_ZERO_SET(v) ELL_3V_SET(v, 0, 0, 0) #define ELL_3V_SET_TT(v, TT, a, b, c) \ ((v)[0] = AIR_CAST(TT, (a)), \ (v)[1] = AIR_CAST(TT, (b)), \ (v)[2] = AIR_CAST(TT, (c))) #define ELL_3V_GET(a, b, c, v) \ ((a) = (v)[0], (b) = (v)[1], (c) = (v)[2]) #define ELL_3V_EQUAL(a, b) \ ((a)[0] == (b)[0] && (a)[1] == (b)[1] && (a)[2] == (b)[2]) #define ELL_3V_COPY(v2, v1) \ ((v2)[0] = (v1)[0], (v2)[1] = (v1)[1], (v2)[2] = (v1)[2]) #define ELL_3V_COPY_TT(v2, TYPE, v1) \ ((v2)[0] = AIR_CAST(TYPE, (v1)[0]), \ (v2)[1] = AIR_CAST(TYPE, (v1)[1]), \ (v2)[2] = AIR_CAST(TYPE, (v1)[2])) #define ELL_3V_INCR(v2, v1) \ ((v2)[0] += (v1)[0], \ (v2)[1] += (v1)[1], \ (v2)[2] += (v1)[2]) #define ELL_3V_LERP(v3, w, v1, v2) \ ((v3)[0] = AIR_LERP((w), (v1)[0], (v2)[0]), \ (v3)[1] = AIR_LERP((w), (v1)[1], (v2)[1]), \ (v3)[2] = AIR_LERP((w), (v1)[2], (v2)[2])) #define ELL_3V_LERP_TT(v3, TT, w, v1, v2) \ ((v3)[0] = AIR_CAST(TT, AIR_LERP((w), (v1)[0], (v2)[0])), \ (v3)[1] = AIR_CAST(TT, AIR_LERP((w), (v1)[1], (v2)[1])), \ (v3)[2] = AIR_CAST(TT, AIR_LERP((w), (v1)[2], (v2)[2]))) #define ELL_3V_ADD2(v3, v1, v2) \ ((v3)[0] = (v1)[0] + (v2)[0], \ (v3)[1] = (v1)[1] + (v2)[1], \ (v3)[2] = (v1)[2] + (v2)[2]) #define ELL_3V_ADD3(v4, v1, v2, v3) \ ((v4)[0] = (v1)[0] + (v2)[0] + (v3)[0], \ (v4)[1] = (v1)[1] + (v2)[1] + (v3)[1], \ (v4)[2] = (v1)[2] + (v2)[2] + (v3)[2]) #define ELL_3V_ADD4(v5, v1, v2, v3, v4) \ ((v5)[0] = (v1)[0] + (v2)[0] + (v3)[0] + (v4)[0], \ (v5)[1] = (v1)[1] + (v2)[1] + (v3)[1] + (v4)[1], \ (v5)[2] = (v1)[2] + (v2)[2] + (v3)[2] + (v4)[2]) #define ELL_3V_SUB(v3, v1, v2) \ ((v3)[0] = (v1)[0] - (v2)[0], \ (v3)[1] = (v1)[1] - (v2)[1], \ (v3)[2] = (v1)[2] - (v2)[2]) #define ELL_3V_DOT(v1, v2) \ ((v1)[0]*(v2)[0] + (v1)[1]*(v2)[1] + (v1)[2]*(v2)[2]) #define ELL_3V_SCALE(v2, a, v1) \ ((v2)[0] = (a)*(v1)[0], \ (v2)[1] = (a)*(v1)[1], \ (v2)[2] = (a)*(v1)[2]) #define ELL_3V_SCALE_TT(v2, TT, a, v1) \ ((v2)[0] = AIR_CAST(TT, (a)*(v1)[0]), \ (v2)[1] = AIR_CAST(TT, (a)*(v1)[1]), \ (v2)[2] = AIR_CAST(TT, (a)*(v1)[2])) #define ELL_3V_SCALE_INCR(v2, s0, v0) \ ((v2)[0] += (s0)*(v0)[0], \ (v2)[1] += (s0)*(v0)[1], \ (v2)[2] += (s0)*(v0)[2]) #define ELL_3V_SCALE_INCR_TT(v2, TT, s0, v0) \ ((v2)[0] += AIR_CAST(TT, (s0)*(v0)[0]), \ (v2)[1] += AIR_CAST(TT, (s0)*(v0)[1]), \ (v2)[2] += AIR_CAST(TT, (s0)*(v0)[2])) #define ELL_3V_SCALE_ADD2(v2, s0, v0, s1, v1) \ ((v2)[0] = (s0)*(v0)[0] + (s1)*(v1)[0], \ (v2)[1] = (s0)*(v0)[1] + (s1)*(v1)[1], \ (v2)[2] = (s0)*(v0)[2] + (s1)*(v1)[2]) #define ELL_3V_SCALE_ADD2_TT(v2, TT, s0, v0, s1, v1) \ ((v2)[0] = AIR_CAST(TT, (s0)*(v0)[0] + (s1)*(v1)[0]), \ (v2)[1] = AIR_CAST(TT, (s0)*(v0)[1] + (s1)*(v1)[1]), \ (v2)[2] = AIR_CAST(TT, (s0)*(v0)[2] + (s1)*(v1)[2])) #define ELL_3V_SCALE_INCR2(v2, s0, v0, s1, v1) \ ((v2)[0] += (s0)*(v0)[0] + (s1)*(v1)[0], \ (v2)[1] += (s0)*(v0)[1] + (s1)*(v1)[1], \ (v2)[2] += (s0)*(v0)[2] + (s1)*(v1)[2]) #define ELL_3V_SCALE_ADD3(v3, s0, v0, s1, v1, s2, v2) \ ((v3)[0] = (s0)*(v0)[0] + (s1)*(v1)[0] + (s2)*(v2)[0], \ (v3)[1] = (s0)*(v0)[1] + (s1)*(v1)[1] + (s2)*(v2)[1], \ (v3)[2] = (s0)*(v0)[2] + (s1)*(v1)[2] + (s2)*(v2)[2]) #define ELL_3V_SCALE_ADD3_TT(v3, TT, s0, v0, s1, v1, s2, v2) \ ((v3)[0] = AIR_CAST(TT, (s0)*(v0)[0] + (s1)*(v1)[0] + (s2)*(v2)[0]), \ (v3)[1] = AIR_CAST(TT, (s0)*(v0)[1] + (s1)*(v1)[1] + (s2)*(v2)[1]), \ (v3)[2] = AIR_CAST(TT, (s0)*(v0)[2] + (s1)*(v1)[2] + (s2)*(v2)[2])) #define ELL_3V_SCALE_ADD4(v4, s0, v0, s1, v1, s2, v2, s3, v3) \ ((v4)[0] = (s0)*(v0)[0] + (s1)*(v1)[0] + (s2)*(v2)[0] + (s3)*(v3)[0], \ (v4)[1] = (s0)*(v0)[1] + (s1)*(v1)[1] + (s2)*(v2)[1] + (s3)*(v3)[1], \ (v4)[2] = (s0)*(v0)[2] + (s1)*(v1)[2] + (s2)*(v2)[2] + (s3)*(v3)[2]) #define ELL_3V_SCALE_ADD6(v6, s0, v0, s1, v1, s2, v2, \ s3, v3, s4, v4, s5, v5) \ ((v6)[0] = (s0)*(v0)[0] + (s1)*(v1)[0] + (s2)*(v2)[0] \ + (s3)*(v3)[0] + (s4)*(v4)[0] + (s5)*(v5)[0], \ (v6)[1] = (s0)*(v0)[1] + (s1)*(v1)[1] + (s2)*(v2)[1] \ + (s3)*(v3)[1] + (s4)*(v4)[1] + (s5)*(v5)[1], \ (v6)[2] = (s0)*(v0)[2] + (s1)*(v1)[2] + (s2)*(v2)[2] \ + (s3)*(v3)[2] + (s4)*(v4)[2] + (s5)*(v5)[2]) #define ELL_3V_SCALE_INCR3(v3, s0, v0, s1, v1, s2, v2) \ ((v3)[0] += (s0)*(v0)[0] + (s1)*(v1)[0] + (s2)*(v2)[0], \ (v3)[1] += (s0)*(v0)[1] + (s1)*(v1)[1] + (s2)*(v2)[1], \ (v3)[2] += (s0)*(v0)[2] + (s1)*(v1)[2] + (s2)*(v2)[2]) #define ELL_3V_LEN(v) (sqrt(ELL_3V_DOT((v),(v)))) #define ELL_3V_DIST(a, b) \ sqrt(((a)[0] - (b)[0])*((a)[0] - (b)[0]) + \ ((a)[1] - (b)[1])*((a)[1] - (b)[1]) + \ ((a)[2] - (b)[2])*((a)[2] - (b)[2])) #define ELL_3V_NORM(v2, v1, length) \ (length = ELL_3V_LEN(v1), ELL_3V_SCALE(v2, 1.0/length, v1)) #define ELL_3V_NORM_TT(v2, TT, v1, length) \ (length = AIR_CAST(TT, ELL_3V_LEN(v1)), \ ELL_3V_SCALE_TT(v2, TT, 1.0/length, v1)) #define ELL_3V_CROSS(v3, v1, v2) \ ((v3)[0] = (v1)[1]*(v2)[2] - (v1)[2]*(v2)[1], \ (v3)[1] = (v1)[2]*(v2)[0] - (v1)[0]*(v2)[2], \ (v3)[2] = (v1)[0]*(v2)[1] - (v1)[1]*(v2)[0]) #define ELL_3V_MIN(v3,v1,v2) ( \ (v3)[0] = AIR_MIN((v1)[0], (v2)[0]), \ (v3)[1] = AIR_MIN((v1)[1], (v2)[1]), \ (v3)[2] = AIR_MIN((v1)[2], (v2)[2])) #define ELL_3V_MAX(v3,v1,v2) ( \ (v3)[0] = AIR_MAX((v1)[0], (v2)[0]), \ (v3)[1] = AIR_MAX((v1)[1], (v2)[1]), \ (v3)[2] = AIR_MAX((v1)[2], (v2)[2])) #define ELL_3V_EXISTS(v) \ (AIR_EXISTS((v)[0]) && AIR_EXISTS((v)[1]) && AIR_EXISTS((v)[2])) #define ELL_3V_AFFINE(v,i,x,I,o,O) ( \ (v)[0] = AIR_AFFINE((i), (x), (I), (o)[0], (O)[0]), \ (v)[1] = AIR_AFFINE((i), (x), (I), (o)[1], (O)[1]), \ (v)[2] = AIR_AFFINE((i), (x), (I), (o)[2], (O)[2])) #define ELL_3V_ABS(v2,v1) ( \ (v2)[0] = AIR_ABS((v1)[0]), \ (v2)[1] = AIR_ABS((v1)[1]), \ (v2)[2] = AIR_ABS((v1)[2])) #define ELL_3V_NAN_SET(v) ( \ (v)[0] = AIR_NAN, \ (v)[1] = AIR_NAN, \ (v)[2] = AIR_NAN) #define ELL_3M_EQUAL(m1, m2) \ ((m1)[0] == (m2)[0] && \ (m1)[1] == (m2)[1] && \ (m1)[2] == (m2)[2] && \ (m1)[3] == (m2)[3] && \ (m1)[4] == (m2)[4] && \ (m1)[5] == (m2)[5] && \ (m1)[6] == (m2)[6] && \ (m1)[7] == (m2)[7] && \ (m1)[8] == (m2)[8]) #define ELL_3M_SET(m, a, b, c, d, e, f, g, h, i) \ (ELL_3V_SET(m + 0*3, a, b, c), \ ELL_3V_SET(m + 1*3, d, e, f), \ ELL_3V_SET(m + 2*3, g, h, i)) #define ELL_3M_SCALE(m2, s, m1) \ (ELL_3V_SCALE((m2)+0, (s), (m1)+0), \ ELL_3V_SCALE((m2)+3, (s), (m1)+3), \ ELL_3V_SCALE((m2)+6, (s), (m1)+6)) #define ELL_3M_SCALE_INCR(m2, s, m1) \ (ELL_3V_SCALE_INCR((m2)+0, (s), (m1)+0), \ ELL_3V_SCALE_INCR((m2)+3, (s), (m1)+3), \ ELL_3V_SCALE_INCR((m2)+6, (s), (m1)+6)) #define ELL_3M_SCALE_ADD2(m2, s0, m0, s1, m1) \ (ELL_3V_SCALE_ADD2((m2)+0, (s0), (m0)+0, (s1), (m1)+0), \ ELL_3V_SCALE_ADD2((m2)+3, (s0), (m0)+3, (s1), (m1)+3), \ ELL_3V_SCALE_ADD2((m2)+6, (s0), (m0)+6, (s1), (m1)+6)) #define ELL_3M_LERP(m3, w, m1, m2) \ (ELL_3V_LERP((m3)+0, (w), (m1)+0, (m2)+0), \ ELL_3V_LERP((m3)+3, (w), (m1)+3, (m2)+3), \ ELL_3V_LERP((m3)+6, (w), (m1)+6, (m2)+6)) #define ELL_3M_ADD2(m3, m1, m2) \ ((m3)[0] = (m1)[0] + (m2)[0], \ (m3)[1] = (m1)[1] + (m2)[1], \ (m3)[2] = (m1)[2] + (m2)[2], \ (m3)[3] = (m1)[3] + (m2)[3], \ (m3)[4] = (m1)[4] + (m2)[4], \ (m3)[5] = (m1)[5] + (m2)[5], \ (m3)[6] = (m1)[6] + (m2)[6], \ (m3)[7] = (m1)[7] + (m2)[7], \ (m3)[8] = (m1)[8] + (m2)[8]) #define ELL_3M_SUB(m3, m1, m2) \ ((m3)[0] = (m1)[0] - (m2)[0], \ (m3)[1] = (m1)[1] - (m2)[1], \ (m3)[2] = (m1)[2] - (m2)[2], \ (m3)[3] = (m1)[3] - (m2)[3], \ (m3)[4] = (m1)[4] - (m2)[4], \ (m3)[5] = (m1)[5] - (m2)[5], \ (m3)[6] = (m1)[6] - (m2)[6], \ (m3)[7] = (m1)[7] - (m2)[7], \ (m3)[8] = (m1)[8] - (m2)[8]) #define ELL_3M_SCALE_ADD3(m3, s0, m0, s1, m1, s2, m2) \ (ELL_3V_SCALE_ADD3((m3)+0, (s0), (m0)+0, (s1), (m1)+0, (s2), (m2)+0), \ ELL_3V_SCALE_ADD3((m3)+3, (s0), (m0)+3, (s1), (m1)+3, (s2), (m2)+3), \ ELL_3V_SCALE_ADD3((m3)+6, (s0), (m0)+6, (s1), (m1)+6, (s2), (m2)+6)) #define ELL_3M_COPY(m2, m1) \ (ELL_3V_COPY((m2)+0, (m1)+0), \ ELL_3V_COPY((m2)+3, (m1)+3), \ ELL_3V_COPY((m2)+6, (m1)+6)) #define ELL_3M_COPY_TT(m2, TYPE, m1) \ (ELL_3V_COPY_TT((m2)+0, TYPE, (m1)+0), \ ELL_3V_COPY_TT((m2)+3, TYPE, (m1)+3), \ ELL_3V_COPY_TT((m2)+6, TYPE, (m1)+6)) #define ELL_3M_IDENTITY_SET(m) \ (ELL_3V_SET((m)+0, 1 , 0 , 0), \ ELL_3V_SET((m)+3, 0 , 1 , 0), \ ELL_3V_SET((m)+6, 0 , 0 , 1)) #define ELL_3M_EXISTS(m) \ (ELL_3V_EXISTS((m) + 0) \ && ELL_3V_EXISTS((m) + 3) \ && ELL_3V_EXISTS((m) + 6)) #define ELL_3M_ZERO_SET(m) \ (ELL_3V_SET((m)+0, 0 , 0 , 0), \ ELL_3V_SET((m)+3, 0 , 0 , 0), \ ELL_3V_SET((m)+6, 0 , 0 , 0)) #define ELL_3M_NAN_SET(m) \ (ELL_3V_NAN_SET((m)+0), \ ELL_3V_NAN_SET((m)+3), \ ELL_3V_NAN_SET((m)+6)) #define ELL_3M_DIAG_SET(m, a, b, c) \ ((m)[0] = (a), (m)[4] = (b), (m)[8] = (c)) #define ELL_3M_TRANSPOSE(m2, m1) \ ((m2)[0] = (m1)[0], \ (m2)[1] = (m1)[3], \ (m2)[2] = (m1)[6], \ (m2)[3] = (m1)[1], \ (m2)[4] = (m1)[4], \ (m2)[5] = (m1)[7], \ (m2)[6] = (m1)[2], \ (m2)[7] = (m1)[5], \ (m2)[8] = (m1)[8]) #define ELL_3M_TRANSPOSE_IP(m, t) \ (ELL_SWAP2((m)[1],(m)[3],(t)), \ ELL_SWAP2((m)[2],(m)[6],(t)), \ ELL_SWAP2((m)[5],(m)[7],(t))) #define ELL_3M_TRACE(m) ((m)[0] + (m)[4] + (m)[8]) #define ELL_3M_FROB(m) \ (sqrt(ELL_3V_DOT((m)+0, (m)+0) + \ ELL_3V_DOT((m)+3, (m)+3) + \ ELL_3V_DOT((m)+6, (m)+6))) #define _ELL_3M_DET(a,b,c,d,e,f,g,h,i) \ ( (a)*(e)*(i) \ + (d)*(h)*(c) \ + (g)*(b)*(f) \ - (g)*(e)*(c) \ - (d)*(b)*(i) \ - (a)*(h)*(f)) #define ELL_3M_DET(m) _ELL_3M_DET((m)[0],(m)[1],(m)[2],\ (m)[3],(m)[4],(m)[5],\ (m)[6],(m)[7],(m)[8]) #define ELL_3MV_COL0_GET(v, m) \ (ELL_3V_SET((v), (m)[0], (m)[3], (m)[6])) #define ELL_3MV_COL1_GET(v, m) \ (ELL_3V_SET((v), (m)[1], (m)[4], (m)[7])) #define ELL_3MV_COL2_GET(v, m) \ (ELL_3V_SET((v), (m)[2], (m)[5], (m)[8])) #define ELL_3MV_ROW0_GET(v, m) \ (ELL_3V_SET((v), (m)[0], (m)[1], (m)[2])) #define ELL_3MV_ROW1_GET(v, m) \ (ELL_3V_SET((v), (m)[3], (m)[4], (m)[5])) #define ELL_3MV_ROW2_GET(v, m) \ (ELL_3V_SET((v), (m)[6], (m)[7], (m)[8])) #define ELL_3MV_COL0_SET(m, v) \ (ELL_3V_GET((m)[0], (m)[3], (m)[6], (v))) #define ELL_3MV_COL1_SET(m, v) \ (ELL_3V_GET((m)[1], (m)[4], (m)[7], (v))) #define ELL_3MV_COL2_SET(m, v) \ (ELL_3V_GET((m)[2], (m)[5], (m)[8], (v))) #define ELL_3MV_ROW0_SET(m, v) \ (ELL_3V_GET((m)[0], (m)[1], (m)[2], (v))) #define ELL_3MV_ROW1_SET(m, v) \ (ELL_3V_GET((m)[3], (m)[4], (m)[5], (v))) #define ELL_3MV_ROW2_SET(m, v) \ (ELL_3V_GET((m)[6], (m)[7], (m)[8], (v))) #define ELL_3MV_OUTER(m, v1, v2) \ (ELL_3V_SCALE((m)+0, (v1)[0], (v2)), \ ELL_3V_SCALE((m)+3, (v1)[1], (v2)), \ ELL_3V_SCALE((m)+6, (v1)[2], (v2))) #define ELL_3MV_OUTER_TT(m, T, v1, v2) \ (ELL_3V_SCALE_TT((m)+0, T, (v1)[0], (v2)), \ ELL_3V_SCALE_TT((m)+3, T, (v1)[1], (v2)), \ ELL_3V_SCALE_TT((m)+6, T, (v1)[2], (v2))) #define ELL_3MV_OUTER_INCR(m, v1, v2) \ (ELL_3V_SCALE_INCR((m)+0, (v1)[0], (v2)), \ ELL_3V_SCALE_INCR((m)+3, (v1)[1], (v2)), \ ELL_3V_SCALE_INCR((m)+6, (v1)[2], (v2))) #define ELL_3MV_SCALE_OUTER_INCR(m, s, v1, v2) \ (ELL_3V_SCALE_INCR((m)+0, (s)*(v1)[0], (v2)), \ ELL_3V_SCALE_INCR((m)+3, (s)*(v1)[1], (v2)), \ ELL_3V_SCALE_INCR((m)+6, (s)*(v1)[2], (v2))) #define ELL_3MV_MUL(v2, m, v1) \ ((v2)[0] = (m)[0]*(v1)[0] + (m)[1]*(v1)[1] + (m)[2]*(v1)[2], \ (v2)[1] = (m)[3]*(v1)[0] + (m)[4]*(v1)[1] + (m)[5]*(v1)[2], \ (v2)[2] = (m)[6]*(v1)[0] + (m)[7]*(v1)[1] + (m)[8]*(v1)[2]) #define ELL_3MV_CONTR(m, v) \ ((m)[0]*(v)[0]*(v)[0] + (m)[1]*(v)[1]*(v)[0] + (m)[2]*(v)[2]*(v)[0] + \ (m)[3]*(v)[0]*(v)[1] + (m)[4]*(v)[1]*(v)[1] + (m)[5]*(v)[2]*(v)[1] + \ (m)[6]*(v)[0]*(v)[2] + (m)[7]*(v)[1]*(v)[2] + (m)[8]*(v)[2]*(v)[2]) #define ELL_3MV_CONTR2(u, m, v) \ ((m)[0]*(v)[0]*(u)[0] + (m)[1]*(v)[1]*(u)[0] + (m)[2]*(v)[2]*(u)[0] + \ (m)[3]*(v)[0]*(u)[1] + (m)[4]*(v)[1]*(u)[1] + (m)[5]*(v)[2]*(u)[1] + \ (m)[6]*(v)[0]*(u)[2] + (m)[7]*(v)[1]*(u)[2] + (m)[8]*(v)[2]*(u)[2]) #define ELL_3MV_MUL_TT(v2, TT, m, v1) \ ((v2)[0] = AIR_CAST(TT, (m)[0]*(v1)[0] + (m)[1]*(v1)[1] + (m)[2]*(v1)[2]), \ (v2)[1] = AIR_CAST(TT, (m)[3]*(v1)[0] + (m)[4]*(v1)[1] + (m)[5]*(v1)[2]), \ (v2)[2] = AIR_CAST(TT, (m)[6]*(v1)[0] + (m)[7]*(v1)[1] + (m)[8]*(v1)[2])) #define ELL_3MV_TMUL(v2, m, v1) \ ((v2)[0] = (m)[0]*(v1)[0] + (m)[3]*(v1)[1] + (m)[6]*(v1)[2], \ (v2)[1] = (m)[1]*(v1)[0] + (m)[4]*(v1)[1] + (m)[7]*(v1)[2], \ (v2)[2] = (m)[2]*(v1)[0] + (m)[5]*(v1)[1] + (m)[8]*(v1)[2]) #define ELL_3M_MUL(m3, m1, m2) \ ((m3)[0] = (m1)[0]*(m2)[0] + (m1)[1]*(m2)[3] + (m1)[2]*(m2)[6], \ (m3)[1] = (m1)[0]*(m2)[1] + (m1)[1]*(m2)[4] + (m1)[2]*(m2)[7], \ (m3)[2] = (m1)[0]*(m2)[2] + (m1)[1]*(m2)[5] + (m1)[2]*(m2)[8], \ \ (m3)[3] = (m1)[3]*(m2)[0] + (m1)[4]*(m2)[3] + (m1)[5]*(m2)[6], \ (m3)[4] = (m1)[3]*(m2)[1] + (m1)[4]*(m2)[4] + (m1)[5]*(m2)[7], \ (m3)[5] = (m1)[3]*(m2)[2] + (m1)[4]*(m2)[5] + (m1)[5]*(m2)[8], \ \ (m3)[6] = (m1)[6]*(m2)[0] + (m1)[7]*(m2)[3] + (m1)[8]*(m2)[6], \ (m3)[7] = (m1)[6]*(m2)[1] + (m1)[7]*(m2)[4] + (m1)[8]*(m2)[7], \ (m3)[8] = (m1)[6]*(m2)[2] + (m1)[7]*(m2)[5] + (m1)[8]*(m2)[8]) #define ELL_3M_MUL_TT(m3, TT, m1, m2) \ ((m3)[0] = AIR_CAST(TT, (m1)[0]*(m2)[0]+(m1)[1]*(m2)[3]+(m1)[2]*(m2)[6]), \ (m3)[1] = AIR_CAST(TT, (m1)[0]*(m2)[1]+(m1)[1]*(m2)[4]+(m1)[2]*(m2)[7]), \ (m3)[2] = AIR_CAST(TT, (m1)[0]*(m2)[2]+(m1)[1]*(m2)[5]+(m1)[2]*(m2)[8]), \ \ (m3)[3] = AIR_CAST(TT, (m1)[3]*(m2)[0]+(m1)[4]*(m2)[3]+(m1)[5]*(m2)[6]), \ (m3)[4] = AIR_CAST(TT, (m1)[3]*(m2)[1]+(m1)[4]*(m2)[4]+(m1)[5]*(m2)[7]), \ (m3)[5] = AIR_CAST(TT, (m1)[3]*(m2)[2]+(m1)[4]*(m2)[5]+(m1)[5]*(m2)[8]), \ \ (m3)[6] = AIR_CAST(TT, (m1)[6]*(m2)[0]+(m1)[7]*(m2)[3]+(m1)[8]*(m2)[6]), \ (m3)[7] = AIR_CAST(TT, (m1)[6]*(m2)[1]+(m1)[7]*(m2)[4]+(m1)[8]*(m2)[7]), \ (m3)[8] = AIR_CAST(TT, (m1)[6]*(m2)[2]+(m1)[7]*(m2)[5]+(m1)[8]*(m2)[8])) #define ELL_3M_INV(m2, m1, det) \ ((det) = ELL_3M_DET(m1), \ (m2)[0] = _ELL_2M_DET((m1)[4],(m1)[5],(m1)[7],(m1)[8])/(det), \ (m2)[1] = -_ELL_2M_DET((m1)[1],(m1)[2],(m1)[7],(m1)[8])/(det), \ (m2)[2] = _ELL_2M_DET((m1)[1],(m1)[2],(m1)[4],(m1)[5])/(det), \ (m2)[3] = -_ELL_2M_DET((m1)[3],(m1)[5],(m1)[6],(m1)[8])/(det), \ (m2)[4] = _ELL_2M_DET((m1)[0],(m1)[2],(m1)[6],(m1)[8])/(det), \ (m2)[5] = -_ELL_2M_DET((m1)[0],(m1)[2],(m1)[3],(m1)[5])/(det), \ (m2)[6] = _ELL_2M_DET((m1)[3],(m1)[4],(m1)[6],(m1)[7])/(det), \ (m2)[7] = -_ELL_2M_DET((m1)[0],(m1)[1],(m1)[6],(m1)[7])/(det), \ (m2)[8] = _ELL_2M_DET((m1)[0],(m1)[1],(m1)[3],(m1)[4])/(det)) #define ELL_3M_SCALE_SET(m, x, y, z) \ (ELL_3V_SET((m)+ 0, (x), 0 , 0 ), \ ELL_3V_SET((m)+ 3, 0 , (y), 0 ), \ ELL_3V_SET((m)+ 6, 0 , 0 , (z))) #define ELL_3M_ROTATE_X_SET(m, th) \ (ELL_3V_SET((m)+ 0, 1 , 0 , 0 ), \ ELL_3V_SET((m)+ 3, 0 , cos(th) , -sin(th)), \ ELL_3V_SET((m)+ 6, 0 , +sin(th) , cos(th))) #define ELL_3M_ROTATE_Y_SET(m, th) \ (ELL_3V_SET((m)+ 0, cos(th) , 0 , +sin(th)), \ ELL_3V_SET((m)+ 3, 0 , 1 , 0 ), \ ELL_3V_SET((m)+ 6, -sin(th) , 0 , cos(th))) #define ELL_3M_ROTATE_Z_SET(m, th) \ (ELL_3V_SET((m)+ 0, cos(th) , -sin(th) , 0), \ ELL_3V_SET((m)+ 3, +sin(th) , cos(th) , 0), \ ELL_3V_SET((m)+ 6, 0 , 0 , 1)) /* ** the 4x4 matrix-related macros assume that the matrix indexing is: ** ** 0 1 2 3 ** 4 5 6 7 ** 8 9 10 11 ** 12 13 14 15 */ #define ELL_4V_SET(v, a, b, c, d) \ ((v)[0] = (a), (v)[1] = (b), (v)[2] = (c), (v)[3] = (d)) #define ELL_4V_ZERO_SET(v) ELL_4V_SET(v, 0, 0, 0, 0) #define ELL_4V_NAN_SET(v) ( \ (v)[0] = AIR_NAN, \ (v)[1] = AIR_NAN, \ (v)[2] = AIR_NAN, \ (v)[3] = AIR_NAN) #define ELL_4V_SET_TT(v, TT, a, b, c, d) \ ((v)[0] = AIR_CAST(TT, (a)), \ (v)[1] = AIR_CAST(TT, (b)), \ (v)[2] = AIR_CAST(TT, (c)), \ (v)[3] = AIR_CAST(TT, (d))) #define ELL_4V_GET(a, b, c, d, v) \ ((a) = (v)[0], (b) = (v)[1], (c) = (v)[2], (d) = (v)[3]) #define ELL_4V_EQUAL(a, b) \ ((a)[0]==(b)[0] && (a)[1]==(b)[1] && (a)[2]==(b)[2] && (a)[3]==(b)[3]) #define ELL_4V_COPY(v2, v1) \ ((v2)[0] = (v1)[0], \ (v2)[1] = (v1)[1], \ (v2)[2] = (v1)[2], \ (v2)[3] = (v1)[3]) #define ELL_4V_COPY_TT(v2, TT, v1) \ ((v2)[0] = AIR_CAST(TT, (v1)[0]), \ (v2)[1] = AIR_CAST(TT, (v1)[1]), \ (v2)[2] = AIR_CAST(TT, (v1)[2]), \ (v2)[3] = AIR_CAST(TT, (v1)[3])) #define ELL_4V_INCR(v2, v1) \ ((v2)[0] += (v1)[0], \ (v2)[1] += (v1)[1], \ (v2)[2] += (v1)[2], \ (v2)[3] += (v1)[3]) #define ELL_4V_ADD2(v3, v1, v2) \ ((v3)[0] = (v1)[0] + (v2)[0], \ (v3)[1] = (v1)[1] + (v2)[1], \ (v3)[2] = (v1)[2] + (v2)[2], \ (v3)[3] = (v1)[3] + (v2)[3]) #define ELL_4V_SUB(v3, v1, v2) \ ((v3)[0] = (v1)[0] - (v2)[0], \ (v3)[1] = (v1)[1] - (v2)[1], \ (v3)[2] = (v1)[2] - (v2)[2], \ (v3)[3] = (v1)[3] - (v2)[3]) #define ELL_4V_DOT(v1, v2) \ ((v1)[0]*(v2)[0] + (v1)[1]*(v2)[1] + (v1)[2]*(v2)[2] + (v1)[3]*(v2)[3]) #define ELL_4V_SCALE(v2, a, v1) \ ((v2)[0] = (v1)[0]*a, (v2)[1] = (v1)[1]*a, \ (v2)[2] = (v1)[2]*a, (v2)[3] = (v1)[3]*a) #define ELL_4V_SCALE_TT(v2, TT, a, v1) \ ((v2)[0] = AIR_CAST(TT, (v1)[0]*(a)), \ (v2)[1] = AIR_CAST(TT, (v1)[1]*(a)), \ (v2)[2] = AIR_CAST(TT, (v1)[2]*(a)), \ (v2)[3] = AIR_CAST(TT, (v1)[3]*(a))) #define ELL_4V_SCALE_ADD2(v2, s0, v0, s1, v1) \ ((v2)[0] = (s0)*(v0)[0] + (s1)*(v1)[0], \ (v2)[1] = (s0)*(v0)[1] + (s1)*(v1)[1], \ (v2)[2] = (s0)*(v0)[2] + (s1)*(v1)[2], \ (v2)[3] = (s0)*(v0)[3] + (s1)*(v1)[3]) #define ELL_4V_SCALE_ADD3(v, s0, v0, s1, v1, s2, v2) \ ((v)[0] = (s0)*(v0)[0] + (s1)*(v1)[0] + (s2)*(v2)[0], \ (v)[1] = (s0)*(v0)[1] + (s1)*(v1)[1] + (s2)*(v2)[1], \ (v)[2] = (s0)*(v0)[2] + (s1)*(v1)[2] + (s2)*(v2)[2], \ (v)[3] = (s0)*(v0)[3] + (s1)*(v1)[3] + (s2)*(v2)[3]) #define ELL_4V_SCALE_ADD4(v, s0, v0, s1, v1, s2, v2, s3, v3) \ ((v)[0] = (s0)*(v0)[0] + (s1)*(v1)[0] + (s2)*(v2)[0] + (s3)*(v3)[0], \ (v)[1] = (s0)*(v0)[1] + (s1)*(v1)[1] + (s2)*(v2)[1] + (s3)*(v3)[1], \ (v)[2] = (s0)*(v0)[2] + (s1)*(v1)[2] + (s2)*(v2)[2] + (s3)*(v3)[2], \ (v)[3] = (s0)*(v0)[3] + (s1)*(v1)[3] + (s2)*(v2)[3] + (s3)*(v3)[3]) #define ELL_4V_SCALE_INCR(v2, s0, v0) \ ((v2)[0] += (s0)*(v0)[0], \ (v2)[1] += (s0)*(v0)[1], \ (v2)[2] += (s0)*(v0)[2], \ (v2)[3] += (s0)*(v0)[3]) #define ELL_4V_LEN(v) (sqrt(ELL_4V_DOT((v),(v)))) #define ELL_4V_NORM(v2, v1, length) \ (length = ELL_4V_LEN(v1), ELL_4V_SCALE(v2, 1.0/length, v1)) #define ELL_4V_LERP(v3, w, v1, v2) \ ((v3)[0] = AIR_LERP((w), (v1)[0], (v2)[0]), \ (v3)[1] = AIR_LERP((w), (v1)[1], (v2)[1]), \ (v3)[2] = AIR_LERP((w), (v1)[2], (v2)[2]), \ (v3)[3] = AIR_LERP((w), (v1)[3], (v2)[3])) #define ELL_4V_LERP_TT(v3, TT, w, v1, v2) \ ((v3)[0] = AIR_CAST(TT, AIR_LERP((w), (v1)[0], (v2)[0])), \ (v3)[1] = AIR_CAST(TT, AIR_LERP((w), (v1)[1], (v2)[1])), \ (v3)[2] = AIR_CAST(TT, AIR_LERP((w), (v1)[2], (v2)[2])), \ (v3)[3] = AIR_CAST(TT, AIR_LERP((w), (v1)[3], (v2)[3]))) #define ELL_4V_EXISTS(v) \ (AIR_EXISTS((v)[0]) && AIR_EXISTS((v)[1]) \ && AIR_EXISTS((v)[2]) && AIR_EXISTS((v)[3])) #define ELL_4M_EQUAL(m1, m2) \ ((m1)[ 0] == (m2)[ 0] && \ (m1)[ 1] == (m2)[ 1] && \ (m1)[ 2] == (m2)[ 2] && \ (m1)[ 3] == (m2)[ 3] && \ (m1)[ 4] == (m2)[ 4] && \ (m1)[ 5] == (m2)[ 5] && \ (m1)[ 6] == (m2)[ 6] && \ (m1)[ 7] == (m2)[ 7] && \ (m1)[ 8] == (m2)[ 8] && \ (m1)[ 9] == (m2)[ 9] && \ (m1)[10] == (m2)[10] && \ (m1)[11] == (m2)[11] && \ (m1)[12] == (m2)[12] && \ (m1)[13] == (m2)[13] && \ (m1)[14] == (m2)[14] && \ (m1)[15] == (m2)[15]) #define ELL_4M_ADD2(m3, m1, m2) \ (ELL_4V_ADD2((m3)+ 0, (m1)+ 0, (m2)+ 0), \ ELL_4V_ADD2((m3)+ 4, (m1)+ 4, (m2)+ 4), \ ELL_4V_ADD2((m3)+ 8, (m1)+ 8, (m2)+ 8), \ ELL_4V_ADD2((m3)+12, (m1)+12, (m2)+12)) #define ELL_4M_SUB(m3, m1, m2) \ (ELL_4V_SUB((m3)+ 0, (m1)+ 0, (m2)+ 0), \ ELL_4V_SUB((m3)+ 4, (m1)+ 4, (m2)+ 4), \ ELL_4V_SUB((m3)+ 8, (m1)+ 8, (m2)+ 8), \ ELL_4V_SUB((m3)+12, (m1)+12, (m2)+12)) #define ELL_4M_SCALE(m2, a, m1) \ (ELL_4V_SCALE((m2)+ 0, (a), (m1)+ 0), \ ELL_4V_SCALE((m2)+ 4, (a), (m1)+ 4), \ ELL_4V_SCALE((m2)+ 8, (a), (m1)+ 8), \ ELL_4V_SCALE((m2)+12, (a), (m1)+12)) #define ELL_4M_COPY(m2, m1) \ (ELL_4V_COPY((m2)+ 0, (m1)+ 0), \ ELL_4V_COPY((m2)+ 4, (m1)+ 4), \ ELL_4V_COPY((m2)+ 8, (m1)+ 8), \ ELL_4V_COPY((m2)+12, (m1)+12)) #define ELL_4M_COPY_TT(m2, TT, m1) \ (ELL_4V_COPY_TT((m2)+ 0, TT, (m1)+ 0), \ ELL_4V_COPY_TT((m2)+ 4, TT, (m1)+ 4), \ ELL_4V_COPY_TT((m2)+ 8, TT, (m1)+ 8), \ ELL_4V_COPY_TT((m2)+12, TT, (m1)+12)) #define ELL_4M_TRANSPOSE(m2, m1) \ ((m2)[ 0] = (m1)[ 0], \ (m2)[ 1] = (m1)[ 4], \ (m2)[ 2] = (m1)[ 8], \ (m2)[ 3] = (m1)[12], \ (m2)[ 4] = (m1)[ 1], \ (m2)[ 5] = (m1)[ 5], \ (m2)[ 6] = (m1)[ 9], \ (m2)[ 7] = (m1)[13], \ (m2)[ 8] = (m1)[ 2], \ (m2)[ 9] = (m1)[ 6], \ (m2)[10] = (m1)[10], \ (m2)[11] = (m1)[14], \ (m2)[12] = (m1)[ 3], \ (m2)[13] = (m1)[ 7], \ (m2)[14] = (m1)[11], \ (m2)[15] = (m1)[15]) #define ELL_4M_TRANSPOSE_TT(m2, TT, m1) \ ((m2)[ 0] = AIR_CAST(TT, (m1)[ 0]), \ (m2)[ 1] = AIR_CAST(TT, (m1)[ 4]), \ (m2)[ 2] = AIR_CAST(TT, (m1)[ 8]), \ (m2)[ 3] = AIR_CAST(TT, (m1)[12]), \ (m2)[ 4] = AIR_CAST(TT, (m1)[ 1]), \ (m2)[ 5] = AIR_CAST(TT, (m1)[ 5]), \ (m2)[ 6] = AIR_CAST(TT, (m1)[ 9]), \ (m2)[ 7] = AIR_CAST(TT, (m1)[13]), \ (m2)[ 8] = AIR_CAST(TT, (m1)[ 2]), \ (m2)[ 9] = AIR_CAST(TT, (m1)[ 6]), \ (m2)[10] = AIR_CAST(TT, (m1)[10]), \ (m2)[11] = AIR_CAST(TT, (m1)[14]), \ (m2)[12] = AIR_CAST(TT, (m1)[ 3]), \ (m2)[13] = AIR_CAST(TT, (m1)[ 7]), \ (m2)[14] = AIR_CAST(TT, (m1)[11]), \ (m2)[15] = AIR_CAST(TT, (m1)[15])) #define ELL_4M_TRANSPOSE_IP(m, t) \ (ELL_SWAP2((m)[ 1],(m)[ 4],(t)), \ ELL_SWAP2((m)[ 2],(m)[ 8],(t)), \ ELL_SWAP2((m)[ 3],(m)[12],(t)), \ ELL_SWAP2((m)[ 6],(m)[ 9],(t)), \ ELL_SWAP2((m)[ 7],(m)[13],(t)), \ ELL_SWAP2((m)[11],(m)[14],(t))) #define ELL_4MV_ROW0_GET(v, m) \ (ELL_4V_SET((v), (m)[ 0], (m)[ 1], (m)[ 2], (m)[ 3])) #define ELL_4MV_ROW1_GET(v, m) \ (ELL_4V_SET((v), (m)[ 4], (m)[ 5], (m)[ 6], (m)[ 7])) #define ELL_4MV_ROW2_GET(v, m) \ (ELL_4V_SET((v), (m)[ 8], (m)[ 9], (m)[10], (m)[11])) #define ELL_4MV_ROW3_GET(v, m) \ (ELL_4V_SET((v), (m)[12], (m)[13], (m)[14], (m)[15])) #define ELL_4MV_COL0_GET(v, m) \ (ELL_4V_SET((v), (m)[ 0], (m)[ 4], (m)[ 8], (m)[12])) #define ELL_4MV_COL1_GET(v, m) \ (ELL_4V_SET((v), (m)[ 1], (m)[ 5], (m)[ 9], (m)[13])) #define ELL_4MV_COL2_GET(v, m) \ (ELL_4V_SET((v), (m)[ 2], (m)[ 6], (m)[10], (m)[14])) #define ELL_4MV_COL3_GET(v, m) \ (ELL_4V_SET((v), (m)[ 3], (m)[ 7], (m)[11], (m)[15])) #define ELL_4MV_ROW0_SET(m, v) \ (ELL_4V_GET((m)[ 0], (m)[ 1], (m)[ 2], (m)[ 3], (v))) #define ELL_4MV_ROW1_SET(m, v) \ (ELL_4V_GET((m)[ 4], (m)[ 5], (m)[ 6], (m)[ 7], (v))) #define ELL_4MV_ROW2_SET(m, v) \ (ELL_4V_GET((m)[ 8], (m)[ 9], (m)[10], (m)[11], (v))) #define ELL_4MV_ROW3_SET(m, v) \ (ELL_4V_GET((m)[12], (m)[13], (m)[14], (m)[15], (v))) #define ELL_4MV_COL0_SET(m, v) \ (ELL_4V_GET((m)[ 0], (m)[ 4], (m)[ 8], (m)[12], (v))) #define ELL_4MV_COL1_SET(m, v) \ (ELL_4V_GET((m)[ 1], (m)[ 5], (m)[ 9], (m)[13], (v))) #define ELL_4MV_COL2_SET(m, v) \ (ELL_4V_GET((m)[ 2], (m)[ 6], (m)[10], (m)[14], (v))) #define ELL_4MV_COL3_SET(m, v) \ (ELL_4V_GET((m)[ 3], (m)[ 7], (m)[11], (m)[15], (v))) #define ELL_4MV_MUL(v2, m, v1) \ ((v2)[0]=(m)[ 0]*(v1)[0]+(m)[ 1]*(v1)[1]+(m)[ 2]*(v1)[2]+(m)[ 3]*(v1)[3], \ (v2)[1]=(m)[ 4]*(v1)[0]+(m)[ 5]*(v1)[1]+(m)[ 6]*(v1)[2]+(m)[ 7]*(v1)[3], \ (v2)[2]=(m)[ 8]*(v1)[0]+(m)[ 9]*(v1)[1]+(m)[10]*(v1)[2]+(m)[11]*(v1)[3], \ (v2)[3]=(m)[12]*(v1)[0]+(m)[13]*(v1)[1]+(m)[14]*(v1)[2]+(m)[15]*(v1)[3]) #define ELL_4MV_MUL_TT(v2, TT, m, v1) \ ((v2)[0]=AIR_CAST(TT, ((m)[ 0]*(v1)[0]+(m)[ 1]*(v1)[1] \ +(m)[ 2]*(v1)[2]+(m)[ 3]*(v1)[3])), \ (v2)[1]=AIR_CAST(TT, ((m)[ 4]*(v1)[0]+(m)[ 5]*(v1)[1] \ +(m)[ 6]*(v1)[2]+(m)[ 7]*(v1)[3])), \ (v2)[2]=AIR_CAST(TT, ((m)[ 8]*(v1)[0]+(m)[ 9]*(v1)[1] \ +(m)[10]*(v1)[2]+(m)[11]*(v1)[3])), \ (v2)[3]=AIR_CAST(TT, ((m)[12]*(v1)[0]+(m)[13]*(v1)[1] \ +(m)[14]*(v1)[2]+(m)[15]*(v1)[3]))) #define ELL_4MV_TMUL(v2, m, v1) \ ((v2)[0]=(m)[ 0]*(v1)[0]+(m)[ 4]*(v1)[1]+(m)[ 8]*(v1)[2]+(m)[12]*(v1)[3], \ (v2)[1]=(m)[ 1]*(v1)[0]+(m)[ 5]*(v1)[1]+(m)[ 9]*(v1)[2]+(m)[13]*(v1)[3], \ (v2)[2]=(m)[ 2]*(v1)[0]+(m)[ 6]*(v1)[1]+(m)[10]*(v1)[2]+(m)[14]*(v1)[3], \ (v2)[3]=(m)[ 3]*(v1)[0]+(m)[ 7]*(v1)[1]+(m)[11]*(v1)[2]+(m)[15]*(v1)[3]) #define ELL_34V_HOMOG(v2, v1) \ ((v2)[0] = (v1)[0]/(v1)[3], \ (v2)[1] = (v1)[1]/(v1)[3], \ (v2)[2] = (v1)[2]/(v1)[3]) /* 3-vector v2 = 4x4 homog coord matrix m times 3-vector v1, using 4-vector tv as tmp */ #define ELL_4M3V_HOMOG_MUL(v2, m, v1, tv) \ ((tv)[0]=(m)[ 0]*(v1)[0]+(m)[ 1]*(v1)[1]+(m)[ 2]*(v1)[2]+(m)[ 3], \ (tv)[1]=(m)[ 4]*(v1)[0]+(m)[ 5]*(v1)[1]+(m)[ 6]*(v1)[2]+(m)[ 7], \ (tv)[2]=(m)[ 8]*(v1)[0]+(m)[ 9]*(v1)[1]+(m)[10]*(v1)[2]+(m)[11], \ (tv)[3]=(m)[12]*(v1)[0]+(m)[13]*(v1)[1]+(m)[14]*(v1)[2]+(m)[15]); \ ELL_34V_HOMOG(v2, tv) #define ELL_34V_HOMOG_TT(v2, TT, v1) \ ((v2)[0] = AIR_CAST(TT, (v1)[0]/(v1)[3]), \ (v2)[1] = AIR_CAST(TT, (v1)[1]/(v1)[3]), \ (v2)[2] = AIR_CAST(TT, (v1)[2]/(v1)[3])) #define ELL_4V_HOMOG(v2, v1) \ ((v2)[0] = (v1)[0]/(v1)[3], \ (v2)[1] = (v1)[1]/(v1)[3], \ (v2)[2] = (v1)[2]/(v1)[3], \ (v2)[3] = 1.0) /* ** These macros are intended to be used as aids with homogeneous transforms */ #define ELL_4M_COLS_SET(m, a, b, c, d) \ (ELL_4V_SET((m)+ 0, (a)[0], (b)[0], (c)[0], (d)[0]), \ ELL_4V_SET((m)+ 4, (a)[1], (b)[1], (c)[1], (d)[1]), \ ELL_4V_SET((m)+ 8, (a)[2], (b)[2], (c)[2], (d)[2]), \ ELL_4V_SET((m)+12, (a)[3], (b)[3], (c)[3], (d)[3])) #define ELL_4M_ROWS_SET(m, a, b, c, d) \ (ELL_4V_COPY((m)+ 0, a), \ ELL_4V_COPY((m)+ 4, b), \ ELL_4V_COPY((m)+ 8, c), \ ELL_4V_COPY((m)+12, d)) #define ELL_4M_IDENTITY_SET(m) \ (ELL_4V_SET((m)+ 0, 1 , 0 , 0 , 0), \ ELL_4V_SET((m)+ 4, 0 , 1 , 0 , 0), \ ELL_4V_SET((m)+ 8, 0 , 0 , 1 , 0), \ ELL_4V_SET((m)+12, 0 , 0 , 0 , 1)) #define ELL_4M_EXISTS(m) \ (ELL_4V_EXISTS((m) + 0) \ && ELL_4V_EXISTS((m) + 4) \ && ELL_4V_EXISTS((m) + 8) \ && ELL_4V_EXISTS((m) + 12)) #define ELL_4M_ZERO_SET(m) \ (ELL_4V_SET((m)+ 0, 0 , 0 , 0 , 0), \ ELL_4V_SET((m)+ 4, 0 , 0 , 0 , 0), \ ELL_4V_SET((m)+ 8, 0 , 0 , 0 , 0), \ ELL_4V_SET((m)+12, 0 , 0 , 0 , 0)) #define ELL_4M_SCALE_SET(m, x, y, z) \ (ELL_4V_SET((m)+ 0, (x), 0 , 0 , 0), \ ELL_4V_SET((m)+ 4, 0 , (y), 0 , 0), \ ELL_4V_SET((m)+ 8, 0 , 0 , (z), 0), \ ELL_4V_SET((m)+12, 0 , 0 , 0 , 1)) #define ELL_4M_TRANSLATE_SET(m, x, y, z) \ (ELL_4V_SET((m)+ 0, 1 , 0 , 0 , (x)), \ ELL_4V_SET((m)+ 4, 0 , 1 , 0 , (y)), \ ELL_4V_SET((m)+ 8, 0 , 0 , 1 , (z)), \ ELL_4V_SET((m)+12, 0 , 0 , 0 , 1)) #define ELL_4M_ROTATE_X_SET(m, th) \ (ELL_4V_SET((m)+ 0, 1 , 0 , 0 , 0), \ ELL_4V_SET((m)+ 4, 0 , cos(th) , -sin(th) , 0), \ ELL_4V_SET((m)+ 8, 0 , +sin(th) , cos(th) , 0), \ ELL_4V_SET((m)+12, 0 , 0 , 0 , 1)) #define ELL_4M_ROTATE_Y_SET(m, th) \ (ELL_4V_SET((m)+ 0, cos(th) , 0 , +sin(th) , 0), \ ELL_4V_SET((m)+ 4, 0 , 1 , 0 , 0), \ ELL_4V_SET((m)+ 8, -sin(th) , 0 , cos(th) , 0), \ ELL_4V_SET((m)+12, 0 , 0 , 0 , 1)) #define ELL_4M_ROTATE_Z_SET(m, th) \ (ELL_4V_SET((m)+ 0, cos(th) , -sin(th) , 0 , 0), \ ELL_4V_SET((m)+ 4, +sin(th) , cos(th) , 0 , 0), \ ELL_4V_SET((m)+ 8, 0 , 0 , 1 , 0), \ ELL_4V_SET((m)+12, 0 , 0 , 0 , 1)) #define ELL_4M_NAN_SET(m) \ (ELL_4V_NAN_SET((m)+ 0), \ ELL_4V_NAN_SET((m)+ 4), \ ELL_4V_NAN_SET((m)+ 8), \ ELL_4V_NAN_SET((m)+ 12)) #define ELL_4M_MUL(n, l, m) \ ((n)[ 0]=(l)[ 0]*(m)[ 0]+(l)[ 1]*(m)[ 4]+(l)[ 2]*(m)[ 8]+(l)[ 3]*(m)[12], \ (n)[ 1]=(l)[ 0]*(m)[ 1]+(l)[ 1]*(m)[ 5]+(l)[ 2]*(m)[ 9]+(l)[ 3]*(m)[13], \ (n)[ 2]=(l)[ 0]*(m)[ 2]+(l)[ 1]*(m)[ 6]+(l)[ 2]*(m)[10]+(l)[ 3]*(m)[14], \ (n)[ 3]=(l)[ 0]*(m)[ 3]+(l)[ 1]*(m)[ 7]+(l)[ 2]*(m)[11]+(l)[ 3]*(m)[15], \ \ (n)[ 4]=(l)[ 4]*(m)[ 0]+(l)[ 5]*(m)[ 4]+(l)[ 6]*(m)[ 8]+(l)[ 7]*(m)[12], \ (n)[ 5]=(l)[ 4]*(m)[ 1]+(l)[ 5]*(m)[ 5]+(l)[ 6]*(m)[ 9]+(l)[ 7]*(m)[13], \ (n)[ 6]=(l)[ 4]*(m)[ 2]+(l)[ 5]*(m)[ 6]+(l)[ 6]*(m)[10]+(l)[ 7]*(m)[14], \ (n)[ 7]=(l)[ 4]*(m)[ 3]+(l)[ 5]*(m)[ 7]+(l)[ 6]*(m)[11]+(l)[ 7]*(m)[15], \ \ (n)[ 8]=(l)[ 8]*(m)[ 0]+(l)[ 9]*(m)[ 4]+(l)[10]*(m)[ 8]+(l)[11]*(m)[12], \ (n)[ 9]=(l)[ 8]*(m)[ 1]+(l)[ 9]*(m)[ 5]+(l)[10]*(m)[ 9]+(l)[11]*(m)[13], \ (n)[10]=(l)[ 8]*(m)[ 2]+(l)[ 9]*(m)[ 6]+(l)[10]*(m)[10]+(l)[11]*(m)[14], \ (n)[11]=(l)[ 8]*(m)[ 3]+(l)[ 9]*(m)[ 7]+(l)[10]*(m)[11]+(l)[11]*(m)[15], \ \ (n)[12]=(l)[12]*(m)[ 0]+(l)[13]*(m)[ 4]+(l)[14]*(m)[ 8]+(l)[15]*(m)[12], \ (n)[13]=(l)[12]*(m)[ 1]+(l)[13]*(m)[ 5]+(l)[14]*(m)[ 9]+(l)[15]*(m)[13], \ (n)[14]=(l)[12]*(m)[ 2]+(l)[13]*(m)[ 6]+(l)[14]*(m)[10]+(l)[15]*(m)[14], \ (n)[15]=(l)[12]*(m)[ 3]+(l)[13]*(m)[ 7]+(l)[14]*(m)[11]+(l)[15]*(m)[15]) #define ELL_34M_EXTRACT(m, l) \ ((m)[0] = (l)[ 0], (m)[1] = (l)[ 1], (m)[2] = (l)[ 2], \ (m)[3] = (l)[ 4], (m)[4] = (l)[ 5], (m)[5] = (l)[ 6], \ (m)[6] = (l)[ 8], (m)[7] = (l)[ 9], (m)[8] = (l)[10]) #define ELL_43M_INSET(l, m) \ ((l)[ 0] = (m)[0], (l)[ 1] = (m)[1], (l)[ 2] = (m)[2], (l)[ 3] = 0, \ (l)[ 4] = (m)[3], (l)[ 5] = (m)[4], (l)[ 6] = (m)[5], (l)[ 7] = 0, \ (l)[ 8] = (m)[6], (l)[ 9] = (m)[7], (l)[10] = (m)[8], (l)[11] = 0, \ (l)[12] = 0 , (l)[13] = 0 , (l)[14] = 0 , (l)[15] = 1) #define ELL_4M_FROB(m) \ (sqrt(ELL_4V_DOT((m)+ 0, (m)+ 0) + \ ELL_4V_DOT((m)+ 4, (m)+ 4) + \ ELL_4V_DOT((m)+ 8, (m)+ 8) + \ ELL_4V_DOT((m)+12, (m)+12))) #define ELL_4M_DET(m) \ ( (m)[ 0] * _ELL_3M_DET((m)[ 5], (m)[ 6], (m)[ 7], \ (m)[ 9], (m)[10], (m)[11], \ (m)[13], (m)[14], (m)[15]) \ - (m)[ 1] * _ELL_3M_DET((m)[ 4], (m)[ 6], (m)[ 7], \ (m)[ 8], (m)[10], (m)[11], \ (m)[12], (m)[14], (m)[15]) \ + (m)[ 2] * _ELL_3M_DET((m)[ 4], (m)[ 5], (m)[ 7], \ (m)[ 8], (m)[ 9], (m)[11], \ (m)[12], (m)[13], (m)[15]) \ - (m)[ 3] * _ELL_3M_DET((m)[ 4], (m)[ 5], (m)[ 6], \ (m)[ 8], (m)[ 9], (m)[10], \ (m)[12], (m)[13], (m)[14])) #define ELL_4MV_OUTER(m, v1, v2) \ (ELL_4V_SCALE((m)+ 0, (v1)[0], (v2)), \ ELL_4V_SCALE((m)+ 4, (v1)[1], (v2)), \ ELL_4V_SCALE((m)+ 8, (v1)[2], (v2)), \ ELL_4V_SCALE((m)+12, (v1)[3], (v2))) #define ELL_4MV_OUTER_TT(m, TT, v1, v2) \ (ELL_4V_SCALE_TT((m)+ 0, TT, (v1)[0], (v2)), \ ELL_4V_SCALE_TT((m)+ 4, TT, (v1)[1], (v2)), \ ELL_4V_SCALE_TT((m)+ 8, TT, (v1)[2], (v2)), \ ELL_4V_SCALE_TT((m)+12, TT, (v1)[3], (v2))) #define ELL_4MV_OUTER_INCR(m, v1, v2) \ (ELL_4V_SCALE_INCR((m)+ 0, (v1)[0], (v2)), \ ELL_4V_SCALE_INCR((m)+ 4, (v1)[1], (v2)), \ ELL_4V_SCALE_INCR((m)+ 8, (v1)[2], (v2)), \ ELL_4V_SCALE_INCR((m)+12, (v1)[3], (v2))) #define ELL_Q_MUL(q3, q1, q2) \ ELL_4V_SET((q3), \ (q1)[0]*(q2)[0] - (q1)[1]*(q2)[1] - (q1)[2]*(q2)[2] - (q1)[3]*(q2)[3], \ (q1)[0]*(q2)[1] + (q1)[1]*(q2)[0] + (q1)[2]*(q2)[3] - (q1)[3]*(q2)[2], \ (q1)[0]*(q2)[2] - (q1)[1]*(q2)[3] + (q1)[2]*(q2)[0] + (q1)[3]*(q2)[1], \ (q1)[0]*(q2)[3] + (q1)[1]*(q2)[2] - (q1)[2]*(q2)[1] + (q1)[3]*(q2)[0]) #define ELL_Q_CONJ(q2, q1) \ ELL_4V_SET((q2), (q1)[0], -(q1)[1], -(q1)[2], -(q1)[3]) #define ELL_Q_INV(i, q, n) \ (n = ELL_4V_DOT(q, q), \ ELL_4V_SET((i), (q)[0]/(n), -(q)[1]/(n), -(q)[2]/(n), -(q)[3]/(n))) #define ELL_Q_TO_3M(m, q) \ (ELL_3V_SET((m)+0, \ (q)[0]*(q)[0] + (q)[1]*(q)[1] - (q)[2]*(q)[2] - (q)[3]*(q)[3], \ 2*((q)[1]*(q)[2] - (q)[0]*(q)[3]), \ 2*((q)[1]*(q)[3] + (q)[0]*(q)[2])), \ ELL_3V_SET((m)+3, \ 2*((q)[1]*(q)[2] + (q)[0]*(q)[3]), \ (q)[0]*(q)[0] - (q)[1]*(q)[1] + (q)[2]*(q)[2] - (q)[3]*(q)[3], \ 2*((q)[2]*(q)[3] - (q)[0]*(q)[1])), \ ELL_3V_SET((m)+6, \ 2*((q)[1]*(q)[3] - (q)[0]*(q)[2]), \ 2*((q)[2]*(q)[3] + (q)[0]*(q)[1]), \ (q)[0]*(q)[0] - (q)[1]*(q)[1] - (q)[2]*(q)[2] + (q)[3]*(q)[3])) #define ELL_Q_TO_4M(m, q) \ (ELL_4V_SET((m)+0, \ (q)[0]*(q)[0] + (q)[1]*(q)[1] - (q)[2]*(q)[2] - (q)[3]*(q)[3], \ 2*((q)[1]*(q)[2] - (q)[0]*(q)[3]), \ 2*((q)[1]*(q)[3] + (q)[0]*(q)[2]), \ 0), \ ELL_4V_SET((m)+4, \ 2*((q)[1]*(q)[2] + (q)[0]*(q)[3]), \ (q)[0]*(q)[0] - (q)[1]*(q)[1] + (q)[2]*(q)[2] - (q)[3]*(q)[3], \ 2*((q)[2]*(q)[3] - (q)[0]*(q)[1]), \ 0), \ ELL_4V_SET((m)+8, \ 2*((q)[1]*(q)[3] - (q)[0]*(q)[2]), \ 2*((q)[2]*(q)[3] + (q)[0]*(q)[1]), \ (q)[0]*(q)[0] - (q)[1]*(q)[1] - (q)[2]*(q)[2] + (q)[3]*(q)[3], \ 0), \ ELL_4V_SET((m)+12, 0, 0, 0, 1)) #define ELL_5V_SET(v, a, b, c, d, e) \ ((v)[0]=(a), (v)[1]=(b), (v)[2]=(c), (v)[3]=(d), (v)[4]=(e)) #define ELL_5V_COPY(v, w) \ ((v)[0]=(w)[0], (v)[1]=(w)[1], (v)[2]=(w)[2], (v)[3]=(w)[3], (v)[4]=(w)[4]) #define ELL_6V_SET(v, a, b, c, d, e, f) \ ((v)[0]=(a), (v)[1]=(b), (v)[2]=(c), (v)[3]=(d), (v)[4]=(e), (v)[5]=(f)) #define ELL_6V_ZERO_SET(v) ELL_6V_SET(v, 0, 0, 0, 0, 0, 0) #define ELL_6V_COPY(v2, v1) \ ((v2)[0] = (v1)[0], \ (v2)[1] = (v1)[1], \ (v2)[2] = (v1)[2], \ (v2)[3] = (v1)[3], \ (v2)[4] = (v1)[4], \ (v2)[5] = (v1)[5]) \ #define ELL_6V_INCR(v2, v1) \ ((v2)[0] += (v1)[0], \ (v2)[1] += (v1)[1], \ (v2)[2] += (v1)[2], \ (v2)[3] += (v1)[3], \ (v2)[4] += (v1)[4], \ (v2)[5] += (v1)[5]) #define ELL_6V_SCALE_INCR2(v2, s0, v0, s1, v1) \ ((v2)[0] += (s0)*(v0)[0] + (s1)*(v1)[0], \ (v2)[1] += (s0)*(v0)[1] + (s1)*(v1)[1], \ (v2)[2] += (s0)*(v0)[2] + (s1)*(v1)[2], \ (v2)[3] += (s0)*(v0)[3] + (s1)*(v1)[3], \ (v2)[4] += (s0)*(v0)[4] + (s1)*(v1)[4], \ (v2)[5] += (s0)*(v0)[5] + (s1)*(v1)[5]) #define ELL_6V_SCALE_INCR(v2, a, v1) \ ((v2)[0] += (a)*(v1)[0], \ (v2)[1] += (a)*(v1)[1], \ (v2)[2] += (a)*(v1)[2], \ (v2)[3] += (a)*(v1)[3], \ (v2)[4] += (a)*(v1)[4], \ (v2)[5] += (a)*(v1)[5]) #define ELL_6V_SCALE(v2, a, v1) \ ((v2)[0] = (a)*(v1)[0], \ (v2)[1] = (a)*(v1)[1], \ (v2)[2] = (a)*(v1)[2], \ (v2)[3] = (a)*(v1)[3], \ (v2)[4] = (a)*(v1)[4], \ (v2)[5] = (a)*(v1)[5]) #define ELL_6V_DOT(v1, v2) \ ((v1)[0]*(v2)[0] + \ (v1)[1]*(v2)[1] + \ (v1)[2]*(v2)[2] + \ (v1)[3]*(v2)[3] + \ (v1)[4]*(v2)[4] + \ (v1)[5]*(v2)[5]) #define ELL_6V_SUB(m3, m1, m2) \ ((m3)[0] = (m1)[0] - (m2)[0], \ (m3)[1] = (m1)[1] - (m2)[1], \ (m3)[2] = (m1)[2] - (m2)[2], \ (m3)[3] = (m1)[3] - (m2)[3], \ (m3)[4] = (m1)[4] - (m2)[4], \ (m3)[5] = (m1)[5] - (m2)[5]) #define ELL_6V_ADD2(m3, m1, m2) \ ((m3)[0] = (m1)[0] + (m2)[0], \ (m3)[1] = (m1)[1] + (m2)[1], \ (m3)[2] = (m1)[2] + (m2)[2], \ (m3)[3] = (m1)[3] + (m2)[3], \ (m3)[4] = (m1)[4] + (m2)[4], \ (m3)[5] = (m1)[5] + (m2)[5]) #define ELL_9V_SET(v, a, b, c, d, e, f, g, h, i) \ ((v)[0]=(a), (v)[1]=(b), (v)[2]=(c), \ (v)[3]=(d), (v)[4]=(e), (v)[5]=(f), \ (v)[6]=(g), (v)[7]=(h), (v)[8]=(i)) #define ELL_9V_COPY(v2, v1) \ ((v2)[0] = (v1)[0], \ (v2)[1] = (v1)[1], \ (v2)[2] = (v1)[2], \ (v2)[3] = (v1)[3], \ (v2)[4] = (v1)[4], \ (v2)[5] = (v1)[5], \ (v2)[6] = (v1)[6], \ (v2)[7] = (v1)[7], \ (v2)[8] = (v1)[8]) #define ELL_9V_SUB(v3, v1, v2) \ ((v3)[0] = (v1)[0] - (v2)[0], \ (v3)[1] = (v1)[1] - (v2)[1], \ (v3)[2] = (v1)[2] - (v2)[2], \ (v3)[3] = (v1)[3] - (v2)[3], \ (v3)[4] = (v1)[4] - (v2)[4], \ (v3)[5] = (v1)[5] - (v2)[5], \ (v3)[6] = (v1)[6] - (v2)[6], \ (v3)[7] = (v1)[7] - (v2)[7], \ (v3)[8] = (v1)[8] - (v2)[8]) #define ELL_9V_DOT(v1, v2) \ ((v1)[0]*(v2)[0] + \ (v1)[1]*(v2)[1] + \ (v1)[2]*(v2)[2] + \ (v1)[3]*(v2)[3] + \ (v1)[4]*(v2)[4] + \ (v1)[5]*(v2)[5] + \ (v1)[6]*(v2)[6] + \ (v1)[7]*(v2)[7] + \ (v1)[8]*(v2)[8]) #define ELL_9V_LEN(v) (sqrt(ELL_9V_DOT((v),(v)))) #define ELL_10V_ZERO_SET(v) \ ((v)[0]=0, (v)[1]=0, (v)[2]=0, \ (v)[3]=0, (v)[4]=0, (v)[5]=0, \ (v)[6]=0, (v)[7]=0, (v)[8]=0, (v)[9]=0) #define ELL_10V_SET(v, a, b, c, d, e, f, g, h, i, j) \ ((v)[0]=(a), (v)[1]=(b), (v)[2]=(c), \ (v)[3]=(d), (v)[4]=(e), (v)[5]=(f), \ (v)[6]=(g), (v)[7]=(h), (v)[8]=(i), (v)[9]=(j)) #define ELL_10V_COPY(v2, v1) \ ((v2)[0] = (v1)[0], \ (v2)[1] = (v1)[1], \ (v2)[2] = (v1)[2], \ (v2)[3] = (v1)[3], \ (v2)[4] = (v1)[4], \ (v2)[5] = (v1)[5], \ (v2)[6] = (v1)[6], \ (v2)[7] = (v1)[7], \ (v2)[8] = (v1)[8], \ (v2)[9] = (v1)[9]) #define ELL_10V_SCALE(v2, a, v1) \ ((v2)[0] = (a)*(v1)[0], \ (v2)[1] = (a)*(v1)[1], \ (v2)[2] = (a)*(v1)[2], \ (v2)[3] = (a)*(v1)[3], \ (v2)[4] = (a)*(v1)[4], \ (v2)[5] = (a)*(v1)[5], \ (v2)[6] = (a)*(v1)[6], \ (v2)[7] = (a)*(v1)[7], \ (v2)[8] = (a)*(v1)[8], \ (v2)[9] = (a)*(v1)[9]) #ifdef __cplusplus } #endif #endif /* ELLMACROS_HAS_BEEN_INCLUDED */ teem-1.11.0~svn6057/src/ell/GNUmakefile0000664000175000017500000000362712165631065017237 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #### Library name #### #### L := ell #### #### #### # boilerplate: default targets and include tricks TEEM_ROOT ?= ../.. TEEM_SRC ?= .. ifeq (,$(DEF_TARGETS)) DEF_TARGETS = true dev : $(L)/dev install : $(L)/install clean : $(L)/clean clobber : $(L)/clobber include ../GNUmakefile endif ifeq (,$($(L).SEEN)) $(L).SEEN := true #### Describe library here #### #### $(L).NEED = nrrd biff air $(L).PUBLIC_HEADERS = ell.h ellMacros.h $(L).PRIVATE_HEADERS = $(L).OBJS = cubicEll.o eigen.o miscEll.o vecEll.o mat.o quat.o genmat.o $(L).TESTS = test/sort3 test/invert test/tq test/wheel test/rot2aa \ test/inter test/mmul test/es6 #### #### #### # boilerplate: declare rules for this library include $(TEEM_SRC)/make/template.mk endif ifeq (,$(INCLUDED)) include $(TEEM_SRC)/bin/GNUmakefile endif teem-1.11.0~svn6057/src/ell/sources.cmake0000664000175000017500000000037311113047450017634 0ustar domibeldomibel# This variable will help provide a master list of all the sources. # Add new source files here. SET(ELL_SOURCES cubicEll.c eigen.c ell.h ellMacros.h genmat.c mat.c miscEll.c quat.c vecEll.c ) ADD_TEEM_LIBRARY(ell ${ELL_SOURCES}) teem-1.11.0~svn6057/src/ell/cubicEll.c0000664000175000017500000001233312165631065017045 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ell.h" /* ******** ell_cubic(): ** ** finds real roots of x^3 + A*x^2 + B*x + C. ** ** records the found real roots in the given root array. ** ** returns information about the roots according to ellCubicRoot enum, ** the set the following values in given root[] array: ** ell_cubic_root_single: root[0], root[1] == root[2] == AIR_NAN ** ell_cubic_root_triple: root[0] == root[1] == root[2] ** ell_cubic_root_single_double: single root[0]; double root[1] == root[2] ** or double root[0] == root[1], single root[2] ** ell_cubic_root_three: root[0], root[1], root[2] ** ** The values stored in root[] are, in a change from the past, sorted ** in descending order! No need to sort them any more! ** ** This does NOT use biff */ int ell_cubic(double root[3], double A, double B, double C, int newton) { char me[]="ell_cubic"; double epsilon = 1.0E-11, AA, Q, R, QQQ, D, sqrt_D, der, u, v, x, theta, t, sub; /* printf("%s: A B C = %g %g %g\n", me, A, B, C); */ sub = A/3.0; AA = A*A; Q = (AA/3.0 - B)/3.0; R = (-2.0*A*AA/27.0 + A*B/3.0 - C)/2.0; QQQ = Q*Q*Q; D = R*R - QQQ; /* printf(" R = %15.30f\n Q = %15.30f\n QQQ = %15.30f\n D = %15.30f\n", R, Q, QQQ, D); */ if (D < -epsilon) { /* three distinct roots- this is the most common case, it has been tested the most, its code should go first */ theta = acos(R/sqrt(QQQ))/3.0; t = 2*sqrt(Q); /* yes, these are sorted, because the C definition of acos says that it returns values in in [0, pi] */ root[0] = t*cos(theta) - sub; root[1] = t*cos(theta - 2*AIR_PI/3.0) - sub; root[2] = t*cos(theta + 2*AIR_PI/3.0) - sub; /* if (!AIR_EXISTS(root[0])) { fprintf(stderr, "%s: %g %g %g --> nan!!!\n", me, A, B, C); } */ return ell_cubic_root_three; } else if (D > epsilon) { double nr, fnr; /* one real solution, except maybe also a "rescued" double root */ sqrt_D = sqrt(D); u = airCbrt(sqrt_D+R); v = -airCbrt(sqrt_D-R); x = u+v - sub; if (!newton) { root[0] = x; root[1] = root[2] = AIR_NAN; return ell_cubic_root_single; } /* else refine x, the known root, with newton-raphson, so as to get the most accurate possible calculation for nr, the possible new root */ x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); nr = -(A + x)/2.0; fnr = ((nr + A)*nr + B)*nr + C; /* the polynomial evaluated at nr */ /* if (ell_debug) { fprintf(stderr, "%s: root = %g -> %g, nr=% 20.15f\n" " fnr=% 20.15f\n", me, x, (((x + A)*x + B)*x + C), nr, fnr); } */ if (fnr < -epsilon || fnr > epsilon) { root[0] = x; root[1] = root[2] = AIR_NAN; return ell_cubic_root_single; } else { if (ell_debug) { fprintf(stderr, "%s: rescued double root:% 20.15f\n", me, nr); } if (x > nr) { root[0] = x; root[1] = nr; root[2] = nr; } else { root[0] = nr; root[1] = nr; root[2] = x; } return ell_cubic_root_single_double; } } else { /* else D is in the interval [-epsilon, +epsilon] */ if (R < -epsilon || epsilon < R) { /* one double root and one single root */ u = airCbrt(R); if (u > 0) { root[0] = 2*u - sub; root[1] = -u - sub; root[2] = -u - sub; } else { root[0] = -u - sub; root[1] = -u - sub; root[2] = 2*u - sub; } return ell_cubic_root_single_double; } else { /* one triple root */ root[0] = root[1] = root[2] = -sub; return ell_cubic_root_triple; } } /* shouldn't ever get here */ /* return ell_cubic_root_unknown; */ } teem-1.11.0~svn6057/src/ell/test/0000775000175000017500000000000012203513760016126 5ustar domibeldomibelteem-1.11.0~svn6057/src/ell/test/mmul.c0000664000175000017500000000545212042322635017252 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ell.h" char *mulInfo = ("Tests ell_Nm_mul"); int main(int argc, const char *argv[]) { const char *me; char *outS, *err; hestOpt *hopt; hestParm *hparm; airArray *mop; Nrrd *_ninA, *_ninB, *ninA, *ninB, *nmul; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, NULL, "matrix", airTypeOther, 1, 1, &_ninA, NULL, "first matrix", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, NULL, "matrix", airTypeOther, 1, 1, &_ninB, NULL, "first matrix", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "file to write output nrrd to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, mulInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); ninA = nrrdNew(); airMopAdd(mop, ninA, (airMopper)nrrdNuke, airMopAlways); ninB = nrrdNew(); airMopAdd(mop, ninB, (airMopper)nrrdNuke, airMopAlways); nmul = nrrdNew(); airMopAdd(mop, nmul, (airMopper)nrrdNuke, airMopAlways); nrrdConvert(ninA, _ninA, nrrdTypeDouble); nrrdConvert(ninB, _ninB, nrrdTypeDouble); if (ell_Nm_mul(nmul, ninA, ninB)) { airMopAdd(mop, err = biffGetDone(ELL), airFree, airMopAlways); fprintf(stderr, "%s: problem inverting:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, nmul, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem saving output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/ell/test/es6.c0000664000175000017500000000647412042322635017002 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ell.h" char *es6Info = ("Tests ell_6ms_eigensolve_d"); int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt; hestParm *hparm; airArray *mop; Nrrd *nin, *_nin; double *in, sym[21], eval[6], evec[36]; unsigned int rr, cc, ii, jj; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, NULL, "matrix", airTypeOther, 1, 1, &_nin, NULL, "6x6 matrix", NULL, NULL, nrrdHestNrrd); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, es6Info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); if (!(2 == _nin->dim && 6 == _nin->axis[0].size && 6 == _nin->axis[1].size)) { fprintf(stderr, "%s: didn't get 2-D 6x6 matrix (got %u-D %ux%u)\n", me, _nin->dim, AIR_CAST(unsigned int, _nin->axis[0].size), AIR_CAST(unsigned int, _nin->axis[1].size)); airMopError(mop); return 1; } nrrdConvert(nin, _nin, nrrdTypeDouble); in = AIR_CAST(double *, nin->data); sym[ 0] = in[ 0]; sym[ 1] = in[ 1]; sym[ 2] = in[ 2]; sym[ 3] = in[ 3]; sym[ 4] = in[ 4]; sym[ 5] = in[ 5]; sym[ 6] = in[ 7]; sym[ 7] = in[ 8]; sym[ 8] = in[ 9]; sym[ 9] = in[10]; sym[10] = in[11]; sym[11] = in[14]; sym[12] = in[15]; sym[13] = in[16]; sym[14] = in[17]; sym[15] = in[21]; sym[16] = in[22]; sym[17] = in[23]; sym[18] = in[28]; sym[19] = in[29]; sym[20] = in[35]; for (rr=1; rr<6; rr++) { for (cc=0; ccelideMultipleNonExistFloatDefault = AIR_TRUE; hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, NULL, "input matrices", airTypeOther, 1, 1, &_nmat, NULL, "list of rotation matrices, 2-D array with one matrix " "(in ROW-major order) per scanline", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, NULL, "output filename", airTypeString, 1, 1, &outS, "-", "file to write EPS output to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, rot2aaInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( 2 == _nmat->dim && 9 == _nmat->axis[0].size )) { fprintf(stderr, "%s: need a 2-D 9-by-N nrrd\n", me); airMopError(mop); return 1; } nmat = nrrdNew(); airMopAdd(mop, nmat, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nmat, _nmat, nrrdTypeDouble)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't convert to double:\n%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/ell/test/tq.c0000664000175000017500000001345112042322635016722 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "../ell.h" char *me; #define CA 1 int main(int argc, char *argv[]) { float angleA_f, axisA_f[3], angleB_f, axisB_f[3], qA_f[4], qB_f[4], qC_f[4], mat3A_f[9], mat4A_f[16], mat3B_f[9], mat4B_f[16], mat3C_f[9], mat4C_f[16], pntA_f[4], pntB_f[4], pntC_f[4]; double angleA_d, axisA_d[3], angleB_d, axisB_d[3], qA_d[4], qB_d[4], qC_d[4], mat3A_d[9], mat4A_d[16], mat3B_d[9], mat4B_d[16], mat3C_d[9], mat4C_d[16], pntA_d[4], pntB_d[4], pntC_d[4]; int I, N; double tmp, det, frob; me = argv[0]; N = 100000; AIR_UNUSED(pntA_d); AIR_UNUSED(pntB_d); AIR_UNUSED(pntC_d); AIR_UNUSED(mat4C_d); AIR_UNUSED(mat3C_d); AIR_UNUSED(mat4B_d); AIR_UNUSED(mat3B_d); AIR_UNUSED(mat4A_d); AIR_UNUSED(mat3A_d); AIR_UNUSED(qC_d); AIR_UNUSED(qB_d); AIR_UNUSED(qA_d); AIR_UNUSED(axisB_d); AIR_UNUSED(angleB_d); AIR_UNUSED(axisA_d); AIR_UNUSED(angleA_d); AIR_UNUSED(argc); for (I=0; I q -> aa error: %g, %g\n", CA + AIR_ABS(angleA_f - angleB_f), CA + ELL_3V_LEN(axisA_f)); /* convert to 3m and back, and back */ ell_q_to_3m_f(mat3A_f, qA_f); ell_3m_to_q_f(qB_f, mat3A_f); if (ELL_4V_DOT(qA_f, qB_f) < 0) { ELL_4V_SCALE(qB_f, -1, qB_f); } ELL_4V_SUB(qC_f, qA_f, qB_f); ELL_Q_TO_3M(mat3B_f, qA_f); ELL_3M_SUB(mat3C_f, mat3B_f, mat3A_f); printf(" q -> 3m -> q error: %g, %g\n", CA + ELL_4V_LEN(qC_f), CA + ELL_3M_FROB(mat3C_f)); /* convert to 4m and back, and back */ ell_q_to_4m_f(mat4A_f, qA_f); ell_4m_to_q_f(qB_f, mat4A_f); if (ELL_4V_DOT(qA_f, qB_f) < 0) { ELL_4V_SCALE(qB_f, -1, qB_f); } ELL_4V_SUB(qC_f, qA_f, qB_f); ELL_Q_TO_4M(mat4B_f, qA_f); ELL_4M_SUB(mat4C_f, mat4B_f, mat4A_f); printf(" q -> 4m -> q error: %g, %g\n", CA + ELL_4V_LEN(qC_f), CA + ELL_4M_FROB(mat4C_f)); /* make a point that we'll rotate */ ELL_3V_SET(pntA_f, 2*airDrandMT()-1, 2*airDrandMT()-1, 2*airDrandMT()-1); /* effect rotation in two different ways, and compare results */ ELL_3MV_MUL(pntB_f, mat3A_f, pntA_f); ell_q_3v_rotate_f(pntC_f, qA_f, pntA_f); ELL_3V_SUB(pntA_f, pntB_f, pntC_f); printf(" rotation error = %g\n", CA + ELL_3V_LEN(pntA_f)); /* mix up inversion with conversion */ ell_3m_inv_f(mat3C_f, mat3A_f); ell_3m_to_q_f(qB_f, mat3C_f); ell_q_mul_f(qC_f, qA_f, qB_f); if (ELL_4V_DOT(qA_f, qC_f) < 0) { ELL_4V_SCALE(qC_f, -1, qC_f); } printf(" inv mul = %g %g %g %g\n", qC_f[0], CA + qC_f[1], CA + qC_f[2], CA + qC_f[3]); ell_q_inv_f(qC_f, qB_f); ELL_4V_SUB(qC_f, qB_f, qB_f); printf(" inv diff = %g %g %g %g\n", CA + qC_f[0], CA + qC_f[1], CA + qC_f[2], CA + qC_f[3]); /* exp and log */ ell_q_log_f(qC_f, qA_f); ell_q_log_f(qB_f, qC_f); ell_q_exp_f(qC_f, qB_f); ell_q_exp_f(qB_f, qC_f); ELL_4V_SUB(qC_f, qB_f, qA_f); printf(" exp/log diff = %g %g %g %g\n", CA + qC_f[0], CA + qC_f[1], CA + qC_f[2], CA + qC_f[3]); /* pow, not very exhaustive */ ell_q_to_3m_f(mat3A_f, qA_f); ell_3m_post_mul_f(mat3A_f, mat3A_f); ell_3m_post_mul_f(mat3A_f, mat3A_f); ell_q_pow_f(qB_f, qA_f, 4); ell_q_to_3m_f(mat3B_f, qB_f); ELL_3M_SUB(mat3B_f, mat3B_f, mat3A_f); printf(" pow diff = %g\n", CA + ELL_3M_FROB(mat3B_f)); if (ELL_3M_FROB(mat3B_f) > 2) { printf(" start q = %g %g %g %g\n", qA_f[0], qA_f[1], qA_f[2], qA_f[3]); angleA_f = ell_q_to_aa_f(axisA_f, qA_f); printf(" --> aa = %g (%g %g %g)\n", angleA_f, axisA_f[0], axisA_f[1], axisA_f[2]); printf(" q^3 = %g %g %g %g\n", qB_f[0], qB_f[1], qB_f[2], qB_f[3]); angleA_f = ell_q_to_aa_f(axisA_f, qB_f); printf(" --> aa = %g (%g %g %g)\n", angleA_f, axisA_f[0], axisA_f[1], axisA_f[2]); exit(1); } /* make sure it looks like a rotation matrix */ ell_q_to_3m_f(mat3A_f, qA_f); det = ELL_3M_DET(mat3A_f); frob = ELL_3M_FROB(mat3A_f); ELL_3M_TRANSPOSE(mat3B_f, mat3A_f); ell_3m_inv_f(mat3C_f, mat3A_f); ELL_3M_SUB(mat3C_f, mat3B_f, mat3C_f); printf(" det = %g; size = %g; err = %g\n", det, frob*frob/3, CA + ELL_3M_FROB(mat3C_f)); } exit(0); } teem-1.11.0~svn6057/src/ell/test/invert.c0000664000175000017500000000657612042322635017617 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ell.h" char *invInfo = ("Tests ell_Nm_inv and ell_Nm_pseudo_inv, " "and ell_{3,4}inv_d where possible "); int main(int argc, const char *argv[]) { const char *me; char *outS, *err; hestOpt *hopt; hestParm *hparm; airArray *mop; Nrrd *nin, *nmat, *ninv, *nidn; int (*func)(Nrrd *, Nrrd *); double m3[9], m4[16]; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, NULL, "matrix", airTypeOther, 1, 1, &nin, NULL, "transform(s) to apply to image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "file to write output nrrd to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, invInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); ninv = nrrdNew(); airMopAdd(mop, ninv, (airMopper)nrrdNuke, airMopAlways); nidn = nrrdNew(); airMopAdd(mop, nidn, (airMopper)nrrdNuke, airMopAlways); nmat = nrrdNew(); airMopAdd(mop, nmat, (airMopper)nrrdNuke, airMopAlways); nrrdConvert(nmat, nin, nrrdTypeDouble); if (3 == nmat->axis[0].size && 3 == nmat->axis[1].size) { ell_3m_inv_d(m3, (double *)nmat->data); fprintf(stderr, "%s: input:\n", me); ell_3m_print_d(stderr, (double *)nmat->data); fprintf(stderr, "%s: inverse:\n", me); ell_3m_print_d(stderr, m3); } if (4 == nmat->axis[0].size && 4 == nmat->axis[1].size) { ell_4m_inv_d(m4, (double *)nmat->data); fprintf(stderr, "%s: input:\n", me); ell_4m_print_d(stderr, (double *)nmat->data); fprintf(stderr, "%s: inverse:\n", me); ell_4m_print_d(stderr, m4); } func = (nmat->axis[0].size == nmat->axis[1].size ? ell_Nm_inv : ell_Nm_pseudo_inv); if (func(ninv, nmat)) { airMopAdd(mop, err = biffGetDone(ELL), airFree, airMopAlways); fprintf(stderr, "%s: problem inverting:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, ninv, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem saving output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/ell/test/wheel.c0000664000175000017500000003335612042322635017410 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ell.h" char *wheelInfo = ("Makes pictures of the eigenvalue wheel"); void wheelTenToGeom(double geom[3], double a, double b, double c, double d, double e, double f) { double A, B, C, Q, QQQ, R, Th; A = -a - d - f; B = a*d + a*f + d*f - b*b - c*c - e*e; C = c*c*d + a*e*e + b*b*f - 2*b*c*e - a*d*f; Q = (A*A - 3*B)/9; R = (9*A*B - 27*C - 2*A*A*A)/54; QQQ = Q*Q*Q; Th = QQQ ? acos(R/sqrt(QQQ))/3 : 0; geom[0] = -A/3; geom[1] = 2*sqrt(Q); geom[2] = 180.0*Th/AIR_PI; return; } void wheelABCToGeom(double *geom, double A, double B, double C) { double Q, R, QQQ, Th; Q = (A*A - 3*B)/9; R = (9*A*B - 27*C - 2*A*A*A)/54; QQQ = Q*Q*Q; Th = QQQ ? acos(R/sqrt(QQQ))/3 : 0; geom[0] = -A/3; geom[1] = 2*sqrt(Q); geom[2] = 180.0*Th/AIR_PI; return; } void wheelGeomToRoot(double xroot[3], double yroot[3], double geom[3]) { double Th; Th = AIR_PI*geom[2]/180.0; xroot[0] = geom[0] + geom[1]*cos(Th); xroot[1] = geom[0] + geom[1]*cos(Th - 2*AIR_PI/3); xroot[2] = geom[0] + geom[1]*cos(Th + 2*AIR_PI/3); yroot[0] = geom[1]*sin(Th); yroot[1] = geom[1]*sin(Th - 2*AIR_PI/3); yroot[2] = geom[1]*sin(Th + 2*AIR_PI/3); return; } void wheelGeomToABC(double ABC[3], double center, double radius, double angle) { double geom[3], x[3], yroot[3]; ELL_3V_SET(geom, center, radius, angle); wheelGeomToRoot(x, yroot, geom); ELL_3V_SET(ABC, -x[0] - x[1] - x[2], x[0]*x[1] + x[1]*x[2] + x[0]*x[2], -x[0]*x[1]*x[2]); return; } void wheelGeomToRNTH(double rnth[3], double center, double radius, double angle) { double mu1, mu2; mu1 = center; mu2 = radius*radius/2; rnth[0] = sqrt(mu2)/(sqrt(2)*mu1); rnth[1] = sqrt(3*(mu1*mu1 + mu2)); rnth[2] = angle; } typedef struct { FILE *file; double psc; double bbox[4]; double maxX, maxY, yscale; } wheelPS; #define WPS_X(x) AIR_AFFINE(wps->bbox[0], (x), wps->bbox[2], 0, wps->maxX) #define WPS_Y(y) AIR_AFFINE(wps->bbox[1], (y), wps->bbox[3], 0, wps->maxY) #define WPS_S(s) AIR_DELTA(wps->bbox[1], (s), wps->bbox[3], 0, wps->maxY) void wheelPreamble(wheelPS *wps) { wps->maxX = wps->psc*(wps->bbox[2] - wps->bbox[0]); wps->maxY = wps->psc*(wps->bbox[3] - wps->bbox[1]); fprintf(wps->file, "%%!PS-Adobe-2.0 EPSF-2.0\n"); fprintf(wps->file, "%%%%Creator: limn\n"); fprintf(wps->file, "%%%%Pages: 1\n"); fprintf(wps->file, "%%%%BoundingBox: 0 0 %d %d\n", (int)(wps->maxX), (int)(wps->maxY)); fprintf(wps->file, "%%%%EndComments\n"); fprintf(wps->file, "%%%%EndProlog\n"); fprintf(wps->file, "%%%%Page: 1 1\n"); fprintf(wps->file, "gsave\n"); fprintf(wps->file, "0 0 moveto\n"); fprintf(wps->file, "%g 0 lineto\n", wps->maxX); fprintf(wps->file, "%g %g lineto\n", wps->maxX, wps->maxY); fprintf(wps->file, "0 %g lineto\n", wps->maxY); fprintf(wps->file, "closepath\n"); fprintf(wps->file, "clip\n"); fprintf(wps->file, "gsave newpath\n"); fprintf(wps->file, "1 setlinejoin\n"); fprintf(wps->file, "1 setlinecap\n"); fprintf(wps->file, "/M {moveto} bind def\n"); fprintf(wps->file, "/L {lineto} bind def\n"); fprintf(wps->file, "/W {setlinewidth} bind def\n"); fprintf(wps->file, "/F {fill} bind def\n"); fprintf(wps->file, "/S {stroke} bind def\n"); fprintf(wps->file, "/CP {closepath} bind def\n"); fprintf(wps->file, "/RGB {setrgbcolor} bind def\n"); fprintf(wps->file, "/Gr {setgray} bind def\n"); fprintf(wps->file, "\n"); return; } void wheelWidth(wheelPS *wps, double width) { fprintf(wps->file, "%g W\n", width); return; } void wheelGray(wheelPS *wps, double gray) { fprintf(wps->file, "%g Gr\n", gray); return; } void wheelLabel(wheelPS *wps, double x, double y, char *str) { fprintf(wps->file, "%g %g M (%s) show\n", WPS_X(x), WPS_Y(y), str); return; } void wheelGraph(wheelPS *wps, double a, double d, double f) { double A, B, C; int xi; double x, y; A = -a - d - f; B = a*d + a*f + d*f; C = -a*d*f; for (xi=0; xi<=99; xi++) { x = AIR_AFFINE(0, xi, 99, wps->bbox[0], wps->bbox[2]); y = (((x + A)*x + B)*x + C)/2; fprintf(wps->file, "%g %g %s\n", WPS_X(x), WPS_Y(wps->yscale*y), xi ? "L" : "M"); } fprintf(wps->file, "S\n"); return; } void wheelLine(wheelPS *wps, double x0, double y0, double x1, double y1) { fprintf(wps->file, "%g %g M\n", WPS_X(x0), WPS_Y(y0)); fprintf(wps->file, "%g %g L S\n", WPS_X(x1), WPS_Y(y1)); return; } void wheelCircle(wheelPS *wps, double xc, double yc, double rad) { fprintf(wps->file, "%g %g %g 0 360 arc closepath S\n", WPS_X(xc), WPS_Y(yc), WPS_S(rad)); return; } void wheelArc(wheelPS *wps, double xc, double yc, double rad, double angle1, double angle2) { fprintf(wps->file, "newpath %g %g %g %g %g arc S\n", WPS_X(xc), WPS_Y(yc), WPS_S(rad), angle1, angle2); } void wheelDot(wheelPS *wps, double x, double y, double rad) { fprintf(wps->file, "%g %g %g 0 360 arc closepath F S\n", WPS_X(x), WPS_Y(y), WPS_S(rad)); return; } void wheelEpilog(wheelPS *wps) { fprintf(wps->file, "grestore\n"); fprintf(wps->file, "grestore\n"); fprintf(wps->file, "%%%%Trailer\n"); return; } int main(int argc, const char *argv[]) { const char *me; char *outS; hestOpt *hopt; hestParm *hparm; airArray *mop; double tval[6], ABC[3], geom[3], rnth[3], RA, norm, mu2, tmpr=0, tmpa=0, xroot[3], yroot[3], bbox[4], htick, htth, psc; wheelPS wps; int correct, labels, drawRA; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "t", "a b c d e f", airTypeDouble, 6, 6, tval, "nan nan nan nan nan nan nan", "six values of symmetric tensors"); hestOptAdd(&hopt, "ABC", "A B C", airTypeDouble, 3, 3, ABC, "nan nan nan", "directly give coefficients of cubic polynomial " "(and override info from \"-t\")"); hestOptAdd(&hopt, "g", "c rad th", airTypeDouble, 3, 3, geom, "nan nan nan", "directly give center, radius, and angle (in degrees) of wheel " "(and override info from \"-t\" and \"-ABC\""); hestOptAdd(&hopt, "p", "RA norm th", airTypeDouble, 3, 3, rnth, "nan nan nan", "directly give RA, norm, and angle (in degrees) of tensor " "(and override info from \"-t\", \"-ABC\", and \"-geom\""); hestOptAdd(&hopt, "correct", NULL, airTypeInt, 0, 0, &correct, NULL, "when using \"-g\", be honest about what the estimated " "acos(sqrt(2)*skew)/3 is going to be"); hestOptAdd(&hopt, "labels", NULL, airTypeInt, 0, 0, &labels, NULL, "put little labels on things; fix with psfrag in LaTeX"); hestOptAdd(&hopt, "RA", NULL, airTypeInt, 0, 0, &drawRA, NULL, "draw extra geometry associated with RA"); hestOptAdd(&hopt, "htick", "pos", airTypeDouble, 1, 1, &htick, "nan", "location of single tick mark on horizontal axis"); hestOptAdd(&hopt, "htth", "thick", airTypeDouble, 1, 1, &htth, "3", "thickness of horizontal tick"); hestOptAdd(&hopt, "bb", "bbox", airTypeDouble, 4, 4, bbox, "nan nan nan nan", "bounding box, in world space around the " "region of the graph that should be drawn to EPS"); hestOptAdd(&hopt, "ysc", "scale", airTypeDouble, 1, 1, &(wps.yscale), "0.5", "scaling on Y axis for drawing graph of characteristic " "polynomial, or \"0\" to turn this off."); hestOptAdd(&hopt, "psc", "scale", airTypeDouble, 1, 1, &psc, "100", "scaling from world space to PostScript points"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "file to write EPS output to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, wheelInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!(wps.file = airFopen(outS, stdout, "wb"))) { fprintf(stderr, "%s: couldn't open output file\n", me); airMopError(mop); return 1; } airMopAdd(mop, wps.file, (airMopper)airFclose, airMopAlways); if (AIR_EXISTS(rnth[0])) { RA = rnth[0]; norm = rnth[1]; mu2 = (norm*norm/3)*(2*RA*RA/(1 + 2*RA*RA)); geom[0] = sqrt(mu2)/(sqrt(2)*RA); geom[1] = sqrt(2*mu2); geom[2] = rnth[2]; wheelGeomToRoot(xroot, yroot, geom); wheelGeomToABC(ABC, geom[0], geom[1], geom[2]); } else if (AIR_EXISTS(geom[0])) { wheelGeomToRoot(xroot, yroot, geom); if (correct) { tval[0] = xroot[0]; tval[1] = tval[2] = 0; tval[3] = xroot[1]; tval[4] = 0; tval[5] = xroot[2]; wheelTenToGeom(geom, tval[0], tval[1], tval[2], tval[3], tval[4], tval[5]); wheelGeomToRoot(xroot, yroot, geom); } wheelGeomToABC(ABC, geom[0], geom[1], geom[2]); wheelGeomToRNTH(rnth, geom[0], geom[1], geom[2]); } else if (AIR_EXISTS(ABC[0])) { wheelABCToGeom(geom, ABC[0], ABC[1], ABC[2]); wheelGeomToRNTH(rnth, geom[0], geom[1], geom[2]); wheelGeomToRoot(xroot, yroot, geom); } else { wheelTenToGeom(geom, tval[0], tval[1], tval[2], tval[3], tval[4], tval[5]); wheelGeomToRoot(xroot, yroot, geom); wheelGeomToRNTH(rnth, geom[0], geom[1], geom[2]); wheelGeomToABC(ABC, geom[0], geom[1], geom[2]); } fprintf(stderr, "%s: RNTH: %g %g %g\n", me, rnth[0], rnth[1], rnth[2]); fprintf(stderr, "%s: ABC: %g %g %g\n", me, ABC[0], ABC[1], ABC[2]); fprintf(stderr, "%s: xroot: %g %g %g\n", me, xroot[0], xroot[1], xroot[2]); fprintf(stderr, "%s: geom: %g %g %g\n", me, geom[0], geom[1], geom[2]); if (!AIR_EXISTS(bbox[0])) { bbox[0] = geom[0] - 1.2*geom[1]; bbox[1] = - 1.2*geom[1]; bbox[2] = geom[0] + 1.2*geom[1]; bbox[3] = + 1.2*geom[1]; fprintf(stderr, "%s: bbox %g %g %g %g\n", me, bbox[0], bbox[1], bbox[2], bbox[3]); } wps.psc = psc; ELL_4V_COPY(wps.bbox, bbox); wheelPreamble(&wps); /* graph */ if (wps.yscale) { wheelWidth(&wps, 4); wheelGray(&wps, 0.5); wheelGraph(&wps, xroot[0], xroot[1], xroot[2]); } /* axis */ wheelWidth(&wps, 2); wheelGray(&wps, 0.0); wheelLine(&wps, bbox[0], 0, bbox[2], 0); /* circle */ wheelWidth(&wps, 3); wheelCircle(&wps, geom[0], 0, geom[1]); /* spokes */ wheelWidth(&wps, 4); wheelLine(&wps, geom[0], 0, xroot[0], yroot[0]); wheelLine(&wps, geom[0], 0, xroot[1], yroot[1]); wheelLine(&wps, geom[0], 0, xroot[2], yroot[2]); /* dots at spoke ends */ wheelDot(&wps, xroot[0], yroot[0], 0.025*geom[1]); wheelDot(&wps, xroot[1], yroot[1], 0.025*geom[1]); wheelDot(&wps, xroot[2], yroot[2], 0.025*geom[1]); /* lines from dots to roots */ wheelWidth(&wps, 2); fprintf(wps.file, "gsave\n"); fprintf(wps.file, "[2 4] 0 setdash\n"); wheelLine(&wps, xroot[0], 0, xroot[0], yroot[0]); wheelLine(&wps, xroot[1], 0, xroot[1], yroot[1]); wheelLine(&wps, xroot[2], 0, xroot[2], yroot[2]); fprintf(wps.file, "grestore\n"); /* tickmarks */ wheelWidth(&wps, 6); wheelLine(&wps, xroot[0], -0.02*geom[1], xroot[0], 0.02*geom[1]); wheelLine(&wps, xroot[1], -0.02*geom[1], xroot[1], 0.02*geom[1]); wheelLine(&wps, xroot[2], -0.02*geom[1], xroot[2], 0.02*geom[1]); if (AIR_EXISTS(htick)) { wheelWidth(&wps, htth); wheelLine(&wps, htick, -0.04, htick, 0.04); } /* RA angle */ if (drawRA) { wheelWidth(&wps, 3); wheelLine(&wps, 0.0, 0.0, geom[0], geom[1]); wheelWidth(&wps, 2); fprintf(wps.file, "gsave\n"); fprintf(wps.file, "[2 4] 0 setdash\n"); wheelLine(&wps, geom[0], geom[1], geom[0], 0); fprintf(wps.file, "grestore\n"); } /* labels, if wanted */ if (labels) { fprintf(wps.file, "/Helvetica findfont 20 scalefont setfont\n"); wheelLabel(&wps, geom[0], 0, "center"); wheelLine(&wps, geom[0], -0.02*geom[1], geom[0], 0.02*geom[1]); wheelLabel(&wps, (geom[0] + xroot[0])/1.8, yroot[0]/1.8, "radius"); wheelWidth(&wps, 2); wheelArc(&wps, geom[0], 0, geom[1]/2, 0, geom[2]); wheelLabel(&wps, geom[0] + geom[1]*cos(AIR_PI*geom[2]/180/2)/2.5, geom[1]*sin(AIR_PI*geom[2]/180/2)/2.5, "theta"); if (drawRA) { tmpr = sqrt(geom[0]*geom[0] + geom[1]*geom[1]); tmpa = atan(2.0*rnth[0]); wheelWidth(&wps, 2); wheelArc(&wps, 0, 0, 0.2*tmpr, 0, 180*tmpa/AIR_PI); wheelLabel(&wps, 0.2*tmpr*cos(tmpa/2), 0.2*tmpr*sin(tmpa/2), "phi"); } wheelLabel(&wps, xroot[0], yroot[0], "spoke0"); wheelLabel(&wps, xroot[1], yroot[1], "spoke-"); wheelLabel(&wps, xroot[2], yroot[2], "spoke+"); wheelLabel(&wps, xroot[0], 0, "root0"); wheelLabel(&wps, xroot[1], 0, "root-"); wheelLabel(&wps, xroot[2], 0, "root+"); } wheelEpilog(&wps); airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/src/ell/test/inter.c0000664000175000017500000002125112042322635017414 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ell.h" char *interInfo = ("Recreates memories of college physics"); typedef struct { FILE *file; double psc; double bbox[4]; double maxX, maxY, yscale; } wheelPS; #define WPS_X(x) AIR_AFFINE(wps->bbox[0], (x), wps->bbox[2], 0, wps->maxX) #define WPS_Y(y) AIR_AFFINE(wps->bbox[1], (y), wps->bbox[3], 0, wps->maxY) #define WPS_S(s) AIR_DELTA(wps->bbox[1], (s), wps->bbox[3], 0, wps->maxY) void wheelPreamble(wheelPS *wps) { wps->maxX = wps->psc*(wps->bbox[2] - wps->bbox[0]); wps->maxY = wps->psc*(wps->bbox[3] - wps->bbox[1]); fprintf(wps->file, "%%!PS-Adobe-2.0 EPSF-2.0\n"); fprintf(wps->file, "%%%%Creator: limn\n"); fprintf(wps->file, "%%%%Pages: 1\n"); fprintf(wps->file, "%%%%BoundingBox: 0 0 %d %d\n", (int)(wps->maxX), (int)(wps->maxY)); fprintf(wps->file, "%%%%EndComments\n"); fprintf(wps->file, "%%%%EndProlog\n"); fprintf(wps->file, "%%%%Page: 1 1\n"); fprintf(wps->file, "gsave\n"); fprintf(wps->file, "0 0 moveto\n"); fprintf(wps->file, "%g 0 lineto\n", wps->maxX); fprintf(wps->file, "%g %g lineto\n", wps->maxX, wps->maxY); fprintf(wps->file, "0 %g lineto\n", wps->maxY); fprintf(wps->file, "closepath\n"); fprintf(wps->file, "clip\n"); fprintf(wps->file, "gsave newpath\n"); fprintf(wps->file, "1 setlinejoin\n"); fprintf(wps->file, "1 setlinecap\n"); fprintf(wps->file, "/M {moveto} bind def\n"); fprintf(wps->file, "/L {lineto} bind def\n"); fprintf(wps->file, "/W {setlinewidth} bind def\n"); fprintf(wps->file, "/F {fill} bind def\n"); fprintf(wps->file, "/S {stroke} bind def\n"); fprintf(wps->file, "/CP {closepath} bind def\n"); fprintf(wps->file, "/RGB {setrgbcolor} bind def\n"); fprintf(wps->file, "/Gr {setgray} bind def\n"); fprintf(wps->file, "\n"); return; } void wheelWidth(wheelPS *wps, double width) { fprintf(wps->file, "%g W\n", width); return; } void wheelGray(wheelPS *wps, double gray) { fprintf(wps->file, "%g Gr\n", gray); return; } void wheelArrow(wheelPS *wps, double x0, double y0, double x1, double y1, double alen, double awidth) { double len, dir[2], perp[2]; dir[0] = x0 - x1; dir[1] = y0 - y1; ELL_2V_NORM(dir, dir, len); ELL_2V_SET(perp, -dir[1], dir[0]); fprintf(wps->file, "%g %g M\n", WPS_X(x0), WPS_Y(y0)); fprintf(wps->file, "%g %g L S\n", WPS_X(x1 + alen*dir[0]/2), WPS_Y(y1 + alen*dir[1]/2)); if (alen && awidth) { if (len < alen) { awidth *= len/alen; alen = len; } fprintf(wps->file, "%g %g M\n", WPS_X(x1 + alen*dir[0] + awidth*perp[0]), WPS_Y(y1 + alen*dir[1] + awidth*perp[1])); fprintf(wps->file, "%g %g L\n", WPS_X(x1), WPS_Y(y1)); fprintf(wps->file, "%g %g L CP F\n", WPS_X(x1 + alen*dir[0] - awidth*perp[0]), WPS_Y(y1 + alen*dir[1] - awidth*perp[1])); } return; } void wheelEpilog(wheelPS *wps) { fprintf(wps->file, "grestore\n"); fprintf(wps->file, "grestore\n"); fprintf(wps->file, "%%%%Trailer\n"); return; } int main(int argc, const char *argv[]) { const char *me; char *outS; hestOpt *hopt; hestParm *hparm; airArray *mop; int fidx, aidx, num, frames; double psc, width[2], arrowWidth, lineWidth, angle, seglen, x0, y0, x1, y1, cc, ss; wheelPS wps; char filename[AIR_STRLEN_MED]; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "w", "arrowWidth lineWidth", airTypeDouble, 2, 2, width, "1.0 0.2", "widths"); hestOptAdd(&hopt, "n", "number", airTypeInt, 1, 1, &num, "10", "number of arrows"); hestOptAdd(&hopt, "f", "frames", airTypeInt, 1, 1, &frames, "10", "number of frames"); hestOptAdd(&hopt, "psc", "scale", airTypeDouble, 1, 1, &psc, "200", "scaling from world space to PostScript points"); hestOptAdd(&hopt, "o", "prefix", airTypeString, 1, 1, &outS, NULL, "prefix of file names"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, interInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); for (fidx=0; fidx #include "../ell.h" char *me; void usage() { /* 0 1 2 3 (4) */ fprintf(stderr, "usage: %s \n", me); exit(1); } int main(int argc, char *argv[]) { char *aS, *bS, *cS; float a, b, c, t; me = argv[0]; if (4 != argc) { usage(); } aS = argv[1]; bS = argv[2]; cS = argv[3]; if (3 != (sscanf(aS, "%g", &a) + sscanf(bS, "%g", &b) + sscanf(cS, "%g", &c))) { fprintf(stderr, "%s: couldn't parse \"%s\", \"%s\", \"%s\" as floats\n", me, aS, bS, cS); usage(); } printf("%g %g %g (max idx %d; min idx %d) --> ", a, b, c, ELL_MAX3_IDX(a, b, c), ELL_MIN3_IDX(a, b, c)); ELL_SORT3(a, b, c, t); printf("%g %g %g\n", a, b, c); exit(0); } teem-1.11.0~svn6057/src/ell/eigen.c0000664000175000017500000005771512174426262016430 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ell.h" /* lop A fprintf(stderr, "_ellAlign3: ----------\n"); fprintf(stderr, "_ellAlign3: v0 = %g %g %g\n", (v+0)[0], (v+0)[1], (v+0)[2]); fprintf(stderr, "_ellAlign3: v3 = %g %g %g\n", (v+3)[0], (v+3)[1], (v+3)[2]); fprintf(stderr, "_ellAlign3: v6 = %g %g %g\n", (v+6)[0], (v+6)[1], (v+6)[2]); fprintf(stderr, "_ellAlign3: d = %g %g %g -> %d %d %d\n", d0, d1, d2, Mi, ai, bi); fprintf(stderr, "_ellAlign3: pre dot signs (03, 06, 36): %d %d %d\n", airSgn(ELL_3V_DOT(v+0, v+3)), airSgn(ELL_3V_DOT(v+0, v+6)), airSgn(ELL_3V_DOT(v+3, v+6))); */ /* lop B fprintf(stderr, "_ellAlign3: v0 = %g %g %g\n", (v+0)[0], (v+0)[1], (v+0)[2]); fprintf(stderr, "_ellAlign3: v3 = %g %g %g\n", (v+3)[0], (v+3)[1], (v+3)[2]); fprintf(stderr, "_ellAlign3: v6 = %g %g %g\n", (v+6)[0], (v+6)[1], (v+6)[2]); fprintf(stderr, "_ellAlign3: post dot signs %d %d %d\n", airSgn(ELL_3V_DOT(v+0, v+3)), airSgn(ELL_3V_DOT(v+0, v+6)), airSgn(ELL_3V_DOT(v+3, v+6))); if (airSgn(ELL_3V_DOT(v+0, v+3)) < 0 || airSgn(ELL_3V_DOT(v+0, v+6)) < 0 || airSgn(ELL_3V_DOT(v+3, v+6)) < 0) { exit(1); } */ void _ell_align3_d(double v[9]) { double d0, d1, d2; int Mi, ai, bi; d0 = ELL_3V_DOT(v+0, v+0); d1 = ELL_3V_DOT(v+3, v+3); d2 = ELL_3V_DOT(v+6, v+6); Mi = ELL_MAX3_IDX(d0, d1, d2); ai = (Mi + 1) % 3; bi = (Mi + 2) % 3; /* lop A */ if (ELL_3V_DOT(v+3*Mi, v+3*ai) < 0) { ELL_3V_SCALE(v+3*ai, -1, v+3*ai); } if (ELL_3V_DOT(v+3*Mi, v+3*bi) < 0) { ELL_3V_SCALE(v+3*bi, -1, v+3*bi); } /* lob B */ /* we can't guarantee that dot(v+3*ai,v+3*bi) > 0 . . . */ } /* ** leaves v+3*0 untouched, but makes sure that v+3*0, v+3*1, and v+3*2 ** are mutually orthogonal. Also leaves the magnitudes of all ** vectors unchanged. */ void _ell_3m_enforce_orthogonality(double v[9]) { double d00, d10, d11, d20, d21, d22, scl, tv[3]; d00 = ELL_3V_DOT(v+3*0, v+3*0); d10 = ELL_3V_DOT(v+3*1, v+3*0); d11 = ELL_3V_DOT(v+3*1, v+3*1); ELL_3V_SCALE_ADD2(tv, 1, v+3*1, -d10/d00, v+3*0); scl = sqrt(d11/ELL_3V_DOT(tv, tv)); ELL_3V_SCALE(v+3*1, scl, tv); d20 = ELL_3V_DOT(v+3*2, v+3*0); d21 = ELL_3V_DOT(v+3*2, v+3*1); d22 = ELL_3V_DOT(v+3*2, v+3*2); ELL_3V_SCALE_ADD3(tv, 1, v+3*2, -d20/d00, v+3*0, -d21/d00, v+3*1); scl = sqrt(d22/ELL_3V_DOT(tv, tv)); ELL_3V_SCALE(v+3*2, scl, tv); return; } /* ** makes sure that v+3*2 has a positive dot product with ** cross product of v+3*0 and v+3*1 */ void _ell_3m_make_right_handed_d(double v[9]) { double x[3]; ELL_3V_CROSS(x, v+3*0, v+3*1); if (0 > ELL_3V_DOT(x, v+3*2)) { ELL_3V_SCALE(v+3*2, -1, v+3*2); } } /* lop A fprintf(stderr, "=== pre ===\n"); fprintf(stderr, "crosses: %g %g %g\n", (t+0)[0], (t+0)[1], (t+0)[2]); fprintf(stderr, " %g %g %g\n", (t+3)[0], (t+3)[1], (t+3)[2]); fprintf(stderr, " %g %g %g\n", (t+6)[0], (t+6)[1], (t+6)[2]); fprintf(stderr, "cross dots: %g %g %g\n", ELL_3V_DOT(t+0, t+3), ELL_3V_DOT(t+0, t+6), ELL_3V_DOT(t+3, t+6)); */ /* lop B fprintf(stderr, "=== post ===\n"); fprintf(stderr, "crosses: %g %g %g\n", (t+0)[0], (t+0)[1], (t+0)[2]); fprintf(stderr, " %g %g %g\n", (t+3)[0], (t+3)[1], (t+3)[2]); fprintf(stderr, " %g %g %g\n", (t+6)[0], (t+6)[1], (t+6)[2]); fprintf(stderr, "cross dots: %g %g %g\n", ELL_3V_DOT(t+0, t+3), ELL_3V_DOT(t+0, t+6), ELL_3V_DOT(t+3, t+6)); */ /* ******** ell_3m_1d_nullspace_d() ** ** the given matrix is assumed to have a nullspace of dimension one. ** A normalized vector which spans the nullspace is put into ans. ** ** The given nullspace matrix is NOT modified. ** ** This does NOT use biff */ void ell_3m_1d_nullspace_d(double ans[3], const double _n[9]) { double t[9], n[9], norm; ELL_3M_TRANSPOSE(n, _n); /* find the three cross-products of pairs of column vectors of n */ ELL_3V_CROSS(t+0, n+0, n+3); ELL_3V_CROSS(t+3, n+0, n+6); ELL_3V_CROSS(t+6, n+3, n+6); /* lop A */ _ell_align3_d(t); /* lop B */ /* add them up (longer, hence more accurate, should dominate) */ ELL_3V_ADD3(ans, t+0, t+3, t+6); /* normalize */ ELL_3V_NORM(ans, ans, norm); return; } /* ******** ell_3m_2d_nullspace_d() ** ** the given matrix is assumed to have a nullspace of dimension two. ** ** The given nullspace matrix is NOT modified ** ** This does NOT use biff */ void ell_3m_2d_nullspace_d(double ans0[3], double ans1[3], const double _n[9]) { double n[9], tmp[3], norm; ELL_3M_TRANSPOSE(n, _n); _ell_align3_d(n); ELL_3V_ADD3(tmp, n+0, n+3, n+6); ELL_3V_NORM(tmp, tmp, norm); /* any two vectors which are perpendicular to the (supposedly 1D) span of the column vectors span the nullspace */ ell_3v_perp_d(ans0, tmp); ELL_3V_NORM(ans0, ans0, norm); ELL_3V_CROSS(ans1, tmp, ans0); return; } /* ******** ell_3m_eigenvalues_d() ** ** finds eigenvalues of given matrix. ** ** returns information about the roots according to ellCubeRoot enum, ** see header for ellCubic for details. ** ** given matrix is NOT modified ** ** This does NOT use biff ** ** Doing the frobenius normalization proved successfull in avoiding the ** the creating of NaN eigenvalues when the coefficients of the matrix ** were really large (> 50000). Also, when the matrix norm was really ** small, the comparison to "epsilon" in ell_cubic mistook three separate ** roots for a single and a double, with this matrix in particular: ** 1.7421892 0.0137642 0.0152975 ** 0.0137642 1.7565432 -0.0062296 ** 0.0152975 -0.0062296 1.7700019 ** (actually, this is prior to tenEigensolve's isotropic removal) ** ** HEY: tenEigensolve_d and tenEigensolve_f start by removing the ** isotropic part of the tensor. It may be that that smarts should ** be migrated here, but GLK is uncertain how it would change the ** handling of non-symmetric matrices. */ int ell_3m_eigenvalues_d(double _eval[3], const double _m[9], const int newton) { double A, B, C, scale, frob, m[9], eval[3]; int roots; frob = ELL_3M_FROB(_m); scale = frob ? 1.0/frob : 1.0; ELL_3M_SCALE(m, scale, _m); /* printf("!%s: m = %g %g %g; %g %g %g; %g %g %g\n", "ell_3m_eigenvalues_d", m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); */ /* ** from gordon with mathematica; these are the coefficients of the ** cubic polynomial in x: det(x*I - M). The full cubic is ** x^3 + A*x^2 + B*x + C. */ A = -m[0] - m[4] - m[8]; B = m[0]*m[4] - m[3]*m[1] + m[0]*m[8] - m[6]*m[2] + m[4]*m[8] - m[7]*m[5]; C = (m[6]*m[4] - m[3]*m[7])*m[2] + (m[0]*m[7] - m[6]*m[1])*m[5] + (m[3]*m[1] - m[0]*m[4])*m[8]; /* printf("!%s: A B C = %g %g %g\n", "ell_3m_eigenvalues_d", A, B, C); */ roots = ell_cubic(eval, A, B, C, newton); /* no longer need to sort here */ ELL_3V_SCALE(_eval, 1.0/scale, eval); return roots; } void _ell_3m_evecs_d(double evec[9], double eval[3], int roots, const double m[9]) { double n[9], e0=0, e1=0.0, e2=0.0, t /* , tmpv[3] */ ; ELL_3V_GET(e0, e1, e2, eval); /* if (ell_debug) { printf("ell_3m_evecs_d: numroots = %d\n", numroots); } */ /* we form m - lambda*I by doing a memcpy from m, and then (repeatedly) over-writing the diagonal elements */ ELL_3M_COPY(n, m); switch (roots) { case ell_cubic_root_three: /* if (ell_debug) { printf("ell_3m_evecs_d: evals: %20.15f %20.15f %20.15f\n", eval[0], eval[1], eval[2]); } */ ELL_3M_DIAG_SET(n, m[0]-e0, m[4]-e0, m[8]-e0); ell_3m_1d_nullspace_d(evec+0, n); ELL_3M_DIAG_SET(n, m[0]-e1, m[4]-e1, m[8]-e1); ell_3m_1d_nullspace_d(evec+3, n); ELL_3M_DIAG_SET(n, m[0]-e2, m[4]-e2, m[8]-e2); ell_3m_1d_nullspace_d(evec+6, n); _ell_3m_enforce_orthogonality(evec); _ell_3m_make_right_handed_d(evec); ELL_3V_SET(eval, e0, e1, e2); break; case ell_cubic_root_single_double: ELL_SORT3(e0, e1, e2, t); if (e0 > e1) { /* one big (e0) , two small (e1, e2) : more like a cigar */ ELL_3M_DIAG_SET(n, m[0]-e0, m[4]-e0, m[8]-e0); ell_3m_1d_nullspace_d(evec+0, n); ELL_3M_DIAG_SET(n, m[0]-e1, m[4]-e1, m[8]-e1); ell_3m_2d_nullspace_d(evec+3, evec+6, n); } else { /* two big (e0, e1), one small (e2): more like a pancake */ ELL_3M_DIAG_SET(n, m[0]-e0, m[4]-e0, m[8]-e0); ell_3m_2d_nullspace_d(evec+0, evec+3, n); ELL_3M_DIAG_SET(n, m[0]-e2, m[4]-e2, m[8]-e2); ell_3m_1d_nullspace_d(evec+6, n); } _ell_3m_enforce_orthogonality(evec); _ell_3m_make_right_handed_d(evec); ELL_3V_SET(eval, e0, e1, e2); break; case ell_cubic_root_triple: /* one triple root; use any basis as the eigenvectors */ ELL_3V_SET(evec+0, 1, 0, 0); ELL_3V_SET(evec+3, 0, 1, 0); ELL_3V_SET(evec+6, 0, 0, 1); ELL_3V_SET(eval, e0, e1, e2); break; case ell_cubic_root_single: /* only one real root */ ELL_3M_DIAG_SET(n, m[0]-e0, m[4]-e0, m[8]-e0); ell_3m_1d_nullspace_d(evec+0, n); ELL_3V_SET(evec+3, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(evec+6, AIR_NAN, AIR_NAN, AIR_NAN); ELL_3V_SET(eval, e0, AIR_NAN, AIR_NAN); break; } /* if (ell_debug) { printf("ell_3m_evecs_d (numroots = %d): evecs: \n", numroots); ELL_3MV_MUL(tmpv, m, evec[0]); printf(" (%g:%g): %20.15f %20.15f %20.15f\n", eval[0], ELL_3V_DOT(evec[0], tmpv), evec[0][0], evec[0][1], evec[0][2]); ELL_3MV_MUL(tmpv, m, evec[1]); printf(" (%g:%g): %20.15f %20.15f %20.15f\n", eval[1], ELL_3V_DOT(evec[1], tmpv), evec[1][0], evec[1][1], evec[1][2]); ELL_3MV_MUL(tmpv, m, evec[2]); printf(" (%g:%g): %20.15f %20.15f %20.15f\n", eval[2], ELL_3V_DOT(evec[2], tmpv), evec[2][0], evec[2][1], evec[2][2]); } */ return; } /* ******** ell_3m_eigensolve_d() ** ** finds eigenvalues and eigenvectors of given matrix m ** ** returns information about the roots according to ellCubeRoot enum, ** see header for ellCubic for details. When eval[i] is set, evec+3*i ** is set to a corresponding eigenvector. The eigenvectors are ** (evec+0)[], (evec+3)[], and (evec+6)[] ** ** NOTE: Even in the post-Teem-1.7 switch from column-major to ** row-major- its still the case that the eigenvectors are at ** evec+0, evec+3, evec+6: this means that they USED to be the ** "columns" of the matrix, and NOW they're the rows. ** ** The eigenvalues (and associated eigenvectors) are sorted in ** descending order. ** ** This does NOT use biff */ int ell_3m_eigensolve_d(double eval[3], double evec[9], const double m[9], const int newton) { int roots; /* if (ell_debug) { printf("ell_3m_eigensolve_d: input matrix:\n"); printf("{{%20.15f,\t%20.15f,\t%20.15f},\n", m[0], m[1], m[2]); printf(" {%20.15f,\t%20.15f,\t%20.15f},\n", m[3], m[4], m[5]); printf(" {%20.15f,\t%20.15f,\t%20.15f}};\n",m[6], m[7], m[8]); } */ roots = ell_3m_eigenvalues_d(eval, m, newton); _ell_3m_evecs_d(evec, eval, roots, m); return roots; } /* ____________________________ 3m2sub ____________________________ */ /* ******** ell_3m2sub_eigenvalues_d ** ** for doing eigensolve of the upper-left 2x2 matrix sub-matrix of a ** 3x3 matrix. The other entries are assumed to be zero. A 0 root is ** put last (in eval[2]), possibly in defiance of the usual eigenvalue ** ordering. */ int ell_3m2sub_eigenvalues_d(double eval[3], const double _m[9]) { double A, B, m[4], D, Dsq, eps=1.0E-11; int roots; /* static const char me[]="ell_3m2sub_eigenvalues_d"; */ m[0] = _m[0]; m[1] = _m[1]; m[2] = _m[3]; m[3] = _m[4]; /* cubic characteristic equation is L^3 + A*L^2 + B*L = 0 */ A = -m[0] - m[3]; B = m[0]*m[3] - m[1]*m[2]; Dsq = A*A - 4*B; /* fprintf(stderr, "!%s: m = {{%f,%f},{%f,%f}} -> A=%f B=%f Dsq=%.17f %s 0 (%.17f)\n", me, m[0], m[1], m[2], m[3], A, B, Dsq, (Dsq > 0 ? ">" : (Dsq < 0 ? "<" : "==")), eps); fprintf(stderr, "!%s: Dsq = \n", me); airFPFprintf_d(stderr, Dsq); fprintf(stderr, "!%s: eps = \n", me); airFPFprintf_d(stderr, eps); ell_3m_print_d(stderr, _m); */ if (Dsq > eps) { D = sqrt(Dsq); eval[0] = (-A + D)/2; eval[1] = (-A - D)/2; eval[2] = 0; roots = ell_cubic_root_three; } else if (Dsq < -eps) { /* no quadratic roots; only the implied zero */ ELL_3V_SET(eval, AIR_NAN, AIR_NAN, 0); roots = ell_cubic_root_single; } else { /* a quadratic double root */ ELL_3V_SET(eval, -A/2, -A/2, 0); roots = ell_cubic_root_single_double; } /* fprintf(stderr, "!%s: Dsq=%f, roots=%d (%f %f %f)\n", me, Dsq, roots, eval[0], eval[1], eval[2]); */ return roots; } void ell_2m_1d_nullspace_d(double ans[2], const double _n[4]) { double n[4], dot, len, span[2]; /* static const char me[]="ell_2m_1d_nullspace_d"; */ ELL_2M_TRANSPOSE(n, _n); dot = ELL_2V_DOT(n + 2*0, n + 2*1); /* fprintf(stderr, "!%s: n = {{%g,%g},{%g,%g}}\n", me, n[0], n[1], n[2], n[3]); fprintf(stderr, "!%s: dot = %g\n", me, dot); */ if (dot > 0) { ELL_2V_ADD2(span, n + 2*0, n + 2*1); } else { ELL_2V_SUB(span, n + 2*0, n + 2*1); } /* have found good description of column span; now need the (perpendicular) nullspace */ ans[0] = span[1]; ans[1] = -span[0]; ELL_2V_NORM(ans, ans, len); /* if (!(AIR_EXISTS(ans[0]) && AIR_EXISTS(ans[1]))) { fprintf(stderr, "!%s: bad! %g %g\n", me, ans[0], ans[1]); } */ return; } void _ell_22v_enforce_orthogonality(double uu[2], double _vv[2]) { double dot, vv[2], len; dot = ELL_2V_DOT(uu, _vv); ELL_2V_SCALE_ADD2(vv, 1, _vv, -dot, uu); ELL_2V_NORM(_vv, vv, len); return; } /* ** NOTE: assumes that eval and roots have come from ** ell_3m2sub_eigenvalues_d(m) */ void _ell_3m2sub_evecs_d(double evec[9], double eval[3], int roots, const double m[9]) { double n[4]; static const char me[]="_ell_3m2sub_evecs_d"; if (ell_cubic_root_three == roots) { /* set off-diagonal entries once */ n[1] = m[1]; n[2] = m[3]; /* find first evec */ n[0] = m[0] - eval[0]; n[3] = m[3] - eval[0]; ell_2m_1d_nullspace_d(evec + 3*0, n); (evec + 3*0)[2] = 0; /* find second evec */ n[0] = m[0] - eval[1]; n[3] = m[3] - eval[1]; ell_2m_1d_nullspace_d(evec + 3*1, n); (evec + 3*1)[2] = 0; _ell_22v_enforce_orthogonality(evec + 3*0, evec + 3*1); /* make right-handed */ ELL_3V_CROSS(evec + 3*2, evec + 3*0, evec + 3*1); } else if (ell_cubic_root_single_double == roots) { /* can pick any 2D basis */ ELL_3V_SET(evec + 3*0, 1, 0, 0); ELL_3V_SET(evec + 3*1, 0, 1, 0); ELL_3V_SET(evec + 3*2, 0, 0, 1); } else { /* ell_cubic_root_single == roots, if assumptions are met */ ELL_3V_SET(evec + 3*0, AIR_NAN, AIR_NAN, 0); ELL_3V_SET(evec + 3*1, AIR_NAN, AIR_NAN, 0); ELL_3V_SET(evec + 3*2, 0, 0, 1); } if (!ELL_3M_EXISTS(evec)) { fprintf(stderr, "%s: given m = \n", me); ell_3m_print_d(stderr, m); fprintf(stderr, "%s: got roots = %s (%d) and evecs = \n", me, airEnumStr(ell_cubic_root, roots), roots); ell_3m_print_d(stderr, evec); } return; } int ell_3m2sub_eigensolve_d(double eval[3], double evec[9], const double m[9]) { int roots; roots = ell_3m2sub_eigenvalues_d(eval, m); _ell_3m2sub_evecs_d(evec, eval, roots, m); return roots; } /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 3m2sub ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ /* ******** ell_3m_svd_d ** ** singular value decomposition: ** mat = uu * diag(sval) * vv ** ** singular values are square roots of eigenvalues of mat * mat^T ** columns of uu are eigenvectors of mat * mat^T ** rows of vv are eigenvectors of mat^T * mat ** ** returns info about singular values according to ellCubeRoot enum ** ** HEY: I think this does the wrong thing when given a symmetric ** matrix with negative eigenvalues . . . */ int ell_3m_svd_d(double uu[9], double sval[3], double vv[9], const double mat[9], const int newton) { double trn[9], msqr[9], eval[3], evec[9]; int roots; ELL_3M_TRANSPOSE(trn, mat); ELL_3M_MUL(msqr, mat, trn); roots = ell_3m_eigensolve_d(eval, evec, msqr, newton); sval[0] = sqrt(eval[0]); sval[1] = sqrt(eval[1]); sval[2] = sqrt(eval[2]); ELL_3M_TRANSPOSE(uu, evec); ELL_3M_MUL(msqr, trn, mat); _ell_3m_evecs_d(vv, eval, roots, msqr); return roots; } /* ** NOTE: profiling showed that about a quarter of the execution time of ** ell_6ms_eigensolve_d() is spent here; so reconsider its need and ** implementation . . . (fabs vs. AIR_ABS() made no difference) */ static void _maxI_sum_find(unsigned int maxI[2], double *sumon, double *sumoff, double mat[6][6]) { double maxm, tmp; unsigned int rrI, ccI; /* we hope that all these loops are unrolled by the optimizer */ *sumon = *sumoff = 0.0; for (rrI=0; rrI<6; rrI++) { *sumon += AIR_ABS(mat[rrI][rrI]); } maxm = -1; maxI[0] = maxI[1] = 0; for (rrI=0; rrI<5; rrI++) { for (ccI=rrI+1; ccI<6; ccI++) { tmp = AIR_ABS(mat[rrI][ccI]); *sumoff += tmp; if (tmp > maxm) { maxm = tmp; maxI[0] = rrI; maxI[1] = ccI; } } } /* if (1) { double nrm, trc; nrm = trc = 0; for (rrI=0; rrI<6; rrI++) { trc += mat[rrI][rrI]; nrm += mat[rrI][rrI]*mat[rrI][rrI]; } for (rrI=0; rrI<5; rrI++) { for (ccI=rrI+1; ccI<6; ccI++) { nrm += 2*mat[rrI][ccI]*mat[rrI][ccI]; } } fprintf(stderr, "---------------- invars = %g %g\n", trc, nrm); } */ return; } static int _compar(const void *A_void, const void *B_void) { const double *A, *B; A = AIR_CAST(const double *, A_void); B = AIR_CAST(const double *, B_void); return (A[0] < B[0] ? 1 : (A[0] > B[0] ? -1 : 0)); } /* ******* ell_6ms_eigensolve_d ** ** uses Jacobi iterations to find eigensystem of 6x6 symmetric matrix, ** given in sym[21], to within convergence threshold eps. Puts ** eigenvalues, in descending order, in eval[6], and corresponding ** eigenvectors in _evec+0, _evec+6, . . ., _evec+30. NOTE: you can ** pass a NULL _evec if eigenvectors aren't needed. ** ** does NOT use biff */ int ell_6ms_eigensolve_d(double eval[6], double _evec[36], const double sym[21], const double eps) { /* char me[]="ell_6ms_eigensolve_d"; */ double mat[2][6][6], evec[2][6][6], sumon, sumoff, evtmp[12]; unsigned int cur, rrI, ccI, maxI[2], iter; if (!( eval && sym && eps >= 0 )) { return 1; } /* copy symmetric matrix sym[] into upper tris of mat[0][][] & mat[1][][] */ mat[0][0][0] = sym[ 0]; mat[0][0][1] = sym[ 1]; mat[0][0][2] = sym[ 2]; mat[0][0][3] = sym[ 3]; mat[0][0][4] = sym[ 4]; mat[0][0][5] = sym[ 5]; mat[0][1][1] = sym[ 6]; mat[0][1][2] = sym[ 7]; mat[0][1][3] = sym[ 8]; mat[0][1][4] = sym[ 9]; mat[0][1][5] = sym[10]; mat[0][2][2] = sym[11]; mat[0][2][3] = sym[12]; mat[0][2][4] = sym[13]; mat[0][2][5] = sym[14]; mat[0][3][3] = sym[15]; mat[0][3][4] = sym[16]; mat[0][3][5] = sym[17]; mat[0][4][4] = sym[18]; mat[0][4][5] = sym[19]; mat[0][5][5] = sym[20]; if (_evec) { /* initialize evec[0]; */ for (rrI=0; rrI<6; rrI++) { for (ccI=0; ccI<6; ccI++) { evec[0][ccI][rrI] = (rrI == ccI); } } } /* fprintf(stderr, "!%s(INIT): m = [", me); for (rrI=0; rrI<6; rrI++) { for (ccI=0; ccI<6; ccI++) { fprintf(stderr, "%f%s", (rrI <= ccI ? mat[0][rrI][ccI] : mat[0][ccI][rrI]), ccI<5 ? "," : (rrI<5 ? ";" : "]")); } fprintf(stderr, "\n"); } */ maxI[0] = maxI[1] = UINT_MAX; /* quiet warnings about using maxI unset */ _maxI_sum_find(maxI, &sumon, &sumoff, mat[0]); cur = 1; /* fake out anticipating first line of loop */ iter = 0; while (sumoff/sumon > eps) { double th, tt, cc, ss; unsigned int P, Q; /* fprintf(stderr, "!%s(%u): sumoff/sumon = %g/%g = %g > %g\n", me, iter, sumoff, sumon, sumoff/sumon, eps); */ cur = 1 - cur; P = maxI[0]; Q = maxI[1]; th = (mat[cur][Q][Q] - mat[cur][P][P])/(2*mat[cur][P][Q]); tt = (th > 0 ? +1 : -1)/(AIR_ABS(th) + sqrt(th*th + 1)); cc = 1/sqrt(tt*tt + 1); ss = cc*tt; /* fprintf(stderr, "!%s(%u): maxI = (P,Q) = (%u,%u) --> ss=%f, cc=%f\n", me, iter, P, Q, ss, cc); fprintf(stderr, " r = ["); for (rrI=0; rrI<6; rrI++) { for (ccI=0; ccI<6; ccI++) { fprintf(stderr, "%g%s", (rrI == ccI ? (rrI == P || rrI == Q ? cc : 1.0) : (rrI == P && ccI == Q ? ss : (rrI == Q && ccI == P ? -ss : 0))), ccI<5 ? "," : (rrI<5 ? ";" : "]")); } fprintf(stderr, "\n"); } */ /* initialize by copying whole matrix */ for (rrI=0; rrI<6; rrI++) { for (ccI=rrI; ccI<6; ccI++) { mat[1-cur][rrI][ccI] = mat[cur][rrI][ccI]; } } /* perform Jacobi rotation */ for (rrI=0; rrI 0.9999999) { /* "from" and "to"-vector almost parallel */ double tu[3], tv[3]; /* temporary storage vectors */ double xx[3]; /* vector most nearly orthogonal to "from" */ double c1, c2, c3; /* coefficients for later use */ int i, j; xx[0] = AIR_ABS(from[0]); xx[1] = AIR_ABS(from[1]); xx[2] = AIR_ABS(from[2]); if (xx[0] < xx[1]) { if (xx[0] < xx[2]) { xx[0] = 1.0; xx[1] = xx[2] = 0.0; } else { xx[2] = 1.0; xx[0] = xx[1] = 0.0; } } else { if (xx[1] < xx[2]) { xx[1] = 1.0; xx[0] = xx[2] = 0.0; } else { xx[2] = 1.0; xx[0] = xx[1] = 0.0; } } tu[0] = xx[0] - from[0]; tu[1] = xx[1] - from[1]; tu[2] = xx[2] - from[2]; tv[0] = xx[0] - to[0]; tv[1] = xx[1] - to[1]; tv[2] = xx[2] - to[2]; c1 = 2.0 / ELL_3V_DOT(tu, tu); c2 = 2.0 / ELL_3V_DOT(tv, tv); c3 = c1 * c2 * ELL_3V_DOT(tu, tv); for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { rot[3*i + j] = - c1 * tu[i] * tu[j] - c2 * tv[i] * tv[j] + c3 * tv[i] * tu[j]; } rot[3*i + i] += 1.0; } } else { /* the most common case, unless "from"="to", or "from"=-"to" */ double hvx, hvz, hvxy, hvxz, hvyz; h = 1.0/(1.0 + e); /* optimization by Gottfried Chen */ hvx = h * vv[0]; hvz = h * vv[2]; hvxy = hvx * vv[1]; hvxz = hvx * vv[2]; hvyz = hvz * vv[1]; rot[3*0 + 0] = e + hvx * vv[0]; rot[3*0 + 1] = hvxy - vv[2]; rot[3*0 + 2] = hvxz + vv[1]; rot[3*1 + 0] = hvxy + vv[2]; rot[3*1 + 1] = e + h * vv[1] * vv[1]; rot[3*1 + 2] = hvyz - vv[0]; rot[3*2 + 0] = hvxz - vv[1]; rot[3*2 + 1] = hvyz + vv[0]; rot[3*2 + 2] = e + hvz * vv[2]; } return; } teem-1.11.0~svn6057/CMake/0000775000175000017500000000000012203513752014565 5ustar domibeldomibelteem-1.11.0~svn6057/CMake/TeemUse.cmake0000664000175000017500000000411212165631065017141 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # IF(NOT Teem_FOUND) MESSAGE(FATAL_ERROR "Something went wrong. You are including TeemUse.cmake but Teem was not found") ENDIF(NOT Teem_FOUND) # Make Teem easier to use INCLUDE_DIRECTORIES(${Teem_INCLUDE_DIRS}) LINK_DIRECTORIES(${Teem_LIBRARY_DIRS}) # Load the compiler settings used for Teem. IF(Teem_BUILD_SETTINGS_FILE) INCLUDE(CMakeImportBuildSettings) CMAKE_IMPORT_BUILD_SETTINGS(${Teem_BUILD_SETTINGS_FILE}) ENDIF(Teem_BUILD_SETTINGS_FILE) # Support static builds IF(NOT Teem_BUILD_SHARED_LIBS) ADD_DEFINITIONS(-DTEEM_STATIC=1) ENDIF(NOT Teem_BUILD_SHARED_LIBS) # Add compiler flags needed to use Teem. SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${Teem_REQUIRED_C_FLAGS}") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${Teem_REQUIRED_EXE_LINKER_FLAGS}") SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${Teem_REQUIRED_SHARED_LINKER_FLAGS}") SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${Teem_REQUIRED_MODULE_LINKER_FLAGS}") teem-1.11.0~svn6057/CMake/TestNO_ICC_IDYNAMIC_NEEDED.cxx0000664000175000017500000000305612165631065021435 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __INTEL_COMPILER //If -i_dynamic is required (i.e. icc v7.1 on Redhat 9 or similar glibc version), //this simple program will fail to compile. #include int main(int argc, char * argv[]) { return 1; } #else //__INTEL_COMPILER // If not the INTEL compiler, just fall though to simplest program int main(int argc, char * argv[]) { return 1; } #endif //__INTEL_COMPILER teem-1.11.0~svn6057/CMake/TestNO_ICC_IDYNAMIC_NEEDED.cmake0000664000175000017500000000573612165631065021722 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # # Check if the system is big endian or little endian # # VARIABLE - variable to store the result to # MACRO(TESTNO_ICC_IDYNAMIC_NEEDED VARIABLE LOCAL_TEST_DIR) IF("HAVE_${VARIABLE}" MATCHES "^HAVE_${VARIABLE}$") TRY_RUN(${VARIABLE} HAVE_${VARIABLE} ${CMAKE_BINARY_DIR} ${LOCAL_TEST_DIR}/TestNO_ICC_IDYNAMIC_NEEDED.cxx OUTPUT_VARIABLE OUTPUT) MESSAGE(STATUS "Check if using the Intel icc compiler, and if -i_dynamic is needed... COMPILE_RESULT...${HAVE_${VARIABLE}} RUN_RESULT...${VARIABLE}\n") IF(HAVE_${VARIABLE}) #Test compiled, either working intel w/o -i_dynamic, or another compiler IF(${VARIABLE}) #Intel icc compiler, -i_dynamic not needed FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeError.log "-i_dynamic not needed, (Not Intel icc, or this version of Intel icc does not conflict with OS glibc.") MESSAGE(STATUS "-i_dynamic not needed, (Not Intel icc, or this version of Intel icc does not conflict with OS glibc.") ELSE(${VARIABLE}) #The compiler is not Intel icc FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeError.log "The compiler ERROR--This should never happen") MESSAGE(STATUS "The compiler ERROR--This should never happen") ENDIF(${VARIABLE}) ELSE(HAVE_${VARIABLE}) #Test did not compile, either badly broken compiler, or intel -i_dynamic needed FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeError.log "\tThe -i_dynamic compiler flag is needed for the Intel icc compiler on this platform.\n") MESSAGE("The -i_dynamic compiler flag is needed for the Intel icc compiler on this platform.") ENDIF(HAVE_${VARIABLE}) FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeError.log "TestNO_ICC_IDYNAMIC_NEEDED produced following output:\n${OUTPUT}\n\n") ENDIF("HAVE_${VARIABLE}" MATCHES "^HAVE_${VARIABLE}$") ENDMACRO(TESTNO_ICC_IDYNAMIC_NEEDED) teem-1.11.0~svn6057/CMake/FindFFTW3.cmake0000664000175000017500000000060611616676203017233 0ustar domibeldomibel FIND_PATH(FFTW3_INCLUDE_DIR fftw3.h /usr/local/include /usr/include ) FIND_LIBRARY(FFTW3_LIBRARY fftw3 /usr/lib /usr/local/lib ) SET(FFTW3_FOUND FALSE) IF(FFTW3_INCLUDE_DIR AND FFTW3_LIBRARY) SET(FFTW3_LIBRARIES ${FFTW3_LIBRARY} ) SET(FFTW3_FOUND TRUE) ENDIF(FFTW3_INCLUDE_DIR AND FFTW3_LIBRARY) MARK_AS_ADVANCED( FFTW3_INCLUDE_DIR FFTW3_LIBRARIES FFTW3_FOUND ) teem-1.11.0~svn6057/CMake/FindLEVMAR.cmake0000664000175000017500000000070511616676203017370 0ustar domibeldomibel FIND_PATH(LEVMAR_INCLUDE_DIR lm.h /usr/local/include /usr/include ) FIND_LIBRARY(LEVMAR_LIBRARY levmar /usr/lib /usr/local/lib ) SET(LEVMAR_FOUND FALSE) IF(LEVMAR_INCLUDE_DIR AND LEVMAR_LIBRARY) SET(LEVMAR_INCLUDE_DIRS ${LEVMAR_INCLUDE_DIR} ) SET(LEVMAR_LIBRARIES ${LEVMAR_LIBRARY} ) SET(LEVMAR_FOUND TRUE) ENDIF(LEVMAR_INCLUDE_DIR AND LEVMAR_LIBRARY) MARK_AS_ADVANCED( LEVMAR_INCLUDE_DIR LEVMAR_LIBRARIES LEVMAR_FOUND ) teem-1.11.0~svn6057/CMake/TestQnanhibit.c0000664000175000017500000000355112165631065017517 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #if defined(__BORLANDC__) # include # include #endif int main(int argc, char *argv[]) { const char * const me=argv[0]; const float zero=0.0F; union { float flt32bit; int int32bit; } qnan; #if defined(__BORLANDC__) // Disable floating point exceptions in Borland _control87(MCW_EM, MCW_EM); #endif // defined(__BORLANDC__) if (sizeof(float) != sizeof(int)) { fprintf(stderr, "%s: MADNESS: sizeof(float)=%d != sizeof(int)=%d\n", me, (int)sizeof(float), (int)sizeof(int)); return -1; } qnan.flt32bit=zero/zero; printf("-DTEEM_QNANHIBIT=%d\n", (qnan.int32bit >> 22) & 1); return (int)((qnan.int32bit >> 22) & 1); } teem-1.11.0~svn6057/CMake/FindBZ2.cmake0000664000175000017500000000331512165631065016774 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # # Find the native BZ2 includes and library # # BZ2_INCLUDE_DIR - where to find bzlib.h, etc. # BZ2_LIBRARIES - List of fully qualified libraries to link against when using bzlib. # BZ2_FOUND - Do not attempt to use zlib if "no" or undefined. FIND_PATH(BZ2_INCLUDE_DIR bzlib.h /usr/local/include /usr/include ) FIND_LIBRARY(BZ2_LIBRARY bz2 /usr/lib /usr/local/lib ) IF(BZ2_INCLUDE_DIR) IF(BZ2_LIBRARY) SET( BZ2_LIBRARIES ${BZ2_LIBRARY} ) SET( BZ2_FOUND "YES" ) ENDIF(BZ2_LIBRARY) ENDIF(BZ2_INCLUDE_DIR) MARK_AS_ADVANCED( BZ2_LIBRARY BZ2_INCLUDE_DIR ) teem-1.11.0~svn6057/CMake/TeemConfig.cmake.in0000664000175000017500000000513112165631065020221 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #----------------------------------------------------------------------------- # # TeemConfig.cmake - Teem CMake configuration file for external projects. # # This file is configured by Teem and used by the TeemUse.cmake module # to load Teem's settings for an external project. # The directory of TeemConfig.cmake is, by definition, Teem_DIR. # (this_dir == Teem_DIR) # GET_FILENAME_COMPONENT(this_dir "${CMAKE_CURRENT_LIST_FILE}" PATH) GET_FILENAME_COMPONENT(Teem_ROOT_DIR "${this_dir}/@Teem_CV_CONFIG_TO_ROOT@" ABSOLUTE) # CMake files required to build client applications that use Teem. SET(Teem_BUILD_SETTINGS_FILE "@Teem_CV_BUILD_SETTINGS_FILE@") SET(Teem_USE_FILE "@Teem_CV_USE_FILE@") # The Teem directories. SET(Teem_EXECUTABLE_DIRS "@Teem_CV_EXECUTABLE_DIRS@") SET(Teem_LIBRARY_DIRS "@Teem_CV_LIBRARY_DIRS@") SET(Teem_INCLUDE_DIRS "@Teem_CV_INCLUDE_DIRS@") # The Teem libraries. SET(Teem_LIBRARIES "@Teem_CV_BUILT_LIBRARIES@") # The C flags added by Teem to the cmake-configured flags. SET(Teem_REQUIRED_C_FLAGS "@Teem_REQUIRED_C_FLAGS@") # The Teem version number SET(Teem_VERSION_MAJOR "@Teem_VERSION_MAJOR@") SET(Teem_VERSION_MINOR "@Teem_VERSION_MINOR@") SET(Teem_VERSION_PATCH "@Teem_VERSION_PATCH@") # Is Teem using shared libraries? SET(Teem_BUILD_SHARED_LIBS "@BUILD_SHARED_LIBS@") # The list of tools in teem SET(Teem_TOOLS "@Teem_TOOLS@") # The Teem library dependencies. IF(NOT Teem_NO_LIBRARY_DEPENDS) INCLUDE("@Teem_CV_LIBRARY_DEPENDS_FILE@") ENDIF(NOT Teem_NO_LIBRARY_DEPENDS) teem-1.11.0~svn6057/CMake/TestQnanhibit.cmake0000664000175000017500000000570212165631065020355 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # # Checks whether the 22nd bit of a 32-bit quiet-NaN is 1 (1) or 0 (0). This # distinction is needed in handling of IEEE floating point special values. # This quantity is independent of endian-ness. # # VARIABLE - variable to store the result to # MACRO(TEST_QNANHIBIT VARIABLE LOCAL_TEST_DIR) IF("HAVE_${VARIABLE}" MATCHES "^HAVE_${VARIABLE}$") TRY_RUN(${VARIABLE} HAVE_${VARIABLE} ${CMAKE_BINARY_DIR} ${LOCAL_TEST_DIR}/TestQnanhibit.c OUTPUT_VARIABLE OUTPUT) MESSAGE(STATUS "Check the value of the 22nd bit of a 32-bit quiet-NaN") IF(HAVE_${VARIABLE}) IF(${VARIABLE} LESS 0) MESSAGE(ERROR " A test (qnanhibit.c) necessary for NrrdIO configuration returned error code. NrrdIO may not properly handle NaN's.") ENDIF(${VARIABLE} LESS 0) IF(${VARIABLE}) FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeError.log "Value of the 22nd bit of a 32-bit quiet-NaN is 1") MESSAGE(STATUS "Check the value of the 22nd bit of a 32-bit quiet-NaN - 1") ELSE(${VARIABLE}) FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeError.log "Value of the 22nd bit of a 32-bit quiet-NaN is 0") MESSAGE(STATUS "Check the value of the 22nd bit of a 32-bit quiet-NaN - 0") ENDIF(${VARIABLE}) ELSE(HAVE_${VARIABLE}) FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeError.log "\tFailed to compile a test (TestQnanhibit.c) necessary to configure for proper handling of IEEE floating point NaN's.\n") MESSAGE(STATUS "Failed to compile a test (TestQnanhibit.c) necessary to configure for proper handling of IEEE floating point NaN's") ENDIF(HAVE_${VARIABLE}) FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeError.log "TestQnanhibit.c produced following output:\n${OUTPUT}\n\n") ENDIF("HAVE_${VARIABLE}" MATCHES "^HAVE_${VARIABLE}$") ENDMACRO(TEST_QNANHIBIT) teem-1.11.0~svn6057/TestBuild.cmake0000664000175000017500000000477012165631065016523 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # Where the source code lives SET (CTEST_SOURCE_DIRECTORY "${CTEST_SCRIPT_DIRECTORY}") SET (CTEST_BINARY_DIRECTORY "${CTEST_SOURCE_DIRECTORY}/build-ctest") # Make sure we always reconfigure cmake stuff from scratch and don't # rely on previously built libraries SET (CTEST_START_WITH_EMPTY_BINARY_DIRECTORY TRUE) SET (CTEST_CMAKE_COMMAND "cmake") # NOTE(bigler): On windows you should make sure something like cygwin is in # your path. Then it will find cygwin's svn. Otherwise, your nightlies # won't find svn and won't do the updates. I put it in the bat script at # the end: # set PATH=%PATH%;"C:\Program Files\CMake 2.4\bin";"c:/cygwin/bin" SET (CTEST_CVS_COMMAND "svn") # A smoke test only builds the code and doesn't run any tests, so we # exclude all tests here #SET (CTEST_COMMAND "ctest -D Nightly") SET (CTEST_COMMAND "ctest -VV -D Experimental -A \"${CTEST_BINARY_DIRECTORY}/CMakeCache.txt\"") SET (CTEST_CMAKE_COMMAND "cmake") SET(CTEST_INITIAL_CACHE " BUILD_EXPERIMENTAL_LIBS:BOOL=OFF BUILD_EXPERIMENTAL_APPS:BOOL=OFF BUILD_HEX:BOOL=OFF BUILD_SHARED_LIBS:BOOL=OFF BUILD_TESTING:BOOL=ON CMAKE_BUILD_TYPE:STRING=Release DART_TESTING_TIMEOUT:INTEGER=600 TEEM_BZIP2:BOOL=ON TEEM_PNG:BOOL=ON TEEM_ZLIB:BOOL=ON ") # Here are some other intersting things you can set for the initial cache # MAKECOMMAND:STRING=/usr/bin/make -j16 # BUILDNAME:STRING=MySpecialConfig teem-1.11.0~svn6057/Testing/0000775000175000017500000000000012203513761015222 5ustar domibeldomibelteem-1.11.0~svn6057/Testing/unrrdu/0000775000175000017500000000000012203513760016540 5ustar domibeldomibelteem-1.11.0~svn6057/Testing/unrrdu/unulist.c0000664000175000017500000000454512165631065020425 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/unrrdu.h" /* ** Tests: */ int main(int argc, const char **argv) { const char *me; airArray *mop; hestParm *hparm; unsigned int uci; FILE *out; int ret; AIR_UNUSED(argc); out = stdout; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); /* This just generates all the usage information for unu itself, and then for all the unu sub-commands. The purpose is to exercise hest's generation of usage info for all the options, and to make sure (by human inspection now, later by something automated) that the use of stderr vs stdout, and return values, is consistent across all commands */ fprintf(out, "%s: ################### BEGIN unu\n", me); unrrduUsageUnu("unu", hparm); fprintf(out, "%s: ################### END unu\n", me); uci = 0; do { fprintf(out, "%s: ################### BEGIN unu %s\n", me, unrrduCmdList[uci]->name); ret = unrrduCmdList[uci]->main(0, NULL, unrrduCmdList[uci]->name, hparm); fprintf(out, "%s: ################### END unu %s (ret=%d)\n", me, unrrduCmdList[uci]->name, ret); uci++; } while (unrrduCmdList[uci]); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/Testing/unrrdu/CMakeLists.txt0000664000175000017500000000241012165631065021303 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images . # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # ADD_EXECUTABLE(test_unulist unulist.c) TARGET_LINK_LIBRARIES(test_unulist teem) ADD_TEST(unulist ${EXECUTABLE_OUTPUT_PATH}/test_unulist) teem-1.11.0~svn6057/Testing/air/0000775000175000017500000000000012203513761015775 5ustar domibeldomibelteem-1.11.0~svn6057/Testing/air/miscAir.c0000664000175000017500000002043212165631065017536 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/air.h" /* ** to test: AIR_EXPORT FILE *airFopen(const char *name, FILE *std, const char *mode); AIR_EXPORT FILE *airFclose(FILE *file); AIR_EXPORT int airSinglePrintf(FILE *file, char *str, const char *fmt, ...); AIR_EXPORT unsigned int airIndex(double min, double val, double max, unsigned int N); AIR_EXPORT unsigned int airIndexClamp(double min, double val, double max, unsigned int N); AIR_EXPORT airULLong airIndexULL(double min, double val, double max, airULLong N); AIR_EXPORT airULLong airIndexClampULL(double min, double val, double max, airULLong N); AIR_EXPORT char *airDoneStr(double start, double here, double end, char *str); AIR_EXPORT void airBinaryPrintUInt(FILE *file, int digits, unsigned int N); AIR_EXPORT int airILoad(void *v, int t); AIR_EXPORT float airFLoad(void *v, int t); AIR_EXPORT double airDLoad(void *v, int t); AIR_EXPORT int airIStore(void *v, int t, int i); AIR_EXPORT float airFStore(void *v, int t, float f); AIR_EXPORT double airDStore(void *v, int t, double d); AIR_EXPORT void airEqvAdd(airArray *eqvArr, unsigned int j, unsigned int k); AIR_EXPORT unsigned int airEqvMap(airArray *eqvArr, unsigned int *map, unsigned int len); AIR_EXPORT unsigned int airEqvSettle(unsigned int *map, unsigned int len); */ static size_t multiply(size_t aa, size_t bb) { return aa*bb; } int main(int argc, const char *argv[]) { const char *me; void *ptr, *ptr2; AIR_UNUSED(argc); me = argv[0]; /* airNull */ { ptr = airNull(); if (NULL != ptr) { fprintf(stderr, "%s: airNull() returned %p not NULL\n", me, ptr); exit(1); } } /* airSetNull */ { ptr = AIR_CAST(void *, airNull); if (NULL == ptr) { fprintf(stderr, "%s: couldn't set a non-NULL pointer", me); exit(1); } ptr2 = airSetNull(&ptr); if (!(NULL == ptr && NULL == ptr2)) { fprintf(stderr, "%s: airSetNull() didn't set (%p) or return (%p) NULL\n", me, ptr, ptr2); exit(1); } } /* AIR_CALLOC, airFree, airTime */ { size_t big = 1024, times = 1, ii, jj; double time0, dtime; unsigned int *data, sum; big = big*big*128; /* 128 megs */ time0 = airTime(); sum = 0; for (ii=0; ii 0 )) { fprintf(stderr, "%s: airTime() => nonsense delta time %g\n", me, dtime); exit(1); } } /* airStderr, airStdout, airStdin */ { FILE *fret; fret = airStderr(); if (stderr != fret) { fprintf(stderr, "%s: airStderr() returned %p not stderr %p\n", me, AIR_CAST(void *, fret), AIR_CAST(void *, stderr)); exit(1); } fret = airStdout(); if (stdout != fret) { fprintf(stdout, "%s: airStdout() returned %p not stdout %p\n", me, AIR_CAST(void *, fret), AIR_CAST(void *, stdout)); exit(1); } fret = airStdin(); if (stdin != fret) { fprintf(stdin, "%s: airStdin() returned %p not stdin %p\n", me, AIR_CAST(void *, fret), AIR_CAST(void *, stdin)); exit(1); } } /* airSprintSize_t, airSprintPtrdiff_t in pptest.c */ /* airPrettySprintSize_t */ { char prstmp[AIR_STRLEN_SMALL]; size_t vals[] = {0, /* 0 */ 800, /* 1 */ 1024, /* 2 = 2^10 */ 1024 + 1, /* 3 */ 500*1024, /* 4 */ 1024*1024, /* 5 = 2^20 */ 1024*(1024 + 1), /* 6 */ 500*1024*1024, /* 7 */ 1024*1024*1024ul, /* 8 = 2^30 */ 1024*1024*(1024ul + 1), /* 9 */ 500*1024ul, /* 10 (will be multiplied below) */ 1024*1024ul, /* 11 = 2^40 */ 1024*(1024ul + 1), /* 12 */ 500*1024*1024, /* 13 */ 1024*1024*1024ul, /* 14 = 2^50 */ 1024*1024*(1024ul + 1), /* 15 */ 500*1024*1024, /* 16 */ 1024*1024*1024ul, /* 17 = 2^60 */ 1024*1024*(1024ul + 1), /* 18 */ 2*1024*1024ul, /* 19 = 2^61 */ 16*1023*1024ul, /* 20 */ 16*1024*1024ul, /* 21 = 2^64 */ 0}; char *string[] = { "0 bytes", /* 0 */ "800 bytes", /* 1 */ "1024 bytes", /* 2 */ "1.00098 KB", /* 3 */ "500 KB", /* 4 */ "1024 KB", /* 5 */ "1.00098 MB", /* 6 */ "500 MB", /* 7 */ "1024 MB", /* 8 */ "1.00098 GB", /* 9 */ "500 GB", /* 10 */ "1024 GB", /* 11 */ "1.00098 TB", /* 12 */ "500 TB", /* 13 */ "1024 TB", /* 14 */ "1.00098 PB", /* 15 */ "500 PB", /* 16 */ "1024 PB", /* 17 */ "1.00098 EB", /* 18 */ "2 EB", /* 19 */ "15.9844 EB", /* 20 */ "blah", /* 21 */ }; unsigned int ii; /* hiding some multiplications in function calls, to quiet compiler warnings. Its ok if there is wrap-around here because then we'll stop testing. However, it would be better if we did NOT rely on what is strictly speaking undefined behavior: the overflow of *unsigned* integral quantities. */ vals[10] = multiply(multiply(vals[10], 1024), 1024); vals[11] = multiply(multiply(vals[11], 1024), 1024); vals[12] = multiply(multiply(vals[12], 1024), 1024); vals[13] = multiply(multiply(vals[13], 1024), 1024); vals[14] = multiply(multiply(vals[14], 1024), 1024); vals[15] = multiply(multiply(vals[15], 1024), 1024); vals[16] = multiply(multiply(multiply(vals[16], 1024), 1024), 1024); vals[17] = multiply(multiply(multiply(vals[17], 1024), 1024), 1024); vals[18] = multiply(multiply(multiply(vals[18], 1024), 1024), 1024); vals[19] = multiply(multiply(multiply(multiply(vals[19], 1024), 1024), 1024), 1024); vals[20] = multiply(multiply(multiply(multiply(vals[20], 1024), 1024), 1024), 1024); vals[21] = multiply(multiply(multiply(multiply(vals[21], 1024), 1024), 1024), 1024); for (ii=0; !ii || vals[ii] > vals[ii-1]; ii++) { airPrettySprintSize_t(prstmp, vals[ii]); if (strcmp(string[ii], prstmp)) { fprintf(stderr, "%s: airPrettySprintSize_t made |%s| not |%s|\n", me, prstmp, string[ii]); exit(1); } fprintf(stderr, "%u: %s\n", ii, prstmp); } } exit(0); } teem-1.11.0~svn6057/Testing/air/amath.c0000664000175000017500000000520312165631065017240 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/air.h" /* ** Tests: ** airLog2, airCbrt */ int main(int argc, const char *argv[]) { char stmp[AIR_STRLEN_SMALL]; airArray *mop; AIR_UNUSED(argc); AIR_UNUSED(argv); mop = airMopNew(); { /* airLog2 */ int ret, l2 = 0; size_t ee = 1; do { if (l2 != (ret = airLog2(ee))) { fprintf(stderr, "airLog2(%s) = %d != %d\n", airSprintSize_t(stmp, ee), ret, l2); airMopError(mop); return 1; } if (ee > 1) { if (-1 != (ret = airLog2(ee+1))) { fprintf(stderr, "airLog2(%s) = %d != -1\n", airSprintSize_t(stmp, ee+1), ret); airMopError(mop); return 1; } } l2 += 1; ee *= 2; } while (l2 < 31); } { /* airCbrt */ unsigned int pi, ti, testnum = 2000; airRandMTState *rng; double aa[2], uu, eps = 8e-27, error; rng = airRandMTStateNew(4242); airMopAdd(mop, rng, (airMopper)airRandMTStateNix, airMopAlways); error = 0; for (ti=0; ti eps) { fprintf(stderr, "average cbrt error was %.17g > eps %0.17g\n", error, eps); airMopError(mop); return 1; } else { fprintf(stderr, "average cbrt error = %.17g\n", error); } } exit(0); } teem-1.11.0~svn6057/Testing/air/pptest.c0000664000175000017500000000331112165631065017463 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/air.h" /* ** Tests: ** airSprintSize_t ** airSprintPtrdiff_t */ int main(int argc, const char *argv[]) { const char *me; size_t sz; ptrdiff_t pd; char stmp[AIR_STRLEN_SMALL]; AIR_UNUSED(argc); me = argv[0]; sz = 123456789; airSprintSize_t(stmp, sz); if (strcmp("123456789", stmp)) { fprintf(stderr, "%s: airSprintSize_t: |%s|\n", me, stmp); exit(1); } pd = -123456789; airSprintPtrdiff_t(stmp, pd); if (strcmp("-123456789", stmp)) { fprintf(stderr, "%s: airSprintPtrdiff_t: |%s|\n", me, stmp); exit(1); } exit(0); } teem-1.11.0~svn6057/Testing/air/strtok.c0000664000175000017500000000666512165631065017511 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/air.h" /* ** Tests: ** airStrtok ** airStrntok ** ** Also uses: ** airArrayNew, airArrayNuke, airArrayLenSet, airArrayLenIncr ** airMopNew, airMopAdd, airMopError, airMopDone */ #define INCR 10 int main(int argc, const char *argv[]) { airArray *mop; const char *me; const char *word[] = { "There's", "a", "certain", "slant", "of", "light", "On", "winter", "afternoons", "That", "oppresses,", "like", "the", "weight", "Of", "cathedral", "tunes.", "Heavenly", "hurt", "it", "gives", "us;", "We", "can", "find", "no", "scar,", "But", "internal", "difference", "Where", "the", "meanings", "are.", "None", "may", "teach", "it", "anything,", "'T", "is", "the", "seal,", "despair,", "An", "imperial", "affliction", "Sent", "us", "of", "the", "air.", "When", "it", "comes,", "the", "landscape", "listens,", "Shadows", "hold", "their", "breath;", "When", "it", "goes,", "'t", "is", "like", "the", "distance", "On", "the", "look", "of", "death.", ""}; const char *sep = " \t\n_", *ww; unsigned int wi, sepLen, lineLen, wordNum; airArray *lineArr; char wordsp[AIR_STRLEN_MED], *line, *last=NULL; AIR_UNUSED(argc); me = argv[0]; sepLen = AIR_CAST(unsigned int, airStrlen(sep)); mop = airMopNew(); lineArr = airArrayNew((void**)(&line), &lineLen, sizeof(char), INCR); airMopAdd(mop, lineArr, (airMopper)airArrayNuke, airMopAlways); /* initialize line with "" */ airArrayLenSet(lineArr, 1); strcpy(line, ""); /* add words and separators onto line */ for (wi=0; airStrlen(word[wi]); wi++) { sprintf(wordsp, "%s%c", word[wi], sep[AIR_MOD(wi, sepLen)]); airArrayLenIncr(lineArr, AIR_CAST(int, airStrlen(wordsp))); strcat(line, wordsp); } /* lose last sep char */ line[strlen(line)-1] = '\0'; /* start tokenizing and comparing */ wordNum = airStrntok(line, sep); if (75 != wordNum) { fprintf(stderr, "%s: wordNum %u != 75\n", me, wordNum); airMopError(mop); return 1; } wi = 0; for (ww = airStrtok(line, sep, &last); ww; ww = airStrtok(NULL, sep, &last)) { if (strcmp(word[wi], ww)) { fprintf(stderr, "%s: word[%u] |%s| != parsed |%s|\n", me, wi, word[wi], ww); airMopError(mop); exit(1); } wi++; } /* we're done */ airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/Testing/air/CMakeLists.txt0000664000175000017500000000363112165631065020545 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images . # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # ADD_EXECUTABLE(test_miscAir miscAir.c) TARGET_LINK_LIBRARIES(test_miscAir teem) ADD_TEST(misc ${EXECUTABLE_OUTPUT_PATH}/test_miscAir) ADD_EXECUTABLE(test_mtrand mtrand.c) TARGET_LINK_LIBRARIES(test_mtrand teem) ADD_TEST(mtrand ${EXECUTABLE_OUTPUT_PATH}/test_mtrand) ADD_EXECUTABLE(test_amath amath.c) TARGET_LINK_LIBRARIES(test_amath teem) ADD_TEST(amath ${EXECUTABLE_OUTPUT_PATH}/test_amath) ADD_EXECUTABLE(test_string string.c) TARGET_LINK_LIBRARIES(test_string teem) ADD_TEST(string ${EXECUTABLE_OUTPUT_PATH}/test_string) ADD_EXECUTABLE(test_strtok strtok.c) TARGET_LINK_LIBRARIES(test_strtok teem) ADD_TEST(strtok ${EXECUTABLE_OUTPUT_PATH}/test_strtok) ADD_EXECUTABLE(test_pptest pptest.c) TARGET_LINK_LIBRARIES(test_pptest teem) ADD_TEST(pptest ${EXECUTABLE_OUTPUT_PATH}/test_pptest) teem-1.11.0~svn6057/Testing/air/string.c0000664000175000017500000001066212165631065017461 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/air.h" /* ** Tests: ** airStrlen ** airStrdup ** airStrcpy ** ** Also uses: ** airMopNew, airMopAdd, airMopError, airMopDone */ /* random test strings */ #define STR_A1 "The facts of life: To make an alteration in the evolvement of an organic life system is fatal. A coding sequence cannot be revised once it's been established." #define STR_A2 "Because by the second day of incubation, any cells that have undergone reversion mutations give rise to revertant colonies like rats leaving a sinking ship; then the ship sinks." #define STR_A (STR_A1 " " STR_A2) #define STR_B1 "We've already tried it. Ethyl methane sulfonate is an alkylating agent and a potent mutagen. It created a virus so lethal the subject was dead before he left the table." #define STR_B2 "Wouldn't obstruct replication, but it does give rise to an error in replication so that the newly formed DNA strand carries a mutation and you've got a virus again. But this - all of this is academic. You were made as well as we could make you." #define STR_B (STR_B1 " " STR_B2) #define STR_AB (STR_A " " STR_B) int main(int argc, const char *argv[]) { airArray *mop; const char *me; char *aa, *aaCopy, *ab; size_t aaSize, abSize; AIR_UNUSED(argc); me = argv[0]; mop = airMopNew(); if (0 != airStrlen(NULL)) { fprintf(stderr, "%s: 0 != airStrlen(NULL)\n", me); airMopError(mop); exit(1); } if (0 != airStrlen("")) { fprintf(stderr, "%s: 0 != airStrlen(\"\")\n", me); airMopError(mop); exit(1); } if (1 != airStrlen("A")) { fprintf(stderr, "%s: 1 != airStrlen(\"A\")\n", me); airMopError(mop); exit(1); } if (NULL != airStrdup(NULL)) { fprintf(stderr, "%s: NULL != airStrdup(NULL)\n", me); airMopError(mop); exit(1); } if (strlen(STR_A) != airStrlen(STR_A)) { fprintf(stderr, "%s: strlen %u != airStrlen %u of |%s|\n", me, AIR_CAST(unsigned int, strlen(STR_A)), AIR_CAST(unsigned int, airStrlen(STR_A)), STR_A); airMopError(mop); exit(1); } aa = airStrdup(STR_A); airMopAdd(mop, aa, airFree, airMopAlways); if (strcmp(aa, STR_A)) { fprintf(stderr, "%s: airStrdup failure: |%s| != |%s|\n", me, aa, STR_A); airMopError(mop); exit(1); } aaSize = strlen(aa)+1; aaCopy = AIR_CALLOC(aaSize, char); airMopAdd(mop, aaCopy, airFree, airMopAlways); abSize = strlen(STR_A) + strlen(" ") + strlen(STR_B) + 1; ab = AIR_CALLOC(abSize, char); airMopAdd(mop, ab, airFree, airMopAlways); sprintf(ab, "%s %s", STR_A, STR_B); #define TEST(COPY, COMP) \ airStrcpy(aaCopy, aaSize, COPY); \ if (strcmp(aaCopy, COMP)) { \ fprintf(stderr, "%s: airStrcpy failure: |%s| != |%s|\n", \ me, aaCopy, COMP); \ airMopError(mop); \ exit(1); \ } /* different args (to copy) to airStrcpy */ /* arg to copy is smaller than dest */ TEST("", ""); TEST(NULL, ""); /* depends on previous test result */ TEST(STR_A1, STR_A1); TEST(STR_A2, STR_A2); /* arg to copy is same size as dest */ TEST(STR_A, STR_A); TEST(aa, aa); /* arg to copy is bigger than dest */ TEST(ab, aa); airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/Testing/air/TODO.txt0000664000175000017500000000122612042402031017270 0ustar domibeldomibel Testing airEnums: construct different kinds of enums, okay and broken, and use functions on them, including airEnumUnknown mop.c: airMopSub, airMopMem, airMopUnMem (though never used?) ?? airMopPrint, airMopDebug, finish miscAir.c tests array.c: airArrays (though used in biff, which is tested) ? threadAir.c: airThreads 754.c: airFPPartsToVal_f, airFPPartsToVal_d, airFPValToParts_d, airFPGen_f, airFPGen_d, airFPClass_f, airFPClass_d, airIsInf_f, airExists, airFPFprintf_f, airFPFprintf_d parseAir.c: (though used as part of nrrdRead) string.c: (though used as part of nrrdRead) math.c: not even sure how to test most of this ? dio.c teem-1.11.0~svn6057/Testing/air/mtrand.c0000664000175000017500000000650712165631065017443 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/air.h" /* ** Tests: ** airRandMTStateNew ** airRandMTStateNix ** airSrandMT_r ** airRandMT ** ** Also uses: ** airMopNew, airMopAdd, airMopError, airMopDone */ #define NUM 10 int main(int argc, const char *argv[]) { airArray *mop; airRandMTState *rng; const char *me; unsigned int rval[NUM][NUM] = { {2357136044u, 2546248239u, 3071714933u, 3626093760u, 2588848963u, 3684848379u, 2340255427u, 3638918503u, 1819583497u, 2678185683u}, {1608637542u, 3421126067u, 4083286876u, 787846414u, 3143890026u, 3348747335u, 2571218620u, 2563451924u, 670094950u, 1914837113u}, {197744554u, 2405527281u, 1590178649u, 2055114040u, 1040749045u, 1355459964u, 2699301070u, 1591340141u, 4252490304u, 3121394926u}, {451710822u, 4140706415u, 550374602u, 880776961u, 375407235u, 576831824u, 495976644u, 1350392909u, 3211465673u, 1227650870u}, {2567526101u, 397661439u, 2237017401u, 316000557u, 1060138423u, 2802111455u, 1449535759u, 751581949u, 3635455645u, 658021748u}, {429171210u, 2009581671u, 1300722668u, 3858470021u, 3363216262u, 1963629412u, 2166299591u, 229689286u, 484002369u, 2062223911u}, {23250075u, 3670330222u, 1860540774u, 4216169317u, 1062279565u, 2886996639u, 2197431119u, 3112004045u, 3229777453u, 1632140913u}, {2869147098u, 1558248213u, 585501645u, 3600180646u, 2654279825u, 3658135664u, 287832047u, 912891514u, 2926707351u, 937957965u}, {1891499427u, 1885608988u, 3850740167u, 3832766153u, 2073041664u, 3289176644u, 989474400u, 2841420218u, 4096852366u, 1816963771u}, {2552602868u, 2086504389u, 219288614u, 3347214808u, 215326247u, 3609464630u, 3506494207u, 997691580u, 1726903302u, 3302470737u} }; unsigned int ii, jj, rr; AIR_UNUSED(argc); me = argv[0]; mop = airMopNew(); rng = airRandMTStateNew(0); airMopAdd(mop, rng, (airMopper)airRandMTStateNix, airMopAlways); for (jj=0; jj /* ** Tests: ** */ #define INTERP_KERN_NUM 4 #define BLUR_KERN_NUM 5 int main(int argc, const char **argv) { const char *me; Nrrd *nscl; double *dscl; airArray *mop; char *fullname; gageContext *igctx[INTERP_KERN_NUM], *bgctx[BLUR_KERN_NUM]; const NrrdKernel *ikern[INTERP_KERN_NUM] = { nrrdKernelBox, nrrdKernelTent, nrrdKernelBCCubic, nrrdKernelCatmullRom, }; double ikparm[INTERP_KERN_NUM][NRRD_KERNEL_PARMS_NUM] = { {1.0}, {1.0}, {1.0, 0.0, 0.5}, {AIR_NAN}, }; const NrrdKernel *bkern[BLUR_KERN_NUM] = { nrrdKernelTent, nrrdKernelBSpline3, nrrdKernelBSpline5, nrrdKernelBCCubic, nrrdKernelGaussian, }; const NrrdKernel *bkernD[BLUR_KERN_NUM] = { nrrdKernelForwDiff, nrrdKernelBSpline3D, nrrdKernelBSpline5D, nrrdKernelBCCubicD, nrrdKernelGaussianD, }; const NrrdKernel *bkernDD[BLUR_KERN_NUM] = { nrrdKernelZero, nrrdKernelBSpline3DD, nrrdKernelBSpline5DD, nrrdKernelBCCubicDD, nrrdKernelGaussianDD, }; double bkparm[BLUR_KERN_NUM][NRRD_KERNEL_PARMS_NUM] = { {1.0}, {AIR_NAN}, {AIR_NAN}, {2.0, 1.0, 0.0}, {1.2, 5.0}, }; const double *ivalAns[INTERP_KERN_NUM], *bvalAns[BLUR_KERN_NUM], *bgrdAns[BLUR_KERN_NUM], *bhesAns[BLUR_KERN_NUM]; int E; unsigned int sx, sy, sz, ki; AIR_UNUSED(argc); me = argv[0]; mop = airMopNew(); nscl = nrrdNew(); airMopAdd(mop, nscl, (airMopper)nrrdNuke, airMopAlways); fullname = testDataPathPrefix("fmob-c4h.nrrd"); airMopAdd(mop, fullname, airFree, airMopAlways); if (nrrdLoad(nscl, fullname, NULL)) { char *err; airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble reading data \"%s\":\n%s", me, fullname, err); airMopError(mop); return 1; } /* make sure its a double-type volume (assumed below) */ if (nrrdTypeDouble != nscl->type) { fprintf(stderr, "%s: volume type %s != expected type %s\n", me, airEnumStr(nrrdType, nscl->type), airEnumStr(nrrdType, nrrdTypeDouble)); airMopError(mop); return 1; } dscl = AIR_CAST(double *, nscl->data); sx = AIR_CAST(unsigned int, nscl->axis[0].size); sy = AIR_CAST(unsigned int, nscl->axis[1].size); sz = AIR_CAST(unsigned int, nscl->axis[2].size); for (ki=0; kiname, err); airMopError(mop); return 1; } ivalAns[ki] = gageAnswerPointer(igctx[ki], gpvl, gageSclValue); } /* traverse all samples of volume, probing with the interpolating kernels, make sure we recover the original values */ { unsigned int xi, yi, zi; double pval[INTERP_KERN_NUM], err, rval; int pret; for (zi=0; ziname, igctx[ki]->errNum, igctx[ki]->errStr); airMopError(mop); return 1; } pval[ki] = *ivalAns[ki]; err = AIR_ABS(rval - pval[ki]); if (err) { fprintf(stderr, "%s: interp's [%u,%u,%u] %s probe %f " "!= true %f (err %f)\n", me, xi, yi, zi, ikern[ki]->name, pval[ki], rval, err); airMopError(mop); return 1; } } } } } } /* set up contexts for non-interpolating (blurring) kernels, and their first and second derivatives */ for (ki=0; kiname, err); airMopError(mop); return 1; } fprintf(stderr, "%s radius = %u\n", bkern[ki]->name, bgctx[ki]->radius); bvalAns[ki] = gageAnswerPointer(bgctx[ki], gpvl, gageSclValue); bgrdAns[ki] = gageAnswerPointer(bgctx[ki], gpvl, gageSclGradVec); bhesAns[ki] = gageAnswerPointer(bgctx[ki], gpvl, gageSclHessian); } { #define POS_NUM 12 double xp[POS_NUM], yp[POS_NUM], zp[POS_NUM], pos[POS_NUM*POS_NUM*POS_NUM][3], *prbd, offs[POS_NUM/2] = {0, 1.22222, 2.444444, 3.777777, 5.88888, 7.55555}; Nrrd *nprbd, *ncorr; unsigned int ii, jj, kk, qlen = 1 + 3 + 9; char *corrfn, explain[AIR_STRLEN_LARGE]; int pret, differ; corrfn = testDataPathPrefix("test/probeSclAns.nrrd"); airMopAdd(mop, corrfn, airFree, airMopAlways); ncorr = nrrdNew(); airMopAdd(mop, ncorr, (airMopper)nrrdNuke, airMopAlways); if (nrrdLoad(ncorr, corrfn, NULL)) { char *err; airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble reading data \"%s\":\n%s", me, corrfn, err); airMopError(mop); return 1; } for (ii=0; iidata); for (ii=0; iiname, bgctx[ki]->errNum, bgctx[ki]->errStr); airMopError(mop); return 1; } prbd[0 + qlen*(ki + BLUR_KERN_NUM*(ii))] = bvalAns[ki][0]; ELL_3V_COPY(prbd + 1 + qlen*(ki + BLUR_KERN_NUM*(ii)), bgrdAns[ki]); ELL_9V_COPY(prbd + 4 + qlen*(ki + BLUR_KERN_NUM*(ii)), bhesAns[ki]); } } /* HEY: weirdly, so far its only on Windows (and more than 10 times worse on Cygwin) this epsilon needs to be larger than zero, and only for the radius 6 Gaussian? */ if (nrrdCompare(ncorr, nprbd, AIR_FALSE /* onlyData */, 8.0e-14 /* epsilon */, &differ, explain)) { char *err; airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble comparing:\n%s", me, err); airMopError(mop); return 1; } if (differ) { fprintf(stderr, "%s: probed values not correct: %s\n", me, explain); airMopError(mop); return 1; } else { fprintf(stderr, "all good\n"); } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/Testing/gage/probeMulti.c0000664000175000017500000004414012165631065020423 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/gage.h" /* ** Tests: ** nrrdQuantize (to 8-bits), nrrdUnquantize ** nrrdArithBinaryOp (with nrrdBinaryOpSubtract) */ #define KERN_SIZE_MAX 10 #define PROBE_NUM 300 static void errPrefix(char *dst, int typi, unsigned int supi, unsigned int prbi, unsigned int probePass, double dxi, double dyi, double dzi, unsigned int xi, unsigned int yi, unsigned int zi) { sprintf(dst, "%s[%s][%u] #%u pp %u: (%g,%g,%g)->(%u,%u,%u): ", "probeMulti", airEnumStr(nrrdType, typi), supi, prbi, probePass, dxi, dyi, dzi, xi, yi, zi); return; } int main(int argc, const char **argv) { airArray *mop, *submop; char *err; int typi; unsigned int supi, probePass, cti /* context copy index */, pvlIdx[NRRD_TYPE_MAX+1], sx, sy, sz, subnum; size_t sizes[3] = {42,61,50} /* one of these must be even */, ii, nn; Nrrd *norigScl, *nucharScl, *nunquant, *nqdiff, *nconvScl[NRRD_TYPE_MAX+1]; unsigned char *ucharScl; gageContext *gctx[2][KERN_SIZE_MAX+1]; gagePerVolume *gpvl[2][NRRD_TYPE_MAX+1][KERN_SIZE_MAX+1]; const double *vansScl[2][NRRD_TYPE_MAX+1][KERN_SIZE_MAX+1], *gansScl[2][NRRD_TYPE_MAX+1][KERN_SIZE_MAX+1], *hansScl[2][NRRD_TYPE_MAX+1][KERN_SIZE_MAX+1]; double *origScl, omin, omax, dsx, dsy, dsz, spcOrig[NRRD_SPACE_DIM_MAX] = {0.0, 0.0, 0.0}, spcVec[3][NRRD_SPACE_DIM_MAX] = { {1.1, 0.0, 0.0}, {0.0, 2.2, 0.0}, {0.0, 0.0, 3.3}}; mop = airMopNew(); #define NRRD_NEW(name, mop) \ (name) = nrrdNew(); \ airMopAdd((mop), (name), (airMopper)nrrdNuke, airMopAlways) /* --------------------------------------------------------------- */ /* Creating initial volume */ NRRD_NEW(norigScl, mop); if (nrrdMaybeAlloc_nva(norigScl, nrrdTypeDouble, 3, sizes)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "trouble allocating:\n%s", err); airMopError(mop); return 1; } origScl = AIR_CAST(double *, norigScl->data); nn = nrrdElementNumber(norigScl); airSrandMT(42*42); for (ii=0; iioldMin == omin && nucharScl->oldMax == omax )) { fprintf(stderr, "quantization range [%g,%g] != real range [%g,%g]\n", nucharScl->oldMin, nucharScl->oldMax, omin, omax); airMopError(submop); airMopError(mop); return 1; } { double *qdiff, *unquant; /* empirically determined tolerance, which had to be increased in order to work under valgrind (!)- perhaps because of a difference in the use of 80-bit registers */ double epsilon=0.50000000000004; qdiff = AIR_CAST(double *, nqdiff->data); unquant = AIR_CAST(double *, nunquant->data); for (ii=0; ii dd = 0.5 */ dd = qdiff[ii]*256/(omax - omin); if (AIR_ABS(dd) > epsilon) { unsigned int ui; ui = AIR_CAST(unsigned int, ii); fprintf(stderr, "|orig[%u]=%.17g - unquant=%.17g|*256/%.17g " "= %.17g > %.17g!\n", ui, origScl[ii], unquant[ii], omax - omin, AIR_ABS(dd), epsilon); airMopError(submop); airMopError(mop); return 1; } } } airMopOkay(submop); ucharScl = AIR_CAST(unsigned char *, nucharScl->data); /* --------------------------------------------------------------- */ /* Converting to all other types */ for (typi=nrrdTypeUnknown+1; typipvl[] array for the per-type perVolume. Having to do this is a symptom of bad API design in gage */ pvlIdx[typi] = pvii++; } if (!E) E |= gageQueryItemOn(gctx[0][supi], gpvl[0][typi][supi], gageSclValue); if (E) { break; } } if (!E) E |= gageUpdate(gctx[0][supi]); if (E) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "trouble (supi=%u, %d/%s) set-up:\n%s\n", supi, typi, airEnumStr(nrrdType, typi), err); airMopError(mop); return 1; } if (gctx[0][supi]->radius != supi) { fprintf(stderr, "supi %u != gageContext->radius %u\n", supi, gctx[0][supi]->radius); airMopError(mop); return 1; } for (typi=nrrdTypeUnknown+1; typipvl[pvlIdx[typi]]; vansScl[1][typi][supi] = gageAnswerPointer(gctx[1][supi], gpvl[1][typi][supi], gageSclValue); gansScl[1][typi][supi] = gageAnswerPointer(gctx[1][supi], gpvl[1][typi][supi], gageSclGradVec); hansScl[1][typi][supi] = gageAnswerPointer(gctx[1][supi], gpvl[1][typi][supi], gageSclHessian); } } /* --------------------------------------------------------------- */ /* the two different probing passes are just to use two different sets of kernels (first nrrdKernelBoxSupportDebug on pass 0, then nrrdKernelCos4SupportDebug and its derivatives on pass 1). Because nrrdKernelBoxSupportDebug has already been set prior to the first pass, there is some some second-time only "if (1 == probePass)" logic that confuses the clarity of this */ for (probePass=0; probePass<=1; probePass++) { unsigned int prbi, lastjj=0, xi, yi, zi; double thet, xu, yu, zu, dxi, dyi, dzi, elapsed[2][KERN_SIZE_MAX+1], time0; char errpre[AIR_STRLEN_LARGE]; if (1 == probePass) { /* switch to cos^4 kernel, turn on gradient and hessian */ for (cti=0; cti<2; cti++) { for (supi=1; supi<=KERN_SIZE_MAX; supi++) { int E; double kparm[1]; gageParmSet(gctx[cti][supi], gageParmRenormalize, AIR_FALSE); gageParmSet(gctx[cti][supi], gageParmCheckIntegrals, AIR_TRUE); kparm[0] = supi; E = 0; if (!E) E |= gageKernelSet(gctx[cti][supi], gageKernel00, nrrdKernelCos4SupportDebug, kparm); if (!E) E |= gageKernelSet(gctx[cti][supi], gageKernel11, nrrdKernelCos4SupportDebugD, kparm); if (!E) E |= gageKernelSet(gctx[cti][supi], gageKernel22, nrrdKernelCos4SupportDebugDD, kparm); for (typi=nrrdTypeUnknown+1; typierrNum, gctx[cti][supi]->errStr); airMopError(mop); return 1; } elapsed[cti][supi] = airTime() - time0; for (typi=nrrdTypeUnknown+1; typidata, xi + sx*(yi + sy*zi)); /* when using box, gage-reconstructed value should match value from probing */ if (arrayval != probeval) { #define SPRINT_ERR_PREFIX \ errPrefix(errpre, typi, supi, prbi, probePass, \ dxi, dyi, dzi, xi, yi, zi) SPRINT_ERR_PREFIX; fprintf(stderr, "%s: (cti %u) probed %g != conv %g\n", errpre, cti, probeval, arrayval); airMopError(mop); return 1; } /* trueval on pass 0 is the original uchar value */ trueval = AIR_CAST(double, ucharScl[xi + sx*(yi + sy*zi)]); } else { /* trueval on pass 1 is the value from probing uchar volume */ trueval = vansScl[cti][nrrdTypeUChar][supi][0]; } if (nrrdTypeChar == typi && trueval > 127) { /* recreate value wrapping of signed char */ trueval -= 256; } if (0 == cti) { truevalOrig[typi] = trueval; } else { /* make sure that result from gageContextCopy'd context (trueval) is same result as original (truevalOrig[typi]) */ if (truevalOrig[typi] != trueval) { SPRINT_ERR_PREFIX; fprintf(stderr, "%s: original->%g, gageContextCopy->%g\n", errpre, truevalOrig[typi], trueval); airMopError(mop); return 1; } } /* regardless of the volume (excepting where we've continue'd, above) the reconstructed value probeval should match trueval */ if (trueval != probeval) { SPRINT_ERR_PREFIX; fprintf(stderr, "%s: (cti %u) probed %g != true %g\n", errpre, cti, probeval, trueval); airMopError(mop); return 1; } if (1 == probePass) { /* and when we use a differentiable kernel, the gradient and Hessian had better agree too */ double diff3[3], diff9[9]; ELL_3V_SUB(diff3, gansScl[cti][nrrdTypeUChar][supi], gansScl[cti][typi][supi]); if (ELL_3V_LEN(diff3) > 0.0) { SPRINT_ERR_PREFIX; fprintf(stderr, "%s: (cti %u) probed gradient error len %f\n", errpre, cti, ELL_3V_LEN(diff3)); airMopError(mop); return 1; } ELL_9V_SUB(diff9, hansScl[cti][nrrdTypeUChar][supi], hansScl[cti][typi][supi]); if (ELL_9V_LEN(diff9) > 0.0) { SPRINT_ERR_PREFIX; fprintf(stderr, "%s: (cti %u) probed hessian error len %f\n", errpre, cti, ELL_9V_LEN(diff9)); airMopError(mop); return 1; } } } } } } for (cti=0; cti<2; cti++) { for (supi=1; supi<=KERN_SIZE_MAX; supi++) { fprintf(stderr, "elapsed[%u][%u] = %g ms\n", cti, supi, 1000*elapsed[cti][supi]); } } } #undef NRRD_NEW #undef SPRINT_ERR_PREFIX airMopOkay(mop); exit(0); } teem-1.11.0~svn6057/Testing/gage/CMakeLists.txt0000664000175000017500000000313012165631065020667 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images . # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # ADD_EXECUTABLE(test_probeScl probeScl.c) TARGET_LINK_LIBRARIES(test_probeScl teem) ADD_TEST(probeScl ${EXECUTABLE_OUTPUT_PATH}/test_probeScl) ADD_EXECUTABLE(test_probePolynomial probePolynomial.c) TARGET_LINK_LIBRARIES(test_probePolynomial teem) ADD_TEST(probePolynomial ${EXECUTABLE_OUTPUT_PATH}/test_probePolynomial) ADD_EXECUTABLE(test_probeMulti probeMulti.c) TARGET_LINK_LIBRARIES(test_probeMulti teem) ADD_TEST(probeMulti ${EXECUTABLE_OUTPUT_PATH}/test_probeMulti) teem-1.11.0~svn6057/Testing/gage/probePolynomial.c0000664000175000017500000002572012165631065021457 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/gage.h" #define PROBE "probePolynomial" /* ** Tests: ** */ /* the hope is that without too much work it would be possible to increase this to 4th-order polynomials and 4th-order derivatives */ #define POWER_MAX 3 /* ** polyeval takes a polynomial defined by coef -- ** coef[i][j][k] is the coefficient for (x^i)*(y^j)*(z^k) -- ** and evaluates at position pos[] the dx-th derivative along X, ** the dy-th derivative along Y, and the dz-th derivative along Z, ** dx = dy = dz = 0 to evaluate the polynomial itself */ static double polyeval(double coef[POWER_MAX+1][POWER_MAX+1][POWER_MAX+1], unsigned int dx, unsigned int dy, unsigned int dz, const double pos[3]) { unsigned int xi, yi, zi; double tmp, ret, xp[POWER_MAX+1], yp[POWER_MAX+1], zp[POWER_MAX+1], dc[POWER_MAX+1][POWER_MAX+1] = { {1.0, 1.0, 1.0, 1.0}, /* how coeffs are scaled for 0th derivative */ {0.0, 1.0, 2.0, 3.0}, /* how coeffs are scaled for 1st derivative */ {0.0, 0.0, 2.0, 6.0}, /* how coeffs are scaled for 2nd derivative */ {0.0, 0.0, 0.0, 6.0}}; /* how coeffs are scaled for 3rd derivative */ tmp = 1.0; for (xi=0; xi<=POWER_MAX; xi++) { xp[xi] = tmp; tmp *= pos[0]; } tmp = 1.0; for (yi=0; yi<=POWER_MAX; yi++) { yp[yi] = tmp; tmp *= pos[1]; } tmp = 1.0; for (zi=0; zi<=POWER_MAX; zi++) { zp[zi] = tmp; tmp *= pos[2]; } ret = 0.0; for (xi=dx; xi<=POWER_MAX; xi++) { for (yi=dy; yi<=POWER_MAX; yi++) { for (zi=dz; zi<=POWER_MAX; zi++) { ret += (coef[xi][yi][zi]*xp[xi-dx]*yp[yi-dy]*zp[zi-dz] *dc[dx][xi]*dc[dy][yi]*dc[dz][zi]); } } } return ret; } static void randvec(double vec[NRRD_SPACE_DIM_MAX], double lenexp, double lensig, airRandMTState *rng) { double tmp, clen[2], len; airNormalRand_r(vec + 0, vec + 1, rng); airNormalRand_r(vec + 2, NULL, rng); ELL_3V_NORM(vec, vec, tmp); airNormalRand_r(clen + 0, clen + 1, rng); /* rician noise, actually */ clen[0] = lenexp + lensig*clen[0]; clen[1] = lensig*clen[1]; len = ELL_2V_LEN(clen); ELL_3V_SCALE(vec, len, vec); } /* ** makeVolume allocates inside nin a new randomly oriented volume of ** size sx-by-sy-sz; the spaceDirection vectors are random but enforced ** to not be too parallel */ static gageContext * makeVolume(Nrrd *nin, unsigned int sx, unsigned int sy, unsigned int sz, double coef[POWER_MAX+1][POWER_MAX+1][POWER_MAX+1], airRandMTState *rng) { static const char me[]="makeVolume"; double spcOrig[NRRD_SPACE_DIM_MAX], spcVec[3][NRRD_SPACE_DIM_MAX], angle10, angle20, angle21, ooff[3], aperm=0.6, lexp=0.1, lstdv=0.04, kparm[NRRD_KERNEL_PARMS_NUM]; gageContext *gctx; gagePerVolume *gpvl; unsigned int xi, yi, zi; airArray *submop; int EE; submop = airMopNew(); randvec(spcVec[0], lexp, lstdv, rng); do { randvec(spcVec[1], lexp, lstdv, rng); angle10 = ell_3v_angle_d(spcVec[1], spcVec[0]); } while (!( AIR_IN_OP(AIR_PI*(1-aperm)/2, angle10, AIR_PI*(1+aperm)/2) )); do { randvec(spcVec[2], lexp, lstdv, rng); angle20 = ell_3v_angle_d(spcVec[2], spcVec[0]); angle21 = ell_3v_angle_d(spcVec[2], spcVec[1]); } while (!( AIR_IN_OP(AIR_PI*(1-aperm)/2, angle20, AIR_PI*(1+aperm)/2) && AIR_IN_OP(AIR_PI*(1-aperm)/2, angle21, AIR_PI*(1+aperm)/2) )); /* center volume near origin */ airNormalRand_r(ooff + 0, ooff + 1, rng); airNormalRand_r(ooff + 2, NULL, rng); ELL_3V_SET(spcOrig, 0.0, 0.0, 0.0); ELL_3V_SCALE_INCR(spcOrig, -AIR_CAST(double, sx)/2.0 + 2*ooff[0], spcVec[0]); ELL_3V_SCALE_INCR(spcOrig, -AIR_CAST(double, sy)/2.0 + 2*ooff[1], spcVec[1]); ELL_3V_SCALE_INCR(spcOrig, -AIR_CAST(double, sz)/2.0 + 2*ooff[1], spcVec[2]); /* allocate data */ if (nrrdMaybeAlloc_va(nin, nrrdTypeDouble, 3, AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, sz)) || nrrdSpaceSet(nin, nrrdSpaceRightAnteriorSuperior) || nrrdSpaceOriginSet(nin, spcOrig)) { biffMovef(PROBE, NRRD, "%s: trouble setting volume", me); airMopError(submop); return NULL; } nrrdAxisInfoSet_va(nin, nrrdAxisInfoSpaceDirection, spcVec[0], spcVec[1], spcVec[2]); nrrdAxisInfoSet_va(nin, nrrdAxisInfoCenter, nrrdCenterCell, nrrdCenterCell, nrrdCenterCell); /* set data */ { double *ddata = AIR_CAST(double *, nin->data); for (zi=0; zi POWER_MAX) { coef[xi][yi][zi] = 0.0; } else { airNormalRand_r(&(coef[xi][yi][zi]), NULL, rng); } } } } sx = 20 + airRandInt_r(rng, 120); sy = 20 + airRandInt_r(rng, 120); sz = 20 + airRandInt_r(rng, 120); fprintf(stderr, "%u: %u %u %u: ", runIdx, sx, sy, sz); fflush(stderr); if (!(gctx = makeVolume(nin, sx, sy, sz, coef, rng))) { airMopAdd(mop, err = biffGetDone(PROBE), airFree, airMopAlways); fprintf(stderr, "trouble creating volume:\n%s", err); airMopError(submop); airMopError(mop); return 1; } airMopAdd(submop, gctx, (airMopper)gageContextNix, airMopAlways); vmeas = gageAnswerPointer(gctx, gctx->pvl[0], gageSclValue); gmeas = gageAnswerPointer(gctx, gctx->pvl[0], gageSclGradVec); hmeas = gageAnswerPointer(gctx, gctx->pvl[0], gageSclHessian); ELL_3V_SET(pos, 0, 0, 0); gageProbeSpace(gctx, pos[0], pos[1], pos[2], AIR_FALSE /* indexSpace */, AIR_TRUE /* clamp */); probeNum = 0; avgDiff = 0.0; /* take a random walk starting at (0,0,0), making sure that at the visited positions the gage-measured values, gradients, and hessians are the same as the analytical ones */ do { double vanal, ganal[3], hanal[9], vdiff, gdiff[3], hdiff[9], step[3], stepSize = 0.2; probeNum++; vanal = polyeval(coef, 0, 0, 0, pos); ganal[0] = polyeval(coef, 1, 0, 0, pos); ganal[1] = polyeval(coef, 0, 1, 0, pos); ganal[2] = polyeval(coef, 0, 0, 1, pos); hanal[0] = polyeval(coef, 2, 0, 0, pos); hanal[1] = polyeval(coef, 1, 1, 0, pos); hanal[2] = polyeval(coef, 1, 0, 1, pos); hanal[3] = hanal[1]; hanal[4] = polyeval(coef, 0, 2, 0, pos); hanal[5] = polyeval(coef, 0, 1, 1, pos); hanal[6] = hanal[2]; hanal[7] = hanal[5]; hanal[8] = polyeval(coef, 0, 0, 2, pos); vdiff = fabs(vmeas[0] - vanal); ELL_3V_SUB(gdiff, gmeas, ganal); ELL_3M_SUB(hdiff, hmeas, hanal); if (vdiff > epsilon || ELL_3V_LEN(gdiff) > epsilon || ELL_3M_FROB(hdiff) > epsilon) { fprintf(stderr, "at (%g,%g,%g) one diff %.17g %.17g %.17g " "> eps %.17g\n", pos[0], pos[1], pos[2], vdiff, ELL_3V_LEN(gdiff), ELL_3M_FROB(hdiff), epsilon); airMopError(submop); airMopError(mop); return 1; } avgDiff += vdiff + ELL_3V_LEN(gdiff) + ELL_3M_FROB(hdiff); airNormalRand_r(step + 0, step + 1, rng); airNormalRand_r(step + 2, NULL, rng); ELL_3V_SCALE_INCR(pos, stepSize, step); gageProbeSpace(gctx, pos[0], pos[1], pos[2], AIR_FALSE /* indexSpace */, AIR_TRUE /* clamp */); } while (!gctx->edgeFrac); avgDiff /= probeNum; fprintf(stderr, "%u (%.17g)\n", probeNum, avgDiff); airMopOkay(submop); } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/Testing/gage/TODO.txt0000664000175000017500000000021412015632400017421 0ustar domibeldomibeladd scale-space scalar test add (non-SS) vector test add scale-space vector test add (non-SS) tensor test add scale-space tensor test teem-1.11.0~svn6057/Testing/testDataPath.h.in0000664000175000017500000000441712165631065020401 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012 University of Chicago Copyright (C) 2011 James Bigler Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define TESTING_DATA_PATH "${TESTING_DATA_PATH}" #include #include #include /* ** testDataPathPrefix allocates and returns a string which is ** the given "base" prefixed with the path to the test datasets, ** including the "/" between the path and "base". Caller is ** responsible for freeing. ** ** Thanks to James Bigler for writing the first version of this. */ static char* testDataPathPrefix(const char* base) { size_t pathLen, baseLen; char* result; /* You could add an environment variable override here */ /* if (getenv(TESTING_DATA_PATH)) */ /* concatenate the strings together */ pathLen = strlen(TESTING_DATA_PATH); baseLen = strlen(base); /* Allocate enough for the two parts of the string, plus one for the / * and one for the null terminator */ result = AIR_CALLOC(pathLen + baseLen + 2, char); if (result) { /* strcat(result, TESTING_DATA_PATH); strcat(result, "/"); strcat(result, base); */ airStrcpy(result, pathLen + 1, TESTING_DATA_PATH); result[pathLen] = '/'; airStrcpy(result+pathLen+1, baseLen + 1, base); } return result; } teem-1.11.0~svn6057/Testing/meet/0000775000175000017500000000000012203513761016154 5ustar domibeldomibelteem-1.11.0~svn6057/Testing/meet/probeSS.c0000664000175000017500000012615212176664253017720 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/meet.h" /* ** Tests: ** ... lots of gage stuff ... ** ** The main point of all this is to make sure of two (or maybe 2.5) separate ** things about gage, the testing of which requires so much in common that one ** program might as well do them all. The tasks are: ** ** 1) ensure that values and their derivatives (where the gageKind supports ** it) are correctly handled in the multi-value gageKinds (gageKindVec, ** tenGageKind, tenDwiGageKind), relative to the gageKindScl ground-truth: the ** per-component values and derivatives have to match those reconstructed from ** sets of scalar volumes of the components. ** ==> Also, that gageContextCopy works even on dynamic kinds (like DWIs) ** ** 1.5) that there is expected consistency between the scalar, vector, tensor, ** and DWI properties of a related set of volumes. So the test starts with a ** diffusion tensor field and generates DWIs, scalars (norm squared of the ** tensor), and vectors (gradient of norm squared) from this. ** ** 2) that scale-space reconstruction works: that sets of pre-blurred volumes ** can be generated and saved via the utilities in meet, that the smart ** hermite-spline based scale-space interpolation is working (for all kinds), ** and that gageContextCopy works on scale-space contexts */ #define BKEY "probeSS" #define KIND_NUM 4 #define KI_SCL 0 #define KI_VEC 1 #define KI_TEN 2 #define KI_DWI 3 /* static const char *kindStr[KIND_NUM] = { "scalar", "vector", "tensor", " DWI " }; */ #define HALTON_BASE 100 static unsigned int volSize[3] = {45, 46, 47}; #define NRRD_NEW(nn, mm) \ (nn) = nrrdNew(); \ airMopAdd((mm), (nn), (airMopper)nrrdNuke, airMopAlways) /* the weird function names of the various local functions here (created in a rather adhoc and organic way) should be read in usual Teem order: from right to left */ static int engageGenTensor(gageContext *gctx, Nrrd *ncten, /* out/in */ double noiseStdv, unsigned int seed, unsigned int sx, unsigned int sy, unsigned int sz) { static const char me[]="engageGenTensor"; hestParm *hparm; airArray *smop; char tmpStr[4][AIR_STRLEN_SMALL]; Nrrd *nclean; NrrdIter *narg0, *narg1; const char *helixArgv[] = /* 0 1 2 3 4 5 6 7 8 9 */ {"-s", NULL, NULL, NULL, "-v", "0", "-r", "40", "-o", NULL, "-ev", "0.00086", "0.00043", "0.00021", "-bg", "0.003", "-b", "3", NULL}; int helixArgc; gagePerVolume *pvl; smop = airMopNew(); /* NOTE: this is currently the only place where a unrrduCmd is called from within C code; it was educational to get working. Learned: * hest does NOT tolerate having empty or NULL elements of its argv[]! More error checking for this in hest is needed. * the "const char **argv" type is not very convenient to set up in a dynamic way; the per-element setting done below is certainly awkward */ hparm = hestParmNew(); airMopAdd(smop, hparm, (airMopper)hestParmFree, airMopAlways); sprintf(tmpStr[0], "%u", sx); helixArgv[1] = tmpStr[0]; sprintf(tmpStr[1], "%u", sy); helixArgv[2] = tmpStr[1]; sprintf(tmpStr[2], "%u", sz); helixArgv[3] = tmpStr[2]; sprintf(tmpStr[3], "tmp-ten.nrrd"); helixArgv[9] = tmpStr[3]; helixArgc = AIR_CAST(int, sizeof(helixArgv)/sizeof(char *)) - 1; if (tend_helixCmd.main(helixArgc, helixArgv, me, hparm)) { /* error already went to stderr, not to any biff container */ biffAddf(BKEY, "%s: problem running tend %s", me, tend_helixCmd.name); airMopError(smop); return 1; } NRRD_NEW(nclean, smop); if (nrrdLoad(nclean, tmpStr[3], NULL)) { biffAddf(BKEY, "%s: trouble loading from new vol %s", me, tmpStr[3]); airMopError(smop); return 1; } /* add some noise to tensor value; no, this isn't really physical; since we're adding noise to the tensor and then simulating DWIs, rather than adding noise to DWIs and then estimating tensor, but for the purposes of gage testing its fine */ narg0 = nrrdIterNew(); narg1 = nrrdIterNew(); airMopAdd(smop, narg0, (airMopper)nrrdIterNix, airMopAlways); airMopAdd(smop, narg1, (airMopper)nrrdIterNix, airMopAlways); nrrdIterSetNrrd(narg0, nclean); nrrdIterSetValue(narg1, noiseStdv); airSrandMT(seed); if (nrrdArithIterBinaryOp(ncten, nrrdBinaryOpNormalRandScaleAdd, narg0, narg1)) { biffMovef(BKEY, NRRD, "%s: trouble noisying output", me); airMopError(smop); return 1; } /* wrap in gage context */ if ( !(pvl = gagePerVolumeNew(gctx, ncten, tenGageKind)) || gagePerVolumeAttach(gctx, pvl) ) { biffMovef(BKEY, GAGE, "%s: trouble engaging tensor", me); return 1; } airMopOkay(smop); return 0; } /* ** takes in ncten, measures "S" to nscl, copies that to nsclCopy, ** wraps those in gctx and gctxComp */ static int engageGenScalar(gageContext *gctx, Nrrd *nscl, gageContext *gctxComp, Nrrd *nsclCopy, /* out/in */ const Nrrd *ncten) { static const char me[]="engageGenScalar"; gagePerVolume *pvl; if (tenAnisoVolume(nscl, ncten, tenAniso_S, 0)) { biffMovef(BKEY, TEN, "%s: trouble creating scalar volume", me); return 1; } if (nrrdCopy(nsclCopy, nscl)) { biffMovef(BKEY, NRRD, "%s: trouble copying scalar volume", me); return 1; } /* wrap both in gage context */ if ( !(pvl = gagePerVolumeNew(gctx, nscl, gageKindScl)) || gagePerVolumeAttach(gctx, pvl) || !(pvl = gagePerVolumeNew(gctxComp, nsclCopy, gageKindScl)) || gagePerVolumeAttach(gctxComp, pvl)) { biffMovef(BKEY, GAGE, "%s: trouble engaging scalars", me); return 1; } return 0; } /* ** Makes a vector volume by measuring the gradient ** Being the gradient of the given scalar volume is just to make ** something vaguely interesting, and to test consistency with ** the the same gradient as measured in the tensor field. */ static int engageGenVector(gageContext *gctx, Nrrd *nvec, /* out/in */ const Nrrd *nscl) { static const char me[]="engageGenVector"; ptrdiff_t padMin[4] = {0, 0, 0, 0}, padMax[4]; Nrrd *ntmp; airArray *smop; float *vec, *scl; size_t sx, sy, sz, xi, yi, zi, Px, Mx, Py, My, Pz, Mz; double dnmX, dnmY, dnmZ; gagePerVolume *pvl; smop = airMopNew(); if (nrrdTypeFloat != nscl->type) { biffAddf(BKEY, "%s: expected %s not %s type", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nscl->type)); airMopError(smop); return 1; } NRRD_NEW(ntmp, smop); sx = nscl->axis[0].size; sy = nscl->axis[1].size; sz = nscl->axis[2].size; ELL_4V_SET(padMax, 2, AIR_CAST(ptrdiff_t, sx-1), AIR_CAST(ptrdiff_t, sy-1), AIR_CAST(ptrdiff_t, sz-1)); /* we do axinsert and pad in order to keep all the per-axis info */ if (nrrdAxesInsert(ntmp, nscl, 0) || nrrdPad_nva(nvec, ntmp, padMin, padMax, nrrdBoundaryPad, 0.0)) { biffMovef(BKEY, NRRD, "%s: trouble", me); airMopError(smop); return 1; } dnmX = 0.5/nrrdSpaceVecNorm(nscl->spaceDim, nscl->axis[0].spaceDirection); dnmY = 0.5/nrrdSpaceVecNorm(nscl->spaceDim, nscl->axis[1].spaceDirection); dnmZ = 0.5/nrrdSpaceVecNorm(nscl->spaceDim, nscl->axis[2].spaceDirection); vec = AIR_CAST(float *, nvec->data); scl = AIR_CAST(float *, nscl->data); #define INDEX(xj, yj, zj) (xj + sx*(yj + sy*zj)) for (zi=0; ziverbose = 0; gparm->minMean = 0.002; gparm->seed = 4242; gparm->insertZeroVec = AIR_TRUE; if (tenGradientGenerate(ngrad, gradNum, gparm) || tenExperSpecGradSingleBValSet(espec, AIR_FALSE /* insertB0 */, bval, AIR_CAST(double *, ngrad->data), AIR_CAST(unsigned int, ngrad->axis[1].size))) { biffMovef(BKEY, TEN, "%s: trouble generating grads or espec", me); airMopError(smop); return 1; } NRRD_NEW(nten, smop); NRRD_NEW(nb0, smop); ELL_4V_SET(cropMax, ncten->axis[0].size-1, ncten->axis[1].size-1, ncten->axis[2].size-1, ncten->axis[3].size-1); if (nrrdSlice(nb0, ncten, 0, 0) || nrrdCrop(nten, ncten, cropMin, cropMax)) { biffMovef(BKEY, NRRD, "%s: trouble slicing or cropping ten vol", me); airMopError(smop); return 1; } narg0 = nrrdIterNew(); narg1 = nrrdIterNew(); airMopAdd(smop, narg0, (airMopper)nrrdIterNix, airMopAlways); airMopAdd(smop, narg1, (airMopper)nrrdIterNix, airMopAlways); nrrdIterSetValue(narg1, 50000.0); nrrdIterSetNrrd(narg0, nb0); if (nrrdArithIterBinaryOp(nb0, nrrdBinaryOpMultiply, narg0, narg1)) { biffMovef(BKEY, NRRD, "%s: trouble generating b0 vol", me); airMopError(smop); return 1; } if (tenModelSimulate(ndwi, nrrdTypeUShort, espec, tenModel1Tensor2, nb0, nten, AIR_TRUE /* keyValueSet */)) { biffMovef(BKEY, TEN, "%s: trouble simulating DWI vol", me); airMopError(smop); return 1; } airMopOkay(smop); return 0; } int engageMopDiceVector(gageContext *gctx, Nrrd *nvecComp[3], /* out/in */ airArray *mop, const Nrrd* nvec) { static const char me[]="engageMopDiceVector"; gagePerVolume *pvl; unsigned int ci; char stmp[AIR_STRLEN_SMALL]; if (!( 4 == nvec->dim && 3 == nvec->axis[0].size )) { biffAddf(BKEY, "%s: expected 4-D 3-by-X nrrd (not %u-D %s-by-X)", me, nvec->dim, airSprintSize_t(stmp, nvec->axis[0].size)); return 1; } for (ci=0; ci<3; ci++) { NRRD_NEW(nvecComp[ci], mop); if (nrrdSlice(nvecComp[ci], nvec, 0, ci)) { biffMovef(BKEY, NRRD, "%s: trouble getting component %u", me, ci); return 1; } if ( !(pvl = gagePerVolumeNew(gctx, nvecComp[ci], gageKindScl)) || gagePerVolumeAttach(gctx, pvl) ) { biffMovef(BKEY, GAGE, "%s: trouble engaging component %u", me, ci); return 1; } } return 0; } int engageMopDiceTensor(gageContext *gctx, Nrrd *nctenComp[7], /* out/in */ airArray *mop, const Nrrd* ncten) { static const char me[]="engageMopDiceTensor"; gagePerVolume *pvl; unsigned int ci; if (tenTensorCheck(ncten, nrrdTypeFloat, AIR_TRUE /* want4F */, AIR_TRUE /* useBiff */)) { biffMovef(BKEY, TEN, "%s: didn't get tensor volume", me); return 1; } for (ci=0; ci<7; ci++) { NRRD_NEW(nctenComp[ci], mop); if (nrrdSlice(nctenComp[ci], ncten, 0, ci)) { biffMovef(BKEY, NRRD, "%s: trouble getting component %u", me, ci); return 1; } if ( !(pvl = gagePerVolumeNew(gctx, nctenComp[ci], gageKindScl)) || gagePerVolumeAttach(gctx, pvl) ) { biffMovef(BKEY, GAGE, "%s: trouble engaging component %u", me, ci); return 1; } } return 0; } int engageMopDiceDwi(gageContext *gctx, Nrrd ***ndwiCompP, /* out/in */ airArray *mop, const Nrrd* ndwi) { static const char me[]="mopDiceDwi"; Nrrd **ndwiComp; size_t dwiNum; char stmp[AIR_STRLEN_SMALL]; gagePerVolume *pvl; unsigned int ci; if (!( 4 == ndwi->dim )) { biffAddf(BKEY, "%s: wanted 4D volume (not %u)", me, ndwi->dim); return 1; } if (!( nrrdKindList == ndwi->axis[0].kind && nrrdKindSpace == ndwi->axis[1].kind && nrrdKindSpace == ndwi->axis[2].kind && nrrdKindSpace == ndwi->axis[3].kind )) { biffAddf(BKEY, "%s: wanted kinds %s,3x%s, not %s,%s,%s,%s", me, airEnumStr(nrrdKind, nrrdKindList), airEnumStr(nrrdKind, nrrdKindSpace), airEnumStr(nrrdKind, ndwi->axis[0].kind), airEnumStr(nrrdKind, ndwi->axis[1].kind), airEnumStr(nrrdKind, ndwi->axis[2].kind), airEnumStr(nrrdKind, ndwi->axis[3].kind)); return 1; } dwiNum = ndwi->axis[0].size; if (!(ndwiComp = AIR_CALLOC(dwiNum, Nrrd *))) { biffAddf(BKEY, "%s: couldn't alloc %s Nrrd*", me, airSprintSize_t(stmp, dwiNum)); return 1; } airMopAdd(mop, ndwiComp, airFree, airMopAlways); *ndwiCompP = ndwiComp; for (ci=0; ciaptr = airFree(AIR_VOIDP(man->aptr)); man->ispec = airFree(man->ispec); man->alen = airFree(man->alen); man->sidx = airFree(man->sidx); man->anum = 0; man->san = airFree(man->san); man->slen = 0; } static multiAnswer* multiAnswerNew(char *name) { multiAnswer *man; man = AIR_CALLOC(1, multiAnswer); airStrcpy(man->name, AIR_STRLEN_SMALL, name); multiAnswerInit(man); return man; } void multiAnswerLenSet(multiAnswer *man, unsigned int anum) { /* static const char me[]="multiAnswerLenSet"; fprintf(stderr, "!%s: %s hello -> answer number = %u\n", me, man->name, anum); */ man->aptr = AIR_CALLOC(anum, const double *); man->ispec = AIR_CALLOC(anum, gageItemSpec); man->alen = AIR_CALLOC(anum, unsigned int); man->sidx = AIR_CALLOC(anum, unsigned int); man->anum = anum; /* don't know answer lengths yet */ man->san = airFree(man->san); man->slen = 0; return; } static multiAnswer* multiAnswerNix(multiAnswer *man) { airFree(AIR_VOIDP(man->aptr)); airFree(man->ispec); airFree(man->alen); airFree(man->sidx); airFree(man->san); airFree(man); return NULL; } static void multiAnswerAdd(multiAnswer *man, unsigned int ansIdx, const gageContext *gctx, const gagePerVolume *pvl, unsigned int item) { /* static const char me[]="multiAnswerAdd"; fprintf(stderr, "!%s: %s hello (aidx = %u)\n", me, man->name, ansIdx); */ man->aptr[ansIdx] = gageAnswerPointer(gctx, pvl, item); man->ispec[ansIdx].kind = pvl->kind; man->ispec[ansIdx].item = item; man->alen[ansIdx] = gageAnswerLength(gctx, pvl, item); /* HEY hack: doing this tally and allocation only on the last time we're called, presuming calls with increasing ansIdx */ man->slen = 0; if (ansIdx == man->anum-1) { unsigned int ai; /* fprintf(stderr, "!%s: %s hello aidx reached anum-1 = %u\n", me, man->name, man->anum-1); */ for (ai=0; aianum; ai++) { man->sidx[ai] = man->slen; man->slen += man->alen[ai]; } man->san = AIR_CALLOC(man->slen, double); /* fprintf(stderr, "!%s: (%s) slen = %u\n", "multiAnswerAdd", pvl->kind->name, man->slen); */ } return; } static void multiAnswerCollect(multiAnswer *man) { unsigned int ai; /* static const char me[]="multiAnswerCollect"; fprintf(stderr, "%s: %s hi\n", me, man->name); */ for (ai=0; aianum; ai++) { /* fprintf(stderr, "!%s: (%s/%s) ai=%u/%u to %p+%u=%p for %u doubles\n", "multiAnswerCollect", man->ispec[ai].kind->name, airEnumStr(man->ispec[ai].kind->enm, man->ispec[ai].item), ai, man->anum, man->san, man->sidx[ai], man->san + man->sidx[ai], man->alen[ai]); */ memcpy(man->san + man->sidx[ai], man->aptr[ai], man->alen[ai]*sizeof(double)); } return; } static int multiAnswerCompare(multiAnswer *man1, multiAnswer *man2) { static const char me[]="multiAnswerCompare"; unsigned int si, slen; #if 1 if (man1->slen != man2->slen) { biffAddf(BKEY, "%s: man1->slen %u != man2->slen %u\n", me, man1->slen, man2->slen); return 1; } slen = man1->slen; #else slen = AIR_MIN(man1->slen, man2->slen); #endif for (si=0; sisan[si] != man2->san[si]) { /* HEY should track down which part of which answer, in man1 and man2, is different, which was the purpose of recording ispec and sidx */ biffAddf(BKEY, "%s: man1->san[si] %.17g != man2->san[si] %.17g", me, man1->san[si], man2->san[si]); return 1; } } return 0; } /* ** setting up gageContexts for the first of the two tasks listed above: making ** sure per-component information is handled correctly. NOTE: The combination ** of function calls made here is very atypical for a Teem-using program */ static int updateQueryKernelSetTask1(gageContext *gctxComp[KIND_NUM], gageContext *gctx[KIND_NUM], int gSetup, multiAnswer *manComp[KIND_NUM], multiAnswer *man[KIND_NUM], NrrdKernel *kpack[3], double support) { static const char me[]="updateQueryKernelSetTask1"; double parm1[NRRD_KERNEL_PARMS_NUM], parmV[NRRD_KERNEL_PARMS_NUM]; unsigned int kindIdx; if (4 != KIND_NUM) { biffAddf(BKEY, "%s: sorry, confused: KIND_NUM %u != 4", me, KIND_NUM); return 1; } parm1[0] = 1.0; parmV[0] = support; if (gSetup) { for (kindIdx=0; kindIdxpvlNum); fprintf(stderr, "!%s: gctxComp[%u] has %u pvls\n", me, kindIdx, gctxComp[kindIdx]->pvlNum); */ for (pvi=0; pvipvlNum; pvi++) { if (0 /* (no-op for formatting) */ || gageQueryItemOn(gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi], gageSclValue) || gageQueryItemOn(gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi], gageSclGradVec) || gageQueryItemOn(gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi], gageSclHessian)) { biffMovef(BKEY, GAGE, "%s: trouble setting query (kind %u) on pvi %u", me, kindIdx, pvi); return 1; } } if (gageUpdate(gctxComp[kindIdx])) { biffMovef(BKEY, GAGE, "%s: trouble updating comp gctx %u", me, kindIdx); return 1; } } /* For the original contexts, we have to use the kind-specific items that correspond to the values and derivatives */ if (0 /* (no-op for formatting) */ || gageQueryItemOn(gctx[KI_SCL], gctx[KI_SCL]->pvl[0], gageSclValue) || gageQueryItemOn(gctx[KI_SCL], gctx[KI_SCL]->pvl[0], gageSclGradVec) || gageQueryItemOn(gctx[KI_SCL], gctx[KI_SCL]->pvl[0], gageSclHessian) || gageQueryItemOn(gctx[KI_VEC], gctx[KI_VEC]->pvl[0], gageVecVector) || gageQueryItemOn(gctx[KI_VEC], gctx[KI_VEC]->pvl[0], gageVecJacobian) || gageQueryItemOn(gctx[KI_VEC], gctx[KI_VEC]->pvl[0], gageVecHessian) || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageTensor) || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageTensorGrad) || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageS) || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageSGradVec) || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageHessian) || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageSHessian) || gageQueryItemOn(gctx[KI_DWI], gctx[KI_DWI]->pvl[0], tenDwiGageAll) || gageQueryItemOn(gctx[KI_DWI], gctx[KI_DWI]->pvl[0], tenDwiGageTensorLLS)) { biffMovef(BKEY, GAGE, "%s: trouble setting item", me); return 1; } for (kindIdx=0; kindIdxpvlNum); for (pvi=0; pvipvlNum; pvi++) { multiAnswerAdd(manComp[kindIdx], pvi, gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi], gageSclValue); } if (KI_DWI != kindIdx) { for (pvi=0; pvipvlNum; pvi++) { multiAnswerAdd(manComp[kindIdx], pvi + gctxComp[kindIdx]->pvlNum, gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi], gageSclGradVec); } for (pvi=0; pvipvlNum; pvi++) { multiAnswerAdd(manComp[kindIdx], pvi + 2*gctxComp[kindIdx]->pvlNum, gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi], gageSclHessian); } } multiAnswerLenSet(man[kindIdx], KI_DWI != kindIdx ? 3 : 1); switch(kindIdx) { case KI_SCL: multiAnswerAdd(man[kindIdx], 0, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageSclValue); multiAnswerAdd(man[kindIdx], 1, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageSclGradVec); multiAnswerAdd(man[kindIdx], 2, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageSclHessian); break; case KI_VEC: multiAnswerAdd(man[kindIdx], 0, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageVecVector); multiAnswerAdd(man[kindIdx], 1, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageVecJacobian); multiAnswerAdd(man[kindIdx], 2, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageVecHessian); break; case KI_TEN: multiAnswerAdd(man[kindIdx], 0, gctx[kindIdx], gctx[kindIdx]->pvl[0], tenGageTensor); multiAnswerAdd(man[kindIdx], 1, gctx[kindIdx], gctx[kindIdx]->pvl[0], tenGageTensorGrad); multiAnswerAdd(man[kindIdx], 2, gctx[kindIdx], gctx[kindIdx]->pvl[0], tenGageHessian); break; case KI_DWI: multiAnswerAdd(man[kindIdx], 0, gctx[kindIdx], gctx[kindIdx]->pvl[0], tenDwiGageAll); break; } } return 0; } static int probeTask1(gageContext *gctxComp[KIND_NUM], gageContext *gctx[KIND_NUM], multiAnswer *manComp[KIND_NUM], multiAnswer *man[KIND_NUM], unsigned int probeNum, NrrdKernel *kpack[3], double ksupport) { static const char me[]="probeTask1"; unsigned int kindIdx, probeIdx, errNumMax, tenErrNum, sclErrNum; double pos[3], upos[3], minp[3], maxp[3], tenDiff[7], tenAvg[7], tenErr, tenErrMax, vecDiff[3], vecAvg[3], vecErr, vecErrMax, vecErrNum, sclDiff, sclAvg, sclErr, sclErrMax, errNumFrac; const double *dwiTenEstP, *tenTenP, *tenTenNormP, *tenTenNormGradP, *sclSclP, *sclGradP, *vecVecP; ELL_3V_SET(minp, ksupport, ksupport, ksupport); ELL_3V_SET(maxp, volSize[0]-1-ksupport, volSize[1]-1-ksupport, volSize[2]-1-ksupport); /* this is all for task 1.5 */ sclSclP = gageAnswerPointer(gctx[KI_SCL], gctx[KI_SCL]->pvl[0], gageSclValue); sclGradP = gageAnswerPointer(gctx[KI_SCL], gctx[KI_SCL]->pvl[0], gageSclGradVec); vecVecP = gageAnswerPointer(gctx[KI_VEC], gctx[KI_VEC]->pvl[0], gageVecVector); tenTenP = gageAnswerPointer(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageTensor); tenTenNormP = gageAnswerPointer(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageS); tenTenNormGradP = gageAnswerPointer(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageSGradVec); dwiTenEstP = gageAnswerPointer(gctx[KI_DWI], gctx[KI_DWI]->pvl[0], tenDwiGageTensorLLS); tenErrNum = sclErrNum = 0; vecErrNum = 0.0; errNumFrac = 0.02; if (nrrdKernelBoxSupportDebug == kpack[0]) { /* not actually here for any derivatives, mainly to check on tensor esimation (in addition to usual check of multivariate as set of scalars) */ tenErrMax = 0.0002; vecErrMax = AIR_NAN; sclErrMax = AIR_NAN; } else if (nrrdKernelCos4SupportDebug == kpack[0]) { tenErrMax = AIR_NAN; vecErrMax = AIR_NAN; sclErrMax = AIR_NAN; } else if (nrrdKernelCatmullRomSupportDebug == kpack[0]) { tenErrMax = 0.14; /* honestly, not sure how meaningful this test is, given how significant we're allowing the error to be. might be more meaningful if this was with comparison to a pre-computed non-linear least-squares fit, instead of the (log-based) linear least-squares. */ vecErrMax = 0.0016; sclErrMax = 0.0008; } else { biffAddf(BKEY, "%s: unexpected kpack[0] %s\n", me, kpack[0]->name); return 1; } errNumMax = AIR_CAST(unsigned int, errNumFrac*probeNum); for (probeIdx=0; probeIdxerrNum, (gctx)->errStr); \ return 1; \ } PROBE(gctxComp[kindIdx], "comp", pos); PROBE(gctx[kindIdx], "single", pos); #undef PROBE multiAnswerCollect(man[kindIdx]); multiAnswerCollect(manComp[kindIdx]); if (multiAnswerCompare(manComp[kindIdx], man[kindIdx])) { biffAddf(BKEY, "%s: on point %u of kindIdx %u", me, probeIdx, kindIdx); return 1; } } /* see if probed tensors mostly agree with tensors estimated from probed DWIs */ if (AIR_EXISTS(tenErrMax)) { TEN_T_SUB(tenDiff, dwiTenEstP, tenTenP); TEN_T_LERP(tenAvg, 0.5, dwiTenEstP, tenTenP); tenErr = TEN_T_NORM(tenDiff)/TEN_T_NORM(tenAvg); if (tenErr > tenErrMax) { tenErrNum++; if (tenErrNum > errNumMax) { biffAddf(BKEY, "%s: (probe %u) tenErr %g > %g too many times %u > %u", me, probeIdx, tenErr, tenErrMax, tenErrNum, errNumMax); return 1; } } } /* see if invariant gradient learned from tensor volume agrees with volume of pre-computed invariant gradients, and with gradient of pre-computed invariants */ if (AIR_EXISTS(vecErrMax)) { ELL_3V_SUB(vecDiff, sclGradP, vecVecP); ELL_3V_LERP(vecAvg, 0.5, sclGradP, vecVecP); vecErr = ELL_3V_LEN(vecDiff)/sqrt(ELL_3V_LEN(vecAvg)); if (vecErr > vecErrMax) { vecErrNum += 0.5; if (vecErrNum > errNumMax) { biffAddf(BKEY, "%s: (probe %u) (A) vecErr %g > %g too many times %g > %u", me, probeIdx, vecErr, vecErrMax, vecErrNum, errNumMax); return 1; } } ELL_3V_SUB(vecDiff, tenTenNormGradP, vecVecP); ELL_3V_LERP(vecAvg, 0.5, tenTenNormGradP, vecVecP); vecErr = ELL_3V_LEN(vecDiff)/sqrt(ELL_3V_LEN(vecAvg)); if (vecErr > vecErrMax) { vecErrNum += 0.5; if (vecErrNum > errNumMax) { biffAddf(BKEY, "%s: (probe %u) (B) vecErr %g > %g too many times %g > %u", me, probeIdx, vecErr, vecErrMax, vecErrNum, errNumMax); return 1; } } } /* see if invariant learned from tensor volume agrees with volume of precomputed invariants */ if (AIR_EXISTS(sclErrMax)) { sclDiff = sclSclP[0] - tenTenNormP[0]; sclAvg = (sclSclP[0] + tenTenNormP[0])/2; sclErr = AIR_ABS(sclDiff)/sqrt(AIR_ABS(sclAvg)); if (sclErr > sclErrMax) { sclErrNum++; if (sclErrNum > errNumMax) { biffAddf(BKEY, "%s: (probe %u) (B) sclErr %g > %g too many times %u > %u", me, probeIdx, sclErr, sclErrMax, sclErrNum, errNumMax); return 1; } } } } return 0; } static const char *testInfo = "for testing gage"; int main(int argc, const char **argv) { const char *me; char *err = NULL; hestOpt *hopt=NULL; hestParm *hparm; airArray *mop; const gageKind *kind[KIND_NUM] = { /* 0 1 2 3 */ gageKindScl, gageKindVec, tenGageKind, NULL /* dwi */}; char name[KIND_NUM][AIR_STRLEN_SMALL] = { "scl", "vec", "ten", "dwi" }; char nameComp[KIND_NUM][AIR_STRLEN_SMALL] = { "sclComp", "vecComp", "tenComp", "dwiComp" }; char *kernS; gageKind *dwikind = NULL; gageContext *gctxComp[KIND_NUM], *gctxCompCopy[KIND_NUM], *gctx[KIND_NUM], *gctxCopy[KIND_NUM]; multiAnswer *manComp[KIND_NUM], *man[KIND_NUM]; Nrrd *nin[KIND_NUM], /* these are the volumes that are used in gctxComp[] */ *nsclCopy, *nvecComp[3], *nctenComp[7], **ndwiComp, *ngrad; /* need access to list of gradients used to make DWIs; (this is not the gradient of a scalar field) */ double bval = 1000, noiseStdv=0.0001, ksupport; unsigned int kindIdx, probeNum, gradNum = 10; /* small number so that missing one will produce a big reconstruction error */ NrrdKernel *kpack[3]; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); /* learn things from hest */ hestOptAdd(&hopt, "supp", "r", airTypeDouble, 1, 1, &ksupport, "1.0", "kernel support"); hestOptAdd(&hopt, "pnum", "N", airTypeUInt, 1, 1, &probeNum, "100", "# of probes"); hestOptAdd(&hopt, "k", "kern", airTypeString, 1, 1, &kernS, NULL, "kernel to use; can be: box, cos, or ctmr"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, testInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!strcmp(kernS, "box")) { ELL_3V_SET(kpack, nrrdKernelBoxSupportDebug, nrrdKernelZero, nrrdKernelZero); } else if (!strcmp(kernS, "cos")) { ELL_3V_SET(kpack, nrrdKernelCos4SupportDebug, nrrdKernelCos4SupportDebugD, nrrdKernelCos4SupportDebugDD); } else if (!strcmp(kernS, "ctmr")) { ELL_3V_SET(kpack, nrrdKernelCatmullRomSupportDebug, nrrdKernelCatmullRomSupportDebugD, nrrdKernelCatmullRomSupportDebugDD); } else { fprintf(stderr, "%s: kernel \"%s\" not recognized", me, kernS); airMopError(mop); return 1; } fprintf(stderr, "kpack = %s, %s, %s\n", kpack[0]->name, kpack[1]->name, kpack[2]->name); /* This was a tricky bug: Adding gageContextNix(gctx) to the mop as soon as a gctx is created makes sense. But, in the first version of this code, gageContextNix was added to the mop BEFORE tenDwiGageKindNix was added, which meant that gageContextNix was being called AFTER tenDwiGageKindNix. However, that meant that existing pvls had their pvl->kind being freed from under them, but gagePerVolumeNix certainly needs to look at and possibly call pvl->kind->pvlDataNix in order clean up pvl->data. *Therefore*, we have to add tenDwiGageKindNix to the mop prior to gageContextNix, even though nothing about the (dyanamic) kind has been set yet. If we had something like smart pointers, then tenDwiGageKindNix would not have freed something that an extant pvl was using */ dwikind = tenDwiGageKindNew(); airMopAdd(mop, dwikind, (airMopper)tenDwiGageKindNix, airMopAlways); #define GAGE_CTX_NEW(gg, mm) \ (gg) = gageContextNew(); \ airMopAdd((mm), (gg), (airMopper)gageContextNix, airMopAlways); \ gageParmSet((gg), gageParmRenormalize, AIR_FALSE); \ gageParmSet((gg), gageParmCheckIntegrals, AIR_TRUE) for (kindIdx=0; kindIdxdynamicAlloc)) { if (kind[kindIdx] != meetGageKindParse(kind[kindIdx]->name)) { fprintf(stderr, "%s: static kind[%u]->name %s wasn't parsed\n", me, kindIdx, kind[kindIdx]->name); airMopError(mop); return 1; } } else { gageKind *ktmp; ktmp = meetGageKindParse(kind[kindIdx]->name); if (!ktmp) { fprintf(stderr, "%s: dynamic kind[%u]->name %s wasn't parsed\n", me, kindIdx, kind[kindIdx]->name); airMopError(mop); return 1; } if (airStrcmp(ktmp->name, kind[kindIdx]->name)) { fprintf(stderr, "%s: parsed dynamic kind[%u]->name %s didn't match %s\n", me, kindIdx, ktmp->name, kind[kindIdx]->name); airMopError(mop); return 1; } if (!airStrcmp(TEN_DWI_GAGE_KIND_NAME, ktmp->name)) { /* HEY: there needs to be a generic way of freeing a dynamic kind, perhaps a new nixer field in gageKind */ ktmp = tenDwiGageKindNix(ktmp); } } } /* for (kindIdx=0; kindIdx (%u) %u\n", me, kind[kindIdx]->name, kind[kindIdx]->dynamicAlloc ? "dynamic" : "static", kind[kindIdx]->baseDim, kind[kindIdx]->valLen); } */ /* nrrdSave("tmp-cten.nrrd", nin[KI_TEN], NULL); nrrdSave("tmp-scl.nrrd", nin[KI_SCL], NULL); nrrdSave("tmp-vec.nrrd", nin[KI_VEC], NULL); nrrdSave("tmp-dwi.nrrd", nin[KI_DWI], NULL); */ /* ========================== TASK 1 */ if (updateQueryKernelSetTask1(gctxComp, gctx, AIR_TRUE, manComp, man, kpack, ksupport) || probeTask1(gctxComp, gctx, manComp, man, probeNum, kpack, ksupport)) { airMopAdd(mop, err = biffGetDone(BKEY), airFree, airMopAlways); fprintf(stderr, "%s: trouble (orig, orig):\n%s", me, err); airMopError(mop); return 1; } /* testing gageContextCopy on multi-variate volumes */ for (kindIdx=0; kindIdxname); ret = tendCmdList[tci]->main(0, NULL, tendCmdList[tci]->name, hparm); fprintf(out, "%s: ################### END tend %s (ret=%d)\n", me, tendCmdList[tci]->name, ret); tci++; } while (tendCmdList[tci]); airMopOkay(mop); return 0; } teem-1.11.0~svn6057/Testing/ten/glyphBqd.c0000664000175000017500000000651412165631065017741 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/ten.h" #include /* ** Tests: */ int main(int argc, const char **argv) { airArray *mop; Nrrd *nabc, *nref; double *abc; unsigned int sz, ui, vi, bi, betaMaxNum; double uv[2], betaMax[2] = {2.5555555555, 3.888888888888}; char *err, *refname, explain[AIR_STRLEN_LARGE]; int differ, ret; mop = airMopNew(); nabc = nrrdNew(); airMopAdd(mop, nabc, (airMopper)nrrdNuke, airMopAlways); sz = 100; betaMaxNum = AIR_CAST(unsigned int, sizeof(betaMax)/sizeof(double)); if (nrrdMaybeAlloc_va(nabc, nrrdTypeDouble, 3, AIR_CAST(size_t, 3), AIR_CAST(size_t, sz), AIR_CAST(size_t, sz*betaMaxNum))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "trouble allocating:\n%s", err); airMopError(mop); return 1; } abc = AIR_CAST(double *, nabc->data); for (bi=0; bi /* ** Tests: ** nrrdLoad */ int main(int argc, const char **argv) { const char *me; Nrrd *nin; airArray *mop; char *fullname; int differ; AIR_UNUSED(argc); me = argv[0]; mop = airMopNew(); nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); fullname = testDataPathPrefix("fmob-c4h.nrrd"); airMopAdd(mop, fullname, airFree, airMopAlways); if (nrrdLoad(nin, fullname, NULL)) { char *err; airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble reading data \"%s\":\n%s", me, fullname, err); airMopError(mop); return 1; } { Nrrd *ncopy; char *blah, *blah1L, explain[AIR_STRLEN_LARGE]; size_t ii, blen; ncopy = nrrdNew(); airMopAdd(mop, ncopy, (airMopper)nrrdNuke, airMopAlways); blen = AIR_STRLEN_HUGE*42; blah = AIR_CALLOC(blen, char); airMopAdd(mop, blah, airFree, airMopAlways); for (ii=0; iispaceUnits[0] = airOneLinify(airStrdup("\nsp\"\nu0\n")); nin->spaceUnits[1] = airStrdup("bob"); nin->spaceUnits[2] = airStrdup(blah1L); /* see NOTE above */ if (nrrdCommentAdd(nin, "the first comment") || nrrdCommentAdd(nin, "very long comment follows") || nrrdCommentAdd(nin, blah) || nrrdKeyValueAdd(nin, "first key", "first value") || nrrdKeyValueAdd(nin, "2nd key", "2nd value") || nrrdKeyValueAdd(nin, "big key", blah)) { char *err; airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble w/ comments or KVPs:\n%s", me, err); airMopError(mop); return 1; } if (nrrdCopy(ncopy, nin) || nrrdCompare(nin, ncopy, AIR_FALSE /* onlyData */, 0.0 /* epsilon */, &differ, explain)) { char *err; airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble w/ copy or compare:\n%s\n", me, err); airMopError(mop); return 1; } if (differ) { fprintf(stderr, "%s: difference in in-memory copy: %s\n", me, explain); airMopError(mop); return 1; } if (nrrdSave("tloadTest.nrrd", nin, NULL) || nrrdLoad(ncopy, "tloadTest.nrrd", NULL) || nrrdCompare(nin, ncopy, AIR_FALSE /* onlyData */, 0.0 /* epsilon */, &differ, explain)) { char *err; airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble w/ save, load, compare:\n%s\n", me, err); airMopError(mop); return 1; } if (differ) { fprintf(stderr, "%s: difference in on-disk copy: %s\n", me, explain); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } teem-1.11.0~svn6057/Testing/nrrd/macros.c0000664000175000017500000002114412165631065017626 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include /* nrrd doesn't depend on ell but the tests don't need to be constrained by the same dependency order; included here for the macros */ /* ** Tests: ** NRRD_COORD_UPDATE, NRRD_COORD_INCR, NRRD_INDEX_GEN, NRRD_COORD_GEN */ int main(int argc, char *argv[]) { size_t II, *coord0, *ctmp0, *size0, coord1[1], ctmp1[1], size1[1], coord2[2], ctmp2[2], size2[2], coord3[3], ctmp3[3], size3[3], coord4[4], ctmp4[4], size4[4]; char sbuff[NRRD_DIM_MAX*AIR_STRLEN_SMALL], scomp[NRRD_DIM_MAX*AIR_STRLEN_SMALL], sigab[NRRD_DIM_MAX*AIR_STRLEN_SMALL], /* index gen and back */ swhat[NRRD_DIM_MAX*AIR_STRLEN_SMALL]; unsigned int ii, jj, kk, ll; /* This macro makes sure the given length-N coord##N vector is what it should be ("want"), via string comparison after the output of airSprintVecSize_t. Also uses NRRD_INDEX_GEN and NRRD_COORD_GEN to go from coord##N to a linear index II and back to ctmp##N, and makes sure that that too matches "want" */ #define CHECK(N, want) \ airSprintVecSize_t(swhat, size##N, N); \ airSprintVecSize_t(sbuff, coord##N, N); \ if (strcmp(sbuff, want)) { \ fprintf(stderr, "for %s: got %s but wanted %s\n", \ swhat, sbuff, want); \ return 1; \ } \ NRRD_INDEX_GEN(II, coord##N, size##N, N); \ NRRD_COORD_GEN(ctmp##N, size##N, N, II); \ airSprintVecSize_t(sigab, ctmp##N, N); \ if (strcmp(sbuff, want)) { \ fprintf(stderr, "for %s: NRRD_{INDEX,COORD}_GEN gave %s but want %s\n", \ swhat, sbuff, want); \ return 1; \ } /* actually airTime will always be positive; this is just to thwart the compiler thinking coord0 and size0 are necessarily NULL */ if (airTime() > -100) { coord0 = NULL; ctmp0 = NULL; size0 = NULL; } else { coord0 = coord1; ctmp0 = ctmp1; size0 = size1; } /* http://teem.svn.sourceforge.net/viewvc/teem/teem/trunk/src/nrrd/nrrdMacros.h?r1=3931&r2=4587 BUG: previous version causes array subscript is below array bounds. */ /* 00000000000000000000000000000000000000000000000 */ /* using coord0 == size0 == NULL here is to make sure that its not even accessed */ NRRD_COORD_UPDATE(coord0, size0, 0); CHECK(0, "[]"); /* 11111111111111111111111111111111111111111111111 */ #define RUN1 \ coord1[0] = 0; \ for (ii=0; iidata); fprintf(stderr, "generating data: . . . "); fflush(stderr); time0 = airTime(); progress = AIR_FALSE; tick = nn/100; for (ii=0; ii 1.0) { /* if it took more than a second to do 1% of the thing, would be good to generate some progress indication */ progress = AIR_TRUE; } if (progress) { fprintf(stderr, "%s", airDoneStr(0, ii, nn, doneStr)); fflush(stderr); } } } if (progress) { fprintf(stderr, "%s\n", airDoneStr(0, ii, nn, doneStr)); fflush(stderr); } else { fprintf(stderr, "\n"); } fprintf(stderr, "finding reference (big-endian) CRC: "); fflush(stderr); refCRC = nrrdCRC32(nref, airEndianBig); fprintf(stderr, "%u\n", refCRC); /* write data, with padding */ fprintf(stderr, "saving data . . . "); fflush(stderr); if (!(fout = fopen(outS[0], "wb" COMMIT))) { fprintf(stderr, "\n%s: couldn't open %s for writing: %s\n", me, outS[0], strerror(errno)); airMopError(mop); return 1; } airMopAdd(mop, fout, (airMopper)airFclose, airMopAlways); for (ii=0; iidata, nrrdElementSize(nref), nn, fout)) { fprintf(stderr, "\n%s: error writing data\n", me); airMopError(mop); return 1; } for (ii=0; iidata); fprintf(stderr, "CORRECT %s bytes at beginning:\n", airSprintSize_t(stmp, rpb)); for (bi=0; bidata); fprintf(stderr, "FOUND %s bytes at beginning:\n", airSprintSize_t(stmp, rpb)); for (bi=0; bi /* ** Tests: ** nrrdSanity */ int main(int argc, char *argv[]) { int ret; char *me, *err; AIR_UNUSED(argc); me = argv[0]; if (!nrrdSanity()) { err = biffGetDone(NRRD); fprintf(stderr, "%s: nrrdSanity failed:\n%s", me, err); free(err); ret = 1; } else { fprintf(stderr, "%s: nrrdSanity passed\n", me); ret = 0; } return ret; } teem-1.11.0~svn6057/Testing/nrrd/CMakeLists.txt0000664000175000017500000000457612165631065020750 0ustar domibeldomibel# # Teem: Tools to process and visualize scientific data and images . # Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # (LGPL) as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The terms of redistributing and/or modifying this software also # include exceptions to the LGPL that facilitate static linking. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, write to Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # ADD_EXECUTABLE(test_trand trand.c) TARGET_LINK_LIBRARIES(test_trand teem) ADD_TEST(trand ${EXECUTABLE_OUTPUT_PATH}/test_trand) ADD_EXECUTABLE(test_tload tload.c) TARGET_LINK_LIBRARIES(test_tload teem) ADD_TEST(tload ${EXECUTABLE_OUTPUT_PATH}/test_tload) ADD_EXECUTABLE(test_tskip tskip.c) TARGET_LINK_LIBRARIES(test_tskip teem) # Note the different file names; tests are run in parallel ADD_TEST(tskip11p ${EXECUTABLE_OUTPUT_PATH}/test_tskip -s 101 102 103 -p 66 81 -o tsA.raw tsA.nhdr) ADD_TEST(tskip11n ${EXECUTABLE_OUTPUT_PATH}/test_tskip -s 101 102 103 -p 66 81 -ns -o tsB.raw tsB.nhdr) ADD_TEST(tskip01p ${EXECUTABLE_OUTPUT_PATH}/test_tskip -s 101 102 103 -p 0 99 -o tsC.raw tsC.nhdr) ADD_TEST(tskip01n ${EXECUTABLE_OUTPUT_PATH}/test_tskip -s 101 102 103 -p 0 99 -ns -o tsD.raw tsD.nhdr) ADD_TEST(tskip10p ${EXECUTABLE_OUTPUT_PATH}/test_tskip -s 101 102 103 -p 77 0 -o tsE.raw tsE.nhdr) ADD_TEST(tskip10n ${EXECUTABLE_OUTPUT_PATH}/test_tskip -s 101 102 103 -p 77 0 -ns -o tsF.raw tsF.nhdr) ADD_EXECUTABLE(test_sanity sanity.c) TARGET_LINK_LIBRARIES(test_sanity teem) ADD_TEST(sanity ${EXECUTABLE_OUTPUT_PATH}/test_sanity) ADD_EXECUTABLE(test_macros macros.c) TARGET_LINK_LIBRARIES(test_macros teem) ADD_TEST(macros ${EXECUTABLE_OUTPUT_PATH}/test_macros) teem-1.11.0~svn6057/Testing/nrrd/trand.c0000664000175000017500000001265312202606733017453 0ustar domibeldomibel/* Teem: Tools to process and visualize scientific data and images . Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The terms of redistributing and/or modifying this software also include exceptions to the LGPL that facilitate static linking. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "teem/nrrd.h" #include /* ** Tests: ** airSrandMT ** airNormalRand ** nrrdNew ** nrrdAlloc_va ** nrrdHisto ** nrrdHistoDraw ** nrrdSave (to .pgm file) ** nrrdNuke */ #define BINS 1000 #define HGHT 1000 int main(int argc, const char *argv[]) { const char *me; size_t vi, ii, qvalLen; Nrrd *nval, *nhist, *nimg, *nread, *ncorr, *ninmem[3]; double aa, bb, *val; airArray *mop; char *name, explain[AIR_STRLEN_LARGE]; #define VALS 0 #define HIST 1 #define IMAG 2 /* PGM image since this Teem build might not support PNG */ char *mine[3] = { "vals.nrrd", "histo.nrrd", "histo.pgm" }; char *corr[3] = { "test/trandvals.nrrd", "test/trandhisto.nrrd", "test/trandhisto.pgm"}; char *what[3] = { "value", "histogram", "histogram image" }; int differ, wi; AIR_UNUSED(argc); me = argv[0]; mop = airMopNew(); qvalLen = 10*BINS; nrrdAlloc_va(nval=nrrdNew(), nrrdTypeDouble, 1, 4*qvalLen); airMopAdd(mop, nval, (airMopper)nrrdNuke, airMopAlways); val = AIR_CAST(double*, nval->data); nhist=nrrdNew(); airMopAdd(mop, nhist, (airMopper)nrrdNuke, airMopAlways); nimg=nrrdNew(); airMopAdd(mop, nimg, (airMopper)nrrdNuke, airMopAlways); nread = nrrdNew(); airMopAdd(mop, nread, (airMopper)nrrdNuke, airMopAlways); ncorr = nrrdNew(); airMopAdd(mop, ncorr, (airMopper)nrrdNuke, airMopAlways); airSrandMT(999); vi = 0; /* without first casting to float, the platform-dependent differences in the values from airNormalRand() would lead to testing errors, e.g.: correct (test/trandvals.nrrd) and generated values differ: valA[0]=0.36654774192269141 < valB[0]=0.36654774192269146 by 5.55112e-17 Would be nice to figure out exactly what the origin of that is ... */ for (ii=0; ii to see documentation. IF(COMMAND cmake_policy) # Library paths (/path/to/libmy.so not translated to -L/path/to -lmy) CMAKE_POLICY(SET CMP0003 NEW) ENDIF(COMMAND cmake_policy) PROJECT(Teem) #----------------------------------------------------------------------------- # Teem version number. Must reflect the values in teem/src/air/air.h SET(Teem_VERSION_MAJOR "1") SET(Teem_VERSION_MINOR "11") SET(Teem_VERSION_PATCH "0") # Version string should not include patch level. The major.minor is # enough to distinguish available features of the toolkit. SET(Teem_VERSION_STRING "${Teem_VERSION_MAJOR}.${Teem_VERSION_MINOR}.${Teem_VERSION_PATCH}") SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/CMake") # We need ansi c-flags, especially on HP SET(CMAKE_C_FLAGS "${CMAKE_ANSI_CFLAGS} ${CMAKE_C_FLAGS}") SET(CMAKE_REQUIRED_FLAGS ${CMAKE_ANSI_CFLAGS}) #----------------------------------------------------------------------------- # Test for some required system information. INCLUDE (CMakeBackwardCompatibilityC) #----------------------------------------------------------------------------- # Output directories. SET (LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin CACHE INTERNAL "Single output directory for building all libraries.") SET (EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin CACHE INTERNAL "Single output directory for building all executables.") #output directory for installing all include files.") MARK_AS_ADVANCED(LIBRARY_OUTPUT_PATH EXECUTABLE_OUTPUT_PATH) SET(Teem_LIBRARY_PATH "${LIBRARY_OUTPUT_PATH}") SET(Teem_EXECUTABLE_PATH "${EXECUTABLE_OUTPUT_PATH}") SET(C_TEST_PATH ${EXECUTABLE_OUTPUT_PATH}) FILE(MAKE_DIRECTORY ${Teem_BINARY_DIR}/include) FILE(MAKE_DIRECTORY ${Teem_BINARY_DIR}/include/teem) #----------------------------------------------------------------------------- # Find platform-specific differences in the handling of IEEE floating point # special values. INCLUDE(${Teem_SOURCE_DIR}/CMake/TestQnanhibit.cmake) TEST_QNANHIBIT(QNANHIBIT_VALUE ${Teem_SOURCE_DIR}/CMake) IF(QNANHIBIT_VALUE) SET(QNANHIBIT 1 CACHE INTERNAL "The 22nd bit of 32-bit floating-point quiet NaN.") ELSE(QNANHIBIT_VALUE) SET(QNANHIBIT 0 CACHE INTERNAL "The 22nd bit of 32-bit floating-point quiet NaN.") ENDIF(QNANHIBIT_VALUE) #----------------------------------------------------------------------------- # Teem build configuration options. OPTION(BUILD_SHARED_LIBS "Build Teem with shared libraries." OFF) SET(Teem_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) IF(NOT Teem_BUILD_SHARED_LIBS) ADD_DEFINITIONS(-DTEEM_STATIC) ENDIF(NOT Teem_BUILD_SHARED_LIBS) OPTION(Teem_ZLIB "Build Teem with support for gzip compression." ON) OPTION(Teem_PNG "Build Teem with support for PNG images." ON) OPTION(Teem_VTK_MANGLE "Build Teem using the mangled libraries from VTK for ZLIB and PNG." OFF) SET(Teem_VTK_ZLIB_MANGLE_IPATH "" CACHE PATH "Location of vtk_zlib_mangle.h") SET(Teem_VTK_TOOLKITS_IPATH "" CACHE PATH "Location of VTK Build directory for ZLIB and PNG includes.") # Can't build PNG without ZLIB, so force it on. IF(Teem_PNG AND NOT Teem_ZLIB) SET(Teem_ZLIB ON) ENDIF(Teem_PNG AND NOT Teem_ZLIB) IF(Teem_VTK_MANGLE) ADD_DEFINITIONS(-DTEEM_VTK_MANGLE=1) INCLUDE_DIRECTORIES( ${Teem_VTK_TOOLKITS_IPATH} ) INCLUDE_DIRECTORIES( ${Teem_VTK_ZLIB_MANGLE_IPATH} ) ENDIF(Teem_VTK_MANGLE) SET(Teem_ZLIB_LIB "") SET(Teem_PNG_LIB "") IF(Teem_ZLIB) # Find ZLIB FIND_PACKAGE(ZLIB) IF(ZLIB_FOUND) ADD_DEFINITIONS(-DTEEM_ZLIB) SET(Teem_ZLIB_LIB ${ZLIB_LIBRARIES}) SET(Teem_ZLIB_IPATH ${ZLIB_INCLUDE_DIR}) ELSE(ZLIB_FOUND) # We need to set this as a cache variable, so that it will show up as # being turned off in the cache. MESSAGE("warning: Turning off Teem_ZLIB, because it wasn't found.") SET(Teem_ZLIB OFF CACHE BOOL "Build Teem with support for gzip compression." FORCE) ENDIF(ZLIB_FOUND) IF(Teem_PNG) FIND_PACKAGE(PNG) IF(PNG_FOUND) ADD_DEFINITIONS(-DTEEM_PNG ${PNG_DEFINITIONS}) SET(Teem_PNG_LIB ${PNG_LIBRARIES}) SET(Teem_PNG_IPATH ${PNG_INCLUDE_DIR}) ELSE(PNG_FOUND) # We need to set this as a cache variable, so that it will show up as # being turned off in the cache. MESSAGE("warning: Turning off Teem_PNG, because it wasn't found.") SET(Teem_PNG OFF CACHE BOOL "Build Teem with support for PNG images." FORCE) ENDIF(PNG_FOUND) ENDIF(Teem_PNG) ENDIF(Teem_ZLIB) # Try and locate BZIP2 stuff OPTION(Teem_BZIP2 "Build Teem with support for bzip compression." ON) SET(Teem_BZIP2_LIB "") IF(Teem_BZIP2) FIND_PACKAGE(BZ2) IF(BZ2_FOUND) ADD_DEFINITIONS(-DTEEM_BZIP2) SET(Teem_BZIP2_LIB ${BZ2_LIBRARIES}) SET(Teem_BZIP2_IPATH ${BZ2_INCLUDE_DIR}) ELSE(BZ2_FOUND) # We need to set this as a cache variable, so that it will show up as # being turned off in the cache. MESSAGE("warning: Turning off Teem_BZIP2, because it wasn't found.") SET(Teem_BZIP2 OFF CACHE BOOL "Build Teem with support for bzip compression." FORCE) ENDIF(BZ2_FOUND) ENDIF(Teem_BZIP2) # Look for threading libraries OPTION(Teem_PTHREAD "Build Teem with pthread library support." ON) IF(Teem_PTHREAD) INCLUDE(FindThreads) IF(CMAKE_USE_PTHREADS_INIT) ADD_DEFINITIONS(-DTEEM_PTHREAD) ELSE(CMAKE_USE_PTHREADS_INIT) # We need to set this as a cache variable, so that it will show up as # being turned off in the cache. MESSAGE("warning: Turning off Teem_PTHREAD, because it wasn't found.") SET(Teem_PTHREAD OFF CACHE BOOL "Build Teem with pthread library support." FORCE) SET(Teem_PTHREAD OFF) ENDIF(CMAKE_USE_PTHREADS_INIT) ENDIF(Teem_PTHREAD) # Look for "levmar" library OPTION(Teem_LEVMAR "Build Teem with levmar library support." OFF) SET(Teem_LEVMAR_LIB "") IF(Teem_LEVMAR) FIND_PACKAGE(LEVMAR) IF(LEVMAR_FOUND) ADD_DEFINITIONS(-DTEEM_LEVMAR) SET(Teem_LEVMAR_LIB ${LEVMAR_LIBRARIES}) SET(Teem_LEVMAR_IPATH ${LEVMAR_INCLUDE_DIR}) ELSE(LEVMAR_FOUND) # We need to set this as a cache variable, so that it will show up as # being turned off in the cache. MESSAGE("warning: Turning off Teem_LEVMAR, because it wasn't found.") SET(Teem_LEVMAR OFF CACHE BOOL "Build Teem with levmar library support." FORCE) ENDIF(LEVMAR_FOUND) ENDIF(Teem_LEVMAR) # Look for fftw OPTION(Teem_FFTW3 "Build Teem with fftw library support." OFF) SET(Teem_FFTW3_LIB "") IF(Teem_FFTW3) FIND_PACKAGE(FFTW3) IF(FFTW3_FOUND) ADD_DEFINITIONS(-DTEEM_FFTW3) SET(Teem_FFTW3_LIB ${FFTW3_LIBRARIES}) SET(Teem_FFTW3_IPATH ${FFTW3_INCLUDE_DIR}) ELSE(FFTW3_FOUND) # We need to set this as a cache variable, so that it will show up as # being turned off in the cache. MESSAGE("warning: Turning off Teem_FFTW3, because it wasn't found.") SET(Teem_FFTW3 OFF CACHE BOOL "Build Teem with fftw library support." FORCE) ENDIF(FFTW3_FOUND) ENDIF(Teem_FFTW3) #----------------------------------------------------------------------------- IF(${CMAKE_GENERATOR} MATCHES "Visual Studio") # For Visual Studio we don't care about warnings about deprecated sprintf and # the like. ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE) ENDIF(${CMAKE_GENERATOR} MATCHES "Visual Studio") #Teem Defines #The QNANHIBIT variable is configured by the root level CMakeLists.txt IF(QNANHIBIT) ADD_DEFINITIONS(-DTEEM_QNANHIBIT=1) ELSE(QNANHIBIT) ADD_DEFINITIONS(-DTEEM_QNANHIBIT=0) ENDIF(QNANHIBIT) #DirectIO is the fast way to do multi-gigabyte I/O and currently only available #for SGI platforms. Use of DirectIO is enabled manually for now. #OPTION(USE_DIRECTIO "Use DirectIO for Nrrd file IO. Only valid on SGI systems." 0) #MARK_AS_ADVANCED(USE_DIRECTIO) #IF(USE_DIRECTIO) # ADD_DEFINITIONS(-DTEEM_DIO=1) #ELSE(USE_DIRECTIO) ADD_DEFINITIONS(-DTEEM_DIO=0) #ENDIF(USE_DIRECTIO) SET(BUILD_EXPERIMENTAL_LIBS OFF CACHE BOOL "Build Teem's experimental libraries") SET(BUILD_EXPERIMENTAL_APPS OFF CACHE BOOL "Build Teem's non-essential command-line tools") # If its a Dashboard build; turn ON both # BUILD_EXPERIMENTAL_APPS and BUILD_EXPERIMENTAL_LIBS # (h/t David Cole) IF(NOT "$ENV{DASHBOARD_TEST_FROM_CTEST}" STREQUAL "") # it is a dashboard build SET(BUILD_EXPERIMENTAL_APPS ON) SET(BUILD_EXPERIMENTAL_LIBS ON) ENDIF() # (TEEM_LIB_LIST) SET(Teem_LIBRARIES air hest biff nrrd ell unrrdu moss gage dye limn echo hoover seek ten pull mite meet) IF(BUILD_EXPERIMENTAL_LIBS) # This list of the "experimental" libraries must be kept in sync with: # * definition of Teem_HEADER_FILES (below) # * teem/src/meet/meet.h # * teem/src/meet/enumsall.c # re-setting list so libraries appear in expected order SET(Teem_LIBRARIES air hest biff nrrd ell unrrdu alan moss tijk gage dye bane limn echo hoover seek ten elf pull coil push mite meet) ADD_DEFINITIONS(-DTEEM_BUILD_EXPERIMENTAL_LIBS) ENDIF(BUILD_EXPERIMENTAL_LIBS) IF(BUILD_EXPERIMENTAL_APPS) ADD_DEFINITIONS(-DTEEM_BUILD_EXPERIMENTAL_APPS) ENDIF(BUILD_EXPERIMENTAL_APPS) SET(Teem_HEADER_FILES air/air.h hest/hest.h biff/biff.h nrrd/nrrd.h nrrd/nrrdDefines.h nrrd/nrrdMacros.h nrrd/nrrdEnums.h ell/ell.h ell/ellMacros.h unrrdu/unrrdu.h moss/moss.h gage/gage.h dye/dye.h limn/limn.h echo/echo.h hoover/hoover.h seek/seek.h ten/ten.h ten/tenMacros.h pull/pull.h mite/mite.h meet/meet.h ) IF(BUILD_EXPERIMENTAL_LIBS) SET(Teem_HEADER_FILES ${Teem_HEADER_FILES} alan/alan.h tijk/tijk.h bane/bane.h elf/elf.h coil/coil.h push/push.h ) ENDIF(BUILD_EXPERIMENTAL_LIBS) SET(Teem_INSTALLED_HEADER_FILES) FOREACH(header_file ${Teem_HEADER_FILES}) # When debugging, uncomment this line #MESSAGE(STATUS "Copy header file: ${header_file}") GET_FILENAME_COMPONENT(file_no_path "${header_file}" NAME) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/src/${header_file}" "${CMAKE_CURRENT_BINARY_DIR}/include/teem/${file_no_path}" COPYONLY IMMEDIATE) SET(Teem_INSTALLED_HEADER_FILES ${Teem_INSTALLED_HEADER_FILES} "${CMAKE_CURRENT_BINARY_DIR}/include/teem/${file_no_path}") ENDFOREACH(header_file) #--- # Include directory INCLUDE_DIRECTORIES( "${Teem_BINARY_DIR}/include/" "${Teem_SOURCE_DIR}/include/" ) ## Add external library path includes IF(Teem_ZLIB) INCLUDE_DIRECTORIES(${Teem_ZLIB_IPATH}) INCLUDE_DIRECTORIES(${Teem_ZLIB_DLLCONF_IPATH}) IF(Teem_PNG) INCLUDE_DIRECTORIES(${Teem_PNG_IPATH}) INCLUDE_DIRECTORIES(${Teem_PNG_DLLCONF_IPATH}) ENDIF(Teem_PNG) ENDIF(Teem_ZLIB) IF(Teem_BZIP2) INCLUDE_DIRECTORIES(${Teem_BZIP2_IPATH}) ENDIF(Teem_BZIP2) IF(Teem_LEVMAR) INCLUDE_DIRECTORIES(${Teem_LEVMAR_IPATH}) ENDIF(Teem_LEVMAR) IF(Teem_FFTW3) INCLUDE_DIRECTORIES(${Teem_FFTW3_IPATH}) ENDIF(Teem_FFTW3) # All the source files SET(Teem_SOURCES) #----------------------------------------------------------------------------- # Macro for adding the current set of source files to the master list MACRO(ADD_Teem_LIBRARY dir) SET(srcs) FOREACH(src ${ARGN}) SET(srcs ${srcs} "src/${dir}/${src}") ENDFOREACH(src ${ARGN}) SET(Teem_SOURCES ${Teem_SOURCES} ${srcs}) # This will group all the source files in the VS project by directory SOURCE_GROUP( ${dir} FILES ${srcs} ) ENDMACRO(ADD_Teem_LIBRARY name) #----------------------------------------------------------------------------- # Dispatch the build into the proper subdirectories. FOREACH(dir ${Teem_LIBRARIES}) INCLUDE(src/${dir}/sources.cmake) ENDFOREACH(dir ${Teem_LIBRARIES}) #----------------------------------------------------------------------------- # Build the Mega library # # This needs to occur after the parsing of the subdirectories. # Create the library target ADD_LIBRARY(teem ${Teem_SOURCES}) # Set up some library paths for installation. Windows will ingore the # RPATH stuff, and mac will ingore the INSTALL_RPATH. Make sure for # macs to set BUILD_WITH_INSTALL_RPATH OFF and set INSTALL_NAME_DIR. # I belive INSTALL_NAME_DIR will be ignored on linux. SET_TARGET_PROPERTIES(teem PROPERTIES BUILD_WITH_INSTALL_RPATH OFF INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/lib SOVERSION 1 VERSION ${Teem_VERSION_STRING} ) IF(Teem_BZIP2_LIB) TARGET_LINK_LIBRARIES(teem ${Teem_BZIP2_LIB}) ENDIF(Teem_BZIP2_LIB) IF(Teem_ZLIB_LIB) TARGET_LINK_LIBRARIES(teem ${Teem_ZLIB_LIB}) IF(Teem_PNG_LIB) TARGET_LINK_LIBRARIES(teem ${Teem_PNG_LIB}) ENDIF(Teem_PNG_LIB) ENDIF(Teem_ZLIB_LIB) IF(Teem_LEVMAR_LIB) TARGET_LINK_LIBRARIES(teem ${Teem_LEVMAR_LIB}) ENDIF(Teem_LEVMAR_LIB) IF(Teem_FFTW3_LIB) TARGET_LINK_LIBRARIES(teem ${Teem_FFTW3_LIB}) ENDIF(Teem_FFTW3_LIB) IF(Teem_PTHREAD) TARGET_LINK_LIBRARIES(teem ${CMAKE_THREAD_LIBS_INIT}) ENDIF(Teem_PTHREAD) IF(UNIX) TARGET_LINK_LIBRARIES(teem -lm) ENDIF(UNIX) OPTION(Teem_USE_LIB_INSTALL_SUBDIR "Add a Teem-X.Y.Z directory layer to the installation tree for libraries and archives." OFF) IF(Teem_USE_LIB_INSTALL_SUBDIR) SET(EXTRA_INSTALL_PATH /Teem-${Teem_VERSION_STRING}) ELSE(Teem_USE_LIB_INSTALL_SUBDIR) SET(EXTRA_INSTALL_PATH "") ENDIF(Teem_USE_LIB_INSTALL_SUBDIR) INSTALL(TARGETS teem RUNTIME DESTINATION bin LIBRARY DESTINATION lib${EXTRA_INSTALL_PATH} ARCHIVE DESTINATION lib${EXTRA_INSTALL_PATH} ) # Stand-alone programs to process hex encoding of data OPTION(BUILD_HEX "Build dehex and enhex" OFF) IF(BUILD_HEX) ADD_SUBDIRECTORY(src/hex) ENDIF(BUILD_HEX) #----------------------------------------------------------------------------- # For testing OPTION(BUILD_TESTING "Enable this to perform testing of Teem" ON) IF(BUILD_TESTING) INCLUDE(CTest) ENABLE_TESTING() SET(BUILDNAME "${BUILDNAME}" CACHE STRING "Name of build on the dashboard") MARK_AS_ADVANCED(BUILDNAME) MARK_AS_ADVANCED(TCL_TCLSH) ENDIF(BUILD_TESTING) #----------------------------------------------------------------------------- # Now compile the binaries ADD_SUBDIRECTORY(src/bin) IF(BUILD_TESTING) ADD_SUBDIRECTORY(Testing) ENDIF(BUILD_TESTING) #----------------------------------------------------------------------------- # Help outside projects build Teem projects. INCLUDE(CMakeExportBuildSettings) EXPORT_LIBRARY_DEPENDENCIES(${Teem_BINARY_DIR}/TeemLibraryDepends.cmake) CMAKE_EXPORT_BUILD_SETTINGS(${Teem_BINARY_DIR}/TeemBuildSettings.cmake) SET(CFLAGS "${CMAKE_C_FLAGS}") SET(CC "${CMAKE_C_COMPILER}") SET(Teem_EXECUTABLE_DIRS ${EXECUTABLE_OUTPUT_PATH} CACHE INTERNAL "Single output directory for building all executables.") # Teem_CV_ prefixed variables are only used inside TeemConfig.cmake.in for # replacement during the following two CONFIGURE_FILE calls. One is for use # from the build tree, one is for use from the install tree. # For build tree usage # In the build tree, TeemConfig.cmake is in Teem_BINARY_DIR. The root of the # tree for finding include files relative to TeemConfig.cmake is "." # SET(Teem_CV_CONFIG_TO_ROOT ".") SET(Teem_CV_LIBRARY_DEPENDS_FILE ${Teem_BINARY_DIR}/TeemLibraryDepends.cmake) SET(Teem_CV_EXECUTABLE_DIRS ${Teem_EXECUTABLE_DIRS}) SET(Teem_CV_LIBRARY_DIRS ${LIBRARY_OUTPUT_PATH}) SET(Teem_CV_USE_FILE ${Teem_SOURCE_DIR}/CMake/TeemUse.cmake) SET(Teem_CV_INCLUDE_DIRS "${Teem_BINARY_DIR}/include") SET(Teem_CV_BUILD_SETTINGS_FILE ${Teem_BINARY_DIR}/TeemBuildSettings.cmake) SET(Teem_CV_BUILT_LIBRARIES teem) # The libraries built by teem. Currently we only build the mega library. CONFIGURE_FILE("${Teem_SOURCE_DIR}/CMake/TeemConfig.cmake.in" "${Teem_BINARY_DIR}/TeemConfig.cmake" @ONLY IMMEDIATE) # For install tree usage # In the install tree, TeemConfig.cmake is in lib or lib/Teem-X.Y based on the # value of Teem_USE_LIB_INSTALL_SUBDIR. The root of the tree for finding # include files relative to TeemConfig.cmake is therefore ".." or "../.." # IF(Teem_USE_LIB_INSTALL_SUBDIR) SET(Teem_CV_CONFIG_TO_ROOT "../..") ELSE(Teem_USE_LIB_INSTALL_SUBDIR) SET(Teem_CV_CONFIG_TO_ROOT "..") ENDIF(Teem_USE_LIB_INSTALL_SUBDIR) SET(Teem_CV_LIBRARY_DEPENDS_FILE "\${Teem_ROOT_DIR}/lib${EXTRA_INSTALL_PATH}/TeemLibraryDepends.cmake") SET(Teem_CV_EXECUTABLE_DIRS "\${Teem_ROOT_DIR}/bin") SET(Teem_CV_LIBRARY_DIRS "\${Teem_ROOT_DIR}/lib${EXTRA_INSTALL_PATH}") SET(Teem_CV_USE_FILE "\${Teem_ROOT_DIR}/lib${EXTRA_INSTALL_PATH}/TeemUse.cmake") SET(Teem_CV_INCLUDE_DIRS "\${Teem_ROOT_DIR}/include") SET(Teem_CV_BUILD_SETTINGS_FILE "\${Teem_ROOT_DIR}/lib${EXTRA_INSTALL_PATH}/TeemBuildSettings.cmake") SET(Teem_CV_BUILT_LIBRARIES teem) # The libraries built by teem. Currently we only build the mega library. CONFIGURE_FILE("${Teem_SOURCE_DIR}/CMake/TeemConfig.cmake.in" "${Teem_BINARY_DIR}/CMake/TeemConfig.cmake" @ONLY IMMEDIATE) INSTALL(FILES ${Teem_INSTALLED_HEADER_FILES} DESTINATION include/teem ) INSTALL(FILES "${Teem_BINARY_DIR}/CMake/TeemConfig.cmake" "${Teem_SOURCE_DIR}/CMake/TeemUse.cmake" "${Teem_BINARY_DIR}/TeemBuildSettings.cmake" "${Teem_BINARY_DIR}/TeemLibraryDepends.cmake" DESTINATION lib${EXTRA_INSTALL_PATH} ) teem-1.11.0~svn6057/UseTeemCMakeDemo/0000775000175000017500000000000012203513761016662 5ustar domibeldomibelteem-1.11.0~svn6057/UseTeemCMakeDemo/sanity.c0000664000175000017500000000243511606235777020357 0ustar domibeldomibel/* sanity.c: stand-alone demo of nrrdSanity() from Teem Copyright (C) 2008 This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include int main(int argc, char *argv[]) { int ret; char *me, *err; AIR_UNUSED(argc); me = argv[0]; if (!nrrdSanity()) { err = biffGetDone(NRRD); fprintf(stderr, "%s: nrrdSanity failed:\n%s", me, err); free(err); ret = 1; } else { fprintf(stderr, "%s: nrrdSanity passed\n", me); ret = 0; } return ret; } /* EOF */ teem-1.11.0~svn6057/UseTeemCMakeDemo/CMakeLists.txt0000664000175000017500000000042211107327445021424 0ustar domibeldomibelproject (TeemSampleProject) cmake_minimum_required(VERSION 2.4) if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) find_package(Teem REQUIRED) include(${Teem_USE_FILE}) add_executable(sample sanity.c) target_link_libraries(sample teem)