'******************************************************************************************* 
'   program demonstrating making a virtual volumn of MRI scan 
'    by loading many bitmap files onto 3D planes 
' 
' Copyright John Kelly <klaffky2825...> 
'****************************************************************************************** 
' 
' 
$OPTIMIZE ON
$TYPECHECK ON
$ESCAPECHARS OFF
$INCLUDE <RAPIDQ2.INC>
$INCLUDE <RapidQ_D3D.INC>       'constants 
$OPTION ICON "MRIview.ico"
$RESOURCE App_ICO AS "MRIview.ico"
Application.IcoHandle = App_ICO

DECLARE FUNCTION LoadStandardImageName(indx as integer) AS STRING
DECLARE SUB Cancel_Load
DECLARE SUB Center_Terrain
DECLARE SUB CheckSectionDepth
DECLARE SUB CloseApp
DECLARE SUB CloseHelpForm
DECLARE SUB CreateNewMeshBuilders
DECLARE SUB DXInitialize(Sender AS QDXScreen)
DECLARE SUB DXInitializeSurface(Sender AS QDXScreen)
DECLARE SUB DXMouseDown(Button AS INTEGER, x AS INTEGER, y AS INTEGER)
DECLARE SUB DXMouseUp(Button AS INTEGER, x AS INTEGER, y AS INTEGER)
DECLARE SUB Dec_Speed
DECLARE SUB DeleteAllVisuals
DECLARE SUB FormKeyPress(TheKey AS WORD, Shift AS INTEGER)
DECLARE SUB Inc_Speed
DECLARE SUB Load_Background
DECLARE SUB Load_MRI_Images
DECLARE SUB MRI_Texture_Detail
DECLARE SUB MakeMRIVolumesFromImage
DECLARE SUB MakeRuler
DECLARE SUB Make_MRI_Planes
DECLARE SUB Make_MRI_Volumes
DECLARE SUB MeshWhiteBackground
DECLARE SUB RenderLoop
DECLARE SUB ScaleTheObject (Sender AS QBUTTON)
DECLARE SUB ScaleXModel
DECLARE SUB SegmentAllMRIimages
DECLARE SUB SegmentBMP (TheBitMap AS QBITMAP, MaskColor1 AS INTEGER, MaskColor2 AS INTEGER, Blend AS BYTE)
DECLARE SUB SetMeshExponentAlpha
DECLARE SUB SetMeshFixedAlpha(indx AS SINGLE)
DECLARE SUB SetPerspectiveView
DECLARE SUB SetThreshold
DECLARE SUB ShowAllVisuals
DECLARE SUB ShowHelpWindow
DECLARE SUB ShowMeshExponentAlpha(indx AS SINGLE)
DECLARE SUB StopAllActions
DECLARE SUB TogglePerspectiveView
DECLARE SUB Toggle_Flying
DECLARE SUB Toggle_KnifeDepth
DECLARE SUB Toggle_Move_Light
DECLARE SUB Toggle_Oscill
DECLARE SUB Toggle_Ruler_On
DECLARE SUB Toggle_Ruler_Visible
DECLARE SUB UpdateMRIPlanes
DECLARE SUB UpdateScaleScrollBar
DECLARE SUB VaryAlphaBlend
DECLARE SUB WrapTexutureToMesh(TheMesh AS QD3DMeshBuilder)



' **************    Constants  ************************* 

CONST Max_MRIimages         AS INTEGER = 27         'Maximum images and meshbuilders 
CONST null              AS STRING = ""      'for standards 

TYPE Rectf
    Top         AS SINGLE
    Bottom  AS SINGLE
    Front   AS SINGLE
    Back    AS SINGLE
END TYPE




' **************    RapidQ - Specific Objects  ************************* 
DIM XmodelMesh(1 to Max_MRIimages)_
                    AS QD3DMeshBuilder      'models load on meshbuilder, can use this to attach to worldFrame 
DIM WorldFrame      AS QD3DFrame            'root (parent) frame all 3d objects moving together attached to this frame 
DIM LightFrame      AS QD3DFrame            'use this for alternate light to be placed on a frame 
DIM RulerFrame      AS QD3DFrame            'so you can move the ruler around 
DIM TerrMesh        AS QD3DMeshBuilder      'mesh builder to create a mesh out of the face 
DIM RulerMesh       AS QD3DMeshBuilder      'build a flat plane on the Ruler 
DIM wrap            AS QD3DWRAP                 'object to hold texture wrapping information 
DIM HeightMap       AS QBITMAP              'bitmap with pixel values the determine the height of our scene 
DIM OpenDialog      AS QOPENDIALOG          'open file dialog 

DIM NumMRIs             AS INTEGER: NumMRIs = 0         'keep track of which model 
DIM Mod_Scale       AS SINGLE : Mod_Scale = 1   'Modify Scaling of x models 
DIM Wrap_Scale      AS DOUBLE : Wrap_Scale = 0.5'texture wrapping scale 
DIM Max_X           AS DOUBLE                   'dimensions of scene mesh 
DIM Min_X           AS DOUBLE
DIM Max_Z           AS DOUBLE
DIM Min_Z           AS DOUBLE
DIM Max             AS Q3DVector            'coordinates of max/min 
DIM Min             AS Q3DVector
    Max.x = 10
    Max.y = 10
    Max.z = 10
    Min.x = -10
    Min.y = -10
    Min.z = -10
'   *****  define the limits of the volume  ******* 
DIM Depth           AS Rectf
    Depth.Top       =255
    Depth.Bottom    = 0
    Depth.Front         = 1
    Depth.Back      = Max_MRIimages
DIM ActiveView      AS Rectf
ActiveView.Top      =255
ActiveView.Bottom   = 0
ActiveView.Front    = 1
ActiveView.Back         = Max_MRIimages

DIM MinObj          AS Q3DVector
DIM MaxObj          AS Q3DVector                    'find out the size of a meshbuilder object 
DIM yScale          AS DOUBLE                       'scale terrain for height 
DIM Mid_X           AS INTEGER
DIM Mid_Y           AS INTEGER                      'middle of the Screen 
DIM Cam_Orient      AS Q3DVector                    'fix bug 
DIM Cam_Pos         AS Q3DVector
DIM Omega           AS SINGLE: Omega = -3.1416          '*2     'for camera rotation = pi/2 or 90 degrees 
DIM Old_mouseX      AS INTEGER                      'keep track of mouse 
DIM Old_mouseY      AS INTEGER                      'x, y 
DIM Ruler_On        AS INTEGER: Ruler_On = 0        'move a virtual ruler on the screen 
DIM RulerMoving     AS INTEGER: RulerMoving = 0         'Are we moving the ruler? Turn off rotation 
DIM Ruler_Orient    AS Q3DVector                    'orientation 
DIM Ruler_Pos       AS Q3DVector                    'position 
DIM RulerNotMade    AS INTEGER: RulerNotMade = True     'made it yet? 
DIM Step_size       AS SINGLE:  Step_size = 0.3         'size thickness through meshbuilders 
DIM Psuedo_Color    AS INTEGER: Psuedo_Color = 1    'toggle  mesh face fake color 
DIM CancelTerrLoad  AS INTEGER

DIM Fly_On          AS INTEGER: Fly_on = 0          'don't fly just yet 
DIM Knife_On        AS INTEGER: Knife_On = 0        'let's slice off/on MRI images 
DIM Oscill_On       AS INTEGER: Oscill_On = 1       'toggle simulation of oscillating camera 
DIM Oscill_Tick     AS INTEGER                      'keep track of time tick for oscillating camera 
DIM Oscill_Time     AS INTEGER: Oscill_Time = 30    '# of ticks before reversing direction 
DIM The_Speed       AS SINGLE : The_Speed = 0.01    'speed of translation motion 
DIM Knife_Speed     AS SINGLE : Knife_Speed = 1         'speed of knife sectioning 
DIM RotAngl         AS DOUBLE : RotAngl = 0.02      'constant angle of rotation 
DIM Shade_Quality   AS INTEGER: Shade_Quality = D3DRMRENDER_FLAT
DIM Texture_Quality     AS INTEGER: Texture_Quality = D3DRMTEXTURE_LINEAR       'low pass filter 
DIM MeshAlpha       AS SINGLE:  MeshAlpha = 0.8         'alphablend the images 
DIM AlphaExponent   AS SINGLE:  AlphaExponent = 1.0     'alphablend exponent for density 
DIM SegThreshold    AS LONG:    SegThreshold = &HC0C0C0     'mask to threshold MRI images 

