DigitalAxis.cc 6.33 KB
/*
 * DigitalAxis.cc
 *
 *  Created on: 3 déc. 2013
 *      Author: CS
 */

#include "DigitalAxis.hh"
#include "PlotLogger.hh"
#include "PanelPlotOutputException.hh"

#include <sstream>
#include <iomanip>
#include <iostream>
#include "plplot/plplotP.h"

namespace plot {

const int DigitalAxis::MAX_DIGIT_NUMBER = 3;
const int DigitalAxis::STANDARD_FORMAT_PRECISION = 1;
const int DigitalAxis::LOG_CONSTANT = 1000000;

std::string DigitalAxis::getPlotOpt() {
	std::string options = Axis::getPlotOpt();

	// Remove m or n plplot option if tickmark not visible
	// This will prevent plplot to draw any tickmark
	if (_showTickMark == false) {
		size_t pos;
		if ((pos = options.find("m")) != std::string::npos) {
			options.replace(pos, 1, "");
		}
		if ((pos = options.find("n")) != std::string::npos) {
			options.replace(pos, 1, "");
		}
	}

	if (_scale == Scale::LOGARITHMIC) {
			options += "l";
	} else if (getFormat() == Format::SCIENTIFIC) {
		// Check format to use for label
		options += "of";
	} else {
		options += "f";
	}

	return options;
}

Range DigitalAxis::getRange() {
	Range lRange = Axis::getRange();
	try {
		if (_scale == Scale::LOGARITHMIC) {

			double min;
			if (lRange.getMin() <= 0) {
				min = log10(lRange.getMax() / LOG_CONSTANT);
			} else {
				min = log10(lRange.getMin());
			}

			double max;
			if (lRange.getMax() <= 0) {
				max = log10(lRange.getMin() / LOG_CONSTANT);
			} else {
				max = log10(lRange.getMax());
			}

			lRange.setMin(min);
			lRange.setMax(max);

			if (lRange._extend) {
				return extendRange(lRange);
			} else {
				return lRange;
			}
		} else {
			if (lRange._extend) {
				return extendRange(lRange);
			} else {
				return lRange;
			}
		}
	} catch (std::exception& e) {
		// negative value for log ... stop process
		std::stringstream lError;
		lError << "DigitalAxis::getRange : range min and max have to be positive value (" << e.what() << ")";
		BOOST_THROW_EXCEPTION(
				PanelPlotOutputException() << AMDA::ex_msg(lError.str()));
	}
}

DigitalAxis::Format DigitalAxis::getFormat() {
	double lMin = pow(10, -MAX_DIGIT_NUMBER);
	double lMax = pow(10, MAX_DIGIT_NUMBER);

	double lDesiredMin = std::abs(getRange().getMin());
	double lDesiredMax = std::abs(getRange().getMax());


	// If maximum range doesn't include desired range,
	// label need to be formatted.
	if ( (lDesiredMin != 0 && (lDesiredMin <= lMin || lDesiredMin >= lMax)) ||
		(lDesiredMax != 0 && (lDesiredMax <= lMin || lDesiredMax >= lMax)) ) {
		return DigitalAxis::Format::SCIENTIFIC;
	} else {
		return DigitalAxis::Format::STANDARD;
	}
}

std::pair<int, int> DigitalAxis::getTickMarkSize() {
	// If tickmarks are not visible return a null size
	if (_showTickMark == false) {
		return std::pair<int , int> (0,0);
	}

	std::pair<int, int> tickMarkSize(0, getTickMarkLines());

	// If TickMark width is fixed, return it !
	if (_fixedTickMarkWidth != -1) {
		tickMarkSize.first = _fixedTickMarkWidth;
		return tickMarkSize;
	}

        Range lRange(getRange());
	if (_scale == Scale::LOGARITHMIC) {
		tickMarkSize.first = 3;
	} else if (getFormat() == Format::SCIENTIFIC){
		// (i.e. 1.3e⁴)
                _scientific_format_precision = computeScientificFormatPrecision(lRange.getMin(), lRange.getMax());
		tickMarkSize.first = _scientific_format_precision + 4 + (lRange.getMin() < 0 ? 1 : 0);
	} else {
		double dMinFloor = floor(fabs(lRange.getMin()));
		double dMaxFloor = floor(fabs(lRange.getMax()));

		// Check that min and max for is not null to avoid log10(0) !
		if ((dMinFloor == 0) && (dMaxFloor == 0)) {
			char	buf1 [64], buf2 [64];
			sprintf (buf1,"%.3f", lRange.getMin());
			sprintf (buf2,"%.3f", lRange.getMax());
			tickMarkSize.first = std::max(strlen (buf1), strlen (buf2));
		} else {

			int iMinFloor = log10 (dMinFloor) + 1;
			int iMaxFloor = log10 (dMaxFloor) + 1;

			double rangeSize = lRange.getMin() - lRange.getMax();
			int precision = abs(floor(log10(fabs(rangeSize))));

			tickMarkSize.first = precision + std::max(iMinFloor, iMaxFloor) + 1;

			if (lRange.getMin() < 0 || lRange.getMax() < 0) {
				tickMarkSize.first += 1;
			}
		}
	}

	return tickMarkSize;
}

/**
 * @overrides Axis::getComputedValues
 */
double* DigitalAxis::getComputedValues(double* originalValues_, int size_, double min, double max) {

	double *computedValues = new double [size_];

	// Duplicates data
	memcpy (computedValues, originalValues_, size_ * sizeof (double));

	// Need to filter min max values ?
	if ((std::isnan(min) == false) || (std::isnan(max) == false)) {
		for (int i = 0; i < size_; ++i) {
			if ((originalValues_[i] < min) || (originalValues_[i] > max)) {
				computedValues [i] = nan("");
			}
		}
	}

	// if scale is logarithmic, just calculate log for each value
	if ((_scale == Scale::LOGARITHMIC) && !_isZAxis) {
		for (int i = 0; i < size_; ++i) {
			if (computedValues[i] <= 0) {
				computedValues [i] = log10(std::max(min, max) / DigitalAxis::LOG_CONSTANT);
				LOG4CXX_WARN(gLogger, "Negative value found, no log calculated");

			} else {
				computedValues [i] = log10(computedValues[i]);
			}
		}
	}

	return computedValues;
}

void generateDigitalLabel(PLINT /*axis*/, PLFLT value, char *label, PLINT length, PLPointer data) {
    DigitalAxis *pAxis = (DigitalAxis *)data;
    getDigitalLabel(value, pAxis->getScientificFormatPrecision(), label, length);
}

void generateNoDigitalLabel(PLINT /*axis*/, PLFLT /*value*/, char *label, PLINT /*length*/, PLPointer /*data*/) {
	// Builds an empty Time label
	label [0] = 0;
}

int computeScientificFormatPrecision(double min, double max) {
    int precision = 1;
    
    int lMinExp = floor(log10(fabs(min)));
    int lMaxExp = floor(log10(fabs(max)));
    
    if (lMaxExp != lMinExp) {
        return precision;
    }
    
    PLFLT tick = 0;
    PLINT nsubt = 0;
    pldtik( min, max, &tick, &nsubt, false);
    
    double v = tick / pow(10, lMinExp);
    
    precision = ceil(fabs(log10(v)));
    
    return precision;
}

void getDigitalLabel(double value, int precision, char* label, int length) {
    if (value != 0) {
        int lExp = floor(log10(fabs(value)));
	double lValue = value / pow(10, lExp);
	if (lExp == 0)
            snprintf( label, length, "%.*f", precision, lValue);
        else
            snprintf( label, length, "%.*fe#u%i#d", precision, lValue, lExp);
    } else {
        std::stringstream lStrValue;
	lStrValue << value;
	snprintf( label, length, "%s", lStrValue.str().c_str());
    }
}

} /* namespace plot */