/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "stdafx.h" #include "Ruler.h" #include #include #include namespace DrawingPrimitives { enum { RULER_MIN_PIXELS_PER_TICK = 3, }; std::vector CalculateTicks(uint size, Range visibleRange, Range rulerRange, int* pRulerPrecision, Range* pScreenRulerRange) { std::vector ticks; if (size == 0) { if (pRulerPrecision) { *pRulerPrecision = 0; } return ticks; } const float pixelsPerUnit = visibleRange.Length() > 0.0f ? (float)size / visibleRange.Length() : 1.0f; const float startTime = rulerRange.start; const float endTime = rulerRange.end; const float totalDuration = endTime - startTime; const float ticksMinPower = log10f(RULER_MIN_PIXELS_PER_TICK); const float ticksPowerDelta = ticksMinPower - log10f(pixelsPerUnit); const int digitsAfterPoint = max(-int(ceil(ticksPowerDelta)) - 1, 0); if (pRulerPrecision) { *pRulerPrecision = digitsAfterPoint; } const float scaleStep = powf(10.0f, ceil(ticksPowerDelta)); const float scaleStepPixels = scaleStep * pixelsPerUnit; const int numMarkers = int(totalDuration / scaleStep) + 1; const float startTimeRound = int(startTime / scaleStep) * scaleStep; const int startOffsetMod = int(startTime / scaleStep) % 10; const int scaleOffsetPixels = aznumeric_cast((startTime - startTimeRound) * pixelsPerUnit); const int startX = aznumeric_cast((rulerRange.start - visibleRange.start) * pixelsPerUnit); const int endX = aznumeric_cast(startX + (numMarkers - 1) * scaleStepPixels - scaleOffsetPixels); if (pScreenRulerRange) { *pScreenRulerRange = Range(aznumeric_cast(startX), aznumeric_cast(endX)); } const int startLoop = std::max((int)((scaleOffsetPixels - startX) / scaleStepPixels) - 1, 0); const int endLoop = std::min((int)((size + scaleOffsetPixels - startX) / scaleStepPixels) + 1, numMarkers); for (int i = startLoop; i < endLoop; ++i) { STick tick; const int x = aznumeric_cast(startX + i * scaleStepPixels - scaleOffsetPixels); const float value = startTimeRound + i * scaleStep; tick.m_bTenth = (startOffsetMod + i) % 10 != 0; tick.m_position = x; tick.m_value = value; ticks.push_back(tick); } return ticks; } QColor Interpolate(const QColor& a, const QColor& b, float k) { float mk = 1.0f - k; return QColor(aznumeric_cast(a.red() * mk + b.red() * k), aznumeric_cast(a.green() * mk + b.green() * k), aznumeric_cast(a.blue() * mk + b.blue() * k), aznumeric_cast(a.alpha() * mk + b.alpha() * k)); } void DrawTicks(const std::vector& ticks, QPainter& painter, const QPalette& palette, const STickOptions& options) { QColor midDark = Interpolate(palette.color(QPalette::Dark), palette.color(QPalette::Button), 0.5f); painter.setPen(QPen(midDark)); const int height = options.m_rect.height(); const int top = options.m_rect.top(); for (const STick& tick : ticks) { const int x = tick.m_position + options.m_rect.left(); if (tick.m_bTenth) { painter.drawLine(QPoint(x, top + height - options.m_markHeight / 2), QPoint(x, top + height)); } else { painter.drawLine(QPoint(x, top + height - options.m_markHeight), QPoint(x, top + height)); } } } void DrawTicks(QPainter& painter, const QPalette& palette, const SRulerOptions& options) { const std::vector ticks = CalculateTicks(options.m_rect.width(), options.m_visibleRange, options.m_rulerRange, nullptr, nullptr); DrawTicks(ticks, painter, palette, options); } void DrawRuler(QPainter& painter, const QPalette& palette, const SRulerOptions& options, int* pRulerPrecision) { int rulerPrecision; Range screenRulerRange; const std::vector ticks = CalculateTicks(options.m_rect.width(), options.m_visibleRange, options.m_rulerRange, &rulerPrecision, &screenRulerRange); if (pRulerPrecision) { *pRulerPrecision = rulerPrecision; } if (options.m_shadowSize > 0) { QRect shadowRect = QRect(options.m_rect.left(), options.m_rect.height(), options.m_rect.width(), options.m_shadowSize); QLinearGradient upperGradient(shadowRect.left(), shadowRect.top(), shadowRect.left(), shadowRect.bottom()); upperGradient.setColorAt(0.0f, QColor(0, 0, 0, 128)); upperGradient.setColorAt(1.0f, QColor(0, 0, 0, 0)); QBrush upperBrush(upperGradient); painter.fillRect(shadowRect, upperBrush); } painter.fillRect(options.m_rect, Interpolate(palette.color(QPalette::Button), palette.color(QPalette::Midlight), 0.25f)); if (options.m_drawBackgroundCallback) { options.m_drawBackgroundCallback(); } QColor midDark = Interpolate(palette.color(QPalette::Dark), palette.color(QPalette::Button), 0.5f); painter.setPen(QPen(midDark)); QFont font; font.setPixelSize(10); painter.setFont(font); char format[16] = ""; sprintf_s(format, "%%.%df", rulerPrecision); const int height = options.m_rect.height(); const int top = options.m_rect.top(); QString str; for (const STick& tick : ticks) { const int x = tick.m_position + options.m_rect.left(); const float value = tick.m_value; if (tick.m_bTenth) { painter.drawLine(QPoint(x, top + height - options.m_markHeight / 2), QPoint(x, top + height)); } else { painter.drawLine(QPoint(x, top + height - options.m_markHeight), QPoint(x, top + height)); painter.setPen(palette.color(QPalette::Disabled, QPalette::Text)); str.asprintf(format, value); painter.drawText(QPoint(x + 2, top + height - options.m_markHeight + 1), str); painter.setPen(midDark); } } painter.setPen(QPen(palette.color(QPalette::Dark))); painter.drawLine(QPoint(aznumeric_cast(options.m_rect.left() + screenRulerRange.start), 0), QPoint(aznumeric_cast(options.m_rect.left() + screenRulerRange.start), options.m_rect.top() + height)); painter.drawLine(QPoint(aznumeric_cast(options.m_rect.left() + screenRulerRange.end), 0), QPoint(aznumeric_cast(options.m_rect.left() + screenRulerRange.end), options.m_rect.top() + height)); } }