DIM MyPath          AS STRING                       'the path this program is running in 
DIM The_MRI_Image   AS STRING                       'current loading MRI image 
DIM Start_MRI_Image AS STRING                       'general string before num indexing 

Start_MRI_image = Command$(0)                                                       'path and name of the application 
'MyPath = LEFT$(Start_MRI_image, LEN(Start_MRI_image) - LEN(Application.ExeName))   'exract path 
CHDIR Application.Path 'MyPath                                                                      'go there in case 
Start_MRI_image = MyPath + "00"                                 'file names =001.bmp to 00nn.bmp 




CREATE Form AS QFormEx
    Caption = "MRI viewer (c) John Kelly"
    Width = 640:    Height = 480:   Center
    OnKeyDown = FormKeyPress
    OnClose = CloseApp
    CREATE MainMenu AS QMAINMENU
        CREATE FileMenu AS QMENUITEM                    '***** FILE  MENUS ******** 
            Caption = "  &File"
        CREATE LoadMRIimagesMnu AS QMENUITEM
                Caption = " &Open MRI images": OnClick = Load_MRI_Images
        END CREATE
        CREATE MakeVolumesFromMRIimagesMnu AS QMENUITEM
                Caption = " &Make volume from MRI images": OnClick = MakeMRIVolumesFromImage
        END CREATE
        CREATE AddXmodelMnu AS QMENUITEM
                Caption = "  &Load images onto Volume Model":   OnClick = Make_MRI_Volumes
        END CREATE
        CREATE ScaleXmodelMnu1 AS QMENUITEM
                Caption = "  &Scale Volume": Enabled = 0:       OnClick = ScaleXModel
        END CREATE
        CREATE ExitMnu AS QMENUITEM
                Caption = "  E&xit":        OnClick = CloseApp
        END CREATE
        END CREATE

        CREATE TextureMenu AS QMENUITEM
            Caption = "  &Images"
        CREATE XModelTexDetailMnu AS QMENUITEM
                Caption = "  &Scale MRI Detail": Enabled = 1: OnClick = MRI_Texture_Detail
        END CREATE
        CREATE SegmentMRIMnu AS QMENUITEM
                Caption = "  Segment by &Grey scale": OnClick = SegmentAllMRIimages
        END CREATE
        CREATE DummyMnu00 AS QMENUITEM
                Caption = "__________________________"
        END CREATE
        CREATE WhiteBckgrdTexMnu AS QMENUITEM
                Caption = "  Psuedo &Color Ruler": Checked = 1:OnClick = MeshWhiteBackground
        END CREATE
        CREATE DummyMnu0 AS QMENUITEM
                Caption = "__________________________"
        END CREATE
        CREATE LoadBackgrdMnu AS QMENUITEM
                Caption = "  Load &Background Image": OnClick = Load_Background
        END CREATE
        CREATE DummyMnu AS QMENUITEM
                Caption = "__________________________"
        END CREATE
        CREATE SetThresholdMnu AS QMENUITEM
                Caption = "Set segmentation threshold": OnClick = SetThreshold
        END CREATE
        END CREATE

        CREATE ViewMenu AS QMENUITEM
            Caption = "  &View"
        CREATE ScaleXmodelMnu2 AS QMENUITEM
                Caption = "  Scale Volume Model": Enabled = 0:  OnClick = ScaleXModel
        END CREATE
        CREATE VaryAlphaMnu AS QMENUITEM
                Caption = "  Vary MRI Transparency": OnClick = VaryAlphaBlend
        END CREATE
        CREATE VaryAlphaExpMnu AS QMENUITEM
                Caption = "  Vary Density of Transparency": OnClick = ShowMeshExponentAlpha
        END CREATE
        CREATE ShowAllVisualsMnu AS QMENUITEM
                Caption = "  Show all MRI planes": Checked = True:  OnClick = ShowAllVisuals
        END CREATE
        CREATE DummyMnu2 AS QMENUITEM
                Caption = "__________________________"
        END CREATE

        CREATE RulerVisibleMnu AS QMENUITEM
                Caption = "  Ruler": Checked = 0: OnClick = Toggle_Ruler_Visible
        END CREATE
        CREATE DummyMnu3 AS QMENUITEM
                Caption = "__________________________"
        END CREATE
        CREATE PerspectiveViewOnMnu AS QMENUITEM
                Caption = " Set Zoom/Perspective with Mouse": Checked = 0: OnClick = TogglePerspectiveView
        END CREATE
        END CREATE

        CREATE SpeedMenu AS QMENUITEM
            Caption = "  &Actions"
        CREATE OscillMnu AS QMENUITEM
                Caption = "Oscillate view       (O)": Checked = Oscill_On: OnClick = Toggle_Oscill
        END CREATE
        CREATE FlyMnu AS QMENUITEM
                Caption = "Mouse moves &Volume  (V)": Checked = Fly_On: OnClick = Toggle_Flying
        END CREATE
        CREATE MoveLightMnu AS QMENUITEM
                Caption = "Mouse moves &MRIs    (M)": Checked = 0: OnClick = Toggle_Move_Light
        END CREATE
        CREATE KnifeDepthMnu AS QMENUITEM
                Caption = "Mouse varies &Depth  (D)": Checked = 0: OnClick = Toggle_KnifeDepth
        END CREATE
        CREATE RulerOnMnu AS QMENUITEM
                Caption = "Mouse varies &Ruler  (R)": Checked = 0: OnClick = Toggle_Ruler_On
        END CREATE
        CREATE IncSpeedMnu AS QMENUITEM
                Caption = "increase speed        +": OnClick = Inc_Speed
        END CREATE
        CREATE DecSpeedMnu AS QMENUITEM
                Caption = "decrease speed        -": OnClick = Dec_Speed
        END CREATE
        CREATE CenterMeMnu AS QMENUITEM
                Caption = "Center in viewport": OnClick = Center_Terrain
        END CREATE
        CREATE StopActionsMnu AS QMENUITEM
                Caption = "Stop Mouse Movements  (Esc)": OnClick = StopAllActions
        END CREATE
        END CREATE
    END CREATE

    CREATE DXScreen AS QDXScreen                    'really a direct draw surface? 
        Init(640,480)
        Align = 5                                   'alClient - need this to center Direct Draw screen onto Form 
        BitCount = 32                               '16,24 bits/pixel use 32 for alpha bending on fast 3d cards 
        Use3D = 1                                   'load Direct3D Retained mode 
        UseHardware = 1                                 '3D accelerated video cards are cheap, get one!! 
        OnInitialize = DXInitialize                     'load your meshs/faces to the frames, set lights, camera 
        OnInitializeSurface = DXInitializeSurface   'screen is ready to be drawn 
        OnMouseDown = DXMouseDown'(Button%, X%, Y%)  
        OnMouseUp = DXMouseUp
    END CREATE
END CREATE


'   generic form for scaling  X models, and other objects 
'--------------------------------------------------------- 
CREATE ScaleForm AS QFORM
    BorderStyle=bsToolWindow
    Width = 160: Height = 430
    Left = 620: Top = 100

    CREATE ScButton1 AS QBUTTON
        Left = 45: Top = 25
        Width = 107: Height = 35
        OnClick = ScaleTheObject
    END CREATE
    CREATE ScButton2 AS QBUTTON
        Left = 45: Top = 310
        Width = 107: Height = 35
        OnClick = ScaleTheObject
    END CREATE
    CREATE ScButton3 AS QBUTTON
        Left = 45: Top = 150: Width = 80: Height = 50
        Enabled = False
        OnClick = ScaleTheObject
    END CREATE
    CREATE ScaleScrollBar as QScrollbar
        Kind=sbVertical
        Kind = 1: Left = 4: Top = 12: Width = 20: Height = 361
        PageSize=.5: SmallChange=1 : LargeChange = 20
        Min = 1: Position = 50      'don't let a zero!, midway of default size 
        OnChange = UpdateScaleScrollBar
    END CREATE
