From 9b5b6d91396f7b7794ef2d8b699f5448c5fbd498 Mon Sep 17 00:00:00 2001 From: Chris Rorden Date: Wed, 20 Feb 2019 09:27:56 -0500 Subject: [PATCH] Hide redundant color bars (https://www.nitrc.org/forum/forum.php?thread_id=10001&forum_id=6713) --- Resources/lut/Inferno.clut | 15 + Resources/lut/Plasma.clut | 15 + Resources/lut/PlasmaSeg.clut | 37 + Resources/lut/Viridis.clut | 15 + Resources/lut/unused/GE_color.clut | 17 - Resources/lut/unused/cwn_roybigBL.clut | 25 + Resources/lut/unused/cwp_roybigBL.clut | 25 + Resources/lut/unused/electric_blue.clut | 15 - Resources/lut/unused/gold.clut | 15 - Resources/lut/unused/power_surf.clut | 43 ++ Resources/lut/unused/surface.clut | 13 - Resources/lut/unused/x_rain.clut | 21 - Resources/script/basic_paint_surface.py | 12 + Resources/script/fmri_mesh.py | 8 + Resources/script/help.py | 7 + Resources/script/hide_curves.py | 8 + Resources/script/mesh.py | 9 + Resources/script/newer_2017.py | 18 + Resources/script/node.py | 18 + Resources/script/shaders.py | 33 + Resources/script/track.py | 10 + Resources/shaders/Phong.txt | 1 + Resources/shaders/PhongX.txt | 70 -- Resources/shaders/Unused/Fragment.txt | 59 ++ Resources/shaders/{ => Unused}/Matte.txt | 0 Resources/shaders/Unused/MinimalNoise.txt | 53 ++ Resources/shaders/White_SSAO_only.txt | 39 + Resources/shadersOld/Fastest.txt | 8 +- Resources/shadersOld/{ => Unused}/Matte.txt | 0 _osx.command | 1 + _windows.bat | 15 +- clustering.pas | 33 +- define_types.pas | 2 +- mainunit.lfm | 4 +- mainunit.pas | 73 +- mesh.pas | 715 +++++++++++++----- meshify.pas | 145 +++- prefs.pas | 4 +- .../Contents/Resources/script/startup.gls | 4 +- .../script/{startup.py => startupx.py} | 0 surfice.lps | 296 ++++---- xscriptengine.lfm | 2 +- 42 files changed, 1385 insertions(+), 518 deletions(-) create mode 100755 Resources/lut/Inferno.clut create mode 100755 Resources/lut/Plasma.clut create mode 100755 Resources/lut/PlasmaSeg.clut create mode 100755 Resources/lut/Viridis.clut delete mode 100755 Resources/lut/unused/GE_color.clut create mode 100755 Resources/lut/unused/cwn_roybigBL.clut create mode 100755 Resources/lut/unused/cwp_roybigBL.clut delete mode 100755 Resources/lut/unused/electric_blue.clut delete mode 100755 Resources/lut/unused/gold.clut create mode 100755 Resources/lut/unused/power_surf.clut delete mode 100755 Resources/lut/unused/surface.clut delete mode 100755 Resources/lut/unused/x_rain.clut create mode 100755 Resources/script/basic_paint_surface.py create mode 100755 Resources/script/fmri_mesh.py create mode 100755 Resources/script/help.py create mode 100755 Resources/script/hide_curves.py create mode 100755 Resources/script/mesh.py create mode 100644 Resources/script/newer_2017.py create mode 100755 Resources/script/node.py create mode 100755 Resources/script/shaders.py create mode 100755 Resources/script/track.py delete mode 100755 Resources/shaders/PhongX.txt create mode 100755 Resources/shaders/Unused/Fragment.txt rename Resources/shaders/{ => Unused}/Matte.txt (100%) create mode 100755 Resources/shaders/Unused/MinimalNoise.txt create mode 100755 Resources/shaders/White_SSAO_only.txt rename Resources/shadersOld/{ => Unused}/Matte.txt (100%) rename surfice.app/Contents/Resources/script/{startup.py => startupx.py} (100%) diff --git a/Resources/lut/Inferno.clut b/Resources/lut/Inferno.clut new file mode 100755 index 0000000..ba5bce0 --- /dev/null +++ b/Resources/lut/Inferno.clut @@ -0,0 +1,15 @@ +[FLT] +min=0 +max=0 +[INT] +numnodes=4 +[BYT] +nodeintensity0=0 +nodeintensity1=64 +nodeintensity2=192 +nodeintensity3=255 +[RGBA255] +nodergba0=0|0|4|0 +nodergba1=120|28|109|56 +nodergba2=237|105|37|80 +nodergba3=240|249|33|88 \ No newline at end of file diff --git a/Resources/lut/Plasma.clut b/Resources/lut/Plasma.clut new file mode 100755 index 0000000..f1c5e97 --- /dev/null +++ b/Resources/lut/Plasma.clut @@ -0,0 +1,15 @@ +[FLT] +min=0 +max=0 +[INT] +numnodes=4 +[BYT] +nodeintensity0=0 +nodeintensity1=64 +nodeintensity2=192 +nodeintensity3=255 +[RGBA255] +nodergba0=13|8|135|0 +nodergba1=156|23|158|56 +nodergba2=237|121|83|80 +nodergba3=240|249|33|88 \ No newline at end of file diff --git a/Resources/lut/PlasmaSeg.clut b/Resources/lut/PlasmaSeg.clut new file mode 100755 index 0000000..bb4d880 --- /dev/null +++ b/Resources/lut/PlasmaSeg.clut @@ -0,0 +1,37 @@ +[FLT] +min=0 +max=0 +[INT] +numnodes=15 +[BYT] +nodeintensity0=0 +nodeintensity1=1 +nodeintensity2=36 +nodeintensity3=37 +nodeintensity4=73 +nodeintensity5=74 +nodeintensity6=109 +nodeintensity7=110 +nodeintensity8=146 +nodeintensity9=147 +nodeintensity10=182 +nodeintensity11=183 +nodeintensity12=219 +nodeintensity13=220 +nodeintensity14=255 +[RGBA255] +nodergba0=13|8|135|0 +nodergba1=13|8|135|1 +nodergba2=13|8|135|32 +nodergba3=85|16|147|32 +nodergba4=85|16|147|48 +nodergba5=156|23|158|48 +nodergba6=156|23|158|60 +nodergba7=197|72|121|60 +nodergba8=197|72|121|70 +nodergba9=237|121|83|70 +nodergba10=237|121|83|76 +nodergba11=239|185|58|76 +nodergba12=239|185|58|82 +nodergba13=240|249|33|82 +nodergba14=240|249|33|84 \ No newline at end of file diff --git a/Resources/lut/Viridis.clut b/Resources/lut/Viridis.clut new file mode 100755 index 0000000..4b3c49c --- /dev/null +++ b/Resources/lut/Viridis.clut @@ -0,0 +1,15 @@ +[FLT] +min=0 +max=0 +[INT] +numnodes=4 +[BYT] +nodeintensity0=0 +nodeintensity1=64 +nodeintensity2=192 +nodeintensity3=255 +[RGBA255] +nodergba0=68|1|84|0 +nodergba1=49|104|142|56 +nodergba2=53|183|121|80 +nodergba3=253|231|37|88 \ No newline at end of file diff --git a/Resources/lut/unused/GE_color.clut b/Resources/lut/unused/GE_color.clut deleted file mode 100755 index 6ff865b..0000000 --- a/Resources/lut/unused/GE_color.clut +++ /dev/null @@ -1,17 +0,0 @@ -[FLT] -min=0 -max=0 -[INT] -numnodes=5 -[BYT] -nodeintensity0=0 -nodeintensity1=63 -nodeintensity2=128 -nodeintensity3=192 -nodeintensity4=255 -[RGBA255] -nodergba0=0|0|0|0 -nodergba1=0|128|125|32 -nodergba2=128|0|255|64 -nodergba3=255|128|0|96 -nodergba4=255|255|255|128 diff --git a/Resources/lut/unused/cwn_roybigBL.clut b/Resources/lut/unused/cwn_roybigBL.clut new file mode 100755 index 0000000..444c112 --- /dev/null +++ b/Resources/lut/unused/cwn_roybigBL.clut @@ -0,0 +1,25 @@ +#Connectome Workbench: used with permission +# https://github.com/Washington-University/workbench/tree/master/src/Palette +[FLT] +min=0 +max=0 +[INT] +numnodes=8 +[BYT] +nodeintensity0=0 +nodeintensity1=36 +nodeintensity2=73 +nodeintensity3=109 +nodeintensity4=146 +nodeintensity5=182 +nodeintensity6=219 +nodeintensity7=255 +[RGBA255] +nodergba0=0|0|80|0 +nodergba1=0|0|170|36 +nodergba2=75|0|125|73 +nodergba3=125|0|160|109 +nodergba4=75|125|0|146 +nodergba5=0|200|0|182 +nodergba6=0|255|0|219 +nodergba7=0|255|255|255 \ No newline at end of file diff --git a/Resources/lut/unused/cwp_roybigBL.clut b/Resources/lut/unused/cwp_roybigBL.clut new file mode 100755 index 0000000..089e9ca --- /dev/null +++ b/Resources/lut/unused/cwp_roybigBL.clut @@ -0,0 +1,25 @@ +#Connectome Workbench: used with permission +# https://github.com/Washington-University/workbench/tree/master/src/Palette +[FLT] +min=0 +max=0 +[INT] +numnodes=8 +[BYT] +nodeintensity0=0 +nodeintensity1=36 +nodeintensity2=73 +nodeintensity3=109 +nodeintensity4=146 +nodeintensity5=182 +nodeintensity6=219 +nodeintensity7=255 +[RGBA255] +nodergba0=60|0|0|0 +nodergba1=100|0|0|36 +nodergba2=150|0|0|73 +nodergba3=200|0|0|109 +nodergba4=255|0|0|146 +nodergba5=255|120|0|182 +nodergba6=255|200|0|219 +nodergba7=255|255|0|255 \ No newline at end of file diff --git a/Resources/lut/unused/electric_blue.clut b/Resources/lut/unused/electric_blue.clut deleted file mode 100755 index 11a9f52..0000000 --- a/Resources/lut/unused/electric_blue.clut +++ /dev/null @@ -1,15 +0,0 @@ -[FLT] -min=0 -max=0 -[INT] -numnodes=4 -[BYT] -nodeintensity0=0 -nodeintensity1=92 -nodeintensity2=192 -nodeintensity3=255 -[RGBA255] -nodergba0=0|0|0|0 -nodergba1=10|39|223|48 -nodergba2=136|220|253|64 -nodergba3=255|255|255|70 \ No newline at end of file diff --git a/Resources/lut/unused/gold.clut b/Resources/lut/unused/gold.clut deleted file mode 100755 index 66787fb..0000000 --- a/Resources/lut/unused/gold.clut +++ /dev/null @@ -1,15 +0,0 @@ -[FLT] -min=0 -max=0 -[INT] -numnodes=4 -[BYT] -nodeintensity0=0 -nodeintensity1=85 -nodeintensity2=170 -nodeintensity3=255 -[RGBA255] -nodergba0=0|0|0|0 -nodergba1=142|85|14|42 -nodergba2=227|170|76|84 -nodergba3=255|255|255|128 diff --git a/Resources/lut/unused/power_surf.clut b/Resources/lut/unused/power_surf.clut new file mode 100755 index 0000000..bf25ae7 --- /dev/null +++ b/Resources/lut/unused/power_surf.clut @@ -0,0 +1,43 @@ +[FLT] +min=0 +max=0 +[INT] +numnodes=18 +[BYT] +nodeintensity0=0 +nodeintensity1=15 +nodeintensity2=30 +nodeintensity3=45 +nodeintensity4=60 +nodeintensity5=75 +nodeintensity6=90 +nodeintensity7=105 +nodeintensity8=120 +nodeintensity9=135 +nodeintensity10=150 +nodeintensity11=165 +nodeintensity12=180 +nodeintensity13=195 +nodeintensity14=210 +nodeintensity15=225 +nodeintensity16=240 +nodeintensity17=255 +[RGBA255] +nodergba0=255|0|0|0 +nodergba1=0|0|153|15 +nodergba2=255|255|0|30 +nodergba3=255|179|102|45 +nodergba4=0|204|0|60 +nodergba5=255|75 +nodergba6=0|153|153|90 +nodergba7=0|0|0|105 +nodergba8=77|0|153|120 +nodergba9=51|255|255|135 +nodergba10=255|128|0|150 +nodergba11=153|51|255|165 +nodergba12=0|51|102|180 +nodergba13=51|255|51|195 +nodergba14=0|0|255|210 +nodergba15=255|255|204|225 +nodergba16=0|102|0|240 +nodergba17=64|64|64|255 \ No newline at end of file diff --git a/Resources/lut/unused/surface.clut b/Resources/lut/unused/surface.clut deleted file mode 100755 index c68d5f2..0000000 --- a/Resources/lut/unused/surface.clut +++ /dev/null @@ -1,13 +0,0 @@ -[FLT] -min=0 -max=0 -[INT] -numnodes=3 -[BYT] -nodeintensity0=0 -nodeintensity1=153 -nodeintensity2=255 -[RGBA255] -nodergba0=1|1|1|0 -nodergba1=240|128|128|76 -nodergba2=255|255|255|128 diff --git a/Resources/lut/unused/x_rain.clut b/Resources/lut/unused/x_rain.clut deleted file mode 100755 index 5d60566..0000000 --- a/Resources/lut/unused/x_rain.clut +++ /dev/null @@ -1,21 +0,0 @@ -[FLT] -min=0 -max=0 -[INT] -numnodes=7 -[BYT] -nodeintensity0=0 -nodeintensity1=32 -nodeintensity2=64 -nodeintensity3=96 -nodeintensity4=160 -nodeintensity5=192 -nodeintensity6=255 -[RGBA255] -nodergba0=3|0|0|0 -nodergba1=64|0|128|8 -nodergba2=0|0|255|16 -nodergba3=0|255|0|24 -nodergba4=255|255|0|32 -nodergba5=255|192|0|52 -nodergba6=255|3|0|80 diff --git a/Resources/script/basic_paint_surface.py b/Resources/script/basic_paint_surface.py new file mode 100755 index 0000000..0ac5034 --- /dev/null +++ b/Resources/script/basic_paint_surface.py @@ -0,0 +1,12 @@ +import gl +gl.resetdefaults() +gl.azimuthelevation(70, 15) +gl.meshload('BrainMesh_ICBM152Right.mz3') +gl.overlayload('motor_4t95vol.nii.gz') +gl.overlayminmax(1,2,12) +gl.overlayload('motor_4t95vol.nii.gz') +gl.overlayminmax(2,-1,-2) +gl.colorbarvisible(1) +gl.overlaytransparencyonbackground(25) +gl.meshcurv() + diff --git a/Resources/script/fmri_mesh.py b/Resources/script/fmri_mesh.py new file mode 100755 index 0000000..09afb44 --- /dev/null +++ b/Resources/script/fmri_mesh.py @@ -0,0 +1,8 @@ +import gl +gl.resetdefaults() +gl.meshload('BrainMesh_ICBM152Right.mz3') +gl.overlayload('motor_4t95mesh.mz3') +gl.overlaycolorname(1, 'red') +gl.shaderxray(1.0, 0.3) +gl.azimuthelevation(110, 15) +gl.meshcurv() \ No newline at end of file diff --git a/Resources/script/help.py b/Resources/script/help.py new file mode 100755 index 0000000..9286334 --- /dev/null +++ b/Resources/script/help.py @@ -0,0 +1,7 @@ +import gl +print(gl.__doc__) +for key in dir( gl ): + if not key.startswith('_'): + x = getattr( gl, key ).__doc__ + print(key+' (built-in function): ') + print(x) \ No newline at end of file diff --git a/Resources/script/hide_curves.py b/Resources/script/hide_curves.py new file mode 100755 index 0000000..98f4716 --- /dev/null +++ b/Resources/script/hide_curves.py @@ -0,0 +1,8 @@ +import gl +gl.resetdefaults() +gl.meshload('BrainMesh_ICBM152Left_smoothed.mz3') +gl.meshcurv() +gl.shadername('hidecurves') +gl.overlayload('CIT168.mz3') +gl.shaderforbackgroundonly(1) +gl.shaderadjust('curvthreshhi', 0.44) diff --git a/Resources/script/mesh.py b/Resources/script/mesh.py new file mode 100755 index 0000000..fa44556 --- /dev/null +++ b/Resources/script/mesh.py @@ -0,0 +1,9 @@ +import gl +gl.resetdefaults() +gl.meshload('mni152_2009.mz3') +gl.overlayload('motor_4t95mesh.mz3') +gl.clipazimuthelevation(0.37, 0, 140) +gl.shaderambientocclusion(0.5) +gl.azimuthelevation(90, 20) +gl.shaderxray(1.0, 0.3) + diff --git a/Resources/script/newer_2017.py b/Resources/script/newer_2017.py new file mode 100644 index 0000000..ff23e78 --- /dev/null +++ b/Resources/script/newer_2017.py @@ -0,0 +1,18 @@ +import gl +gl.resetdefaults() +gl.meshload('BrainMesh_ICBM152Right.mz3') +gl.meshcurv() +gl.overlayminmax(1,-1,1) +gl.overlaycolorname(1,'surface') +gl.overlayinvert(1,1) +gl.overlayload('motor_4t95vol.nii.gz') +gl.overlaycolorname(2,'kelvin') +gl.overlayminmax(2,2,7) +gl.overlayload('scalp.mz3') +gl.overlaycolorname(3,'gold') +gl.shaderxray(1.0, 0.9) +gl.meshoverlayorder(1) +gl.colorbarvisible(0) +gl.shaderambientocclusion(0.05) +gl.azimuthelevation(90, 15) +gl.clipazimuthelevation(0.5, 0, 90) diff --git a/Resources/script/node.py b/Resources/script/node.py new file mode 100755 index 0000000..d52894c --- /dev/null +++ b/Resources/script/node.py @@ -0,0 +1,18 @@ +import gl +gl.resetdefaults() +gl.meshload('BrainMesh_ICBM152Left.mz3') +gl.edgeload('LPBA40.edge') +gl.clipazimuthelevation(0.3, 0, 130) +gl.nodesize(6, 1) +gl.edgesize(3,1) +gl.nodehemisphere(-1) +gl.azimuthelevation(250, 35) +gl.edgecolor('actc',1) +gl.nodecolor('blue',1) +gl.nodethresh(1.0,1.0) +gl.edgethresh(0.5,1.0) +gl.meshcurv() +gl.overlayminmax(1,-1,1) +gl.overlaycolorname(1,'surface') +gl.overlayinvert(1,1) +gl.overlaytranslucent(1, 1) diff --git a/Resources/script/shaders.py b/Resources/script/shaders.py new file mode 100755 index 0000000..566d660 --- /dev/null +++ b/Resources/script/shaders.py @@ -0,0 +1,33 @@ +import gl +gl.resetdefaults() +kframesperrotation = 180 +gl.meshcolor(210, 148, 148) +gl.meshload('BrainMesh_ICBM152Left.mz3') +gl.edgeload('LPBA40.edge') +gl.clipazimuthelevation(0.3, 0, 130) +gl.nodesize(6, 1) +gl.edgesize(3,1) +gl.nodehemisphere(-1) +gl.azimuthelevation(250, 35) +gl.edgecolor('actc',1) +gl.nodecolor('red',1) +gl.nodethresh(1.0,1.0) +gl.edgethresh(0.5,1.0) +for i in range(1, kframesperrotation * 5): + s = 0.5 + (i+0.0)/72 + if (s <= 1): + gl.cameradistance(s) + if ((i % kframesperrotation) == 0): + rot = (i / kframesperrotation) + if rot == 1: + gl.shadername('metal') + elif rot == 2: + gl.shadername('wireframe') + elif rot == 3: + gl.shadername('toon') + gl.shaderambientocclusion(0) + else : + gl.shadername('wire') + gl.shaderforbackgroundonly(1) + gl.azimuth( int(round(360.0/kframesperrotation))) + gl.wait(20) \ No newline at end of file diff --git a/Resources/script/track.py b/Resources/script/track.py new file mode 100755 index 0000000..8bb97ae --- /dev/null +++ b/Resources/script/track.py @@ -0,0 +1,10 @@ +import gl +ksteps = 100 +gl.resetdefaults() +gl.meshload('stroke.mz3') +gl.trackload('stroke.trk.gz') +gl.trackprefs(15, 3, 0.5) +for i in range(1, ksteps): + gl.clipazimuthelevation(( (0.8*i)/ksteps ), 0, 130) + gl.wait(20) + diff --git a/Resources/shaders/Phong.txt b/Resources/shaders/Phong.txt index 14a4277..26c8207 100755 --- a/Resources/shaders/Phong.txt +++ b/Resources/shaders/Phong.txt @@ -60,6 +60,7 @@ void main() { vec3 frontcolor = a + d + s; frontcolor *= min((max(dot(n,normalize(v)), 0.0) - 0.5) * Edge, 0.0) + 1.0; backface = 1.0 - step(0.0, n.z); //1=backface + //backcolor = vec3(1.0,0.0,0.0); color = vec4(mix(frontcolor,backcolor, backface), 1.0); } // Copyright (C) 2007 Dave Griffiths diff --git a/Resources/shaders/PhongX.txt b/Resources/shaders/PhongX.txt deleted file mode 100755 index b08beea..0000000 --- a/Resources/shaders/PhongX.txt +++ /dev/null @@ -1,70 +0,0 @@ -//pref -Ambient|float|0|0.9|1 -Diffuse|float|0|0.9|1 -Specular|float|0|0.4|1 -Material|int|0|0|23 -Blinn-Phong shading. Adapted from Fluxus Library, Copyright 2007 Dave Griffiths, GPLv2. http://devernay.free.fr/cours/opengl/materials.html|note -//vert -#version 330 -layout(location = 0) in vec3 Vert; -layout(location = 3) in vec3 Norm; -layout(location = 6) in vec4 Clr; -out vec3 vN, vL, vV; -out vec4 vClr, vP; -uniform mat4 ModelViewProjectionMatrix; -uniform mat4 ModelViewMatrix; -uniform mat3 NormalMatrix; -uniform vec3 LightPos = vec3(0.0, 20.0, 30.0); //LR, -DU+, -FN+ -void main() { - vN = normalize((NormalMatrix * Norm)); - vP = vec4(Vert, 1.0); - gl_Position = ModelViewProjectionMatrix * vec4(Vert, 1.0); - vL = normalize(LightPos); - vV = -vec3(ModelViewMatrix*vec4(Vert,1.0)); - vClr = Clr; -} -//frag -#version 330 -uniform float Ambient, Diffuse, Specular; -uniform vec4 ClipPlane; -uniform int Material; -in vec3 vN, vV, vL; -in vec4 vP, vClr; -out vec4 color; - -//http://openglut.sourceforge.net/teapots_8c.html -float ambR[24] = float[](0.0215, 0.135, 0.05375, 0.25, 0.1745, 0.1, 0.329412, 0.2125, 0.25, 0.19125, 0.24725, 0.19225, 0, 0, 0, 0, 0, 0, 0.02, 0, 0, 0.05, 0.05, 0.05); -float ambG[24] = float[](0.1745, 0.2225, 0.05, 0.20725, 0.01175, 0.18725, 0.223529, 0.1275, 0.25, 0.0735, 0.1995, 0.19225, 0, 0.1, 0, 0, 0, 0, 0.02, 0.05, 0.05, 0, 0.05, 0.05); -float ambB[24] = float[](0.0215, 0.1575, 0.06625, 0.20725, 0.01175, 0.1745, 0.027451, 0.054, 0.25, 0.0225, 0.0745, 0.19225, 0, 0.06, 0, 0, 0, 0, 0.02, 0.05, 0, 0, 0.05, 0); -float difR[24] = float[](0.07568, 0.54, 0.18275, 1, 0.61424, 0.396, 0.780392, 0.714, 0.4, 0.7038, 0.75164, 0.50754, 0.01, 0, 0.1, 0.5, 0.55, 0.5, 0.01, 0.4, 0.4, 0.5, 0.5, 0.5); -float difG[24] = float[](0.61424, 0.89, 0.17, 0.829, 0.04136, 0.74151, 0.568627, 0.4284, 0.4, 0.27048, 0.60648, 0.50754, 0.01, 0.50980392, 0.35, 0, 0.55, 0.5, 0.01, 0.5, 0.5, 0.4, 0.5, 0.5); -float difB[24] = float[](0.07568, 0.63, 0.22525, 0.829, 0.04136, 0.69102, 0.113725, 0.18144, 0.4, 0.0828, 0.22648, 0.50754, 0.01, 0.50980392, 0.1, 0, 0.55, 0, 0.01, 0.5, 0.4, 0.4, 0.5, 0.4); -float specR[24] = float[](0.633, 0.316228, 0.332741, 0.296648, 0.727811, 0.297254, 0.992157, 0.393548, 0.774597, 0.256777, 0.628281, 0.508273, 0.5, 0.50196078, 0.45, 0.7, 0.7, 0.6, 0.4, 0.04, 0.04, 0.7, 0.7, 0.7); -float specG[24] = float[](0.727811, 0.316228, 0.328634, 0.296648, 0.626959, 0.30829, 0.941176, 0.271906, 0.774597, 0.137622, 0.555802, 0.508273, 0.5, 0.50196078, 0.55, 0.6, 0.7, 0.6, 0.4, 0.7, 0.7, 0.04, 0.7, 0.7); -float specB[24] = float[](0.633, 0.316228, 0.346435, 0.296648, 0.626959, 0.306678, 0.807843, 0.166721, 0.774597, 0.086014, 0.366065, 0.508273, 0.5, 0.50196078, 0.45, 0.6, 0.7, 0.5, 0.4, 0.7, 0.04, 0.04, 0.7, 0.04); -float shiny[24] = float[](0.6, 0.1, 0.3, 0.088, 0.6, 0.1, 0.21794872, 0.2, 0.6, 0.1, 0.4, 0.4, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.078125, 0.078125, 0.078125, 0.078125, 0.078125, 0.078125); - -void main() { - if ((ClipPlane[0] < 1.5) && (dot( ClipPlane, vP) > 0.0)) discard; - vec3 l = normalize(vL); - vec3 n = normalize(vN); - vec3 v = normalize(vV); - vec3 h = normalize(l+v); - vec3 a = vec3(ambR[Material], ambG[Material], ambB[Material]) * Ambient; - vec3 d = vec3(difR[Material], difG[Material], difB[Material]) * Diffuse; - float backface = 1.0 - step(0.0, n.z); //1=backface - vec3 dback = d * max(dot( l, -n), 0.0); - d *= max(dot( l, n), 0.0); - vec3 s = vec3(specR[Material], specG[Material], specB[Material]) * pow(max(0.0, dot(n, h)), 128.0 * shiny[Material]) * Specular; - //pow(max(0.0, dot(surfaceNormal, H)), shininess); - color = vec4(mix(a + d + s,a + dback, backface), 1.0); -} -// Copyright (C) 2007 Dave Griffiths -// Fluxus Shader Library -// --------------------- -// Glossy Specular Reflection Shader -// A more controllable version of blinn shading, -// Useful for ceramic or fluids - from Advanced -// Renderman, thanks to Larry Gritz -// http://www.pawfal.org/fluxus/ -// https://github.com/danomatika/fluxus/blob/master/LICENCE \ No newline at end of file diff --git a/Resources/shaders/Unused/Fragment.txt b/Resources/shaders/Unused/Fragment.txt new file mode 100755 index 0000000..cf6d600 --- /dev/null +++ b/Resources/shaders/Unused/Fragment.txt @@ -0,0 +1,59 @@ +//pref +Ambient|float|0|0.35|1 +Diffuse|float|0|0.9|1 +Specular|float|0|0.4|1 +DiffuseRough|float|0|1|5 +SpecularRough|float|0.001|0.1|1 +Sharpness|float|0|0.0|1 +Edge|float|0|0|1.0 +LightBackfaces|bool|false +Blinn-Phong shading. Adapted from Fluxus Library, Copyright 2007 Dave Griffiths, GPLv2.|note +//vert +#version 330 +layout(location = 0) in vec3 Vert; +layout(location = 3) in vec3 Norm; +layout(location = 6) in vec4 Clr; +out vec4 vClr, vP; +uniform mat4 ModelViewProjectionMatrix; +uniform mat4 ModelViewMatrix; +uniform mat3 NormalMatrix; +uniform float Ambient, Diffuse, Specular, SpecularRough, DiffuseRough, Edge, Sharpness; +uniform bool LightBackfaces; + +uniform vec3 LightPos = vec3(0.0, 20.0, 30.0); //LR, -DU+, -FN+ +vec3 desaturate(vec3 color, float amount) { + vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), color)); + return vec3(mix(color, gray, amount)); +} +void main() { + vP = vec4(Vert, 1.0); + gl_Position = ModelViewProjectionMatrix * vec4(Vert, 1.0); + vec3 l = normalize(LightPos); + vec3 n = normalize(normalize((NormalMatrix * Norm))); + vec3 v = normalize(-vec3(ModelViewMatrix*vec4(Vert,1.0))); + vec3 h = normalize(l+v); + vec3 a = Clr.rgb; + vec3 d = a * Diffuse; + a *= Ambient; + vec3 backcolor = desaturate(0.75 * a + 0.75 * d * abs(dot(n,l)), 0.5); + float backface = 1.0 - step(0.0, n.z); //1=backface + n = mix(n, -n, backface * float(LightBackfaces)); //reverse normal if backface AND two-sided lighting + d *= max(pow(max(dot( l, n), 0.0), DiffuseRough), 0.0); + float s = pow(max(0.0,dot(n,h)), 1.0/(SpecularRough * SpecularRough)); + float w = 0.72*(1.0-Sharpness); + s = smoothstep(0.72-w,0.72+w,s) * Specular; + vec3 frontcolor = a + d + s; + frontcolor *= min((max(dot(n,normalize(v)), 0.0) - 0.5) * Edge, 0.0) + 1.0; + backface = 1.0 - step(0.0, n.z); //1=backface + //backcolor = vec3(1.0,0.0,0.0); + vClr = vec4(mix(frontcolor,backcolor, backface), 1.0); +} +//frag +#version 330 +uniform vec4 ClipPlane; +in vec4 vClr, vP; +out vec4 color; +void main() { + if ((ClipPlane[0] < 1.5) && (dot( ClipPlane, vP) > 0.0)) discard; + color = vec4(vClr.rgb, 1.0); +} diff --git a/Resources/shaders/Matte.txt b/Resources/shaders/Unused/Matte.txt similarity index 100% rename from Resources/shaders/Matte.txt rename to Resources/shaders/Unused/Matte.txt diff --git a/Resources/shaders/Unused/MinimalNoise.txt b/Resources/shaders/Unused/MinimalNoise.txt new file mode 100755 index 0000000..1aa6970 --- /dev/null +++ b/Resources/shaders/Unused/MinimalNoise.txt @@ -0,0 +1,53 @@ +//pref +Ambient|float|0.0|0.5|1 +Diffuse|float|0.0|0.7|1 +Specular|float|0.0|0.2|1 +Shininess|float|1|60|120 +Blinn-Phong shading with Lambertian diffuse. Copyright 2015 Chris Rorden, BSD2clause.|note +//vert +#version 330 +layout(location = 0) in vec3 Vert; +layout(location = 3) in vec3 Norm; +layout(location = 6) in vec4 Clr; +out vec3 vClr, vN, vL, vV; +out vec4 vP; +uniform mat4 ModelViewProjectionMatrix; +uniform mat4 ModelViewMatrix; +uniform mat3 NormalMatrix; +uniform vec3 LightPos = vec3(0.0, 20.0, 30.0); +void main() { + vN = normalize((NormalMatrix * Norm)); + vP = vec4(Vert, 1.0); + gl_Position = ModelViewProjectionMatrix * vec4(Vert, 1.0); + vL = normalize(LightPos); + vV = -vec3(ModelViewMatrix*vec4(Vert,1.0)); + vClr = Clr.rgb; +} +//frag +#version 330 +in vec4 vP; +in vec3 vClr, vN, vL, vV; +out vec4 color; +uniform float Ambient = 0.5; +uniform float Diffuse = 0.7; +uniform float Specular = 0.2; +uniform float Shininess = 60.0; +uniform vec4 ClipPlane = vec4(2.0, 0.0, 0.0, 0.0); +void main() { + if ((ClipPlane[0] < 1.5) && (dot( ClipPlane, vP) > 0.0)) discard; + vec3 l = normalize(vL); + vec3 n = normalize(vN); + vec3 h = normalize(l+normalize(vV)); + vec3 a = vClr * Ambient; + vec3 d = vClr * dot(n,l) * Diffuse; + float s = pow(max(0.0,dot(n,h)), Shininess) * Specular; + vec3 backcolor = (a - d) * 0.6; + vec3 frontcolor = a + d + s; + float backface = step(0.00, n.z); + float noise = fract(sin(gl_FragCoord.x * 12.9898 + gl_FragCoord.y * 78.233) * 43758.5453); + noise = (noise * 2) - 1.0; + noise = noise * 0.05; + frontcolor += noise; + + color = vec4(mix(backcolor, frontcolor, backface), 1.0); +} \ No newline at end of file diff --git a/Resources/shaders/White_SSAO_only.txt b/Resources/shaders/White_SSAO_only.txt new file mode 100755 index 0000000..5e68494 --- /dev/null +++ b/Resources/shaders/White_SSAO_only.txt @@ -0,0 +1,39 @@ +//pref +Ambient|float|0.0|0.5|1 +Diffuse|float|0.0|0.7|1 +Specular|float|0.0|0.2|1 +Shininess|float|1|60|120 +Blinn-Phong shading with Lambertian diffuse. Copyright 2015 Chris Rorden, BSD2clause.|note +//vert +#version 330 +layout(location = 0) in vec3 Vert; +layout(location = 3) in vec3 Norm; +layout(location = 6) in vec4 Clr; +out vec3 vClr, vN, vL, vV; +out vec4 vP; +uniform mat4 ModelViewProjectionMatrix; +uniform mat4 ModelViewMatrix; +uniform mat3 NormalMatrix; +uniform vec3 LightPos = vec3(0.0, 20.0, 30.0); +void main() { + vN = normalize((NormalMatrix * Norm)); + vP = vec4(Vert, 1.0); + gl_Position = ModelViewProjectionMatrix * vec4(Vert, 1.0); + vL = normalize(LightPos); + vV = -vec3(ModelViewMatrix*vec4(Vert,1.0)); + vClr = Clr.rgb; +} +//frag +#version 330 +in vec4 vP; +in vec3 vClr, vN, vL, vV; +out vec4 color; +uniform float Ambient = 0.5; +uniform float Diffuse = 0.7; +uniform float Specular = 0.2; +uniform float Shininess = 60.0; +uniform vec4 ClipPlane = vec4(2.0, 0.0, 0.0, 0.0); +void main() { + if ((ClipPlane[0] < 1.5) && (dot( ClipPlane, vP) > 0.0)) discard; + color = vec4(1.0, 1.0, 1.0, 1.0); +} \ No newline at end of file diff --git a/Resources/shadersOld/Fastest.txt b/Resources/shadersOld/Fastest.txt index 47b3565..d75f302 100755 --- a/Resources/shadersOld/Fastest.txt +++ b/Resources/shadersOld/Fastest.txt @@ -1,6 +1,5 @@ //pref -Ambient|float|0.0|0.5|1 -Diffuse|float|0.0|0.7|1 +Diffuse|float|0|1|1 AOradius|float|0|0|16 Fastest but poorest: for software emulation like Mesa. Clip plane will be ignored|note //vert @@ -14,10 +13,11 @@ void main() { vClr = gl_Color; } //frag -uniform float Ambient, Diffuse; +uniform float Diffuse; +//uniform float Ambient; varying vec3 vL, vN; varying vec4 vClr; void main() { - gl_FragColor = vec4( (Ambient + (dot(normalize(vN),normalize(vL)) * Diffuse)) * mix(gl_FrontMaterial.ambient.rgb, vClr.rgb, vClr.a), 1.0); + gl_FragColor = vec4( ((dot(normalize(vN),normalize(vL)) * Diffuse)) * mix(gl_FrontMaterial.ambient.rgb, vClr.rgb, vClr.a), 1.0); } diff --git a/Resources/shadersOld/Matte.txt b/Resources/shadersOld/Unused/Matte.txt similarity index 100% rename from Resources/shadersOld/Matte.txt rename to Resources/shadersOld/Unused/Matte.txt diff --git a/_osx.command b/_osx.command index 64c0862..565c65d 100755 --- a/_osx.command +++ b/_osx.command @@ -52,6 +52,7 @@ rm *.bak rm surfice rm -rf lib rm -rf backup +find . -name ‘*.DS_Store’ -type f -delete #remove Cocoa as widgetset awk '{gsub(/Active="MacOS"/,"");}1' surfice.lpi > surfice.tmp && mv surfice.tmp surfice.lpi diff --git a/_windows.bat b/_windows.bat index bcac4ca..1da5ff4 100755 --- a/_windows.bat +++ b/_windows.bat @@ -1,19 +1,20 @@ REM COMPILE Surf Ice +D: cd D:\pas\surfice -#NO LONGER REQUIRED: we now use glcorearb.pas for OpenGL core -# copy new version of GLEXT that supports geometry shaders -# copy /Y .\coregl\glext.pp .\glext.pp +REM NO LONGER REQUIRED: we now use glcorearb.pas for OpenGL core +REM copy new version of GLEXT that supports geometry shaders +REM copy /Y .\coregl\glext.pp .\glext.pp -#create core version +REM create core version rmdir /S /Q lib copy /Y .\optsCore.inc .\opts.inc d:\lazarus\lazbuild --cpu=x86_64 -B surfice.lpi move /Y "D:\pas\surfice\surfice.exe" "D:\neuro\Surf_Ice\surfice.exe" -#create legacy version with geometry support +REM create legacy version with geometry support rmdir /S /Q lib copy /Y .\optsCompat.inc .\opts.inc d:\lazarus\lazbuild --cpu=x86_64 -B surfice.lpi @@ -23,8 +24,8 @@ move /Y "D:\pas\surfice\surfice.exe" "D:\neuro\Surf_Ice\surficeOld.exe" del d:\neuro\Surf_Ice\*.ini c:\Progra~1\7-Zip\7z a -tzip d:\surfice_windows.zip d:\neuro\Surf_Ice -#return to default libraries -del .\glext.pp +REM return to default libraries +REM del .\glext.pp copy /Y .\optsCompat.inc .\opts.inc rmdir /S /Q lib diff --git a/clustering.pas b/clustering.pas index a7ace65..db5df97 100755 --- a/clustering.pas +++ b/clustering.pas @@ -10,6 +10,9 @@ interface Classes, SysUtils, define_types, nifti_loader, dialogs; function ApplyClusterThreshold(nii: TNIFTI; lThresh: single; lMinClusterVox: integer): boolean; +//lMinClusterVox = 0 : do not filter data +//lMinClusterVox = -1 : preserve largest cluster only + implementation type @@ -17,7 +20,7 @@ implementation procedure FindClusters (lXDim, lYDim, lZDim, lMinClusterVox: integer; var lClusterBuff0: TIntImg); var - lClusterSign,lClusterSz,lClusterFillValue,lQTail,lQHead,lSliceSz,lQSz,lInc,lVolSz: integer;//lScaledThresh + lClusterSign,lClusterSz,lClusterFillValue,lQTail,lQHead,lSliceSz,lQSz,lInc,lVolSz, lMaxClusterVox: integer;//lScaledThresh lQra0: TIntImg; const kFillValue = -2; @@ -129,7 +132,7 @@ procedure SelectClusters (lSign: integer); end; begin - if (lMinClusterVox < 2) then exit; + if (lMinClusterVox = 0) or (lMinClusterVox = 1) then exit; lVolSz := lXdim*lYdim*lZdim; lSliceSz := lXdim * lYdim; if (lXDim < 4) or (lYDim < 4) or (lZDim < 4) or (lVolSz < 1) then exit; @@ -140,9 +143,22 @@ procedure SelectClusters (lSign: integer); SelectClusters(1); Setlength(lQra0, 0); //NEXT: mask image data with cluster size - for lInc := 0 to (lVolSz-1) do - if lClusterBuff0[lInc] < lMinClusterVox then - lClusterBuff0[lInc] := 0; + if (lMinClusterVox < 0) then begin + //find largest cluster + lMaxClusterVox := lClusterBuff0[0]; + for lInc := 0 to (lVolSz-1) do + if lClusterBuff0[lInc] > lMaxClusterVox then + lMaxClusterVox := lClusterBuff0[lInc]; + //only preservelargest cluster + for lInc := 0 to (lVolSz-1) do + if lClusterBuff0[lInc] < lMaxClusterVox then + lClusterBuff0[lInc] := 0; + end else begin + for lInc := 0 to (lVolSz-1) do + if lClusterBuff0[lInc] < lMinClusterVox then + lClusterBuff0[lInc] := 0; + + end; end; @@ -156,7 +172,7 @@ function ApplyClusterThreshold(nii: TNIFTI; lThresh: single; lMinClusterVox: int begin result := true; nVox := nii.hdr.dim[1] * nii.hdr.dim[2] * nii.hdr.dim[3]; - if (lThresh = 0) or (lMinClusterVox < 2) or (nVox < lMinClusterVox) or (nVox <> length(nii.img)) then exit; //clustering has no effect + if (lThresh = 0) or (lMinClusterVox = 0) or (lMinClusterVox = 1) or (nVox < lMinClusterVox) or (nVox <> length(nii.img)) then exit; //clustering has no effect lThreshAbs := abs(lThresh); setlength(intimg, nVox); if lThresh < 0 then begin @@ -179,9 +195,10 @@ function ApplyClusterThreshold(nii: TNIFTI; lThresh: single; lMinClusterVox: int FindClusters (nii.hdr.dim[1], nii.hdr.dim[2], nii.hdr.dim[3], lMinClusterVox, intimg); nSurvive2 := 0; for i := 0 to (nVox -1) do - if(intimg[i] >= lMinClusterVox) then + if(intimg[i] >= 0) then nSurvive2 := nSurvive2 + 1; - showmessage(format('%d voxels exceed %g, of which %d are part of clusters larger than %d voxels',[nSurvive, lThresh, nSurvive2, lMinClusterVox])); + if lMinClusterVox > 0 then + showmessage(format('%d voxels exceed %g, of which %d are part of clusters larger than %d voxels',[nSurvive, lThresh, nSurvive2, lMinClusterVox])); for i := 0 to (nVox -1) do if (intimg[i] = 0) and (nii.img[i] > lThreshAbs) then nii.img[i] := lAlmostThresh; diff --git a/define_types.pas b/define_types.pas index fd01440..5ea69d9 100755 --- a/define_types.pas +++ b/define_types.pas @@ -4,7 +4,7 @@ interface uses graphics; {$endif} const - kVers = 'v1.0.20190131'; + kVers = 'v1.0.20190220'; NaN : double = 1/0; kTab = chr(9); kCR = chr (13); diff --git a/mainunit.lfm b/mainunit.lfm index 24b3617..7b59694 100755 --- a/mainunit.lfm +++ b/mainunit.lfm @@ -1,7 +1,7 @@ object GLForm1: TGLForm1 - Left = 233 + Left = 137 Height = 684 - Top = 22 + Top = 45 Width = 1047 AllowDropFiles = True Caption = 'Surf Ice' diff --git a/mainunit.pas b/mainunit.pas index edd294e..594a311 100755 --- a/mainunit.pas +++ b/mainunit.pas @@ -416,7 +416,7 @@ procedure LayerAlphaTrackMouseUp(Sender: TObject; Button: TMouseButton; Shift: T procedure SaveBitmap(FilenameIn: string); overload; procedure SaveBitmap(FilenameIn: string; lX, lY: integer); overload; procedure SaveMenuClick(Sender: TObject); - procedure SaveMz3(var mesh: TMesh; isSaveOverlays: boolean); + //procedure SaveMz3(var mesh: TMesh; isSaveOverlays: boolean); procedure SaveTrack (var lTrack: TTrack); function SaveMeshCore(lFilename: string): boolean; procedure SaveMesh(var mesh: TMesh; isSaveOverlays: boolean); @@ -2903,6 +2903,29 @@ procedure sph2cartDeg90x(Azimuth,Elevation,R: single; var lX,lY,lZ: single); end; procedure TGLForm1.ClipTrackChange(Sender: TObject); +var + a,e,scale: single; +begin + GetOrigin(scale); + a := ClipAziTrack.Position; + e := ClipElevTrack.Position; + (*if not gPrefs.ObjectBasedClipPlane then begin + a := gAzimuth - a - 180; + e := gElevation - e; + end;*) + sph2cartDeg90x(a,e,1,clipPlane.X,clipPlane.Y,clipPlane.Z); + if ClipTrack.Position < 1 then + clipPlane.X := 2 //tell GLSL that plane is disabled: normalized value must be <= 1.0 + else + clipPlane.W := ((ClipTrack.Position/ClipTrack.Max) - 0.5) * scale * 2.0; + Memo1.Lines.clear; + Memo1.Lines.Add(format('Clipping Amount %d',[ClipTrack.Position])); + Memo1.Lines.Add(format('Clipping Azimuth %d',[ClipAziTrack.Position])); + Memo1.Lines.Add(format('Clipping Elevation %d',[ClipElevTrack.Position])); + GLBox.invalidate; //show change immediately!, for delay: GLBoxRequestUpdate(Sender); +end; + +(*procedure TGLForm1.ClipTrackChange(Sender: TObject); var scale: single; begin @@ -2917,7 +2940,7 @@ procedure TGLForm1.ClipTrackChange(Sender: TObject); Memo1.Lines.Add(format('Clipping Azimuth %d',[ClipAziTrack.Position])); Memo1.Lines.Add(format('Clipping Elevation %d',[ClipElevTrack.Position])); GLBox.invalidate; //show change immediately!, for delay: GLBoxRequestUpdate(Sender); -end; +end; *) procedure TGLForm1.CloseMenuClick(Sender: TObject); begin @@ -4261,6 +4284,7 @@ function TGLForm1.UpdateClrBar: integer; var nLUT, lI, lJ: integer; mn, mx: single; + isDuplicate : boolean; begin nLUT := 0; result := 0; @@ -4274,7 +4298,18 @@ function TGLForm1.UpdateClrBar: integer; if (gMesh.OpenOverlays > 0) then for lI := 1 to gMesh.OpenOverlays do - if (length(gMesh.overlay[lI].intensity) > 0) and(gMesh.overlay[lI].LUTvisible <> kLUTinvisible) and (not isFreeSurferLUT(gMesh.overlay[lI].LUTindex)) then begin + //https://www.nitrc.org/forum/forum.php?thread_id=10001&forum_id=6713 + if (length(gMesh.overlay[lI].intensity) > 0) and (gMesh.overlay[lI].LUTvisible <> kLUTinvisible) and (not isFreeSurferLUT(gMesh.overlay[lI].LUTindex)) then begin + isDuplicate := false; + lJ := 1; + while (lJ < lI) do begin + if (gMesh.overlay[lI].LUTindex = gMesh.overlay[lJ].LUTindex) and(gMesh.overlay[lJ].LUTvisible <> kLUTinvisible) + and (gMesh.overlay[lI].windowScaledMin = gMesh.overlay[lJ].windowScaledMin) + and (gMesh.overlay[lI].windowScaledMax = gMesh.overlay[lJ].windowScaledMax) then + isDuplicate := true; + lJ := lJ + 1; + end; + if isDuplicate then continue; inc(nLUT); gClrbar.SetLUT(nLUT, UpdateTransferFunction(gMesh.overlay[lI].LUTindex,gMesh.overlay[lI].LUTinvert), gMesh.overlay[lI].windowScaledMin,gMesh.overlay[lI].windowScaledMax); end; @@ -4500,6 +4535,8 @@ procedure TGLForm1.CreateRender(w,h: integer; isToScreen: boolean); procedure TGLForm1.GLboxPaint(Sender: TObject); begin + //if not gPrefs.ObjectBasedClipPlane then + // ClipTrackChange(Sender); CreateRender(GLBoxBackingWidth, GLboxBackingHeight, true); if UpdateTimer.enabled then UpdateTimerTimer(Sender); @@ -5527,7 +5564,6 @@ procedure TGLForm1.SaveBitmap(FilenameIn: string; lX, lY: integer); overload; bmp.Free; end; - procedure TGLForm1.SaveBitmap(FilenameIn: string); overload; var bmp: TBitmap; @@ -5566,19 +5602,19 @@ procedure TGLForm1.SaveMenuClick(Sender: TObject); SaveBitmap(SaveBitmapDialog.Filename); end; -procedure TGLForm1.SaveMz3(var mesh: TMesh; isSaveOverlays: boolean); +(*procedure TGLForm1.SaveMz3(var mesh: TMesh; isSaveOverlays: boolean); var i : integer; nam: string; begin - //showmessage(inttostr(gMesh.OpenOverlays)); exit; - Mesh.SaveMz3(SaveMeshDialog.Filename); + Mesh.SaveMz3(SaveMeshDialog.Filename, gPrefs.ObjColor); if not isSaveOverlays then exit; for i := 1 to gMesh.OpenOverlays do begin + if length(gMesh.overlay[i].vertices) < 3 then continue; //only for meshes, not for colors nam := changefileext(SaveMeshDialog.Filename, '_'+inttostr(i)+extractfileext(SaveMeshDialog.Filename)); gMesh.SaveOverlay(nam, i); end; -end; +end;*) //{$DEFINE XL} {$IFDEF XL} @@ -5607,7 +5643,7 @@ procedure TGLForm1.SaveMesh(var mesh: TMesh; isSaveOverlays: boolean); {$ELSE} procedure TGLForm1.SaveMesh(var mesh: TMesh; isSaveOverlays: boolean); const - kMeshFilter = 'OBJ (Widely supported)|*.obj|GIfTI (Neuroimaging)|*.gii|MZ3 (Small and fast)|*.mz3|PLY (Widely supported)|*.ply'; + kMeshFilter = 'OBJ (Widely supported)|*.obj|GIfTI (Neuroimaging)|*.gii|MZ3 (Small and fast)|*.mz3|PLY (Widely supported)|*.ply|VRML (Shapeways color printing)|*.wrl'; var nam, ext, x: string; begin @@ -5641,22 +5677,31 @@ procedure TGLForm1.SaveMesh(var mesh: TMesh; isSaveOverlays: boolean); if length(SaveMeshDialog.Filename) < 1 then exit; //caption := inttostr(SaveMeshDialog.FilterIndex)+' '+SaveMeshDialog.Filename; exit; //666 x := UpperCase(ExtractFileExt(SaveMeshDialog.Filename)); - if (x <> '.MZ3') and (x <> '.PLY') and (x <> '.OBJ') and (x <> '.GII') then begin + if (x <> '.MZ3') and (x <> '.PLY') and (x <> '.OBJ') and (x <> '.GII') and (x <> '.WRL') then begin x := ext; SaveMeshDialog.Filename := SaveMeshDialog.Filename + x; end; - if (x = '.MZ3') then + mesh.SaveMesh(SaveMeshDialog.Filename); + (*if (x = '.WRL') then + mesh.SaveVrml(SaveMeshDialog.Filename, gPrefs.ObjColor) + else if (x = '.MZ3') then SaveMz3(mesh, isSaveOverlays) else if (x = '.GII') then mesh.SaveGii(SaveMeshDialog.Filename) else if (x = '.PLY') then mesh.SavePly(SaveMeshDialog.Filename) else - mesh.SaveObj(SaveMeshDialog.Filename); + mesh.SaveObj(SaveMeshDialog.Filename); *) end; {$ENDIF} function TGLForm1.SaveMeshCore(lFilename: string): boolean; +begin + gMesh.SaveMesh(lFilename); + result := true; +end; + +(*function TGLForm1.SaveMeshCore(lFilename: string): boolean; var x: string; begin @@ -5670,6 +5715,7 @@ function TGLForm1.SaveMeshCore(lFilename: string): boolean; x := '.MZ3'; lFilename := lFilename + x; end; + xxx if (x = '.MZ3') then gMesh.SaveMz3(lFilename) else if (x = '.GII') then @@ -5678,7 +5724,7 @@ function TGLForm1.SaveMeshCore(lFilename: string): boolean; gMesh.SavePly(lFilename) else gMesh.SaveObj(lFilename); -end; +end; *) procedure TGLForm1.SaveMeshMenuClick(Sender: TObject); @@ -5908,7 +5954,6 @@ procedure TGLForm1.FormCreate(Sender: TObject); if (gPrefs.OcclusionAmount <> occlusionTrack.Position) and (gPrefs.OcclusionAmount >= 0) and (gPrefs.OcclusionAmount <= 100) then occlusionTrack.Position:= gPrefs.OcclusionAmount; ColorBarVisibleMenu.Checked := gPrefs.Colorbar; - AdditiveOverlayMenu.Checked := gPrefs.AdditiveOverlay; gMesh.isAdditiveOverlay := gPrefs.AdditiveOverlay; if gPrefs.InitScript <> '' then diff --git a/mesh.pas b/mesh.pas index cb05584..1c5e435 100755 --- a/mesh.pas +++ b/mesh.pas @@ -84,7 +84,8 @@ TMesh = class {$IFDEF TREFOIL} procedure MakeTrefoil; {$ENDIF} procedure MakeSphere; function SetLutIndex(layer:integer): integer; - procedure BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var vtxRGBA: TVertexRGBA); + procedure BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var vtxRGBA: TVertexRGBA; vRemap: TInts = nil; isWriteToGPU: boolean = true); + //procedure BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var vtxRGBA: TVertexRGBA; vRemap: TInts = nil); procedure BuildListPartialAtlas(Clr: TRGBA; vtxRGBA: TVertexRGBA); procedure BuildList(Clr: TRGBA); procedure FilterOverlay(c: integer; var f: TFaces; var v: TVertices; var vRGBA: TVertexRGBA); @@ -118,6 +119,7 @@ TMesh = class function LoadSrf(const FileName: string): boolean; function LoadSurf(const FileName: string): boolean; function LoadTri(const FileName: string): boolean; + function LoadVrml(const FileName: string): boolean; function LoadWfr(const FileName: string): boolean; //EMSE wireframe procedure LoadAsc_Srf(const FileName: string); procedure LoadCtm(const FileName: string); @@ -156,11 +158,12 @@ TMesh = class procedure CloseOverlays; procedure Close; constructor Create; - procedure SaveMz3(const FileName: string); + (*procedure SaveVrml(const FileName: string; MeshColor: TColor = clWhite); + procedure SaveMz3(const FileName: string; MeshColor: TColor = clWhite); procedure SaveGii(const FileName: string); procedure SaveObj(const FileName: string); - procedure SavePly(const FileName: string); - procedure SaveMesh(const FileName: string); + procedure SavePly(const FileName: string; MeshColor: TColor = clWhite);*) + procedure SaveMesh(const FileName: string; MeshColor: TColor = clWhite); procedure SaveOverlay(const FileName: string; OverlayIndex: integer); destructor Destroy; override; end; @@ -252,7 +255,7 @@ procedure AddPt4f(var v: TPoint4f; c1,c2,c3: TRGBA); //create float vector v.W := v.W + c1.a+ c2.a+ c3.a; end; -procedure TMesh.BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var vtxRGBA: TVertexRGBA); +procedure TMesh.BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var vtxRGBA: TVertexRGBA; vRemap: TInts = nil; isWriteToGPU: boolean = true); var i,volInc, c, translucent: integer; mn, mx: single; @@ -260,7 +263,7 @@ procedure TMesh.BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var v vRGBA, vRGBAmx :TVertexRGBA; vNumNeighbor: array of integer; vSumRGBBA: array of TPoint4f; - isCurvLayer: boolean; + //isCurvLayer: boolean; isOverlayPainting : boolean = false; begin if (length(f) < 1) or (length(v) < 3) then exit; @@ -284,7 +287,9 @@ procedure TMesh.BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var v vRGBA[i] := desaturateRGBA ( vtxRGBA[i], vertexRgbaSaturation, C); end; {$IFDEF COREGL} - if (vertexRGBAAlpha < 1.0) then + //2019: required only when no overlays open - try adjusting transparency of Altas + //GLForm1.Caption := floattostr(vertexRGBAAlpha); + if (OpenOverlays < 1) and (vertexRGBAAlpha < 1.0) then for i := 0 to (length(v)-1) do vRGBA[i] := mixRGBA( Clr, vRGBA[i], vertexRGBAAlpha); {$ENDIF} @@ -313,11 +318,19 @@ procedure TMesh.BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var v mx := overlay[c].windowScaledMin; mn := overlay[c].windowScaledMax; end; - for i := 0 to (length(v)-1) do begin - rgb := inten2rgb(overlay[c].intensity[i+volInc], mn, mx, overlay[c].LUT); + if vRemap <> nil then begin //filtered atlas: vertices have been decimated + for i := 0 to (length(v)-1) do begin + rgb := inten2rgb(overlay[c].intensity[vRemap[i]+volInc], mn, mx, overlay[c].LUT); rgb.A := rgb.A div translucent; vRGBAmx[i] := maxRGBA(vRGBAmx[i], rgb); - end; //for i + end; //for i + end else begin + for i := 0 to (length(v)-1) do begin + rgb := inten2rgb(overlay[c].intensity[i+volInc], mn, mx, overlay[c].LUT); + rgb.A := rgb.A div translucent; + vRGBAmx[i] := maxRGBA(vRGBAmx[i], rgb); + end; //for i + end; end; //if visible end; //for c @@ -337,16 +350,22 @@ procedure TMesh.BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var v mn := overlay[c].windowScaledMax; end; volInc := length(v) * (overlay[c].currentVolume - 1); - - for i := 0 to (length(v)-1) do begin - rgb := inten2rgb(overlay[c].intensity[i+volInc], mn, mx, overlay[c].LUT); - rgb.A := rgb.A div translucent; - vRGBA[i] := blendRGBA(vRGBA[i], rgb); - end; //for i + if vRemap <> nil then begin //filtered atlas: vertices have been decimated + for i := 0 to (length(v)-1) do begin + rgb := inten2rgb(overlay[c].intensity[vRemap[i]+volInc], mn, mx, overlay[c].LUT); + rgb.A := rgb.A div translucent; + vRGBA[i] := blendRGBA(vRGBA[i], rgb); + end; //for i + end else begin + for i := 0 to (length(v)-1) do begin + rgb := inten2rgb(overlay[c].intensity[i+volInc], mn, mx, overlay[c].LUT); + rgb.A := rgb.A div translucent; + vRGBA[i] := blendRGBA(vRGBA[i], rgb); + end; //for i + end; end; //if visible end; //for c //finally composite - for i := 0 to (length(v)-1) do vRGBA[i] := blendRGBA(vRGBA[i],vRGBAmx[i]); end else begin //not additive: mix overlays @@ -364,12 +383,19 @@ procedure TMesh.BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var v mn := overlay[c].windowScaledMax; end; volInc := length(v) * (overlay[c].currentVolume - 1); - - for i := 0 to (length(v)-1) do begin - rgb := inten2rgb(overlay[c].intensity[i+volInc], mn, mx, overlay[c].LUT); - rgb.A := rgb.A div translucent; - vRGBA[i] := blendRGBA(vRGBA[i], rgb); - end; //for i + if vRemap <> nil then begin //filtered atlas: vertices have been decimated + for i := 0 to (length(v)-1) do begin + rgb := inten2rgb(overlay[c].intensity[vRemap[i]+volInc], mn, mx, overlay[c].LUT); + rgb.A := rgb.A div translucent; + vRGBA[i] := blendRGBA(vRGBA[i], rgb); + end; //for i + end else begin + for i := 0 to (length(v)-1) do begin + rgb := inten2rgb(overlay[c].intensity[i+volInc], mn, mx, overlay[c].LUT); + rgb.A := rgb.A div translucent; + vRGBA[i] := blendRGBA(vRGBA[i], rgb); + end; //for i + end; end; //if visible end; //for c end; @@ -448,6 +474,13 @@ procedure TMesh.BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var v end; end; //for c {$ENDIF} + if not isWriteToGPU then begin + setlength(vtxRGBA, length(v)); + for i := 0 to (length(v)-1) do + vRGBA[i].a := 255; //opaque + vtxRGBA := Copy(vRGBA, Low(vRGBA), Length(vRGBA)); + exit; + end; {$IFDEF COREGL} BuildDisplayList(f, v, vRGBA, vao, vbo, Clr); {$ELSE} @@ -456,6 +489,7 @@ procedure TMesh.BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var v end else begin Clr.A := 128; //just a bit less than 50% - only for hiding curvature setLength(vRGBA,0); + if not isWriteToGPU then exit; {$IFDEF COREGL} BuildDisplayList(f, v, vRGBA,vao, vbo, Clr); {$ELSE} @@ -464,117 +498,7 @@ procedure TMesh.BuildListCore(Clr: TRGBA; var f: TFaces; var v: TVertices; var v end; end; // BuildListCore() -(*procedure FilterAtlas(Clr: TRGBA; maxROI: integer; faces: TFaces; vertices: TVertices; vertexAtlas: TInts; vtxRGBA: TVertexRGBA; atlasHideFilter: TInts; atlasTransparentFilter: TBools; var f: TFaces; var v: TVertices; var vRGBA: TVertexRGBA); -label - 123; -var - i, j, nfOK, nvOK, nFace, nVert: integer; - vOK: array of integer; - filterLUT: array of boolean; - fOK: array of boolean; -begin - setlength(v,0); - setlength(f,0); - setlength(vRGBA,0); - nVert := length(vertices); - nFace := length(faces); - if (nVert < 3) or (nFace < 1) or (maxROI < 1) or (length(vertexAtlas) <> nVert) then exit; - if (length(atlasHideFilter) < 1) then begin //everything survives - //atlasTransparentFilter - goto 123; - end; - //set up lookup table - list of regions that survive - setlength(filterLUT, maxROI+1); - for i := 0 to maxROI do - filterLUT[i] := false; //assume we will not filter these regions - for i := 0 to (length(atlasHideFilter) - 1) do - if (atlasHideFilter[i] > 0) and (atlasHideFilter[i] <= maxROI) then - filterLUT[atlasHideFilter[i]] := true; //filter the specified region - //mark surviving vertices - setlength(vOK, nVert); - nvOK := 0; - for i := 0 to (nVert -1) do - if (vertexAtlas[i] > 0) and (not filterLUT[vertexAtlas[i]]) then begin - vOK[i] := nvOK; - nvOK := nvOK + 1; - end else - vOK[i] := -1; //does not survive - setlength(filterLUT, 0); - //conditionals for unusual situations - if nvOK = 0 then begin //nothing survives - setlength(vOK,0); - exit; - end; - if (nvOK = nVert) then begin //everything survives - //BuildListCore(Clr, faces, vertices, vtxRGBA); //show complete array - setlength(vOK,0); - goto 123; - end; - //see how many faces survive - setlength(fOK,nFace); - nfOK := 0; - for i := 0 to (nFace -1) do begin - fOK[i] := (vOK[faces[i].X] >= 0) and (vOK[faces[i].Y] >= 0) and (vOK[faces[i].Z] >= 0); - if fOK[i] then nfOK := nfOK + 1; - end; - //no faces survive - if nvOK = 0 then begin //nothing survives - setlength(vOK,0); - setlength(fOK,0); - exit; - end; - //copy surviving vertices - setlength(v,nvOK); - for i := 0 to (nVert -1) do - if (vOK[i] >= 0) then - v[vOK[i]] := vertices[i]; - //copy surviving vertices - setlength(f,nfOK); - j := 0; - for i := 0 to (nFace -1) do - if fOK[i] then begin - f[j].X := vOK[faces[i].X]; - f[j].Y := vOK[faces[i].Y]; - f[j].Z := vOK[faces[i].Z]; - j := j + 1; - end; - //set vertex colors (if loaded) - if length(vtxRGBA) = nVert then begin - setlength(vRGBA, nvOK); - j := 0; - for i := 0 to (nVert -1) do begin - if vOK[i] >= 0 then begin - vRGBA[j] := vtxRGBA[i]; - j := j + 1; - end; //if vertex survives - end; //for each vertex - end; //if vertices are colored (vtxRGBA) - setlength(vOK,0); - setlength(fOK,0); - //>> BuildListCore(Clr, f, v, vRGBA); - //release filtered faces, vertices and colors - 123: - if (length(v) < 1) then begin //nothing hidden - nVert := length(vertices); - nFace := length(faces); - setlength(f,nFace); - setlength(v,nVert); - setlength(vRGBA,nVert); - f := copy(faces, 0, maxint); - v := copy(vertices, 0, maxint); - vRGBA := copy(vtxRGBA, 0, maxint); - end; - if (length(atlasTransparentFilter)) < 1 then exit; - for i := 0 to (nVert-1) do begin - j := vertexAtlas[i]; - if (j < 1) or (j >= length(atlasTransparentFilter)) then continue; - if atlasTransparentFilter[j] then - vtxRGBA[i] := Clr; - end; - -end; *) - -procedure FilterAtlas(Clr: TRGBA; maxROI: integer; faces: TFaces; vertices: TVertices; vertexAtlas: TInts; vtxRGBA: TVertexRGBA; atlasHideFilter: TInts; atlasTransparentFilter: TBools; var f: TFaces; var v: TVertices; var vRGBA: TVertexRGBA); +procedure FilterAtlas(Clr: TRGBA; maxROI: integer; faces: TFaces; vertices: TVertices; vertexAtlas: TInts; vtxRGBA: TVertexRGBA; atlasHideFilter: TInts; atlasTransparentFilter: TBools; var f: TFaces; var v: TVertices; var vRGBA: TVertexRGBA; var vRemap: TInts); var i, j, nfOK, nvOK, nFace, nVert: integer; vOK: array of integer; @@ -658,12 +582,14 @@ procedure FilterAtlas(Clr: TRGBA; maxROI: integer; faces: TFaces; vertices: TVer j := j + 1; end; //set vertex colors (if loaded) + setlength(vRemap, nVert); if length(vtxRGBA) = nVert then begin j := 0; for i := 0 to (nVert -1) do begin if vOK[i] >= 0 then begin vRGBA[j] := vRGBA[i]; //note we read from vRGBA not vtxRGBA to preserve gray vertices - j := j + 1; + vRemap[j] := i; + j := j + 1; end; //if vertex survives end; //for each vertex setlength(vRGBA, nvOK); @@ -672,7 +598,6 @@ procedure FilterAtlas(Clr: TRGBA; maxROI: integer; faces: TFaces; vertices: TVer setlength(fOK,0); //>> BuildListCore(Clr, f, v, vRGBA); //release filtered faces, vertices and colors - end; procedure TMesh.BuildListPartialAtlas(Clr: TRGBA; vtxRGBA: TVertexRGBA); @@ -680,10 +605,12 @@ procedure TMesh.BuildListPartialAtlas(Clr: TRGBA; vtxRGBA: TVertexRGBA); f: TFaces; v: TVertices; vRGBA: TVertexRGBA; + vRemap: TInts; begin - FilterAtlas(Clr, AtlasMaxIndex, faces, vertices, vertexAtlas, vtxRGBA, atlasHideFilter, atlasTransparentFilter, f, v, vRGBA); + FilterAtlas(Clr, AtlasMaxIndex, faces, vertices, vertexAtlas, vtxRGBA, atlasHideFilter, atlasTransparentFilter, f, v, vRGBA, vRemap); if length(v) > 0 then - BuildListCore(Clr, f, v, vRGBA); + BuildListCore(Clr, f, v, vRGBA, vRemap); + setlength(vRemap, 0); setlength(f,0); setlength(v,0); setlength(vRGBA,0); @@ -692,9 +619,9 @@ procedure TMesh.BuildListPartialAtlas(Clr: TRGBA; vtxRGBA: TVertexRGBA); procedure TMesh.BuildList (Clr: TRGBA); begin if (length(faces) < 1) or (length(vertices) < 3) then exit; - if ((length(atlasTransparentFilter) > 0) or (length(atlasHideFilter) > 0)) and (length(vertexAtlas) = length(vertices)) then + if ((length(atlasTransparentFilter) > 0) or (length(atlasHideFilter) > 0)) and (length(vertexAtlas) = length(vertices)) then begin BuildListPartialAtlas(Clr, vertexRGBA) //hide parts of an overlay atlas - else + end else BuildListCore(Clr, faces, vertices, vertexRGBA) end; @@ -810,6 +737,7 @@ procedure TMesh.BuildListOverlay(Clr: TRGBA); oFaces, sFaces, fFaces: TFaces; oVerts, sVerts, fVerts: TVertices; vRGBA, sRGBA, fRGBA: TVertexRGBA; + vRemap: TInts; //sFaces, sVerts, sRGBA //rgb: TRGBA; begin @@ -837,7 +765,8 @@ procedure TMesh.BuildListOverlay(Clr: TRGBA); fFaces := copy(overlay[c].faces, 0, maxint); fVerts := copy(overlay[c].vertices, 0, maxint); fRGBA := copy(overlay[c].vertexRGBA, 0, maxint);*) - FilterAtlas(overlay[c].LUT[192], overlay[c].AtlasMaxIndex, overlay[c].faces, overlay[c].vertices, overlay[c].vertexAtlas, overlay[c].vertexRGBA, overlay[c].atlasHideFilter, overlay[c].atlasTransparentFilter, fFaces, fVerts, fRGBA); + FilterAtlas(overlay[c].LUT[192], overlay[c].AtlasMaxIndex, overlay[c].faces, overlay[c].vertices, overlay[c].vertexAtlas, overlay[c].vertexRGBA, overlay[c].atlasHideFilter, overlay[c].atlasTransparentFilter, fFaces, fVerts, fRGBA, vRemap); + setlength(vRemap, 0); nVert := length(fVerts); nFace := length(fFaces); if (nVert < 3) or (nFace < 1) then continue; @@ -2973,16 +2902,23 @@ function MemoryStreamAsString(vms: TMemoryStream): string; SetString(Result, vms.Memory, vms.Size) end; // MemoryStreamAsString() -procedure TMesh.SavePly(const FileName: string); +procedure SavePly(const FileName: string; Faces: TFaces; Vertices: TVertices; vertexRGBA: TVertexRGBA); +//procedure TMesh.SavePly(const FileName: string); +type + TRGB = packed record //Next: analyze Format Header structure + R,G,B : byte; + end; const kEOLN = chr($0A); var i: integer; - s: string; + isRGB: boolean; + s,clrStr: string; f: file; byt: byte; w: array[0..2] of word; FileNamePly: string; + rgb: TRGB; begin if (length(faces) < 1) or (length(vertices) < 3) then begin showmessage('You need to open a mesh before you can save it'); @@ -2996,6 +2932,11 @@ procedure TMesh.SavePly(const FileName: string); s := 'property list uchar short vertex_indices' else s := 'property list uchar uint vertex_indices'; + isRGB := length(vertexRGBA) = length(vertices); + clrStr := ''; + if isRGB then + clrStr := 'property uchar red'+kEOLN+'property uchar green'+kEOLN+'property uchar blue'+kEOLN; + s := 'ply'+kEOLN {$IFDEF ENDIAN_LITTLE} +'format binary_little_endian 1.0'+kEOLN @@ -3007,6 +2948,7 @@ procedure TMesh.SavePly(const FileName: string); +'property float x'+kEOLN +'property float y'+kEOLN +'property float z'+kEOLN + + clrStr +'element face '+inttostr(length(faces))+kEOLN +s+kEOLN +'end_header'+kEOLN; @@ -3014,6 +2956,15 @@ procedure TMesh.SavePly(const FileName: string); AssignFile(f, FileNamePly); ReWrite(f, 1); BlockWrite(f, s[1], length(s)); + if (isRGB) then begin + for i := 0 to (length(vertices) -1) do begin + BlockWrite(f, vertices[i], 3 * 4); + RGB.R := vertexRGBA[i].R; + RGB.G := vertexRGBA[i].G; + RGB.B := vertexRGBA[i].B; + BlockWrite(f, RGB, 3); + end; + end else BlockWrite(f, vertices[0], 3 * 4 * length(vertices)); byt := 3; if length(faces) < 32767 then begin @@ -3034,7 +2985,7 @@ procedure TMesh.SavePly(const FileName: string); FileMode := fmOpenRead; end; // SavePly() -procedure TMesh.SaveObj(const FileName: string); +procedure SaveObj(const FileName: string; Faces: TFaces; Vertices: TVertices; vertexRGBA: TVertexRGBA); //create WaveFront object file // https://en.wikipedia.org/wiki/Wavefront_.obj_file var @@ -3046,7 +2997,7 @@ procedure TMesh.SaveObj(const FileName: string); showmessage('You need to open a mesh before you can save it'); exit; end; - if not CheckMesh then exit; + if (length(vertices) < 3) or (length(faces) < 1) then exit; //if not CheckMesh then exit; FileNameObj := changeFileExt(FileName, '.obj'); {$IFDEF UNIX} FileNameObj := ExpandUNCFileNameUTF8(FileNameObj); // ~/tst.pl -> /Users/rorden/home/tst.ply @@ -3117,15 +3068,13 @@ procedure ReadSwizzle(var mStream : TMemoryStream; Buffer: pointer; nBytes: Long end; {$ENDIF} -procedure SaveMz3Core(const FileName: string; Faces: TFaces; Vertices: TVertices; vertexRGBA: TVertexRGBA; intensity: TFloats); +procedure SaveMz3(const FileName: string; Faces: TFaces; Vertices: TVertices; vertexRGBA: TVertexRGBA; intensity: TFloats); const kMagic = 23117; //"MZ" var {$IFDEF SwizzleMZ}isSwizzle{$ENDIF} isFace, isVert, isRGBA, isScalar: boolean; Magic, Attr: uint16; - //bs: TUInt8s; - //sz: integer; nFace, nVert, nSkip: uint32; mStream : TMemoryStream; zStream: TGZFileStream; @@ -3194,27 +3143,229 @@ procedure SaveMz3Core(const FileName: string; Faces: TFaces; Vertices: TVertices FileMode := fmOpenRead; end; -procedure TMesh.SaveMz3(const FileName: string); +procedure SaveVrml(const FileName: string; Faces: TFaces; Vertices: TVertices; vertexRGBA: TVertexRGBA); var - i: TFloats; + i, nFace, nVert, nRGBA: integer; + FileNameWrl: string; + f : TextFile; begin - setlength(i,0); - SaveMz3Core(Filename, Faces,Vertices,vertexRGBA, i); + nFace := length(Faces); + nVert := length(Vertices); + nRGBA := length(vertexRGBA); + if (nVert < 3) or (nFace < 1) then exit; + FileNameWrl := DefaultToHomeDir(FileName); + FileNameWrl := changeFileExt(FileNameWrl, '.wrl'); + {$IFDEF UNIX} //I do not think we need to do this! + FileNameWrl := ExpandUNCFileNameUTF8(FileNameWrl); + {$ENDIF} + FileMode := fmOpenWrite; + AssignFile(f, FileNameWrl); + ReWrite(f); + WriteLn(f, '#VRML V2.0 utf8'); + WriteLn(f, '# Generated by Surfice\n'); + WriteLn(f, 'NavigationInfo {'); + WriteLn(f, ' type [ "EXAMINE", "ANY" ]'); + WriteLn(f, '}'); + WriteLn(f, 'Transform {'); + WriteLn(f, ' scale 1 1 1'); + WriteLn(f, ' translation 0 0 0'); + WriteLn(f, ' children'); + WriteLn(f, ' ['); + WriteLn(f, ' Shape'); + WriteLn(f, ' {'); + WriteLn(f, ' geometry IndexedFaceSet'); + WriteLn(f, ' {'); + WriteLn(f, ' creaseAngle .5'); + WriteLn(f, ' solid FALSE'); + WriteLn(f, ' coord Coordinate'); + WriteLn(f, ' {'); + WriteLn(f, ' point'); + WriteLn(f, ' ['); + Write(f,' '); + for i := 0 to (nVert -1) do begin + if (i > 0) then Write(f,', '); + Write(f,format('%g %g %g', [Vertices[i].X, Vertices[i].Y, Vertices[i].Z])); + end; + WriteLn(f,''); + WriteLn(f, ' ]'); + WriteLn(f, ' }'); + if (nRGBA = nVert) then begin + WriteLn(f, ' color Color'); + WriteLn(f, ' {'); + WriteLn(f, ' color'); + WriteLn(f, ' ['); + Write(f,' '); + for i := 0 to (nRGBA -1) do begin + if (i > 0) then Write(f,', '); + Write(f,format('%g %g %g', [vertexRGBA[i].R/255, vertexRGBA[i].G/255, vertexRGBA[i].B/255])); + end; + WriteLn(f,''); + WriteLn(f, ' ]'); + WriteLn(f, ' }'); + end; + WriteLn(f, ' coordIndex'); + WriteLn(f, ' ['); + //nFace := length(Faces); + + Write(f,' '); + for i := 0 to (nFace -1) do begin + if (i > 0) then Write(f,', '); + Write(f,format('%d,%d,%d,-1', [Faces[i].X, Faces[i].Y, Faces[i].Z])); + end; + WriteLn(f,''); + WriteLn(f, ' ]'); + WriteLn(f, ' }'); + WriteLn(f, ' appearance Appearance'); + WriteLn(f, ' {'); + WriteLn(f, ' material Material'); + WriteLn(f, ' {'); + WriteLn(f, ' ambientIntensity 0.2'); + WriteLn(f, ' diffuseColor 0.9 0.9 0.9'); + WriteLn(f, ' specularColor .1 .1 .1'); + WriteLn(f, ' shininess .5'); + WriteLn(f, ' }'); + WriteLn(f, ' }'); + WriteLn(f, ' }'); + WriteLn(f, ' ]'); + WriteLn(f, '}'); + //for i := 0 to (length(vertices)-1) do + // WriteLn(f, 'v ' + floattostr(vertices[i].X)+' '+floattostr(vertices[i].Y)+' '+ floattostr(vertices[i].Z)); + //for i := 0 to (length(faces)-1) do + // WriteLn(f, 'f ' + inttostr(faces[i].X+1)+' '+inttostr(faces[i].Y+1)+' '+ inttostr(faces[i].Z+1)); //+1 since "A valid vertex index starts from 1 " + //fprintf(fid, '# WaveFront Object format image created with MRIcroS\n'); + //fprintf(fid, 'v %.12g %.12g %.12g\n', vertex'); + //fprintf(fid, 'f %d %d %d\n', (face)'); + CloseFile(f); + FileMode := fmOpenRead; + end; -procedure TMesh.SaveMesh(const FileName: string); +function ParseStartEnd(key : string; var Str: string; out keyEnd: integer): integer; +begin + keyEnd := -1; + result := posEx(key, Str); + if result < 1 then exit(); + result := result + length(key)+1; + result := posEx('[', Str, result); + if result < 1 then exit; + result := result +1; + keyEnd := posEx(']', Str, result); + keyEnd := keyEnd - 1; + if keyEnd < 1 then exit(-1); +end; + +function TMesh.LoadVrml(const FileName: string): boolean; //VRML wireframe +//this is a very basic VRML reader +// probably only able toread VRML files created by MeshLab and Surfice +label + 555, 666; +const + kBlockSz = 4096; + kNaN : single = 1/0; var - x: string; + f: file; + sList: TStringList; + Str: string; + isQuad : boolean; + fl: single; + i,j, n,tStart, tEnd, szRead, nFace, nVert: integer; begin - x := UpperCase(ExtractFileExt(Filename)); - if (x = '.MZ3') then - SaveMz3(FileName) - else if (x = '.GII') then - SaveGii(Filename) - else if (x = '.PLY') then - SavePly(Filename) - else - SaveObj(Filename); + result := false; + FileMode := fmOpenRead; + AssignFile(f, FileName); + Reset(f,1); + szRead := FileSize(f); + SetLength(Str, szRead); + BlockRead(f, Str[1],szRead); + CloseFile(f); + FileMode := fmOpenRead; + tStart := posEx('IndexedFaceSet', Str); + sList := TStringList.Create; + if tStart < 1 then goto 666; + //read faces + tStart := ParseStartEnd('coordIndex', Str, tEnd); + if (tStart < 1) then goto 666; + sList.DelimitedText := copy(Str,tStart, tEnd-tStart+1); + if (sList.Count) < 3 then goto 666; + n := 0; + nFace := 0; + isQuad := false; + setlength(Faces, kBlockSz); + for i := 0 to (sList.Count-1) do begin + j := StrToIntDef(sList[i],-1); + if j < 0 then begin + n := 0; + continue; + end; + if n = 0 then Faces[nFace].X:=j; + if n = 1 then Faces[nFace].Y:=j; + if n = 2 then begin + Faces[nFace].Z:=j; + nFace := nFace + 1; + if (nFace >= length(Faces)) then + setLength(Faces, nFace+ kBlockSz); + end; + if n > 2 then + isQuad := true; + n := n + 1; + end; + if isQuad then showmessage('Only designed to read triangular VRML meshes.'); + setLength(Faces, nFace); + //read vertices + tStart := ParseStartEnd('point', Str, tEnd); + if (tStart < 1) then goto 666; + sList.DelimitedText := copy(Str,tStart, tEnd-tStart+1); + if (sList.Count) < 3 then goto 666; + n := 0; + nVert := 0; + setlength(Vertices, kBlockSz); + for i := 0 to (sList.Count-1) do begin + fl := StrToFloatDef(sList[i],kNaN); + if specialsingle(fl) then + continue; + if n = 0 then Vertices[nVert].X:=fl; + if n = 1 then Vertices[nVert].Y:=fl; + if n = 2 then begin + Vertices[nVert].Z:=fl; + nVert := nVert + 1; + if (nVert >= length(Vertices)) then + setLength(Vertices, nVert+ kBlockSz); + n := -1; + end; + n := n + 1; + end; + setLength(Vertices, nVert); + //read colors + tStart := ParseStartEnd('color', Str, tEnd); + if (tStart < 1) then goto 555; + sList.DelimitedText := copy(Str,tStart, tEnd-tStart+1); + if (sList.Count) < (3 * nVert) then goto 555; + n := 0; + setlength(vertexRGBA, nVert); + nVert := 0; + for i := 0 to (sList.Count-1) do begin + fl := StrToFloatDef(sList[i],kNaN); + if specialsingle(fl) then + continue; + j := round(fl * 255); + if (j < 0) or (j > 255) then continue; + if n = 0 then vertexRGBA[nVert].R:=j; + if n = 1 then vertexRGBA[nVert].G:=j; + if n = 2 then begin + vertexRGBA[nVert].B:=j; + vertexRGBA[nVert].A:=255; + nVert := nVert + 1; + n := -1; + end; + n := n + 1; + end; + 555: //no color + result := true; + 666: + sList.free; + if not result then begin + showmessage('Unable to parse VRML image. You may want to convert this to a simpler format.'); + end; end; procedure TMesh.SaveOverlay(const FileName: string; OverlayIndex: integer); @@ -3227,15 +3378,15 @@ procedure TMesh.SaveOverlay(const FileName: string; OverlayIndex: integer); //setlength(i,0); setlength(c,0); if (length(overlay[OverlayIndex].faces) > 0) and (length(overlay[OverlayIndex].vertices) > 0) then - SaveMz3Core(FileName, overlay[OverlayIndex].Faces, overlay[OverlayIndex].Vertices, c, overlay[OverlayIndex].intensity) + SaveMz3(FileName, overlay[OverlayIndex].Faces, overlay[OverlayIndex].Vertices, c, overlay[OverlayIndex].intensity) else if length(overlay[OverlayIndex].intensity) > 1 then begin setlength(f,0); setlength(v,0); - SaveMz3Core(FileName, f,v,c, overlay[OverlayIndex].intensity); + SaveMz3(FileName, f,v,c, overlay[OverlayIndex].intensity); end; end; -procedure TMesh.SaveGii(const FileName: string); +procedure SaveGii(const FileName: string; Faces: TFaces; Vertices: TVertices; vertexRGBA: TVertexRGBA); var f : TextFile; FileNameGii : string; @@ -3257,8 +3408,7 @@ procedure TMesh.SaveGii(const FileName: string); if (length(faces) < 1) or (length(vertices) < 3) then begin showmessage('You need to open a mesh before you can save it'); exit; - end; - if not CheckMesh then exit; + end; //if not CheckMesh then exit; FileMode := fmOpenWrite; //FileMode := fmOpenRead; AssignFile(f, FileNameGii); ReWrite(f); @@ -4212,6 +4362,11 @@ function LoadMz3Core(const FileName: string; var Faces: TFaces; var Vertices: TV for i := 0 to (nVert -1) do if vertexAtlas[i] > atlasMaxIndex then atlasMaxIndex := vertexAtlas[i]; + (*atlasMinxIndex := vertexAtlas[0]; + for i := 0 to (nVert -1) do + if vertexAtlas[i] < atlasMinxIndex then + atlasMinxIndex := vertexAtlas[i]; + GLForm1.Caption := format('%d..%d',[atlasMinxIndex,atlasMaxIndex]);*) end; result := true; 666 : @@ -6352,8 +6507,10 @@ function TMesh.LoadFromFile(const FileName: string): boolean; LoadAsc_Srf(Filename); if (ext = '.OBJ') then LoadObj(Filename); + if (ext = '.WRL') then + LoadVrml(Filename); if (ext = '.WFR') then - LoadWfr(Filename); + LoadWfr(Filename); if length(faces) < 1 then begin//not yet loaded - see if it is freesurfer format (often saved without filename extension) LoadPial(Filename); if length(faces) > 0 then @@ -6508,6 +6665,43 @@ function TMesh.LoadGcs(const FileName: string): boolean; result := true; end; // LoadGcs() +function asInt(i : TRGBA): longint; +type + swaptype = packed record + case byte of + 0:(Lng : longint); + 1:(rgba : TRGBA); + end; + swaptypep = ^swaptype; +var + inguy:swaptypep; +begin + {$IFNDEF ENDIAN_LITTLE} + please check byte order on big endian systems! + {$ENDIF} + inguy := @i; //assign address of s to inguy + result := inguy^.Lng; +end; // asRGBA() + +function asIntA0(i : TRGBA): longint; +type + swaptype = packed record + case byte of + 0:(Lng : longint); + 1:(rgba : TRGBA); + end; + swaptypep = ^swaptype; +var + inguy:swaptypep; +begin + {$IFNDEF ENDIAN_LITTLE} + please check byte order on big endian systems! + {$ENDIF} + inguy := @i; //assign address of s to inguy + inguy^.rgba.a := 0; + result := inguy^.Lng; +end; // asRGBA() + function asRGBA(i : longint): TRGBA; type swaptype = packed record @@ -6526,16 +6720,132 @@ function asRGBA(i : longint): TRGBA; result := inguy^.rgba; end; // asRGBA() -(*function swizzleRGBA(rgba: TRGBA): TRGBA; + +{$DEFINE ANNOTasTEMPLATE} +{$IFDEF ANNOTasTEMPLATE} +function TMesh.LoadAnnot(const FileName: string): boolean; +//freesurfer Annotation file provides vertex colors +// https://surfer.nmr.mgh.harvard.edu/fswiki/LabelsClutsAnnotationFiles +label + 666; +var + f: File; + v,i,j,sz: integer; + nVert: LongWord; + labelRGBA: TRGBA; + num_entries, tag, ctabversion, maxstruc, labelNum, len, r,g,b,a: int32; + idx: array of LongInt; begin - result := rgba; - if rgba.b > 128 then begin - result.r := rgba.b; - result.b := rgba.r; - result.g := 12; + result := false; + AssignFile(f, FileName); + FileMode := fmOpenRead; + Reset(f,1); + sz := FileSize(f); + if (sz < 20) or (length(vertices) < 3) then //not a valid annot file + goto 666; + blockread(f, nVert, 4); + {$IFDEF ENDIAN_LITTLE} //FreeSurfer files ALWAYS big endian + SwapLongWord(nVert); + {$ENDIF} + if (nVert <> length(vertices)) then begin + showmessage('Annot file does not match currently loaded image: it describes '+inttostr(nVert)+' vertices but the mesh has '+inttostr(length(vertices))); + goto 666; end; -end;*) + if (sz < (4+ (nVert * 8) )) then begin //too small + showmessage('Annot file corrupted: file is smaller than expected'); + goto 666; + end; + setlength(idx, 2*nVert); + blockread(f, idx[0], 2*4*nVert); + {$IFDEF ENDIAN_LITTLE} //FreeSurfer files ALWAYS big endian + for i := 0 to ((2*nVert)-1) do + SwapLongInt(idx[i]); + {$ENDIF} + setlength(vertexRGBA, nVert); + j := 0; + for i := 0 to (nVert-1) do begin + v := idx[j]; + if (v < 0) or (v >= nVert) then begin + showmessage(inttostr(i)+'/'+inttostr(nVert)+'Index out of range '+inttostr(v)); + goto 666; + end; + vertexRGBA[v] := asRGBA(idx[j+1]); + j := j + 2; + end; + //next ATLAS data + //GLForm1.Caption := (format('%d %d', [filepos(f),sz])); + if ((filepos(f)+128) < sz) then begin + blockread(f, tag, 4); + blockread(f, ctabversion, 4); + blockread(f, maxstruc, 4); + blockread(f, len, 4); + {$IFDEF ENDIAN_LITTLE} + SwapLongInt(ctabversion); + SwapLongInt(maxstruc); + SwapLongInt(len); + {$ENDIF} + //GLForm1.Caption := (format('%d %d %d', [sz, filepos(f),len])); + if (ctabversion >= 0) or ((filepos(f)+len+8) > sz) then begin + showmessage('Undocumented old ctabversion not supported'); + goto 666; + end; + seek(f, filepos(f)+len); + blockread(f, num_entries, 4); + {$IFDEF ENDIAN_LITTLE} + SwapLongInt(num_entries); + {$ENDIF} + if (num_entries < 1) then begin + showmessage('Annot file does not make sense'); + goto 666; + end; + //GLForm1.Caption := (format('%d %d %d', [num_entries, filepos(f),len])); + setlength(vertexAtlas, nVert); + for v := 0 to (nVert -1) do + vertexAtlas[v] := -1; + for i := 0 to (num_entries-1) do begin + blockread(f, labelNum, 4); + blockread(f, len, 4); + {$IFDEF ENDIAN_LITTLE} + SwapLongInt(labelNum); + SwapLongInt(len); + {$ENDIF} + seek(f, filepos(f)+len); + blockread(f, r, 4); + blockread(f, g, 4); + blockread(f, b, 4); + blockread(f, a, 4); + {$IFDEF ENDIAN_LITTLE} + SwapLongInt(r); + SwapLongInt(g); + SwapLongInt(b); + SwapLongInt(a); + {$ENDIF} + labelNum := labelNum + 1; //index from 1 not zero! + //GLForm1.ScriptOutputMemo.Lines.Add(format('%d rgba %d %d %d %d',[labelNum, r, g, b, a])); + if i = 0 then atlasMaxIndex := labelNum; + if labelNum > atlasMaxIndex then atlasMaxIndex := labelNum; + labelRGBA := RGBA(r,g,b,a); + //we need to use the RGB not RGBA value to infer label number! + // mri_annotation2label can use a different alpha for label and vertex! + for v := 0 to (nVert -1) do + if asIntA0(labelRGBA) = asIntA0(vertexRGBA[v]) then + vertexAtlas[v] := labelNum; + end; + end; + for v := 0 to (nVert -1) do + if (vertexAtlas[v] < 0) then begin + GLForm1.ScriptOutputMemo.Lines.Add(format('annot file ERROR %d rgba %d %d %d %d',[666, vertexRGBA[v].r, vertexRGBA[v].g, vertexRGBA[v].b, vertexRGBA[v].a])); + break; + end; + result := true; + CloseFile(f); + exit; + 666: + CloseFile(f); + setlength(vertexRGBA, 0); +end; // LoadAnnot() +{$ELSE} function TMesh.LoadAnnot(const FileName: string): boolean; //freesurfer Annotation file provides vertex colors // https://surfer.nmr.mgh.harvard.edu/fswiki/LabelsClutsAnnotationFiles @@ -6591,6 +6901,7 @@ function TMesh.LoadAnnot(const FileName: string): boolean; CloseFile(f); setlength(vertexRGBA, 0); end; // LoadAnnot() +{$ENDIF} procedure RemoveNaNs(var intensity: TFloats; var faces : TFaces; var vertices: TVertices; kUndefined: single); //remove all faces and vertices where intensity = kUndefined @@ -7267,6 +7578,58 @@ function TMesh.SetLutIndex(layer:integer): integer; result := 0; end; +procedure SaveMeshCore(const FileName: string; Faces: TFaces; Vertices: TVertices; vertexRGBA: TVertexRGBA; intensity: TFloats); +var + x: string; +begin + x := UpperCase(ExtractFileExt(Filename)); + if (x = '.WRL') then + SaveVrml(FileName, Faces, Vertices, vertexRGBA) + else if (x = '.MZ3') then + SaveMz3(FileName, Faces, Vertices, vertexRGBA, intensity) + else if (x = '.GII') then + SaveGii(Filename, Faces, Vertices, vertexRGBA) + else if (x = '.PLY') then + SavePly(Filename, Faces, Vertices, vertexRGBA) + else if (x = '.OBJ') then + SaveObj(Filename, Faces, Vertices, vertexRGBA) + else begin + SaveObj(Filename+'.obj', Faces, Vertices, vertexRGBA); + //SaveMz3(Filename+'.mz3', Faces, Vertices, vertexRGBA); + end; +end; + +procedure TMesh.SaveMesh(const FileName: string; MeshColor: TColor = clWhite); +var + intensities: TFloats; + i: integer; + isOverlayPainting : boolean = false; + vRGBA: TVertexRGBA; + clr: TRGBA; +begin + if (length(Faces) < 1) or (length(Vertices) < 3) then exit; + setlength(intensities,0); + //showmessage(format ('%dx%d', [length(vertexRGBA), Self.OpenOverlays])); + if (length(vertexRGBA) > 0) or (Self.OpenOverlays < 1) then begin + SaveMeshCore(Filename, Faces,Vertices, vertexRGBA, intensities); + exit; + end; + for i := OpenOverlays downto 1 do + if (overlay[i].LUTvisible <> kLUTinvisible) and (length(overlay[i].intensity) >= length(Vertices)) then + isOverlayPainting := true; + if (not isOverlayPainting) then begin + SaveMeshCore(Filename, Faces,Vertices, vertexRGBA, intensities); + exit; + end; + clr := asRGBA(MeshColor); + vRGBA := nil; + //overlay painting + BuildListCore(Clr, Faces, Vertices, vRGBA, nil, false); + SaveMeshCore(Filename, Faces,Vertices, vRGBA, intensities); + vRGBA := nil; +end; + + function TMesh.LoadOverlay(const FileName: string; lLoadSmooth: boolean): boolean; //; isSmooth: boolean var i, nOverlays: integer; diff --git a/meshify.pas b/meshify.pas index 2c35eb3..e80ea6e 100755 --- a/meshify.pas +++ b/meshify.pas @@ -141,7 +141,7 @@ function MeshPref(min, max: single; out Thresh, Decim: single; out SmoothStyle, MinClusterVoxEdit, ThreshEdit: TEdit; {$ENDIF} DecimateEdit: TSpinEdit; - SmoothCombo: TComboBox; + SmoothCombo, SingleCombo: TComboBox; begin PrefForm:=TForm.Create(nil); //PrefForm.SetBounds(100, 100, 510, 212); @@ -271,9 +271,6 @@ function MeshPref(min, max: single; out Thresh, Decim: single; out SmoothStyle, DecimateEdit.Parent:=PrefForm; //Smooth SmoothCombo:=TComboBox.create(PrefForm); - //SmoothCombo.Left := 8; - //SmoothCombo.Top := 132; - //SmoothCombo.Width := PrefForm.Width -16; SmoothCombo.Items.Add('Raw (Jagged)'); SmoothCombo.Items.Add('Masked smooth (Smooth except at brain mask)'); SmoothCombo.Items.Add('Smooth (Eroded by brain mask)'); @@ -292,6 +289,25 @@ function MeshPref(min, max: single; out Thresh, Decim: single; out SmoothStyle, SmoothCombo.BorderSpacing.Right := 6; SmoothCombo.Parent:=PrefForm; SmoothCombo.Parent:=PrefForm; + //SingleCombo + SingleCombo:=TComboBox.create(PrefForm); + SingleCombo.Items.Add('Multiple objects'); + SingleCombo.Items.Add('Largest object only'); + SingleCombo.ItemIndex:= 0; + SingleCombo.Style := csDropDownList; + SingleCombo.AutoSize := true; + SingleCombo.Constraints.MinWidth:= 400; + SingleCombo.AnchorSide[akTop].Side := asrBottom; + SingleCombo.AnchorSide[akTop].Control := DecimateEdit; + SingleCombo.BorderSpacing.Top := 6; + SingleCombo.AnchorSide[akLeft].Side := asrLeft; + SingleCombo.AnchorSide[akLeft].Control := PrefForm; + SingleCombo.BorderSpacing.Left := 6; + SingleCombo.AnchorSide[akRight].Side := asrRight; + SingleCombo.AnchorSide[akRight].Control := PrefForm; + SingleCombo.BorderSpacing.Right := 6; + SingleCombo.Parent:=PrefForm; + SingleCombo.Parent:=PrefForm; //OK button OkBtn:=TButton.create(PrefForm); OkBtn.Caption:='OK'; @@ -300,7 +316,7 @@ function MeshPref(min, max: single; out Thresh, Decim: single; out SmoothStyle, //OkBtn.Left := PrefForm.Width - OkBtn.Width - 8; OkBtn.AutoSize := true; OkBtn.AnchorSide[akTop].Side := asrBottom; - OkBtn.AnchorSide[akTop].Control := SmoothCombo; + OkBtn.AnchorSide[akTop].Control := SingleCombo; OkBtn.BorderSpacing.Top := 6; OkBtn.AnchorSide[akLeft].Side := asrCenter; OkBtn.AnchorSide[akLeft].Control := PrefForm; @@ -317,6 +333,8 @@ function MeshPref(min, max: single; out Thresh, Decim: single; out SmoothStyle, MinClusterVox := StrToIntDef(MinClusterVoxEdit.Caption, 1); Decim := DecimateEdit.value/100.0; SmoothStyle := SmoothCombo.ItemIndex; + if (SingleCombo.ItemIndex = 1) then + MinClusterVox := -1; result := PrefForm.ModalResult = mrOK; FreeAndNil(PrefForm); if (Decim <= 0.0) then @@ -371,6 +389,112 @@ function Nii2MeshCore(niiname, meshname: string; threshold, decimateFrac: single lMesh.Free(); end; //Nii2MeshCore() +(*Type + TUInt8s = array of uint8; + +procedure PreserveLargestCluster(var lImg: TUInt8s; Xi,Yi,Zi: integer; lClusterValue,ValueForSmallClusters: byte); +var + mx, i, j, XY, XYZ, qlo, qhi: integer; + qimg, img32: TInt32s; +procedure checkPixel(vxl: integer); +begin + if img32[vxl] <> -1 then exit; //already found or not a target + qhi := qhi + 1; + img32[vxl] := 1; //found + qimg[qhi] := vxl; //location +end;//nested checkPixel() +procedure retirePixel(); +var + vxl: integer; +begin + vxl := qimg[qlo]; + checkPixel(vxl-1); + checkPixel(vxl+1); + checkPixel(vxl-Xi); + checkPixel(vxl+Xi); + checkPixel(vxl-XY); + checkPixel(vxl+XY); + qlo := qlo + 1; +end;//nested retirePixel() +begin //main PreserveLargestCluster() + if (Zi < 1) then exit; + XY := Xi * Yi; + XYZ := XY * Zi; + setlength(img32, XYZ); + setlength(qimg, XYZ); + //set target voxels + for i := 0 to (XYZ-1) do begin + img32[i] := 0; + if lImg[i] = lClusterValue then + img32[i] := -1; + end; + //clear bottom and top slices + for i := 0 to (XY-1) do + img32[i] := 0; + for i := (XYZ-1-XY) to (XYZ-1) do + img32[i] := 0; + //now seed each voxel + mx := 0; + for i := (XY) to (XYZ-1-XY) do begin + if (img32[i] < 0) then begin //voxels not yet part of any region + qlo := 0; + qhi := -1; + checkPixel(i); + while qlo <= qhi do + retirePixel(); + for j := 0 to qhi do + img32[qimg[j]] := qhi + 1; + if (qhi+1) > mx then mx := qhi + 1; + end; + end; + if mx < 2 then begin + qimg := nil; + img32 := nil; + exit; + end; + //delete voxels not part of largest cluster + for i := 0 to (XYZ-1) do + if img32[i] <> mx then + img32[i] := 0; + //recover bottom and top slices + for i := 0 to (XY-1) do + if (img32[i+XY] = mx) then + img32[i] := mx; + for i := (XYZ-1-XY) to (XYZ-1) do + if (img32[i-XY] = mx) then + img32[i] := mx; + //apply filter to input image + for i := 0 to (XYZ-1) do + if img32[i] = 0 then + lImg[i] := 0; + qimg := nil; + img32 := nil; +end;// PreserveLargestCluster() + +procedure PreserveLargestObjectOnly(var nii: TNIFTI; lThresh: single); +var + i,vx: integer; + vol8: TUInt8s; + halfThresh : single; +begin + vx := nii.hdr.dim[1] * nii.hdr.dim[2] * nii.hdr.dim[3]; + if (vx < 1) then exit; + SetLength(vol8, vx); + for i := 0 to (vx-1) do begin + vol8[i] := 0; + if (nii.img[i] >= lThresh) then + vol8[i] := 255; + end; + PreserveLargestCluster(vol8, nii.hdr.dim[1], nii.hdr.dim[2], nii.hdr.dim[3],255,0 ); + halfThresh := lThresh * 0.5; + for i := 0 to (vx-1) do begin + if ((nii.img[i] >= lThresh) and (vol8[i] < 255)) then + nii.img[i] := halfThresh; //voxel does not survive + end; + SetLength(vol8, 0); + +end;*) + function Nii2Mesh(const FileName: string): boolean; var nii: TNIFTI; @@ -416,8 +540,10 @@ function Nii2Mesh(const FileName: string): boolean; nii.Free; exit; end; - IsoSurfaceEx := TIsoSurfaceExtractor.Create(nii.hdr.dim[1], nii.hdr.dim[2],nii.hdr.dim[3], nii.img); - lMesh := TMesh.Create; + //if singleObject then + // PreserveLargestObjectOnly(nii, lThresh); + IsoSurfaceEx := TIsoSurfaceExtractor.Create(nii.hdr.dim[1], nii.hdr.dim[2],nii.hdr.dim[3], nii.img); + lMesh := TMesh.Create; //IsoSurfaceEx.MarchingTetrahedra(Threshold,lMesh.vertices, lMesh.faces); IsoSurfaceEx.MarchingCubes(lThresh,lMesh.vertices, lMesh.faces); //showmessage( Format('converted= %d f= %d', [length(lMesh.vertices), length(lMesh.faces)])); @@ -469,17 +595,14 @@ procedure save2Mesh(var nii: TNIfTI; lDecimate, lThresh: single; img: TImgScaled //GLForm1.SaveMesh(lMesh, false); if gPrefs.SaveAsFormat = 4 then begin GLForm1.SaveMeshDialog.Filename := changefileext(GLForm1.SaveMeshDialog.Filename, '.ply'); - lMesh.SavePly(GLForm1.SaveMeshDialog.Filename); end else if gPrefs.SaveAsFormat = 0 then begin GLForm1.SaveMeshDialog.Filename := changefileext(GLForm1.SaveMeshDialog.Filename, '.obj'); - lMesh.SaveObj(GLForm1.SaveMeshDialog.Filename); end else if gPrefs.SaveAsFormat = 1 then begin GLForm1.SaveMeshDialog.Filename := changefileext(GLForm1.SaveMeshDialog.Filename, '.gii'); - lMesh.SaveGii(GLForm1.SaveMeshDialog.Filename); end else begin GLForm1.SaveMeshDialog.Filename := changefileext(GLForm1.SaveMeshDialog.Filename, '.mz3'); - lMesh.SaveMz3(GLForm1.SaveMeshDialog.Filename); end; +lMesh.SaveMesh(GLForm1.SaveMeshDialog.Filename); lMesh.Free(); end; diff --git a/prefs.pas b/prefs.pas index 2bb33a1..8e21084 100755 --- a/prefs.pas +++ b/prefs.pas @@ -18,7 +18,7 @@ interface type TMRU = array [1..knMRU] of string; - TPrefs = record + TPrefs = record //ObjectBasedClipPlane, SmoothVoxelwiseData, OverlayClip, StartupScript, SupportBetterRenderQuality, AdditiveOverlay,Perspective, OrientCube, MultiSample, BlackDefaultBackground, Colorbar,TracksAreTubes, ScreenCaptureTransparentBackground,LoadTrackOnLaunch,ColorBarPrecedenceTracksNotOverlays, @@ -345,6 +345,7 @@ procedure SetDefaultPrefs (var lPrefs: TPrefs; lEverything, askUserIfMissing: b BlackDefaultBackground := false; OverlayClip := false; StartupScript := false; + //ObjectBasedClipPlane := false; ScreenCaptureTransparentBackground := true; SmoothVoxelwiseData := true; PrevTrackname := ''; @@ -609,6 +610,7 @@ function IniFile(lRead: boolean; lFilename: string; var lPrefs: TPrefs): boolean IniBool(lRead,lIniFile, 'Perspective',lPrefs.Perspective); IniBool(lRead,lIniFile, 'AdditiveOverlay',lPrefs.AdditiveOverlay); IniBool(lRead,lIniFile, 'StartupScript',lPrefs.StartupScript); + //IniBool(lRead,lIniFile, 'ObjectBasedClipPlane',lPrefs.ObjectBasedClipPlane); {$IFDEF LCLCocoa} IniBool(lRead,lIniFile, 'RetinaDisplay',lPrefs.RetinaDisplay); IniBool(lRead,lIniFile, 'DarkMode',lPrefs.DarkMode); diff --git a/surfice.app/Contents/Resources/script/startup.gls b/surfice.app/Contents/Resources/script/startup.gls index 039c688..c74c06d 100755 --- a/surfice.app/Contents/Resources/script/startup.gls +++ b/surfice.app/Contents/Resources/script/startup.gls @@ -5,10 +5,10 @@ begin overlayload('motor_4t95vol.nii.gz'); overlayminmax(1,2,12); overlayload('motor_4t95vol.nii.gz'); - overlayminmax(2,-1,-2); + overlayminmax(2,2,12); + overlaycolorname(2, 'red-yellow'); colorbarvisible(true); overlaytransparencyonbackground(25); meshcurv(); -wait(0); end. diff --git a/surfice.app/Contents/Resources/script/startup.py b/surfice.app/Contents/Resources/script/startupx.py similarity index 100% rename from surfice.app/Contents/Resources/script/startup.py rename to surfice.app/Contents/Resources/script/startupx.py diff --git a/surfice.lps b/surfice.lps index 2e81b91..def64ed 100644 --- a/surfice.lps +++ b/surfice.lps @@ -3,7 +3,7 @@ - + @@ -18,8 +18,9 @@ - - + + + @@ -29,7 +30,7 @@ - + @@ -39,33 +40,31 @@ - + - - - + + + - - - + + - + - - + @@ -73,7 +72,7 @@ - + @@ -81,7 +80,7 @@ - + @@ -89,7 +88,7 @@ - + @@ -97,7 +96,7 @@ - + @@ -105,7 +104,7 @@ - + @@ -113,7 +112,7 @@ - + @@ -121,14 +120,14 @@ - + - - - + + + @@ -138,15 +137,15 @@ - + - - - + + + @@ -154,14 +153,13 @@ - + - - - + + @@ -171,14 +169,16 @@ - + - - - + + + + + @@ -187,13 +187,13 @@ - + - - + + @@ -203,7 +203,7 @@ - + @@ -211,15 +211,16 @@ - + - - - - + + + + + @@ -228,7 +229,7 @@ - + @@ -236,14 +237,15 @@ - + - - - - + + + + + @@ -251,15 +253,14 @@ - + - + - - + @@ -267,17 +268,16 @@ - + - - + + - - + @@ -285,21 +285,21 @@ - + - + - + @@ -307,192 +307,208 @@ - + - - - + + + - + - - - - - - - - - - + + + - + - - + - - + + - + - - + - - + + - + - - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + diff --git a/xscriptengine.lfm b/xscriptengine.lfm index c251253..39be1e2 100755 --- a/xscriptengine.lfm +++ b/xscriptengine.lfm @@ -361,7 +361,7 @@ object ScriptForm: TScriptForm object atlasgray1: TMenuItem Tag = 1218 Caption = 'atlasgray' - Hint = 'atlasgray (overlay: integer; const filt: array of integer) Gray atlas areas. For example atlasgray(0,[3,7]) will gray-out areas 3 and 7 of the background atlas. On the other hand, atlashide(1,[2,5]) will gray-out areas 2 and 5 of the first overlay image. For example atlashide(0,[3,7]) will gray the areas 3 and 7 of the background atlas. On the other hand, atlashide(1,[2,5]) will gray areas 2 and 5 of the first overlay image.' + Hint = 'atlasgray (overlay: integer; const filt: array of integer) Gray atlas areas. For example atlasgray(0,[3,7]) will gray-out areas 3 and 7 of the background atlas. On the other hand, atlasgray(1,[2,5]) will gray-out areas 2 and 5 of the first overlay image. For example atlashide(0,[3,7]) will gray the areas 3 and 7 of the background atlas. On the other hand, atlashide(1,[2,5]) will gray areas 2 and 5 of the first overlay image.' OnClick = InsertCommand end object atlashide1: TMenuItem