Comprehensive Example #1: MR Physics Visualization

In Chapter 3, we developed a fairly simple Java 2D application to plot a trajectory through Kspace as part of the KspaceModeller application. We now begin to expand on this application using Java 3D models.

Our goal in this part of the ongoing example is to create a model voxel to examine the behavior of spins in magnetic resonance. Briefly, let's begin by sketching the scene graph for the part of the application we will do here (shown in Figure 11.11).

Figure 11.11. Scene graph diagram for MRVector.java.


We begin by creating a 3D axis in a separate class MRAxis.java (see Listing 11.16).

Listing 11.16 MRAxis.java
import java.lang.Math.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.Cone;

public class MRAxis extends TransformGroup {


    public MRAxis() {

        Appearance axisapp = new Appearance();
        Material axismat = new Material();
        axismat.setShininess(60.f);
        axismat.setSpecularColor(.5f, 01.f, 01.f);
        axismat.setDiffuseColor(.5f, .1f, 0.1f);

        axismat.setLightingEnable(true);
        axisapp.setMaterial(axismat);

        this.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        this.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);

        Axis3D axis3d = new Axis3D(10, 8.f, .05f, axisapp);
        this.addChild(axis3d);

  }

} //end MRAxis

In its current state, the MRAxis class does little more than instantiate an Axis3D object. The Axis3D is substantially similar to the MRVector class; therefore, we don't include it in the text for brevity. As we continue to develop this example in the next few chapters, you will see that having the classes separated in this fashion will allow you to easily add several ornaments to the axis, including some labels that always face the viewer regardless of orientation and some other objects related to enhancing the visualization. These ornaments will be added in the next chapter when we take up OrientedShape3D and the Billboard Behavior.

We are now at an intermediate step and will examine our results. The 3D axis can be seen by instantiating the object in our BasicRecipeJ3D application. Uncomment the lines for MRAxis to see our axis triplet (see Figure 11.12).

Figure 11.12. Screenshot from BasicRecipeJ3D with an object of MRAxis class added to the scene.


Now that we have an axis, we want to create a series of vectors (in the sense of a physical entity with a magnitude and direction). These vectors will be represented by an arrow. The first vector that we will add to the scene is called Mnet and represents the macroscopic sum of a large number of individual spins (as described previously). The code in Listing 11.17 is used to create each vector in a set.

Listing 11.17 MRVector.java
public class MRVector extends TransformGroup {

    Appearance app;
    private float length;
    private float sval = -0.1f;
    Transform3D currentTran;

    public MRVector(Appearance app, float length) {

        this.app = app;
        this.length = length;

        this.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        this.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        this.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
        this.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);

         VecBody vb = new VecBody(10, length, .1f, app);
         VecHead vh = new VecHead(50, length, app);

        this.addChild(vb);
        this.addChild(vh);

  }


Listing 11.17 is quite simple at present but a great deal of changes will occur when we add the ability to scale the Mnet vector. These changes will take place in the next chapter. For now, we will leave this class as a simple, non-scalable vector.

We are now ready to create the series of vectors that will represent the activity of a family of spins contained within a voxel. Listing 11.18 is the code for the MRVoxel class.

Listing 11.18 MRVoxel.java
import java.lang.Math.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.Cone;

public class MRVoxel extends TransformGroup {

    int nspins = 8;

    public MRVoxel() {

       //part 1 - set the capabilities of this object to allow
       //reading and writing of
      //the Transform3D
    this.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        this.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);

       //set capabilities to allow reading and writing of the children
        this.setCapability(Group.ALLOW_CHILDREN_READ);
        this.setCapability(Group.ALLOW_CHILDREN_WRITE);


       //part 2 – set up the appearance bundles
        Appearance mnetapp = new Appearance();
        Material mnetmat = new Material();

        mnetmat.setSpecularColor(1.f, 0.f, 0.f);
        mnetmat.setShininess(5.f);
         mnetapp.setMaterial(mnetmat);

        Appearance spinapp = new Appearance();
        Material spinmat = new Material();

        spinmat.setShininess(5.f);
        spinmat.setLightingEnable(true);
        spinapp.setMaterial(spinmat);

        Bounds bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
        MRVector mnet = new MRVector(mnetapp, nspins*1.f);

        float dephaseFac = -1.f*nspins/2;
        float dephaseFacGlobal = 20.f;