END CREATE

'**************  Alphablend by non-linear exponent ********* 
CREATE AlphaExpDialogForm as qform
    BorderStyle=bsDialog
    Height=165: Width=393: Left=571: Top=120
    create AlphaLabel as QLabel
        Caption="Exponent for transparency (1 = linear)"
        Font.Addstyles fsBold: Font.size=10
        Height=19: Left=6: Top=6: Width=331
    END CREATE
    CREATE AlphaEdit as QEdit
        Height=27: Left=18: Top=36: Width=145
    END CREATE
    CREATE AlphaGoButton as QButton
        Caption="OK"
        Default = True
        Left=300: Top=84
        OnClick = SetMeshExponentAlpha
    END CREATE
END CREATE



'**************  Set threshold level ********* 
CREATE SegThresholdDialogForm as qform
    BorderStyle=bsDialog
    Height=165: Width=393: Left=571: Top=120
    create SegLabel as QLabel
        Caption="Threshold mask value (hex)"
        Font.Addstyles fsBold: Font.size=12
        Height=19: Left=6: Top=6: Width=331
    END CREATE
    CREATE SegEdit as QEdit
        Height=35: Left=18: Top=36: Width=145: Font.Size = 12
    END CREATE
    CREATE segGoButton as QButton
        Caption="OK"
        Default = True
        Left=300: Top=84
        OnClick = SetThreshold
    END CREATE
END CREATE




CREATE WaitForm AS QFORM
    Parent = Form:  Caption = "Calculating surface ..."
    BorderStyle = bsDialog
    Width = 300: Height = 120: Center
    CREATE CancelMyLoad AS QBUTTON
        left = 120: top = 45:   Caption = "Cancel":         OnClick = Cancel_Load
    END CREATE
    CREATE Gauge AS QGauge
            Height = 20: Top = 15: left = 20: Width = 250: max = 256
    END CREATE
END CREATE


SUB Cancel_Load
    CancelTerrLoad = 1
END SUB


' ********************************************** 
' ********************************************** 
' *************** for hlp file ************* 


DIM HelpMenu AS QMENUITEM
    HelpMenu.Caption = "  &Help"
    HelpMenu.OnClick = ShowHelpWindow
    MainMenu.AddItems HelpMenu

$ESCAPECHARS ON
Create HelpForm as qform
    Caption="MRI View (c) John Kelly"
    Color=&H00EEEEEE
    Height=540
    Left=272
    Top=137
    Width=460
    create Button1 as QButton
        Caption="OK"
        Color=&H00EEEEEE
        Font.Addstyles fsBold
        Font.size=12
        Height=35
        Left=157
        Top=440
        Width=90
        OnClick = CloseHelpForm
    end create
    create Label1 as QLabel
        Font.Size = 9
        Caption="Demonstration of 3-D visualization of MRI/CT images"
        Font.Addstyles fsBold
        Height=23
        LabelStyle=lsRecessed
        Left=19
        Top=22
        Width=358
    end create
    create Label2 as QLabel
        Font.Size = 11
        Caption=_
        "Images must be in windows/OS2 bitmap format (.BMP)\n__
        all in a single folder and numbered in sequence e.g.,\n_
        mri1.bmp, mri2.bmp, mri3.bmp ... etc.,\n\n_
        you can use the keyboard for moving the image:\n_
        V - moves the Volume that holds the MRI/CT images\n_
        M - Moves the images within the volume for sectioning\n_
        D - varies view of sections in depth (move the mouse\n_
            up/down to the left or right of MRI volume center)\n_
        also L/R mouse clicks on images perform other actions\n\n_
        This program is for demonstration purposes only.\n_
        Do not use for medical or any other purpose.\n\n_
                  Version 1.0\n_
                copyright May, "2004."
        Height=360
        Left=18
        Top=63
        Width=410
    end create
end create

$ESCAPECHARS OFF

SUB ShowHelpWindow
    HelpForm.Show
END SUB


SUB CloseHelpForm
    HelpForm.Close
END SUB

' ********************************************** 
' ********************************************** 



DIM DXTimer AS QDXTimer                     'regularly update display 
    DXTimer.Enabled = 1
    DXTimer.Interval = 0
    DXTimer.OnTimer = RenderLoop

Form.ShowModal                      'get the program running 

'*********************************************************************** 



SUB DXInitialize(Sender AS QDXScreen)
    DIM Ambient AS QD3DLight            'need at least one light this can be local sub-- we won't modify it 
    DIM Light AS QD3DLight              'will define as point ligth that gives rich illumination to see the walls 
    DIM x as double, y as double, z as double

    Mid_X = Screen.Width/2
    Mid_Y = Screen.Height/2
    DXScreen.CreateFrame(WorldFrame)    'create the terrain frame, all other frames will be attached to it in heirarchy 

''      *********  First we create the lights  ****************** 
    DXScreen.CreateLightRGB(D3DRMLIGHT_AMBIENT, 1, 1, 1, Ambient)       'full on ambient 
    DXScreen.AddLight(Ambient)                                          'ambient light moves with root frame 

'   MakeMRIVolumesFromImage 
    DXScreen.SetTextureQuality(Texture_Quality)
    Make_MRI_planes
    AlphaExponent = 0.7: SetMeshExponentAlpha
    Center_Terrain
END SUB



SUB DXInitializeSurface(Sender AS QDXScreen)
    DxScreen.SetRenderMode(D3DRMRENDERMODE_SORTEDTRANSPARENCY OR D3DRMRENDERMODE_BLENDEDTRANSPARENCY)
END SUB


