summaryrefslogtreecommitdiff
path: root/muse2/muse/widgets/scldiv.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse2/muse/widgets/scldiv.cpp')
-rw-r--r--muse2/muse/widgets/scldiv.cpp652
1 files changed, 652 insertions, 0 deletions
diff --git a/muse2/muse/widgets/scldiv.cpp b/muse2/muse/widgets/scldiv.cpp
new file mode 100644
index 00000000..a0b0e628
--- /dev/null
+++ b/muse2/muse/widgets/scldiv.cpp
@@ -0,0 +1,652 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: scldiv.cpp,v 1.1.1.1 2003/10/27 18:54:32 wschweer Exp $
+//
+// Copyright (C) 1997 Josef Wilgen
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2,
+// as published by the Free Software Foundation.
+//
+// (C) Copyright 2000 Werner Schweer (ws@seh.de)
+//=========================================================
+
+#include <cmath>
+#include "scldiv.h"
+#include "mmath.h"
+//Added by qt3to4:
+#include <Q3MemArray>
+
+// ScaleDiv - A class for building scale divisions
+//
+// The ScaleDiv class can build
+// linear and logarithmic scale divisions for specified
+// intervals. It uses an adjustable algorithm to
+// generate the major and minor step widths automatically.
+// A scale division has a minimum value, a maximum value,
+// a vector of major marks, and a vector of minor marks.
+//
+// ScaleDiv uses implicit sharing for the mark vectors.
+//
+// Build a logarithmic scale division from 0.01 to 1000
+// and print out the major and minor marks.
+//.c
+// #include <scldiv.h>
+// #include <iostream.h>
+//
+// main()
+// {
+// int i,k;
+// ScaleDiv sd;
+//
+// sd.rebuild(0.01, 100, 10, 10, TRUE, 0.0);
+//
+// k=0;
+// for (i=0;i<sd.majCnt();i++)
+// {
+// while(k < sd.minCnt())
+// {
+// if(sd.minMark(k) < sd.majMark(i))
+// {
+// cout << " - " << sd.minMark(i) << "\n";
+// k++;
+// }
+// else
+// break;
+// }
+// cout << "-- " << sd.majMark(i) << "\n";
+// }
+// while(k < sd.minCnt())
+// {
+// cout << " - " << sd.minMark(i) << "\n";
+// k++;
+// }
+// }
+//
+//------------------------------------------------------------
+
+static const double step_eps = 1.0e-3;
+static const double border_eps = 1.0e-10;
+
+static bool limRange(double &val, double v1, double v2, double eps_rel = 0.0,
+ double eps_abs = 0.0)
+ {
+
+ bool rv = TRUE;
+ double vmin = qwtMin(v1, v2);
+ double vmax = qwtMax(v1, v2);
+ double delta_min = qwtMax(qwtAbs(eps_rel * vmin), qwtAbs(eps_abs));
+ double delta_max = qwtMax(qwtAbs(eps_rel * vmax), qwtAbs(eps_abs));
+
+ if (val < vmin)
+ {
+ if (val < vmin - delta_min) rv = FALSE;
+ val = vmin;
+ }
+ else if (val > vmax)
+ {
+ if (val > vmax + delta_max) rv = FALSE;
+ val = vmax;
+ }
+ return rv;
+
+}
+
+//------------------------------------------------------------
+//.F ScaleDiv::ScaleDiv
+// Construct a ScaleDiv instance.
+//
+//.u Syntax
+//.f ScaleDiv::ScaleDiv()
+//------------------------------------------------------------
+
+ScaleDiv::ScaleDiv()
+ {
+ d_lBound = 0.0;
+ d_hBound = 0.0;
+ d_majStep = 0.0;
+ d_log = FALSE;
+ }
+
+//------------------------------------------------------------
+//.F ScaleDiv::~ScaleDiv
+// Destroy a ScaleDiv instance.
+//
+//.u Syntax
+//.f ScaleDiv::~ScaleDiv()
+//------------------------------------------------------------
+
+ScaleDiv::~ScaleDiv()
+ {
+ }
+
+//------------------------------------------------------------
+//.F ScaleDiv::ScaleDiv
+// Copy Constructor
+//
+//.u Syntax
+//.f ScaleDiv::ScaleDiv(const ScaleDiv &s)
+//
+//.u Parameters
+//.p const ScaleDiv &s -- scale division to be copied
+//------------------------------------------------------------
+
+ScaleDiv::ScaleDiv(const ScaleDiv &s)
+ {
+ copy(s);
+ }
+
+//------------------------------------------------------------
+//.F ScaleDiv::operator=
+// Assignment operator
+//
+//.u Syntax
+//.f ScaleDiv & ScaleDiv::operator=(const ScaleDiv &s)
+//
+//.u Parameters
+//.p const ScaleDiv &s -- scale divison to be assigned
+//------------------------------------------------------------
+
+ScaleDiv& ScaleDiv::operator=(const ScaleDiv &s)
+ {
+ copy(s);
+ return *this;
+ }
+
+//------------------------------------------------------------
+//.F ScaleDiv::copy
+// Copy member data from another ScaleDiv instance.
+//
+//.u Syntax
+//.f void ScaleDiv::copy(const ScaleDiv &s)
+//
+//.u Parameters
+//.p const ScaleDiv &s
+//------------------------------------------------------------
+
+void ScaleDiv::copy(const ScaleDiv &s)
+ {
+ d_lBound = s.d_lBound;
+ d_hBound = s.d_hBound;
+ d_log = s.d_log;
+ d_majStep = s.d_majStep;
+ d_minMarks = s.d_minMarks;
+ d_majMarks = s.d_majMarks;
+ }
+
+//------------------------------------------------------------
+//.F ScaleDiv::rebuild
+// Build a scale width major and minor divisions
+//
+//.p
+// double x1 -- first boundary value
+// double x2 -- second boundary value
+// int maxMajSteps -- max. number of major step intervals
+// int maxMinSteps -- max. number of minor step intervals
+// bool log -- logarithmic division (TRUE/FALSE)
+// double step -- fixed major step width. Defaults to 0.0.
+// bool ascend -- if TRUE, sort in ascending order from min(x1, x2)
+// to max(x1, x2). If FALSE, sort in the direction
+// from x1 to x2. Defaults to TRUE.
+//
+//.u Return Value
+// True if the arrays have been allocated successfully.
+//
+//.u Description
+// If no fixed step width is specified or if it is set to 0, the
+// major step width will be calculated automatically according to the
+// the value of maxMajSteps. The maxMajSteps parameter has no effect
+// if a fixed step size is specified. The minor step width is always
+// calculated automatically.
+// If the step width is to be calculated automatically, the algorithm
+// tries to find reasonable values fitting into the scheme {1,2,5}*10^n
+// with an integer number n for linear scales.
+// For logarithmic scales, there are three different cases:
+//.i
+// -- If the major step width is one decade, the minor marks
+// will fit into one of the schemes {1,2,...9}, {2,4,6,8}, {2,5} or {5},
+// depending on the maxMinSteps parameter.
+// -- If the major step size spans
+// more than one decade, the minor step size will be {1,2,5}*10^n decades
+// with a natural number n.
+// -- If the whole range is less than one decade, a linear scale
+// division will be built
+//
+//.u Note
+// For logarithmic scales, the step width is measured in decades.
+//------------------------------------------------------------
+
+bool ScaleDiv::rebuild(double x1, double x2, int maxMajSteps, int maxMinSteps,
+ bool log, double step, bool ascend)
+{
+
+ int rv;
+
+ d_lBound = qwtMin(x1, x2);
+ d_hBound = qwtMax(x1, x2);
+ d_log = log;
+
+ if (d_log)
+ rv = buildLogDiv(maxMajSteps,maxMinSteps,step);
+ else
+ rv = buildLinDiv(maxMajSteps, maxMinSteps, step);
+
+ if ((!ascend) && (x2 < x1))
+ {
+ d_lBound = x1;
+ d_hBound = x2;
+ qwtTwistArray(d_majMarks.data(), d_majMarks.size());
+ qwtTwistArray(d_minMarks.data(), d_minMarks.size());
+ }
+
+ return rv;
+
+}
+
+//------------------------------------------------------------
+//.F ScaleDiv::buildLinDiv
+// Build a linear scale division in ascending order
+//
+//.u Syntax
+//.f bool ScaleDiv::buildLinDiv(int majSteps, int minSteps, double step)
+//
+//.u Parameters
+//.p int maxSteps -- max. number of step intervals
+// double step -- fixed step width
+//
+//.u Return Value
+// TRUE if array has been successfully resized
+//
+//.u Description
+// If the 'step' parameter is set to 0.0, this function
+// cal[culates the step width automatically according to
+// the value of 'maxSteps'. MaxSteps must be greater than or
+// equal to 2. It will be guessed if an invalid value is specified.
+// The maximum possible number of steps is limited to 10000.
+// The maxSteps parameter has no effect if a fixed step width is
+// specified.
+//
+//.u Note
+// This function uses the data members d_lBound and d_hBound and assumes
+// that d_hBound > d_lBound.
+//------------------------------------------------------------
+
+bool ScaleDiv::buildLinDiv(int maxMajSteps, int maxMinSteps, double step)
+ {
+
+ int nMaj, nMin, minSize, i0,i,k;
+ double val, mval;
+ double firstTick, lastTick;
+ double minStep;
+ Q3MemArray<double> buffer;
+ bool rv = TRUE;
+
+ // parameter range check
+ maxMajSteps = qwtMax(1, maxMajSteps);
+ maxMinSteps = qwtMax(0, maxMinSteps);
+ step = qwtAbs(step);
+
+ // detach arrays
+ d_majMarks.duplicate(0,0);
+ d_minMarks.duplicate(0,0);
+
+ if (d_lBound == d_hBound) return TRUE;
+
+ //
+ // Set up major divisions
+ //
+ if (step == 0.0)
+ d_majStep = qwtCeil125(qwtAbs(d_hBound - d_lBound) * 0.999999
+ / double(maxMajSteps));
+ else
+ d_majStep = step;
+
+ if (d_majStep == 0.0) return TRUE;
+
+ firstTick = ceil( (d_lBound - step_eps * d_majStep) / d_majStep) * d_majStep;
+ lastTick = floor( (d_hBound + step_eps * d_majStep) / d_majStep) * d_majStep;
+
+ nMaj = qwtMin(10000, int(rint((lastTick - firstTick) / d_majStep)) + 1);
+
+ if ((rv = d_majMarks.resize(nMaj)))
+ qwtLinSpace(d_majMarks.data(), d_majMarks.size(), firstTick, lastTick);
+ else
+ return FALSE;
+
+ //
+ // Set up minor divisions
+ //
+ if (maxMinSteps < 1) // no minor divs
+ return TRUE;
+
+ minStep = qwtCeil125( d_majStep / double(maxMinSteps) );
+
+ if (minStep == 0.0) return TRUE;
+
+ nMin = qwtAbs(int(rint(d_majStep / minStep))) - 1; // # minor steps per interval
+
+ // Do the minor steps fit into the interval?
+ if ( qwtAbs(double(nMin + 1) * minStep - d_majStep) > step_eps * d_majStep)
+ {
+ nMin = 1;
+ minStep = d_majStep * 0.5;
+ }
+
+ // Are there minor ticks below the first major tick?
+ if (d_majMarks[0] > d_lBound )
+ i0 = -1;
+ else
+ i0 = 0;
+
+ // resize buffer to the maximum possible number of minor ticks
+ rv = buffer.resize(nMin * (nMaj + 1));
+
+ // calculate minor ticks
+ if (rv)
+ {
+ minSize = 0;
+ for (i = i0; i < (int)d_majMarks.size(); i++)
+ {
+ if (i >= 0)
+ val = d_majMarks[i];
+ else
+ val = d_majMarks[0] - d_majStep;
+
+ for (k=0; k< nMin; k++)
+ {
+ mval = (val += minStep);
+ if (limRange(mval, d_lBound, d_hBound, border_eps))
+ {
+ buffer[minSize] = mval;
+ minSize++;
+ }
+ }
+ }
+ d_minMarks.duplicate(buffer.data(), minSize);
+ }
+
+ return rv;
+ }
+
+//------------------------------------------------------------
+//.F ScaleDiv::buildLogDiv
+// Build a logarithmic scale division
+//
+//.u Syntax
+//.f bool ScaleDiv::buildLogDiv(int maxMajSteps, int maxMinSteps, int majStep)
+//
+//.u Parameters
+//.p int maxMajSteps, int maxMinSteps, int majStep
+//
+//.u Return Value
+// True if memory has been successfully allocated
+//
+//.u Note
+// This function uses the data members d_lBound and d_hBound and assumes
+// that d_hBound > d_lBound.
+//------------------------------------------------------------
+
+bool ScaleDiv::buildLogDiv(int maxMajSteps, int maxMinSteps, double majStep)
+ {
+ double firstTick, lastTick;
+ double lFirst, lLast;
+ double val, sval, minStep, minFactor;
+ int nMaj, nMin, minSize, i, k, k0, kstep, kmax, i0;
+ int rv = TRUE;
+ double width;
+
+ Q3MemArray<double> buffer;
+
+
+ // Parameter range check
+ maxMajSteps = qwtMax(1, qwtAbs(maxMajSteps));
+ maxMinSteps = qwtMax(0, qwtAbs(maxMinSteps));
+ majStep = qwtAbs(majStep);
+
+ // boundary check
+ limRange(d_hBound, LOG_MIN, LOG_MAX);
+ limRange(d_lBound, LOG_MIN, LOG_MAX);
+
+ // detach arrays
+ d_majMarks.duplicate(0,0);
+ d_minMarks.duplicate(0,0);
+
+ if (d_lBound == d_hBound) return TRUE;
+
+ // scale width in decades
+ width = log10(d_hBound) - log10(d_lBound);
+
+ // scale width is less than one decade -> build linear scale
+ if (width < 1.0)
+ {
+ rv = buildLinDiv(maxMajSteps, maxMinSteps, 0.0);
+ // convert step width to decades
+ if (d_majStep > 0)
+ d_majStep = log10(d_majStep);
+
+ return rv;
+ }
+
+ //
+ // Set up major scale divisions
+ //
+ if (majStep == 0.0)
+ d_majStep = qwtCeil125( width * 0.999999 / double(maxMajSteps));
+ else
+ d_majStep = majStep;
+
+ // major step must be >= 1 decade
+ d_majStep = qwtMax(d_majStep, 1.0);
+
+
+ lFirst = ceil((log10(d_lBound) - step_eps * d_majStep) / d_majStep) * d_majStep;
+ lLast = floor((log10(d_hBound) + step_eps * d_majStep) / d_majStep) * d_majStep;
+
+ firstTick = pow(10.0, lFirst);
+ lastTick = pow(10.0, lLast);
+
+ nMaj = qwtMin(10000, int(rint(qwtAbs(lLast - lFirst) / d_majStep)) + 1);
+
+ if (d_majMarks.resize(nMaj))
+ qwtLogSpace(d_majMarks.data(), d_majMarks.size(), firstTick, lastTick);
+ else
+ return FALSE;
+
+
+ //
+ // Set up minor scale divisions
+ //
+
+ if ((d_majMarks.size() < 1) || (maxMinSteps < 1)) return TRUE; // no minor marks
+
+ if (d_majStep < 1.1) // major step width is one decade
+ {
+ if (maxMinSteps >= 8)
+ {
+ k0 = 2;
+ kmax = 9;
+ kstep = 1;
+ minSize = (d_majMarks.size() + 1) * 8;
+ }
+ else if (maxMinSteps >= 4)
+ {
+ k0 = 2;
+ kmax = 8;
+ kstep = 2;
+ minSize = (d_majMarks.size() + 1) * 4;
+ }
+ else if (maxMinSteps >= 2)
+ {
+ k0 = 2;
+ kmax = 5;
+ kstep = 3;
+ minSize = (d_majMarks.size() + 1) * 2;
+ }
+ else
+ {
+ k0 = 5;
+ kmax = 5;
+ kstep = 1;
+ minSize = (d_majMarks.size() + 1);
+ }
+
+ // resize buffer to the max. possible number of minor marks
+ buffer.resize(minSize);
+
+ // Are there minor ticks below the first major tick?
+ if ( d_lBound < firstTick )
+ i0 = -1;
+ else
+ i0 = 0;
+
+ minSize = 0;
+ for (i = i0; i< (int)d_majMarks.size(); i++)
+ {
+ if (i >= 0)
+ val = d_majMarks[i];
+ else
+ val = d_majMarks[0] / pow(10.0, d_majStep);
+
+ for (k=k0; k<= kmax; k+=kstep)
+ {
+ sval = val * double(k);
+ if (limRange(sval, d_lBound, d_hBound, border_eps))
+ {
+ buffer[minSize] = sval;
+ minSize++;
+ }
+ }
+ }
+
+ // copy values into the minMarks array
+ d_minMarks.duplicate(buffer.data(), minSize);
+
+ }
+ else // major step > one decade
+ {
+
+ // substep width in decades, at least one decade
+ minStep = qwtCeil125( (d_majStep - step_eps * (d_majStep / double(maxMinSteps)))
+ / double(maxMinSteps) );
+ minStep = qwtMax(1.0, minStep);
+
+ // # subticks per interval
+ nMin = int(rint(d_majStep / minStep)) - 1;
+
+ // Do the minor steps fit into the interval?
+ if ( qwtAbs( double(nMin + 1) * minStep - d_majStep) > step_eps * d_majStep)
+ nMin = 0;
+
+ if (nMin < 1) return TRUE; // no subticks
+
+ // resize buffer to max. possible number of subticks
+ buffer.resize((d_majMarks.size() + 1) * nMin );
+
+ // substep factor = 10^substeps
+ minFactor = qwtMax(pow(10,minStep), 10.0);
+
+ // Are there minor ticks below the first major tick?
+ if ( d_lBound < firstTick )
+ i0 = -1;
+ else
+ i0 = 0;
+
+ minSize = 0;
+ for (i = i0; i< (int)d_majMarks.size(); i++)
+ {
+ if (i >= 0)
+ val = d_majMarks[i];
+ else
+ val = firstTick / pow(10.0, d_majStep);
+
+ for (k=0; k< nMin; k++)
+ {
+ sval = (val *= minFactor);
+ if (limRange(sval, d_lBound, d_hBound, border_eps))
+ {
+ buffer[minSize] = sval;
+ minSize++;
+ }
+ }
+ }
+ d_minMarks.duplicate(buffer.data(), minSize);
+ }
+
+ return rv;
+}
+
+//------------------------------------------------------------
+//.F ScaleDiv::operator==
+// Equality operator
+//
+//.u Syntax
+//.f int ScaleDiv::operator==(const ScaleDiv &s)
+//
+//.u Parameters
+//.p const ScaleDiv &s
+//
+//.u Return Value
+// TRUE if this instance is equal to s
+//------------------------------------------------------------
+
+int ScaleDiv::operator==(const ScaleDiv &s) const
+ {
+ if (d_lBound != s.d_lBound)
+ return 0;
+ if (d_hBound != s.d_hBound)
+ return 0;
+ if (d_log != s.d_log)
+ return 0;
+ if (d_majStep != s.d_majStep)
+ return 0;
+ if (d_majMarks != s.d_majMarks)
+ return 0;
+ return (d_minMarks == s.d_minMarks);
+ }
+
+//------------------------------------------------------------
+//.F ScaleDiv::operator!=
+// Inequality
+//
+//.u Syntax
+//.f int ScaleDiv::operator!=(const ScaleDiv &s)
+//
+//.u Parameters
+//.p const ScaleDiv &s
+//
+//.u Return Value
+// TRUE if this instance is not equal to s
+//------------------------------------------------------------
+
+int ScaleDiv::operator!=(const ScaleDiv &s) const
+ {
+ return (!(*this == s));
+ }
+
+//------------------------------------------------------------
+//.F ScaleDiv::reset
+// Detach the shared data and set everything to zero.
+//
+//.u Syntax
+//.f void ScaleDiv::reset()
+//------------------------------------------------------------
+
+void ScaleDiv::reset()
+ {
+ // detach arrays
+ d_majMarks.duplicate(0,0);
+ d_minMarks.duplicate(0,0);
+
+ d_lBound = 0.0;
+ d_hBound = 0.0;
+ d_majStep = 0.0;
+ d_log = FALSE;
+ }
+
+
+
+
+
+
+
+