Пример #1
0
        protected internal virtual void OnPaint(TimelineViewTrackPaintEventArgs e)
        {
            Rectangle rect = e.TargetRect;

            // Draw curve
            switch (e.GetAdjustedQuality(this.parentTrack.CurveQuality))
            {
            case QualityLevel.High:
                e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
                break;

            default:
            case QualityLevel.Medium:
            case QualityLevel.Low:
                e.Graphics.SmoothingMode = SmoothingMode.HighSpeed;
                break;
            }
            float pixelWidth = this.ParentView.ConvertUnitsToPixels(this.model.EndTime - this.model.BeginTime);

            if (pixelWidth < 5)
            {
                e.Graphics.SmoothingMode = SmoothingMode.HighSpeed;
            }
            if (pixelWidth > 2)
            {
                this.OnPaintCurve(e);
            }
            e.Graphics.SmoothingMode = SmoothingMode.Default;

            // Draw overlay
            this.OnPaintBoundaries(e);
        }
Пример #2
0
        protected virtual void OnPaintBoundaries(TimelineViewTrackPaintEventArgs e)
        {
            Rectangle rect   = e.TargetRect;
            float     beginX = this.ParentView.GetPosAtUnit(this.model.BeginTime);
            float     endX   = this.ParentView.GetPosAtUnit(this.model.EndTime);

            // Draw boundaries
            {
                Pen boundaryPen = new Pen(Color.FromArgb(128, this.baseColor));
                boundaryPen.DashStyle = DashStyle.Dash;
                e.Graphics.DrawLine(boundaryPen, beginX, rect.Top, beginX, rect.Bottom);
                e.Graphics.DrawLine(boundaryPen, endX, rect.Top, endX, rect.Bottom);
            }
        }