SUB RenderLoop
    DIM MouseX_Now  AS SINGLE
    DIM MouseY_Now  AS SINGLE
    DIM i           AS INTEGER

    Old_mouseX = Screen.MouseX
    Old_mouseY = Screen.MouseY
    MouseX_Now = Omega * (Old_mouseX - Mid_X)/ Mid_X        'omega sets the sensitivity of the "stick" 
    MouseY_Now = Omega * (Old_mouseY - Mid_Y)/ Mid_Y
    Cam_Orient.dvx = SIN(MouseX_Now)                'These vectors set the orientation of camera z-axis (-1 to 1) 
    Cam_Orient.dvy = COS(MouseY_Now)'               'to set it along the default orientation you would have 
    Cam_Orient.dvz = COS(MouseX_Now)                'x = 1, y = 0, z = 0 
    Cam_Orient.x = 0                                'these three vectors set the camera's y-axis orientation 
    Cam_Orient.y = 1                                'default is x = 0, y = 1, z = 0 
    Cam_Orient.z = 0


    IF Fly_On THEN
        WorldFrame.SetOrientation(Cam_Orient.dvx, 0, Cam_Orient.dvz,_       'x-axis vector 
                                  Cam_Orient.x, Cam_Orient.y, Cam_Orient.z)     'y-axis vector 
        WorldFrame.SetRotation(1,0, 0, MouseY_Now)
        WorldFrame.Move(1)
    ELSE
        WorldFrame.SetRotation(0, 0, 0, 0)
    END IF


    IF Ruler_On THEN
        IF RulerMoving = True THEN
            RulerFrame.SetPosition(Ruler_Pos.x, Ruler_Pos.y, Ruler_Pos.z)
            RulerFrame.SetRotation(0,0,0,0)
        ELSE
            RulerFrame.SetOrientation(Cam_Orient.dvx, 0, Cam_Orient.dvz,_       'x-axis vector 
                                      Cam_Orient.x, Cam_Orient.y, Cam_Orient.z)             'y-axis vector 
            RulerFrame.SetRotation(1,0, 0, MouseY_Now)
            RulerFrame.Move(1)
        END IF
    END IF



    IF Knife_On THEN
        DXScreen.TextOut(10,8,"Move Mouse up/down from center to see sections", &HFFFFFF, 0)
        IF MouseY_Now < -0.2 THEN           'mouse going down 
            IF (Cam_Orient.dvz < 0) THEN    'looking at the back of the scans 
                INC Depth.Back,Knife_Speed
            ELSE                            'looking at the front, mouse down 
                DEC Depth.Front,Knife_Speed
            END IF
            CheckSectionDepth
            UpdateMRIPlanes
            DXScreen.TextOut(8,24,"Front slice: " + STR$(INT(Depth.Front)) + "     ", &HFFFFFF, 0)
            DXScreen.TextOut(8,40,"Back slice: " + STR$(INT(Depth.Back)) + "     ", &HFFFFFF, 0)
            DXScreen.Flip
        END IF

        IF MouseY_Now > 0.2 THEN            'mouse going up 
            IF (Cam_Orient.dvz > 0) THEN    'looking at the front of the scans 
                INC Depth.Front,Knife_Speed
            ELSE                            'looking at the back of scans 
                DEC Depth.Back,Knife_Speed
            END IF
            CheckSectionDepth
            UpdateMRIPlanes
            DXScreen.TextOut(8,24,"Front slice: " + STR$(INT(Depth.Front)) + "     ", &HFFFFFF, 0)
            DXScreen.TextOut(8,40,"Back slice: " + STR$(INT(Depth.Back)) + "     ", &HFFFFFF, 0)
            DXScreen.Flip
        END IF
    END IF


    IF Oscill_On THEN
        INC Oscill_Tick
        WorldFrame.SetRotation(0, 1, 0, RotAngl)        ' rotate about y-axis, Angle of rotation is variable 
        IF Oscill_Tick > Oscill_Time THEN 
            RotAngl = -1* RotAngl : Oscill_Tick = 0
        END IF
    END IF


    IF MoveLightMnu.Checked THEN
        DXScreen.createWrap(D3DRMWRAP_SHEET, _
            Min.x, Min.y, 0,_                       'wrap origin x,y,z 
            0, 0, 1,_                               'z-axis vector 
            0, -1, 0,_                              'y-axis vector 
           Min.x+MouseX_Now, Min.y+MouseY_Now, 0.5/Max.x, 0.5/Max.z, wrap) 'origin u, v, and scale factor u, v 
        FOR i = 1 TO NumMRIs
            wrap.apply(XmodelMesh(i))
        Next i
    END IF

    IF PerspectiveViewOnMnu.Checked THEN SetPerspectiveView

    DXScreen.Move(1)                    ' This does the rotation if SetRotation angle <> 0 
    DXScreen.Render
'   DXScreen.ForceUpdate(0,0,50,50)     ' Update FPS text only 
'   DXScreen.TextOut(10,8,"FPS: "+STR$(DXTimer.FrameRate), &HFFFFFF, -1) 
'   DXScreen.TextOut(10,8,STR$(Min.x+MouseX_Now) + "     ", &HFFFFFF, -1) 

    IF PerspectiveViewOnMnu.Checked THEN
        DXScreen.ForceUpdate(0,0,50,50)
        DXScreen.TextOut(10,20,"Click Mouse to Finish Perspective", &HFFFFFF, -1)
    END IF
    DXScreen.Flip
END SUB




SUB Load_Background
    DIM TheTexture AS QD3DTexture
    DIM openDialog AS QOPENDIALOG
    openDialog.Caption = "select a bitmap for texture"
    openDialog.filter = "*.bmp (bitmaps)|*.bmp"

    IF openDialog.execute THEN
        DXScreen.LoadTexture(openDialog.filename, TheTexture)
        DXScreen.SetBackgroundImage(TheTexture)
    END IF
END SUB



SUB MeshWhiteBackground
    IF Psuedo_Color = 1 THEN
        Psuedo_Color = 0
        WhiteBckgrdTexMnu.Checked = 0           'once you turn it off it is gone forever 
        WorldFrame.SetBackgroundRGB(1,1,1)
        RulerMesh.SetRGBA(1,1,1,1)
    ELSE
        Psuedo_Color = 1
        WhiteBckgrdTexMnu.Checked = 1
        ShowMessage "Changes affect next loaded mesh"
    END IF
END SUB









SUB FormKeyPress(TheKey AS WORD, Shift AS INTEGER)

    SELECT CASE TheKey
    CASE = 38, 107      'up arrow and "+" 
        Inc_Speed
'       KILLMESSAGE Form.handle, WM_CHAR        'use to stop other controls from getting key 
    CASE 40, 109            'dn arrow "-" 
        Dec_Speed
'       KILLMESSAGE Form.handle, WM_CHAR 
    CASE 86             'v key 
        Toggle_Flying
    CASE 77             'm key 
        Toggle_Move_Light
    CASE 79             'o key 
        Toggle_Oscill
    CASE 68             'd key 
        Toggle_KnifeDepth
    CASE 82             'R key 
        Toggle_Ruler_On
    CASE 27             'Esc key 
        StopAllActions
    CASE 49                 '1 
        Texture_Quality = D3DRMTEXTURE_NEAREST
        DXScreen.SetTextureQuality(Texture_Quality)
    CASE 50
        Texture_Quality = D3DRMTEXTURE_LINEAR
        DXScreen.SetTextureQuality(Texture_Quality)
    END SELECT
END SUB



SUB StopAllActions
    OscillMnu.Checked = True:               Toggle_Oscill
    Fly_On = True:                              Toggle_Flying
    MoveLightMnu.Checked = True:            Toggle_Move_Light
    KnifeDepthMnu.Checked = True:           Toggle_KnifeDepth
    PerspectiveViewOnMnu.Checked = True:    TogglePerspectiveView
    RulerOnMnu.Checked = True:                  IF RulerNotMade = False THEN Toggle_Ruler_On
END SUB


SUB Inc_Speed
    IF Oscill_On THEN RotAngl = RotAngl * 1.1
    IF Fly_On THEN The_Speed = The_Speed * 1.5
    IF Fly_On THEN Omega = Omega * 1.2
    IF Knife_On THEN INC (Knife_Speed, Knife_Speed * 0.2)
END SUB

SUB Dec_Speed
    IF Oscill_On THEN RotAngl = RotAngl * 0.9
    IF Fly_On THEN The_Speed = The_Speed * 0.85':   IF The_Speed < 0.01 THEN The_Speed = 0.01 
    IF Fly_On THEN Omega = Omega * 0.8':                IF Omega < 0.005 THEN Omega = 0.005 
    IF Knife_On THEN DEC (Knife_Speed, Knife_Speed * 0.2): IF Knife_Speed < 0.01 THEN Knife_Speed = 0.01
END SUB





SUB Center_Terrain
    Cam_Orient.dvx = 0              'These vectors set the orientation of camera z-axis (-1 to 1) 
    Cam_Orient.dvy = 0              'to set it along the default orientation you would have 
    Cam_Orient.dvz = 1              'x = 1, y = 0, z = 0 
    Cam_Orient.x = 0                'these three vectors set the camera's y-axis orientation 
    Cam_Orient.y = 1                'default is x = 0, y = 1, z = 0 
    Cam_Orient.z = 0

    Cam_Pos.x = 0
    Cam_Pos.y = 0
    Cam_Pos.z = -50
    DXScreen.CameraLookAt(WorldFrame, D3DRMCONSTRAIN_Y)             'constrain the camera to look down z-axis 
    DXScreen.SetCameraPosition(Cam_Pos.x, Cam_Pos.y, Cam_Pos.z)     'x - axis into screen , y-up, z - left right 
    DXScreen.SetCameraOrientation(Cam_Orient.dvx, Cam_Orient.dvy, Cam_Orient.dvz,_
                                  Cam_Orient.x, Cam_Orient.y, Cam_Orient.z)
'  
    WorldFrame.SetRotation(0, 0, 0, 0)
    WorldFrame.SetPosition(0, 0, 0)
    WorldFrame.SetOrientation(Cam_Orient.dvx, Cam_Orient.dvy, Cam_Orient.dvz,_
                                  Cam_Orient.x, Cam_Orient.y, Cam_Orient.z)
'  
END SUB



SUB Toggle_Flying
    IF Fly_On = 1 THEN Fly_On = 0 ELSE Fly_On = 1
    FlyMnu.Checked = Fly_On
END SUB


SUB Toggle_Oscill
    IF Oscill_On THEN 
        Oscill_On = 0
        WorldFrame.SetRotation(0, 0, 0, 0)
     ELSE
        Oscill_On = 1
        Oscill_Tick = Oscill_Time\2
        WorldFrame.SetRotation(0, 0, 0, (RotAngl * Oscill_Tick))
    END IF
    OscillMnu.Checked = Oscill_On
END SUB


SUB Toggle_Move_Light
    IF MoveLightMnu.Checked = 1 THEN MoveLightMnu.Checked = 0 ELSE MoveLightMnu.Checked = 1
END SUB




SUB Toggle_Ruler_Visible
    IF RulerVisibleMnu.Checked THEN
        RulerVisibleMnu.Checked = 0
        IF RulerNotMade = False THEN RulerFrame.DeleteVisual(RulerMesh)
    ELSE
        RulerVisibleMnu.Checked = 1
        IF RulerNotMade THEN MakeRuler
        RulerFrame.AddVisual(RulerMesh)
    END IF
END SUB


SUB Toggle_Ruler_On
    IF RulerOnMnu.Checked THEN
        RulerOnMnu.Checked = False
        Ruler_On = False
        RulerFrame.SetRotation(0,0,0,0)
        ELSE
        RulerOnMnu.Checked = True
        Ruler_On = True
    END IF
END SUB






SUB CloseApp
    DIM i AS INTEGER

'   WorldFrame.DeleteVisual(RulerMesh) 
    Application.Terminate
END SUB



SUB ScaleXModel
    IF NumMRIs < 1 THEN
        ShowMessage "No volume models loaded"
        EXIT SUB
    END IF

    ScaleForm.Caption="Scale Volume Model"
    ScButton1.Caption = "Increase Size"
    ScButton2.Caption = "Decrease Size"
    ScButton3.Caption = "Update"
    ScaleForm.Show
END SUB

SUB VaryAlphaBlend
    ScaleForm.Caption="Vary transparency"
    ScButton1.Caption = "Less transparent"
    ScButton2.Caption = "More transparent"
    ScButton3.Caption = "Go"
    ScaleForm.Show
END SUB



SUB MRI_Texture_Detail
    Mod_Scale = 1.0
    ScaleForm.Caption= "Scale MRI image"
    ScButton1.Caption = "Increase Detail"       'allow different scaling methods by captions 
    ScButton2.Caption = "Decrease Detail"
    ScButton3.Caption = "Use Slider"
    ScaleForm.Show
END SUB


SUB UpdateScaleScrollBar
    ScButton3.Enabled = True
END SUB


SUB ScaleTheObject (Sender AS QBUTTON)
    DIM i       AS INTEGER

    Screen.Cursor = crHourGlass
    If Sender.Caption = "Decrease Size" THEN Mod_Scale = .9
    If Sender.Caption = "Increase Size" THEN Mod_Scale = 1.1
    If Sender.Caption = "Decrease Detail" THEN Wrap_Scale = Wrap_Scale * .8
    If Sender.Caption = "Increase Detail" THEN Wrap_Scale = Wrap_Scale * 1.2
    If Sender.Caption = "Less transparent" THEN MeshAlpha = MeshAlpha * 1.25    'will be limited later 
    If Sender.Caption = "More transparent" THEN MeshAlpha = MeshAlpha * 0.75 

    If Sender.Caption = "Update" THEN
        Mod_Scale = 50# / ScaleScrollBar.Position       'convert upside down 
        IF Mod_Scale = 0.0 THEN Mod_Scale = 0.01
        ScaleScrollBar.Position = 50                    'reset 
        ScButton3.Enabled = False
    end if

    If Sender.Caption = "Go" THEN
        MeshAlpha = 10/ScaleScrollBar.Position          'will be limited later 
        ScaleScrollBar.Position = 50                    'reset 
        ScButton3.Enabled = False
    end if

    If Sender.Caption = "Use Slider" THEN
        Wrap_Scale = 50# / ScaleScrollBar.Position      'convert upside down 
        IF Wrap_Scale = 0.0 THEN Wrap_Scale = 0.01
        ScaleScrollBar.Position = 50                    'reset 
        ScButton3.Enabled = False
    end if


    SELECT CASE ScaleForm.Caption
        CASE IS = "Scale Volume Model"
            FOR i =1 TO NumMRIs
                XmodelMesh(i).Scale(Mod_Scale, Mod_Scale, Mod_Scale)
            NEXT i
        CASE IS = "Scale MRI image"
            DXScreen.createWrap(D3DRMWRAP_SHEET,_
                Min.x, Min.y, 0,_                       'wrap origin x,y,z 
                0, 0, 1,_                               'z-axis vector 
                0, -1, 0,_                              'y-axis vector 
                Min.x, Min.y, Wrap_Scale/Max.x, Wrap_Scale/Max.y, wrap)     'origin u, v, and scale factor u, v 
                FOR i = 1 TO NumMRIs
                    wrap.apply(XmodelMesh(i))
                NEXT i
        CASE IS = "Vary transparency"
            SetMeshFixedAlpha(MeshAlpha)            'SUB limits alpha 
    END SELECT
    Screen.Cursor = crDefault
END SUB



SUB Toggle_KnifeDepth
        IF KnifeDepthMnu.Checked THEN
        KnifeDepthMnu.Checked = 0
        Knife_On = False
    ELSE
        KnifeDepthMnu.Checked = 1
        Knife_On = True
    END IF
END SUB



SUB TogglePerspectiveView
    IF PerspectiveViewOnMnu.Checked THEN
        PerspectiveViewOnMnu.Checked = 0
    ELSE
        PerspectiveViewOnMnu.Checked = 1
    END IF
END SUB



SUB SetPerspectiveView
    DIM x AS SINGLE
    DIM y AS SINGLE

    x = MouseX * 4/ Form.ClientHeight: IF x < .101 THEN x = 0.101
    y = MouseY * 4/ Form.ClientHeight: IF x < .101 THEN x = 0.101
    DXScreen.VIEW.SETPLANE(-1/x, 1/x, -y, y)        'this is an undocumented command, works great! 
END SUB




SUB SetMeshFixedAlpha(indx AS SINGLE)
    DIM i as integer

    IF indx < 0.01 THEN indx = 0.01
    IF indx > 1.0 THEN indx = 1.0
    FOR i = 1 TO Max_MRIimages
        XmodelMesh(i).SetRGBA(1, 1, 1, indx)
    NEXT i
END SUB



SUB ShowMeshExponentAlpha(indx AS SINGLE)
    AlphaEdit.Text = STR$(AlphaExponent)
    AlphaExpDialogForm.Show
END SUB




SUB SetMeshExponentAlpha
    DIM i                   AS INTEGER
    DIM AlphaGrad           AS SINGLE


    'equation for directx alphablend by MS default: 
    'Final Color = ObjectColor * SourceBlendFactor + PixelColor * DestinationBlendFactor  
    AlphaExponent = VAL(AlphaEdit.Text): IF AlphaExponent = 0 THEN AlphaExponent = 1.0      'handle errors 
    IF AlphaExpDialogForm.Visible THEN AlphaExpDialogForm.Close

    FOR i = 1 TO Max_MRIimages
        AlphaGrad = (i/Max_MRIimages)^AlphaExponent
        IF AlphaGrad < 0.01 THEN AlphaGrad = 0.01
        IF AlphaGrad > 1.0 THEN AlphaGrad = 1.0
        XmodelMesh(i).SetRGBA(1, 1, 1, AlphaGrad)
    NEXT i
END SUB


SUB CheckSectionDepth
    IF Depth.Back < 1.0 THEN Depth.Back = 1.0
    IF Depth.Front > NumMRIs THEN Depth.Front = NumMRIs
    IF Depth.Back < Depth.Front THEN Depth.Back = Depth.Front + 1.0
    IF Depth.Back > NumMRIs THEN Depth.Back = NumMRIs
    IF Depth.Front > Depth.Back THEN Depth.Front = Depth.Back - 1.0
    IF Depth.Front < 1.0 THEN Depth.Front = 1.0
