#include "ChangeChartTypeTransition.hpp"
#include "ParallelTransition.hpp"
#include "AtomicSequentialTransition.hpp"
#include "CompoundSequentialTransition.hpp"
#include "MoveTransition.hpp"
#include "ScaleTransition.hpp"
#include "RotateTransition.hpp"
#include "MorphTransition.hpp"
#include "BlendTransition.hpp"
#include "Delay.hpp"
#include "Bar.hpp"
#include "PieSector.hpp"
#include "MorphElement.hpp"
#include "StackedBar.hpp"
#include "GroupedBar.hpp"

ChangeChartTypeTransition::ChangeChartTypeTransition(Chart* pInitialChart, int iChartIndex) {
  iType = CHANGE_CHART_TYPE_TRANSITION;
  bStarted = false;
  this->pInitialChart = pInitialChart;
  bool bDescending = pInitialChart->GetDataSet()->GetSortingOrder();

  switch (pInitialChart->GetType()) {
    case BAR_CHART: {
      std::vector<DataElement*> vInitialBars = pInitialChart->GetDataElements();

      switch (iChartIndex) {
        case GROUPED_BAR_CHART: {
          pResultChart = new GroupedBarChart(pInitialChart->GetDataSet());

          //parallel transition to move all bars at once
          ParallelTransition* pParallelTransition = new ParallelTransition();
          AddTransition(pParallelTransition);

          for (unsigned int i = 0; i < vInitialBars.size(); i++) {
            Bar* pInitialBar = (Bar*) vInitialBars.at(i);
            Bar* pResultBar = (Bar*) ((GroupedBarChart*) pResultChart)->GetConnectedBar(pInitialBar->GetDataEntry());

            if (pResultBar != NULL) {
              pParallelTransition->AddTransition(new MoveTransition(pInitialBar,
                pResultBar->GetLeft() - pInitialBar->GetLeft(), 0.0f, 0.0f, true, MOVE_STEPS));
              pParallelTransition->AddTransition(new ScaleTransition(pInitialBar,
                pResultBar->GetWidth() - pInitialBar->GetWidth(), 0.0f, VALUE_CHANGE_SCALE_STEPS));
            }
          }

          break;
        }
        case STACKED_BAR_CHART: {
          //convert the bar chart to a grouped bar chart first, then to a stacked bar chart
          ChangeChartTypeTransition* pChangeChartTypeToGroupedBarChartTransition = new ChangeChartTypeTransition(pInitialChart, GROUPED_BAR_CHART);
          AddTransition(pChangeChartTypeToGroupedBarChartTransition);

          ChangeChartTypeTransition* pChangeChartTypeToStackedBarChartTransition = new ChangeChartTypeTransition(pChangeChartTypeToGroupedBarChartTransition->GetResultChart(), STACKED_BAR_CHART);
          AddTransition(pChangeChartTypeToStackedBarChartTransition);

          pResultChart = pChangeChartTypeToStackedBarChartTransition->GetResultChart();

          break;
        }
        case PIE_CHART: {
          //convert the stacked bar chart to a grouped bar chart first, then to a pie chart
          ChangeChartTypeTransition* pChangeChartTypeToGroupedBarChartTransition = new ChangeChartTypeTransition(pInitialChart, GROUPED_BAR_CHART);
          AddTransition(pChangeChartTypeToGroupedBarChartTransition);

          ChangeChartTypeTransition* pChangeChartTypeToPieChartTransition = new ChangeChartTypeTransition(pChangeChartTypeToGroupedBarChartTransition->GetResultChart(), PIE_CHART);
          AddTransition(pChangeChartTypeToPieChartTransition);

          pResultChart = pChangeChartTypeToPieChartTransition->GetResultChart();

          break;
        }
        default: {
          pResultChart = new BarChart(pInitialChart->GetDataSet());
        }
      }

      break;
    }
    case GROUPED_BAR_CHART: {
      std::vector<DataElement*> vInitialBars = ((GroupedBarChart*) pInitialChart)->GetAllBars();
      std::vector<DataElement*> vInitialGroupedBars = pInitialChart->GetDataElements();

      switch (iChartIndex) {
        case BAR_CHART: {
          pResultChart = new BarChart(pInitialChart->GetDataSet());

          //parallel transition to move all bars at once
          ParallelTransition* pParallelTransition = new ParallelTransition();
          AddTransition(pParallelTransition);

          for (unsigned int i = 0; i < vInitialBars.size(); i++) {
            Bar* pInitialBar = (Bar*) vInitialBars.at(i);
            Bar* pResultBar = (Bar*) ((BarChart*) pResultChart)->GetConnectedDataElement(pInitialBar->GetDataEntry());

            if (pResultBar != NULL) {
              pParallelTransition->AddTransition(new MoveTransition(pInitialBar,
                pResultBar->GetLeft() - pInitialBar->GetLeft(), 0.0f, 0.0f, true, MOVE_STEPS));
              pParallelTransition->AddTransition(new ScaleTransition(pInitialBar,
                pResultBar->GetWidth() - pInitialBar->GetWidth(), 0.0f, VALUE_CHANGE_SCALE_STEPS));
            }
          }

          break;
        }
        case STACKED_BAR_CHART: {
          pResultChart = new StackedBarChart(pInitialChart->GetDataSet());

          //sequential transition to rescale grid, blend out the old labels,
          //move and scale the bars, blend in the new labels
          AtomicSequentialTransition* pAtomicSequentialTransition = new AtomicSequentialTransition();
          AddTransition(pAtomicSequentialTransition);

          //check if the grid has to be rescaled
          AtomicSequentialTransition* pGridScaleAtomicSequentialTransition = new AtomicSequentialTransition();
          pAtomicSequentialTransition->AddTransition(pGridScaleAtomicSequentialTransition);

          double dHightestInitialValue = pInitialChart->GetHighestVisibleValue();
          double dHightestResultValue = ((StackedBarChart*) pResultChart)->GetHighestVisibleSum();

          bool bGridScale = false;

          if (((dHightestInitialValue <= GRID_LOW_VALUE) && (dHightestResultValue > GRID_LOW_VALUE)) ||
            ((dHightestInitialValue <= GRID_MID_VALUE) && (dHightestResultValue > GRID_MID_VALUE)) ||
            ((dHightestInitialValue <= GRID_SECOND_HIGH_VALUE) && (dHightestResultValue > GRID_SECOND_HIGH_VALUE))) {
              bGridScale = true;
          }

          double dResultHighValue = 0.0;
          double dInitialHighValue = 0.0;

          if (bGridScale) {
            //calculate the grid scaling factor
            int iInitialStepSize = 0;

            if (dHightestInitialValue <= GRID_LOW_VALUE) {
              dInitialHighValue = GRID_LOW_VALUE;
              iInitialStepSize = GRID_LOW_VALUE_STEP_SIZE;
            }
            else if (dHightestInitialValue <= GRID_MID_VALUE) {
              dInitialHighValue = GRID_MID_VALUE;
              iInitialStepSize = GRID_MID_VALUE_STEP_SIZE;
            }
            else if (dHightestInitialValue <= GRID_SECOND_HIGH_VALUE) {
              dInitialHighValue = GRID_SECOND_HIGH_VALUE;
              iInitialStepSize = GRID_SECOND_HIGH_VALUE_STEP_SIZE;
            }
            else {
              dInitialHighValue = GRID_HIGH_VALUE;
              iInitialStepSize = GRID_HIGH_VALUE_STEP_SIZE;
            }

            int iResultStepSize = 0;

            if (dHightestResultValue <= GRID_LOW_VALUE) {
              dResultHighValue = GRID_LOW_VALUE;
              iResultStepSize = GRID_LOW_VALUE_STEP_SIZE;
            }
            else if (dHightestResultValue <= GRID_MID_VALUE) {
              dResultHighValue = GRID_MID_VALUE;
              iResultStepSize = GRID_MID_VALUE_STEP_SIZE;
            }
            else if (dHightestResultValue <= GRID_SECOND_HIGH_VALUE) {
              dResultHighValue = GRID_SECOND_HIGH_VALUE;
              iResultStepSize = GRID_SECOND_HIGH_VALUE_STEP_SIZE;
            }
            else {
              dResultHighValue = GRID_HIGH_VALUE;
              iResultStepSize = GRID_HIGH_VALUE_STEP_SIZE;
            }

            //do the grid scaling (elements and grid lines)
            ParallelTransition* pGridScaleElementsParallelTransition = new ParallelTransition();
            pGridScaleAtomicSequentialTransition->AddTransition(pGridScaleElementsParallelTransition);

            //add new invisible grid lines
            int iNewGridLines = (int) (dResultHighValue - dInitialHighValue) / iInitialStepSize;

            for (int i = 0; i < iNewGridLines; i++) {
              GridLine* pGridLine = new GridLine(GRID_LINES * iInitialStepSize + (i + 1) * iInitialStepSize, GRID_LINES + i + 1);
              pGridLine->ChangeAlpha(-1.0f);
              pInitialChart->AddGridLine(pGridLine);
            }

            //move every grid line
            std::vector<GridLine*> vGridLines = pInitialChart->GetGridLines();

            for (unsigned int i = 0; i < vGridLines.size(); i++) {
              GridLine* pGridLine = vGridLines.at(i);

              ParallelTransition* pBlendInAndMoveGridLineParallelTransition = new ParallelTransition();
              pGridScaleElementsParallelTransition->AddTransition(pBlendInAndMoveGridLineParallelTransition);

              AtomicSequentialTransition* pBlendInAndMoveGridLineAtomicSequentialTransition = new AtomicSequentialTransition();
              pBlendInAndMoveGridLineParallelTransition->AddTransition(pBlendInAndMoveGridLineAtomicSequentialTransition);

              //blend in the new grid lines delayed
              if (i >= GRID_LINES) {
                pBlendInAndMoveGridLineAtomicSequentialTransition->AddTransition(new Delay(NEW_GRID_LINE_DELAY_IN_MS * (i - GRID_LINES)));
                pBlendInAndMoveGridLineAtomicSequentialTransition->AddTransition(new BlendTransition(pGridLine, 1.0f));
              }

              pBlendInAndMoveGridLineParallelTransition->AddTransition(new MoveTransition(pGridLine,
                0.0f, (pGridLine->GetValue() / dResultHighValue - pGridLine->GetValue() / dInitialHighValue) * (UPPER_Y - ORIGIN_Y), 0.0f, false));
            }

            //new grid lines might have been added, update vector
            vGridLines = pInitialChart->GetGridLines();

            //fade out the obsolete and fade in the additional grid lines
            ParallelTransition* pFadeOutGridLinesParallelTransition = new ParallelTransition();
            pGridScaleAtomicSequentialTransition->AddTransition(pFadeOutGridLinesParallelTransition);

            for (unsigned int i = 0; i < vGridLines.size(); i++) {
              GridLine* pGridLine = vGridLines.at(i);

              //blend out the new grid lines delayed
              if (((int) pGridLine->GetValue() % iResultStepSize != 0)) {
                pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, -1.0f));
              }
            }

            //scale the elements
            for (unsigned int i = 0; i < vInitialBars.size(); i++) {
              Bar* pInitialBar = (Bar*) vInitialBars.at(i);
              DataElement* pResultBar = (Bar*) ((StackedBarChart*) pResultChart)->GetConnectedBar(pInitialBar->GetDataEntry());

              if (pResultBar != NULL) {
                pGridScaleElementsParallelTransition->AddTransition(new ScaleTransition(pInitialBar,
                  0.0f, pResultBar->GetHeight() - pInitialBar->GetHeight(), MOVE_STEPS));
              }
            }
          }

          //blend out the labels of the individual bars
          ParallelTransition* pBlendOutBarLabelsTransition = new ParallelTransition();
          pAtomicSequentialTransition->AddTransition(pBlendOutBarLabelsTransition);

          //move the bars up to their new top value
          ParallelTransition* pMoveUpBarsTransition = new ParallelTransition();
          pAtomicSequentialTransition->AddTransition(pMoveUpBarsTransition);

          //after moving the bars up, move them to their new left value and scale them
          ParallelTransition* pMoveBarsLeftAndScaleTransition = new ParallelTransition();
          pAtomicSequentialTransition->AddTransition(pMoveBarsLeftAndScaleTransition);

          for (unsigned int i = 0; i < vInitialBars.size(); i++) {
            Bar* pInitialBar = (Bar*) vInitialBars.at(i);
            DataElement* pResultBar = (Bar*) ((StackedBarChart*) pResultChart)->GetConnectedBar(pInitialBar->GetDataEntry());

            //blend out the label of the bar
            pBlendOutBarLabelsTransition->AddTransition(new BlendTransition(pInitialBar->GetLabel(), -1.0f));

            //move and scale the bar
            if (pResultBar != NULL) {
              pMoveUpBarsTransition->AddTransition(new MoveTransition(pInitialBar, 0.0f,
                pResultBar->GetTop() - (pInitialBar->GetBottom() + pResultBar->GetHeight()),
                pResultBar->GetBottom() - pInitialBar->GetBottom(), false, MOVE_STEPS));
              pMoveBarsLeftAndScaleTransition->AddTransition(new MoveTransition(pInitialBar,
                pResultBar->GetLeft() - pInitialBar->GetLeft(), 0.0f, 0.0f, false, MOVE_STEPS));
              pMoveBarsLeftAndScaleTransition->AddTransition(new ScaleTransition(pInitialBar,
                 pResultBar->GetWidth() - pInitialBar->GetWidth(),0.0f, MOVE_STEPS));
            }
          }

          //blend in the labels of the stacked bars
          ParallelTransition* pBlendInStackedBarLabelsTransition = new ParallelTransition();
          pAtomicSequentialTransition->AddTransition(pBlendInStackedBarLabelsTransition);

          std::vector<DataElement*> vResultStackedBars = pResultChart->GetDataElements();

          for (unsigned int i = 0; i < vResultStackedBars.size(); i++) {
            Label* pResultLabel = ((StackedBar*) vResultStackedBars.at(i))->GetLabel();
            Label* pNewLabel = new Label(pResultLabel->GetText(), pResultLabel->GetStyle(),
              pResultLabel->GetLeft(), pResultLabel->GetTop(), pResultLabel->GetOrientation(),
              pResultLabel->GetAlignment());
            pNewLabel->ChangeAlpha(-1.0f);

            pInitialChart->AddLabel(pNewLabel);

            pBlendInStackedBarLabelsTransition->AddTransition(new BlendTransition(pNewLabel, 1.0f));
          }

          break;
        }
        case PIE_CHART: {
          pResultChart = new PieChart(pInitialChart->GetDataSet());

          //sequential transition to blend out grid and labels, rotate the bars,
          //move and morph them, fade in the new labels
          AtomicSequentialTransition* pSequentialTransition = new AtomicSequentialTransition();
          AddTransition(pSequentialTransition);

          //blend out the grid, axes, and labels
          ParallelTransition* pBlendOutTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pBlendOutTransition);

          std::vector<GridLine*> vGridLines = pInitialChart->GetGridLines();

          for (unsigned int i = 0; i < vGridLines.size(); i++) {
            pBlendOutTransition->AddTransition(new BlendTransition(vGridLines.at(i), -1.0f));
          }

          std::vector<Axis*> vAxes = pInitialChart->GetAxes();

          for (unsigned int i = 0; i < vAxes.size(); i++) {
            pBlendOutTransition->AddTransition(new BlendTransition(vAxes.at(i), -1.0f));
          }

          for (unsigned int i = 0; i < vInitialBars.size(); i++) {
            pBlendOutTransition->AddTransition(new BlendTransition(vInitialBars.at(i)->GetLabel(), -1.0f));
          }

          //replace the bars with morph objects
          std::vector<MorphElement*> vMorphElements;
          float fAccumulatedHeights = 0.0f;

          for (unsigned int i = 0; i < vInitialGroupedBars.size(); i++) {
            GroupedBar* pGroupedBar = (GroupedBar*) vInitialGroupedBars.at(i);
            std::vector<Bar*> vBars = pGroupedBar->GetBars();

            for (unsigned int j = 0; j < vBars.size(); j++) {
              Bar* pBar = (Bar*) vBars.at(j);
              PieSlice* pPieSlice = ((PieChart*) pResultChart)->GetConnectedSlice(pBar->GetDataEntry());

              if (pPieSlice != NULL) {
                MorphElement* pSliceMorphElement = new MorphElement(pBar->GetDataEntry(), NULL,
                  pPieSlice->GetOutterVertices() - 2, pPieSlice->GetBeginValue(), pPieSlice->GetWidth());
                vToBeDeleted.push_back(pSliceMorphElement);
                MorphElement* pBarMorphElement = new MorphElement(pBar->GetDataEntry(), pSliceMorphElement,
                  pPieSlice->GetOutterVertices() - 2, pBar->GetLeft(), pBar->GetTop(), pBar->GetBottom(), pBar->GetWidth());
                vMorphElements.push_back(pBarMorphElement);
                pInitialChart->AddDataElement(pBarMorphElement);
                fAccumulatedHeights += pBarMorphElement->GetHeight();

                pBar->ChangeAlpha(-1.0f);
              }
            }
          }

          //move and scale morph elements to connect them
          ParallelTransition* pScaleMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pScaleMorphElementsTransition);

          float fScaleFactor = 2.0f * abs(ORIGIN_X) / fAccumulatedHeights;
          float fCurrentLeft = ORIGIN_X;

          if (bDescending) {
            for (unsigned int i = 0; i < vMorphElements.size(); i++) {
              MorphElement* pMorphElement = vMorphElements.at(i);

              pScaleMorphElementsTransition->AddTransition(new ScaleTransition(pMorphElement,
                pMorphElement->GetHeight() * fScaleFactor - pMorphElement->GetWidth(),
                PIE_CHART_RADIUS - pMorphElement->GetHeight(), VALUE_CHANGE_SCALE_STEPS));
              pScaleMorphElementsTransition->AddTransition(new MoveTransition(pMorphElement,
                fCurrentLeft - pMorphElement->GetLeft(), -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - pMorphElement->GetBottom(),
                -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - pMorphElement->GetBottom(), false, VALUE_CHANGE_SCALE_STEPS));

              fCurrentLeft += pMorphElement->GetHeight() * fScaleFactor;
            }
          }
          else {
            int iIndex = 0;

            for (unsigned int i = 0; i < vInitialGroupedBars.size(); i++) {
              GroupedBar* pGroupedBar = (GroupedBar*) vInitialGroupedBars.at(i);
              std::vector<Bar*> vBars = pGroupedBar->GetBars();

              for (int j = (int) vBars.size() - 1; j >= 0; j--) {
                MorphElement* pMorphElement = vMorphElements.at(iIndex + j);

                pScaleMorphElementsTransition->AddTransition(new ScaleTransition(pMorphElement,
                  pMorphElement->GetHeight() * fScaleFactor - pMorphElement->GetWidth(),
                  PIE_CHART_RADIUS - pMorphElement->GetHeight(), VALUE_CHANGE_SCALE_STEPS));
                pScaleMorphElementsTransition->AddTransition(new MoveTransition(pMorphElement,
                  fCurrentLeft - pMorphElement->GetLeft(), -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - pMorphElement->GetBottom(),
                  -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - pMorphElement->GetBottom(), false, VALUE_CHANGE_SCALE_STEPS));

                fCurrentLeft += pMorphElement->GetHeight() * fScaleFactor;
              }

              iIndex += vBars.size();
            }
          }

          //morph the elements
          ParallelTransition* pMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pMorphElementsTransition);

          for (unsigned int i = 0; i < vMorphElements.size(); i++) {
            MorphElement* pMorphElement = vMorphElements.at(i);

            pMorphElementsTransition->AddTransition(new MorphTransition(pMorphElement, pMorphElement->GetResultElement(), MOVE_STEPS));
          }

          //fade in the new labels
          ParallelTransition* pFadeLabelsInTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pFadeLabelsInTransition);

          std::vector<DataElement*> vResultDataElements = pResultChart->GetDataElements();

          for (unsigned int i = 0; i < vResultDataElements.size(); i++) {
            Label* pResultLabel = ((PieSector*) vResultDataElements.at(i))->GetLabel();
            Label* pNewLabel = new Label(pResultLabel->GetText(), pResultLabel->GetStyle(),
              pResultLabel->GetLeft(), pResultLabel->GetTop(), pResultLabel->GetOrientation(),
              pResultLabel->GetAlignment());
            pNewLabel->ChangeAlpha(-1.0f);

            pInitialChart->AddLabel(pNewLabel);

            pFadeLabelsInTransition->AddTransition(new BlendTransition(pNewLabel, 1.0f));
          }

          break;
        }
        default: {
          pResultChart = new GroupedBarChart(pInitialChart->GetDataSet());
        }
      }

      break;
    }
    case STACKED_BAR_CHART: {
      std::vector<DataElement*> vInitialStackedBars = pInitialChart->GetDataElements();
      std::vector<DataElement*> vInitialBars = ((StackedBarChart*) pInitialChart)->GetAllBars();

      switch (iChartIndex) {
        case BAR_CHART: {
          //convert the stacked bar chart to a grouped bar chart first, then to a bar chart
          ChangeChartTypeTransition* pChangeChartTypeToGroupedBarChartTransition = new ChangeChartTypeTransition(pInitialChart, GROUPED_BAR_CHART);
          AddTransition(pChangeChartTypeToGroupedBarChartTransition);

          ChangeChartTypeTransition* pChangeChartTypeToBarChartTransition = new ChangeChartTypeTransition(pChangeChartTypeToGroupedBarChartTransition->GetResultChart(), BAR_CHART);
          AddTransition(pChangeChartTypeToBarChartTransition);

          pResultChart = pChangeChartTypeToBarChartTransition->GetResultChart();

          break;
        }
        case GROUPED_BAR_CHART: {
          pResultChart = new GroupedBarChart(pInitialChart->GetDataSet());

          //sequential transition to blend out the old labels,
          //move and scale the bars, rescale the grid, blend in the new labels
          AtomicSequentialTransition* pAtomicSequentialTransition = new AtomicSequentialTransition();
          AddTransition(pAtomicSequentialTransition);

          //blend out the labels of the stacked bars
          ParallelTransition* pBlendOutStackedBarLabelsTransition = new ParallelTransition();
          pAtomicSequentialTransition->AddTransition(pBlendOutStackedBarLabelsTransition);

          for (unsigned int i = 0; i < vInitialStackedBars.size(); i++) {
            pBlendOutStackedBarLabelsTransition->AddTransition(new BlendTransition(vInitialStackedBars.at(i)->GetLabel(), -1.0f));
          }

          for (unsigned int i = 0; i < vInitialBars.size(); i++) {
            pBlendOutStackedBarLabelsTransition->AddTransition(new BlendTransition(vInitialBars.at(i)->GetLabel(), -1.0f));
          }

          //move the bars to their new right value and scale them
          ParallelTransition* pMoveBarsRightAndScaleTransition = new ParallelTransition();
          pAtomicSequentialTransition->AddTransition(pMoveBarsRightAndScaleTransition);
          
          //move the bars down to their new bottom value
          ParallelTransition* pMoveDownBarsTransition = new ParallelTransition();
          pAtomicSequentialTransition->AddTransition(pMoveDownBarsTransition);

          for (unsigned int i = 0; i < vInitialBars.size(); i++) {
            Bar* pInitialBar = (Bar*) vInitialBars.at(i);
            Bar* pResultBar = (Bar*) ((GroupedBarChart*) pResultChart)->GetConnectedBar(pInitialBar->GetDataEntry());

            if (pResultBar != NULL) {
              pMoveBarsRightAndScaleTransition->AddTransition(new MoveTransition(pInitialBar,
                pResultBar->GetLeft() - pInitialBar->GetLeft(), 0.0f, 0.0f, false, MOVE_STEPS));
              pMoveBarsRightAndScaleTransition->AddTransition(new ScaleTransition(pInitialBar,
                pResultBar->GetWidth() - pInitialBar->GetWidth(), 0.0f, MOVE_STEPS));
              pMoveDownBarsTransition->AddTransition(new MoveTransition(pInitialBar,
                0.0f, pResultBar->GetBottom() + pInitialBar->GetHeight() - pInitialBar->GetTop(),
                pResultBar->GetBottom() - pInitialBar->GetBottom(), false, MOVE_STEPS));
            }
          }

          //check if the grid has to be rescaled
          AtomicSequentialTransition* pGridScaleAtomicSequentialTransition = new AtomicSequentialTransition();
          pAtomicSequentialTransition->AddTransition(pGridScaleAtomicSequentialTransition);

          double dHightestInitialValue = ((StackedBarChart*) pInitialChart)->GetHighestVisibleSum();
          double dHightestResultValue = ((GroupedBarChart*) pResultChart)->GetHighestVisibleValue();

          bool bGridScale = false;

          if (((dHightestResultValue <= GRID_LOW_VALUE) && (dHightestInitialValue > GRID_LOW_VALUE)) ||
            ((dHightestResultValue <= GRID_MID_VALUE) && (dHightestInitialValue > GRID_MID_VALUE)) ||
            ((dHightestResultValue <= GRID_SECOND_HIGH_VALUE) && (dHightestInitialValue > GRID_SECOND_HIGH_VALUE))) {
              bGridScale = true;
          }

          double dResultHighValue = 0.0;
          double dInitialHighValue = 0.0;

          if (bGridScale) {
            //calculate the grid scaling factor
            int iInitialStepSize = 0;

            if (dHightestInitialValue <= GRID_LOW_VALUE) {
              dInitialHighValue = GRID_LOW_VALUE;
              iInitialStepSize = GRID_LOW_VALUE_STEP_SIZE;
            }
            else if (dHightestInitialValue <= GRID_MID_VALUE) {
              dInitialHighValue = GRID_MID_VALUE;
              iInitialStepSize = GRID_MID_VALUE_STEP_SIZE;
            }
            else if (dHightestInitialValue <= GRID_SECOND_HIGH_VALUE) {
              dInitialHighValue = GRID_SECOND_HIGH_VALUE;
              iInitialStepSize = GRID_SECOND_HIGH_VALUE_STEP_SIZE;
            }
            else {
              dInitialHighValue = GRID_HIGH_VALUE;
              iInitialStepSize = GRID_HIGH_VALUE_STEP_SIZE;
            }

            int iResultStepSize = 0;

            if (dHightestResultValue <= GRID_LOW_VALUE) {
              dResultHighValue = GRID_LOW_VALUE;
              iResultStepSize = GRID_LOW_VALUE_STEP_SIZE;
            }
            else if (dHightestResultValue <= GRID_MID_VALUE) {
              dResultHighValue = GRID_MID_VALUE;
              iResultStepSize = GRID_MID_VALUE_STEP_SIZE;
            }
            else if (dHightestResultValue <= GRID_SECOND_HIGH_VALUE) {
              dResultHighValue = GRID_SECOND_HIGH_VALUE;
              iResultStepSize = GRID_SECOND_HIGH_VALUE_STEP_SIZE;
            }
            else {
              dResultHighValue = GRID_HIGH_VALUE;
              iResultStepSize = GRID_HIGH_VALUE_STEP_SIZE;
            }

            //do the grid scaling (elements and grid lines)
            ParallelTransition* pGridScaleElementsParallelTransition = new ParallelTransition();
            pGridScaleAtomicSequentialTransition->AddTransition(pGridScaleElementsParallelTransition);

            //add new invisible grid lines
            int iNewGridLines = (int) (dResultHighValue - dInitialHighValue) / iInitialStepSize;

            for (int i = 0; i < iNewGridLines; i++) {
              GridLine* pGridLine = new GridLine(GRID_LINES * iInitialStepSize + (i + 1) * iInitialStepSize, GRID_LINES + i + 1);
              pGridLine->ChangeAlpha(-1.0f);
              pInitialChart->AddGridLine(pGridLine);
            }

            //move every grid line
            std::vector<GridLine*> vGridLines = pInitialChart->GetGridLines();

            for (unsigned int i = 0; i < vGridLines.size(); i++) {
              GridLine* pGridLine = vGridLines.at(i);

              pGridScaleElementsParallelTransition->AddTransition(new MoveTransition(pGridLine,
                0.0f, pGridLine->GetValue() / dResultHighValue * (UPPER_Y - ORIGIN_Y) + ORIGIN_Y - pGridLine->GetRelativeHeight(), 0.0f, false));

              //add new invisible grid lines
              for (int j = 1; j < GRID_LINES + 1; j++) {
                GridLine* pGridLine = new GridLine(j * iResultStepSize, j);
                pGridLine->ChangeAlpha(-1.0f);
                pInitialChart->AddGridLine(pGridLine);
              }
            }

            //new grid lines might have been added, update vector
            vGridLines = pInitialChart->GetGridLines();

            //fade out the obsolete and fade in the additional grid lines
            ParallelTransition* pFadeOutGridLinesParallelTransition = new ParallelTransition();
            pGridScaleAtomicSequentialTransition->AddTransition(pFadeOutGridLinesParallelTransition);

            for (unsigned int i = 0; i < vGridLines.size(); i++) {
              GridLine* pGridLine = vGridLines.at(i);

              //blend out the new grid lines delayed
              if (pGridLine->GetValue() > dResultHighValue) {
                pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, -1.0f));
              }
              else {
                pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, 1.0f));
              }
            }

            //scale the elements
            for (unsigned int i = 0; i < vInitialBars.size(); i++) {
              Bar* pInitialBar = (Bar*) vInitialBars.at(i);
              DataElement* pResultBar = (Bar*) ((GroupedBarChart*) pResultChart)->GetConnectedBar(pInitialBar->GetDataEntry());

              if (pResultBar != NULL) {
                pGridScaleElementsParallelTransition->AddTransition(new ScaleTransition(pInitialBar,
                  0.0f, pResultBar->GetHeight() - pInitialBar->GetHeight(), MOVE_STEPS));
              }
            }
          }

          //blend in the labels of the individual bars
          ParallelTransition* pBlendInBarLabelsTransition = new ParallelTransition();
          pAtomicSequentialTransition->AddTransition(pBlendInBarLabelsTransition);

          std::vector<DataElement*> vResultBars = ((GroupedBarChart*) pResultChart)->GetAllBars();

          for (unsigned int i = 0; i < vResultBars.size(); i++) {
            Bar* pResultBar = (Bar*) vResultBars.at(i);

            Label* pResultLabel = pResultBar->GetLabel();
            Label* pNewLabel = new Label(pResultLabel->GetText(), pResultLabel->GetStyle(),
              pResultLabel->GetLeft(), pResultLabel->GetTop(), pResultLabel->GetOrientation(),
              pResultLabel->GetAlignment());
            pNewLabel->ChangeAlpha(-1.0f);

            pInitialChart->AddLabel(pNewLabel);

            pBlendInBarLabelsTransition->AddTransition(new BlendTransition(pNewLabel, 1.0f));
          }

          break;
        }
        case PIE_CHART: {
          pResultChart = new PieChart(pInitialChart->GetDataSet());

          //sequential transition to blend out grid and labels, rotate the bars,
          //move and morph them, fade in the new labels
          AtomicSequentialTransition* pSequentialTransition = new AtomicSequentialTransition();
          AddTransition(pSequentialTransition);

          //blend out the grid, axes, and labels
          ParallelTransition* pBlendOutTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pBlendOutTransition);

          std::vector<GridLine*> vGridLines = pInitialChart->GetGridLines();

          for (unsigned int i = 0; i < vGridLines.size(); i++) {
            pBlendOutTransition->AddTransition(new BlendTransition(vGridLines.at(i), -1.0f));
          }

          std::vector<Axis*> vAxes = pInitialChart->GetAxes();

          for (unsigned int i = 0; i < vAxes.size(); i++) {
            pBlendOutTransition->AddTransition(new BlendTransition(vAxes.at(i), -1.0f));
          }

          for (unsigned int i = 0; i < vInitialStackedBars.size(); i++) {
            pBlendOutTransition->AddTransition(new BlendTransition(vInitialStackedBars.at(i)->GetLabel(), -1.0f));
          }

          for (unsigned int i = 0; i < vInitialBars.size(); i++) {
            pBlendOutTransition->AddTransition(new BlendTransition(vInitialBars.at(i)->GetLabel(), -1.0f));
          }

          //replace the bars with morph objects
          std::vector<MorphElement*> vMorphElements;
          float fAccumulatedHeights = 0.0f;

          for (unsigned int i = 0; i < vInitialStackedBars.size(); i++) {
            StackedBar* pStackedBar = (StackedBar*) vInitialStackedBars.at(i);
            std::vector<Bar*> vBars = pStackedBar->GetBars();

            for (unsigned int j = 0; j < vBars.size(); j++) {
              Bar* pBar = (Bar*) vBars.at(j);
              PieSlice* pPieSlice = ((PieChart*) pResultChart)->GetConnectedSlice(pBar->GetDataEntry());

              if (pPieSlice != NULL) {
                MorphElement* pSliceMorphElement = new MorphElement(pBar->GetDataEntry(), NULL,
                  pPieSlice->GetOutterVertices() - 2, pPieSlice->GetBeginValue(), pPieSlice->GetWidth());
                vToBeDeleted.push_back(pSliceMorphElement);
                MorphElement* pBarMorphElement = new MorphElement(pBar->GetDataEntry(), pSliceMorphElement,
                  pPieSlice->GetOutterVertices() - 2, pBar->GetLeft(), pBar->GetTop(), pBar->GetBottom(), pBar->GetWidth());
                vMorphElements.push_back(pBarMorphElement);
                pInitialChart->AddDataElement(pBarMorphElement);
                fAccumulatedHeights += pBarMorphElement->GetHeight();

                pBar->ChangeAlpha(-1.0f);
              }
            }
          }

          //move and scale morph elements to connect them
          ParallelTransition* pScaleMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pScaleMorphElementsTransition);

          float fScaleFactor = 2.0f * abs(ORIGIN_X) / fAccumulatedHeights;
          float fCurrentLeft = ORIGIN_X;

          if (bDescending) {
            for (unsigned int i = 0; i < vMorphElements.size(); i++) {
              MorphElement* pMorphElement = vMorphElements.at(i);

              pScaleMorphElementsTransition->AddTransition(new ScaleTransition(pMorphElement,
                pMorphElement->GetHeight() * fScaleFactor - pMorphElement->GetWidth(),
                PIE_CHART_RADIUS - pMorphElement->GetHeight(), VALUE_CHANGE_SCALE_STEPS));
              pScaleMorphElementsTransition->AddTransition(new MoveTransition(pMorphElement,
                fCurrentLeft - pMorphElement->GetLeft(), -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - pMorphElement->GetBottom(),
                -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - pMorphElement->GetBottom(), false, VALUE_CHANGE_SCALE_STEPS));

              fCurrentLeft += pMorphElement->GetHeight() * fScaleFactor;
            }
          }
          else {
            int iIndex = 0;

            for (unsigned int i = 0; i < vInitialStackedBars.size(); i++) {
              StackedBar* pStackedBar = (StackedBar*) vInitialStackedBars.at(i);
              std::vector<Bar*> vBars = pStackedBar->GetBars();

              for (int j = (int) vBars.size() - 1; j >= 0; j--) {
                MorphElement* pMorphElement = vMorphElements.at(iIndex + j);

                pScaleMorphElementsTransition->AddTransition(new ScaleTransition(pMorphElement,
                  pMorphElement->GetHeight() * fScaleFactor - pMorphElement->GetWidth(),
                  PIE_CHART_RADIUS - pMorphElement->GetHeight(), VALUE_CHANGE_SCALE_STEPS));
                pScaleMorphElementsTransition->AddTransition(new MoveTransition(pMorphElement,
                  fCurrentLeft - pMorphElement->GetLeft(), -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - pMorphElement->GetBottom(),
                  -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - pMorphElement->GetBottom(), false, VALUE_CHANGE_SCALE_STEPS));

                fCurrentLeft += pMorphElement->GetHeight() * fScaleFactor;
              }

              iIndex += vBars.size();
            }
          }

          //morph the elements
          ParallelTransition* pMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pMorphElementsTransition);

          for (unsigned int i = 0; i < vMorphElements.size(); i++) {
            MorphElement* pMorphElement = vMorphElements.at(i);

            pMorphElementsTransition->AddTransition(new MorphTransition(pMorphElement, pMorphElement->GetResultElement(), MOVE_STEPS));
          }

          //fade in the new labels
          ParallelTransition* pFadeLabelsInTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pFadeLabelsInTransition);

          std::vector<DataElement*> vResultDataElements = pResultChart->GetDataElements();

          for (unsigned int i = 0; i < vResultDataElements.size(); i++) {
            Label* pResultLabel = ((PieSector*) vResultDataElements.at(i))->GetLabel();
            Label* pNewLabel = new Label(pResultLabel->GetText(), pResultLabel->GetStyle(),
              pResultLabel->GetLeft(), pResultLabel->GetTop(), pResultLabel->GetOrientation(),
              pResultLabel->GetAlignment());
            pNewLabel->ChangeAlpha(-1.0f);

            pInitialChart->AddLabel(pNewLabel);

            pFadeLabelsInTransition->AddTransition(new BlendTransition(pNewLabel, 1.0f));
          }

          break;
        }
        default: {
          pResultChart = new StackedBarChart(pInitialChart->GetDataSet());
        }
      }

      break;
    }
    case PIE_CHART: {
      std::vector<DataElement*> vInitialPieSectors = pInitialChart->GetDataElements();

      switch (iChartIndex) {
        case BAR_CHART: {
          //convert the pie chart to a grouped bar chart first, then to a bar chart
          ChangeChartTypeTransition* pChangeChartTypeToGroupedBarChartTransition = new ChangeChartTypeTransition(pInitialChart, GROUPED_BAR_CHART);
          AddTransition(pChangeChartTypeToGroupedBarChartTransition);

          ChangeChartTypeTransition* pChangeChartTypeToBarChartTransition = new ChangeChartTypeTransition(pChangeChartTypeToGroupedBarChartTransition->GetResultChart(), BAR_CHART);
          AddTransition(pChangeChartTypeToBarChartTransition);

          pResultChart = pChangeChartTypeToBarChartTransition->GetResultChart();

          break;
        }
        case GROUPED_BAR_CHART: {
          pResultChart = new GroupedBarChart(pInitialChart->GetDataSet());

          //sequential transition to blend out labels, morph the elements,
          //rotate the morph elements, blend in grid and labels
          AtomicSequentialTransition* pSequentialTransition = new AtomicSequentialTransition();
          AddTransition(pSequentialTransition);

          //fade out the labels
          ParallelTransition* pFadeLabelsOutTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pFadeLabelsOutTransition);

          for (unsigned int i = 0; i < vInitialPieSectors.size(); i++) {
            pFadeLabelsOutTransition->AddTransition(new BlendTransition(vInitialPieSectors.at(i)->GetLabel(), -1.0f));
          }

          //replace the bars with morph objects
          std::vector<MorphElement*> vSliceMorphElements;
          std::vector<MorphElement*> vBarMorphElements;
          float fAccumulatedHeights = 0.0f;

          for (unsigned int i = 0; i < vInitialPieSectors.size(); i++) {
            PieSector* pPieSector = (PieSector*) vInitialPieSectors.at(i);
            std::vector<PieSlice*> vSlices = pPieSector->GetSlices();

            for (unsigned int j = 0; j < vSlices.size(); j++) {
              PieSlice* pSlice = (PieSlice*) vSlices.at(j);
              Bar* pBar = (Bar*) ((GroupedBarChart*) pResultChart)->GetConnectedBar(pSlice->GetDataEntry());

              if (pBar != NULL) {
                MorphElement* pBarMorphElement = new MorphElement(pBar->GetDataEntry(), NULL,
                  pSlice->GetOutterVertices() - 2, pBar->GetLeft(), pBar->GetTop(), pBar->GetBottom(), pBar->GetWidth());
                pBarMorphElement->ChangeAlpha(-1.0f);
                vBarMorphElements.push_back(pBarMorphElement);
                MorphElement* pSliceMorphElement = new MorphElement(pSlice->GetDataEntry(), pBarMorphElement,
                  pSlice->GetOutterVertices() - 2, pSlice->GetBeginValue(), pSlice->GetWidth());
                vSliceMorphElements.push_back(pSliceMorphElement);
                pInitialChart->AddDataElement(pSliceMorphElement);
                pInitialChart->AddDataElement(pBarMorphElement);
                fAccumulatedHeights += pBarMorphElement->GetHeight();

                pSlice->ChangeAlpha(-1.0f);
              }
            }
          }

          //move the invisible bar morph elements
          float fScaleFactor = 2.0f * abs(ORIGIN_X) / fAccumulatedHeights;
          float fCurrentLeft = ORIGIN_X;

          for (unsigned int i = 0; i < vBarMorphElements.size(); i++) {
            MorphElement* pMorphElement = vBarMorphElements.at(i);
            float fLeftOld = pMorphElement->GetLeft();
            float fBottomOld = pMorphElement->GetBottom();
            float fHeightOld = pMorphElement->GetHeight();

            pMorphElement->Scale(fHeightOld * fScaleFactor - pMorphElement->GetWidth(),
              PIE_CHART_RADIUS - fHeightOld);
            pMorphElement->ChangePosition(fCurrentLeft - fLeftOld,
              -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - fBottomOld,
              -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - fBottomOld);

            fCurrentLeft += fHeightOld * fScaleFactor;
          }

          //morph the slice elements to bars
          ParallelTransition* pMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pMorphElementsTransition);

          for (unsigned int i = 0; i < vSliceMorphElements.size(); i++) {
            MorphElement* pMorphElement = vSliceMorphElements.at(i);

            pMorphElementsTransition->AddTransition(new MorphTransition(pMorphElement, pMorphElement->GetResultElement(), MOVE_STEPS));
          }

          //fade out the slice morph elements and fade in the bar morph elements for easier handling
          ParallelTransition* pBlendInMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pBlendInMorphElementsTransition);

          for (unsigned int i = 0; i < vBarMorphElements.size(); i++) {
            MorphElement* pBarMorphElement = vBarMorphElements.at(i);

            pBlendInMorphElementsTransition->AddTransition(new BlendTransition(pBarMorphElement, 1.0f, 1));
          }

          ParallelTransition* pBlendOutMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pBlendOutMorphElementsTransition);

          for (unsigned int i = 0; i < vSliceMorphElements.size(); i++) {
            MorphElement* pSliceMorphElement = vSliceMorphElements.at(i);

            pBlendOutMorphElementsTransition->AddTransition(new BlendTransition(pSliceMorphElement, -1.0f, 1));
          }

          //move and scale the elements
          ParallelTransition* pScaleMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pScaleMorphElementsTransition);

          for (unsigned int i = 0; i < vBarMorphElements.size(); i++) {
            MorphElement* pBarMorphElement = vBarMorphElements.at(i);
            Bar* pBar = (Bar*) ((GroupedBarChart*) pResultChart)->GetConnectedBar(pBarMorphElement->GetDataEntry());

            if (pBar != NULL) {
              pScaleMorphElementsTransition->AddTransition(new MoveTransition(pBarMorphElement,
                pBar->GetLeft() - pBarMorphElement->GetLeft(),
                pBar->GetTop() - pBarMorphElement->GetTop(),
                pBar->GetBottom() - pBarMorphElement->GetBottom(), false, MOVE_STEPS));
              pScaleMorphElementsTransition->AddTransition(new ScaleTransition(pBarMorphElement,
                pBar->GetWidth() - pBarMorphElement->GetWidth(),
                pBar->GetHeight() - pBarMorphElement->GetHeight(), MOVE_STEPS));
            }
          }

          //blend in the grid, axes, and labels
          ParallelTransition* pBlendInTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pBlendInTransition);

          std::vector<GridLine*> vGridLines = pResultChart->GetGridLines();

          for (unsigned int i = 0; i < vGridLines.size(); i++) {
            GridLine* pGridLine = vGridLines.at(i);
            GridLine* pNewGridLine = new GridLine(pGridLine->GetHeight(), pGridLine->GetRow());
            pNewGridLine->ChangeAlpha(-1.0f);
            pInitialChart->AddGridLine(pNewGridLine);

            pBlendInTransition->AddTransition(new BlendTransition(pNewGridLine, 1.0f));
          }

          std::vector<Axis*> vAxes = pResultChart->GetAxes();

          for (unsigned int i = 0; i < vAxes.size(); i++) {
            Axis* pAxis = vAxes.at(i);
            Axis* pNewAxis = new Axis(pAxis->GetOrientation(), pAxis->GetText());
            pNewAxis->ChangeAlpha(-1.0f);
            pInitialChart->AddAxis(pNewAxis);

            pBlendInTransition->AddTransition(new BlendTransition(pNewAxis, 1.0f));
          }

          std::vector<DataElement*> vResultBars = ((GroupedBarChart*) pResultChart)->GetAllBars();

          for (unsigned int i = 0; i < vResultBars.size(); i++) {
            Label* pLabel = vResultBars.at(i)->GetLabel();
            Label* pNewLabel = new Label(pLabel->GetText(), pLabel->GetStyle(), pLabel->GetLeft(),
              pLabel->GetTop(), pLabel->GetOrientation(), pLabel->GetAlignment());
            pNewLabel->ChangeAlpha(-1.0f);
            pInitialChart->AddLabel(pNewLabel);

            pBlendInTransition->AddTransition(new BlendTransition(pNewLabel, 1.0f));
          }

          break;
        }
        case STACKED_BAR_CHART: {
          pResultChart = new StackedBarChart(pInitialChart->GetDataSet());

          //sequential transition to blend out labels, morph the elements,
          //rotate the morph elements, blend in grid and labels
          AtomicSequentialTransition* pSequentialTransition = new AtomicSequentialTransition();
          AddTransition(pSequentialTransition);

          //fade out the labels
          ParallelTransition* pFadeLabelsOutTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pFadeLabelsOutTransition);

          for (unsigned int i = 0; i < vInitialPieSectors.size(); i++) {
            pFadeLabelsOutTransition->AddTransition(new BlendTransition(vInitialPieSectors.at(i)->GetLabel(), -1.0f));
          }

          //replace the bars with morph objects
          std::vector<MorphElement*> vSliceMorphElements;
          std::vector<MorphElement*> vBarMorphElements;
          float fAccumulatedHeights = 0.0f;

          for (unsigned int i = 0; i < vInitialPieSectors.size(); i++) {
            PieSector* pPieSector = (PieSector*) vInitialPieSectors.at(i);
            std::vector<PieSlice*> vSlices = pPieSector->GetSlices();

            for (unsigned int j = 0; j < vSlices.size(); j++) {
              PieSlice* pSlice = (PieSlice*) vSlices.at(j);
              Bar* pBar = (Bar*) ((StackedBarChart*) pResultChart)->GetConnectedBar(pSlice->GetDataEntry());

              if (pBar != NULL) {
                MorphElement* pBarMorphElement = new MorphElement(pBar->GetDataEntry(), NULL,
                  pSlice->GetOutterVertices() - 2, pBar->GetLeft(), pBar->GetTop(), pBar->GetBottom(), pBar->GetWidth());
                pBarMorphElement->ChangeAlpha(-1.0f);
                vBarMorphElements.push_back(pBarMorphElement);
                MorphElement* pSliceMorphElement = new MorphElement(pSlice->GetDataEntry(), pBarMorphElement,
                  pSlice->GetOutterVertices() - 2, pSlice->GetBeginValue(), pSlice->GetWidth());
                vSliceMorphElements.push_back(pSliceMorphElement);
                pInitialChart->AddDataElement(pSliceMorphElement);
                pInitialChart->AddDataElement(pBarMorphElement);
                fAccumulatedHeights += pBarMorphElement->GetHeight();

                pSlice->ChangeAlpha(-1.0f);
              }
            }
          }

          //move the invisible bar morph elements
          float fScaleFactor = 2.0f * abs(ORIGIN_X) / fAccumulatedHeights;
          float fCurrentLeft = ORIGIN_X;

          for (unsigned int i = 0; i < vBarMorphElements.size(); i++) {
            MorphElement* pMorphElement = vBarMorphElements.at(i);
            float fLeftOld = pMorphElement->GetLeft();
            float fBottomOld = pMorphElement->GetBottom();
            float fHeightOld = pMorphElement->GetHeight();

            pMorphElement->Scale(fHeightOld * fScaleFactor - pMorphElement->GetWidth(),
              PIE_CHART_RADIUS - fHeightOld);
            pMorphElement->ChangePosition(fCurrentLeft - fLeftOld,
              -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - fBottomOld,
              -PIE_CHART_RADIUS + PIE_CHART_CENTER_Y - fBottomOld);

            fCurrentLeft += fHeightOld * fScaleFactor;
          }

          //morph the slice elements to bars
          ParallelTransition* pMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pMorphElementsTransition);

          for (unsigned int i = 0; i < vSliceMorphElements.size(); i++) {
            MorphElement* pMorphElement = vSliceMorphElements.at(i);

            pMorphElementsTransition->AddTransition(new MorphTransition(pMorphElement, pMorphElement->GetResultElement(), MOVE_STEPS));
          }

          //fade out the slice morph elements and fade in the bar morph elements for easier handling
          ParallelTransition* pBlendInMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pBlendInMorphElementsTransition);

          for (unsigned int i = 0; i < vBarMorphElements.size(); i++) {
            MorphElement* pBarMorphElement = vBarMorphElements.at(i);

            pBlendInMorphElementsTransition->AddTransition(new BlendTransition(pBarMorphElement, 1.0f, 1));
          }

          ParallelTransition* pBlendOutMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pBlendOutMorphElementsTransition);

          for (unsigned int i = 0; i < vSliceMorphElements.size(); i++) {
            MorphElement* pSliceMorphElement = vSliceMorphElements.at(i);

            pBlendOutMorphElementsTransition->AddTransition(new BlendTransition(pSliceMorphElement, -1.0f, 1));
          }

          //move and scale the elements
          ParallelTransition* pScaleMorphElementsTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pScaleMorphElementsTransition);

          for (unsigned int i = 0; i < vBarMorphElements.size(); i++) {
            MorphElement* pBarMorphElement = vBarMorphElements.at(i);
            Bar* pBar = (Bar*) ((StackedBarChart*) pResultChart)->GetConnectedBar(pBarMorphElement->GetDataEntry());

            if (pBar != NULL) {
              pScaleMorphElementsTransition->AddTransition(new MoveTransition(pBarMorphElement,
                pBar->GetLeft() - pBarMorphElement->GetLeft(),
                pBar->GetTop() - pBarMorphElement->GetTop(),
                pBar->GetBottom() - pBarMorphElement->GetBottom(), false, MOVE_STEPS));
              pScaleMorphElementsTransition->AddTransition(new ScaleTransition(pBarMorphElement,
                pBar->GetWidth() - pBarMorphElement->GetWidth(),
                pBar->GetHeight() - pBarMorphElement->GetHeight(), MOVE_STEPS));
            }
          }

          //blend in the grid, axes, and labels
          ParallelTransition* pBlendInTransition = new ParallelTransition();
          pSequentialTransition->AddTransition(pBlendInTransition);

          std::vector<GridLine*> vGridLines = pResultChart->GetGridLines();

          for (unsigned int i = 0; i < vGridLines.size(); i++) {
            GridLine* pGridLine = vGridLines.at(i);
            GridLine* pNewGridLine = new GridLine(pGridLine->GetHeight(), pGridLine->GetRow());
            pNewGridLine->ChangeAlpha(-1.0f);
            pInitialChart->AddGridLine(pNewGridLine);

            pBlendInTransition->AddTransition(new BlendTransition(pNewGridLine, 1.0f));
          }

          std::vector<Axis*> vAxes = pResultChart->GetAxes();

          for (unsigned int i = 0; i < vAxes.size(); i++) {
            Axis* pAxis = vAxes.at(i);
            Axis* pNewAxis = new Axis(pAxis->GetOrientation(), pAxis->GetText());
            pNewAxis->ChangeAlpha(-1.0f);
            pInitialChart->AddAxis(pNewAxis);

            pBlendInTransition->AddTransition(new BlendTransition(pNewAxis, 1.0f));
          }

          std::vector<DataElement*> vResultStackedBars = pResultChart->GetDataElements();

          for (unsigned int i = 0; i < vResultStackedBars.size(); i++) {
            Label* pLabel = vResultStackedBars.at(i)->GetLabel();
            Label* pNewLabel = new Label(pLabel->GetText(), pLabel->GetStyle(), pLabel->GetLeft(),
              pLabel->GetTop(), pLabel->GetOrientation(), pLabel->GetAlignment());
            pNewLabel->ChangeAlpha(-1.0f);
            pInitialChart->AddLabel(pNewLabel);

            pBlendInTransition->AddTransition(new BlendTransition(pNewLabel, 1.0f));
          }

          break;
        }
        default: {
          pResultChart = new PieChart(pInitialChart->GetDataSet());
        }
      }

      break;
    }
    default: {
      //this should not be possible, fall back to bar chart
      pResultChart = new BarChart(pInitialChart->GetDataSet());
    }
  }
}

