/*******************************************************************************
* Copyright (c) 2013 Luigi Sgro. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Luigi Sgro - initial API and implementation
******************************************************************************/
package com.quantcomponents.chart.series;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import com.quantcomponents.chart.BaseAxis;
import com.quantcomponents.chart.IAxis;
import com.quantcomponents.chart.IMark;
import com.quantcomponents.chart.IMarkScale;
import com.quantcomponents.chart.IMarkScaleSelector;
import com.quantcomponents.chart.series.scale.DayScale;
import com.quantcomponents.core.calendar.CalendarTradingSchedule;
import com.quantcomponents.core.calendar.ITradingCalendar;
import com.quantcomponents.core.calendar.ITradingDay;
import com.quantcomponents.core.calendar.ITradingSchedule;
/**
*
* Chart axis for time values
*/
public class TimeAxis extends BaseAxis<Date> implements IAxis<Date> {
private static final long MAX_SPAN_TO_EVALUATE_TRADING_PERIODS = 7L * 24 * 60 * 60 * 1000;
private static final long DAY_DURATION = 24L * 60 * 60 * 1000;
private final DayScale dayScale;
private final ITradingCalendar tradingCalendar;
private final ITradingSchedule tradingSchedule;
private final int padding;
private final IMarkScale<Date> baseMarkScale;
private final List<IMark<Date>> baseMarks;
private final List<IMark<Date>> parentMarks;
private final int pointWidth;
public TimeAxis(ITradingCalendar tradingCalendar, IMarkScaleSelector<Date> scaleSelector, Date rangeLow, Date rangeHigh, int pixelLow, int pixelHigh, long pointInterval, int maxMarkNumber) {
this.tradingCalendar = tradingCalendar;
tradingSchedule = new CalendarTradingSchedule(tradingCalendar);
dayScale = new DayScale(tradingCalendar.getTimeZone());
setRangeLow(rangeLow);
setRangeHigh(rangeHigh);
setPixelLow(pixelLow);
setPixelHigh(pixelHigh);
long realValueSpan = getRangeHigh().getTime() - getRangeLow().getTime();
double effectiveValueSpan;
if (realValueSpan < MAX_SPAN_TO_EVALUATE_TRADING_PERIODS) { // if span is below 1 week we must take into consideration trading hours
effectiveValueSpan = tradingSchedule.intervalBeetwen(getRangeLow(), getRangeHigh());
} else {
effectiveValueSpan = realValueSpan;
}
baseMarkScale = scaleSelector.markScale(effectiveValueSpan, maxMarkNumber);
IMarkScale<Date> parentMarkScale = baseMarkScale.parent();
baseMarks = new LinkedList<IMark<Date>>();
for (IMark<Date> mark = baseMarkScale.followingMark(getRangeLow()); mark != null && mark.getValue().compareTo(getRangeHigh()) < 0; mark = baseMarkScale.followingMark(mark.getValue())) {
Date date = mark.getValue();
Date adjustedDate = tradingSchedule.firstTradingTime(date);
if (isValid(adjustedDate)) {
baseMarks.add(new TimeMark(mark.getScale(), adjustedDate));
}
}
parentMarks = new LinkedList<IMark<Date>>();
for (IMark<Date> mark = parentMarkScale.followingMark(getRangeLow()); mark != null && mark.getValue().compareTo(getRangeHigh()) < 0; mark = parentMarkScale.followingMark(mark.getValue())) {
Date date = mark.getValue();
Date adjustedDate = tradingSchedule.firstTradingTime(date);
if (isValid(adjustedDate)) {
parentMarks.add(new TimeMark(mark.getScale(), adjustedDate));
}
}
long effectiveSpan;
if (pointInterval < DAY_DURATION) {
effectiveSpan = tradingSchedule.intervalBeetwen(getRangeLow(), getRangeHigh());
} else {
effectiveSpan = getRangeHigh().getTime() - getRangeLow().getTime();
}
int numberOfPoints = (int) Math.max((effectiveSpan / pointInterval), 1);
pointWidth = (getPixelHigh() - getPixelLow()) / numberOfPoints;
padding = pointWidth / 2 + 1;
}
@Override
public int calculatePixel(Date value) {
double displacement = tradingSchedule.intervalBeetwen(getRangeLow(), value) / (double) tradingSchedule.intervalBeetwen(getRangeLow(), getRangeHigh());
return getPixelLow() + padding + (int) (displacement * (getPixelHigh() - getPixelLow() - padding * 2));
}
@Override
public boolean isValid(Date abscissa) {
ITradingDay tradingDay = tradingCalendar.tradingDay(abscissa);
if (tradingDay.getTradingPeriods().length == 0) {
return false;
}
if (baseMarkScale.compareTo(dayScale) >= 0) {
return true;
}
return tradingSchedule.isTradingTime(abscissa);
}
@Override
public List<IMark<Date>> baseMarks() {
return Collections.unmodifiableList(baseMarks);
}
@Override
public List<IMark<Date>> parentMarks() {
return Collections.unmodifiableList(parentMarks);
}
@Override
public IMarkScale<Date> baseMarkScale() {
return baseMarkScale;
}
@Override
public int getPointSize() {
return pointWidth;
}
}