Пример #3
0
        protected virtual void OnPaintCurve(TimelineViewTrackPaintEventArgs e)
        {
            // Ignore e.BeginTime and e.EndTime for sampling, as we're heavily dependent on rounding errors, etc. while undersampling. Instead, always sample the whole curve.
            float beginUnitX = Math.Max(this.ParentView.UnitOriginOffset - this.ParentView.UnitScroll, this.model.BeginTime);
            float endUnitX = Math.Min(this.ParentView.UnitOriginOffset - this.ParentView.UnitScroll + this.ParentView.VisibleUnitWidth, this.model.EndTime);

            // Determine graph parameters
            float minPixelStep;
            switch (e.GetAdjustedQuality(this.parentTrack.CurvePrecision))
            {
                case QualityLevel.High:
                    minPixelStep = 0.25f;
                    break;
                default:
                case QualityLevel.Medium:
                    minPixelStep = 0.5f;
                    break;
                case QualityLevel.Low:
                    minPixelStep = 1.0f;
                    break;
            }
            float visibleMax = this.model.GetMaxValueInRange(beginUnitX, endUnitX);
            float visibleMin = this.model.GetMinValueInRange(beginUnitX, endUnitX);
            float visiblePixelHeight = this.ParentTrack.ConvertUnitsToPixels(Math.Abs(visibleMax - visibleMin));
            if (visiblePixelHeight < 10.0f)			minPixelStep *= 8.0f;
            else if (visiblePixelHeight < 20.0f)	minPixelStep *= 4.0f;
            else if (visiblePixelHeight < 40.0f)	minPixelStep *= 2.0f;
            else if (visiblePixelHeight < 70.0f)	minPixelStep *= 1.5f;
            minPixelStep = Math.Min(minPixelStep, 4);
            float minUnitStep = this.ParentView.ConvertPixelsToUnits(minPixelStep);
            Rectangle rect = e.TargetRect;

            if (this.curveCacheDirty)
            {
                // Begin a little sooner, so interpolation / error checking can gather some data
                beginUnitX -= minUnitStep * 5.0f;

                // Determine sample points
                PointF[] curvePointsEnvMax = null;
                PointF[] curvePointsEnvMin = null;
                if (this.curveOpacity > 0.0f)
                {
                    this.cacheCurveVertices = this.GetCurvePoints(rect, e.GetAdjustedQuality(this.parentTrack.CurvePrecision), this.model.GetValueAtX, minPixelStep, beginUnitX, endUnitX);
                }
                if (this.envelopeOpacity > 0.0f && !this.skipEnvelope)
                {
                    float envelopeUnitRadius = this.ParentView.ConvertPixelsToUnits(EnvelopeBasePixelRadius);
                    float minEnvelopeStepFactor;
                    switch (e.GetAdjustedQuality(this.parentTrack.EnvelopePrecision))
                    {
                        case QualityLevel.High:
                            minEnvelopeStepFactor = 0.1f;
                            break;
                        default:
                        case QualityLevel.Medium:
                            minEnvelopeStepFactor = 0.5f;
                            break;
                        case QualityLevel.Low:
                            minEnvelopeStepFactor = 1.0f;
                            break;
                    }
                    float envelopePixelStep = minEnvelopeStepFactor * EnvelopeBasePixelRadius;
                    float envelopeUnitStep = minEnvelopeStepFactor * envelopeUnitRadius;
                    curvePointsEnvMax = this.GetCurvePoints(
                        rect,
             						e.GetAdjustedQuality(this.parentTrack.CurvePrecision),
                        x => this.model.GetMaxValueInRange(x - envelopeUnitRadius, x + envelopeUnitRadius),
                        envelopePixelStep,
                        envelopeUnitStep * (int)(beginUnitX / envelopeUnitStep),
                        envelopeUnitStep * ((int)(endUnitX / envelopeUnitStep) + 1));
                    curvePointsEnvMin = this.GetCurvePoints(
                        rect,
             						e.GetAdjustedQuality(this.parentTrack.CurvePrecision),
                        x => this.model.GetMinValueInRange(x - envelopeUnitRadius, x + envelopeUnitRadius),
                        envelopePixelStep,
                        envelopeUnitStep * (int)(beginUnitX / envelopeUnitStep),
                        envelopeUnitStep * ((int)(endUnitX / envelopeUnitStep) + 1));

                    if (curvePointsEnvMax == null || curvePointsEnvMin == null || curvePointsEnvMax.Length + curvePointsEnvMin.Length < 3)
                        this.skipEnvelope = true;
                }

                if (curvePointsEnvMax != null && curvePointsEnvMin != null && curvePointsEnvMax.Length + curvePointsEnvMin.Length >= 3)
                {
                    // Calculate the visible envelope polygon
                    this.cacheEnvelopeVertices = new PointF[curvePointsEnvMax.Length + curvePointsEnvMin.Length];
                    for (int i = 0; i < curvePointsEnvMax.Length; i++)
                    {
                        this.cacheEnvelopeVertices[i] = curvePointsEnvMax[i];
                    }
                    for (int i = 0; i < curvePointsEnvMin.Length; i++)
                    {
                        this.cacheEnvelopeVertices[curvePointsEnvMax.Length + i] = curvePointsEnvMin[curvePointsEnvMin.Length - i - 1];
                    }

                    // Calculate the envelope and curve gradients
                    if (this.cacheCurveVertices != null)
                    {
                        float varianceUnitRadius = this.ParentView.ConvertPixelsToUnits(0.5f);
                        KeyValuePair<float,float>[] baseBlend = new KeyValuePair<float,float>[Math.Max(this.cacheCurveVertices.Length, 2)];
                        for (int i = 0; i < baseBlend.Length; i++)
                        {
                            float relativeX = (float)(this.cacheCurveVertices[(int)((float)i * (this.cacheCurveVertices.Length - 1) / (float)(baseBlend.Length - 1))].X - rect.X) / (float)rect.Width;
                            float unitX = this.ParentView.GetUnitAtPos(rect.X + relativeX * rect.Width);
                            float localOpacity = this.GetEnvelopeVisibility(
                                this.model.GetMaxValueInRange(unitX - varianceUnitRadius, unitX + varianceUnitRadius) -
                                this.model.GetMinValueInRange(unitX - varianceUnitRadius, unitX + varianceUnitRadius));
                            baseBlend[i] = new KeyValuePair<float,float>(relativeX, localOpacity);
                        }

                        this.cacheEnvelopeGradient = new LinearGradientBrush(rect, Color.Transparent, Color.Transparent, LinearGradientMode.Horizontal);
                        this.cacheCurveGradient = new LinearGradientBrush(rect, Color.Transparent, Color.Transparent, LinearGradientMode.Horizontal);

                        const int Samples = 21;
                        const int SamplesHalf = Samples / 2;
                        const int BlendSamplesPerChunk = 4;
                        float highestOpacity = 0.0f;
                        ColorBlend envelopeBlend = new ColorBlend(Math.Max(baseBlend.Length * BlendSamplesPerChunk / Samples, 2));
                        ColorBlend curveBlend = new ColorBlend(Math.Max(baseBlend.Length * BlendSamplesPerChunk / Samples, 2));
                        for (int i = 0; i < envelopeBlend.Colors.Length; i++)
                        {
                            int firstIndex = Math.Min(Math.Max(i * Samples / BlendSamplesPerChunk - SamplesHalf, 0), baseBlend.Length - 1);
                            int lastIndex = Math.Min(Math.Max(i * Samples / BlendSamplesPerChunk + SamplesHalf, 0), baseBlend.Length - 1);
                            float sum = 0.0f;
                            for (int j = firstIndex; j <= lastIndex; j++)
                            {
                                sum += baseBlend[j].Value;
                            }
                            float localOpacity = sum / (float)(1 + lastIndex - firstIndex);
                            highestOpacity = Math.Max(highestOpacity, localOpacity);
                            envelopeBlend.Colors[i] = Color.FromArgb((int)(localOpacity * this.envelopeOpacity * 255.0f), this.baseColor);
                            envelopeBlend.Positions[i] = baseBlend[firstIndex + (lastIndex - firstIndex) / 2].Key;
                            curveBlend.Colors[i] = Color.FromArgb((int)((1.0f - localOpacity) * (1.0f - localOpacity) * this.curveOpacity * 255.0f), this.baseColor);
                            curveBlend.Positions[i] = baseBlend[firstIndex + (lastIndex - firstIndex) / 2].Key;
                        }

                        if (highestOpacity <= 0.05f)
                        {
                            this.cacheEnvelopeGradient = null;
                            this.cacheCurveGradient = null;
                            this.skipEnvelope = true;
                        }
                        else
                        {
                            envelopeBlend.Positions[0] = 0.0f;
                            envelopeBlend.Positions[envelopeBlend.Positions.Length - 1] = 1.0f;
                            this.cacheEnvelopeGradient.InterpolationColors = envelopeBlend;
                            curveBlend.Positions[0] = 0.0f;
                            curveBlend.Positions[curveBlend.Positions.Length - 1] = 1.0f;
                            this.cacheCurveGradient.InterpolationColors = curveBlend;
                        }
                    }
                }
            }

            // Draw the envelope area
            if (this.cacheEnvelopeGradient != null && this.cacheEnvelopeVertices != null && this.cacheEnvelopeVertices.Length >= 3)
            {
                e.Graphics.FillPolygon(this.cacheEnvelopeGradient, this.cacheEnvelopeVertices);
            }

            // Draw the graph
            if (this.cacheCurveVertices != null && this.cacheCurveVertices.Length >= 2)
            {
                Pen linePen;
                if (this.cacheCurveGradient != null)
                {
                    linePen = new Pen(this.cacheCurveGradient);
                }
                else
                {
                    linePen = new Pen(Color.FromArgb((int)(this.curveOpacity * 255.0f), this.baseColor));
                }
                e.Graphics.DrawLines(linePen, this.cacheCurveVertices);
            }

            // Keep in mind that our cache is no valid again
            if (e.GetAdjustedQuality(this.parentTrack.CurvePrecision) == this.parentTrack.CurvePrecision &&
                e.GetAdjustedQuality(this.parentTrack.EnvelopePrecision) == this.parentTrack.EnvelopePrecision)
            {
                this.curveCacheDirty = false;
            }
        }
