home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 14 / IOPROG_14.ISO / soft / sdkjava / dxma.exe / DXMA05.cab / samples / da / java / showcase / Lighthouse / lhouse.java next >
Encoding:
Java Source  |  1997-11-13  |  25.6 KB  |  627 lines

  1. // lighthouse.java 
  2. // Club Microsoft Lighthouse Demo
  3. // 
  4. // <Tutorial Section=1.0 Title="Section1: Club Microsoft Lighthouse Tutorial Introduction">
  5. /**
  6.   This is one of the premier demonstrations of the kinds of integration and
  7. the possibilities available when using the DirectAnimation
  8. libraries. The main feature of this sample is the use of audio and graphics
  9. that are both controlled by a common animation description (the wind speed). It
  10. integrates 3-D models (the bird, the boat, and the weather vane).  It also has
  11. more traditional sprite animation (the surf, the trees, the sky, and the boy
  12. with the kite).  In addition, the sample makes use of synthetic audio, where
  13. sound seeds are dynamically manipulated and mixed together randomly for the
  14. both the ocean and seagull ambient sounds.  This gives a rich audio environment
  15. which can respond to the user-controlled wind speed without having to download
  16. a long audio track.  Events are used to synchronize the surf
  17. animation with the ocean surf sound.  In addition, the seagull's sound is 
  18. spatialized based on its 3-D location, and it has a flying behavior which is
  19. both autonomous and interactive (when dragging the bird).  All three forms
  20. of media are coordinated and influenced by weather intensity, which is driven
  21. from VBScript by a slider.
  22.  
  23.   The tutorial leads you through the process of creating this complex animation, 
  24. step-by-step.  Section 2 illustrates how to use VBScript to make the scrollbar control
  25. the wind speed.  Section 3 describes how animated cell images are constructed using
  26. a helper class.  Section 4 describes the process of integrating 3-D geometries into
  27. the sample.  Section 5 illustrates the synthetic audio helper class and how it has been used
  28. to create some handy synthetic modules.  Finally, Section 6 shows how to make the seagull
  29. interact with the mouse.<BR><BR>
  30. **/
  31. // </Tutorial>
  32.  
  33. import com.ms.dxmedia.*;  // gets DirectX Media environment
  34. import java.net.*;
  35. import java_utility.*;   // get cellImage1 and helper funcs
  36. import lhouse_module.*;   // gets oceanSynth,seagull,sailboat and weatherVane
  37.  
  38. public class lhouse extends DXMApplet  {
  39.     private lhouseModel _model;
  40.  
  41.   public lhouse()  { 
  42.         _model = new lhouseModel();
  43.         setModel(_model); 
  44.   }
  45.  
  46.   public void setWindSpeed(int val)  {     
  47.         _model.setWindSpeed(val); 
  48.   }
  49. }
  50.  
  51. class lhouseModel extends Model  {
  52.   public grabbedBird _grabbedNotify;
  53.   public thrownBird  _thrownNotify;
  54.  
  55.   // <Tutorial Section=2.1>
  56.   // Here, back in the lhouseModel class, declare a number of variables that
  57.   // will be used throughout the sample.  These variables are declared as 
  58.   // switchers so that the behavior (Bvr) they contain (a number) can be 
  59.   // updated from an external method or a notifier.
  60.   public ModifiableBehavior _windSpeed;  // Normalized value between 0 and 1
  61.   public ModifiableBehavior _windMode;
  62.   public ModifiableBehavior _breezeInterval;
  63.   public DXMEvent _grabEvent;
  64.   public Vector3Bvr  _originalPath;
  65.   public Vector3Bvr _actualPath;
  66.  
  67.   // This is the method that the applet will call to update the wind speed.
  68.   // Notice the switchTo call that swaps the current Bvr for a new one.
  69.   // Note: to convert a primitive Java data types (such as integers) to behaviors
  70.   // use the toBvr() helper method.
  71.   public void setWindSpeed(int val)  { 
  72.     _windSpeed.switchTo( div(toBvr(val),toBvr(10)) );
  73.     _windMode.switchTo(mul(add(toBvr(val),toBvr(1)),toBvr(0.9)));
  74.   }
  75.  
  76.   // The createModel method, tells DirectAnimation how to construct a 
  77.   // run time model for the animation. 
  78.   public void createModel(BvrsToRun blist)  {
  79.  
  80.     // Begin the demo code by initializing switchers and
  81.     // giving them their default number behaviors. For convenience
  82.     // get the actual Bvr out of the switcher and store it as 
  83.     // the variable windSpeed.  Also, create a windDistance value based
  84.     // on the windSpeed (this will be used to move the clouds later on).
  85.     // The most interesting variable here is the WindAngle which is a 
  86.     // value that changes randomly over time.  The example on this page
  87.     // allows you to see the changing values of these numbers as you move
  88.     // the slider.
  89.     _windSpeed = new ModifiableBehavior(toBvr(0));
  90.     _windMode = new ModifiableBehavior(toBvr(0));
  91.     _breezeInterval = new ModifiableBehavior(toBvr(2));   
  92.  
  93.     NumberBvr windSpeed = (NumberBvr)_windSpeed.getBvr();
  94.     NumberBvr windMode = (NumberBvr)_windMode.getBvr();
  95.     NumberBvr windDistance = integral(windSpeed);
  96.     NumberBvr windAngle = generateWindAngle(blist);
  97.     NumberBvr random = seededRandom(.11);
  98.     blist.add(random);
  99.  
  100.     // </Tutorial>
  101.  
  102.     // <Tutorial Section=3.0 Title="Section3: Including Cell Based Images">
  103.     // Here, begin importing the raw images.  If the image has multiple cells
  104.     // make use of the cellImage1 utility.
  105.     ImageBvr clouds,scene;
  106.     cellImage1 kite,ocean1,ocean2,palm1,palm2,palm3,beachboy;
  107.                 
  108.     URL mediaBase = getImportBase();
  109.     URL imgBase = buildURL(mediaBase, "image/");
  110.     URL geoBase = buildURL(mediaBase, "geometry/");
  111.     URL sndBase = buildURL(mediaBase, "sound/");
  112.         
  113.     kite     = new cellImage1(buildURL(imgBase, "kite.gif"),4);
  114.     scene    = importImage(buildURL(imgBase, "scene.jpg"));
  115.     ocean1   = new cellImage1(buildURL(imgBase, "ocean.gif"),4);
  116.     ocean2   = new cellImage1(buildURL(imgBase, "ocean2.gif"),4);
  117.     palm1    = new cellImage1(buildURL(imgBase, "palm1.gif"),5);
  118.     palm2    = new cellImage1(buildURL(imgBase, "palm2.gif"),5);
  119.     palm3    = new cellImage1(buildURL(imgBase, "palm3.gif"),5);
  120.     clouds   = importImage(buildURL(imgBase, "clouds.gif"));
  121.     beachboy = new cellImage1(buildURL(imgBase, "beachboy.gif"),2);
  122.  
  123.     // Here, use the synthetic ocean sounds that are explained in Section 5.
  124.     // The event caused by a crashing wave is used to create an ocean index.
  125.     // An animateTrees method is called to generate psuedo-random tree
  126.     // indexes. These indexes will be used with the showIndex method to
  127.     // determine which tree cell to display. 
  128.     oceanSynth ocean = new oceanSynth(add(toBvr(.2),windSpeed));
  129.     NumberBvr oceanIndex = animateOcean(ocean.soundEvent());
  130.     NumberBvr treeIndex1 = animateTrees(windSpeed,   1);
  131.     NumberBvr treeIndex2 = animateTrees(windSpeed, .88);
  132.     NumberBvr treeIndex3 = animateTrees(windSpeed, .71);
  133.  
  134.     // </Tutorial>
  135.  
  136.     // <Tutorial Section=4.1>
  137.     // Back in the lhouseModel class, use the getGeo() model to construct
  138.     // the vane, and transform it to match the perspective of the scene.
  139.     GeometryBvr vane = 
  140.       weatherVane.getGeo(windAngle,windDistance, geoBase).transform(
  141.         compose(translate(toBvr(-1.1),toBvr(-0.5),toBvr(0)),
  142.           compose(rotate(xVector3, toBvr(Math.PI/12)),
  143.             compose(rotate(yVector3, toBvr(Math.PI/4)),
  144.               scale3(toBvr(0.6))))));
  145.  
  146.     // </Tutorial>
  147.  
  148.     // <Tutorial Section=3.3>
  149.     // Here, construct moving clouds by translating an infinitely tiled
  150.     // image of clouds by the windDistance.
  151.     Bbox2Bvr BB = clouds.boundingBox();
  152.  
  153.     Vector2Bvr cloudPos = 
  154.       vector2(neg(mod(div(windDistance, toBvr(200)), 
  155.         sub(BB.getMax().getX(), BB.getMin().getX()) )), toBvr(0));
  156.  
  157.     ImageBvr xfClouds = clouds.transform(translate(cloudPos));
  158.  
  159.     ImageBvr movingClouds = xfClouds.tile().crop(BB.getMin(), BB.getMax());
  160.  
  161.     // </Tutorial>
  162.  
  163.     Point2Bvr kitePos = (Point2Bvr)
  164.       add( point2(mul(toBvr(-220),pixelBvr), mul(toBvr(100),pixelBvr)),                     
  165.         vector2(mul(add( mul( toBvr(30), mul(windSpeed,sin(localTime))), 
  166.           mul(toBvr(4),random)), pixelBvr), 
  167.             mul(add( mul( toBvr(10), 
  168.           mul(windSpeed,sin(add(localTime,toBvr(5))))),
  169.                 mul(toBvr(4),random)), pixelBvr))).runOnce();
  170.  
  171.     Point2Bvr bboyPos  = point2( mul(toBvr(-45),pixelBvr), mul(toBvr(-125),pixelBvr));
  172.     Point2Bvr startPos = add( kitePos, vector2(mul(toBvr(34),pixelBvr), mul(toBvr(10),pixelBvr)) );
  173.     Point2Bvr endPos   = add( bboyPos, vector2(mul(toBvr(-17),pixelBvr), mul(toBvr(16),pixelBvr)) ); 
  174.     Point2Bvr halfPos  = add( startPos, sub(endPos, startPos).mul(toBvr(.5)));
  175.                 
  176.     NumberBvr[] knots = { toBvr(0), toBvr(0), toBvr(0), 
  177.             toBvr(1), toBvr(2), toBvr(2), toBvr(2) };
  178.  
  179.     NumberBvr[] weights = { toBvr(1), toBvr(1), toBvr(1), toBvr(1), toBvr(1) };
  180.  
  181.     Point2Bvr[] points =  {
  182.       endPos,
  183.       add(add( halfPos, sub(endPos, halfPos).mul(toBvr(.5)) ),
  184.       vector2(mul(mul(toBvr(15),pixelBvr), sin(localTime)), toBvr(0)) ),
  185.       halfPos,
  186.       add(add( startPos, sub(halfPos, startPos).mul(toBvr(.5)) ),
  187.       vector2(mul(mul(toBvr(-25),pixelBvr), sin(localTime)), toBvr(0)) ),
  188.       startPos 
  189.     };
  190.  
  191.     Path2Bvr stringPth2 = cubicBSplinePath(points, knots);
  192.     LineStyleBvr stringLn = defaultLineStyle.width(toBvr(0.1*mm));
  193.     ColorBvr stringCol = colorRgb(toBvr(140/256),toBvr(181/256),toBvr(241/256));
  194.     ImageBvr kiteString = stringPth2.draw(stringLn.color(stringCol));
  195.  
  196.     // <Tutorial Section=3.4>
  197.     // Here, create an array of all the time-varying sprite images, using
  198.     // loopStrip and showIndex where appropiate.
  199.     ImageBvr[] sprites = {             
  200.       kite.loopStrip(add(windSpeed,toBvr(.2))), 
  201.       scene,
  202.       ocean1.showIndex(oceanIndex),
  203.       ocean2.showIndex(oceanIndex),
  204.       palm1.showIndex(treeIndex1),
  205.       palm2.showIndex(treeIndex2),
  206.       palm3.showIndex(treeIndex3),
  207.       movingClouds,
  208.       beachboy.loopStrip(toBvr(1))
  209.     };
  210.         
  211.     // Also create an array of points which will be used as the initial
  212.     // positions of the above mentioned time-varying sprites. 
  213.     Point2Bvr[] spritePos = { 
  214.       kitePos,
  215.       point2(toBvr(         0),toBvr(         0)),
  216.       point2(mul(toBvr(  78),pixelBvr),mul(toBvr(-100),pixelBvr)),
  217.       point2(mul(toBvr(-252),pixelBvr),mul(toBvr( -21),pixelBvr)),
  218.       point2(mul(toBvr(-250),pixelBvr),mul(toBvr(  20),pixelBvr)),
  219.       point2(mul(toBvr(-158),pixelBvr),mul(toBvr(   2),pixelBvr)),
  220.       point2(mul(toBvr( -98),pixelBvr),mul(toBvr(  -1),pixelBvr)),
  221.       point2(toBvr(         0),mul(toBvr( 106),pixelBvr)),
  222.       bboyPos
  223.     };
  224.  
  225.     // Then apply the sprite translation to all the sprites, to move them
  226.     // to their initial location.
  227.     ImageBvr[] iSprites = new ImageBvr[9];
  228.       for(int i=0; i<9; i++) 
  229.         iSprites[i] = sprites[i]
  230.           .transform(translate(sub(spritePos[i],origin2)));
  231.  
  232.     // Overlay all the 2-D sprites (the first overlay is at the top
  233.     // and the last will be at the bottom).  This new composite is made 
  234.     // pickable for later use.
  235.     PickableImage allSprites = new PickableImage(
  236.       overlay(iSprites[0], overlay(iSprites[8], overlay(kiteString,
  237.         overlay(iSprites[4], overlay(iSprites[5], overlay(iSprites[6],
  238.           overlay(iSprites[7], overlay(iSprites[2], 
  239.             overlay(iSprites[3], iSprites[1] ))))))))));
  240.  
  241.     // Finally, obtain the upper right corner of the composite, which will
  242.     // be used to scale the geometries.
  243.     Point2Bvr imageUR = allSprites.getImageBvr().boundingBox().getMax();
  244.  
  245.     // </Tutorial>
  246.  
  247.     // <Tutorial Section=6.1>
  248.     // The original path is then constructed with the help of the figure8
  249.     // utility method.  This method uses the spline method to return a path
  250.     // in the form of a figure8.
  251.     Vector3Bvr subPath = 
  252.       (Vector3Bvr)(add( DxmVector3.figure8(),
  253.         vector3(sin(localTime), 
  254.           sin(mul(localTime, toBvr(.25))),
  255.             sin(mul(localTime, toBvr(.33))))
  256.           .mul(toBvr(.25)) )).runOnce();
  257.                                 
  258.     _originalPath = (Vector3Bvr)
  259.       subPath.transform(
  260.         compose( rotate(xVector3, toBvr(Math.PI/8)), 
  261.           rotate(yVector3, toBvr(Math.PI/2)) ))
  262.           .substituteTime(div(localTime, toBvr(2)));
  263.  
  264.     // Next an event is created that triggers when the left mouse button is
  265.     // pressed on the pickable image that was created in section 3.
  266.     _grabEvent = andEvent(leftButtonDown, allSprites.getPickEvent()); 
  267.  
  268.     // Construct two class objects.  Their classes handle the behavior of the
  269.     // bird when it is grabbed, and when it is released.
  270.     _grabbedNotify = new grabbedBird(this);
  271.     _thrownNotify = new thrownBird(this);
  272.  
  273.     // </Tutorial>
  274.  
  275.     // <Tutorial Section=6.4>
  276.     // Create a behavior that starts out as _original path, and then changes to
  277.     // the behavior constructed in the grabbedBird class when _grabEvent is
  278.     // triggered.
  279.     Vector3Bvr finalPath = (Vector3Bvr)untilNotify(
  280.       _originalPath, _grabEvent, _grabbedNotify);
  281.  
  282.     // Create an event that gets triggered when the left mouse button is
  283.     // released.  Also create an event that snapshots the value of finalPath
  284.     // when throwEvent is triggered.
  285.     DXMEvent throwEvent = leftButtonUp;
  286.     DXMEvent throwSnapEvent = throwEvent.snapshotEvent(finalPath);
  287.  
  288.     // Create a behavior that starts out as finalPath, and then changes to
  289.     // the behavior constructed in the thrownBird class when throwSnapEvent is
  290.     // triggered.  This is the composite seagull path that allows the user to 
  291.     // grab it, drag it, and release it.
  292.     _actualPath = (Vector3Bvr)untilNotify(
  293.       finalPath, throwSnapEvent, _thrownNotify);
  294.     // </Tutorial>
  295.  
  296.     // <Tutorial Section=6.0 Title="Section6: Making the seagull react to the mouse">
  297.     // This section describes how to make the seagull react to the user's
  298.     // mouse.  When the user clicks and holds down the left mouse button,
  299.     // the seagull can be dragged across the screen.  When the mouse button is
  300.     // released, the seagull returns to its original path.
  301.     // First construct the seagull in the same manner as the weather vane in
  302.     // Section 4, and apply its flight path to it.
  303.     seagullSynth seagull = new seagullSynth(_actualPath);        
  304.     GeometryBvr gull = seagull.getGeo(true, mediaBase)
  305.       .transform(translate(toBvr(0), toBvr(.5), toBvr(0)));
  306.  
  307.     // </Tutorial>
  308.  
  309.     // <Tutorial Section=4.3>
  310.     // Overlay all of the rendered geometry images over the image composed of
  311.     // all the sprites.
  312.     ImageBvr composite =
  313.       overlay(geometryImage(gull, imageUR), 
  314.         overlay(geometryImage(sailBoat.getGeo(windSpeed, geoBase), imageUR),                     
  315.           overlay(geometryImage(vane, imageUR), allSprites.getImageBvr())));
  316.  
  317.     // </Tutorial>
  318.  
  319.     // To see a script version of animated trees, comment out the setImage(composite) line,
  320.     // and activate the line below.
  321.  
  322.     //setImage(overlay(finalPalm,solidColorImage(white)));
  323.  
  324.     setImage(composite);
  325.  
  326.     // <Tutorial Section=5.0 Title="Section5: Using Synthetic Audio">
  327.     // Here, compose the audio for this demo.  Both the ocean and seagull
  328.     // classes have static methods for defining the sound.  This tutorial 
  329.     // discusses how the sound is generated for the ocean module.
  330.     setSound(mix(ocean.getSound(sndBase), seagull.getSound(sndBase))); 
  331.  
  332.     // </Tutorial>
  333.   }
  334.  
  335.   // <Tutorial Section=3.2>
  336.   // Here is how these two methods operate.
  337.   private NumberBvr animateOcean(DXMEvent ev)  {
  338.  
  339.     //Sequence for clam ocean.
  340.     Behavior[] calm = { toBvr(0), toBvr(1) }; 
  341.  
  342.     //Sequence for crashing wave.
  343.     Behavior[] wave = { toBvr(2), toBvr(3), toBvr(2) };
  344.  
  345.     // Cycles through the calm values every 0.5 seconds.
  346.     Cycler calmCycle = new Cycler(calm, timer(toBvr(.5)));
  347.  
  348.     // Cycles through the calm values every 0.25 seconds.
  349.     Cycler crashCycle = new Cycler(wave, timer(toBvr(.25)));
  350.  
  351.     // Use an uninitialized behavior so it can refer to itself.
  352.     NumberBvr animateOcean = NumberBvr.newUninitBvr();
  353.  
  354.     // The behavior is calmCycle until ev, then crashCycle for 0.75 seconds,
  355.     // and then back to the initial state.
  356.     animateOcean.init((NumberBvr)
  357.       until(calmCycle.getBvr(), ev, 
  358.         until(crashCycle.getBvr(), timer(toBvr(.75)), animateOcean)));
  359.  
  360.     return animateOcean;
  361.   }
  362.  
  363.   private NumberBvr animateTrees(NumberBvr windSpeed, double seed)  {
  364.  
  365.     // A jitter method gives a semi-random value that oscillates between
  366.     // zero and one.
  367.     NumberBvr jitter = 
  368.       floor(mod(
  369.         add(DxmNumber.smooth0to1(mul(sub(toBvr(1.2),windSpeed), toBvr(10*seed))),                                         
  370.           (NumberBvr)_breezeInterval.getBvr()),
  371.             toBvr(2)));
  372.  
  373.     // As the wind speed increases, the trees tend to bend back.  To get this
  374.     // effect increase the cell image used.
  375.     NumberBvr normalIndex = floor(mul(toBvr(3), windSpeed));
  376.  
  377.     // The final index jiggles the bent tree.
  378.     return add(normalIndex, jitter);
  379.   }
  380.   // </Tutorial>
  381.  
  382.   // <Tutorial Section=4.2>
  383.   // In order to use the geometry in the scene, convert the geometry
  384.   // to an image.  Here, define a helper method that takes any geometry,
  385.   // and the upper right corner of the scene.  It then return a rendered and
  386.   // scaled image.
  387.   private ImageBvr geometryImage(GeometryBvr geo, Point2Bvr UR)  {
  388.     CameraBvr camera = 
  389.       perspectiveCamera(toBvr(1),toBvr(0))
  390.         .transform(translate(toBvr(0), toBvr(0), toBvr(2)));
  391.         
  392.     NumberBvr scaleFactor = (NumberBvr) 
  393.       cond(lte(UR.getX(), UR.getY()), UR.getX(), UR.getY());
  394.         
  395.     return geo.render(camera).transform(scale2(mul(toBvr(2.8), scaleFactor)));                      
  396.   }
  397.  
  398.   // </Tutorial>
  399.  
  400.   // <Tutorial Section=2.2>
  401.   // Above, you saw the use of the generateWindAngle to actually get the random
  402.   // value, here is what the method does:
  403.   private NumberBvr generateWindAngle(BvrsToRun blist)  {
  404.     ModifiableBehavior breezeAngle = new ModifiableBehavior(toBvr(0));
  405.  
  406.     // Explicitly start a seededRandom behavior.
  407.     NumberBvr random = seededRandom(.27);  
  408.     blist.add(random);
  409.  
  410.     // Create an event which triggers after each breeze interval passes.
  411.     DXMEvent timerEv = timer((NumberBvr)_breezeInterval.getBvr());
  412.  
  413.     // Attach the random value as eventData.
  414.     timerEv = timerEv.snapshotEvent(random); 
  415.         
  416.     // You want a number behavior that refers to itself, so use newUninitBvr().        
  417.     NumberBvr loop = NumberBvr.newUninitBvr();
  418.  
  419.     // To get a randomly changing breeze, create a method that will 
  420.     // get called every time the timerEv event occurs, then smoothly
  421.     // transitions our current breezeAngle to a new random breezeAngle.
  422.     // This is the purpose of the newBreeze class, which extends UntilNotifier.
  423.     // Pass it the values it will need to construct and return a new behavior.
  424.     UntilNotifier newBreeze = new Breeze((NumberBvr)_windSpeed.getBvr(),
  425.       _breezeInterval, breezeAngle, loop);
  426.  
  427.     NumberBvr angle = (NumberBvr)breezeAngle.getBvr();
  428.  
  429.     timerEv = andEvent(timerEv, timerEv.snapshotEvent(angle));
  430.  
  431.     // Now, initialize the loop to the value that is the current breezeAngle,
  432.     // until timerEv, when the newBreeze notify method is called.
  433.     loop.init((NumberBvr)untilNotify(angle, timerEv, newBreeze));
  434.                 
  435.     // return this value as the actual breeze angle.          
  436.     return loop; 
  437.   }
  438.  
  439.   // </Tutorial>
  440.  
  441.   private ImageBvr cropStrip(ImageBvr img, NumberBvr index, NumberBvr frames)  {
  442.     Bbox2Bvr stripBBx2 = img.boundingBox();
  443.     Point2Bvr minPt2 = stripBBx2.getMin();
  444.     Point2Bvr maxPt2 = stripBBx2.getMax();
  445.     NumberBvr ttlHghtNum = sub(maxPt2.getY(), minPt2.getY());
  446.     NumberBvr frmHghtNum = div(ttlHghtNum, frames);
  447.  
  448.     Point2Bvr crop1Pt2 = point2(minPt2.getX(), 
  449.       div(neg(frmHghtNum),toBvr(2)));
  450.     Point2Bvr crop2Pt2 = point2(maxPt2.getX(), 
  451.       div(frmHghtNum,toBvr(2)));
  452.  
  453.     NumberBvr temp1_1 = add(ttlHghtNum, frmHghtNum);
  454.     NumberBvr temp1_2 = mul(index, frmHghtNum);
  455.     NumberBvr temp1_3 = div(temp1_1,toBvr(2));
  456.     NumberBvr temp1_4 = sub(temp1_2, temp1_3);
  457.  
  458.     Transform2Bvr temp = translate(toBvr(0), temp1_4);
  459.  
  460.     ImageBvr temp2 = img.transform(temp);
  461.     
  462.     return temp2.crop(crop1Pt2, crop2Pt2);
  463.   }
  464.  
  465.   private ImageBvr animate(ImageBvr img, NumberBvr frames, NumberBvr index)  {
  466.     BooleanBvr condBool = lte(frames,toBvr(1));
  467.  
  468.     NumberBvr temp1 = floor(index);
  469.     NumberBvr temp2 = mod(temp1, frames);
  470.     NumberBvr temp3 = add(temp2, toBvr(1));
  471.  
  472.     ImageBvr cropImg = cropStrip(img, temp3  ,frames);
  473.  
  474.     return (ImageBvr)cond(condBool,img,cropImg);
  475.   }
  476.  
  477.   private ImageBvr applySettings(ImageBvr img, Point2Bvr pt, 
  478.     NumberBvr frames, NumberBvr index)  {
  479.     return animate(img, frames, index).transform(translate(sub(pt, origin2)));
  480.   }
  481.  
  482.   private NumberBvr treeFrame (NumberBvr windMode, NumberBvr num)  {
  483.     NumberBvr clamp1Num = mul(div(sub(windMode,toBvr(2)),toBvr(8)),toBvr(3));
  484.  
  485.     NumberBvr random = seededRandom(0.1);
  486.  
  487.  
  488.     // added extra tweak (*0.9) to stretch the sine function out.
  489.     NumberBvr condNum = sin(mul(mul(mul(windMode,random),num),toBvr(0.9)));
  490.  
  491.     BooleanBvr condBool = lte(condNum,toBvr(0));
  492.     
  493.     NumberBvr jitterNum = (NumberBvr)cond(condBool,toBvr(0),toBvr(1));
  494.  
  495.     return add(DxmNumber.clamp(clamp1Num, toBvr(0), toBvr(3)),jitterNum);
  496.   }
  497. }
  498.  
  499. // <Tutorial Section=2.3>
  500. // The callback serves an important function in DirectAnimation. It allows
  501. // the reconstruction of a running behavior based on its current value and
  502. // the time that it was triggered.  In the breeze class, the sample uses the 
  503. // constructor to pass it all the values it needs to update the current
  504. // behavior.
  505. class Breeze extends Statics implements UntilNotifier {
  506.   private ModifiableBehavior _breezeInterval;
  507.   private ModifiableBehavior _breezeAngle;
  508.   private NumberBvr _windSpeed;
  509.   private NumberBvr _loop;
  510.   
  511.   public Breeze(NumberBvr windSpeed, ModifiableBehavior breezeInterval, 
  512.         ModifiableBehavior breezeAngle, NumberBvr loop)  {
  513.     _windSpeed = windSpeed;
  514.     _breezeInterval = breezeInterval; 
  515.     _breezeAngle = breezeAngle; 
  516.     _loop = loop;
  517.    }
  518.  
  519.   // Now in the notify method, you can do some actual work.
  520.   public Behavior notify(Object eventData, Behavior curBvr, BvrsToRun lst)  { 
  521.         
  522.     // First snapshot (freeze) the random value attached as eventData.
  523.     NumberBvr random = (NumberBvr)((PairObject)eventData).getFirst(); 
  524.         
  525.     // Also snapshot the current breezeAngle based on the event time.
  526.     NumberBvr oldAngle = (NumberBvr)((PairObject)eventData).getSecond(); 
  527.         
  528.     // Calculate the difference delta between the two and transition the 
  529.     // breezeAngle from one to the other over a period of two seconds.
  530.     NumberBvr deltaAngle = mul(toBvr(2), sub(random,toBvr(.5)));
  531.  
  532.     _breezeInterval.switchTo(mul(toBvr(5), random));
  533.     _breezeAngle.switchTo(add(oldAngle,deltaAngle));
  534.  
  535.     NumberBvr changingAngle = 
  536.       add(oldAngle, mul(deltaAngle, DxmNumber.smooth0to1(toBvr(2))));
  537.  
  538.     return (NumberBvr)until(changingAngle, timer(toBvr(2)), _loop);                           
  539.   }
  540. }
  541. // </Tutorial>
  542.  
  543. // <Tutorial Section=6.2>
  544. // The grabbedBird is a callback just like the Breeze class in Section 2.
  545. // It links the movement of the bird to the position of the mouse pointer.
  546. class grabbedBird extends Statics implements UntilNotifier  {
  547.   private lhouseModel _model;     
  548.  
  549.   public grabbedBird(lhouseModel model)  { 
  550.     _model = model; 
  551.   }
  552.  
  553.   // Now in the notify method you can do some actual work.
  554.   public Behavior notify(Object eventData, Behavior curBvr, BvrsToRun lst)  {                                            
  555.     // Grab the position of the mouse pointer from the eventData tree.
  556.     Vector2Bvr offset = 
  557.       (Vector2Bvr)((PairObject)((PairObject)eventData)
  558.         .getSecond()).getSecond();
  559.         
  560.     Vector3Bvr offset3d = 
  561.       vector3( offset.getX(), mul(toBvr(.75), offset.getY()), 
  562.       mul(toBvr(-.25),offset.getY()) ).mul(toBvr(30));
  563.         
  564.     Vector3Bvr newPath = add((Vector3Bvr)curBvr, offset3d);
  565.  
  566.     return newPath;
  567.   }
  568. }
  569.  
  570. // </Tutorial>
  571.  
  572. // <Tutorial Section=6.3>
  573. // The thrownBird is a callback just like the Breeze class in Section 2.
  574. // It returns the seagull to its original path when it's released by the user.
  575. class thrownBird extends Statics implements UntilNotifier  {
  576.   private lhouseModel _model;     
  577.     
  578.   public thrownBird(lhouseModel model)  {
  579.     _model = model; 
  580.   }
  581.  
  582.   // Now in the notify method you can do some actual work.
  583.   public Behavior notify(Object eventData, Behavior curBvr, BvrsToRun lst)  {
  584.     // Obtain actualPath from the lhouseModel class. 
  585.     Vector3Bvr originalPath = _model._actualPath;
  586.  
  587.     // Obtain the initial position of the seagull.
  588.     Vector3Bvr pathVector = (Vector3Bvr)originalPath
  589.       .substituteTime(toBvr(0));
  590.  
  591.     // Create a 3-D vector that contains the postion of the finalPath vector
  592.     // (in class lhouseModel) when throwEvent is triggered.
  593.     Vector3Bvr throwVector = (Vector3Bvr)eventData;
  594.  
  595.     Vector3Bvr throwDirection = derivative((Vector3Bvr)curBvr);
  596.     Vector3Bvr pathDirection = vector3(toBvr(-1), toBvr(0), toBvr(0));
  597.             
  598.     Vector3Bvr[] points = {
  599.       throwVector,
  600.       add(throwVector, throwDirection),
  601.       add(add(throwVector, pathVector).div(toBvr(2)), 
  602.       sub(throwDirection, pathDirection)),
  603.       sub(pathVector, pathDirection.div(toBvr(3))),
  604.       pathVector                  
  605.     };
  606.             
  607.     NumberBvr[] knots = { toBvr(0), toBvr(0), toBvr(0),
  608.       toBvr(1), toBvr(2), toBvr(2), toBvr(2) };
  609.     NumberBvr[] weights = { toBvr(1), toBvr(1), 
  610.       toBvr(1), toBvr(1), toBvr(1) };
  611.  
  612.     // Create a path from the point of where the seagull was released,
  613.     // to its original starting point.
  614.     Vector3Bvr spline = bSpline(3, knots, points, weights,localTime);
  615.         
  616.     // Let the seagull fly the path in two seconds, and then return to its
  617.     // original path.
  618.     Vector3Bvr throwPath = (Vector3Bvr)
  619.       until( spline, timer(toBvr(2)),originalPath);
  620.         
  621.     return throwPath;                                                               
  622.   }
  623. }
  624.  
  625. // </Tutorial>
  626.  
  627.