END SUB


SUB UpdateMRIPlanes
    DIM i   AS INTEGER

'   DeleteAllVisuals 
'   FOR i = INT(Depth.Front) TO INT(Depth.Back) STEP 1 
'       WorldFrame.AddVisual(XmodelMesh(i)) 
'   NEXT i 

    'sort out depth for speed, skips if front = active front 
    IF INT(Depth.Front) < INT(ActiveView.Front) THEN        'add to the front 
        FOR i = INT(Depth.Front) TO INT(ActiveView.Front)
            WorldFrame.AddVisual(XmodelMesh(i))
        NEXT i
    END IF
    IF INT(Depth.Front) > INT(ActiveView.Front) THEN        'need to delete front 
        FOR i = INT(ActiveView.Front) TO INT(Depth.Front)
            WorldFrame.DeleteVisual(XmodelMesh(i))
        NEXT i
    END IF

    'now sort out back side depth, skips if Back = active back 
    IF INT(Depth.Back) > INT(ActiveView.Back) THEN      'add to the Back 
        FOR i = INT(Depth.Back) TO INT(ActiveView.Back)
            WorldFrame.AddVisual(XmodelMesh(i))
        NEXT i
    END IF
    IF INT(Depth.Back) < INT(ActiveView.Back) THEN      'need to delete Back 
        FOR i = INT(ActiveView.Back) TO INT(Depth.Back)
            WorldFrame.DeleteVisual(XmodelMesh(i))
        NEXT i
    END IF

    ActiveView.Front = Depth.Front
    ActiveView.Back = Depth.Back
END SUB





SUB Make_MRI_Planes
    DIM i                   AS INTEGER
    DIM Face                AS QD3DFace

    i = 1
    Screen.Cursor = crHourGlass
    The_MRI_Image = LoadStandardImageName(i)
    IF The_MRI_Image <> null THEN
        DO
            DXScreen.CreateMeshBuilder(XmodelMesh(i))
            DXScreen.CreateFace(Face)
            Face.AddVertex(Min.x, Min.y, 0 + i * Step_size)
            Face.AddVertex(Min.x, Max.y, 0 + i * Step_size)
            Face.AddVertex(Max.x, Max.y, 0 + i * Step_size)     'create a flat plane 
            Face.AddVertex(Max.x, Min.y, 0 + i * Step_size)

            Face.AddVertex(Max.x, Min.y, 0 + i * Step_size)
            Face.AddVertex(Max.x, Max.y, 0 + i * Step_size)     'create a back plane 
            Face.AddVertex(Min.x, Max.y, 0 + i * Step_size) 'to avoid culling 
            Face.AddVertex(Min.x, Min.y, 0 + i * Step_size)

            XmodelMesh(i).AddFace(Face)
            XmodelMesh(i).loadTexture(The_MRI_Image)
            XmodelMesh(i).SetRGBA(1,1,1,MeshAlpha)
            WrapTexutureToMesh(XmodelMesh(i))
            WorldFrame.AddVisual(XmodelMesh(i))
            NumMRIs = i
            INC i
            The_MRI_Image = LoadStandardImageName(i)
        LOOP UNTIL (The_MRI_Image = null) OR (NumMRIs = Max_MRIimages)
        ActiveView.Back = NumMRIs
        ActiveView.Front = 1
    ELSE
        ShowMessage "Error loading MRI images - pick the first image in series"
    END IF
    Screen.Cursor = crDefault
END SUB




SUB MakeRuler
    DIM Face                AS QD3DFace
    DIM RulerTexName        AS STRING

    DXScreen.CreateFrame(RulerFrame)        'create a frame to move/orient the ruler 
    DXScreen.CreateMeshBuilder(RulerMesh)
    DXScreen.CreateFace(Face)
    Face.AddVertex(Min.x, Min.y, Min.z):    Face.AddVertex(Min.x, Min.y, Max.z)
    Face.AddVertex(Max.x,Min.y, Max.z):         Face.AddVertex(Max.x, Min.y, Min.z)         'front face Ruler 
    Face.AddVertex(Max.x, Min.y, Min.z):    Face.AddVertex(Max.x,Min.y, Max.z)
    Face.AddVertex(Min.x, Min.y, Max.z):    Face.AddVertex(Min.x, Min.y, Min.z)         'back face 
    IF Psuedo_Color THEN Face.SetColorRGB(0.3, 0.6, 1 )
    RulerMesh.AddFace(Face)
    RulerTexName = MyPath + "FloorDetail.bmp"
    RulerMesh.loadTexture(RulerTexName)
    DXScreen.createWrap(D3DRMWRAP_FLAT, _       'flat scale width and height of the texture to  width and height of object 
            Min.x, 0,Min.x,_
            0, 1, 0,_
            0, 0, 1,_
            0, 0,   (1/(Max.x*2)),  (1/(Max.z*2)), wrap)
    wrap.apply(RulerMesh)
    RulerFrame.AddVisual RulerMesh
    RulerFrame.SetPosition(0, 0, 0)
    RulerFrame.SetOrientation(Cam_Orient.dvx, Cam_Orient.dvy, Cam_Orient.dvz,_
                                  Cam_Orient.x, Cam_Orient.y, Cam_Orient.z)

    RulerNotMade = False
END SUB





SUB Load_MRI_Images
    DIM i                   AS INTEGER
    DIM SerialFileName      AS STRING

    openDialog.Caption = "select Start MRI image in BMP format"
    openDialog.filter = "*.bmp (bitmaps)|*.bmp"

    IF openDialog.execute = False THEN EXIT SUB
    Screen.Cursor = crHourGlass
    DeleteAllVisuals
    i = 0
    NumMRIs = 0
    DO
        SerialFileName = LEFT$(OpenDialog.FileName, (LEN(OpenDialog.FileName)-5) )  + STR$(i) + ".bmp"
        IF FILEEXISTS(SerialFileName) THEN
            Start_MRI_image = LEFT$(OpenDialog.FileName, (LEN(OpenDialog.FileName)-5))
            INC i
            XmodelMesh(i).loadTexture(SerialFileName)
            WrapTexutureToMesh(XmodelMesh(i))
            NumMRIs = i
        ELSE
            SerialFileName = null
        END IF
    LOOP UNTIL (i = Max_MRIimages) OR (SerialFileName = null)
    ShowAllVisuals
    XModelTexDetailMnu.Enabled = 1
    Screen.Cursor = crDefault
END SUB



SUB SegmentBMP (TheBitMap AS QBITMAP, MaskColor1 AS INTEGER, MaskColor2 AS INTEGER, Blend AS BYTE)
    DIM MaskBMP         AS QBITMAP
    DIM CopyBMP         AS QBITMAP
    DIM BF              AS BLENDFUNCTION
    DIM lpBF            AS LONG
    DIM Rtn                 AS LONG


    IF Blend THEN 
        CopyBMP.Width = TheBitMap.Width     
        CopyBMP.Height = TheBitMap.Height   'make of copy of our original 
        CopyBMP.Draw(0,0,TheBitMap.BMP)     'make a copy we are going to blend it 
        BF.BlendOp = AC_SRC_OVER        'only AC_SRC_OVER is supported as of 2004 
        BF.BlendFlags = 0               'must be 0 
        BF.SourceConstantAlpha = Blend  'Blend amount 0 - transparent, 128=1/2, 255 all 
        BF.AlphaFormat = 0              'if = 1 (AC_SRC_ALPHA = True) then bmp must be 32 bpp (more xforms possible) 
    END IF


    MaskBMP.Width = TheBitMap.Width+3
    MaskBMP.Height = TheBitMap.Height+3
    MaskBMP.FillRect(0,0,MaskBMP.Width, MaskBMP.Height, MaskColor1)
    TheBitMap.CopyMode = cmSrcAnd
    TheBitMap.Draw(0,0,MaskBMP.BMP)         'MRI is now filtered 

    'secondary filter 
    IF MaskColor2 THEN
    MaskBMP.FillRect(0,0,MaskBMP.Width, MaskBMP.Height, MaskColor2)
    TheBitMap.CopyMode = cmSrcAnd
    TheBitMap.Draw(0,0,MaskBMP.BMP)
    END IF


    IF Blend THEN           'AlphaBlend to Dest (1st bmp) from Source (2nd bmp) 
        MEMCPY VARPTR(lpBF), BF, SIZEOF(LONG)       'copy UDT pointer 
        Rtn = AlphaBlend(TheBitMap.Handle, 0, 0, TheBitMap.Width, CopyBMP.Height,_
                    CopyBMP.handle, 0, 0, CopyBMP.Width, TheBitMap.Height, lpBF)
    END IF