Пример #4
0
        protected virtual void OnPaintBoundaries(TimelineViewTrackPaintEventArgs e)
        {
            Rectangle rect = e.TargetRect;
            float beginX = this.ParentView.GetPosAtUnit(this.model.BeginTime);
            float endX = this.ParentView.GetPosAtUnit(this.model.EndTime);

            // Draw boundaries
            {
                Pen boundaryPen = new Pen(Color.FromArgb(128, this.baseColor));
                boundaryPen.DashStyle = DashStyle.Dash;
                e.Graphics.DrawLine(boundaryPen, beginX, rect.Top, beginX, rect.Bottom);
                e.Graphics.DrawLine(boundaryPen, endX, rect.Top, endX, rect.Bottom);
            }
        }
Пример #5
0
        protected internal virtual void OnPaint(TimelineViewTrackPaintEventArgs e)
        {
            Rectangle rect = e.TargetRect;

            // Draw curve
            switch (e.GetAdjustedQuality(this.parentTrack.CurveQuality))
            {
                case QualityLevel.High:
                    e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
                    break;
                default:
                case QualityLevel.Medium:
                case QualityLevel.Low:
                    e.Graphics.SmoothingMode = SmoothingMode.HighSpeed;
                    break;
            }
            float pixelWidth = this.ParentView.ConvertUnitsToPixels(this.model.EndTime - this.model.BeginTime);
            if (pixelWidth < 5) e.Graphics.SmoothingMode = SmoothingMode.HighSpeed;
            if (pixelWidth > 2)
            {
                this.OnPaintCurve(e);
            }
            e.Graphics.SmoothingMode = SmoothingMode.Default;

            // Draw overlay
            this.OnPaintBoundaries(e);
        }
 protected internal override void OnPaintRightSidebar(TimelineViewTrackPaintEventArgs e)
 {
     base.OnPaintRightSidebar(e);
     this.DrawLegend(e.Graphics, e.Renderer, e.TargetRect);
 }
        protected internal override void OnPaintOverlay(TimelineViewTrackPaintEventArgs e)
        {
            base.OnPaintOverlay(e);

            // Display mouseover data / effects
            if (this.ParentView.MouseoverContent && this.ParentView.ActiveMouseAction == TimelineView.MouseAction.None)
            {
                float unitEnvelopeRadius = this.ParentView.ConvertPixelsToUnits(TimelineViewGraph.EnvelopeBasePixelRadius);
                float mouseoverTime = this.ParentView.MouseoverTime;
                float mouseoverPixels = this.ParentView.GetPosAtUnit(mouseoverTime);

                // Accumulate graph value text information
                Font textFont = e.Renderer.FontSmall;
                StringFormat textFormat = new StringFormat();
                textFormat.FormatFlags |= StringFormatFlags.NoWrap;
                textFormat.Trimming = StringTrimming.EllipsisCharacter;
                Rectangle totalTextRect = Rectangle.Empty;
                List<GraphValueTextInfo> textInfoList = new List<GraphValueTextInfo>();
                {
                    int textYAdv = 0;
                    float visibleTimeSpan = this.ParentView.VisibleUnitWidth;
                    float visibleValueSpan = Math.Abs(this.verticalUnitTop - this.verticalUnitBottom);
                    int timeDecimals = Math.Max(0, -(int)Math.Log10(visibleTimeSpan) + 2);
                    int valueDecimals = Math.Max(0, -(int)Math.Log10(visibleValueSpan) + 2);

                    // Time text
                    {
                        GraphValueTextInfo textInfo;
                        textInfo.Text = string.Format(
                            System.Globalization.CultureInfo.InvariantCulture,
                            "{0:F" + timeDecimals + "}",
                            mouseoverTime);
                        textInfo.TargetRect = new Rectangle((int)mouseoverPixels + 2, e.TargetRect.Y + textYAdv + 1, MaxGraphValueTextWidth - 2, e.TargetRect.Height - textYAdv);
                        SizeF textSize = e.Graphics.MeasureString(textInfo.Text, textFont, textInfo.TargetRect.Size, textFormat);
                        textInfo.ActualRect = new Rectangle(textInfo.TargetRect.X, textInfo.TargetRect.Y, (int)textSize.Width, (int)textSize.Height);
                        textInfo.Color = e.Renderer.ColorText;

                        if (totalTextRect.IsEmpty)
                        {
                            totalTextRect = textInfo.ActualRect;
                        }
                        else
                        {
                            totalTextRect.X = Math.Min(totalTextRect.X, textInfo.ActualRect.X);
                            totalTextRect.Y = Math.Min(totalTextRect.Y, textInfo.ActualRect.Y);
                            totalTextRect.Width = Math.Max(totalTextRect.Width, textInfo.ActualRect.Right - totalTextRect.Left);
                            totalTextRect.Height = Math.Max(totalTextRect.Height, textInfo.ActualRect.Bottom - totalTextRect.Top);
                        }
                        textInfoList.Add(textInfo);
                        textYAdv += (int)textSize.Height;
                    }

                    // Graph texts
                    foreach (TimelineViewGraph graph in this.graphList)
                    {
                        GraphAreaInfo info;
                        if (!this.graphDisplayedInfo.TryGetValue(graph, out info)) continue;

                        GraphValueTextInfo textInfo;
                        if (info.EnvelopeVisibility < 0.25f)
                        {
                            textInfo.Text = string.Format(
                                System.Globalization.CultureInfo.InvariantCulture,
                                "{0:F" + valueDecimals + "}",
                                Math.Round(info.AverageValue, 2));
                        }
                        else
                        {
                            textInfo.Text = string.Format(
                                System.Globalization.CultureInfo.InvariantCulture,
                                "[{0:F" + valueDecimals + "}, {1:F" + valueDecimals + "}]", Math.Round(info.MinValue, 2),
                                Math.Round(info.MaxValue, 2));
                        }
                        textInfo.TargetRect = new Rectangle((int)mouseoverPixels + 2, e.TargetRect.Y + textYAdv + 1, MaxGraphValueTextWidth - 2, e.TargetRect.Height - textYAdv);
                        SizeF textSize = e.Graphics.MeasureString(textInfo.Text, textFont, textInfo.TargetRect.Size, textFormat);
                        textInfo.ActualRect = new Rectangle(textInfo.TargetRect.X, textInfo.TargetRect.Y, (int)textSize.Width, (int)textSize.Height);
                        textInfo.Color = graph.BaseColor.ScaleBrightness(0.75f);

                        if (totalTextRect.IsEmpty)
                        {
                            totalTextRect = textInfo.ActualRect;
                        }
                        else
                        {
                            totalTextRect.X = Math.Min(totalTextRect.X, textInfo.ActualRect.X);
                            totalTextRect.Y = Math.Min(totalTextRect.Y, textInfo.ActualRect.Y);
                            totalTextRect.Width = Math.Max(totalTextRect.Width, textInfo.ActualRect.Right - totalTextRect.Left);
                            totalTextRect.Height = Math.Max(totalTextRect.Height, textInfo.ActualRect.Bottom - totalTextRect.Top);
                        }

                        textInfoList.Add(textInfo);
                        textYAdv += (int)textSize.Height;
                    }
                }

                // Draw the texts background rect
                if (!totalTextRect.IsEmpty)
                {
                    e.Graphics.FillRectangle(
                        new SolidBrush(e.Renderer.ColorLightBackground.ScaleAlpha(0.5f)),
                        totalTextRect.X,
                        totalTextRect.Y,
                        totalTextRect.Width + 2,
                        totalTextRect.Height + 2);
                }

                // Draw graph mouseover visualizations
                foreach (TimelineViewGraph graph in this.graphList)
                {
                    GraphAreaInfo info;
                    if (!this.graphDisplayedInfo.TryGetValue(graph, out info)) continue;

                    // Determine mouseover data
                    float averagePixels = e.TargetRect.Y + this.GetPosAtUnit(info.AverageValue);
                    float minEnvelopePixels = Math.Min(e.TargetRect.Y + this.GetPosAtUnit(info.MinValue), e.TargetRect.Bottom - 2);
                    float maxEnvelopePixels = Math.Max(e.TargetRect.Y + this.GetPosAtUnit(info.MaxValue), e.TargetRect.Top + 1);

                    Color valueBaseColor = graph.BaseColor.ScaleBrightness(0.75f);

                    // Draw value range
                    if (info.EnvelopeVisibility > 0.05f)
                    {
                        SolidBrush brush = new SolidBrush(valueBaseColor.ScaleAlpha(info.EnvelopeVisibility));
                        e.Graphics.FillRectangle(brush, mouseoverPixels - 3, minEnvelopePixels, 7, 1);
                        e.Graphics.FillRectangle(brush, mouseoverPixels - 3, maxEnvelopePixels, 7, 1);
                        e.Graphics.FillRectangle(brush, mouseoverPixels, maxEnvelopePixels, 1, minEnvelopePixels - maxEnvelopePixels);
                    }

                    // Draw average / exact value knob
                    if (info.EnvelopeVisibility < 0.95f)
                    {
                        e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
                        e.Graphics.FillEllipse(
                            new SolidBrush(valueBaseColor.ScaleAlpha(1.0f - info.EnvelopeVisibility)),
                            mouseoverPixels - 2.5f,
                            averagePixels - 2.5f,
                            5,
                            5);
                        e.Graphics.SmoothingMode = SmoothingMode.Default;
                    }
                }

                // Draw value information texts
                foreach (GraphValueTextInfo textInfo in textInfoList)
                {
                    e.Graphics.DrawString(textInfo.Text, textFont, new SolidBrush(textInfo.Color), textInfo.TargetRect, textFormat);
                }
            }
        }
 protected internal override void OnPaintLeftSidebar(TimelineViewTrackPaintEventArgs e)
 {
     base.OnPaintLeftSidebar(e);
     this.DrawRuler(e.Graphics, e.Renderer, e.TargetRect, true);
 }
        protected internal override void OnPaint(TimelineViewTrackPaintEventArgs e)
        {
            base.OnPaint(e);

            Rectangle rect = e.TargetRect;

            // Draw extended ruler markings in the background
            {
                Brush bigLineBrush = new SolidBrush(e.Renderer.ColorRulerMarkMajor.ScaleAlpha(0.25f));
                Brush medLineBrush = new SolidBrush(e.Renderer.ColorRulerMarkRegular.ScaleAlpha(0.25f));
                Brush minLineBrush = new SolidBrush(e.Renderer.ColorRulerMarkMinor.ScaleAlpha(0.25f));
                this.drawBufferBigRuler.Clear();
                this.drawBufferMedRuler.Clear();
                this.drawBufferMinRuler.Clear();

                // Vertical ruler marks
                foreach (TimelineViewRulerMark mark in this.GetVisibleRulerMarks())
                {
                    if (mark.PixelValue < e.Graphics.ClipBounds.Top) continue;
                    if (mark.PixelValue > e.Graphics.ClipBounds.Bottom) break;

                    Rectangle lineRect = new Rectangle((int)rect.X, (int)mark.PixelValue, (int)rect.Width, 1);

                    switch (mark.Weight)
                    {
                        case TimelineViewRulerMarkWeight.Major:
                            this.drawBufferBigRuler.Add(lineRect);
                            break;
                        default:
                        case TimelineViewRulerMarkWeight.Regular:
                            this.drawBufferMedRuler.Add(lineRect);
                            break;
                        case TimelineViewRulerMarkWeight.Minor:
                            this.drawBufferMinRuler.Add(lineRect);
                            break;
                    }
                }

                if (this.drawBufferBigRuler.Count > 0) e.Graphics.FillRectangles(bigLineBrush, this.drawBufferBigRuler.ToArray());
                if (this.drawBufferMedRuler.Count > 0) e.Graphics.FillRectangles(medLineBrush, this.drawBufferMedRuler.ToArray());
                if (this.drawBufferMinRuler.Count > 0) e.Graphics.FillRectangles(minLineBrush, this.drawBufferMinRuler.ToArray());
            }

            // Draw the graphs
            {
                float beginUnitX = Math.Max(this.ParentView.UnitOriginOffset - this.ParentView.UnitScroll, this.ContentBeginTime);
                float endUnitX = Math.Min(this.ParentView.UnitOriginOffset - this.ParentView.UnitScroll + this.ParentView.VisibleUnitWidth, this.ContentEndTime);

                beginUnitX = Math.Max(Math.Max(beginUnitX, this.ParentView.GetUnitAtPos(e.Graphics.ClipBounds.Left - 1)), e.BeginTime);
                endUnitX = Math.Min(Math.Min(endUnitX, this.ParentView.GetUnitAtPos(e.Graphics.ClipBounds.Right)), e.EndTime);

                if (beginUnitX <= endUnitX)
                {
                    foreach (TimelineViewGraph graph in this.graphList)
                    {
                        graph.OnPaint(new TimelineViewTrackPaintEventArgs(this, e.Graphics, e.QualityHint, rect, beginUnitX, endUnitX));
                    }
                }
            }

            // Draw top and bottom borders
            e.Graphics.DrawLine(new Pen(e.Renderer.ColorVeryDarkBackground), rect.Left, rect.Top, rect.Right, rect.Top);
            e.Graphics.DrawLine(new Pen(e.Renderer.ColorVeryDarkBackground), rect.Left, rect.Bottom - 1, rect.Right, rect.Bottom - 1);
        }