ChangeChartTypeTransition::~ChangeChartTypeTransition() {
  while (vChildren.size() > 0) {
    std::vector<Transition*>::iterator it = vChildren.begin();

    if (it != vChildren.end()) {
      delete *it;
      vChildren.erase(it);
    }
  }

  while (vToBeDeleted.size() > 0) {
    std::vector<AlterableObject*>::iterator it = vToBeDeleted.begin();

    if (it != vToBeDeleted.end()) {
      delete *it;
      vToBeDeleted.erase(it);
    }
  }
}

void ChangeChartTypeTransition::Update() {
  if (vChildren.size() > 0) {
    if (!vChildren.at(0)->Complete()) {
      vChildren.at(0)->Update();
    }

    if (vChildren.at(0)->Complete()) {
      delete *vChildren.begin();
      vChildren.erase(vChildren.begin());

      if ((vChildren.size() > 0) && ((vChildren.at(0)->GetType() == CHANGE_CHART_TYPE_TRANSITION) ||
        (vChildren.at(0)->GetType() == SORTING_TRANSITION))) {
        delete pInitialChart;

        pInitialChart = ((CompoundTransition*) *vChildren.begin())->GetActiveChart();
      }
    }
  }
}

void ChangeChartTypeTransition::Draw(QPainter* pPainter) {
  pInitialChart->Draw(pPainter);
  pInitialChart->DrawLabels(pPainter);
}

bool ChangeChartTypeTransition::Complete() {
  for (unsigned int i = 0; i < vChildren.size(); i++) {
    if (!vChildren.at(i)->Complete()) {
      return false;
    }
  }

  return true;
}

Chart* ChangeChartTypeTransition::GetActiveChart() {
  if ((vChildren.size() > 0) && ((vChildren.at(0)->GetType() == CHANGE_CHART_TYPE_TRANSITION) ||
    (vChildren.at(0)->GetType() == SORTING_TRANSITION))) {
    return ((CompoundTransition*) vChildren.at(0))->GetActiveChart();
  }

  return pInitialChart;
}