END SUB



SUB SegmentAllMRIimages
    DIM MRIBMP          AS QBITMAP
    DIM DestBMP             AS QBITMAP
    DIM TheMask             AS LONG
    DIM The_MRI_Image   AS STRING
    DIM TempName        AS STRING
    DIM i               AS INTEGER

' To get the surface first mask with &HE0E0E0, then mask with &HDDDDDD 
    Screen.Cursor = crHourGlass
    TempName = "~mriviewtmp.bmp"
    i = 1
    The_MRI_Image = LoadStandardImageName(i)
    WHILE (The_MRI_Image <> null) AND (i <= Max_MRIimages)
        MRIBMP.LoadFromFile(The_MRI_Image)
        MRIBMP.Width = 256: MRIBMP.Height = 256
        SegmentBMP (MRIBMP, &HE0E0E0, &H408020, 128) 
        MRIBMP.SaveToFile(TempName)
        XmodelMesh(i).loadTexture(TempName)
        WrapTexutureToMesh(XmodelMesh(i))
        INC i
        The_MRI_Image = LoadStandardImageName(i)
    WEND
    Screen.Cursor = crDefault
END SUB



SUB Make_MRI_Volumes
    DIM i                   AS INTEGER
    DIM The_MRI_Image       AS STRING
    DIM Face                AS QD3DFace
    DIM TempStr             AS STRING

    DIM ExtraFilter         AS STRING:  ExtraFilter = null          'allow to convert 3ds files 
    DIM ConvertedFileName   AS STRING:  ConvertedFileName = null
    CHDIR "\RapidQ\MRIview"
    IF FILEEXISTS("CONV3DS.EXE") THEN ExtraFilter = "|3D Max files|*.3DS"
    openDialog.filter = "*.x (X models)|*.x" + ExtraFilter

    IF OpenDialog.Execute THEN
        IF UCASE$(RIGHT$(OpenDialog.FileName,4)) = ".3DS" THEN  'got a .3ds file 
            ConvertedFileName = LEFT$(OpenDialog.FileName, (LEN(OpenDialog.FileName)-4))
            ExtraFilter = "CONV3DS -m " + OpenDialog.FileName
            SHELL(ExtraFilter)                                  'blocks execution 
            OpenDialog.FileName = ConvertedFileName + ".x"
        END IF
    ELSE
        EXIT SUB
    END IF
    Screen.Cursor = crHourGlass
    DeleteAllVisuals
    CreateNewMeshBuilders           'nothing to render now 
    ScaleXmodelMnu1.Enabled = 1
    ScaleXmodelMnu2.Enabled = 1
    i = 1
    The_MRI_Image = LoadStandardImageName(i)

        IF The_MRI_Image <> null THEN
            DO
            XmodelMesh(i).Load(OpenDialog.fileName)                 'only works for .x models saved as meshes 
            XmodelMesh(i).Scale(2* Max.x, 2* Max.y, 4*Step_Size)'make each model the same thickness in z-axis 
            XmodelMesh(i).loadTexture(The_MRI_Image)
            XmodelMesh(i).SetQuality = D3DRMRENDER_FLAT
            XmodelMesh(i).SetRGBA(1, 1, 1, MeshAlpha)           'Add color w/alphablending  R, G, B,  A - from 0 to 1.0 
            XmodelMesh(i).Translate(0, 0, Step_Size * i)        'stack them along z-axis 
            WrapTexutureToMesh(XmodelMesh(i))
            WorldFrame.AddVisual(XmodelMesh(i))                     'use for all models move with the world 
            NumMRIs = i
            INC i
            The_MRI_Image = LoadStandardImageName(i)
            LOOP UNTIL (i = Max_MRIimages) OR (The_MRI_Image = null)
            ActiveView.Back = NumMRIs
            ActiveView.Front = 1
        ELSE
            ShowMessage "Error loading MRI images - pick the first image in series"
        END IF
        Screen.Cursor = crDefault
END SUB




FUNCTION LoadStandardImageName(indx as integer) AS STRING
    DIM TheFileName         AS STRING
    DIM tempStr             AS STRING
    DIM Dest                AS QRECT
    DIM Dest2               AS QRECT
    DIM SourceBitmap        AS QBITMAP
    DIM DestBitmap          AS QBITMAP

    Dest.Top = 0:       Dest.Left = 0
    Dest.Right = 256:   Dest.Bottom = 256
    Dest2.Top = 0:      Dest2.Left = 0
    Dest2.Right = 256:  Dest2.Bottom = 256

'   ** get the filenames *** 
    TheFileName = Start_MRI_image
    TempStr = STR$(indx)
    TheFileName = TheFileName + TempStr+ ".bmp"

    IF FILEEXISTS(TheFileName) THEN
        LoadStandardImageName = TheFileName
        SourceBitmap.BMP = TheFileName
    '   ** must have textures a power of 2 
        IF SourceBitmap.Width < 255 THEN
            DestBitmap.Width = 255: DestBitmap.Height = 255
            DestBitmap.FillRect(0,0,255,255,0)      'fill it with 256x256 pixels 
            DestBitmap.CopyRect(Dest, SourceBitmap, Dest2)
            'DestBitmap.Draw(255-SourceBitmap.Width\2, 255-SourceBitmap.Height\2,SourceBitmap)  
            TheFileName = LEFT$(TheFileName, LEN(TheFileName)-4) + "alt.bmp"
                DestBitMap.SaveToFile(TheFileName)
        END IF
    ELSE
        LoadStandardImageName = null
    END IF
END FUNCTION




SUB WrapTexutureToMesh(TheMesh AS QD3DMeshBuilder)
    DXScreen.createWrap(D3DRMWRAP_SHEET, _
        Min.x, Min.y, 0,_                       'wrap origin x,y,z 
        0, 0, 1,_                               'z-axis vector 
        0, 1, 0,_                               'y-axis vector 
        Min.x, Min.y, Wrap_Scale/Max.x, Wrap_Scale/Max.y, wrap)     'origin u, v, and scale factor u, v 
    wrap.apply(TheMesh)
END SUB




SUB DeleteAllVisuals
    DIM i AS INTEGER

    FOR i = 1 TO Max_MRIimages
        WorldFrame.DeleteVisual(XmodelMesh(i))              'remove old planes 
    NEXT i
    ShowAllVisualsMnu.Checked = False
    ShowAllVisualsMnu.Enabled = True
END SUB


SUB ShowAllVisuals
    DIM i AS INTEGER

    FOR i = 1 TO NumMRIs
        WorldFrame.AddVisual(XmodelMesh(i))                 'remove old planes 
    NEXT i
    ShowAllVisualsMnu.Checked = True
    IF Knife_On THEN Knife_On = False: KnifeDepthMnu.Checked = False
END SUB


SUB CreateNewMeshBuilders
    DIM i AS INTEGER

    FOR i = 1 TO Max_MRIimages
        DXScreen.CreateMeshBuilder(XmodelMesh(i))           'allocate new pointers to Direct3D interface 
    NEXT i
END SUB






SUB DXMouseDown(Button AS INTEGER, x AS INTEGER, y AS INTEGER)
    DIM i   AS INTEGER

    IF Ruler_On THEN
        RulerMoving = True  'signal we want to move it not orient it 
        IF Button = 0 THEN
            Ruler_Pos.x = (DXscreen.Width\2 - x) * Max.x
            Ruler_Pos.y = (DXscreen.Height\2 - y) * Max.y
        ELSE
            Ruler_Pos.z = (DXscreen.Width\2 - x) * Max.z
        END IF
