text: // Geometric Tools, LLC
  // Copyright (c) 1998-2014
  // Distributed under the Boost Software License, Version 1.0.
  // http://www.boost.org/LICENSE_1_0.txt
  // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  //
  // File Version: 5.0.0 (2010/01/01)
  
  #include "BSplineCurveFitter.h"
  
  WM5_WINDOW_APPLICATION(BSplineCurveFitter);
  
  //----------------------------------------------------------------------------
  BSplineCurveFitter::BSplineCurveFitter ()
      :
      WindowApplication3("SampleMathematics/BSplineCurveFitter", 0, 0, 640, 480,
          Float4(1.0f, 1.0f, 1.0f, 1.0f)),
          mTextColor(0.0f, 0.0f, 0.0f, 1.0f)
  {
      mSpline = 0;
      mSamples = 0;
  }
  //----------------------------------------------------------------------------
  bool BSplineCurveFitter::OnInitialize ()
  {
      if (!WindowApplication3::OnInitialize())
      {
          return false;
      }
  
      // Set up the camera.
      mCamera->SetFrustum(60.0f, GetAspectRatio(), 0.1f, 100.0f);
      APoint camPosition(0.0f, 0.0f, -4.0f);
      AVector camDVector(0.0f, 0.0f, 1.0f);
      AVector camUVector(0.0f, 1.0f, 0.0f);
      AVector camRVector = camDVector.Cross(camUVector);
      mCamera->SetFrame(camPosition, camDVector, camUVector, camRVector);
  
      CreateScene();
  
      // Initial update of objects.
      mScene->Update();
  
      // Initial culling of scene.
      mCuller.SetCamera(mCamera);
      mCuller.ComputeVisibleSet(mScene);
  
      InitializeCameraMotion(0.1f, 0.01f);
      InitializeObjectMotion(mScene);
      return true;
  }
  //----------------------------------------------------------------------------
  void BSplineCurveFitter::OnTerminate ()
  {
      delete0(mSpline);
      delete1(mSamples);
  
      mScene = 0;
      mEffect = 0;
  
      WindowApplication3::OnTerminate();
  }
  //----------------------------------------------------------------------------
  void BSplineCurveFitter::OnIdle ()
  {
      MeasureTime();
  
      if (MoveCamera())
      {
          mCuller.ComputeVisibleSet(mScene);
      }
  
      if (MoveObject())
      {
          mScene->Update();
          mCuller.ComputeVisibleSet(mScene);
      }
  
      if (mRenderer->PreDraw())
      {
          mRenderer->ClearBuffers();
          mRenderer->Draw(mCuller.GetVisibleSet());
  
          sprintf(mMessage, "samples = %d, degree = %d, controls = %d",
              mNumSamples, mDegree, mNumCtrlPoints);
          mRenderer->Draw(8, 16, mTextColor, mMessage);
  
          sprintf(mMessage, "avr error = %f, rms error = %f", mAvrError,
              mRmsError);
          mRenderer->Draw(8, 36, mTextColor, mMessage);
  
          mRenderer->PostDraw();
          mRenderer->DisplayColorBuffer();
      }
  
      UpdateFrameCount();
  }
  //----------------------------------------------------------------------------
  bool BSplineCurveFitter::OnKeyDown (unsigned char key, int x, int y)
  {
      if (WindowApplication3::OnKeyDown(key, x, y))
      {
          return true;
      }
  
      switch (key)
      {
      case 'd':  // reduce the degree
          if (mDegree > 1)
          {
              --mDegree;
              CreateBSplinePolysegment();
              mScene->Update();
              mCuller.ComputeVisibleSet(mScene);
          }
          return true;
  
      case 'D':  // increase the degree
          ++mDegree;
          CreateBSplinePolysegment();
          mScene->Update();
          mCuller.ComputeVisibleSet(mScene);
          return true;
  
      case 's':  // (small) reduce the number of control points by 1
          if (mNumCtrlPoints - 1 > mDegree + 1)
          {
              --mNumCtrlPoints;
              CreateBSplinePolysegment();
              mScene->Update();
              mCuller.ComputeVisibleSet(mScene);
          }
          return true;
  
      case 'S':  // (small) increase the number of control points by 1
          if (mNumCtrlPoints + 1 < mNumSamples)
          {
              ++mNumCtrlPoints;
              CreateBSplinePolysegment();
              mScene->Update();
              mCuller.ComputeVisibleSet(mScene);
          }
          return true;
  
      case 'm':  // (medium) reduce the number of control points by 10
          if (mNumCtrlPoints - 10 > mDegree + 1)
          {
              mNumCtrlPoints -= 10;
              CreateBSplinePolysegment();
              mScene->Update();
              mCuller.ComputeVisibleSet(mScene);
          }
          return true;
  
      case 'M':  // (medium) increase the number of control points by 10
          if (mNumCtrlPoints + 10 < mNumSamples)
          {
              mNumCtrlPoints += 10;
              CreateBSplinePolysegment();
              mScene->Update();
              mCuller.ComputeVisibleSet(mScene);
          }
          return true;
  
      case 'l':  // (large) reduce the number of control points by 100
          if (mNumCtrlPoints - 100 > mDegree + 1)
          {
              mNumCtrlPoints -= 100;
              CreateBSplinePolysegment();
              mScene->Update();
              mCuller.ComputeVisibleSet(mScene);
          }
          return true;
  
      case 'L':  // (large) increase the number of control points by 100
          if (mNumCtrlPoints + 100 < mNumSamples)
          {
              mNumCtrlPoints += 100;
              CreateBSplinePolysegment();
              mScene->Update();
              mCuller.ComputeVisibleSet(mScene);
          }
          return true;
      }
  
      return false;
  }
  //----------------------------------------------------------------------------
  void BSplineCurveFitter::CreateScene ()
  {
      mScene = new0 Node();
  
      // Generate a spiral curve on a sphere and visualize as a randomly
      // colored polyline.
      VertexFormat* vformat = VertexFormat::Create(2,
          VertexFormat::AU_POSITION, VertexFormat::AT_FLOAT3, 0,
          VertexFormat::AU_COLOR, VertexFormat::AT_FLOAT3, 0);
      int vstride = vformat->GetStride();
  
      mNumSamples = 1000;
      VertexBuffer* vbuffer = new0 VertexBuffer(mNumSamples, vstride);
      VertexBufferAccessor vba(vformat, vbuffer);
  
      mSamples = new1<Vector3f>(mNumSamples);
      float mult = 2.0f/(mNumSamples - 1), t;
      int i;
      for (i = 0; i < mNumSamples; ++i)
      {
          t = -1.0f + mult*i;
          float angle = 2.0f*Mathf::TWO_PI*t;
          float amplitude = 1.0f - t*t;
  
          mSamples[i].X() = amplitude*Mathf::Cos(angle);
          mSamples[i].Y() = amplitude*Mathf::Sin(angle);
          mSamples[i].Z() = t;
          mSamples[i].Normalize();
          vba.Position<Vector3f>(i) = mSamples[i];
          vba.Color<Float3>(0, i) = Float3(Mathf::UnitRandom(),
              Mathf::UnitRandom(), Mathf::UnitRandom());
      }
  
      mEffect = new0 VertexColor3Effect();
  
      Polysegment* segment = new0 Polysegment(vformat, vbuffer, true);
      segment->SetEffectInstance(mEffect->CreateInstance());
      mScene->AttachChild(segment);
  
      // Generate a least-squares fit to the spiral.  The degree and number of
      // control points can be modified during run time.
      mDegree = 3;
      mNumCtrlPoints = mNumSamples/2;
      CreateBSplinePolysegment();
  }
  //----------------------------------------------------------------------------
  void BSplineCurveFitter::CreateBSplinePolysegment ()
  {
      delete0(mSpline);
  
      // Create the curve from the current parameters.
      mSpline = new0 BSplineCurveFitf(3, mNumSamples, (const float*)mSamples,
          mDegree, mNumCtrlPoints);
  
      // Sample it the same number of times as the original data.
      VertexFormat* vformat = VertexFormat::Create(2,
          VertexFormat::AU_POSITION, VertexFormat::AT_FLOAT3, 0,
          VertexFormat::AU_COLOR, VertexFormat::AT_FLOAT3, 0);
      int vstride = vformat->GetStride();
  
      VertexBuffer* vbuffer = new0 VertexBuffer(mNumSamples, vstride);
      VertexBufferAccessor vba(vformat, vbuffer);
  
      float mult = 1.0f/(mNumSamples - 1);
      int i;
      for (i = 0; i < mNumSamples; ++i)
      {
          mSpline->GetPosition(mult*i, (float*)vba.Position<Vector3f>(i));
      }
  
      Polysegment* segment = new0 Polysegment(vformat, vbuffer, true);
      segment->SetEffectInstance(mEffect->CreateInstance());
      mScene->SetChild(1, segment);
  
      // Compute error measurements.
      mAvrError = 0.0f;
      mRmsError = 0.0f;
      for (i = 0; i < mNumSamples; ++i)
      {
          Vector3f diff = mSamples[i] - vba.Position<Vector3f>(i);
          float sqrLength = diff.SquaredLength();
          mRmsError += sqrLength;
          float length = Mathf::Sqrt(sqrLength);
          mAvrError += length;
      }
      mAvrError /= (float)mNumSamples;
      mRmsError /= (float)mNumSamples;
      mRmsError = Mathf::Sqrt(mRmsError);
  }
  //----------------------------------------------------------------------------
  