Пример #10
0
 protected internal virtual void OnPaintOverlay(TimelineViewTrackPaintEventArgs e)
 {
 }
Пример #11
0
 protected internal virtual void OnPaintRightSidebar(TimelineViewTrackPaintEventArgs e)
 {
 }
Пример #12
0
        protected virtual void OnPaintCurve(TimelineViewTrackPaintEventArgs e)
        {
            // Ignore e.BeginTime and e.EndTime for sampling, as we're heavily dependent on rounding errors, etc. while undersampling. Instead, always sample the whole curve.
            float beginUnitX = Math.Max(this.ParentView.UnitOriginOffset - this.ParentView.UnitScroll, this.model.BeginTime);
            float endUnitX   = Math.Min(this.ParentView.UnitOriginOffset - this.ParentView.UnitScroll + this.ParentView.VisibleUnitWidth, this.model.EndTime);

            // Determine graph parameters
            float minPixelStep;

            switch (e.GetAdjustedQuality(this.parentTrack.CurvePrecision))
            {
            case QualityLevel.High:
                minPixelStep = 0.25f;
                break;

            default:
            case QualityLevel.Medium:
                minPixelStep = 0.5f;
                break;

            case QualityLevel.Low:
                minPixelStep = 1.0f;
                break;
            }
            float visibleMax         = this.model.GetMaxValueInRange(beginUnitX, endUnitX);
            float visibleMin         = this.model.GetMinValueInRange(beginUnitX, endUnitX);
            float visiblePixelHeight = this.ParentTrack.ConvertUnitsToPixels(Math.Abs(visibleMax - visibleMin));

            if (visiblePixelHeight < 10.0f)
            {
                minPixelStep *= 8.0f;
            }
            else if (visiblePixelHeight < 20.0f)
            {
                minPixelStep *= 4.0f;
            }
            else if (visiblePixelHeight < 40.0f)
            {
                minPixelStep *= 2.0f;
            }
            else if (visiblePixelHeight < 70.0f)
            {
                minPixelStep *= 1.5f;
            }
            minPixelStep = Math.Min(minPixelStep, 4);
            float     minUnitStep = this.ParentView.ConvertPixelsToUnits(minPixelStep);
            Rectangle rect        = e.TargetRect;


            if (this.curveCacheDirty)
            {
                // Begin a little sooner, so interpolation / error checking can gather some data
                beginUnitX -= minUnitStep * 5.0f;

                // Determine sample points
                PointF[] curvePointsEnvMax = null;
                PointF[] curvePointsEnvMin = null;
                if (this.curveOpacity > 0.0f)
                {
                    this.cacheCurveVertices = this.GetCurvePoints(rect, e.GetAdjustedQuality(this.parentTrack.CurvePrecision), this.model.GetValueAtX, minPixelStep, beginUnitX, endUnitX);
                }
                if (this.envelopeOpacity > 0.0f && !this.skipEnvelope)
                {
                    float envelopeUnitRadius = this.ParentView.ConvertPixelsToUnits(EnvelopeBasePixelRadius);
                    float minEnvelopeStepFactor;
                    switch (e.GetAdjustedQuality(this.parentTrack.EnvelopePrecision))
                    {
                    case QualityLevel.High:
                        minEnvelopeStepFactor = 0.1f;
                        break;

                    default:
                    case QualityLevel.Medium:
                        minEnvelopeStepFactor = 0.5f;
                        break;

                    case QualityLevel.Low:
                        minEnvelopeStepFactor = 1.0f;
                        break;
                    }
                    float envelopePixelStep = minEnvelopeStepFactor * EnvelopeBasePixelRadius;
                    float envelopeUnitStep  = minEnvelopeStepFactor * envelopeUnitRadius;
                    curvePointsEnvMax = this.GetCurvePoints(
                        rect,
                        e.GetAdjustedQuality(this.parentTrack.CurvePrecision),
                        x => this.model.GetMaxValueInRange(x - envelopeUnitRadius, x + envelopeUnitRadius),
                        envelopePixelStep,
                        envelopeUnitStep * (int)(beginUnitX / envelopeUnitStep),
                        envelopeUnitStep * ((int)(endUnitX / envelopeUnitStep) + 1));
                    curvePointsEnvMin = this.GetCurvePoints(
                        rect,
                        e.GetAdjustedQuality(this.parentTrack.CurvePrecision),
                        x => this.model.GetMinValueInRange(x - envelopeUnitRadius, x + envelopeUnitRadius),
                        envelopePixelStep,
                        envelopeUnitStep * (int)(beginUnitX / envelopeUnitStep),
                        envelopeUnitStep * ((int)(endUnitX / envelopeUnitStep) + 1));

                    if (curvePointsEnvMax == null || curvePointsEnvMin == null || curvePointsEnvMax.Length + curvePointsEnvMin.Length < 3)
                    {
                        this.skipEnvelope = true;
                    }
                }

                if (curvePointsEnvMax != null && curvePointsEnvMin != null && curvePointsEnvMax.Length + curvePointsEnvMin.Length >= 3)
                {
                    // Calculate the visible envelope polygon
                    this.cacheEnvelopeVertices = new PointF[curvePointsEnvMax.Length + curvePointsEnvMin.Length];
                    for (int i = 0; i < curvePointsEnvMax.Length; i++)
                    {
                        this.cacheEnvelopeVertices[i] = curvePointsEnvMax[i];
                    }
                    for (int i = 0; i < curvePointsEnvMin.Length; i++)
                    {
                        this.cacheEnvelopeVertices[curvePointsEnvMax.Length + i] = curvePointsEnvMin[curvePointsEnvMin.Length - i - 1];
                    }

                    // Calculate the envelope and curve gradients
                    if (this.cacheCurveVertices != null)
                    {
                        float varianceUnitRadius = this.ParentView.ConvertPixelsToUnits(0.5f);
                        KeyValuePair <float, float>[] baseBlend = new KeyValuePair <float, float> [Math.Max(this.cacheCurveVertices.Length, 2)];
                        for (int i = 0; i < baseBlend.Length; i++)
                        {
                            float relativeX    = (float)(this.cacheCurveVertices[(int)((float)i * (this.cacheCurveVertices.Length - 1) / (float)(baseBlend.Length - 1))].X - rect.X) / (float)rect.Width;
                            float unitX        = this.ParentView.GetUnitAtPos(rect.X + relativeX * rect.Width);
                            float localOpacity = this.GetEnvelopeVisibility(
                                this.model.GetMaxValueInRange(unitX - varianceUnitRadius, unitX + varianceUnitRadius) -
                                this.model.GetMinValueInRange(unitX - varianceUnitRadius, unitX + varianceUnitRadius));
                            baseBlend[i] = new KeyValuePair <float, float>(relativeX, localOpacity);
                        }

                        this.cacheEnvelopeGradient = new LinearGradientBrush(rect, Color.Transparent, Color.Transparent, LinearGradientMode.Horizontal);
                        this.cacheCurveGradient    = new LinearGradientBrush(rect, Color.Transparent, Color.Transparent, LinearGradientMode.Horizontal);

                        const int  Samples              = 21;
                        const int  SamplesHalf          = Samples / 2;
                        const int  BlendSamplesPerChunk = 4;
                        float      highestOpacity       = 0.0f;
                        ColorBlend envelopeBlend        = new ColorBlend(Math.Max(baseBlend.Length * BlendSamplesPerChunk / Samples, 2));
                        ColorBlend curveBlend           = new ColorBlend(Math.Max(baseBlend.Length * BlendSamplesPerChunk / Samples, 2));
                        for (int i = 0; i < envelopeBlend.Colors.Length; i++)
                        {
                            int   firstIndex = Math.Min(Math.Max(i * Samples / BlendSamplesPerChunk - SamplesHalf, 0), baseBlend.Length - 1);
                            int   lastIndex  = Math.Min(Math.Max(i * Samples / BlendSamplesPerChunk + SamplesHalf, 0), baseBlend.Length - 1);
                            float sum        = 0.0f;
                            for (int j = firstIndex; j <= lastIndex; j++)
                            {
                                sum += baseBlend[j].Value;
                            }
                            float localOpacity = sum / (float)(1 + lastIndex - firstIndex);
                            highestOpacity             = Math.Max(highestOpacity, localOpacity);
                            envelopeBlend.Colors[i]    = Color.FromArgb((int)(localOpacity * this.envelopeOpacity * 255.0f), this.baseColor);
                            envelopeBlend.Positions[i] = baseBlend[firstIndex + (lastIndex - firstIndex) / 2].Key;
                            curveBlend.Colors[i]       = Color.FromArgb((int)((1.0f - localOpacity) * (1.0f - localOpacity) * this.curveOpacity * 255.0f), this.baseColor);
                            curveBlend.Positions[i]    = baseBlend[firstIndex + (lastIndex - firstIndex) / 2].Key;
                        }

                        if (highestOpacity <= 0.05f)
                        {
                            this.cacheEnvelopeGradient = null;
                            this.cacheCurveGradient    = null;
                            this.skipEnvelope          = true;
                        }
                        else
                        {
                            envelopeBlend.Positions[0] = 0.0f;
                            envelopeBlend.Positions[envelopeBlend.Positions.Length - 1] = 1.0f;
                            this.cacheEnvelopeGradient.InterpolationColors = envelopeBlend;
                            curveBlend.Positions[0] = 0.0f;
                            curveBlend.Positions[curveBlend.Positions.Length - 1] = 1.0f;
                            this.cacheCurveGradient.InterpolationColors           = curveBlend;
                        }
                    }
                }
            }

            // Draw the envelope area
            if (this.cacheEnvelopeGradient != null && this.cacheEnvelopeVertices != null && this.cacheEnvelopeVertices.Length >= 3)
            {
                e.Graphics.FillPolygon(this.cacheEnvelopeGradient, this.cacheEnvelopeVertices);
            }

            // Draw the graph
            if (this.cacheCurveVertices != null && this.cacheCurveVertices.Length >= 2)
            {
                Pen linePen;
                if (this.cacheCurveGradient != null)
                {
                    linePen = new Pen(this.cacheCurveGradient);
                }
                else
                {
                    linePen = new Pen(Color.FromArgb((int)(this.curveOpacity * 255.0f), this.baseColor));
                }
                e.Graphics.DrawLines(linePen, this.cacheCurveVertices);
            }

            // Keep in mind that our cache is no valid again
            if (e.GetAdjustedQuality(this.parentTrack.CurvePrecision) == this.parentTrack.CurvePrecision &&
                e.GetAdjustedQuality(this.parentTrack.EnvelopePrecision) == this.parentTrack.EnvelopePrecision)
            {
                this.curveCacheDirty = false;
            }
        }