'       DXScreen.Render 
'       DXScreen.Flip 
        EXIT SUB
    END IF


    IF PerspectiveViewOnMnu.Checked THEN
        PerspectiveViewOnMnu.Checked = 0
        DXScreen.ForceUpdate(0,0,100,100)
        DXScreen.TextOut(8,20,"                                   ", &HFFFFFF, -1)
        EXIT SUB
    END IF


    IF Knife_On THEN
        IF Button = 0 THEN DEC Depth.Front, 1.0
        IF Button = 1 THEN INC Depth.Front, 1.0
        CheckSectionDepth
        UpdateMRIPlanes
        EXIT SUB
    END IF

    'last option is to scale it 
    Screen.Cursor = crHourGlass
    IF Button = 0 THEN Mod_Scale = 1.25
    IF Button = 1 THEN Mod_Scale = 0.75
    FOR i =1 TO NumMRIs: XmodelMesh(i).Scale(Mod_Scale, Mod_Scale, Mod_Scale): NEXT i
    Screen.Cursor = crDefault

END SUB

SUB DXMouseUp(Button AS INTEGER, x AS INTEGER, y AS INTEGER)
    IF Ruler_On THEN RulerMoving = False    'signal we want to orient it now 
END SUB







SUB MakeMRIVolumesFromImage
    DIM MRIBMP          AS QBITMAP
    DIM Face            AS QD3DFace
    DIM The_MRI_Image   AS STRING
    DIM TempName        AS STRING
    DIM i               AS INTEGER
    DIM bmpX            AS INTEGER      'bitmpa x, y location 
    DIM bmpY            AS INTEGER
    DIM MidX            AS INTEGER      'define middle of bmp 
    DIM MidY            AS INTEGER
    DIM Xtolerance      AS INTEGER      'don't go all the way to midline 
    DIM RtBorder        AS INTEGER
    DIM LtBorder        AS INTEGER      'right and left boundaries of vertex 
    DIM RtTolerance     AS INTEGER
    DIM LtTolerance     AS INTEGER      'reset boundaries of vertex for next raster line 
    DIM SkipLeft        AS INTEGER
    DIM SkipRight       AS INTEGER      'break out of loop 
    DIM BrkSearch       AS INTEGER      'break out of search loop when both sides found 
    DIM VertScale       AS SINGLE
    DIM Vertx(2048)         AS SINGLE       'buffer of vertices, must be last declared 


    xTolerance = 24
    CancelTerrLoad = 0                  'let user break if too long 
    WaitForm.Show
    Screen.Cursor = crHourGlass
    i = 1
    The_MRI_Image = LoadStandardImageName(i)
    IF The_MRI_Image = null THEN        'no file exists, or error in naming 
        ShowMessage "Error loading file"
        WaitForm.Close
        Screen.Cursor = crDefault
        EXIT SUB
    END IF
    MRIBMP.LoadFromFile(The_MRI_Image)
    IF MRIBMP.Height > 512 THEN
        ShowMessage "Error: Maximum image height is 512 pixels"
        WaitForm.Close
        Screen.Cursor = crDefault
        EXIT SUB
    END IF
    DeleteAllVisuals

    WHILE (The_MRI_Image <> null) AND (i <= Max_MRIimages)
        VertScale = 2 * Max.x/ MRIBMP.Height
        MidX = MRIBMP.Height\2
        MidY = MRIBMP.Width\2
        SegmentBMP(MRIBMP,  SegThreshold, 0, 0)                         'other masks can be &HE0E0E0 or &H808080 
        DXscreen.Draw(0,0,MRIBMP.BMP): DXscreen.Flip
        DXScreen.CreateMeshBuilder(XmodelMesh(i))
        DXScreen.CreateFace(Face)
        MEMSET(VARPTR(Vertx(0)), 0, 1024 * SIZEOF(SINGLE))      'initialize memory 
        Gauge.Max = Max_MRIimages * MRIBMP.Height

        'new bmp loaded find starting point in x-axis, so search the whole BMP width 
        LtBorder = 1
        RtBorder = MRIBMP.Width-1:  RtTolerance = RtBorder

        FOR bmpY = 0 TO MRIBMP.Height                           'setup for cclockwise, top left 
        LtBorder = LtTolerance
        DO
            IF MRIBMP.Pixel(LtBorder, bmpY) > 0 THEN
                Vertx(bmpY)= LtBorder
                LtTolerance = LtBorder - xTolerance
                EXIT DO
            END IF
            INC LtBorder
        LOOP UNTIL LtBorder = MRIBMP.Width
        IF LtBorder = MRIBMP.Width THEN                 'didn't find a pixel 
            LtTolerance = 1
            RtTolerance = MRIBMP.Width-1            'didn't find one 
        ELSE
            RtBorder = RtTolerance
            DOEVENTS
            DO
            IF MRIBMP.Pixel(RtBorder, bmpY) THEN
                Vertx(MRIBMP.Height + bmpY)= RtBorder
                RtTolerance = RtBorder + xTolerance
                EXIT DO
            END IF
            DEC RtBorder
            LOOP UNTIL RtBorder = 0
        END IF

        Gauge.Position = i * MRIBMP.Height
        NEXT bmpY
        IF CancelTerrLoad THEN WaitForm.Close: EXIT SUB

        FOR bmpY = 0 TO MRIBMP.Height       'start top to bottom 
            IF Vertx(bmpY) THEN
                Face.AddVertex((Vertx(bmpY) - MidX) * VertScale, (bmpY - MidY) * VertScale, i * Step_size)
            END IF
        NEXT bmpY
        FOR bmpY = MRIBMP.Height TO 0 STEP -1                   'finish up along Cclockwise from bottom right 
            IF Vertx(MRIBMP.Height + bmpY) THEN
                Face.AddVertex((Vertx(MRIBMP.Height + bmpY) - MidX) * VertScale, (bmpY - MidY) * VertScale, i * Step_size)
            END IF
        NEXT bmpY

        'this next section adds faces to the backside of the plane to avoid culling 
        FOR bmpY = 0 TO MRIBMP.Height               'backtrack to make both sides visible (no backface culling) 
            IF Vertx(MRIBMP.Height + bmpY) THEN
                Face.AddVertex((Vertx(MRIBMP.Height + bmpY) - MidX) * VertScale, (bmpY - MidY) * VertScale, i * Step_size)
            END IF
        NEXT bmpY

        FOR bmpY = MRIBMP.Height TO 0 STEP -1                   'finish up along Cclockwise from bottom right 
            IF Vertx(bmpY)  THEN
                Face.AddVertex((Vertx(bmpY) - MidX) * VertScale, (bmpY - MidY) * VertScale, i * Step_size)
            END IF
        NEXT bmpY


        IF CancelTerrLoad THEN WaitForm.Close: EXIT SUB
        XmodelMesh(i).AddFace(Face)
        XmodelMesh(i).loadTexture(The_MRI_Image)
        XmodelMesh(i).SetRGBA(1,1,1,MeshAlpha)
        WrapTexutureToMesh(XmodelMesh(i))
        WorldFrame.AddVisual(XmodelMesh(i))
        ActiveView.Back = i
        INC i
        The_MRI_Image = LoadStandardImageName(i)
        IF The_MRI_Image <> null THEN MRIBMP.LoadFromFile(The_MRI_Image)
    WEND
    ActiveView.Front = 1
    WaitForm.Close
    Screen.Cursor = crDefault
END SUB


SUB SetThreshold
    DIM S AS STRING

    IF SegThresholdDialogForm.Visible THEN
        S = UCASE$(SegEdit.Text)
        S = CONVBASE$(S, 16, 10) '-- Convert from base 16 to 10, must be upper case 
        SegThreshold = VAL(S)
        SegThresholdDialogForm.Close
    ELSE
        SegEdit.Text = HEX$(SegThreshold)
        SegThresholdDialogForm.Show
    END IF
END SUB