        //part 3 – instantiate the spins and
        //setUserData for diphase rate and type
        MRVector[] spins = new MRVector[nspins];

        for (int ii=0; ii<nspins; ii++) {
           spins[ii] = new MRVector(spinapp,2.f);  //unit vectors
           spins[ii].setUserData(new MRVectorProperties(dephaseFac/dephaseFacGlobal, 
"spin"));
           this.addChild(spins[ii]);

           dephaseFac += 1.f;
           if (dephaseFac == 0.f)
               dephaseFac += 1.f; //center frequency (dephaseFac=0) occupied by Mnet
           }

        this.addChild(mnet);

        mnet.setUserData(new MRVectorProperties(0.f, "mnet"));

        //add a T2Behavior for spinning
        T2Behavior t2 = new T2Behavior(this, 2.f);
        this.addChild(t2);
        t2.setSchedulingBounds(bounds);

  }

} //end class

Listing 11.18 shows the results of adding MRVoxel and MRAxis to the scene and is displayed in Figure 11.13.

Figure 11.13. Screenshot from BasicRecipeJ3D.java with objects from MRVoxel and MRAxis added to the scene.


There are several aspects of the class in Listing 11.18 to notice. One of the main functions of the MRVoxel class is to instantiate the spin vectors and give them properties. This occurs in part two. Note that we use the setUserData() method to label each vector as either a "spin" or as an "mnet". The setUserData() method is one of the methods of the SceneGraphObject class and is a useful method for working around sometimes difficult problems. In this case, we need to identify which type of vector we are dealing with and to store information about the dephasing rate of the vector. In the next chapter, we will access this data in a custom Behavior.

Listing 11.19 shows the MRVectorProperties class that we will use to access and store vector properties.

Listing 11.19 MRVectorProperties.java
class MRVectorProperties {
   float dephaseRate;
   String vectype;

   public MRVectorProperties(float dephaseRate, String vectype) {
      this.dephaseRate = dephaseRate;
      this.vectype = vectype;
   }

   public float getDephaseRate() {
      return dephaseRate;
   }

   public String getVecType() {
       return vectype;
   }

   public void setDephaseRate(float dephaseRate) {
      this.dephaseRate = dephaseRate;
   }

}

Spins dephase over time because of the physical environment in which they exist. One of these dephasing mechanisms is termed T2* relaxation. As a preview, we will add one of the Behaviors that is developed in the next chapter. The purpose of the T2StarBehavior (see Listing 11.20) is to control the speed of the spin vectors so that they dephase over time.

Listing 11.20 T2Behavior.java
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;


public class T2Behavior
    extends Behavior
{
    private TransformGroup targetTG;
    private Transform3D rotation = new Transform3D();
    private double[] T2angle;
    private double T2Weight;
;

    public T2Behavior( TransformGroup targetTG, double T2Weight )
    {
        super( );
        this.targetTG = targetTG;
        this.T2Weight = T2Weight;
               T2angle = new double[targetTG.numChildren()];
    }

    public void initialize()
    {
      this.wakeupOn(new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED));
    }

    public void processStimulus(Enumeration criterion)
    {
          for (int ii=0; ii<targetTG.numChildren(); ii++) {

            MRVectorProperties vecprop = (MRVectorProperties)targetTG.getChild(ii).
getUserData();

            if (vecprop != null) {

               T2angle[ii] += 0.045*(T2Weight+vecprop.getDephaseRate());
               MRVector tmpTG = (MRVector)targetTG.getChild(ii);
               if (vecprop.getVecType() == "mnet") {
                  System.out.println("Mnet encountered; scale");
               }

               Transform3D rot = new Transform3D();
               //System.out.println("T2Angle: " + T2angle[ii]);
               tmpTG.getTransform(rot);
           rot.rotY(T2angle[ii]);
               tmpTG.setTransform(rot);

           //targetTG.setChild(tmpTG,ii);
                //System.out.println("vectype: " + vecprop.getVecType()...
            }
          }

      this.wakeupOn(new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED));
    }
}

Note that the T2StarBehavior retrieves the MRVectorProperties of each vector and uses that to compute a rotational transform. In the next chapter, we will add scaling of Mnet as well as more complex Behaviors for modeling the physic behavior of the voxel.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset