home *** CD-ROM | disk | FTP | other *** search
/ Shareware Supreme Volume 6 #1 / swsii.zip / swsii / 099 / SCALE.ZIP / SCALE.C next >
C/C++ Source or Header  |  1993-01-12  |  12KB  |  368 lines

  1. /* scale.c */
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <assert.h>
  6. #include <math.h>
  7. #include "scale.h"
  8.  
  9. /* The set of potential multipliers for nice numbers.                */
  10. /* (10.0 is included only as a convenience for computing geometric   */
  11. /* means for Lewart's algorithm.)                                    */
  12. static double pdSet[] = {1.0, 2.0, 5.0, 10.0};
  13. #define SET_LEN   (sizeof (pdSet) / sizeof (double) - 1)
  14.  
  15. /* Function prototypes. */
  16. static double  scFirstNiceNum (double, int *, double *);
  17. static double  scNextNiceNum  (double *, int, int *, double *);
  18. static void    scCalcExtLabel (double, double, double, double *, double *);
  19. static void    scCalcIntLabel (double, double, double, double *, double *);
  20. static double  scPower        (double, int);
  21.  
  22. /* Enhanced Dixon-Kronmal algorithm. */
  23. /* Scale minimum = *pdMinMult * *pdNiceNum */
  24. /* Scale Maximum = *pdMaxMult * *pdNiceNum */
  25.  
  26. void scDixonKronmal (dDataMin, dDataMax, nExactIntervals,
  27.       pdNiceNum, pdMinMult, pdMaxMult)
  28.  
  29. double   dDataMin;         /* (I) Data minimum. */
  30. double   dDataMax;         /* (I) Data maximum. */
  31. int      nExactIntervals;  /* (I) Exact number of intervals to use. */
  32. double   *pdNiceNum;       /* (O) Nice number. */
  33. double   *pdMinMult;       /* (O) Multiplier for scale minimum. */
  34. double   *pdMaxMult;       /* (O) Multiplier for scale maximum. */
  35. {
  36.    double   dIntervalSize;
  37.    int      iIndex;
  38.    double   dPowerOfTen;
  39.    double   dAdjMinMult;
  40.    double   dAdjMaxMult;
  41.    int      nActualIntervals;
  42.    int      nDiffIntervals;
  43.    int      nAdjIntervals;
  44.  
  45.    assert (dDataMin < dDataMax);
  46.    assert (nExactIntervals >= 2);
  47.  
  48.    /* Calculate the smallest potential interval size. */
  49.    dIntervalSize = (dDataMax - dDataMin) / nExactIntervals;
  50.  
  51.    /* Calculate the smallest nice number not smaller than dIntervalSize. */
  52.    for (*pdNiceNum = scFirstNiceNum (dIntervalSize, &iIndex, &dPowerOfTen);
  53.          *pdNiceNum < dIntervalSize;
  54.          *pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen))
  55.    {
  56.       ;
  57.    }
  58.  
  59.    /* Produce the scale using the specified nice number. */
  60.    scCalcExtLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
  61.  
  62.    /* Continue to re-scale the data with new nice numbers until the */
  63.    /* requested number of intervals is not exceeded.                */
  64.    while ((int) (*pdMaxMult - *pdMinMult) > nExactIntervals)
  65.    {
  66.       *pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen);
  67.       scCalcExtLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
  68.    }
  69.  
  70.    /* Calculate the actual number of intervals spanned by data. */
  71.    nActualIntervals = (int) (*pdMaxMult - *pdMinMult);
  72.  
  73.    /* Adjust lo and hi multiples to account for the additional */
  74.    /* intervals required.  Adjust in favor of centering.       */
  75.    nDiffIntervals = nExactIntervals - nActualIntervals;
  76.    nAdjIntervals = nDiffIntervals / 2;
  77.    if (nDiffIntervals & 1)
  78.    /* nDiffIntervals is odd.  Decide where the extra interval should go. */
  79.    {
  80.       if (dDataMin - *pdMinMult * *pdNiceNum <
  81.             *pdMaxMult * *pdNiceNum - dDataMax)
  82.       {
  83.          nAdjIntervals++;  
  84.       }
  85.    }
  86.    dAdjMinMult = *pdMinMult - (double) nAdjIntervals;
  87.    dAdjMaxMult = dAdjMinMult + (double) nExactIntervals;
  88.  
  89.    if (dAdjMinMult < 0.0 && *pdMinMult >= 0.0)
  90.    /* Avoid adjustments that cause negative scales for non-negative data. */
  91.    {
  92.       *pdMinMult = 0.0;
  93.       *pdMaxMult = (double) nExactIntervals;
  94.    }
  95.    else if (dAdjMaxMult > 0.0 && *pdMaxMult <= 0.0)
  96.    /* Avoid adjustments that cause positive scales for non-positive data. */
  97.    {
  98.       *pdMaxMult = 0.0;
  99.       *pdMinMult = (double) -nExactIntervals;
  100.    }
  101.    else
  102.    {
  103.       *pdMinMult = dAdjMinMult;
  104.       *pdMaxMult = dAdjMaxMult;
  105.    }
  106. }
  107.  
  108. /* Lewart's algorithm. */
  109. /* Scale minimum = *pdMinMult * *pdNiceNum */
  110. /* Scale Maximum = *pdMaxMult * *pdNiceNum */
  111.  
  112. void scLewart (dDataMin, dDataMax, nApproxIntervals,
  113.       pdNiceNum, pdMinMult, pdMaxMult)
  114.  
  115. double   dDataMin;         /* (I) Data minimum. */
  116. double   dDataMax;         /* (I) Data maximum. */
  117. int      nApproxIntervals; /* (I) Approximate number of intervals to use. */
  118. double   *pdNiceNum;       /* (O) Nice number. */
  119. double   *pdMinMult;       /* (O) Multiplier for scale minimum. */
  120. double   *pdMaxMult;       /* (O) Multiplier for scale maximum. */
  121. {
  122.    double   dIntervalSize;
  123.    int      iIndex;
  124.    double   dPowerOfTen;
  125.  
  126.    assert (dDataMin < dDataMax);
  127.    assert (nApproxIntervals >= 2);
  128.  
  129.    /* Calculate the smallest potential interval size. */
  130.    dIntervalSize = (dDataMax - dDataMin) / nApproxIntervals;
  131.  
  132.    /* Find the nice number that is "closest to" the smallest potential  */
  133.    /* interval size.  Use the geometric means of adjacent multiplier    */
  134.    /* values as break points.                                           */
  135.    for (*pdNiceNum = scFirstNiceNum (dIntervalSize, &iIndex, &dPowerOfTen);
  136.          sqrt (pdSet[iIndex] * pdSet[iIndex + 1]) * dPowerOfTen <
  137.                dIntervalSize;
  138.          *pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen))
  139.    {
  140.       ;
  141.    }
  142.  
  143.    /* Produce the scale using the specified nice number. */
  144.    scCalcExtLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
  145. }
  146.  
  147. /* Algorithm for scaling with a maximum number of intervals. */
  148. /* Scale minimum = *pdMinMult * *pdNiceNum */
  149. /* Scale Maximum = *pdMaxMult * *pdNiceNum */
  150.  
  151. void scMaxInterval (dDataMin, dDataMax, nMaxIntervals,
  152.       pdNiceNum, pdMinMult, pdMaxMult)
  153.  
  154. double   dDataMin;         /* (I) Data minimum. */
  155. double   dDataMax;         /* (I) Data maximum. */
  156. int      nMaxIntervals;    /* (I) Maximum number of intervals to use. */
  157. double   *pdNiceNum;       /* (O) Nice number. */
  158. double   *pdMinMult;       /* (O) Multiplier for scale minimum. */
  159. double   *pdMaxMult;       /* (O) Multiplier for scale maximum. */
  160. {
  161.    double   dIntervalSize;
  162.    int      iIndex;
  163.    double   dPowerOfTen;
  164.  
  165.    assert (dDataMin < dDataMax);
  166.    assert (nMaxIntervals >= 2);
  167.  
  168.    /* Calculate the smallest potential interval size. */
  169.    dIntervalSize = (dDataMax - dDataMin) / nMaxIntervals;
  170.  
  171.    /* Calculate the smallest nice number not smaller than dIntervalSize. */
  172.    for (*pdNiceNum = scFirstNiceNum (dIntervalSize, &iIndex, &dPowerOfTen);
  173.          *pdNiceNum < dIntervalSize;
  174.          *pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen))
  175.    {
  176.       ;
  177.    }
  178.  
  179.    /* Produce the scale using the specified nice number. */
  180.    scCalcExtLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
  181.  
  182.    /* Continue to re-scale the data with new nice numbers until the */
  183.    /* requested number of intervals is not exceeded.                */
  184.    while ((int) (*pdMaxMult - *pdMinMult) > nMaxIntervals)
  185.    {
  186.       *pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen);
  187.       scCalcExtLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
  188.    }
  189. }
  190.  
  191. /* Algorithm for internal labeling. */
  192. /* First reference value = *pdMinMult * *pdNiceNum */
  193. /* Last reference value  = *pdMaxMult * *pdNiceNum */
  194.  
  195. void scInternal (dDataMin, dDataMax, nMaxIntervals,
  196.       pdNiceNum, pdMinMult, pdMaxMult)
  197.  
  198. double   dDataMin;         /* (I) Data minimum. */
  199. double   dDataMax;         /* (I) Data maximum. */
  200. int      nMaxIntervals;    /* (I) Maximum number of intervals to use. */
  201. double   *pdNiceNum;       /* (O) Nice number. */
  202. double   *pdMinMult;       /* (O) Multiplier for minimum reference value. */
  203. double   *pdMaxMult;       /* (O) Multiplier for maximum reference value. */
  204. {
  205.    double   dIntervalSize;
  206.    int      iIndex;
  207.    double   dPowerOfTen;
  208.  
  209.    assert (dDataMin < dDataMax);
  210.    assert (nMaxIntervals >= 5);
  211.  
  212.    /* Calculate the smallest potential interval size. */
  213.    dIntervalSize = (dDataMax - dDataMin) / nMaxIntervals;
  214.  
  215.    /* Calculate the smallest nice number not smaller than dIntervalSize. */
  216.    for (*pdNiceNum = scFirstNiceNum (dIntervalSize, &iIndex, &dPowerOfTen);
  217.          *pdNiceNum < dIntervalSize;
  218.          *pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen))
  219.    {
  220.       ;
  221.    }
  222.  
  223.    /* Produce the internal scale using the specified nice number. */
  224.    scCalcIntLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
  225. }
  226.  
  227. /* Calculate an initial value for the nice number. */
  228.  
  229. double scFirstNiceNum (dIntervalSize, piIndex, pdPowerOfTen)
  230.  
  231. double   dIntervalSize; /* (I) Interval size. */
  232. int      *piIndex;      /* (O) Index into multiplier array for nice number. */
  233. double   *pdPowerOfTen; /* (O) Power of ten for nice number. */
  234. {
  235.    int      iExponent;
  236.  
  237.    /* Calculate an initial power of 10. */
  238.    iExponent = (int) floor (log10 (dIntervalSize));
  239.    /* Perform some extra checking. */
  240.    *pdPowerOfTen = scPower (10.0, iExponent);
  241.    if (*pdPowerOfTen * 10.0 <= dIntervalSize)
  242.    {
  243.       *pdPowerOfTen *= 10.0;
  244.    }
  245.  
  246.    /* Initial index is always 0. */
  247.    *piIndex = 0;
  248.  
  249.    return (*pdPowerOfTen);
  250. }
  251.  
  252. /* Calculate the next nice number. */
  253.  
  254. double scNextNiceNum (pdSet, nSet, piIndex, pdPowerOfTen)
  255.  
  256. double   *pdSet;        /* (I)  Set of multipliers. */
  257. int      nSet;          /* (I)  Number of elements in set. */
  258. int      *piIndex;      /* (IO) Index into multiplier array for nice number. */
  259. double   *pdPowerOfTen; /* (IO) Power of ten for nice number. */
  260. {
  261.    /* Increment the index. */
  262.    (*piIndex)++;
  263.  
  264.    /* If the maximum index has been exceeded, reset the index to */
  265.    /* 0 and increase the power of 10.                            */
  266.    if (*piIndex >= nSet)
  267.    {
  268.       *piIndex = 0;
  269.       *pdPowerOfTen *= 10.0;
  270.    }
  271.  
  272.    return (pdSet[*piIndex] * *pdPowerOfTen);
  273. }
  274.  
  275. /* Calculate an externally labeled scale. */
  276.  
  277. void scCalcExtLabel (dDataMin, dDataMax, dNiceNum, pdMinMult, pdMaxMult)
  278.  
  279. double   dDataMin;      /* (I) Data minimum. */
  280. double   dDataMax;      /* (I) Data maximum. */
  281. double   dNiceNum;      /* (I) Nice number. */
  282. double   *pdMinMult;    /* (O) Multiplier for scale minimum. */
  283. double   *pdMaxMult;    /* (O) Multiplier for scale maximum. */
  284. {
  285.    /* Calculate the low multiple. */
  286.    *pdMinMult = floor (dDataMin / dNiceNum);
  287.    /* Perform some extra checking. */
  288.    if ((*pdMinMult + 1.0) * dNiceNum <= dDataMin)
  289.    {
  290.       *pdMinMult = *pdMinMult + 1.0;
  291.    }
  292.  
  293.    /* Calculate the high multiple. */
  294.    *pdMaxMult = ceil (dDataMax / dNiceNum);
  295.    /* Perform some extra checking. */
  296.    if ((*pdMaxMult - 1.0) * dNiceNum >= dDataMax)
  297.    {
  298.       *pdMaxMult = *pdMaxMult - 1.0;
  299.    }
  300. }
  301.  
  302. /* Calculate an internally labeled scale. */
  303.  
  304. void scCalcIntLabel (dDataMin, dDataMax, dNiceNum, pdMinMult, pdMaxMult)
  305.  
  306. double   dDataMin;      /* (I) Data minimum. */
  307. double   dDataMax;      /* (I) Data maximum. */
  308. double   dNiceNum;      /* (I) Nice number. */
  309. double   *pdMinMult;    /* (O) Multiplier for minimum reference value. */
  310. double   *pdMaxMult;    /* (O) Multiplier for maximum reference value. */
  311. {
  312.    /* Calculate the low multiple. */
  313.    *pdMinMult = ceil (dDataMin / dNiceNum);
  314.    /* Perform some extra checking. */
  315.    if ((*pdMinMult - 1.0) * dNiceNum >= dDataMin)
  316.    {
  317.       *pdMinMult = *pdMinMult - 1.0;
  318.    }
  319.  
  320.    /* Calculate the high multiple. */
  321.    *pdMaxMult = floor (dDataMax / dNiceNum);
  322.    /* Perform some extra checking. */
  323.    if ((*pdMaxMult + 1.0) * dNiceNum <= dDataMax)
  324.    {
  325.       *pdMaxMult = *pdMaxMult + 1.0;
  326.    }
  327. }
  328.  
  329. /* Raise a double to an integer power. */
  330. /*
  331. ** Adapted from an algorithm described in "Algorithms" by Robert Sedgewick
  332. ** First Edition, pp. 46-47.
  333. */
  334.  
  335. static double scPower (dRoot, iExponent)
  336.  
  337. double   dRoot;         /* (I) Root to be raised to a power. */
  338. int      iExponent;     /* (I) Power to which the root should be raised. */
  339. {
  340.    double   dResult;
  341.  
  342.    /* For negative exponents, invert root and use a positive exponent. */
  343.    if (iExponent < 0)
  344.    {
  345.       dRoot     = 1.0 / dRoot;
  346.       iExponent = -iExponent;
  347.    }
  348.  
  349.    /* Perform multiple multiplications. */
  350.    dResult = 1.0;
  351.    while (iExponent)
  352.    {
  353.       if (iExponent & 1)
  354.       {
  355.          dResult *= dRoot;
  356.       }
  357.  
  358.       iExponent >>= 1;
  359.  
  360.       if (iExponent)
  361.       {
  362.          dRoot *= dRoot;
  363.       }
  364.    }
  365.  
  366.    return (dResult);
  367. }
  368.